diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..90555f1
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,23 @@
+# according to the internet Python envs are hard and it's better to build them from scratch
+language: cpp
+os:
+- osx
+sudo: required
+install:
+- if [ "$TRAVIS_OS_NAME" = "osx" ]; then bash package/prepare_osx.sh; fi
+script:
+- python3 package/dependency_test.py
+before_deploy:
+- export FILENAME=manuskript-$TRAVIS_BRANCH-$TRAVIS_OS_NAME.zip
+- pyinstaller manuskript.spec --clean
+- cd dist && zip $FILENAME -r manuskript && cd ..
+- ls dist
+deploy:
+ provider: releases
+ api_key:
+ secure: lKuWQ9NWkLfDkkAiSnMh2PYkAGE1xh4pkSN5Ceb2IF9ee9i9YrJ9YFcvh420JSP+BYUl1OKczC5b3d9hUUZcfMwUkuZqPFCehOWP0O8dNs3XKZtmSU4POwR8lx7jRSO132qV/zgthBTK8PbeC2jIiMF4+4ESMsuEDptmGSOhgHtOcdJNDNz8gTbygXZVcl4U04se1ywtL62UQcTNMEKKLeBYQlrAXpcPiw4Htv9spEx6BewgarhRvJ/hysqeJgrH4wUVcjFw6Djppu3fmhrvgtaIU3ONxMLqgCuBZa03Q2LuF/RTYl9/DUgIjqsm1aOVooQZttYsUYWrLfyZNnLGD4WkdILcTMyexEVpQ/ejcEPm8gCf2PtklRtENZIxV2eQkLcPuSAPGWC8ue6a1etIUbYDbMT47SsdwkUsbyPpT8bnBvPf7gfmH/e20b1UQtxgmE5GDpbYZCTHf0kvwIFrBQzNmCtiGsXGJxIVx4msndplh8MdWFDBTEr0Ca8Tt45Fp/QPU7KAmdlQog9fWOfnJezIlBpFAXDa+AN2b/9uE+led5fmqQ62nuvjsYvWmJti2NW0IJ8UI26JGfh4Z1offE2bYp4onimfbRXSXWQs+Dm2l4CdTkc3habSWoUMw2R8mGbEfgfhzTFiAryg1mCtWy8AxUgfcUurd6BCwipH1ck=
+ file: dist/$FILENAME
+ overwrite: true
+ skip_cleanup: true
+ on:
+ tags: true
diff --git a/i18n/manuskript_de.qm b/i18n/manuskript_de.qm
index 7c4ce7f..21df451 100644
Binary files a/i18n/manuskript_de.qm and b/i18n/manuskript_de.qm differ
diff --git a/i18n/manuskript_de.ts b/i18n/manuskript_de.ts
index 070b7b6..afbde09 100644
--- a/i18n/manuskript_de.ts
+++ b/i18n/manuskript_de.ts
@@ -1,5 +1,6 @@
-
+
+Export
@@ -162,19 +163,23 @@
-
+ <p>Ein Set von Hervorhebungen.</p>
+ <p>Website: <a href="http://fletcherpenney.net/multimarkdown/">http://fletcherpenney.net/multimarkdown/</a></p>
+
-
+ Ähnlich Plaintext, erlaubt aber Hervorhebungen für Titel
+ Setzt vorraus, dass Texte bereits in Markdown formatiert sind.
-
+ Einfacher Export in Plaintext. Erlaubt die Benutzung von Markups, die von Manuskript nicht
+interpretiert werden können, wie zum Beispiel <a href='www.fountain.io'>Fountain</a>.
@@ -182,43 +187,51 @@
formats.</p>
<p>Website: <a href="http://www.pandoc.org">http://pandoc.org/</a></p>
-
+ <p>Ein universaler Dokumenten-Konverter. Kann zur Konvertierung von Markup in eine Reihe von anderen Formaten genutzt werden.
+ <p>Website: <a href="http://www.pandoc.org">http://pandoc.org/</a></p>
+
-
+ eine funktionierende LaTex-Installation. Siehe Systemvorrausetzungen für Pandoc:
+ <a href="http://pandoc.org/installing.html">http://pandoc.org/installing.html</a>. Wenn Unicode unterstützt werden soll, wird xelatex benötigt.
-
+ Exportiert nach Markdown via pandoc. Erlaubt mehr Formatierungen
+ als der Basis-Manuskript-Export.
-
+ LaTeX ist ein Textverarbeitungsprogramm und Dokumenten Markup-Language, das zur Erstellung
+ wunderschöner Dokumente benutzt wird.
-
+ Der Zweck dieses Formats ist ein einfacher Weg zum Austausch von Informationen
+ zwischen Außenstehenden und Internetservices, die von Außenstehend
+ durchsucht oder kontrolliert werden können.
-
+ Entfernt den YAML-Metadaten-Block.
+Nutze das, wenn du YAML-Errors bekommst.
-
+ Konvertierung nach ePUB3
@@ -299,7 +312,7 @@ Use that if you get YAML related error.
- Wörter ausschließen: <br>(Trennung durch Komma)
+ Wörter ausschließen: <br>(Trennung durch Komma):
@@ -772,229 +785,231 @@ Use that if you get YAML related error.
-
+ Buchinformationen
-
+ &Über
-
+ Über Manuskript
-
+ Die Datei {} existiert nicht. Versuche es noch einmal.
- Manuskript
+ Manuskript
-
+ Projekt {} gespeichert.
-
+ WARNUNG: Projekt {} nicht gespeichert.
-
+ Projekt {} geladen.
-
+ Projekt {} mit einigen Fehlern geladen:
-
+ * {} konnte in der Projektdatei nicht gefunden werden.
-
+ Projekt {} wurde mit einigen Fehlern geladen.
-
+ (~{} Seiten)
-
+ Wörter: {}{}
-
+ Buchzusammenfassung
-
+ Projektbaum
-
+ Metadaten
-
+ Handlung
-
+ Gib Informationen über dein Buch und dich ein.
-
+ Die Ausgangssituation, in Form von 'Was wäre wenn ...?" Fragen. Beispiel: "Was wäre wenn der gefährlichste
+ böse Zauberer nicht in der Lage wäre, ein Baby zu töten?" (Harry Potter)
-
+ Nimm dir Zeit, dir einen Satz zu überlegen (~50 Wörter), um dein Buch zusammenzufassen. Dann erweitere ihn
+ zu einem Absatz, dann zu einer ganzen Seite und abschließend zu einer kompletten Zusammenfassung.
-
+ Erstelle deine Charaktere.
-
+ Entwickle Handlungsstränge.
-
+ Erbaue Welten. Erstelle Hierachien breitgefächterter Kategorien, bis hin zu spezifischen Details.
-
+ Erstelle den Entwurf deines Meisterwerks.
-
+ Schreibe.
-
+ Debuginformationen. Manchmal hilfreich.
-
+ Wörterbuch
-
+ Installiere PyEnchant um die Rechtschreibprüfung zu nutzen
- Keine
+ Keine
- POV
+ Perspektive
- Label
+ Beschriftung
- Fortschritt
+ Fortschritt
- Compile
+ Kompiliere
-
+ Iconfarbe
-
+ Textfarbe
-
+ Hintergrundfarbe
-
+ Icon
- Text
+ Text
- Hintergrund
+ Hintergrund
-
+ Rahmen
-
+ Ecke
-
+ Füge Handlungsschritt hinzu (Strg+Enter)
-
+ Strg+Enter
-
+ Entferne markierte(n) Handlungsschritt(e) (Strg+Löschtaste)
-
+ Strg+Löschtaste
@@ -1267,7 +1282,7 @@ Use that if you get YAML related error.
- Wortzählung
+ Wortanzahl
@@ -1542,77 +1557,77 @@ Use that if you get YAML related error.
-
+ Stil
-
+ Alter Stil
-
+ Neuer Stil
-
+ Zeiger
-
+ Nutze Absatzeinrückung von
-
+ Ausrichtung:
-
+ Blocksatz
-
+ Ausrichtung
-
+ Icongröße
- Textlabel
+ Textbeschriftung
-
+ Blinken Aus
-
+ Textfeld
-
+ Max. Breite
-
+ Abstände Rechts/Links:
-
+ Abstände Oben/Unten:
@@ -1620,17 +1635,17 @@ Use that if you get YAML related error.
-
+ Korrekturvorschläge
-
+ Zum Wörterbuch &hinzufügen
-
+ Aus dem Wörterbuch &entfernen
@@ -1638,12 +1653,12 @@ Use that if you get YAML related error.
-
+ Geladene Übersetzung: {}.
-
+ Notiz: Keine Übersetzung für {} gefunden oder geladen.
@@ -1666,17 +1681,17 @@ Use that if you get YAML related error.
- Wortzahl
+ Wortanzahl
- Inhaltsangabe in einem Satz
+ Zusammenfassung in einem Satz
- Inhaltsangabe in wenigen Sätzen:
+ Zusammenfassung in wenigen Sätzen:
@@ -1684,17 +1699,17 @@ Use that if you get YAML related error.
-
+ Neuer Charakter
- Name
+ Name
-
+ Wert
@@ -1702,17 +1717,17 @@ Use that if you get YAML related error.
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
@@ -1730,37 +1745,37 @@ Use that if you get YAML related error.
-
+ Nebensächlich
-
+ Sekundär
-
+ Primär
- Charaktere
+ Charaktere
-
+ Texte
- Handlungsstränge
+ Handlungsstränge
- Welt
+ Welt
@@ -1768,27 +1783,27 @@ Use that if you get YAML related error.
- Nichts
+ Nichts
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
-
+ Verschiedene
@@ -1796,7 +1811,7 @@ Use that if you get YAML related error.
-
+ Verschiedene
@@ -1804,7 +1819,7 @@ Use that if you get YAML related error.
-
+ Verschiedene
@@ -1812,7 +1827,7 @@ Use that if you get YAML related error.
-
+
@@ -1828,12 +1843,12 @@ Use that if you get YAML related error.
- Inhaltsangabe in einem Satz
+ Inhaltsangabe in einem Satz
- Vollständige Inhaltsangabe
+ Vollständige Zusammenfassung
@@ -1877,7 +1892,7 @@ Use that if you get YAML related error.
-
+ {} (noch nicht implimentiert)
@@ -1992,12 +2007,12 @@ Use that if you get YAML related error.
Typografisches Ersetzen:
-
+ Ersetze ... durch …
-
+ Ersetze --- durch —
@@ -2070,17 +2085,17 @@ Use that if you get YAML related error.
-
+ Ordner
-
+ {}Level {} Ordner
-
+ {}Level {} Text
@@ -2088,37 +2103,37 @@ Use that if you get YAML related error.
-
+ Installiert
- Benutzerdefiniert
+ Benutzerdefiniert
-
+ Nicht gefunden
-
+ {} nicht gefunden. Installiere es oder gib den Pfad manuell ein.
-
+ <b>Status:</b> Nicht installiert.
-
+ <b>Voraussetzung</b>
-
+ Setze {} ausführbaren Pfad.
@@ -2126,17 +2141,17 @@ Use that if you get YAML related error.
-
+ Phrasen
-
+ Häufigkeit
-
+ Wort
@@ -2144,17 +2159,17 @@ Use that if you get YAML related error.
-
+ Thema:
-
+ {} Wörter / {}
- {} Wörter
+ {} Wörter
@@ -2162,7 +2177,7 @@ Use that if you get YAML related error.
-
+ Wenn du mich nicht sehen willst, kannst du mich über das Hilfemenü verschwinden lassen.
@@ -2170,7 +2185,7 @@ Use that if you get YAML related error.
-
+ Verschiedenes
@@ -2213,32 +2228,32 @@ Use that if you get YAML related error.
-
+ ~{} h.
-
+ ~{} m.
-
+
-
+ {} s.
-
+ {} verbleibend
-
+ {} Wörter verbleiben
@@ -2271,27 +2286,27 @@ Use that if you get YAML related error.
-
+ Gehe zum übergeordneten Element
-
+ Alt+Nach oben
-
+ Stamm
-
+ {} Wörter / {}
- {} Wörter
+ {} Wörter
@@ -2299,7 +2314,7 @@ Use that if you get YAML related error.
-
+ Markdown
@@ -2345,7 +2360,7 @@ Use that if you get YAML related error.
-
+ Automatisches Ausblenden
@@ -2408,32 +2423,32 @@ Use that if you get YAML related error.
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
-
+ Öffne Element
-
+ Benutzerdefinierten Icon einfügen
-
+ Auf Standard zurücksetzen
@@ -2441,22 +2456,22 @@ Use that if you get YAML related error.
- Nichts
+ Nichts
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
@@ -2474,37 +2489,37 @@ Use that if you get YAML related error.
- Titel
+ Titel
- POV
+ Perspektive
- Label
+ Beschriftung
- Status
+ Status
- Compile
+ Kompiliere
-
+ Wortanzahl
- Ziel
+ Ziel
@@ -2512,17 +2527,17 @@ Use that if you get YAML related error.
- Allgemein
+ Allgemein
-
+ Inhaltsverzeichnis
-
+ Benutzerdefinierte Einstellungen für {}
@@ -2530,17 +2545,17 @@ Use that if you get YAML related error.
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
@@ -2548,182 +2563,182 @@ Use that if you get YAML related error.
- Allgemein
+ Allgemein
-
+ Versprechen
-
+ Problem
- Fortschritt
+ Fortschritt
-
+ Lösung
-
+ Versuch / Scheitern
-
+ Nein und
-
+ Ja, aber
-
+ Freytags Pyramide
-
+ Darstellung
-
+ Spannungsanstieg
-
+ Klimax
-
+ Spannungsabfall
-
+ Drei Akte
-
+ 1. Aufbau
-
+ 1. Aufmacher
-
+ 1. Wendepunkt
-
+ 2. Wahl
-
+ 2. Wende
-
+ 2. Katastrophe
-
+ 3. Gegenwehr
-
+ 3. Klimax
-
+ 3. Ende
-
+ Heldenreise
-
+ Gewöhnliche Welt
-
+ Ruf des Abenteuers
-
+ Absage des Rufs
-
+ Treffen mit dem Mentor
-
+ Übertreten der Schwelle
-
+ Prüfungen
-
+ Annäherung
-
+ Abgrund
-
+ Belohnung / Offenbarung
-
+ Verwandlung
-
+ Sühne
-
+ Rückkehr
@@ -2731,37 +2746,37 @@ Use that if you get YAML related error.
-
+ Neuer Handlungsstrang
- Name
+ Name
-
+ Meta
-
+ Neuer Schritt
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
@@ -2769,22 +2784,22 @@ Use that if you get YAML related error.
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
-
+ **Handlungsstrang:** {}
@@ -2792,17 +2807,17 @@ Use that if you get YAML related error.
-
+ Primär
-
+ Sekundär
-
+ Nebensächlich
@@ -2983,27 +2998,27 @@ Use that if you get YAML related error.
- Motivation
+ Motivation
- Ziel
+ Ziel
- Schicksal
+ Schicksal
-
+ Kurzzusammenfassung
-
+ Lange Zusammenfassung
@@ -3031,67 +3046,67 @@ Use that if you get YAML related error.
-
+ Zeige Modifikationen
-
+ Zeige vorherige Version
-
+ Zeige Leerzeichen
-
+ Zeige nur die Modifikationen
-
+ {} Jahre zuvor
-
+ {} Monate zuvor
-
+ {} Tage zuvor
-
+ 1 Tag zuvor
-
+ {} Stunden zuvor
-
+ {} Minuten zuvor
-
+ {} Sekunden zuvor
-
+ Zeile {}:
-
+ Leeren
@@ -3109,57 +3124,57 @@ Use that if you get YAML related error.
-
+ Suche in:
-
+ Alles
- Titel
+ Titel
- Text
+ Text
- Inhaltsangabe
+ Zusammenfassung
- Notizen
+ Notizen
- POV
+ Perspektive
- Status
+ Status
- Label
+ Beschriftung
-
+ Optionen:
-
+ Groß-/Kleinschreibung berücksichtigen
@@ -3167,27 +3182,27 @@ Use that if you get YAML related error.
-
+ Neuer Status
-
+ Neue Beschriftung
-
+
-
+ Neues Thema
-
+ (schreibgeschützt)
@@ -3205,17 +3220,17 @@ Use that if you get YAML related error.
-
+ Nebensächlich
-
+ Sekundär
-
+ Primär
@@ -3228,12 +3243,12 @@ Use that if you get YAML related error.
-
+ Zeige Handlungsstränge
-
+ Zeige Charaktere
@@ -3241,22 +3256,22 @@ Use that if you get YAML related error.
-
+ Öffne markierte Elemente in dieser Ansicht.
-
+ Ansicht horizontal teilen
-
+ Teilung beenden
-
+ Ansicht vertikal teilen
@@ -3264,7 +3279,7 @@ Use that if you get YAML related error.
-
+ Referenz einfügen
@@ -3272,7 +3287,7 @@ Use that if you get YAML related error.
-
+ Verschiedenes
@@ -3285,42 +3300,42 @@ Use that if you get YAML related error.
-
+ Strg+B
-
+ Strg+I
-
+ Strg+U
-
+ Strg+P
-
+ Strg+L
-
+ Strg+E
-
+ Strg+R
-
+ Strg+J
@@ -3328,37 +3343,37 @@ Use that if you get YAML related error.
-
+ Stamm
-
+ Öffne {} Elemente in neuen Tabs
-
+ Öffne {} in einem neuen Tab
-
+ {} ausklappen
-
+ {} einklappen
-
+ Alles ausklappen
-
+ Alles einklappen
@@ -3441,147 +3456,147 @@ Use that if you get YAML related error.
-
+ Öffne Projekt
-
+ Manuskript Projekt (*.msk);;Alle Dateien (*)
-
+ Speichern als ...
-
+ Manuskript Projekt (*.msk)
- Manuskript
+ Manuskript
-
+ Erzeuge neues Projekt
-
+ Warnung
-
+ Existierendes Projekt {} überschreiben?
-
+ Leere Geschichte
-
+ Kapitel
-
+ Szene
-
+ Triologie
-
+ Buch
-
+ Absatz
-
+ Leeres Sachbuch
-
+ Wörter.
-
+ von
- Text
+ Text
-
+ Irgendwas
-
+ <b>Gesamt:</b> {} Wörter (~ {} Seiten)
-
+ Geschichte
-
+ Sachtext
-
+ Idee
-
+ Notiz
-
+ Recherche
-
+ ToDo
-
+ Erster Entwurf
-
+ Zweiter Entwurf
-
+ Final
@@ -3589,212 +3604,212 @@ Use that if you get YAML related error.
-
+ Neues Element
-
+ Bau einer Fantasy-Welt
-
+ Geographie
-
+ Klima
-
+ Relief
-
+ Astronomie
-
+ Natur
-
+ Fauna
-
+ Flora
-
+ Geschichte
-
+ Arten
-
+ Krankheiten
-
+ Kultur
-
+ Bräuche
-
+ Essen
-
+ Sprachen
-
+ Erziehung
-
+ Kleidung
-
+ Wissenschaft
-
+ Zeitrechnung
-
+ Körpersprache
-
+ Ethik
-
+ Religion
-
+ Regierung
-
+ Politik
-
+ Geschlichterrollen
-
+ Musik und Kunst
-
+ Architektur
-
+ Militär
-
+ Technologie
-
+ Balzverhalten
-
+ Demographie
-
+ Transportmittel
-
+ Medizin
-
+ Magiesystem
-
+ Regeln
-
+ Organisation
-
+ Magische Objekte
-
+ Magische Orte
-
+ Magische Wesen
-
+ Wichtige Orte
-
+ Wichtige Objekte
diff --git a/i18n/manuskript_sv.qm b/i18n/manuskript_sv.qm
index 82eccc8..a4fd370 100644
Binary files a/i18n/manuskript_sv.qm and b/i18n/manuskript_sv.qm differ
diff --git a/i18n/manuskript_sv.ts b/i18n/manuskript_sv.ts
index 92f2bc8..cb60a6e 100644
--- a/i18n/manuskript_sv.ts
+++ b/i18n/manuskript_sv.ts
@@ -8,7 +8,6 @@
- This makes limited sense in Swedish.<p>En supermängd till markdown.</p>
<p>Websida: <a href="http://fletcherpenney.net/multimarkdown/">http://fletcherpenney.net/multimarkdown/</a></p>
@@ -26,7 +25,6 @@
- Not sure why this is repeated again.python-modulen 'markdown'.
@@ -54,7 +52,6 @@
- Unclear what highlighter is -- going with the physical version used to highlight text.Förhandsgranskning med märkpenna.
@@ -77,7 +74,6 @@
- Is this sarcasm? :)Ett relativt okänt format som används blygsamt, t.ex. på websidor.
@@ -92,14 +88,13 @@
-
+ Fel
- What's the context here? It looks a bit random. :)Böcker som inte mördar träd.
@@ -115,14 +110,12 @@
- Incorrect casing in English.Kräver att LaTex är installerat.
- This string seems incomplete in English?en giltig LaTex-installation. Se vad pandoc rekommenderar på:
<a href="http://pandoc.org/installing.html">http://pandoc.org/installing.html</a>. Vill du ha stöd för unicode behöver du xelatex.
@@ -184,50 +177,62 @@
Välj rotnivå för rubriker:
-
+ Använd referenslänkar i stället för infogade länkar
-
+ Använd ATX-rubriker
-
+ Fristående HTML-filer utan andra beroenden
-
+ Använd <q>-taggar för citat i HTML
-
+ LaTeX-motor som används för att skapa PDF-dokumentet.
-
+ Pappersstorlek:
-
+ Teckenstorlek:
-
+ Klass:
-
+ Radavstånd:
+
+
+
+ Avaktivera YAML metadata block.
+Använd detta om du får ett felmeddelande angående YAML.
+
+
+
+
+ Konvertera till ePUB3
+ ExportersManager
@@ -333,7 +338,7 @@
MainWindow
-
+ Allmänt
@@ -378,7 +383,7 @@
Författare
-
+ Namn
@@ -388,7 +393,7 @@
E-mail
-
+ Sammanfattning
@@ -398,7 +403,7 @@
Situation:
-
+ Sammanfattning:
@@ -408,17 +413,17 @@
En mening
-
+ Ett stycke
-
+ En sida
-
+ Full
@@ -468,7 +473,7 @@
Namn
-
+ Filter
@@ -523,7 +528,7 @@
Detaljerad info
-
+ Handlingar
@@ -538,7 +543,7 @@
Karaktär(er)
-
+ Beskrivning
@@ -553,442 +558,459 @@
Lösningssteg
-
+ Värld
-
+ Fylls med tom data
-
+ Mer
-
+ Passionskälla
-
+ Konfliktkälla
-
+ Utkast
-
+ Redigerare
-
+
- Placeholder?Debug
-
+
- Placeholder?FlatData
-
+
- Placeholder?Persos
-
+ Etiketter
-
+ &Arkiv
-
+ S&enaste
-
+ &Hjälp
-
+ &Verktyg
-
+ &Redigera
-
+ &Visa
-
+ &Läge
-
+ &Fusklapp
-
+ S&ök
-
+ &Navigation
-
+ &Öppna
-
+ Ctrl+O
-
+ &Spara
-
+ Ctrl+S
-
+ Spara s&om...
-
+ Ctrl+Shift+S
-
+ &Avsluta
-
+ Ctrl+Q
-
+ &Visa hjälptexter
-
+ Ctrl+Shift+B
-
+ &Stavningskontroll
-
+ F9
-
+ &Etiketter...
-
+ &Status...
-
+ Träd
-
+ &Enkelt
-
+ &Skönlitteratur
-
+ S&nöflingemetoden
-
+ Registerkort
-
+ I&nställningar
-
+ F8
-
+ S&täng projekt
-
+ Ko&mpilera
-
+ F6
-
+ &Frekvensanalys
-
+ &Om
-
+ Om Manuskript
-
+ Filen {} finns inte. Försök igen.
-
+ Manuskript
-
+ Projekt {} sparades.
-
+ VARNING: Projekt {} sparades ej.
-
+ Projekt {} laddades.
-
+ Projekt {} laddades med vissa fel:
-
+ * {} hittades inte i projektfilen.
-
+ Projekt {} laddades med vissa fel.
-
+ (~{} sidor)
-
+ Ord: {}{}
-
+ Sammanfattning av boken
-
+ Projektträd
-
+ Metadata
-
+ Handling
-
+ Skriv information om din bok och dig själv.
-
+ Den grundläggande situationen i form av en "Tänk om...?"-mening. Exempel: 'Tänk om världens farligaste
onda trollkarl misslyckades med att döda en baby?' (Harry Potter)
-
+ Tänk ut en kort (ca. 50 ord) mening som sammanfattar din bok. Utveckla den sedan till
ett stycke, till en sida och sist till en full sammanfattning.
-
+ Skapa dina karaktärer.
-
+ Utveckla handlingen.
-
+ Utforma världar. Skapa en hierarki av generella kategorier ned till minsta detalj.
-
+ Skapa ett utkast för ditt mästerverk.
-
+ Skriv.
-
+ Debug-information. Kan vara användbar.
-
+ Ordbok
-
+ Installera PyEnchant för stavningskontroll
-
+ Ingenting
-
+ Synvinkel
-
+ Etikett
-
+ Framsteg
-
+ Kompilera
-
+ Ikonfärg
-
+ Textfärg
-
+ Bakgrundsfärg
-
+ Ikon
-
+ Text
-
+ Bakgrund
-
+ Kant
-
+ Hörn
+
+
+
+ Lägg till nytt steg i handlingen (CTRL+Enter)
+
+
+
+
+ Ctrl+Enter
+
+
+
+
+ Ta bort valda steg i handlingen (CTRL+Backspace)
+
+
+
+
+ Ctrl+Backspace
+ Settings
@@ -998,596 +1020,631 @@
Inställningar
-
+ Allmänt
-
+ Ändringshistorik
-
+ Visningar
-
+ Etiketter
-
+ Status
-
+ Fullskärm
-
+ Allmänna inställningar
-
+ Applikationens utseende
-
+ Du kan behöva starta om Manuskript för att undvika problem med utseendet.
-
+ Applikationens språk
-
+ Du behöver starta om Manuskript för att se ändringarna.
-
+ Uppstart
-
+ Ladda automatiskt senaste projekt vid uppstart
-
+ Sparande
-
+ Spara automatiskt var
-
+ minut.
-
+ Om inget har ändrats på
-
+ sekunder.
-
+ Spara vid avslut
-
+ <html><head/><body><p>Markeras detta alternativ sparas ditt projekt som en samlad fil. Detta gör det lättare att ta en backup eller göra en kopia, men tillåter inte gemensam redigering eller versionshantering.<br/>Markeras alternativet inte sparas ditt projekt som en mapp med många små filer.</p></body></html>
-
+ Spara i en samlad fil
-
+ Ändringshistorik låter dig följa dina ändringar. För varje textobjekt sparas ändringar som du har gjort vilket ger dig möjlighet till att se och återskapa tidigare versioner.
-
+ Aktivera Ändringshistorik
-
+ S&mart borttagning
-
+ Behåll:
-
+ Smart borttagning låter dig behålla ett visst antal ändringar. Du rekommenderas att använda den för att undvika att din fil fylls med tusentals av oviktiga ändringar.
-
+ ändringar per dag för senaste månaden
-
+ ändringar per minut för senaste 10 minuterna
-
+ ändringar per timme för senaste dagen
-
+ ändringar per 10 minuter för senaste timmen
-
+ ändringar per vecka tills vidare
-
+ Visningsinställningar
-
+ Träd
-
+ Färger
-
+ Ikonfärg:
-
+ Ingenting
-
+ Synvinkel
-
+ Etikett
-
+ Framsteg
-
+ Kompilera
-
+ Textfärg:
-
+ Bakgrundsfärg:
-
+ Mappar
-
+ Visa antal obje&kt
-
+ Visa antal ord
-
+ Visa framsteg
-
+ Visa sammanfattning
-
+ I&ngenting
-
+ Text
-
+ Utkast
-
+ Synliga kolumner
-
+ Mål
-
+ Antal ord
-
+ Procent
-
+ Titel
-
+ Registerkort
-
+ Bakgrund
-
+ Färg:
-
+ Ctrl+S
-
+ Bild:
-
+ Utseende
-
+ Gammalt utseende
-
+ Nytt utseende
-
+ Objektfärger
-
+ Kantfärg:
-
+ Hörnfärg:
-
+ Textredigerare
-
+ Typsnitt
-
+ Familj:
-
+ Teckenstorlek:
-
+ Felstavat:
-
+ Bakgrund:
-
+ Markör
-
+ Använd blockmarkör på
-
+ pixlar
-
+ Stycke
-
+ Radavstånd:
-
+ Enkelt
-
+ 1.5 rader
-
+ Dubbelt
-
+ Proportionellt
-
+ %
-
+ Tabb-bredd:
-
+ Indrag på rad 1
-
+
- It seems like some labels could be missing here? Should this say "Spacing before and after" or should it be split up into multiple labels?Avstånd:
-
+ Justering:
-
+ Vänster
-
+ Centrera
-
+ Höger
-
+ Justerad
-
+ Ny
-
+ Redigera
-
+ Ta bort
-
+ Temats namn:
-
+ Använd
-
+ Avbryt
-
+ Fönsterbakgrund
-
+ Textbakgrund
-
+ Textinställningar
-
+ Styckesinställningar
-
+ Typ:
-
+ Ingen bild
-
+ Sida vid sida
-
+ Centrerad
-
+ Sträckt
-
+ Skalad
-
+ Zoomad
-
+ Opacitet:
-
+ Position:
-
+ Bredd:
-
+ Hörnradie:
-
+ Marginaler:
-
+ Utfyllnad:
-
+ Typsnitt:
-
+ Justering
+
+
+
+ Storlek på ikoner
+
+
+
+
+ Placeholder string?
+ TextEtikett
+
+
+
+
+ Avaktivera blinkning
+
+
+
+
+ Textområde
+
+
+
+
+ Maximal bredd
+
+
+
+
+ Vänster-/Höger-marginaler:
+
+
+
+
+ Topp-/Botten-marginaler:
+ SpellAction
-
+ Stavningsförslag
-
+ &Lägg till i ordbok
-
+ &Ta bort från ordbok
@@ -1771,7 +1828,6 @@
- This has no good Swedish translation and could be out of context.Verktygsrad för Dock Widgets
@@ -1954,12 +2010,14 @@
-
+
+ Character issue in the source text?Ersätt ... med …
-
+
+ Character issue in source text?Ersätt --- med —
@@ -2168,7 +2226,6 @@
- Is the space after Lock intentional?Lås!
@@ -2240,17 +2297,17 @@
F11
-
+ Rot
-
+ {} ord / {}
-
+ {} ord
@@ -2309,86 +2366,93 @@
Göm automatiskt
-
- outlineBasic
-
-
-
- Öppna Objekt
-
-outlineBasics
-
+ Ny Mapp
-
+ Ny Text
-
+ Ta Bort
-
+ Kopiera
-
+ Klipp Ut
-
+ Klistra In
-
+ Ange Synvinkel
-
+ Ingen
-
+ Huvudkaraktär
-
+ Bikaraktär
-
+ Statist
-
+ Ange Status
-
+ Ange Etikett
-
+ Ny
+
+
+
+ Öppna Objekt
+
+
+
+
+ Välj Anpassad Ikon
+
+
+
+
+ Återställ till standard
+ outlineCharacterDelegate
@@ -2416,47 +2480,47 @@
outlineModel
-
+ Titel
-
+ Synvinkel
-
+ Etikett
-
+ Status
-
+ Kompilera
-
+ Antal ord
-
+ Mål
-
+ {} ord / {} ({})
-
+ {} ord
@@ -2464,17 +2528,17 @@
pandocSettings
-
+ Allmänt
-
+ Innehållsförteckning
-
+ Anpassade inställningar för {}
@@ -2489,7 +2553,6 @@
- Misspelled in EnglishBikaraktär
@@ -2508,7 +2571,6 @@
- Not sure of the context hereLöfte
@@ -2604,7 +2666,6 @@
- Context unclear.3. Stå upp
@@ -2686,37 +2747,37 @@
plotModel
-
+ Ny handling
-
+ Namn
-
+ Meta
-
+ Nytt steg
-
+ Huvudkaraktär
-
+ Bikaraktär
-
+ Statist
@@ -2724,22 +2785,22 @@
plotTreeView
-
+ Huvudkaraktär
-
+ Bikaraktär
-
+ Statist
-
+ **Handling:** {}
@@ -2754,7 +2815,6 @@
- Typo in English textBikaraktär
@@ -3123,28 +3183,27 @@
settingsWindow
-
+ Ny status
-
+ Ny etikett
-
+
- Keeping in English since the formatting looks strange.newtheme
-
+ Nytt tema
-
+ (skrivskyddad)
@@ -3159,7 +3218,6 @@
- Keeping in English since formatting looks strange.TextLabel
@@ -3199,22 +3257,22 @@
tabSplitter
-
+ Öppna valda objekt i den visningen.
-
+ Dela horisontellt
-
+ Stäng delning
-
+ Dela vertikalt
@@ -3230,7 +3288,7 @@
textEditView
-
+ Diverse
@@ -3301,22 +3359,22 @@
Öppna {} i en ny flik
-
+ Expandera {}
-
+ Minimera {}
-
+ Expandera Alla
-
+ Minimera Alla
@@ -3331,7 +3389,6 @@
- Not sure what this is?1
@@ -3365,14 +3422,13 @@
Uppsats
-
+ Demoprojekt
- This functionality seems a bit unclear in both English and SwedishLägg till nivå
@@ -3446,12 +3502,12 @@
Tom skönlitteratur
-
+ Kapitel
-
+ Scen
@@ -3476,72 +3532,72 @@
Tom facklitteratur
-
+ ord vardera.
-
+ av
-
+ Text
-
+ Någonting
-
+ <b>Total:</b> {} ord (~ {} sidor)
-
+ Skönlitteratur
-
+ Facklitteratur
-
+ Idé
-
+ Anteckning
-
+ Referensinformation
-
+ Att Göra
-
+ Första utkast
-
+ Andra utkast
-
+ Slutgiltig
diff --git a/icons/Manuskript/manuskript.svg b/icons/Manuskript/manuskript.svg
new file mode 100644
index 0000000..596b407
--- /dev/null
+++ b/icons/Manuskript/manuskript.svg
@@ -0,0 +1,109 @@
+
+
+
+
diff --git a/icons/NumixMsk/128x128/places/folder-copy.svg b/icons/NumixMsk/128x128/places/folder-copy.svg
new file mode 100644
index 0000000..6474bf4
--- /dev/null
+++ b/icons/NumixMsk/128x128/places/folder-copy.svg
@@ -0,0 +1,157 @@
+
+
+
+
diff --git a/icons/NumixMsk/16x16/actions/document-import.svg b/icons/NumixMsk/16x16/actions/document-import.svg
new file mode 100644
index 0000000..8ddaea3
--- /dev/null
+++ b/icons/NumixMsk/16x16/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/16x16/actions/edit-rename.svg b/icons/NumixMsk/16x16/actions/edit-rename.svg
new file mode 100644
index 0000000..ff35100
--- /dev/null
+++ b/icons/NumixMsk/16x16/actions/edit-rename.svg
@@ -0,0 +1,5 @@
+
diff --git a/icons/NumixMsk/16x16/actions/merge.svg b/icons/NumixMsk/16x16/actions/merge.svg
new file mode 100644
index 0000000..34bf799
--- /dev/null
+++ b/icons/NumixMsk/16x16/actions/merge.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/16x16/actions/split.svg b/icons/NumixMsk/16x16/actions/split.svg
new file mode 100644
index 0000000..e495231
--- /dev/null
+++ b/icons/NumixMsk/16x16/actions/split.svg
@@ -0,0 +1,7 @@
+
diff --git a/icons/NumixMsk/16x16/places/folder-copy.svg b/icons/NumixMsk/16x16/places/folder-copy.svg
new file mode 100644
index 0000000..b4008db
--- /dev/null
+++ b/icons/NumixMsk/16x16/places/folder-copy.svg
@@ -0,0 +1,117 @@
+
+
+
+
diff --git a/icons/NumixMsk/22x22/actions/document-import.svg b/icons/NumixMsk/22x22/actions/document-import.svg
new file mode 100644
index 0000000..a78c48c
--- /dev/null
+++ b/icons/NumixMsk/22x22/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/22x22/actions/edit-rename.svg b/icons/NumixMsk/22x22/actions/edit-rename.svg
new file mode 100644
index 0000000..7742210
--- /dev/null
+++ b/icons/NumixMsk/22x22/actions/edit-rename.svg
@@ -0,0 +1,5 @@
+
diff --git a/icons/NumixMsk/22x22/actions/merge.svg b/icons/NumixMsk/22x22/actions/merge.svg
new file mode 100644
index 0000000..e3996fd
--- /dev/null
+++ b/icons/NumixMsk/22x22/actions/merge.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/22x22/actions/split.svg b/icons/NumixMsk/22x22/actions/split.svg
new file mode 100644
index 0000000..f004015
--- /dev/null
+++ b/icons/NumixMsk/22x22/actions/split.svg
@@ -0,0 +1,7 @@
+
diff --git a/icons/NumixMsk/22x22/places/folder-copy.svg b/icons/NumixMsk/22x22/places/folder-copy.svg
new file mode 100644
index 0000000..09e1ea9
--- /dev/null
+++ b/icons/NumixMsk/22x22/places/folder-copy.svg
@@ -0,0 +1,180 @@
+
+
+
+
diff --git a/icons/NumixMsk/24x24/actions/document-import.svg b/icons/NumixMsk/24x24/actions/document-import.svg
new file mode 100644
index 0000000..77e06c3
--- /dev/null
+++ b/icons/NumixMsk/24x24/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/24x24/actions/edit-rename.svg b/icons/NumixMsk/24x24/actions/edit-rename.svg
new file mode 100644
index 0000000..319f6b4
--- /dev/null
+++ b/icons/NumixMsk/24x24/actions/edit-rename.svg
@@ -0,0 +1,5 @@
+
diff --git a/icons/NumixMsk/24x24/actions/merge.svg b/icons/NumixMsk/24x24/actions/merge.svg
new file mode 100644
index 0000000..96f0ca7
--- /dev/null
+++ b/icons/NumixMsk/24x24/actions/merge.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/24x24/actions/split.svg b/icons/NumixMsk/24x24/actions/split.svg
new file mode 100644
index 0000000..c247872
--- /dev/null
+++ b/icons/NumixMsk/24x24/actions/split.svg
@@ -0,0 +1,7 @@
+
diff --git a/icons/NumixMsk/24x24/places/folder-copy.svg b/icons/NumixMsk/24x24/places/folder-copy.svg
new file mode 100644
index 0000000..d86840b
--- /dev/null
+++ b/icons/NumixMsk/24x24/places/folder-copy.svg
@@ -0,0 +1,180 @@
+
+
+
+
diff --git a/icons/NumixMsk/256x256/places/folder-copy.svg b/icons/NumixMsk/256x256/places/folder-copy.svg
new file mode 100644
index 0000000..483a5d8
--- /dev/null
+++ b/icons/NumixMsk/256x256/places/folder-copy.svg
@@ -0,0 +1,162 @@
+
+
+
+
diff --git a/icons/NumixMsk/32x32/actions/document-import.svg b/icons/NumixMsk/32x32/actions/document-import.svg
new file mode 100644
index 0000000..53d3682
--- /dev/null
+++ b/icons/NumixMsk/32x32/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/32x32/actions/edit-rename.svg b/icons/NumixMsk/32x32/actions/edit-rename.svg
new file mode 100644
index 0000000..e409ff8
--- /dev/null
+++ b/icons/NumixMsk/32x32/actions/edit-rename.svg
@@ -0,0 +1,5 @@
+
diff --git a/icons/NumixMsk/32x32/actions/merge.svg b/icons/NumixMsk/32x32/actions/merge.svg
new file mode 100644
index 0000000..dbd0db4
--- /dev/null
+++ b/icons/NumixMsk/32x32/actions/merge.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/32x32/actions/split.svg b/icons/NumixMsk/32x32/actions/split.svg
new file mode 100644
index 0000000..04d6458
--- /dev/null
+++ b/icons/NumixMsk/32x32/actions/split.svg
@@ -0,0 +1,7 @@
+
diff --git a/icons/NumixMsk/32x32/places/folder-copy.svg b/icons/NumixMsk/32x32/places/folder-copy.svg
new file mode 100644
index 0000000..d9f74d9
--- /dev/null
+++ b/icons/NumixMsk/32x32/places/folder-copy.svg
@@ -0,0 +1,167 @@
+
+
+
+
diff --git a/icons/NumixMsk/48x48/actions/document-import.svg b/icons/NumixMsk/48x48/actions/document-import.svg
new file mode 100644
index 0000000..b2e1682
--- /dev/null
+++ b/icons/NumixMsk/48x48/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/48x48/actions/edit-rename.svg b/icons/NumixMsk/48x48/actions/edit-rename.svg
new file mode 100644
index 0000000..5237dd8
--- /dev/null
+++ b/icons/NumixMsk/48x48/actions/edit-rename.svg
@@ -0,0 +1,5 @@
+
diff --git a/icons/NumixMsk/48x48/actions/merge.svg b/icons/NumixMsk/48x48/actions/merge.svg
new file mode 100644
index 0000000..d8eb78a
--- /dev/null
+++ b/icons/NumixMsk/48x48/actions/merge.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/48x48/actions/split.svg b/icons/NumixMsk/48x48/actions/split.svg
new file mode 100644
index 0000000..6742824
--- /dev/null
+++ b/icons/NumixMsk/48x48/actions/split.svg
@@ -0,0 +1,7 @@
+
diff --git a/icons/NumixMsk/48x48/places/folder-copy.svg b/icons/NumixMsk/48x48/places/folder-copy.svg
new file mode 100644
index 0000000..e943f0c
--- /dev/null
+++ b/icons/NumixMsk/48x48/places/folder-copy.svg
@@ -0,0 +1,352 @@
+
+
+
+
diff --git a/icons/NumixMsk/64x64/actions/document-import.svg b/icons/NumixMsk/64x64/actions/document-import.svg
new file mode 100644
index 0000000..3760178
--- /dev/null
+++ b/icons/NumixMsk/64x64/actions/document-import.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/64x64/actions/edit-rename.svg b/icons/NumixMsk/64x64/actions/edit-rename.svg
new file mode 100644
index 0000000..b6e130b
--- /dev/null
+++ b/icons/NumixMsk/64x64/actions/edit-rename.svg
@@ -0,0 +1,5 @@
+
diff --git a/icons/NumixMsk/64x64/actions/merge.svg b/icons/NumixMsk/64x64/actions/merge.svg
new file mode 100644
index 0000000..c81beca
--- /dev/null
+++ b/icons/NumixMsk/64x64/actions/merge.svg
@@ -0,0 +1,6 @@
+
diff --git a/icons/NumixMsk/64x64/actions/split.svg b/icons/NumixMsk/64x64/actions/split.svg
new file mode 100644
index 0000000..af0d3d0
--- /dev/null
+++ b/icons/NumixMsk/64x64/actions/split.svg
@@ -0,0 +1,7 @@
+
diff --git a/icons/NumixMsk/64x64/places/folder-copy.svg b/icons/NumixMsk/64x64/places/folder-copy.svg
new file mode 100644
index 0000000..31c3ab6
--- /dev/null
+++ b/icons/NumixMsk/64x64/places/folder-copy.svg
@@ -0,0 +1,193 @@
+
+
+
+
diff --git a/icons/NumixMsk/96x96/places/folder-copy.svg b/icons/NumixMsk/96x96/places/folder-copy.svg
new file mode 100644
index 0000000..6fce318
--- /dev/null
+++ b/icons/NumixMsk/96x96/places/folder-copy.svg
@@ -0,0 +1,350 @@
+
+
+
+
diff --git a/manuskript/converters/__init__.py b/manuskript/converters/__init__.py
new file mode 100644
index 0000000..b4c7eb1
--- /dev/null
+++ b/manuskript/converters/__init__.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+"""
+The converters package provide functions to quickly convert on the fly from
+one format to another. It is responsible to check what external library are
+present, and do the job as best as possible with what we have in hand.
+"""
+
+from manuskript.converters.abstractConverter import abstractConverter
+from manuskript.converters.pandocConverter import pandocConverter
+#from manuskript.converters.markdownConverter import markdownConverter
+
+
+def HTML2MD(html):
+
+ # Convert using pandoc
+ if pandocConverter.isValid():
+ return pandocConverter.convert(html, _from="html", to="markdown")
+
+ # Convert to plain text using QTextEdit
+ return HTML2PlainText(html)
+
+
+def HTML2PlainText(html):
+ """
+ Convert from HTML to plain text.
+ """
+
+ if pandocConverter.isValid():
+ return pandocConverter.convert(html, _from="html", to="plain")
+
+ # Last resort: probably resource ineficient
+ e = QTextEdit()
+ e.setHtml(html)
+ return e.toPlainText()
diff --git a/manuskript/converters/abstractConverter.py b/manuskript/converters/abstractConverter.py
new file mode 100644
index 0000000..d38c3af
--- /dev/null
+++ b/manuskript/converters/abstractConverter.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+
+class abstractConverter:
+ """
+ A convertor is used to convert (duh) between stuff. They provide access
+ to external libraries that may or may not be present.
+
+ Now, things are a bit messy, since classes in `exporter` (and `importer` to a lesser extent) do
+ the same. In a better world, classes from `exporter` and `importer` would
+ use convertors to do their stuff. (TODO)
+ """
+ name = ""
+
+ @classmethod
+ def isValid(cls):
+ return False
diff --git a/manuskript/converters/markdownConverter.py b/manuskript/converters/markdownConverter.py
new file mode 100644
index 0000000..f0e577b
--- /dev/null
+++ b/manuskript/converters/markdownConverter.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import os
+import shutil
+import subprocess
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import qApp, QMessageBox
+from PyQt5.QtGui import QCursor
+
+from manuskript.converters import abstractConverter
+from manuskript.functions import mainWindow
+
+try:
+ import markdown as MD
+except ImportError:
+ MD = None
+
+
+class markdownConverter(abstractConverter):
+ """
+ Converter using python module markdown.
+ """
+
+ name = "python module markdown"
+
+ @classmethod
+ def isValid(self):
+ return MD is not None
+
+ @classmethod
+ def convert(self, markdown):
+ if not self.isValid:
+ print("ERROR: markdownConverter is called but not valid.")
+ return ""
+
+ html = MD.markdown(markdown)
+ return html
diff --git a/manuskript/converters/pandocConverter.py b/manuskript/converters/pandocConverter.py
new file mode 100644
index 0000000..ea91e9a
--- /dev/null
+++ b/manuskript/converters/pandocConverter.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import os
+import shutil
+import subprocess
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import qApp, QMessageBox
+from PyQt5.QtGui import QCursor
+
+from manuskript.converters import abstractConverter
+from manuskript.functions import mainWindow
+
+
+class pandocConverter(abstractConverter):
+
+ name = "pandoc"
+ cmd = "pandoc"
+
+ @classmethod
+ def isValid(self):
+ if self.path() != None:
+ return 2
+ elif self.customPath() and os.path.exists(self.customPath):
+ return 1
+ else:
+ return 0
+
+ @classmethod
+ def customPath(self):
+ settings = QSettings()
+ return settings.value("Exporters/{}_customPath".format(self.name), "")
+
+ @classmethod
+ def path(self):
+ return shutil.which(self.cmd)
+
+ @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.")
+ return ""
+
+ cmd = [self.runCmd()]
+
+ cmd += ["--from={}".format(_from)]
+ cmd += ["--to={}".format(to)]
+
+ if args:
+ cmd += args
+
+ if outputfile:
+ cmd.append("--output={}".format(outputfile))
+
+ qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
+
+ p = subprocess.Popen(
+ cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+
+ if not type(src) == bytes:
+ src = src.encode("utf-8") # assumes utf-8
+
+ stdout, stderr = p.communicate(src)
+
+ qApp.restoreOverrideCursor()
+
+ if stderr:
+ err = stderr.decode("utf-8")
+ print(err)
+ QMessageBox.critical(mainWindow().dialog,
+ qApp.translate("Export", "Error"), err)
+ return None
+
+ return stdout.decode("utf-8")
+
+ @classmethod
+ def runCmd(self):
+ if self.isValid() == 2:
+ return self.cmd
+ elif self.isValid() == 1:
+ return self.customPath
diff --git a/manuskript/exporter/__init__.py b/manuskript/exporter/__init__.py
index b8e7df4..c9edb18 100644
--- a/manuskript/exporter/__init__.py
+++ b/manuskript/exporter/__init__.py
@@ -2,13 +2,11 @@
# --!-- coding: utf8 --!--
from manuskript.exporter.manuskript import manuskriptExporter
-from manuskript.exporter.mmd import mmdExporter
from manuskript.exporter.pandoc import pandocExporter
exporters = [
manuskriptExporter(),
- pandocExporter(),
- mmdExporter()
+ pandocExporter()
]
def getExporterByName(name):
@@ -16,4 +14,4 @@ def getExporterByName(name):
if e.name == name:
return e
- return None
\ No newline at end of file
+ return None
diff --git a/manuskript/exporter/basic.py b/manuskript/exporter/basic.py
index 5acbe9e..0cf6100 100644
--- a/manuskript/exporter/basic.py
+++ b/manuskript/exporter/basic.py
@@ -19,6 +19,8 @@ class basicExporter:
cmd = ""
customPath = ""
icon = ""
+ absentTip = "" # A tip displayed when exporter is absent.
+ absentURL = "" # URL to open if exporter is absent.
def __init__(self):
settings = QSettings()
@@ -129,7 +131,7 @@ class basicFormat:
@classmethod
def isValid(cls):
return True
-
+
@classmethod
def projectPath(cls):
return os.path.dirname(os.path.abspath(mainWindow().currentProject))
diff --git a/manuskript/exporter/mmd.py b/manuskript/exporter/mmd.py
deleted file mode 100644
index fbe5bae..0000000
--- a/manuskript/exporter/mmd.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-# --!-- coding: utf8 --!--
-from PyQt5.QtWidgets import qApp
-
-from manuskript.exporter.basic import basicExporter, basicFormat
-
-
-class mmdExporter(basicExporter):
-
- name = "MultiMarkdown"
- description = qApp.translate("Export", """
""")
cmd = "pandoc"
+ absentTip = "Install pandoc to benefit from a wide range of export formats (DocX, ePub, PDF, etc.)"
+ absentURL = "http://pandoc.org/installing.html"
def __init__(self):
basicExporter.__init__(self)
@@ -40,7 +42,7 @@ class pandocExporter(basicExporter):
def version(self):
if self.isValid():
- r = self.run(["-v"])
+ r = self.run(["--version"])
return r.split("\n")[0]
else:
return ""
diff --git a/manuskript/functions.py b/manuskript/functions.py
index b7873ad..0a94e82 100644
--- a/manuskript/functions.py
+++ b/manuskript/functions.py
@@ -6,7 +6,9 @@ import re
from random import *
from PyQt5.QtCore import Qt, QRect, QStandardPaths, QObject, QRegExp, QDir
+from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QBrush, QIcon, QPainter, QColor, QImage, QPixmap
+from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import qApp, QTextEdit
from manuskript.enums import Outline
@@ -24,9 +26,12 @@ def wordCount(text):
def toInt(text):
if text:
- return int(text)
- else:
- return 0
+ try:
+ return int(text)
+ except ValueError:
+ pass
+
+ return 0
def toFloat(text):
@@ -44,8 +49,9 @@ def toString(text):
def drawProgress(painter, rect, progress, radius=0):
+ from manuskript.ui import style as S
painter.setPen(Qt.NoPen)
- painter.setBrush(QColor("#dddddd"))
+ painter.setBrush(QColor(S.base)) # "#dddddd"
painter.drawRoundedRect(rect, radius, radius)
painter.setBrush(QBrush(colorFromProgress(progress)))
@@ -142,14 +148,24 @@ def randomColor(mix=None):
def mixColors(col1, col2, f=.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
- return QColor(r, g, b)
+
+ return QColor(r, g, b) if not fromString else QColor(r, g, b).name()
def outlineItemColors(item):
+
+ from manuskript.ui import style as S
+
"""Takes an OutlineItem and returns a dict of colors."""
colors = {}
mw = mainWindow()
@@ -181,9 +197,9 @@ def outlineItemColors(item):
# Compile
if item.compile() in [0, "0"]:
- colors["Compile"] = QColor(Qt.gray)
+ colors["Compile"] = mixColors(QColor(S.text), QColor(S.window))
else:
- colors["Compile"] = QColor(Qt.black)
+ colors["Compile"] = QColor(Qt.transparent) # will use default
return colors
@@ -229,14 +245,6 @@ def tempFile(name):
return os.path.join(QDir.tempPath(), name)
-def lightBlue():
- """
- A light blue used in several places in manuskript.
- @return: QColor
- """
- return QColor(Qt.blue).lighter(190)
-
-
def totalObjects():
return len(mainWindow().findChildren(QObject))
@@ -273,17 +281,6 @@ def findFirstFile(regex, path="resources"):
if re.match(regex, l):
return os.path.join(p, l)
-
-def HTML2PlainText(html):
- """
- Ressource-inefficient way to convert HTML to plain text.
- @param html:
- @return:
- """
- e = QTextEdit()
- e.setHtml(html)
- return e.toPlainText()
-
def customIcons():
"""
Returns a list of possible customIcons. String from theme.
@@ -355,3 +352,11 @@ def customIcons():
]
return sorted(r)
+
+
+def statusMessage(message, duration=5000):
+ mainWindow().statusBar().showMessage(message, duration)
+
+
+def openURL(url):
+ QDesktopServices.openUrl(QUrl(url))
diff --git a/manuskript/importer/__init__.py b/manuskript/importer/__init__.py
new file mode 100644
index 0000000..16e4b7e
--- /dev/null
+++ b/manuskript/importer/__init__.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+from manuskript.importer.folderImporter import folderImporter
+from manuskript.importer.markdownImporter import markdownImporter
+from manuskript.importer.opmlImporter import opmlImporter
+from manuskript.importer.mindMapImporter import mindMapImporter
+from manuskript.importer.pandocImporters import markdownPandocImporter, \
+ odtPandocImporter, ePubPandocImporter, docXPandocImporter, HTMLPandocImporter, \
+ rstPandocImporter, LaTeXPandocImporter, OPMLPandocImporter
+
+importers = [
+ # Internal
+ markdownImporter,
+ folderImporter,
+ opmlImporter,
+ mindMapImporter,
+
+ # Pandoc
+ markdownPandocImporter,
+ odtPandocImporter,
+ ePubPandocImporter,
+ docXPandocImporter,
+ HTMLPandocImporter,
+ rstPandocImporter,
+ LaTeXPandocImporter,
+ OPMLPandocImporter,
+ ]
diff --git a/manuskript/importer/abstractImporter.py b/manuskript/importer/abstractImporter.py
new file mode 100644
index 0000000..ba810af
--- /dev/null
+++ b/manuskript/importer/abstractImporter.py
@@ -0,0 +1,210 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import os
+import shutil
+import subprocess
+
+from PyQt5.QtCore import QSettings
+from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHBoxLayout, \
+ QLabel, QSpinBox, QComboBox, QLineEdit
+from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
+from manuskript.ui import style
+
+
+class abstractImporter:
+ """
+ abstractImporter is used to import documents into manuskript.
+
+ The startImport function must be subclassed. It takes a filePath (str to
+ the document to import), and must return `outlineItem`s.
+ """
+
+ name = ""
+ description = ""
+ fileFormat = "" # File format accepted. For example: "OPML Files (*.opml)"
+ # For folder, use "<>"
+ icon = ""
+ engine = "Internal"
+
+ def __init__(self):
+ self.settingsList = [] # Keep the name of the settings in order
+ self.settings = {}
+
+ def startImport(self, filePath, parentItem, settingsWidget):
+ """
+ Takes a str path to the file/folder to import, and the settingsWidget
+ returnend by `self.settingsWidget()` containing the user set settings,
+ and return `outlineItem`s.
+ """
+ pass
+
+ @classmethod
+ def isValid(cls):
+ return False
+
+
+ def settingsWidget(self, widget):
+ """
+ Takes a QWidget that can be modified and must be returned.
+ """
+ return widget
+
+ def addPage(self, widget, title):
+ """
+ Convenience function to add a page to the settingsWidget `widget`, at
+ the end.
+
+ Returns the page widget.
+ """
+ w = QWidget(widget)
+ w.setLayout(QVBoxLayout())
+ widget.toolBox.insertItem(widget.toolBox.count(), w, title)
+ widget.toolBox.layout().setSpacing(0)
+ return w
+
+ def addGroup(self, parent, title):
+ """
+ Adds a collapsible group to the given widget.
+ """
+ g = collapsibleGroupBox2(title=title)
+ parent.layout().addWidget(g)
+ g.setLayout(QVBoxLayout())
+ return g
+
+ def addSetting(self, name, type, label, widget=None, default=None,
+ tooltip=None, min=None, max=None, vals=None, suffix=""):
+
+ self.settingsList.append(name)
+ self.settings[name] = self.setting(name, type, label, widget, default,
+ tooltip, min, max, vals, suffix)
+
+ def widget(self, name):
+ if name in self.settings:
+ return self.settings[name].widget()
+
+ def getSetting(self, name):
+ if name in self.settings:
+ return self.settings[name]
+
+ def addSettingsTo(self, widget):
+ """
+ Adds all the settings to the given widget. Assume that the settings
+ have not been called yet, so calling `.widget()` will create their
+ widgets.
+ """
+ for name in self.settingsList:
+ self.settings[name].widget(widget)
+
+
+ class setting:
+ """
+ A class used to store setting, and display a widget for the user to
+ modify it.
+ """
+ def __init__(self, name, type, label, widget=None, default=None,
+ tooltip=None, min=None, max=None, vals=None, suffix=""):
+ self.name = name
+ self.type = type
+ self.label = label
+ self._widget = widget
+ self.default = default
+ self.min = min
+ self.max = max
+ self.vals = vals.split("|") if vals else []
+ self.suffix = suffix
+ self.tooltip = tooltip
+
+ def widget(self, parent=None):
+ """
+ Returns the widget used, or creates it if not done yet. If parent
+ is given, widget is inserted in parent's layout.
+ """
+ if self._widget:
+ return self._widget
+
+ else:
+
+ if "checkbox" in self.type:
+ self._widget = QCheckBox(self.label)
+ if self.default:
+ self._widget.setChecked(True)
+ if parent:
+ parent.layout().addWidget(self._widget)
+
+ elif "number" in self.type:
+ l = QHBoxLayout()
+ label = QLabel(self.label, parent)
+ label.setWordWrap(True)
+ l.addWidget(label, 8)
+ self._widget = QSpinBox()
+ self._widget.setValue(self.default if self.default else 0)
+ if self.min:
+ self._widget.setMinimum(self.min)
+ if self.max:
+ self._widget.setMaximum(self.max)
+ if self.suffix:
+ self._widget.setSuffix(self.suffix)
+ l.addWidget(self._widget, 2)
+ if parent:
+ parent.layout().addLayout(l)
+
+ elif "combo" in self.type:
+ l = QHBoxLayout()
+ label = QLabel(self.label, parent)
+ label.setWordWrap(True)
+ l.addWidget(label, 6)
+ self._widget = QComboBox()
+ self._widget.addItems(self.vals)
+ if self.default:
+ self._widget.setCurrentText(self.default)
+ l.addWidget(self._widget, 2)
+ if parent:
+ parent.layout().addLayout(l)
+
+ elif "text" in self.type:
+ l = QHBoxLayout()
+ label = QLabel(self.label, parent)
+ label.setWordWrap(True)
+ l.addWidget(label, 5)
+ self._widget = QLineEdit()
+ self._widget.setStyleSheet(style.lineEditSS())
+ if self.default:
+ self._widget.setText(self.default)
+ l.addWidget(self._widget, 3)
+ if parent:
+ parent.layout().addLayout(l)
+
+ elif "label" in self.type:
+ self._widget = QLabel(self.label, parent)
+ self._widget.setWordWrap(True)
+ if parent:
+ parent.layout().addWidget(self._widget)
+
+ if self.tooltip:
+ self._widget.setToolTip(self.tooltip)
+
+ return self._widget
+
+ def value(self):
+ """
+ Return the value contained in the widget.
+ """
+ if not self._widget:
+ return self.default
+
+ else:
+
+ if "checkbox" in self.type:
+ return self._widget.isChecked()
+
+ elif "number" in self.type:
+ return self._widget.value()
+
+ elif "combo" in self.type:
+ return self._widget.currentText()
+
+ elif "text" in self.type:
+ return self._widget.text()
+
+
+
diff --git a/manuskript/importer/folderImporter.py b/manuskript/importer/folderImporter.py
new file mode 100644
index 0000000..6789dc2
--- /dev/null
+++ b/manuskript/importer/folderImporter.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+import os
+from manuskript.importer.abstractImporter import abstractImporter
+from manuskript.models.outlineModel import outlineItem
+from manuskript.enums import Outline
+from PyQt5.QtWidgets import qApp
+
+
+class folderImporter(abstractImporter):
+
+ name = "Folder"
+ description = ""
+ fileFormat = "<>"
+ icon = "folder"
+
+ @classmethod
+ def isValid(cls):
+ return True
+
+ def startImport(self, filePath, parentItem, settingsWidget, fromString=None):
+ """
+ Imports from a folder.
+ """
+ ext = self.getSetting("ext").value()
+ ext = [e.strip().replace("*", "").lower() for e in ext.split(",")]
+
+ sorting = self.getSetting("sortItems").value()
+
+ items = []
+ stack = {}
+
+ for dirpath, dirnames, filenames in os.walk(filePath):
+
+ if dirpath in stack:
+ item = stack[dirpath]
+ else:
+ # It's the parent folder, and we are not including it
+ # so every item is attached to parentItem
+ item = parentItem
+
+ def addFile(f):
+ fName, fExt = os.path.splitext(f)
+ if fExt.lower() in ext:
+ try:
+ with open(os.path.join(dirpath, f), "r") as fr:
+ content = fr.read()
+ child = outlineItem(title=fName, _type="md", parent=item)
+ child._data[Outline.text] = content
+ items.append(child)
+ except UnicodeDecodeError:
+ # Probably not a text file
+ pass
+
+ def addFolder(d):
+ child = outlineItem(title=d, parent=item)
+ items.append(child)
+ stack[os.path.join(dirpath, d)] = child
+
+ if not self.getSetting("separateFolderFiles").value():
+ # Import folder and files together (only makes differences if
+ # they are sorted, really)
+ allFiles = dirnames + filenames
+ if sorting:
+ allFiles = sorted(allFiles)
+
+ for f in allFiles:
+ if f in dirnames:
+ addFolder(f)
+ else:
+ addFile(f)
+
+ else:
+ # Import first folders, then files
+ if sorting:
+ dirnames = sorted(dirnames)
+ filenames = sorted(filenames)
+
+ # Import folders
+ for d in dirnames:
+ addFolder(d)
+
+ # Import files
+ for f in filenames:
+ addFile(f)
+
+ return items
+
+ def settingsWidget(self, widget):
+ """
+ Takes a QWidget that can be modified and must be returned.
+ """
+
+ # Add group
+ group = self.addGroup(widget.toolBox.widget(0),
+ qApp.translate("Import", "Folder import"))
+ #group = cls.addPage(widget, "Folder import")
+
+ self.addSetting("info", "label",
+ qApp.translate("Import", """
Info: Imports a whole
+ directory structure. Folders are added as folders, and
+ plaintext documents within (you chose which ones by extension)
+ are added as scene.
+
Only text files are supported (not images, binary or others).
"""))
+
+ self.addSetting("ext", "text",
+ qApp.translate("Import", "Include only those extensions:"),
+ default="*.txt, *.md",
+ tooltip=qApp.translate("Import", "Coma separated values")),
+
+ self.addSetting("sortItems", "checkbox",
+ qApp.translate("Import", "Sort items by name"),
+ default=True),
+
+ self.addSetting("separateFolderFiles", "checkbox",
+ qApp.translate("Import", "Import folder then files"),
+ default=True),
+
+ self.addSettingsTo(group)
+
+ return widget
+
+
+
+
diff --git a/manuskript/importer/markdownImporter.py b/manuskript/importer/markdownImporter.py
new file mode 100644
index 0000000..a57b6be
--- /dev/null
+++ b/manuskript/importer/markdownImporter.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+from manuskript.importer.abstractImporter import abstractImporter
+from manuskript.models.outlineModel import outlineItem
+from manuskript.enums import Outline
+from PyQt5.QtWidgets import qApp
+import re, os
+
+
+class markdownImporter(abstractImporter):
+
+ name = "Markdown"
+ description = ""
+ fileFormat = "Markdown files (*.md *.txt *)"
+ icon = "text-x-markdown"
+
+ @classmethod
+ def isValid(cls):
+ return True
+
+ def startImport(self, filePath, parentItem, settingsWidget, fromString=None):
+ """
+ Very simple import from markdown. We just look at ATX headers (we
+ ignore setext for the sake of simplicity, for now.)
+
+ **A difficulty:** in the following example, we can do things with
+ markdown headers (like go from level 1 to level 4 and back to level 2)
+ that we cannot do in an outline.
+
+ ```
+ # Level 1
+ # Level 1
+ ## Level 2
+ ### Level 3
+ #### Level 4
+ ##### Level 5
+ ### Level 3
+ # Level 1
+ #### Level 4? → Level 2
+ ### Level 3? → Level 2
+ ## Level 2 → Level 2
+ #### Level 4? → Level 3
+ ```
+
+ I think the current version of the imported manages that quite well.
+
+ **A question:** In the following sample, the first Level 1 becomes a
+ text element, because it has no other sub elements. But the content of
+ second Level 1 becomes a text element, with no name. What name should
+ we give it?
+
+ ```
+ # Level 1
+ Some texte content.
+ Level 1 will become a text element.
+ # Level 1
+ This content has no name.
+ ## Level 2
+ ...
+ ```
+ """
+
+ if not fromString:
+ # Read file
+ with open(filePath, "r") as f:
+ txt = f.read()
+ else:
+ txt = fromString
+
+ items = []
+
+ parent = parentItem
+ lastLevel = 0
+ content = ""
+
+ def saveContent(content, parent):
+ if content.strip():
+ child = outlineItem(title=parent.title(), parent=parent, _type="md")
+ child._data[Outline.text] = content
+ items.append(child)
+ return ""
+
+ def addTitle(name, parent, level):
+ child = outlineItem(title=name, parent=parent)
+ child.__miLevel = level
+ items.append(child)
+ return child
+
+ ATXHeader = re.compile(r"(\#+)\s*(.+?)\s*\#*$")
+ setextHeader1 = re.compile(r"([^\#-=].+)\n(===+)$", re.MULTILINE)
+ setextHeader2 = re.compile(r"([^\#-=].+)\n(---+)$", re.MULTILINE)
+
+ # We store the level of each item in a temporary var
+ parent.__miLevel = 0 # markdown importer header level
+
+ txt = txt.split("\n")
+ skipNextLine = False
+ for i in range(len(txt)):
+
+ l = txt[i]
+ l2 = "\n".join(txt[i:i+2])
+
+ header = False
+
+ if skipNextLine:
+ # Last line was a setext-style header.
+ skipNextLine = False
+ continue
+
+ # Check ATX Header
+ m = ATXHeader.match(l)
+ if m:
+ header = True
+ level = len(m.group(1))
+ name = m.group(2)
+
+ # Check setext header
+ m = setextHeader1.match(l2)
+
+ if not header and m and len(m.group(1)) == len(m.group(2)):
+ header = True
+ level = 1
+ name = m.group(1)
+ skipNextLine = True
+
+ m = setextHeader2.match(l2)
+ if not header and m and len(m.group(1)) == len(m.group(2)):
+ header = True
+ level = 2
+ name = m.group(1)
+ skipNextLine = True
+
+ if header:
+
+ # save content
+ content = saveContent(content, parent)
+
+ # get parent level
+ while parent.__miLevel >= level:
+ parent = parent.parent()
+
+ # create title
+ child = addTitle(name, parent, level)
+ child.__miLevel = level
+
+ # title becomes the new parent
+ parent = child
+
+ lastLevel = level
+
+ else:
+ content += l + "\n"
+
+ saveContent(content, parent)
+
+ # Clean up
+ for i in items:
+ if i.childCount() == 1 and i.children()[0].isText():
+ # We have a folder with only one text item
+ # So we make it a text item
+ i._data[Outline.type] = "md"
+ i._data[Outline.text] = i.children()[0].text()
+ c = i.removeChild(0)
+ items.remove(c)
+
+ return items
+
+ def settingsWidget(self, widget):
+ """
+ Takes a QWidget that can be modified and must be returned.
+ """
+
+ # Add group
+ group = self.addGroup(widget.toolBox.widget(0),
+ qApp.translate("Import", "Markdown import"))
+ #group = cls.addPage(widget, "Folder import")
+
+ self.addSetting("info", "label",
+ qApp.translate("Import", """Info: A very simple
+ parser that will go through a markdown document and
+ create items for each titles. """))
+
+ for s in self.settings:
+ self.settings[s].widget(group)
+
+ return widget
diff --git a/manuskript/importer/mindMapImporter.py b/manuskript/importer/mindMapImporter.py
new file mode 100644
index 0000000..6d377ca
--- /dev/null
+++ b/manuskript/importer/mindMapImporter.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+from PyQt5.QtWidgets import qApp, QMessageBox
+from manuskript.models.outlineModel import outlineItem
+from manuskript.enums import Outline
+from lxml import etree as ET
+from manuskript.functions import mainWindow
+from manuskript.importer.abstractImporter import abstractImporter
+from manuskript.converters import HTML2MD, HTML2PlainText
+
+class mindMapImporter(abstractImporter):
+
+ name = "Mind Map"
+ description = ""
+ fileFormat = "Mind map Files (*.mm)"
+ icon = "text-x-opml+xml"
+
+ @classmethod
+ def isValid(cls):
+ return True
+
+ def startImport(self, filePath, parentItem, settingsWidget, fromString=None):
+ """
+ Import/export outline cards in mind map free plane.
+ """
+ ret = False
+
+ if filePath != "":
+ # We have a filePath, so we read the file
+ try:
+ with open(filePath, 'rb') as f:
+ content = f.read()
+ except:
+ return None
+
+ elif fromString == "":
+ # We have neither filePath nor fromString, so we leave
+ return None
+
+ else:
+ # We load from string
+ content = bytes(fromString, "utf-8")
+
+ root = ET.fromstring(content)
+
+ node = root.find("node")
+ items = []
+
+ if node is not 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."))
+
+ return None
+
+ return items
+
+ def settingsWidget(self, widget):
+ """
+ Takes a QWidget that can be modified and must be returned.
+ """
+
+ # Add group
+ group = self.addGroup(widget.toolBox.widget(0),
+ qApp.translate("Import", "Mind Map import"))
+
+ self.addSetting("importTipAs", "combo",
+ qApp.translate("Import", "Import tip as:"),
+ vals="Text|Folder",
+ )
+
+ for s in self.settings:
+ self.settings[s].widget(group)
+
+ return widget
+
+ def parseItems(self, underElement, parentItem=None):
+ items = []
+
+ # Title
+ title = underElement.get('TEXT', "").replace("\n", " ")
+ if not title:
+ title = qApp.translate("Import", "Untitled")
+
+ item = outlineItem(parent=parentItem, title=title)
+ items.append(item)
+
+ # URL
+ url = underElement.get('LINK', None)
+
+ # Rich text content
+ content = ""
+ content = underElement.find("richcontent")
+ if content is not None:
+ # In Freemind, can be note or node
+ # Note: it's a note
+ # Node: it's the title of the node, in rich text
+ content_type = content.get("TYPE", "NOTE")
+ content = ET.tostring(content.find("html"))
+
+ if content and content_type == "NODE":
+ # Content is title
+ # convert rich text title (in html) to plain text
+ title = HTML2PlainText(content) #.replace("\n", " ").strip()
+ # Count the number of lines
+ lines = [l.strip() for l in title.split("\n") if l.strip()]
+
+ # If there is one line, we use it as title.
+ # Otherwise we leave it to be inserted as a note.
+ if len(lines) == 1:
+ item.setData(Outline.title.value, "".join(lines))
+ content = ""
+
+ if content:
+ # Set the note content as text value
+ content = HTML2MD(content)
+ item.setData(Outline.notes.value, content)
+
+ if url:
+ # Set the url in notes
+ item.setData(Outline.notes.value,
+ item.data(Outline.notes.value) + "\n\n" + url)
+
+ children = underElement.findall('node')
+
+ # Process children
+ if children is not None and len(children) > 0:
+ for c in children:
+ items.extend(self.parseItems(c, item))
+
+ # Process if no children
+ elif self.getSetting("importTipAs").value() == "Text":
+ # Transform item to text
+ item.setData(Outline.type.value, 'md')
+ # Move notes to text
+ if item.data(Outline.notes.value):
+ item.setData(Outline.text.value, item.data(Outline.notes.value))
+ item.setData(Outline.notes.value, "")
+
+ return items
diff --git a/manuskript/importer/opmlImporter.py b/manuskript/importer/opmlImporter.py
new file mode 100644
index 0000000..b7b8b99
--- /dev/null
+++ b/manuskript/importer/opmlImporter.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+from PyQt5.QtWidgets import qApp, QMessageBox
+from manuskript.models.outlineModel import outlineItem
+from manuskript.enums import Outline
+from lxml import etree as ET
+from manuskript.functions import mainWindow
+from manuskript.importer.abstractImporter import abstractImporter
+
+class opmlImporter(abstractImporter):
+
+ name = "OPML"
+ description = ""
+ fileFormat = "OPML Files (*.opml *.xml)"
+ icon = "text-x-opml+xml"
+
+ @classmethod
+ def isValid(cls):
+ return True
+
+ @classmethod
+ def startImport(cls, filePath, parentItem, settingsWidget, fromString=None):
+ """
+ Import/export outline cards in OPML format.
+ """
+ ret = False
+
+ if filePath != "":
+ # We have a filePath, so we read the file
+ try:
+ with open(filePath, 'rb') as opmlFile:
+ #opmlContent = cls.saveNewlines(opmlFile.read())
+ opmlContent = opmlFile.read()
+ except:
+ QMessageBox.critical(settingsWidget,
+ qApp.translate("Import", "OPML Import"),
+ qApp.translate("Import", "File open failed."))
+ return None
+
+ elif fromString == "":
+ # We have neither filePath nor fromString, so we leave
+ return None
+
+ else:
+ # We load from string
+ opmlContent = bytes(fromString, "utf-8")
+
+ parsed = ET.fromstring(opmlContent)
+
+ opmlNode = parsed
+ bodyNode = opmlNode.find("body")
+ items = []
+
+ if bodyNode is not None:
+ outlineEls = bodyNode.findall("outline")
+
+ if outlineEls is not None:
+ for element in outlineEls:
+ items.extend(cls.parseItems(element, parentItem))
+ ret = True
+
+ if not ret:
+ QMessageBox.critical(
+ settingsWidget,
+ qApp.translate("Import", "OPML Import"),
+ qApp.translate("Import", "This does not appear to be a valid OPML file."))
+
+ return None
+
+ return items
+
+ @classmethod
+ def parseItems(cls, underElement, parentItem=None):
+ items = []
+ title = underElement.get('text')
+ if title is not 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):
+ #body = cls.restoreNewLines(note)
+ body = note
+
+ children = underElement.findall('outline')
+ if children is not None and len(children) > 0:
+ for el in children:
+ items.extend(cls.parseItems(el, card))
+ else:
+ card.setData(Outline.type.value, 'md')
+ card.setData(Outline.text.value, body)
+
+ return items
+
+ @classmethod
+ def saveNewlines(cls, inString):
+ """
+ Since XML parsers are notorious for stripping out significant newlines,
+ save them in a form we can restore after the parse.
+ """
+ inString = inString.replace("\r\n", "\n")
+ inString = inString.replace("\n", "{{lf}}")
+
+ return inString
+
+ @classmethod
+ def restoreNewLines(cls, inString):
+ """
+ Restore any significant newlines
+ """
+ return inString.replace("{{lf}}", "\n")
+
+ @classmethod
+ def isWhitespaceOnly(cls, inString):
+ """
+ Determine whether or not a string only contains whitespace.
+ """
+ s = cls.restoreNewLines(inString)
+ s = ''.join(s.split())
+
+ return len(s) is 0
diff --git a/manuskript/importer/pandocImporters.py b/manuskript/importer/pandocImporters.py
new file mode 100644
index 0000000..023a4b0
--- /dev/null
+++ b/manuskript/importer/pandocImporters.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+
+from manuskript.importer.abstractImporter import abstractImporter
+from manuskript.exporter.pandoc import pandocExporter
+from manuskript.importer.opmlImporter import opmlImporter
+from manuskript.importer.markdownImporter import markdownImporter
+from PyQt5.QtWidgets import qApp
+
+
+class pandocImporter(abstractImporter):
+
+ formatFrom = ""
+ engine = "Pandoc"
+ extraArgs = []
+
+ @classmethod
+ def isValid(cls):
+ return pandocExporter().isValid()
+
+ def startImport(self, filePath, parentItem, settingsWidget):
+
+ formatTo = self.getSetting("formatTo").value().lower()
+ wrap = self.getSetting("wrap").value().lower()
+
+ # pandoc --from=markdown filename --to=opml --standalone
+ args = [
+ "--from={}".format(self.formatFrom),
+ filePath,
+ "--to={}".format(formatTo),
+ "--wrap={}".format(wrap),
+ ]
+
+ if formatTo == "opml":
+ args.append("--standalone")
+
+ args += self.extraArgs
+
+ r = pandocExporter().run(args)
+
+ if formatTo == "opml":
+ return self.opmlImporter.startImport("", parentItem,
+ settingsWidget, fromString=r)
+ elif formatTo == "markdown":
+ return self.mdImporter.startImport(filePath, parentItem,
+ settingsWidget, fromString=r)
+
+ def settingsWidget(self, widget):
+ """
+ Takes a QWidget that can be modified and must be returned.
+ """
+
+ # Add group
+ group = self.addGroup(widget.toolBox.widget(0),
+ qApp.translate("Import", "Pandoc import"))
+
+ self.addSetting("info", "label",
+ qApp.translate("Import", """Info: Manuskript can
+ import from markdown or OPML. Pandoc will
+ convert your document to either (see option below), and
+ then it will be imported in manuskript. One or the other
+ might give better result depending on your document.
+ """))
+
+ self.addSetting("formatTo", "combo",
+ qApp.translate("Import", "Import using:"),
+ vals="markdown|OPML")
+
+ self.addSetting("wrap", "combo",
+ qApp.translate("Import", "Wrap lines:"),
+ vals="auto|none|preserve",
+ default="none",
+ tooltip=qApp.translate("Import", """
Should pandoc create
+ cosmetic / non-semantic line-breaks?
+ auto: wraps at 72 characters.
+ none: no line wrap.
+ preserve: tries to preserves line wrap from the
+ original document.
"""))
+
+ for s in self.settings:
+ self.settings[s].widget(group)
+
+ self.mdImporter = markdownImporter()
+ widget = self.mdImporter.settingsWidget(widget)
+ self.opmlImporter = opmlImporter()
+ widget = self.opmlImporter.settingsWidget(widget)
+
+ return widget
+
+
+class markdownPandocImporter(pandocImporter):
+
+ name = "Markdown"
+ description = "Markdown, using pandoc"
+ fileFormat = "Markdown files (*.md *.txt *)"
+ icon = "text-x-markdown"
+ formatFrom = "markdown"
+
+class ePubPandocImporter(pandocImporter):
+
+ name = "ePub"
+ description = ""
+ fileFormat = "ePub files (*.epub)"
+ icon = "application-epub+zip"
+ formatFrom = "epub"
+
+class docXPandocImporter(pandocImporter):
+
+ name = "DocX"
+ description = ""
+ fileFormat = "DocX files (*.docx)"
+ icon = "application-vnd.openxmlformats-officedocument.wordprocessingml.document"
+ formatFrom = "docx"
+
+class odtPandocImporter(pandocImporter):
+
+ name = "ODT"
+ description = ""
+ fileFormat = "Open Document files (*.odt)"
+ icon = "application-vnd.oasis.opendocument.text"
+ formatFrom = "odt"
+
+class rstPandocImporter(pandocImporter):
+
+ name = "reStructuredText"
+ description = ""
+ fileFormat = "reStructuredText files (*.rst)"
+ icon = "text-plain"
+ formatFrom = "rst"
+
+class HTMLPandocImporter(pandocImporter):
+
+ name = "HTML"
+ description = ""
+ fileFormat = "HTML files (*.htm *.html)"
+ icon = "text-html"
+ formatFrom = "html"
+
+class LaTeXPandocImporter(pandocImporter):
+
+ name = "LaTeX"
+ description = ""
+ fileFormat = "LaTeX files (*.tex)"
+ icon = "text-x-tex"
+ formatFrom = "latex"
+
+class OPMLPandocImporter(pandocImporter):
+
+ name = "OPML"
+ description = ""
+ fileFormat = "OPML files (*.opml *.xml)"
+ icon = "text-x-opml+xml"
+ formatFrom = "opml"
+
+
+
diff --git a/manuskript/load_save/version_1.py b/manuskript/load_save/version_1.py
index be1eded..6015a3e 100644
--- a/manuskript/load_save/version_1.py
+++ b/manuskript/load_save/version_1.py
@@ -18,7 +18,8 @@ from PyQt5.QtGui import QColor, QStandardItem
from manuskript import settings
from manuskript.enums import Character, World, Plot, PlotStep, Outline
-from manuskript.functions import mainWindow, iconColor, iconFromColorString, HTML2PlainText
+from manuskript.functions import mainWindow, iconColor, iconFromColorString
+from manuskript.converters import HTML2PlainText
from lxml import etree as ET
from manuskript.load_save.version_0 import loadFilesFromZip
diff --git a/manuskript/mainWindow.py b/manuskript/mainWindow.py
index b47892e..a2f68b5 100644
--- a/manuskript/mainWindow.py
+++ b/manuskript/mainWindow.py
@@ -21,6 +21,7 @@ from manuskript.settingsWindow import settingsWindow
from manuskript.ui import style
from manuskript.ui.about import aboutDialog
from manuskript.ui.collapsibleDockWidgets import collapsibleDockWidgets
+from manuskript.ui.importers.importer import importerDialog
from manuskript.ui.exporters.exporter import exporterDialog
from manuskript.ui.helpLabel import helpLabel
from manuskript.ui.mainWindow import Ui_MainWindow
@@ -52,7 +53,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
+
+ # Var
self.currentProject = None
+ self._lastFocus = None
self.readSettings()
@@ -97,22 +101,35 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Main Menu
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuView, self.menuTools, self.menuHelp,
- self.actCompile, self.actSettings]:
+ self.actImport, self.actCompile, self.actSettings]:
i.setEnabled(False)
+ # Main Menu:: File
self.actOpen.triggered.connect(self.welcome.openFile)
self.actSave.triggered.connect(self.saveDatas)
self.actSaveAs.triggered.connect(self.welcome.saveAsFile)
+ self.actImport.triggered.connect(self.doImport)
self.actCompile.triggered.connect(self.doCompile)
self.actLabels.triggered.connect(self.settingsLabel)
self.actStatus.triggered.connect(self.settingsStatus)
self.actSettings.triggered.connect(self.settingsWindow)
self.actCloseProject.triggered.connect(self.closeProject)
self.actQuit.triggered.connect(self.close)
- self.actToolFrequency.triggered.connect(self.frequencyAnalyzer)
- self.actAbout.triggered.connect(self.about)
- self.generateViewMenu()
+ # Main menu:: Documents
+ self.actCopy.triggered.connect(self.documentsCopy)
+ self.actCut.triggered.connect(self.documentsCut)
+ self.actPaste.triggered.connect(self.documentsPaste)
+ self.actDuplicate.triggered.connect(self.documentsDuplicate)
+ self.actDelete.triggered.connect(self.documentsDelete)
+ self.actMoveUp.triggered.connect(self.documentsMoveUp)
+ self.actMoveDown.triggered.connect(self.documentsMoveDown)
+ self.actSplitDialog.triggered.connect(self.documentsSplitDialog)
+ self.actSplitCursor.triggered.connect(self.documentsSplitCursor)
+ self.actMerge.triggered.connect(self.documentsMerge)
+
+ # Main Menu:: view
+ self.generateViewMenu()
self.actModeGroup = QActionGroup(self)
self.actModeSimple.setActionGroup(self.actModeGroup)
self.actModeFiction.setActionGroup(self.actModeGroup)
@@ -121,6 +138,10 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.actModeFiction.triggered.connect(self.setViewModeFiction)
self.actModeSnowflake.setEnabled(False)
+ # Main Menu:: Tool
+ self.actToolFrequency.triggered.connect(self.frequencyAnalyzer)
+ self.actAbout.triggered.connect(self.about)
+
self.makeUIConnections()
# self.loadProject(os.path.join(appPath(), "test_project.zip"))
@@ -180,6 +201,33 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.toolbar.setVisible(True)
self.stack.setCurrentIndex(1)
+ ###############################################################################
+ # GENERAL / UI STUFF
+ ###############################################################################
+
+ def tabMainChanged(self):
+ "Called when main tab changes."
+ self.menuDocuments.menuAction().setVisible(self.tabMain.currentIndex() == self.TabRedac)
+
+ def focusChanged(self, old, new):
+ """
+ We get notified by qApp when focus changes, from old to new widget.
+ """
+
+ # Determine which view had focus last, to send the keyboard shortcuts
+ # to the right place
+
+ targets = [
+ self.treeRedacOutline,
+ self.mainEditor
+ ]
+
+ while new is not None:
+ if new in targets:
+ self._lastFocus = new
+ break
+ new = new.parent()
+
###############################################################################
# SUMMARY
###############################################################################
@@ -381,6 +429,56 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def openIndex(self, index):
self.treeRedacOutline.setCurrentIndex(index)
+ def openIndexes(self, indexes, newTab=True):
+ self.mainEditor.openIndexes(indexes, newTab=True)
+
+ # Menu Documents #############################################################
+
+ # Functions called by the menu Documents
+ # self._lastFocus is the last editor that had focus (either treeView or
+ # mainEditor). So we just pass along the signal.
+
+ def documentsCopy(self):
+ "Copy selected item(s)."
+ if self._lastFocus: self._lastFocus.copy()
+ def documentsCut(self):
+ "Cut selected item(s)."
+ if self._lastFocus: self._lastFocus.cut()
+ def documentsPaste(self):
+ "Paste clipboard item(s) into selected item."
+ if self._lastFocus: self._lastFocus.paste()
+ def documentsDuplicate(self):
+ "Duplicate selected item(s)."
+ if self._lastFocus: self._lastFocus.duplicate()
+ def documentsDelete(self):
+ "Delete selected item(s)."
+ if self._lastFocus: self._lastFocus.delete()
+ def documentsMoveUp(self):
+ "Move up selected item(s)."
+ if self._lastFocus: self._lastFocus.moveUp()
+ def documentsMoveDown(self):
+ "Move Down selected item(s)."
+ if self._lastFocus: self._lastFocus.moveDown()
+
+ def documentsSplitDialog(self):
+ "Opens a dialog to split selected items."
+ if self._lastFocus: self._lastFocus.splitDialog()
+ # current items or selected items?
+ pass
+ # use outlineBasics, to do that on all selected items.
+ # use editorWidget to do that on selected text.
+
+ def documentsSplitCursor(self):
+ """
+ Split current item (open in text editor) at cursor position. If there is
+ a text selection, that selection becomes the title of the new scene.
+ """
+ if self._lastFocus and self._lastFocus == self.mainEditor:
+ self.mainEditor.splitCursor()
+ def documentsMerge(self):
+ "Merges selected item(s)."
+ if self._lastFocus: self._lastFocus.merge()
+
###############################################################################
# LOAD AND SAVE
###############################################################################
@@ -400,6 +498,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if loadFromFile:
# Load empty settings
imp.reload(settings)
+ settings.initDefaultValues()
# Load data
self.loadEmptyDatas()
@@ -408,7 +507,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.makeConnections()
# Load settings
- if settings.openIndexes:
+ if settings.openIndexes and settings.openIndexes != [""]:
self.mainEditor.tabSplitter.restoreOpenIndexes(settings.openIndexes)
self.generateViewMenu()
self.mainEditor.sldCorkSizeFactor.setValue(settings.corkSizeFactor)
@@ -460,7 +559,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
i.setEnabled(False)
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuView, self.menuTools, self.menuHelp,
- self.actCompile, self.actSettings]:
+ self.actImport, self.actCompile, self.actSettings]:
i.setEnabled(True)
# Add project name to Window's name
@@ -504,7 +603,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
i.setEnabled(True)
for i in [self.actSave, self.actSaveAs, self.actCloseProject,
self.menuEdit, self.menuView, self.menuTools, self.menuHelp,
- self.actCompile, self.actSettings]:
+ self.actImport, self.actCompile, self.actSettings]:
i.setEnabled(False)
# Set Window's name - no project loaded
@@ -662,6 +761,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.btnOutlineRemoveItem.clicked.connect(self.outlineRemoveItemsOutline, AUC)
self.tabMain.currentChanged.connect(self.toolbar.setCurrentGroup)
+ self.tabMain.currentChanged.connect(self.tabMainChanged)
+
+ qApp.focusChanged.connect(self.focusChanged)
def makeConnections(self):
@@ -983,16 +1085,6 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# Custom "tab" bar on the left
self.lstTabs.setIconSize(QSize(48, 48))
for i in range(self.tabMain.count()):
- #icons = ["general-128px.png",
- #"summary-128px.png",
- #"characters-128px.png",
- #"plot-128px.png",
- #"world-128px.png",
- #"outline-128px.png",
- #"editor-128px.png",
- #""
- #]
- #self.tabMain.setTabIcon(i, QIcon(appPath("icons/Custom/Tabs/{}".format(icons[i]))))
icons = [QIcon.fromTheme("stock_view-details"), #info
QIcon.fromTheme("application-text-template"), #applications-publishing
@@ -1142,8 +1234,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
w.setDict(settings.dict)
def openPyEnchantWebPage(self):
- from PyQt5.QtGui import QDesktopServices
- QDesktopServices.openUrl(QUrl("http://pythonhosted.org/pyenchant/"))
+ F.openURL("http://pythonhosted.org/pyenchant/")
def toggleSpellcheck(self, val):
settings.spellcheck = val
@@ -1306,9 +1397,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# POV in settings / views
###############################################################################
- # COMPILE
+ # IMPORT / EXPORT
###############################################################################
+ def doImport(self):
+ self.dialog = importerDialog(mw=self)
+ self.dialog.show()
+
+ r = self.dialog.geometry()
+ r2 = self.geometry()
+ self.dialog.move(r2.center() - r.center())
+
def doCompile(self):
self.dialog = exporterDialog(mw=self)
self.dialog.show()
diff --git a/manuskript/models/outlineModel.py b/manuskript/models/outlineModel.py
index 9b4f766..4ce6772 100644
--- a/manuskript/models/outlineModel.py
+++ b/manuskript/models/outlineModel.py
@@ -15,9 +15,15 @@ from manuskript import settings
from lxml import etree as ET
from manuskript.enums import Outline
-from manuskript.functions import mainWindow, toInt, wordCount, HTML2PlainText
+from manuskript.functions import mainWindow, toInt, wordCount
+from manuskript.converters import HTML2PlainText
-locale.setlocale(locale.LC_ALL, '')
+try:
+ locale.setlocale(locale.LC_ALL, '')
+except:
+ # Invalid locale, but not really a big deal because it's used only for
+ # number formating
+ pass
import time, os
@@ -268,6 +274,19 @@ class outlineModel(QAbstractItemModel):
if items is None:
return False
+ # We check if parent is not a child of one of the items
+ if self.isParentAChildOfItems(parent, items):
+ return False
+
+ return True
+
+ def isParentAChildOfItems(self, parent, items):
+ """
+ Takes a parent index, and a list of outlineItems items. Check whether
+ parent is in a child of one of the items.
+ Return True in that case, False if not.
+ """
+
# Get the parent item
if not parent.isValid():
parentItem = self.rootItem
@@ -281,9 +300,9 @@ class outlineModel(QAbstractItemModel):
# Is item in the path? It would mean that it tries to get dropped
# as a children of himself.
if item.ID() in path:
- return False
+ return True
- return True
+ return False
def decodeMimeData(self, data):
if not data.hasFormat("application/xml"):
@@ -340,12 +359,13 @@ class outlineModel(QAbstractItemModel):
if action == Qt.IgnoreAction:
return True # What is that?
- # Strangely, on some cases, we get a call to dropMimeData though
- # self.canDropMimeData returned False.
- # See https://github.com/olivierkes/manuskript/issues/169 to reproduce.
- # So we double check for safety.
- if not self.canDropMimeData(data, action, row, column, parent):
- return False
+ if action == Qt.MoveAction:
+ # Strangely, on some cases, we get a call to dropMimeData though
+ # self.canDropMimeData returned False.
+ # See https://github.com/olivierkes/manuskript/issues/169 to reproduce.
+ # So we double check for safety.
+ if not self.canDropMimeData(data, action, row, column, parent):
+ return False
items = self.decodeMimeData(data)
if items is None:
@@ -361,14 +381,41 @@ class outlineModel(QAbstractItemModel):
else:
beginRow = self.rowCount() + 1
+ if action == Qt.CopyAction:
+ # Behavior if parent is a text item
+ # For example, we select a text and do: CTRL+C CTRL+V
+ if parent.isValid() and not parent.internalPointer().isFolder():
+ # We insert copy in parent folder, just below
+ beginRow = parent.row() + 1
+ parent = parent.parent()
+
+ if parent.isValid() and parent.internalPointer().isFolder():
+ while self.isParentAChildOfItems(parent, items):
+ # We are copying a folder on itself. Assume duplicates.
+ # Copy not in, but next to
+ beginRow = parent.row() + 1
+ parent = parent.parent()
+
if not items:
return False
- r = self.insertItems(items, beginRow, parent)
-
+ # In case of copy actions, items might be duplicates, so we need new IDs.
+ # But they might not be, if we cut, then paste. Paste is a Copy Action.
+ # The first paste would not need new IDs. But subsequent ones will.
if action == Qt.CopyAction:
+ IDs = self.rootItem.listAllIDs()
+
for item in items:
- item.getUniqueID()
+ if item.ID() in IDs:
+ # Recursively remove ID. So will get a new one when inserted.
+ def stripID(item):
+ item.setData(Outline.ID.value, None)
+ for c in item.children():
+ stripID(c)
+
+ stripID(item)
+
+ r = self.insertItems(items, beginRow, parent)
return r
@@ -618,6 +665,7 @@ class outlineItem():
if column == Outline.text.value:
wc = wordCount(data)
self.setData(Outline.wordCount.value, wc)
+ self.emitDataChanged(cols=[Outline.text.value]) # new in 0.5.0
if column == Outline.compile.value:
self.emitDataChanged(cols=[Outline.title.value, Outline.compile.value], recursive=True)
@@ -816,10 +864,103 @@ class outlineItem():
return qApp.translate("outlineModel", "{} words").format(
locale.format("%d", wc, grouping=True))
+ def copy(self):
+ """
+ Returns a copy of item, with no parent, and no ID.
+ """
+ item = outlineItem(xml=self.toXML())
+ item.setData(Outline.ID.value, None)
+ return item
- ###############################################################################
- # XML
- ###############################################################################
+ def split(self, splitMark, recursive=True):
+ """
+ Split scene at splitMark. If multiple splitMark, multiple splits.
+
+ If called on a folder and recursive is True, then it is recursively
+ applied to every children.
+ """
+ if self.isFolder() and recursive:
+ for c in self.children():
+ c.split(splitMark)
+
+ else:
+ txt = self.text().split(splitMark)
+
+ if len(txt) == 1:
+ # Mark not found
+ return False
+
+ else:
+
+ # Stores the new text
+ self.setData(Outline.text.value, txt[0])
+
+ k = 1
+ for subTxt in txt[1:]:
+ # Create a copy
+ item = self.copy()
+
+ # Change title adding _k
+ item.setData(Outline.title.value,
+ "{}_{}".format(item.title(), k+1))
+
+ # Set text
+ item.setData(Outline.text.value, subTxt)
+
+ # Inserting item
+ #self.parent().insertChild(self.row()+k, item)
+ self._model.insertItem(item, self.row()+k, self.parent().index())
+ k += 1
+
+ def splitAt(self, position, length=0):
+ """
+ Splits note at position p.
+
+ If length is bigger than 0, it describes the length of the title, made
+ from the character following position.
+ """
+
+ txt = self.text()
+
+ # Stores the new text
+ self.setData(Outline.text.value, txt[:position])
+
+ # Create a copy
+ item = self.copy()
+
+ # Update title
+ if length > 0:
+ title = txt[position:position+length].replace("\n", "")
+ else:
+ title = "{}_{}".format(item.title(), 2)
+ item.setData(Outline.title.value, title)
+
+ # Set text
+ item.setData(Outline.text.value, txt[position+length:])
+
+ # Inserting item using the model to signal views
+ self._model.insertItem(item, self.row()+1, self.parent().index())
+
+ def mergeWith(self, items, sep="\n\n"):
+ """
+ Merges item with several other items. Merge is basic, it merges only
+ the text.
+
+ @param items: list of `outlineItem`s.
+ @param sep: a text added between each item's text.
+ """
+
+ # Merges the texts
+ text = [self.text()]
+ text.extend([i.text() for i in items])
+ self.setData(Outline.text.value, sep.join(text))
+
+ # Removes other items
+ self._model.removeIndexes([i.index() for i in items])
+
+ ###############################################################################
+ # XML
+ ###############################################################################
def toXML(self):
item = ET.Element("outlineItem")
@@ -879,14 +1020,17 @@ class outlineItem():
elif child.tag == "revision":
self.appendRevision(child.attrib["timestamp"], child.attrib["text"])
+ ###############################################################################
+ # IDS
+ ###############################################################################
- ###############################################################################
- # IDS
- ###############################################################################
-
- def getUniqueID(self):
+ def getUniqueID(self, recursive=False):
self.setData(Outline.ID.value, self._model.rootItem.findUniqueID())
+ if recursive:
+ for c in self.children():
+ c.getUniqueID(recursive)
+
def checkIDs(self):
"""This is called when a model is loaded.
@@ -895,7 +1039,7 @@ class outlineItem():
self.IDs = self.listAllIDs()
if max([self.IDs.count(i) for i in self.IDs if i]) != 1:
- print("There are some doublons:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
+ print("WARNING ! There are some items with same IDs:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
def checkChildren(item):
for c in item.children():
@@ -913,8 +1057,9 @@ class outlineItem():
return IDs
def findUniqueID(self):
- k = 0
- while str(k) in self.IDs:
+ IDs = [int(i) for i in self.IDs]
+ k = 1
+ while k in IDs:
k += 1
self.IDs.append(str(k))
return str(k)
diff --git a/manuskript/models/persosProxyModel.py b/manuskript/models/persosProxyModel.py
index 48aa637..ba3d2e4 100644
--- a/manuskript/models/persosProxyModel.py
+++ b/manuskript/models/persosProxyModel.py
@@ -2,107 +2,108 @@
#--!-- coding: utf8 --!--
from manuskript import enums
+from manuskript.ui import style as S
class persosProxyModel(QSortFilterProxyModel):
-
+
newStatuses = pyqtSignal()
-
+
def __init__(self, parent=None):
QSortFilterProxyModel.__init__(self, parent)
-
+
#self.rootItem = QStandardItem()
self.p1 = QStandardItem(self.tr("Main"))
self.p2 = QStandardItem(self.tr("Secundary"))
self.p3 = QStandardItem(self.tr("Minors"))
-
+
self._cats = [
self.p1,
self.p2,
self.p3
]
-
+
def mapFromSource(self, sourceIndex):
if not sourceIndex.isValid():
return QModelIndex()
-
+
row = self._map.index(sourceIndex.row())
#item = sourceIndex.internalPointer()
item = self.sourceModel().itemFromIndex(sourceIndex)
-
+
return self.createIndex(row, sourceIndex.column(), item)
-
+
def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags
-
+
if index.isValid() and not self.mapToSource(index).isValid():
return Qt.NoItemFlags#Qt.ItemIsEnabled
else:
return Qt.ItemIsEnabled | Qt.ItemIsSelectable
-
+
def mapToSource(self, proxyIndex):
if not proxyIndex.isValid():
return QModelIndex()
-
+
row = self._map[proxyIndex.row()]
-
+
if type(row) != int:
return QModelIndex()
-
+
#item = proxyIndex.internalPointer()
item = self.sourceModel().item(row, proxyIndex.column())
-
+
return self.sourceModel().indexFromItem(item)
-
+
def setSourceModel(self, model):
QSortFilterProxyModel.setSourceModel(self, model)
self.sourceModel().dataChanged.connect(self.mapModelMaybe)
self.sourceModel().rowsInserted.connect(self.mapModel)
self.sourceModel().rowsRemoved.connect(self.mapModel)
self.sourceModel().rowsMoved.connect(self.mapModel)
-
+
self.mapModel()
-
+
def mapModelMaybe(self, topLeft, bottomRight):
if topLeft.column() <= Perso.importance.value <= bottomRight.column():
self.mapModel()
-
+
def mapModel(self):
self.beginResetModel()
src = self.sourceModel()
-
+
self._map = []
-
+
for i, cat in enumerate(self._cats):
self._map.append(cat)
-
+
for p in range(src.rowCount()):
item = src.item(p, Perso.importance.value)
-
+
if item and item.text():
imp = int(item.text())
else:
imp = 0
-
+
if 2-imp == i:
self._map.append(p)
-
+
self.endResetModel()
-
-
+
+
def data(self, index, role=Qt.DisplayRole):
-
+
if index.isValid() and not self.mapToSource(index).isValid():
row = index.row()
-
+
if role == Qt.DisplayRole:
return self._map[row].text()
-
+
elif role == Qt.ForegroundRole:
- return QBrush(Qt.darkBlue)
+ return QBrush(QColor(S.highlightedTextDark))
elif role == Qt.BackgroundRole:
- return QBrush(QColor(Qt.blue).lighter(190))
+ return QBrush(QColor(S.highlightLight))
elif role == Qt.TextAlignmentRole:
return Qt.AlignCenter
elif role == Qt.FontRole:
@@ -113,32 +114,32 @@ class persosProxyModel(QSortFilterProxyModel):
else:
#FIXME: sometimes, the name of the character is not displayed
return self.sourceModel().data(self.mapToSource(index), role)
-
+
def index(self, row, column, parent):
-
+
i = self._map[row]
-
+
if type(i) != int:
-
+
return self.createIndex(row, column, i)
-
+
else:
-
+
return self.mapFromSource(self.sourceModel().index(i, column, QModelIndex()))
-
+
def parent(self, index=QModelIndex()):
return QModelIndex()
-
+
def rowCount(self, parent=QModelIndex()):
return len(self._map)
-
+
def columnCount(self, parent=QModelIndex()):
return self.sourceModel().columnCount(QModelIndex())
-
+
def item(self, row, col, parent=QModelIndex()):
idx = self.mapToSource(self.index(row, col, parent))
return self.sourceModel().item(idx.row(), idx.column())
-
-
+
+
#def setData(self, index, value, role=Qt.EditRole):
- #pass
\ No newline at end of file
+ #pass
diff --git a/manuskript/models/plotsProxyModel.py b/manuskript/models/plotsProxyModel.py
index f182795..f0063ee 100644
--- a/manuskript/models/plotsProxyModel.py
+++ b/manuskript/models/plotsProxyModel.py
@@ -9,6 +9,7 @@ from PyQt5.QtGui import QColor
from PyQt5.QtGui import QStandardItem
from manuskript.enums import Plot
+from manuskript.ui import style as S
class plotsProxyModel(QSortFilterProxyModel):
@@ -105,9 +106,9 @@ class plotsProxyModel(QSortFilterProxyModel):
return self._map[row].text()
elif role == Qt.ForegroundRole:
- return QBrush(Qt.darkBlue)
+ return QBrush(QColor(S.highlightedTextDark))
elif role == Qt.BackgroundRole:
- return QBrush(QColor(Qt.blue).lighter(190))
+ return QBrush(QColor(S.highlightLight))
elif role == Qt.TextAlignmentRole:
return Qt.AlignCenter
elif role == Qt.FontRole:
diff --git a/manuskript/models/references.py b/manuskript/models/references.py
index 0223b76..66660f7 100644
--- a/manuskript/models/references.py
+++ b/manuskript/models/references.py
@@ -9,12 +9,16 @@ import re
# A regex used to match references
from PyQt5.QtWidgets import qApp
+from PyQt5.QtGui import QColor
+from PyQt5.QtCore import Qt
from manuskript.enums import Outline
from manuskript.enums import Character
from manuskript.enums import Plot
from manuskript.enums import PlotStep
-from manuskript.functions import mainWindow
+from manuskript.functions import mainWindow, mixColors
+from manuskript.ui import style as S
+
RegEx = r"{(\w):(\d+):?.*?}"
# A non-capturing regex used to identify references
@@ -27,6 +31,12 @@ TextLetter = "T"
PlotLetter = "P"
WorldLetter = "W"
+# Colors
+TextHighlightColor = QColor(mixColors(QColor(Qt.blue).name(), S.window, .3))
+CharacterHighlightColor = QColor(mixColors(QColor(Qt.yellow).name(), S.window, .3))
+PlotHighlightColor = QColor(mixColors(QColor(Qt.red).name(), S.window, .3))
+WorldHighlightColor = QColor(mixColors(QColor(Qt.green).name(), S.window, .3))
+
def plotReference(ID, searchable=False):
"""Takes the ID of a plot and returns a reference for that plot.
@@ -69,7 +79,7 @@ def worldReference(ID, searchable=False):
###############################################################################
def infos(ref):
- """Returns a full paragraph in HTML format
+ """Returns a full paragraph in HTML format
containing detailed infos about the reference ``ref``.
"""
match = re.fullmatch(RegEx, ref)
diff --git a/manuskript/models/worldModel.py b/manuskript/models/worldModel.py
index 5f600e6..45692d4 100644
--- a/manuskript/models/worldModel.py
+++ b/manuskript/models/worldModel.py
@@ -4,11 +4,12 @@ from PyQt5.QtCore import QModelIndex
from PyQt5.QtCore import QSize
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItem, QBrush, QFontMetrics
-from PyQt5.QtGui import QStandardItemModel
+from PyQt5.QtGui import QStandardItemModel, QColor
from PyQt5.QtWidgets import QMenu, QAction, qApp
from manuskript.enums import World
-from manuskript.functions import mainWindow, lightBlue
+from manuskript.functions import mainWindow
+from manuskript.ui import style as S
class worldModel(QStandardItemModel):
@@ -254,7 +255,7 @@ class worldModel(QStandardItemModel):
if role == Qt.BackgroundRole:
if level == 0:
- return QBrush(lightBlue())
+ return QBrush(QColor(S.highlightLight))
if role == Qt.TextAlignmentRole:
if level == 0:
@@ -268,7 +269,7 @@ class worldModel(QStandardItemModel):
if role == Qt.ForegroundRole:
if level == 0:
- return QBrush(Qt.darkBlue)
+ return QBrush(QColor(S.highlightedTextDark))
if role == Qt.SizeHintRole:
fm = QFontMetrics(qApp.font())
diff --git a/manuskript/settings.py b/manuskript/settings.py
index 4beb4b5..e387e35 100644
--- a/manuskript/settings.py
+++ b/manuskript/settings.py
@@ -58,8 +58,8 @@ defaultTextType = "md"
fullScreenTheme = "spacedreams"
textEditor = {
- "background": "#fff",
- "fontColor": "#000",
+ "background": "",
+ "fontColor": "",
"font": qApp.font().toString(),
"misspelled": "#F00",
"lineSpacing": 100,
@@ -73,6 +73,7 @@ textEditor = {
"maxWidth": 0,
"marginsLR": 0,
"marginsTB": 0,
+ "backgroundTransparent": False,
}
revisions = {
@@ -96,13 +97,27 @@ frequencyAnalyzer = {
viewMode = "fiction" # simple, fiction
saveToZip = True
+dontShowDeleteWarning = False
+
+def initDefaultValues():
+ """
+ Load some default values based on system's settings.
+ Is called anytime we open/create a project.
+ """
+ global textEditor
+ if not textEditor["background"]:
+ from manuskript.ui import style as S
+ textEditor["background"] = S.base
+ if not textEditor["fontColor"]:
+ from manuskript.ui import style as S
+ textEditor["fontColor"] = S.text
def save(filename=None, protocol=None):
global spellcheck, dict, corkSliderFactor, viewSettings, corkSizeFactor, folderView, lastTab, openIndexes, \
autoSave, autoSaveDelay, saveOnQuit, autoSaveNoChanges, autoSaveNoChangesDelay, outlineViewColumns, \
corkBackground, corkStyle, fullScreenTheme, defaultTextType, textEditor, revisions, frequencyAnalyzer, viewMode, \
- saveToZip
+ saveToZip, dontShowDeleteWarning
allSettings = {
"viewSettings": viewSettings,
@@ -127,6 +142,7 @@ def save(filename=None, protocol=None):
"frequencyAnalyzer": frequencyAnalyzer,
"viewMode": viewMode,
"saveToZip": saveToZip,
+ "dontShowDeleteWarning": dontShowDeleteWarning,
}
#pp=pprint.PrettyPrinter(indent=4, compact=False)
@@ -254,6 +270,7 @@ def load(string, fromString=False, protocol=None):
"maxWidth": 0,
"marginsLR": 0,
"marginsTB": 0,
+ "backgroundTransparent": False, # Added in 0.6.0
}
for k in added:
@@ -294,3 +311,7 @@ def load(string, fromString=False, protocol=None):
if "saveToZip" in allSettings:
global saveToZip
saveToZip = allSettings["saveToZip"]
+
+ if "dontShowDeleteWarning" in allSettings:
+ global dontShowDeleteWarning
+ dontShowDeleteWarning = allSettings["dontShowDeleteWarning"]
diff --git a/manuskript/settingsWindow.py b/manuskript/settingsWindow.py
index c505de4..688d7de 100644
--- a/manuskript/settingsWindow.py
+++ b/manuskript/settingsWindow.py
@@ -23,6 +23,7 @@ from manuskript.ui.settings_ui import Ui_Settings
from manuskript.ui.views.outlineView import outlineView
from manuskript.ui.views.textEditView import textEditView
from manuskript.ui.welcome import welcome
+from manuskript.ui import style as S
try:
import enchant
@@ -37,6 +38,15 @@ class settingsWindow(QWidget, Ui_Settings):
self.mw = mainWindow
# UI
+ for l in [self.lblTitleGeneral,
+ self.lblTitleGeneral_2,
+ self.lblTitleViews,
+ self.lblTitleLabels,
+ self.lblTitleStatus,
+ self.lblTitleFullscreen,
+ ]:
+ l.setStyleSheet(S.titleLabelSS())
+
icons = [QIcon.fromTheme("configure"),
QIcon.fromTheme("history-view"),
QIcon.fromTheme("gnome-settings"),
@@ -164,6 +174,9 @@ class settingsWindow(QWidget, Ui_Settings):
self.btnEditorMisspelledColor.clicked.connect(self.choseEditorMisspelledColor)
self.setButtonColor(self.btnEditorBackgroundColor, opt["background"])
self.btnEditorBackgroundColor.clicked.connect(self.choseEditorBackgroundColor)
+ self.chkEditorBackgroundTransparent.setChecked(opt["backgroundTransparent"])
+ self.chkEditorBackgroundTransparent.stateChanged.connect(self.updateEditorSettings)
+ self.btnEditorColorDefault.clicked.connect(self.restoreEditorColors)
f = QFont()
f.fromString(opt["font"])
self.cmbEditorFontFamily.setCurrentFont(f)
@@ -442,7 +455,13 @@ class settingsWindow(QWidget, Ui_Settings):
####################################################################################################
def updateEditorSettings(self):
- # Store settings
+ """
+ Stores settings for editor appareance.
+ """
+
+ # Background
+ settings.textEditor["backgroundTransparent"] = True if self.chkEditorBackgroundTransparent.checkState() else False
+
# Font
f = self.cmbEditorFontFamily.currentFont()
f.setPointSize(self.spnEditorFontSize.value())
@@ -526,6 +545,13 @@ class settingsWindow(QWidget, Ui_Settings):
self.setButtonColor(self.btnEditorBackgroundColor, color.name())
self.updateEditorSettings()
+ def restoreEditorColors(self):
+ settings.textEditor["background"] = S.base
+ self.setButtonColor(self.btnEditorBackgroundColor, S.base)
+ settings.textEditor["fontColor"] = S.text
+ self.setButtonColor(self.btnEditorFontColor, S.text)
+ self.updateEditorSettings()
+
####################################################################################################
# STATUS #
####################################################################################################
diff --git a/manuskript/ui/cheatSheet.py b/manuskript/ui/cheatSheet.py
index 6deb4a5..b44038d 100644
--- a/manuskript/ui/cheatSheet.py
+++ b/manuskript/ui/cheatSheet.py
@@ -1,14 +1,13 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import pyqtSignal, Qt, QTimer, QRect
-from PyQt5.QtGui import QBrush, QCursor, QPalette, QFontMetrics
+from PyQt5.QtGui import QBrush, QCursor, QPalette, QFontMetrics, QColor
from PyQt5.QtWidgets import QWidget, QListWidgetItem, QToolTip, QStyledItemDelegate, QStyle
from manuskript.enums import Character
from manuskript.enums import Plot
-from manuskript.functions import lightBlue
from manuskript.functions import mainWindow
-from manuskript.ui import style
+from manuskript.ui import style as S
from manuskript.ui.cheatSheet_ui import Ui_cheatSheet
from manuskript.models import references as Ref
from manuskript.ui.editors.completer import completer
@@ -20,7 +19,7 @@ class cheatSheet(QWidget, Ui_cheatSheet):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
- self.txtFilter.setStyleSheet(style.lineEditSS())
+ self.txtFilter.setStyleSheet(S.lineEditSS())
self.splitter.setStretchFactor(0, 5)
self.splitter.setStretchFactor(1, 70)
@@ -122,8 +121,8 @@ class cheatSheet(QWidget, Ui_cheatSheet):
def addCategory(self, title):
item = QListWidgetItem(title)
- item.setBackground(QBrush(lightBlue()))
- item.setForeground(QBrush(Qt.darkBlue))
+ item.setBackground(QBrush(QColor(S.highlightLight)))
+ item.setForeground(QBrush(QColor(S.highlightedTextDark)))
item.setFlags(Qt.ItemIsEnabled)
f = item.font()
f.setBold(True)
diff --git a/manuskript/ui/collapsibleGroupBox.py b/manuskript/ui/collapsibleGroupBox.py
index dac9cfc..2b3cc04 100644
--- a/manuskript/ui/collapsibleGroupBox.py
+++ b/manuskript/ui/collapsibleGroupBox.py
@@ -4,6 +4,7 @@ from PyQt5.QtCore import Qt, QRect, QRectF
from PyQt5.QtGui import QColor, QBrush, QRegion, QTextOption, QFont
from PyQt5.QtWidgets import QSizePolicy, QGroupBox, QWidget, QStylePainter, QStyleOptionGroupBox, qApp, QVBoxLayout, \
QStyle, QStyleOptionFrame, QStyleOptionFocusRect
+from manuskript.ui import style as S
class collapsibleGroupBox(QGroupBox):
@@ -57,7 +58,7 @@ class collapsibleGroupBox(QGroupBox):
titleRect.setHeight(textRect.height())
titleRect.moveTop(textRect.top())
- p.setBrush(QBrush(QColor(Qt.blue).lighter(190)))
+ p.setBrush(QBrush(QColor(S.highlightLight)))
p.setPen(Qt.NoPen)
p.drawRoundedRect(titleRect, 10, 10)
p.restore()
@@ -105,7 +106,7 @@ class collapsibleGroupBox(QGroupBox):
f = QFont()
f.setBold(True)
p.setFont(f)
- p.setPen(Qt.darkBlue)
+ p.setPen(QColor(S.highlightedTextDark))
p.drawText(QRectF(titleRect), groupBox.text.replace("&", ""), topt)
p.restore()
diff --git a/manuskript/ui/collapsibleGroupBox2.py b/manuskript/ui/collapsibleGroupBox2.py
index 96f594f..b52e1ee 100644
--- a/manuskript/ui/collapsibleGroupBox2.py
+++ b/manuskript/ui/collapsibleGroupBox2.py
@@ -3,7 +3,6 @@
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QWidget, QFrame, QPushButton, QVBoxLayout, QSizePolicy, qApp
-from manuskript.functions import lightBlue
from manuskript.ui import style
@@ -16,7 +15,6 @@ class collapsibleGroupBox2(QWidget):
self.button.setChecked(True)
self.switched = False
self.vPolicy = None
- # self.button.setStyleSheet("background-color: lightBlue;")
self.button.setStyleSheet(style.collapsibleGroupBoxButton())
diff --git a/manuskript/ui/editors/basicHighlighter.py b/manuskript/ui/editors/basicHighlighter.py
index 1b69033..a09b53a 100644
--- a/manuskript/ui/editors/basicHighlighter.py
+++ b/manuskript/ui/editors/basicHighlighter.py
@@ -37,7 +37,7 @@ class basicHighlighter(QSyntaxHighlighter):
def highlightBlockBefore(self, text):
"""Highlighting to do before anything else.
-
+
When subclassing basicHighlighter, you must call highlightBlockBefore
before you do any custom highlighting.
"""
@@ -55,7 +55,7 @@ class basicHighlighter(QSyntaxHighlighter):
def highlightBlockAfter(self, text):
"""Highlighting to do after everything else.
-
+
When subclassing basicHighlighter, you must call highlightBlockAfter
after your custom highlighting.
"""
@@ -65,22 +65,22 @@ class basicHighlighter(QSyntaxHighlighter):
fmt = self.format(txt.start())
fmt.setFontFixedPitch(True)
fmt.setFontWeight(QFont.DemiBold)
- fmt.setForeground(Qt.black) # or text becomes unreadable in some color scheme
+
if txt.group(1) == Ref.TextLetter:
- fmt.setBackground(QBrush(QColor(Qt.blue).lighter(190)))
+ fmt.setBackground(QBrush(Ref.TextHighlightColor))
elif txt.group(1) == Ref.CharacterLetter:
- fmt.setBackground(QBrush(QColor(Qt.yellow).lighter(170)))
+ fmt.setBackground(QBrush(Ref.CharacterHighlightColor))
elif txt.group(1) == Ref.PlotLetter:
- fmt.setBackground(QBrush(QColor(Qt.red).lighter(170)))
+ fmt.setBackground(QBrush(Ref.PlotHighlightColor))
elif txt.group(1) == Ref.WorldLetter:
- fmt.setBackground(QBrush(QColor(Qt.green).lighter(170)))
+ fmt.setBackground(QBrush(Ref.WorldHighlightColor))
self.setFormat(txt.start(),
txt.end() - txt.start(),
fmt)
# Spell checking
-
+
# Following algorithm would not check words at the end of line.
# This hacks adds a space to every line where the text cursor is not
# So that it doesn't spellcheck while typing, but still spellchecks at
@@ -89,7 +89,7 @@ class basicHighlighter(QSyntaxHighlighter):
if self.currentBlock().position() + len(text) != \
self.editor.textCursor().position():
textedText = text + " "
-
+
# Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
WORDS = '(?iu)([\w\']+)[^\'\w]' # (?iu) means case insensitive and unicode
if hasattr(self.editor, "spellcheck") and self.editor.spellcheck:
diff --git a/manuskript/ui/editors/completer.py b/manuskript/ui/editors/completer.py
index 70e0b43..0840b9e 100644
--- a/manuskript/ui/editors/completer.py
+++ b/manuskript/ui/editors/completer.py
@@ -1,13 +1,13 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import pyqtSignal, Qt, QRect
-from PyQt5.QtGui import QBrush, QFontMetrics, QPalette
+from PyQt5.QtGui import QBrush, QFontMetrics, QPalette, QColor
from PyQt5.QtWidgets import QWidget, QListWidgetItem, QStyledItemDelegate, QStyle
-from manuskript.functions import lightBlue
from manuskript.functions import mainWindow
from manuskript.ui.editors.completer_ui import Ui_completer
from manuskript.models import references as Ref
+from manuskript.ui import style as S
class completer(QWidget, Ui_completer):
@@ -33,8 +33,8 @@ class completer(QWidget, Ui_completer):
def addCategory(self, title):
item = QListWidgetItem(title)
- item.setBackground(QBrush(lightBlue()))
- item.setForeground(QBrush(Qt.darkBlue))
+ item.setBackground(QBrush(QColor(S.highlightLight)))
+ item.setForeground(QBrush(QColor(S.highlightedTextDark)))
item.setFlags(Qt.ItemIsEnabled)
self.list.addItem(item)
@@ -88,6 +88,10 @@ class listCompleterDelegate(QStyledItemDelegate):
r = QRect(option.rect)
r.setLeft(r.left() + w)
painter.save()
- painter.setPen(Qt.gray)
+ if option.state & QStyle.State_Selected:
+ painter.setPen(QColor(S.highlightedTextLight))
+ else:
+ painter.setPen(QColor(S.textLight))
+
painter.drawText(r, Qt.AlignLeft, extra)
painter.restore()
diff --git a/manuskript/ui/editors/editorWidget.py b/manuskript/ui/editors/editorWidget.py
index 75abb56..b23d349 100644
--- a/manuskript/ui/editors/editorWidget.py
+++ b/manuskript/ui/editors/editorWidget.py
@@ -8,6 +8,7 @@ from manuskript import settings
from manuskript.functions import AUC, mainWindow
from manuskript.ui.editors.editorWidget_ui import Ui_editorWidget_ui
from manuskript.ui.views.textEditView import textEditView
+from manuskript.ui.tools.splitDialog import splitDialog
class editorWidget(QWidget, Ui_editorWidget_ui):
@@ -39,6 +40,8 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
toggledSpellcheck = pyqtSignal(bool)
dictChanged = pyqtSignal(str)
+ _maxTabTitleLength = 24
+
def __init__(self, parent):
QWidget.__init__(self, parent)
self.setupUi(self)
@@ -55,6 +58,8 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
self.mw = mainWindow()
self._tabWidget = None # set by mainEditor on creation
+ self._model = None
+
# def setModel(self, model):
# self._model = model
# self.setView()
@@ -83,8 +88,10 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
if r.isValid():
count = r.internalPointer().childCount()
+ elif self._model:
+ count = self._model.rootItem.childCount()
else:
- count = self.mw.mdlOutline.rootItem.childCount()
+ count = 0
for c in range(count):
self.corkView.itemDelegate().sizeHintChanged.emit(r.child(c, 0))
@@ -102,11 +109,21 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
if self.currentIndex.isValid():
item = self.currentIndex.internalPointer()
+ elif self._model:
+ item = self._model.rootItem
else:
- item = self.mw.mdlOutline.rootItem
+ return
i = self._tabWidget.indexOf(self)
- self._tabWidget.setTabText(i, item.title())
+
+ self._tabWidget.setTabText(i, self.ellidedTitle(item.title()))
+ self._tabWidget.setTabToolTip(i, item.title())
+
+ def ellidedTitle(self, title):
+ if len(title) > self._maxTabTitleLength:
+ return "{}…".format(title[:self._maxTabTitleLength])
+ else:
+ return title
def setView(self):
# index = mainWindow().treeRedacOutline.currentIndex()
@@ -202,7 +219,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
self.txtEdits = []
- if item != self.mw.mdlOutline.rootItem:
+ if item != self._model.rootItem:
addTitle(item)
addChildren(item)
@@ -211,7 +228,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
elif item and item.isFolder() and self.folderView == "cork":
self.stack.setCurrentIndex(2)
- self.corkView.setModel(self.mw.mdlOutline)
+ self.corkView.setModel(self._model)
self.corkView.setRootIndex(self.currentIndex)
try:
self.corkView.selectionModel().selectionChanged.connect(mainWindow().redacMetadata.selectionChanged, AUC)
@@ -225,7 +242,7 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
self.outlineView.setModelCharacters(mainWindow().mdlCharacter)
self.outlineView.setModelLabels(mainWindow().mdlLabels)
self.outlineView.setModelStatus(mainWindow().mdlStatus)
- self.outlineView.setModel(self.mw.mdlOutline)
+ self.outlineView.setModel(self._model)
self.outlineView.setRootIndex(self.currentIndex)
try:
@@ -242,9 +259,9 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
self.txtRedacText.setCurrentModelIndex(QModelIndex())
try:
- self.mw.mdlOutline.dataChanged.connect(self.modelDataChanged, AUC)
- self.mw.mdlOutline.rowsInserted.connect(self.updateIndexFromID, AUC)
- self.mw.mdlOutline.rowsRemoved.connect(self.updateIndexFromID, AUC)
+ self._model.dataChanged.connect(self.modelDataChanged, AUC)
+ self._model.rowsInserted.connect(self.updateIndexFromID, AUC)
+ self._model.rowsRemoved.connect(self.updateIndexFromID, AUC)
#self.mw.mdlOutline.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC)
except TypeError:
pass
@@ -254,20 +271,21 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
def setCurrentModelIndex(self, index=None):
if index.isValid():
self.currentIndex = index
- self.currentID = self.mw.mdlOutline.ID(index)
- # self._model = index.model()
+ self._model = index.model()
+ self.currentID = self._model.ID(index)
else:
self.currentIndex = QModelIndex()
self.currentID = None
- self.setView()
+ if self._model:
+ self.setView()
def updateIndexFromID(self):
"""
Index might have changed (through drag an drop), so we keep current
item's ID and update index. Item might have been deleted too.
"""
- idx = self.mw.mdlOutline.getIndexByID(self.currentID)
+ idx = self._model.getIndexByID(self.currentID)
# If we have an ID but the ID does not exist, it has been deleted
if self.currentID and idx == QModelIndex():
@@ -317,3 +335,92 @@ class editorWidget(QWidget, Ui_editorWidget_ui):
def setDict(self, dct):
self.currentDict = dct
self.dictChanged.emit(dct)
+
+ ###############################################################################
+ # FUNCTIONS FOR MENU ACCESS
+ ###############################################################################
+
+ def getCurrentItemView(self):
+ """
+ Returns the current item view, between txtRedacText, outlineView and
+ corkView. If folder/text view, returns None. (Because handled
+ differently)
+ """
+
+ if self.stack.currentIndex() == 0:
+ return self.txtRedacText
+ elif self.folderView == "outline":
+ return self.outlineView
+ elif self.folderView == "cork":
+ return self.corkView
+ else:
+ return None
+
+ def copy(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().copy()
+ def cut(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().cut()
+ def paste(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().paste()
+ def duplicate(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().duplicate()
+ def delete(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().delete()
+ def moveUp(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().moveUp()
+ def moveDown(self):
+ if self.getCurrentItemView(): self.getCurrentItemView().moveDown()
+
+ def splitDialog(self):
+ """
+ Opens a dialog to split selected items.
+ """
+ if self.getCurrentItemView() == self.txtRedacText:
+ # Text editor
+ if not self.currentIndex.isValid():
+ return
+
+ sel = self.txtRedacText.textCursor().selectedText()
+ # selectedText uses \u2029 instead of \n, no idea why.
+ sel = sel.replace("\u2029", "\n")
+ splitDialog(self, [self.currentIndex], mark=sel)
+
+ elif self.getCurrentItemView():
+ # One of the views
+ self.getCurrentItemView().splitDialog()
+
+ def splitCursor(self):
+ """
+ Splits items at cursor position. If there is a selection, that selection
+ becomes the new item's title.
+
+ Call context: Only works when editing a file.
+ """
+
+ if not self.currentIndex.isValid():
+ return
+
+ if self.getCurrentItemView() == self.txtRedacText:
+ c = self.txtRedacText.textCursor()
+
+ title = c.selectedText()
+ # selection can be backward
+ pos = min(c.selectionStart(), c.selectionEnd())
+
+ item = self.currentIndex.internalPointer()
+
+ item.splitAt(pos, len(title))
+
+ def merge(self):
+ """
+ Merges selected items together.
+
+ Call context: Multiple selection, same parent.
+ """
+ if self.getCurrentItemView() == self.txtRedacText:
+ # Text editor, nothing to merge
+ pass
+
+ elif self.getCurrentItemView():
+ # One of the views
+ self.getCurrentItemView().merge()
diff --git a/manuskript/ui/editors/fullScreenEditor.py b/manuskript/ui/editors/fullScreenEditor.py
index 52d1eed..cef0ac6 100644
--- a/manuskript/ui/editors/fullScreenEditor.py
+++ b/manuskript/ui/editors/fullScreenEditor.py
@@ -243,11 +243,12 @@ class fullScreenEditor(QWidget):
def hideWidget(self, widget):
if widget not in self._geometries:
self._geometries[widget] = widget.geometry()
-
+
if hasattr(widget, "_autoHide") and not widget._autoHide:
return
-
- widget.move(self.geometry().bottomRight())
+
+ # Hides wiget in the bottom right corner
+ widget.move(self.geometry().bottomRight() + QPoint(1, 1))
def showWidget(self, widget):
if widget in self._geometries:
@@ -341,7 +342,7 @@ class myPanel(QWidget):
self.show()
self.setAttribute(Qt.WA_TranslucentBackground)
self._autoHide = True
-
+
if not vertical:
self.setLayout(QHBoxLayout())
else:
@@ -355,10 +356,10 @@ class myPanel(QWidget):
r = event.rect()
painter = QPainter(self)
painter.fillRect(r, self._color)
-
+
def setAutoHide(self, value):
self._autoHide = value
-
+
def mouseReleaseEvent(self, event):
if event.button() == Qt.RightButton:
m = QMenu()
diff --git a/manuskript/ui/editors/mainEditor.py b/manuskript/ui/editors/mainEditor.py
index 16358b0..1eafe22 100644
--- a/manuskript/ui/editors/mainEditor.py
+++ b/manuskript/ui/editors/mainEditor.py
@@ -15,8 +15,10 @@ from manuskript.ui.editors.editorWidget import editorWidget
from manuskript.ui.editors.fullScreenEditor import fullScreenEditor
from manuskript.ui.editors.mainEditor_ui import Ui_mainEditor
-locale.setlocale(locale.LC_ALL, '')
-
+try:
+ locale.setlocale(locale.LC_ALL, '')
+except:
+ pass
class mainEditor(QWidget, Ui_mainEditor):
"""
@@ -170,7 +172,6 @@ class mainEditor(QWidget, Ui_mainEditor):
ts = ts.secondTab
return r
-
###############################################################################
# SELECTION AND UPDATES
###############################################################################
@@ -219,7 +220,8 @@ class mainEditor(QWidget, Ui_mainEditor):
editor = editorWidget(self)
editor.setCurrentModelIndex(index)
editor._tabWidget = tabWidget
- tabWidget.addTab(editor, title)
+ i = tabWidget.addTab(editor, editor.ellidedTitle(title))
+ tabWidget.setTabToolTip(i, title)
tabWidget.setCurrentIndex(tabWidget.count() - 1)
else:
self.currentEditor(tabWidget).setCurrentModelIndex(index)
@@ -243,6 +245,21 @@ class mainEditor(QWidget, Ui_mainEditor):
return title
+ ###############################################################################
+ # FUNCTIONS FOR MENU ACCESS
+ ###############################################################################
+
+ def copy(self): self.currentEditor().copy()
+ def cut(self): self.currentEditor().cut()
+ def paste(self): self.currentEditor().paste()
+ def duplicate(self): self.currentEditor().duplicate()
+ def delete(self): self.currentEditor().delete()
+ def moveUp(self): self.currentEditor().moveUp()
+ def moveDown(self): self.currentEditor().moveDown()
+ def splitDialog(self): self.currentEditor().splitDialog()
+ def splitCursor(self): self.currentEditor().splitCursor()
+ def merge(self): self.currentEditor().merge()
+
###############################################################################
# UI
###############################################################################
@@ -301,12 +318,12 @@ class mainEditor(QWidget, Ui_mainEditor):
drawProgress(p, rect, progress, 2)
del p
self.lblRedacProgress.setPixmap(self.px)
- self.lblRedacWC.setText(self.tr("{} words / {}").format(
+ self.lblRedacWC.setText(self.tr("{} words / {} ").format(
locale.format("%d", wc, grouping=True),
locale.format("%d", goal, grouping=True)))
else:
self.lblRedacProgress.hide()
- self.lblRedacWC.setText(self.tr("{} words").format(
+ self.lblRedacWC.setText(self.tr("{} words ").format(
locale.format("%d", wc, grouping=True)))
###############################################################################
diff --git a/manuskript/ui/editors/mainEditor_ui.py b/manuskript/ui/editors/mainEditor_ui.py
index 269f0fc..45c8e6f 100644
--- a/manuskript/ui/editors/mainEditor_ui.py
+++ b/manuskript/ui/editors/mainEditor_ui.py
@@ -2,8 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/editors/mainEditor_ui.ui'
#
-# Created: Sat Oct 14 21:30:36 2017
-# by: PyQt5 UI code generator 5.2.1
+# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
@@ -14,8 +13,8 @@ class Ui_mainEditor(object):
mainEditor.setObjectName("mainEditor")
mainEditor.resize(791, 319)
self.verticalLayout = QtWidgets.QVBoxLayout(mainEditor)
- self.verticalLayout.setSpacing(0)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.tabSplitter = tabSplitter(mainEditor)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
diff --git a/manuskript/ui/editors/mainEditor_ui.ui b/manuskript/ui/editors/mainEditor_ui.ui
index 68a3a68..1110a34 100644
--- a/manuskript/ui/editors/mainEditor_ui.ui
+++ b/manuskript/ui/editors/mainEditor_ui.ui
@@ -50,7 +50,9 @@
-
+
+
+ Alt+Up
diff --git a/manuskript/ui/editors/tabSplitter.py b/manuskript/ui/editors/tabSplitter.py
index c543e77..751ea9a 100644
--- a/manuskript/ui/editors/tabSplitter.py
+++ b/manuskript/ui/editors/tabSplitter.py
@@ -239,7 +239,13 @@ class tabSplitter(QWidget, Ui_tabSplitter):
# border:1px solid darkblue;
# }}""".format(self.splitter.objectName()))
- self.setStyleSheet(style.mainEditorTabSS() + "QWidget{{background:{};}}".format(style.bgHover))
+ self.setStyleSheet(style.mainEditorTabSS() + """
+ QSplitter#{name},
+ QSplitter#{name} > QWidget > QSplitter{{
+ border:3px solid {color};
+ }}""".format(
+ name=self.splitter.objectName(),
+ color=style.highlight))
elif object == self.btnSplit and event.type() == event.HoverLeave:
# self.setAutoFillBackground(False)
# self.setBackgroundRole(QPalette.Window)
diff --git a/manuskript/ui/editors/tabSplitter_ui.py b/manuskript/ui/editors/tabSplitter_ui.py
index 3b20b1d..eac583d 100644
--- a/manuskript/ui/editors/tabSplitter_ui.py
+++ b/manuskript/ui/editors/tabSplitter_ui.py
@@ -2,8 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/editors/tabSplitter_ui.ui'
#
-# Created: Sun Apr 10 16:27:03 2016
-# by: PyQt5 UI code generator 5.2.1
+# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
@@ -20,8 +19,8 @@ class Ui_tabSplitter(object):
tabSplitter.setSizePolicy(sizePolicy)
tabSplitter.setWindowTitle("tabSPlitter")
self.verticalLayout = QtWidgets.QVBoxLayout(tabSplitter)
- self.verticalLayout.setSpacing(0)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.splitter = QtWidgets.QSplitter(tabSplitter)
self.splitter.setMinimumSize(QtCore.QSize(30, 30))
diff --git a/manuskript/ui/exporters/exporter.py b/manuskript/ui/exporters/exporter.py
index da1628c..7a954bc 100644
--- a/manuskript/ui/exporters/exporter.py
+++ b/manuskript/ui/exporters/exporter.py
@@ -5,12 +5,13 @@ import os
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QBrush, QColor, QIcon
-from PyQt5.QtWidgets import QWidget
+from PyQt5.QtWidgets import QWidget, QStyle
from manuskript import exporter
-from manuskript.functions import lightBlue, writablePath
+from manuskript.functions import writablePath, openURL
from manuskript.ui.exporters.exporter_ui import Ui_exporter
from manuskript.ui.exporters.exportersManager import exportersManager
+from manuskript.ui import style as S
class exporterDialog(QWidget, Ui_exporter):
@@ -42,15 +43,19 @@ class exporterDialog(QWidget, Ui_exporter):
self.cmbExporters.clear()
for E in exporter.exporters:
- if not E.isValid():
+ if not E.isValid() and not E.absentTip:
continue
self.cmbExporters.addItem(QIcon(E.icon), E.name)
- self.cmbExporters.setItemData(self.cmbExporters.count() - 1, QBrush(QColor(Qt.darkBlue)), Qt.ForegroundRole)
- self.cmbExporters.setItemData(self.cmbExporters.count() - 1, QBrush(lightBlue()), Qt.BackgroundRole)
+ self.cmbExporters.setItemData(self.cmbExporters.count() - 1, QBrush(QColor(S.highlightedTextDark)), Qt.ForegroundRole)
+ self.cmbExporters.setItemData(self.cmbExporters.count() - 1, QBrush(QColor(S.highlightLight)), Qt.BackgroundRole)
item = self.cmbExporters.model().item(self.cmbExporters.count() - 1)
item.setFlags(Qt.ItemIsEnabled)
+ if not E.isValid() and E.absentTip:
+ self.cmbExporters.addItem(self.style().standardIcon(QStyle.SP_MessageBoxWarning), E.absentTip, "::URL::" + E.absentURL)
+ continue
+
for f in E.exportTo:
if not f.isValid():
@@ -60,6 +65,12 @@ class exporterDialog(QWidget, Ui_exporter):
self.cmbExporters.addItem(QIcon.fromTheme(f.icon), name, E.name)
def updateUi(self, index):
+
+ # We check if we have an URL to open
+ data = self.cmbExporters.currentData()
+ if data and data[:7] == "::URL::" and data[7:]:
+ openURL(data[7:])
+
E, F = self.getSelectedExporter()
if not E or not F or not F.implemented:
diff --git a/manuskript/ui/exporters/exportersManager.py b/manuskript/ui/exporters/exportersManager.py
index bebfc25..60b3ef9 100644
--- a/manuskript/ui/exporters/exportersManager.py
+++ b/manuskript/ui/exporters/exportersManager.py
@@ -10,6 +10,8 @@ from PyQt5.QtWidgets import QWidget, QListWidgetItem, QFileDialog
from manuskript import exporter
from manuskript.ui.exporters.exportersManager_ui import Ui_ExportersManager
+from manuskript.ui import style as S
+
class exportersManager(QWidget, Ui_ExportersManager):
@@ -18,6 +20,7 @@ class exportersManager(QWidget, Ui_ExportersManager):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
+ self.lblExporterName.setStyleSheet(S.titleLabelSS())
# Var
self.currentExporter = None
diff --git a/manuskript/ui/exporters/exportersManager_ui.py b/manuskript/ui/exporters/exportersManager_ui.py
index e98859d..1e4d505 100644
--- a/manuskript/ui/exporters/exportersManager_ui.py
+++ b/manuskript/ui/exporters/exportersManager_ui.py
@@ -2,8 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/exporters/exportersManager_ui.ui'
#
-# Created: Fri Apr 8 12:47:11 2016
-# by: PyQt5 UI code generator 5.2.1
+# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
@@ -31,13 +30,6 @@ class Ui_ExportersManager(object):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lblExporterName.sizePolicy().hasHeightForWidth())
self.lblExporterName.setSizePolicy(sizePolicy)
- self.lblExporterName.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
self.lblExporterName.setText("{Exporter Name}")
self.lblExporterName.setAlignment(QtCore.Qt.AlignCenter)
self.lblExporterName.setObjectName("lblExporterName")
@@ -82,8 +74,8 @@ class Ui_ExportersManager(object):
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
- self.verticalLayout.setSpacing(0)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.lblExportToDescription = QtWidgets.QLabel(self.frame)
font = QtGui.QFont()
diff --git a/manuskript/ui/exporters/exportersManager_ui.ui b/manuskript/ui/exporters/exportersManager_ui.ui
index c5d0c0e..1f275cc 100644
--- a/manuskript/ui/exporters/exportersManager_ui.ui
+++ b/manuskript/ui/exporters/exportersManager_ui.ui
@@ -46,15 +46,6 @@
0
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- {Exporter Name}
diff --git a/manuskript/ui/exporters/manuskript/plainTextSettings.py b/manuskript/ui/exporters/manuskript/plainTextSettings.py
index 23a3556..78fd001 100644
--- a/manuskript/ui/exporters/manuskript/plainTextSettings.py
+++ b/manuskript/ui/exporters/manuskript/plainTextSettings.py
@@ -9,12 +9,14 @@ from PyQt5.QtWidgets import QWidget, QTableWidgetItem, QListWidgetItem, QTreeVie
from manuskript.functions import mainWindow, writablePath
from manuskript.ui.exporters.manuskript.plainTextSettings_ui import Ui_exporterSettings
+from manuskript.ui import style as S
class exporterSettings(QWidget, Ui_exporterSettings):
def __init__(self, _format, parent=None):
QWidget.__init__(self, parent)
self.setupUi(self)
+ self.toolBox.setStyleSheet(S.toolBoxSS())
self.mw = mainWindow()
self._format = _format
diff --git a/manuskript/ui/exporters/manuskript/plainTextSettings_ui.py b/manuskript/ui/exporters/manuskript/plainTextSettings_ui.py
index 77598ba..e8f36c7 100644
--- a/manuskript/ui/exporters/manuskript/plainTextSettings_ui.py
+++ b/manuskript/ui/exporters/manuskript/plainTextSettings_ui.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/exporters/manuskript/plainTextSettings_ui.ui'
#
-# Created by: PyQt5 UI code generator 5.4.2
+# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
@@ -17,20 +17,12 @@ class Ui_exporterSettings(object):
self.verticalLayout_2.setSpacing(10)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.toolBox = QtWidgets.QToolBox(exporterSettings)
- self.toolBox.setStyleSheet("QToolBox::tab{\n"
-" background-color: #BBB;\n"
-" padding: 2px;\n"
-" border: none;\n"
-"}\n"
-"\n"
-"QToolBox::tab:selected, QToolBox::tab:hover{\n"
-" background-color:skyblue;\n"
-"}")
self.toolBox.setObjectName("toolBox")
self.content = QtWidgets.QWidget()
- self.content.setGeometry(QtCore.QRect(0, 0, 491, 842))
+ self.content.setGeometry(QtCore.QRect(0, 0, 349, 842))
self.content.setObjectName("content")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.content)
+ self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.label = QtWidgets.QLabel(self.content)
self.label.setObjectName("label")
@@ -111,9 +103,10 @@ class Ui_exporterSettings(object):
self.verticalLayout_5.addItem(spacerItem1)
self.toolBox.addItem(self.content, "")
self.separations = QtWidgets.QWidget()
- self.separations.setGeometry(QtCore.QRect(0, 0, 511, 522))
+ self.separations.setGeometry(QtCore.QRect(0, 0, 173, 336))
self.separations.setObjectName("separations")
self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.separations)
+ self.verticalLayout_8.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_8.setObjectName("verticalLayout_8")
self.label_3 = QtWidgets.QLabel(self.separations)
font = QtGui.QFont()
@@ -323,6 +316,7 @@ class Ui_exporterSettings(object):
self.transformations.setStyleSheet("QGroupBox{font-weight:bold;}")
self.transformations.setObjectName("transformations")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.transformations)
+ self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.grpTransTypo = collapsibleGroupBox2(self.transformations)
self.grpTransTypo.setStyleSheet("")
@@ -481,10 +475,11 @@ class Ui_exporterSettings(object):
self.verticalLayout_6.addItem(spacerItem10)
self.toolBox.addItem(self.transformations, "")
self.preview = QtWidgets.QWidget()
- self.preview.setGeometry(QtCore.QRect(0, 0, 511, 522))
+ self.preview.setGeometry(QtCore.QRect(0, 0, 369, 130))
self.preview.setStyleSheet("QGroupBox{font-weight:bold;}")
self.preview.setObjectName("preview")
self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.preview)
+ self.verticalLayout_11.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_11.setObjectName("verticalLayout_11")
self.groupBox = QtWidgets.QGroupBox(self.preview)
self.groupBox.setObjectName("groupBox")
diff --git a/manuskript/ui/exporters/manuskript/plainTextSettings_ui.ui b/manuskript/ui/exporters/manuskript/plainTextSettings_ui.ui
index 8064947..1334879 100644
--- a/manuskript/ui/exporters/manuskript/plainTextSettings_ui.ui
+++ b/manuskript/ui/exporters/manuskript/plainTextSettings_ui.ui
@@ -31,17 +31,6 @@
-
- QToolBox::tab{
- background-color: #BBB;
- padding: 2px;
- border: none;
-}
-
-QToolBox::tab:selected, QToolBox::tab:hover{
- background-color:skyblue;
-}
- 2
@@ -53,7 +42,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
00
- 491
+ 349842
@@ -232,8 +221,8 @@ QToolBox::tab:selected, QToolBox::tab:hover{
00
- 511
- 522
+ 173
+ 336
@@ -295,8 +284,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -332,8 +320,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -423,8 +410,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -460,8 +446,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -551,8 +536,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -588,8 +572,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -679,8 +662,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -716,8 +698,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -1113,8 +1094,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -1128,8 +1108,7 @@ QToolBox::tab:selected, QToolBox::tab:hover{
-
-
+ ..
true
@@ -1161,8 +1140,8 @@ QToolBox::tab:selected, QToolBox::tab:hover{
00
- 511
- 522
+ 369
+ 130
diff --git a/manuskript/ui/importers/__init__.py b/manuskript/ui/importers/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/manuskript/ui/importers/generalSettings.py b/manuskript/ui/importers/generalSettings.py
new file mode 100644
index 0000000..cd94d2f
--- /dev/null
+++ b/manuskript/ui/importers/generalSettings.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import json
+import os
+
+from PyQt5.QtCore import Qt, QSize, QSortFilterProxyModel, QModelIndex
+from PyQt5.QtGui import QIcon, QFontMetrics, QFont
+from PyQt5.QtWidgets import QWidget, QTableWidgetItem, QListWidgetItem, QTreeView
+
+from manuskript.functions import mainWindow, writablePath
+from manuskript.ui.importers.generalSettings_ui import Ui_generalSettings
+from manuskript.enums import Outline
+from manuskript.ui import style
+
+
+class generalSettings(QWidget, Ui_generalSettings):
+ def __init__(self, parent=None):
+ QWidget.__init__(self, parent)
+ self.setupUi(self)
+ self.toolBox.setStyleSheet(style.toolBoxSS())
+
+ self.mw = mainWindow()
+ self.txtGeneralSplitScenes.setStyleSheet(style.lineEditSS())
+
+ # TreeView to select parent
+ # We use a proxy to display only folders
+ proxy = QSortFilterProxyModel()
+ proxy.setFilterKeyColumn(Outline.type.value)
+ proxy.setFilterFixedString("folder")
+ proxy.setSourceModel(self.mw.mdlOutline)
+ self.treeGeneralParent.setModel(proxy)
+ for i in range(1, self.mw.mdlOutline.columnCount()):
+ self.treeGeneralParent.hideColumn(i)
+ self.treeGeneralParent.setCurrentIndex(self.getParentIndex())
+ self.chkGeneralParent.toggled.connect(self.treeGeneralParent.setVisible)
+ self.treeGeneralParent.hide()
+
+ def getParentIndex(self):
+ """
+ Returns the currently selected index in the mainWindow.
+ """
+ if len(self.mw.treeRedacOutline.selectionModel().
+ selection().indexes()) == 0:
+ idx = QModelIndex()
+ else:
+ idx = self.mw.treeRedacOutline.currentIndex()
+ return idx
+
+ def importUnderID(self):
+ """
+ Returns the ID of the item selected in treeGeneralParent, if checked.
+ """
+ if self.chkGeneralParent.isChecked():
+ idx = self.treeGeneralParent.currentIndex()
+ # We used a filter proxy model, so we have to map back to source
+ # to get an index from mdlOutline
+ idx = self.treeGeneralParent.model().mapToSource(idx)
+ if idx.isValid():
+ return idx.internalPointer().ID()
+
+ return "0" # 0 is root's ID
+
+ def importInTopLevelFolder(self):
+ """
+ Should the import be flat in the parent folder, or create a top-level
+ folder?
+ """
+ return self.chkGeneralTopLevel.isChecked()
+
+ def trimLongTitles(self):
+ return self.chkGeneralTrimTitles.isChecked()
+
+ def splitScenes(self):
+ """
+ Return wheter the user wants to split scenes.
+ If unchecked, returns False.
+ If checked, returns the escaped split mark, or default (in placeholderText).
+ """
+ if self.chkGeneralSplitScenes.isChecked():
+ split = self.txtGeneralSplitScenes.text()
+
+ if not split:
+ split = self.txtGeneralSplitScenes.placeholderText()
+
+ split = split.replace("\\n", "\n")
+ split = split.replace("\\t", "\t")
+ return split
+
+ else:
+ return False
+
diff --git a/manuskript/ui/importers/generalSettings_ui.py b/manuskript/ui/importers/generalSettings_ui.py
new file mode 100644
index 0000000..d4bb2c5
--- /dev/null
+++ b/manuskript/ui/importers/generalSettings_ui.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'manuskript/ui/importers/generalSettings_ui.ui'
+#
+# Created by: PyQt5 UI code generator 5.9
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_generalSettings(object):
+ def setupUi(self, generalSettings):
+ generalSettings.setObjectName("generalSettings")
+ generalSettings.resize(267, 401)
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(generalSettings)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setSpacing(10)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.toolBox = QtWidgets.QToolBox(generalSettings)
+ self.toolBox.setObjectName("toolBox")
+ self.general = QtWidgets.QWidget()
+ self.general.setGeometry(QtCore.QRect(0, 0, 267, 375))
+ self.general.setObjectName("general")
+ self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.general)
+ self.verticalLayout_5.setContentsMargins(6, 6, 6, 6)
+ self.verticalLayout_5.setObjectName("verticalLayout_5")
+ self.formLayout_4 = QtWidgets.QFormLayout()
+ self.formLayout_4.setRowWrapPolicy(QtWidgets.QFormLayout.WrapLongRows)
+ self.formLayout_4.setObjectName("formLayout_4")
+ self.chkGeneralSplitScenes = QtWidgets.QCheckBox(self.general)
+ self.chkGeneralSplitScenes.setObjectName("chkGeneralSplitScenes")
+ self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.chkGeneralSplitScenes)
+ self.txtGeneralSplitScenes = QtWidgets.QLineEdit(self.general)
+ self.txtGeneralSplitScenes.setText("")
+ self.txtGeneralSplitScenes.setObjectName("txtGeneralSplitScenes")
+ self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.txtGeneralSplitScenes)
+ self.chkGeneralTrimTitles = QtWidgets.QCheckBox(self.general)
+ self.chkGeneralTrimTitles.setObjectName("chkGeneralTrimTitles")
+ self.formLayout_4.setWidget(4, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralTrimTitles)
+ self.treeGeneralParent = QtWidgets.QTreeView(self.general)
+ self.treeGeneralParent.setHeaderHidden(True)
+ self.treeGeneralParent.setObjectName("treeGeneralParent")
+ self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.treeGeneralParent)
+ self.chkGeneralParent = QtWidgets.QCheckBox(self.general)
+ self.chkGeneralParent.setObjectName("chkGeneralParent")
+ self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralParent)
+ self.chkGeneralTopLevel = QtWidgets.QCheckBox(self.general)
+ self.chkGeneralTopLevel.setObjectName("chkGeneralTopLevel")
+ self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.SpanningRole, self.chkGeneralTopLevel)
+ self.verticalLayout_5.addLayout(self.formLayout_4)
+ self.toolBox.addItem(self.general, "")
+ self.verticalLayout_2.addWidget(self.toolBox)
+
+ self.retranslateUi(generalSettings)
+ self.toolBox.setCurrentIndex(0)
+ self.toolBox.layout().setSpacing(0)
+ QtCore.QMetaObject.connectSlotsByName(generalSettings)
+
+ def retranslateUi(self, generalSettings):
+ _translate = QtCore.QCoreApplication.translate
+ generalSettings.setWindowTitle(_translate("generalSettings", "Form"))
+ self.chkGeneralSplitScenes.setText(_translate("generalSettings", "Split scenes at:"))
+ self.txtGeneralSplitScenes.setPlaceholderText(_translate("generalSettings", "\\n---\\n"))
+ self.chkGeneralTrimTitles.setText(_translate("generalSettings", "Trim long titles (> 32 chars)"))
+ self.chkGeneralParent.setText(_translate("generalSettings", "Import under:"))
+ self.chkGeneralTopLevel.setText(_translate("generalSettings", "Import in a top-level folder"))
+ self.toolBox.setItemText(self.toolBox.indexOf(self.general), _translate("generalSettings", "General"))
+
diff --git a/manuskript/ui/importers/generalSettings_ui.ui b/manuskript/ui/importers/generalSettings_ui.ui
new file mode 100644
index 0000000..8bb2f71
--- /dev/null
+++ b/manuskript/ui/importers/generalSettings_ui.ui
@@ -0,0 +1,125 @@
+
+
+ generalSettings
+
+
+
+ 0
+ 0
+ 267
+ 401
+
+
+
+ Form
+
+
+
+ 10
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+
+
+ 0
+ 0
+ 267
+ 375
+
+
+
+ General
+
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+ 6
+
+
+
+
+ QFormLayout::WrapLongRows
+
+
+
+
+ Split scenes at:
+
+
+
+
+
+
+
+
+
+ \n---\n
+
+
+
+
+
+
+ Trim long titles (> 32 chars)
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+ Import under:
+
+
+
+
+
+
+ Import in a top-level folder
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/manuskript/ui/importers/importer.py b/manuskript/ui/importers/importer.py
new file mode 100644
index 0000000..2068451
--- /dev/null
+++ b/manuskript/ui/importers/importer.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+import json
+import os
+
+from PyQt5.QtCore import Qt, QTimer
+from PyQt5.QtGui import QBrush, QColor, QIcon
+from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox, QStyle
+
+from manuskript.functions import writablePath, appPath, openURL
+from manuskript.ui.importers.importer_ui import Ui_importer
+from manuskript.ui.importers.generalSettings import generalSettings
+from manuskript.ui import style
+from manuskript import importer
+from manuskript.models.outlineModel import outlineModel, outlineItem
+from manuskript.enums import Outline
+from manuskript.exporter.pandoc import pandocExporter
+
+class importerDialog(QWidget, Ui_importer):
+
+ formatsIcon = {
+ ".epub": "application-epub+zip",
+ ".odt": "application-vnd.oasis.opendocument.text",
+ ".docx": "application-vnd.openxmlformats-officedocument.wordprocessingml.document",
+ ".md": "text-x-markdown",
+ ".rst": "text-plain",
+ ".tex": "text-x-tex",
+ ".opml": "text-x-opml+xml",
+ ".xml": "text-x-opml+xml",
+ ".html": "text-html",
+ }
+
+ def __init__(self, parent=None, mw=None):
+ QWidget.__init__(self, parent)
+ self.setupUi(self)
+
+ # Var
+ self.mw = mw
+ self.fileName = ""
+ self.setStyleSheet(style.mainWindowSS())
+ self.tree.setStyleSheet("QTreeView{background:transparent;}")
+ self.editor.setStyleSheet("QWidget{background:transparent;}")
+ self.editor.toggleSpellcheck(False)
+
+ # Register importFormats:
+ self.importers = importer.importers
+
+ # Populate combo box with formats
+ self.populateImportList()
+
+ # Connections
+ self.btnChoseFile.clicked.connect(self.selectFile)
+ self.btnClearFileName.clicked.connect(self.setFileName)
+ self.btnPreview.clicked.connect(self.preview)
+ self.btnImport.clicked.connect(self.doImport)
+ self.cmbImporters.currentTextChanged.connect(self.updateSettings)
+
+ self.setFileName("")
+ self.updateSettings()
+
+ ############################################################################
+ # Combobox / Formats
+ ############################################################################
+
+ def populateImportList(self):
+
+ def addFormat(name, icon, identifier):
+ # Identifier serves to distingues 2 importers that would have the
+ # same name.
+ self.cmbImporters.addItem(QIcon.fromTheme(icon), name, identifier)
+
+ def addHeader(name):
+ self.cmbImporters.addItem(name, "header")
+ self.cmbImporters.setItemData(self.cmbImporters.count() - 1, QBrush(QColor(style.highlightedTextDark)), Qt.ForegroundRole)
+ self.cmbImporters.setItemData(self.cmbImporters.count() - 1, QBrush(QColor(style.highlightLight)), Qt.BackgroundRole)
+ item = self.cmbImporters.model().item(self.cmbImporters.count() - 1)
+ item.setFlags(Qt.ItemIsEnabled)
+
+ lastEngine = ""
+
+ for f in self.importers:
+ # Header
+ if f.engine != lastEngine:
+ addHeader(f.engine)
+ lastEngine = f.engine
+
+ addFormat(f.name, f.icon, "{}:{}".format(f.engine, f.name))
+ if not f.isValid():
+ item = self.cmbImporters.model().item(self.cmbImporters.count() - 1)
+ item.setFlags(Qt.NoItemFlags)
+
+ if not pandocExporter().isValid():
+ self.cmbImporters.addItem(
+ self.style().standardIcon(QStyle.SP_MessageBoxWarning),
+ "Install pandoc to import from much more formats",
+ "::URL::http://pandoc.org/installing.html")
+
+ self.cmbImporters.setCurrentIndex(1)
+
+ def currentFormat(self):
+ formatIdentifier = self.cmbImporters.currentData()
+
+ if formatIdentifier == "header":
+ return None
+
+ F = [F for F in self.importers
+ if formatIdentifier == "{}:{}".format(F.engine, F.name)][0]
+ # We instantiate the class
+ return F()
+
+ ############################################################################
+ # Import file
+ ############################################################################
+
+ def selectFile(self):
+ """
+ Called to select a file in the file system. Uses QFileDialog.
+ """
+
+ # We find the current selected format
+ F = self._format
+
+ options = QFileDialog.Options()
+ options |= QFileDialog.DontUseNativeDialog
+ if F.fileFormat == "<>":
+ options = QFileDialog.DontUseNativeDialog | QFileDialog.ShowDirsOnly
+ fileName = QFileDialog.getExistingDirectory(self, "Select import folder",
+ "", options=options)
+ else:
+ fileName, _ = QFileDialog.getOpenFileName(self, "Import from file", "",
+ F.fileFormat, options=options)
+ self.setFileName(fileName)
+ self.preview()
+
+ def setFileName(self, fileName):
+ """
+ Updates Ui with given filename. Filename can be empty.
+ """
+ if fileName:
+ self.fileName = fileName
+ self.lblFileName.setText(os.path.basename(fileName))
+ self.lblFileName.setToolTip(fileName)
+ ext = os.path.splitext(fileName)[1]
+ icon = None
+ if ext and ext in self.formatsIcon:
+ icon = QIcon.fromTheme(self.formatsIcon[ext])
+ elif os.path.isdir(fileName):
+ icon = QIcon.fromTheme("folder")
+
+ if icon:
+ self.lblIcon.setVisible(True)
+ h = self.lblFileName.height()
+ self.lblIcon.setPixmap(icon.pixmap(h, h))
+ else:
+ self.lblIcon.hide()
+
+ else:
+ self.fileName = None
+ self.lblFileName.setText("")
+
+ hasFile = True if fileName else False
+
+ self.btnClearFileName.setVisible(hasFile)
+ self.lblIcon.setVisible(hasFile)
+ self.btnChoseFile.setVisible(not hasFile)
+ self.btnPreview.setEnabled(hasFile)
+ self.btnImport.setEnabled(hasFile)
+
+ ############################################################################
+ # UI
+ ############################################################################
+
+ def updateSettings(self):
+ """
+ When the current format change (through the combobox), we update the
+ settings widget using the current format provided settings widget.
+ """
+
+ # We check if we have to open an URL
+ data = self.cmbImporters.currentData()
+ if data and data[:7] == "::URL::" and data[7:]:
+ # FIXME: use functions.openURL after merge with feature/Exporters
+ openURL(data[7:])
+ return
+
+ F = self.currentFormat()
+ self._format = F
+
+ # Checking if we have a valid importer (otherwise a header)
+ if not F:
+ self.grpSettings.setEnabled(False)
+ self.grpPreview.setEnabled(False)
+ return
+ self.grpSettings.setEnabled(True)
+ self.grpPreview.setEnabled(True)
+
+ self.settingsWidget = generalSettings()
+ #TODO: custom format widget
+ self.settingsWidget = F.settingsWidget(self.settingsWidget)
+
+ # Set the settings widget in place
+ self.setGroupWidget(self.grpSettings, self.settingsWidget)
+ self.grpSettings.setMinimumWidth(200)
+
+ # Clear file name
+ self.setFileName("")
+
+ def setGroupWidget(self, group, widget):
+ """
+ Sets the given widget as main widget for QGroupBox group.
+ """
+
+ # Removes every items from given layout.
+ l = group.layout()
+ while l.count():
+ item = l.itemAt(0)
+ l.removeItem(item)
+ item.widget().deleteLater()
+
+ l.addWidget(widget)
+ widget.setParent(group)
+
+ ############################################################################
+ # Preview / Import
+ ############################################################################
+
+ def preview(self):
+
+ if not self.fileName:
+ return
+
+ # Creating a temporary outlineModel
+ previewModel = outlineModel(self)
+ previewModel.loadFromXML(
+ self.mw.mdlOutline.saveToXML(),
+ fromString=True)
+
+ # Inserting elements
+ result = self.startImport(previewModel)
+
+ if result:
+ self.tree.setModel(previewModel)
+ for i in range(1, previewModel.columnCount()):
+ self.tree.hideColumn(i)
+ self.tree.selectionModel().currentChanged.connect(self.editor.setCurrentModelIndex)
+ self.previewSplitter.setStretchFactor(0, 10)
+ self.previewSplitter.setStretchFactor(1, 40)
+
+ def doImport(self):
+ """
+ Called by the Import button.
+ """
+ self.startImport(self.mw.mdlOutline)
+
+ # Signal every views that important model changes have happened.
+ self.mw.mdlOutline.layoutChanged.emit()
+
+ # I'm getting seg fault over this message sometimes...
+ # Using status bar message instead...
+ #QMessageBox.information(self, self.tr("Import status"),
+ #self.tr("Import Complete."))
+ self.mw.statusBar().showMessage("Import complete!", 5000)
+
+ self.close()
+
+ def startImport(self, outlineModel):
+ """
+ Where most of the magic happens.
+ Is used by preview and by doImport (actual import).
+
+ `outlineModel` is the model where the imported items are added.
+
+ FIXME: Optimisation: when adding many outlineItems, outlineItem.updateWordCount
+ is a bottleneck. It gets called a crazy number of time, and its not
+ necessary.
+ """
+
+ items = []
+
+ # We find the current selected format
+ F = self._format
+
+ # Parent item
+ ID = self.settingsWidget.importUnderID()
+ parentItem = outlineModel.getItemByID(ID)
+
+ # Import in top-level folder?
+ if self.settingsWidget.importInTopLevelFolder():
+ parent = outlineItem(title=os.path.basename(self.fileName),
+ parent=parentItem)
+ parentItem = parent
+ items.append(parent)
+
+ # Calling the importer
+ rItems = F.startImport(self.fileName,
+ parentItem,
+ self.settingsWidget)
+
+ items.extend(rItems)
+
+ # Do transformations
+ items = self.doTransformations(items)
+
+ return True
+
+ def doTransformations(self, items):
+ """
+ Do general transformations.
+ """
+
+ # Trim long titles
+ if self.settingsWidget.trimLongTitles():
+ for item in items:
+ if len(item.title()) > 32:
+ item.setData(Outline.title.value, item.title()[:32])
+
+ # Split at
+ if self.settingsWidget.splitScenes():
+ for item in items:
+ item.split(self.settingsWidget.splitScenes(), recursive=False)
+
+ return items
+
+
diff --git a/manuskript/ui/importers/importer_ui.py b/manuskript/ui/importers/importer_ui.py
new file mode 100644
index 0000000..d23c8e4
--- /dev/null
+++ b/manuskript/ui/importers/importer_ui.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'manuskript/ui/importers/importer_ui.ui'
+#
+# Created by: PyQt5 UI code generator 5.9
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_importer(object):
+ def setupUi(self, importer):
+ importer.setObjectName("importer")
+ importer.resize(867, 560)
+ self.verticalLayout = QtWidgets.QVBoxLayout(importer)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(importer)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.cmbImporters = QtWidgets.QComboBox(importer)
+ self.cmbImporters.setObjectName("cmbImporters")
+ self.horizontalLayout.addWidget(self.cmbImporters)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem)
+ self.btnChoseFile = QtWidgets.QPushButton(importer)
+ icon = QtGui.QIcon.fromTheme("document-import")
+ self.btnChoseFile.setIcon(icon)
+ self.btnChoseFile.setObjectName("btnChoseFile")
+ self.horizontalLayout.addWidget(self.btnChoseFile)
+ self.lblIcon = QtWidgets.QLabel(importer)
+ self.lblIcon.setText("")
+ self.lblIcon.setObjectName("lblIcon")
+ self.horizontalLayout.addWidget(self.lblIcon)
+ self.lblFileName = QtWidgets.QLabel(importer)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.lblFileName.setFont(font)
+ self.lblFileName.setText("")
+ self.lblFileName.setObjectName("lblFileName")
+ self.horizontalLayout.addWidget(self.lblFileName)
+ self.btnClearFileName = QtWidgets.QPushButton(importer)
+ self.btnClearFileName.setText("")
+ icon = QtGui.QIcon.fromTheme("edit-clear")
+ self.btnClearFileName.setIcon(icon)
+ self.btnClearFileName.setFlat(True)
+ self.btnClearFileName.setObjectName("btnClearFileName")
+ self.horizontalLayout.addWidget(self.btnClearFileName)
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout.addItem(spacerItem1)
+ self.btnPreview = QtWidgets.QPushButton(importer)
+ icon = QtGui.QIcon.fromTheme("document-print-preview")
+ self.btnPreview.setIcon(icon)
+ self.btnPreview.setFlat(True)
+ self.btnPreview.setObjectName("btnPreview")
+ self.horizontalLayout.addWidget(self.btnPreview)
+ self.btnImport = QtWidgets.QPushButton(importer)
+ icon = QtGui.QIcon.fromTheme("document-import")
+ self.btnImport.setIcon(icon)
+ self.btnImport.setObjectName("btnImport")
+ self.horizontalLayout.addWidget(self.btnImport)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.splitter = QtWidgets.QSplitter(importer)
+ self.splitter.setOrientation(QtCore.Qt.Horizontal)
+ self.splitter.setChildrenCollapsible(False)
+ self.splitter.setObjectName("splitter")
+ self.grpSettings = QtWidgets.QGroupBox(self.splitter)
+ self.grpSettings.setObjectName("grpSettings")
+ self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.grpSettings)
+ self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_3.setSpacing(0)
+ self.verticalLayout_3.setObjectName("verticalLayout_3")
+ self.grpPreview = QtWidgets.QGroupBox(self.splitter)
+ self.grpPreview.setObjectName("grpPreview")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.grpPreview)
+ self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.previewSplitter = QtWidgets.QSplitter(self.grpPreview)
+ self.previewSplitter.setOrientation(QtCore.Qt.Horizontal)
+ self.previewSplitter.setObjectName("previewSplitter")
+ self.tree = QtWidgets.QTreeView(self.previewSplitter)
+ self.tree.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
+ self.tree.setHeaderHidden(True)
+ self.tree.setObjectName("tree")
+ self.editor = editorWidget(self.previewSplitter)
+ self.editor.setObjectName("editor")
+ self.verticalLayout_2.addWidget(self.previewSplitter)
+ self.verticalLayout.addWidget(self.splitter)
+
+ self.retranslateUi(importer)
+ QtCore.QMetaObject.connectSlotsByName(importer)
+
+ def retranslateUi(self, importer):
+ _translate = QtCore.QCoreApplication.translate
+ importer.setWindowTitle(_translate("importer", "Import"))
+ self.label.setText(_translate("importer", "Format:"))
+ self.btnChoseFile.setText(_translate("importer", "Chose file"))
+ self.btnClearFileName.setToolTip(_translate("importer", "Clear file"))
+ self.btnPreview.setText(_translate("importer", "Preview"))
+ self.btnImport.setText(_translate("importer", "Import"))
+ self.grpSettings.setTitle(_translate("importer", "Settings"))
+ self.grpPreview.setTitle(_translate("importer", "Preview"))
+
+from manuskript.ui.editors.editorWidget import editorWidget
diff --git a/manuskript/ui/importers/importer_ui.ui b/manuskript/ui/importers/importer_ui.ui
new file mode 100644
index 0000000..cbf72b9
--- /dev/null
+++ b/manuskript/ui/importers/importer_ui.ui
@@ -0,0 +1,210 @@
+
+
+ importer
+
+
+
+ 0
+ 0
+ 867
+ 560
+
+
+
+ Import
+
+
+
+
+
+
+
+ Format:
+
+
+
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ Chose file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 75
+ true
+
+
+
+
+
+
+
+
+
+
+ Clear file
+
+
+
+
+
+
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
+
+
+ true
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+ Preview
+
+
+
+ ../../../../../../../../../../.designer/backup../../../../../../../../../../.designer/backup
+
+
+ true
+
+
+
+
+
+
+ Import
+
+
+
+
+
+
+
+
+
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+
+ Settings
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+ Preview
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+
+
+ Qt::Horizontal
+
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ editorWidget
+ QWidget
+ manuskript.ui.editors.editorWidget.h
+ 1
+
+
+
+
+
diff --git a/manuskript/ui/mainWindow.py b/manuskript/ui/mainWindow.py
index 7323796..775dca6 100644
--- a/manuskript/ui/mainWindow.py
+++ b/manuskript/ui/mainWindow.py
@@ -383,7 +383,7 @@ class Ui_MainWindow(object):
self.scrollAreaPersoInfos.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
self.scrollAreaPersoInfos.setObjectName("scrollAreaPersoInfos")
self.scrollAreaPersoInfosWidget = QtWidgets.QWidget()
- self.scrollAreaPersoInfosWidget.setGeometry(QtCore.QRect(0, 0, 444, 709))
+ self.scrollAreaPersoInfosWidget.setGeometry(QtCore.QRect(0, 0, 426, 688))
self.scrollAreaPersoInfosWidget.setObjectName("scrollAreaPersoInfosWidget")
self.formLayout_8 = QtWidgets.QFormLayout(self.scrollAreaPersoInfosWidget)
self.formLayout_8.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
@@ -1036,7 +1036,7 @@ class Ui_MainWindow(object):
self.horizontalLayout_2.addWidget(self.stack)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
- self.menubar.setGeometry(QtCore.QRect(0, 0, 1112, 20))
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1112, 30))
self.menubar.setObjectName("menubar")
self.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
@@ -1054,6 +1054,8 @@ class Ui_MainWindow(object):
self.menuView.setObjectName("menuView")
self.menuMode = QtWidgets.QMenu(self.menuView)
self.menuMode.setObjectName("menuMode")
+ self.menuDocuments = QtWidgets.QMenu(self.menubar)
+ self.menuDocuments.setObjectName("menuDocuments")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
@@ -1099,21 +1101,6 @@ class Ui_MainWindow(object):
self.verticalLayout_16.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_16.setObjectName("verticalLayout_16")
self.lstTabs = QtWidgets.QListWidget(self.dockWidgetContents)
- self.lstTabs.setStyleSheet("QListView {\n"
-" show-decoration-selected: 0;\n"
-" outline: none;\n"
-" background-color: transparent;\n"
-"}\n"
-"\n"
-"QListView::item:selected {\n"
-" background: #DCDEF1;\n"
-" color: black;\n"
-"}\n"
-"\n"
-"QListView::item:hover {\n"
-" background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,\n"
-" stop: 0 #FAFBFE, stop: 1 #DCDEF1);\n"
-"}")
self.lstTabs.setFrameShape(QtWidgets.QFrame.NoFrame)
self.lstTabs.setObjectName("lstTabs")
self.verticalLayout_16.addWidget(self.lstTabs)
@@ -1189,12 +1176,57 @@ class Ui_MainWindow(object):
icon = QtGui.QIcon.fromTheme("stock_view-details")
self.actAbout.setIcon(icon)
self.actAbout.setObjectName("actAbout")
+ self.actImport = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("document-import")
+ self.actImport.setIcon(icon)
+ self.actImport.setObjectName("actImport")
+ self.actCopy = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("edit-copy")
+ self.actCopy.setIcon(icon)
+ self.actCopy.setObjectName("actCopy")
+ self.actCut = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("edit-cut")
+ self.actCut.setIcon(icon)
+ self.actCut.setObjectName("actCut")
+ self.actPaste = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("edit-paste")
+ self.actPaste.setIcon(icon)
+ self.actPaste.setObjectName("actPaste")
+ self.actSplitDialog = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("split")
+ self.actSplitDialog.setIcon(icon)
+ self.actSplitDialog.setObjectName("actSplitDialog")
+ self.actSplitCursor = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("split")
+ self.actSplitCursor.setIcon(icon)
+ self.actSplitCursor.setObjectName("actSplitCursor")
+ self.actMerge = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("merge")
+ self.actMerge.setIcon(icon)
+ self.actMerge.setObjectName("actMerge")
+ self.actDuplicate = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("folder-copy")
+ self.actDuplicate.setIcon(icon)
+ self.actDuplicate.setObjectName("actDuplicate")
+ self.actDelete = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("edit-delete")
+ self.actDelete.setIcon(icon)
+ self.actDelete.setObjectName("actDelete")
+ self.actMoveUp = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("arrow-up")
+ self.actMoveUp.setIcon(icon)
+ self.actMoveUp.setObjectName("actMoveUp")
+ self.actMoveDown = QtWidgets.QAction(MainWindow)
+ icon = QtGui.QIcon.fromTheme("arrow-down")
+ self.actMoveDown.setIcon(icon)
+ self.actMoveDown.setObjectName("actMoveDown")
self.menuFile.addAction(self.actOpen)
self.menuFile.addAction(self.menuRecents.menuAction())
self.menuFile.addAction(self.actSave)
self.menuFile.addAction(self.actSaveAs)
self.menuFile.addAction(self.actCloseProject)
self.menuFile.addSeparator()
+ self.menuFile.addAction(self.actImport)
self.menuFile.addAction(self.actCompile)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actQuit)
@@ -1210,8 +1242,21 @@ class Ui_MainWindow(object):
self.menuMode.addAction(self.actModeSnowflake)
self.menuView.addAction(self.menuMode.menuAction())
self.menuView.addSeparator()
+ self.menuDocuments.addAction(self.actCopy)
+ self.menuDocuments.addAction(self.actCut)
+ self.menuDocuments.addAction(self.actPaste)
+ self.menuDocuments.addAction(self.actDuplicate)
+ self.menuDocuments.addAction(self.actDelete)
+ self.menuDocuments.addSeparator()
+ self.menuDocuments.addAction(self.actMoveUp)
+ self.menuDocuments.addAction(self.actMoveDown)
+ self.menuDocuments.addSeparator()
+ self.menuDocuments.addAction(self.actMerge)
+ self.menuDocuments.addAction(self.actSplitDialog)
+ self.menuDocuments.addAction(self.actSplitCursor)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuEdit.menuAction())
+ self.menubar.addAction(self.menuDocuments.menuAction())
self.menubar.addAction(self.menuView.menuAction())
self.menubar.addAction(self.menuTools.menuAction())
self.menubar.addAction(self.menuHelp.menuAction())
@@ -1329,6 +1374,7 @@ class Ui_MainWindow(object):
self.menuEdit.setTitle(_translate("MainWindow", "&Edit"))
self.menuView.setTitle(_translate("MainWindow", "&View"))
self.menuMode.setTitle(_translate("MainWindow", "&Mode"))
+ self.menuDocuments.setTitle(_translate("MainWindow", "&Documents"))
self.dckCheatSheet.setWindowTitle(_translate("MainWindow", "&Cheat sheet"))
self.dckSearch.setWindowTitle(_translate("MainWindow", "Sea&rch"))
self.dckNavigation.setWindowTitle(_translate("MainWindow", "&Navigation"))
@@ -1360,6 +1406,28 @@ class Ui_MainWindow(object):
self.actToolFrequency.setText(_translate("MainWindow", "&Frequency Analyzer"))
self.actAbout.setText(_translate("MainWindow", "&About"))
self.actAbout.setToolTip(_translate("MainWindow", "About Manuskript"))
+ self.actImport.setText(_translate("MainWindow", "&Import…"))
+ self.actImport.setShortcut(_translate("MainWindow", "F7"))
+ self.actCopy.setText(_translate("MainWindow", "&Copy"))
+ self.actCopy.setShortcut(_translate("MainWindow", "Ctrl+C"))
+ self.actCut.setText(_translate("MainWindow", "C&ut"))
+ self.actCut.setShortcut(_translate("MainWindow", "Ctrl+X"))
+ self.actPaste.setText(_translate("MainWindow", "&Paste"))
+ self.actPaste.setShortcut(_translate("MainWindow", "Ctrl+V"))
+ self.actSplitDialog.setText(_translate("MainWindow", "&Split…"))
+ self.actSplitDialog.setShortcut(_translate("MainWindow", "Ctrl+Shift+K"))
+ self.actSplitCursor.setText(_translate("MainWindow", "Sp&lit at cursor"))
+ self.actSplitCursor.setShortcut(_translate("MainWindow", "Ctrl+K"))
+ self.actMerge.setText(_translate("MainWindow", "Me&rge"))
+ self.actMerge.setShortcut(_translate("MainWindow", "Ctrl+M"))
+ self.actDuplicate.setText(_translate("MainWindow", "&Duplicate"))
+ self.actDuplicate.setShortcut(_translate("MainWindow", "Ctrl+D"))
+ self.actDelete.setText(_translate("MainWindow", "D&elete"))
+ self.actDelete.setShortcut(_translate("MainWindow", "Del"))
+ self.actMoveUp.setText(_translate("MainWindow", "&Move Up"))
+ self.actMoveUp.setShortcut(_translate("MainWindow", "Ctrl+Shift+Up"))
+ self.actMoveDown.setText(_translate("MainWindow", "M&ove Down"))
+ self.actMoveDown.setShortcut(_translate("MainWindow", "Ctrl+Shift+Down"))
from manuskript.ui.cheatSheet import cheatSheet
from manuskript.ui.editors.mainEditor import mainEditor
diff --git a/manuskript/ui/mainWindow.ui b/manuskript/ui/mainWindow.ui
index 05063d7..7a51fa5 100644
--- a/manuskript/ui/mainWindow.ui
+++ b/manuskript/ui/mainWindow.ui
@@ -815,8 +815,8 @@
00
- 444
- 709
+ 426
+ 688
@@ -1601,8 +1601,7 @@
-
-
+ ..
true
@@ -2093,7 +2092,7 @@
001112
- 20
+ 30
@@ -2106,8 +2105,7 @@
-
-
+ ..
@@ -2116,6 +2114,7 @@
+
@@ -2157,8 +2156,26 @@
+
+
+ &Documents
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2258,23 +2275,6 @@
-
- QListView {
- show-decoration-selected: 0;
- outline: none;
- background-color: transparent;
-}
-
-QListView::item:selected {
- background: #DCDEF1;
- color: black;
-}
-
-QListView::item:hover {
- background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
- stop: 0 #FAFBFE, stop: 1 #DCDEF1);
-}
- QFrame::NoFrame
@@ -2370,8 +2370,7 @@ QListView::item:hover {
-
-
+ ..
&Labels...
@@ -2380,8 +2379,7 @@ QListView::item:hover {
-
-
+ ..
&Status...
@@ -2444,8 +2442,7 @@ QListView::item:hover {
-
-
+ ..
&Close project
@@ -2454,8 +2451,7 @@ QListView::item:hover {
-
-
+ ..
Co&mpile
@@ -2472,8 +2468,7 @@ QListView::item:hover {
-
-
+ ..
&About
@@ -2482,6 +2477,138 @@ QListView::item:hover {
About Manuskript
+
+
+
+ ..
+
+
+ &Import…
+
+
+ F7
+
+
+
+
+
+ ..
+
+
+ &Copy
+
+
+ Ctrl+C
+
+
+
+
+
+ ..
+
+
+ C&ut
+
+
+ Ctrl+X
+
+
+
+
+
+ ..
+
+
+ &Paste
+
+
+ Ctrl+V
+
+
+
+
+
+ ..
+
+
+ &Split…
+
+
+ Ctrl+Shift+K
+
+
+
+
+
+ ..
+
+
+ Sp&lit at cursor
+
+
+ Ctrl+K
+
+
+
+
+
+ ..
+
+
+ Me&rge
+
+
+ Ctrl+M
+
+
+
+
+
+ ..
+
+
+ &Duplicate
+
+
+ Ctrl+D
+
+
+
+
+
+ ..
+
+
+ D&elete
+
+
+ Del
+
+
+
+
+
+ ..
+
+
+ &Move Up
+
+
+ Ctrl+Shift+Up
+
+
+
+
+
+ ..
+
+
+ M&ove Down
+
+
+ Ctrl+Shift+Down
+
+
diff --git a/manuskript/ui/settings_ui.py b/manuskript/ui/settings_ui.py
index 62159dd..1fc7ec6 100644
--- a/manuskript/ui/settings_ui.py
+++ b/manuskript/ui/settings_ui.py
@@ -11,7 +11,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Settings(object):
def setupUi(self, Settings):
Settings.setObjectName("Settings")
- Settings.resize(658, 530)
+ Settings.resize(658, 632)
self.horizontalLayout_8 = QtWidgets.QHBoxLayout(Settings)
self.horizontalLayout_8.setObjectName("horizontalLayout_8")
self.lstMenu = QtWidgets.QListWidget(Settings)
@@ -43,13 +43,6 @@ class Ui_Settings(object):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lblTitleGeneral.sizePolicy().hasHeightForWidth())
self.lblTitleGeneral.setSizePolicy(sizePolicy)
- self.lblTitleGeneral.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
self.lblTitleGeneral.setAlignment(QtCore.Qt.AlignCenter)
self.lblTitleGeneral.setObjectName("lblTitleGeneral")
self.verticalLayout_7.addWidget(self.lblTitleGeneral)
@@ -230,13 +223,6 @@ class Ui_Settings(object):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lblTitleGeneral_2.sizePolicy().hasHeightForWidth())
self.lblTitleGeneral_2.setSizePolicy(sizePolicy)
- self.lblTitleGeneral_2.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
self.lblTitleGeneral_2.setAlignment(QtCore.Qt.AlignCenter)
self.lblTitleGeneral_2.setObjectName("lblTitleGeneral_2")
self.verticalLayout.addWidget(self.lblTitleGeneral_2)
@@ -397,13 +383,6 @@ class Ui_Settings(object):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lblTitleViews.sizePolicy().hasHeightForWidth())
self.lblTitleViews.setSizePolicy(sizePolicy)
- self.lblTitleViews.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
self.lblTitleViews.setAlignment(QtCore.Qt.AlignCenter)
self.lblTitleViews.setObjectName("lblTitleViews")
self.verticalLayout_9.addWidget(self.lblTitleViews)
@@ -953,28 +932,53 @@ class Ui_Settings(object):
self.tabViews.addTab(self.tab_3, icon, "")
self.tab_4 = QtWidgets.QWidget()
self.tab_4.setObjectName("tab_4")
- self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.tab_4)
- self.verticalLayout_22.setObjectName("verticalLayout_22")
- self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab_4)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.verticalLayout_21 = QtWidgets.QVBoxLayout()
self.verticalLayout_21.setObjectName("verticalLayout_21")
- self.groupBox_12 = QtWidgets.QGroupBox(self.tab_4)
+ self.groupBox_17 = QtWidgets.QGroupBox(self.tab_4)
font = QtGui.QFont()
font.setBold(True)
font.setWeight(75)
- self.groupBox_12.setFont(font)
- self.groupBox_12.setObjectName("groupBox_12")
- self.formLayout_8 = QtWidgets.QFormLayout(self.groupBox_12)
- self.formLayout_8.setObjectName("formLayout_8")
- self.label_37 = QtWidgets.QLabel(self.groupBox_12)
+ self.groupBox_17.setFont(font)
+ self.groupBox_17.setObjectName("groupBox_17")
+ self.formLayout_12 = QtWidgets.QFormLayout(self.groupBox_17)
+ self.formLayout_12.setObjectName("formLayout_12")
+ self.label_43 = QtWidgets.QLabel(self.groupBox_17)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.label_43.setFont(font)
+ self.label_43.setObjectName("label_43")
+ self.formLayout_12.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_43)
+ self.btnEditorBackgroundColor = QtWidgets.QPushButton(self.groupBox_17)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.btnEditorBackgroundColor.sizePolicy().hasHeightForWidth())
+ self.btnEditorBackgroundColor.setSizePolicy(sizePolicy)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.btnEditorBackgroundColor.setFont(font)
+ self.btnEditorBackgroundColor.setText("")
+ self.btnEditorBackgroundColor.setObjectName("btnEditorBackgroundColor")
+ self.formLayout_12.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.btnEditorBackgroundColor)
+ self.chkEditorBackgroundTransparent = QtWidgets.QCheckBox(self.groupBox_17)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.chkEditorBackgroundTransparent.setFont(font)
+ self.chkEditorBackgroundTransparent.setObjectName("chkEditorBackgroundTransparent")
+ self.formLayout_12.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.chkEditorBackgroundTransparent)
+ self.label_37 = QtWidgets.QLabel(self.groupBox_17)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.label_37.setFont(font)
self.label_37.setObjectName("label_37")
- self.formLayout_8.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_37)
- self.btnEditorFontColor = QtWidgets.QPushButton(self.groupBox_12)
+ self.formLayout_12.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_37)
+ self.btnEditorFontColor = QtWidgets.QPushButton(self.groupBox_17)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
@@ -986,14 +990,30 @@ class Ui_Settings(object):
self.btnEditorFontColor.setFont(font)
self.btnEditorFontColor.setText("")
self.btnEditorFontColor.setObjectName("btnEditorFontColor")
- self.formLayout_8.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.btnEditorFontColor)
+ self.formLayout_12.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.btnEditorFontColor)
+ self.btnEditorColorDefault = QtWidgets.QPushButton(self.groupBox_17)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.btnEditorColorDefault.setFont(font)
+ self.btnEditorColorDefault.setObjectName("btnEditorColorDefault")
+ self.formLayout_12.setWidget(3, QtWidgets.QFormLayout.SpanningRole, self.btnEditorColorDefault)
+ self.verticalLayout_21.addWidget(self.groupBox_17)
+ self.groupBox_12 = QtWidgets.QGroupBox(self.tab_4)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.groupBox_12.setFont(font)
+ self.groupBox_12.setObjectName("groupBox_12")
+ self.formLayout_8 = QtWidgets.QFormLayout(self.groupBox_12)
+ self.formLayout_8.setObjectName("formLayout_8")
self.label_39 = QtWidgets.QLabel(self.groupBox_12)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.label_39.setFont(font)
self.label_39.setObjectName("label_39")
- self.formLayout_8.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_39)
+ self.formLayout_8.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_39)
self.cmbEditorFontFamily = QtWidgets.QFontComboBox(self.groupBox_12)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -1006,21 +1026,21 @@ class Ui_Settings(object):
font.setWeight(50)
self.cmbEditorFontFamily.setFont(font)
self.cmbEditorFontFamily.setObjectName("cmbEditorFontFamily")
- self.formLayout_8.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.cmbEditorFontFamily)
+ self.formLayout_8.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.cmbEditorFontFamily)
self.label_38 = QtWidgets.QLabel(self.groupBox_12)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.label_38.setFont(font)
self.label_38.setObjectName("label_38")
- self.formLayout_8.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_38)
+ self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_38)
self.label_36 = QtWidgets.QLabel(self.groupBox_12)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
self.label_36.setFont(font)
self.label_36.setObjectName("label_36")
- self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_36)
+ self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_36)
self.btnEditorMisspelledColor = QtWidgets.QPushButton(self.groupBox_12)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
@@ -1033,7 +1053,7 @@ class Ui_Settings(object):
self.btnEditorMisspelledColor.setFont(font)
self.btnEditorMisspelledColor.setText("")
self.btnEditorMisspelledColor.setObjectName("btnEditorMisspelledColor")
- self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.btnEditorMisspelledColor)
+ self.formLayout_8.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.btnEditorMisspelledColor)
self.spnEditorFontSize = QtWidgets.QSpinBox(self.groupBox_12)
font = QtGui.QFont()
font.setBold(False)
@@ -1043,61 +1063,8 @@ class Ui_Settings(object):
self.spnEditorFontSize.setMaximum(299)
self.spnEditorFontSize.setProperty("value", 10)
self.spnEditorFontSize.setObjectName("spnEditorFontSize")
- self.formLayout_8.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.spnEditorFontSize)
- self.label_43 = QtWidgets.QLabel(self.groupBox_12)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.label_43.setFont(font)
- self.label_43.setObjectName("label_43")
- self.formLayout_8.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_43)
- self.btnEditorBackgroundColor = QtWidgets.QPushButton(self.groupBox_12)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
- sizePolicy.setHorizontalStretch(0)
- sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.btnEditorBackgroundColor.sizePolicy().hasHeightForWidth())
- self.btnEditorBackgroundColor.setSizePolicy(sizePolicy)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.btnEditorBackgroundColor.setFont(font)
- self.btnEditorBackgroundColor.setText("")
- self.btnEditorBackgroundColor.setObjectName("btnEditorBackgroundColor")
- self.formLayout_8.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.btnEditorBackgroundColor)
+ self.formLayout_8.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.spnEditorFontSize)
self.verticalLayout_21.addWidget(self.groupBox_12)
- self.groupBox_15 = QtWidgets.QGroupBox(self.tab_4)
- font = QtGui.QFont()
- font.setBold(True)
- font.setWeight(75)
- self.groupBox_15.setFont(font)
- self.groupBox_15.setObjectName("groupBox_15")
- self.formLayout_10 = QtWidgets.QFormLayout(self.groupBox_15)
- self.formLayout_10.setObjectName("formLayout_10")
- self.chkEditorCursorWidth = QtWidgets.QCheckBox(self.groupBox_15)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.chkEditorCursorWidth.setFont(font)
- self.chkEditorCursorWidth.setObjectName("chkEditorCursorWidth")
- self.formLayout_10.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.chkEditorCursorWidth)
- self.spnEditorCursorWidth = QtWidgets.QSpinBox(self.groupBox_15)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.spnEditorCursorWidth.setFont(font)
- self.spnEditorCursorWidth.setMinimum(0)
- self.spnEditorCursorWidth.setMaximum(99)
- self.spnEditorCursorWidth.setProperty("value", 9)
- self.spnEditorCursorWidth.setObjectName("spnEditorCursorWidth")
- self.formLayout_10.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.spnEditorCursorWidth)
- self.chkEditorNoBlinking = QtWidgets.QCheckBox(self.groupBox_15)
- font = QtGui.QFont()
- font.setBold(False)
- font.setWeight(50)
- self.chkEditorNoBlinking.setFont(font)
- self.chkEditorNoBlinking.setObjectName("chkEditorNoBlinking")
- self.formLayout_10.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.chkEditorNoBlinking)
- self.verticalLayout_21.addWidget(self.groupBox_15)
self.groupBox_161 = QtWidgets.QGroupBox(self.tab_4)
font = QtGui.QFont()
font.setBold(True)
@@ -1154,6 +1121,8 @@ class Ui_Settings(object):
self.formLayout_11.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.spnEditorMarginsTB)
self.verticalLayout_21.addWidget(self.groupBox_161)
self.horizontalLayout_4.addLayout(self.verticalLayout_21)
+ self.verticalLayout_22 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_22.setObjectName("verticalLayout_22")
self.groupBox_13 = QtWidgets.QGroupBox(self.tab_4)
font = QtGui.QFont()
font.setBold(True)
@@ -1162,6 +1131,28 @@ class Ui_Settings(object):
self.groupBox_13.setObjectName("groupBox_13")
self.formLayout_9 = QtWidgets.QFormLayout(self.groupBox_13)
self.formLayout_9.setObjectName("formLayout_9")
+ self.label_35 = QtWidgets.QLabel(self.groupBox_13)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.label_35.setFont(font)
+ self.label_35.setObjectName("label_35")
+ self.formLayout_9.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_35)
+ self.cmbEditorAlignment = QtWidgets.QComboBox(self.groupBox_13)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.cmbEditorAlignment.setFont(font)
+ self.cmbEditorAlignment.setObjectName("cmbEditorAlignment")
+ icon = QtGui.QIcon.fromTheme("format-justify-left")
+ self.cmbEditorAlignment.addItem(icon, "")
+ icon = QtGui.QIcon.fromTheme("format-justify-center")
+ self.cmbEditorAlignment.addItem(icon, "")
+ icon = QtGui.QIcon.fromTheme("format-justify-right")
+ self.cmbEditorAlignment.addItem(icon, "")
+ icon = QtGui.QIcon.fromTheme("format-justify-fill")
+ self.cmbEditorAlignment.addItem(icon, "")
+ self.formLayout_9.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cmbEditorAlignment)
self.label_40 = QtWidgets.QLabel(self.groupBox_13)
font = QtGui.QFont()
font.setBold(False)
@@ -1267,30 +1258,41 @@ class Ui_Settings(object):
self.spnEditorParaBelow.setProperty("value", 5)
self.spnEditorParaBelow.setObjectName("spnEditorParaBelow")
self.formLayout_9.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.spnEditorParaBelow)
- self.label_35 = QtWidgets.QLabel(self.groupBox_13)
+ self.verticalLayout_22.addWidget(self.groupBox_13)
+ self.groupBox_15 = QtWidgets.QGroupBox(self.tab_4)
+ font = QtGui.QFont()
+ font.setBold(True)
+ font.setWeight(75)
+ self.groupBox_15.setFont(font)
+ self.groupBox_15.setObjectName("groupBox_15")
+ self.formLayout_10 = QtWidgets.QFormLayout(self.groupBox_15)
+ self.formLayout_10.setObjectName("formLayout_10")
+ self.chkEditorCursorWidth = QtWidgets.QCheckBox(self.groupBox_15)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
- self.label_35.setFont(font)
- self.label_35.setObjectName("label_35")
- self.formLayout_9.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_35)
- self.cmbEditorAlignment = QtWidgets.QComboBox(self.groupBox_13)
+ self.chkEditorCursorWidth.setFont(font)
+ self.chkEditorCursorWidth.setObjectName("chkEditorCursorWidth")
+ self.formLayout_10.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.chkEditorCursorWidth)
+ self.spnEditorCursorWidth = QtWidgets.QSpinBox(self.groupBox_15)
font = QtGui.QFont()
font.setBold(False)
font.setWeight(50)
- self.cmbEditorAlignment.setFont(font)
- self.cmbEditorAlignment.setObjectName("cmbEditorAlignment")
- icon = QtGui.QIcon.fromTheme("format-justify-left")
- self.cmbEditorAlignment.addItem(icon, "")
- icon = QtGui.QIcon.fromTheme("format-justify-center")
- self.cmbEditorAlignment.addItem(icon, "")
- icon = QtGui.QIcon.fromTheme("format-justify-right")
- self.cmbEditorAlignment.addItem(icon, "")
- icon = QtGui.QIcon.fromTheme("format-justify-fill")
- self.cmbEditorAlignment.addItem(icon, "")
- self.formLayout_9.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.cmbEditorAlignment)
- self.horizontalLayout_4.addWidget(self.groupBox_13)
- self.verticalLayout_22.addLayout(self.horizontalLayout_4)
+ self.spnEditorCursorWidth.setFont(font)
+ self.spnEditorCursorWidth.setMinimum(0)
+ self.spnEditorCursorWidth.setMaximum(99)
+ self.spnEditorCursorWidth.setProperty("value", 9)
+ self.spnEditorCursorWidth.setObjectName("spnEditorCursorWidth")
+ self.formLayout_10.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.spnEditorCursorWidth)
+ self.chkEditorNoBlinking = QtWidgets.QCheckBox(self.groupBox_15)
+ font = QtGui.QFont()
+ font.setBold(False)
+ font.setWeight(50)
+ self.chkEditorNoBlinking.setFont(font)
+ self.chkEditorNoBlinking.setObjectName("chkEditorNoBlinking")
+ self.formLayout_10.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.chkEditorNoBlinking)
+ self.verticalLayout_22.addWidget(self.groupBox_15)
+ self.horizontalLayout_4.addLayout(self.verticalLayout_22)
icon = QtGui.QIcon.fromTheme("view-text")
self.tabViews.addTab(self.tab_4, icon, "")
self.verticalLayout_9.addWidget(self.tabViews)
@@ -1306,13 +1308,6 @@ class Ui_Settings(object):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lblTitleLabels.sizePolicy().hasHeightForWidth())
self.lblTitleLabels.setSizePolicy(sizePolicy)
- self.lblTitleLabels.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
self.lblTitleLabels.setAlignment(QtCore.Qt.AlignCenter)
self.lblTitleLabels.setObjectName("lblTitleLabels")
self.verticalLayout_3.addWidget(self.lblTitleLabels)
@@ -1369,13 +1364,6 @@ class Ui_Settings(object):
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lblTitleStatus.sizePolicy().hasHeightForWidth())
self.lblTitleStatus.setSizePolicy(sizePolicy)
- self.lblTitleStatus.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
self.lblTitleStatus.setAlignment(QtCore.Qt.AlignCenter)
self.lblTitleStatus.setObjectName("lblTitleStatus")
self.verticalLayout_4.addWidget(self.lblTitleStatus)
@@ -1405,22 +1393,15 @@ class Ui_Settings(object):
self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.page)
self.verticalLayout_10.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_10.setObjectName("verticalLayout_10")
- self.lblTitleStatus_2 = QtWidgets.QLabel(self.page)
+ self.lblTitleFullscreen = QtWidgets.QLabel(self.page)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.lblTitleStatus_2.sizePolicy().hasHeightForWidth())
- self.lblTitleStatus_2.setSizePolicy(sizePolicy)
- self.lblTitleStatus_2.setStyleSheet("background-color:lightBlue;\n"
-"border:none;\n"
-"padding:10px;\n"
-"color:darkBlue;\n"
-"font-size:16px;\n"
-"font-weight:bold;\n"
-"text-align:center;")
- self.lblTitleStatus_2.setAlignment(QtCore.Qt.AlignCenter)
- self.lblTitleStatus_2.setObjectName("lblTitleStatus_2")
- self.verticalLayout_10.addWidget(self.lblTitleStatus_2)
+ sizePolicy.setHeightForWidth(self.lblTitleFullscreen.sizePolicy().hasHeightForWidth())
+ self.lblTitleFullscreen.setSizePolicy(sizePolicy)
+ self.lblTitleFullscreen.setAlignment(QtCore.Qt.AlignCenter)
+ self.lblTitleFullscreen.setObjectName("lblTitleFullscreen")
+ self.verticalLayout_10.addWidget(self.lblTitleFullscreen)
self.themeStack = QtWidgets.QStackedWidget(self.page)
self.themeStack.setObjectName("themeStack")
self.stackedWidgetPage1_3 = QtWidgets.QWidget()
@@ -1797,8 +1778,8 @@ class Ui_Settings(object):
self.horizontalLayout_8.addWidget(self.stack)
self.retranslateUi(Settings)
- self.stack.setCurrentIndex(2)
- self.tabViews.setCurrentIndex(0)
+ self.stack.setCurrentIndex(5)
+ self.tabViews.setCurrentIndex(3)
self.themeStack.setCurrentIndex(1)
self.themeEditStack.setCurrentIndex(3)
self.lstMenu.currentRowChanged['int'].connect(self.stack.setCurrentIndex)
@@ -1873,13 +1854,13 @@ class Ui_Settings(object):
self.lblTreeIconSize.setText(_translate("Settings", "TextLabel"))
self.groupBox_8.setTitle(_translate("Settings", "Folders"))
self.rdoTreeItemCount.setText(_translate("Settings", "Show ite&m count"))
- self.rdoTreeWC.setText(_translate("Settings", "Show wordcount"))
- self.rdoTreeProgress.setText(_translate("Settings", "Show progress"))
- self.rdoTreeSummary.setText(_translate("Settings", "Show summary"))
+ self.rdoTreeWC.setText(_translate("Settings", "Show &wordcount"))
+ self.rdoTreeProgress.setText(_translate("Settings", "S&how progress"))
+ self.rdoTreeSummary.setText(_translate("Settings", "Show summar&y"))
self.rdoTreeNothing.setText(_translate("Settings", "&Nothing"))
self.groupBox_9.setTitle(_translate("Settings", "Text"))
- self.rdoTreeTextWC.setText(_translate("Settings", "Show wordcount"))
- self.rdoTreeTextProgress.setText(_translate("Settings", "Show progress"))
+ self.rdoTreeTextWC.setText(_translate("Settings", "&Show wordcount"))
+ self.rdoTreeTextProgress.setText(_translate("Settings", "Show p&rogress"))
self.rdoTreeTextSummary.setText(_translate("Settings", "Show summary"))
self.rdoTreeTextNothing.setText(_translate("Settings", "Nothing"))
self.tabViews.setTabText(self.tabViews.indexOf(self.tab), _translate("Settings", "Tree"))
@@ -1917,8 +1898,8 @@ class Ui_Settings(object):
self.btnCorkColor.setShortcut(_translate("Settings", "Ctrl+S"))
self.label_16.setText(_translate("Settings", "Image:"))
self.groupBox_11.setTitle(_translate("Settings", "Style"))
- self.rdoCorkOldStyle.setText(_translate("Settings", "Old style"))
- self.rdoCorkNewStyle.setText(_translate("Settings", "New style"))
+ self.rdoCorkOldStyle.setText(_translate("Settings", "Old st&yle"))
+ self.rdoCorkNewStyle.setText(_translate("Settings", "Ne&w style"))
self.groupBox_5.setTitle(_translate("Settings", "Item colors"))
self.label_9.setText(_translate("Settings", "Icon color:"))
self.cmbCorkIcon.setItemText(0, _translate("Settings", "Nothing"))
@@ -1951,16 +1932,15 @@ class Ui_Settings(object):
self.cmbCorkCorner.setItemText(3, _translate("Settings", "Progress"))
self.cmbCorkCorner.setItemText(4, _translate("Settings", "Compile"))
self.tabViews.setTabText(self.tabViews.indexOf(self.tab_3), _translate("Settings", "Index cards"))
- self.groupBox_12.setTitle(_translate("Settings", "Font"))
+ self.groupBox_17.setTitle(_translate("Settings", "Colors"))
+ self.label_43.setText(_translate("Settings", "Background:"))
+ self.chkEditorBackgroundTransparent.setText(_translate("Settings", "Transparent"))
self.label_37.setText(_translate("Settings", "Color:"))
+ self.btnEditorColorDefault.setText(_translate("Settings", "Restore defaults"))
+ self.groupBox_12.setTitle(_translate("Settings", "Font"))
self.label_39.setText(_translate("Settings", "Family:"))
self.label_38.setText(_translate("Settings", "Size:"))
self.label_36.setText(_translate("Settings", "Misspelled:"))
- self.label_43.setText(_translate("Settings", "Background:"))
- self.groupBox_15.setTitle(_translate("Settings", "Cursor"))
- self.chkEditorCursorWidth.setText(_translate("Settings", "Use block insertion of"))
- self.spnEditorCursorWidth.setSuffix(_translate("Settings", " px"))
- self.chkEditorNoBlinking.setText(_translate("Settings", "Disable blinking"))
self.groupBox_161.setTitle(_translate("Settings", "Text area"))
self.chkEditorMaxWidth.setText(_translate("Settings", "Max width"))
self.spnEditorMaxWidth.setSuffix(_translate("Settings", " px"))
@@ -1969,6 +1949,11 @@ class Ui_Settings(object):
self.label_55.setText(_translate("Settings", "Top/Bottom margins:"))
self.spnEditorMarginsTB.setSuffix(_translate("Settings", " px"))
self.groupBox_13.setTitle(_translate("Settings", "Paragraphs"))
+ self.label_35.setText(_translate("Settings", "Alignment:"))
+ self.cmbEditorAlignment.setItemText(0, _translate("Settings", "Left"))
+ self.cmbEditorAlignment.setItemText(1, _translate("Settings", "Center"))
+ self.cmbEditorAlignment.setItemText(2, _translate("Settings", "Right"))
+ self.cmbEditorAlignment.setItemText(3, _translate("Settings", "Justify"))
self.label_40.setText(_translate("Settings", "Line spacing:"))
self.cmbEditorLineSpacing.setItemText(0, _translate("Settings", "Single"))
self.cmbEditorLineSpacing.setItemText(1, _translate("Settings", "1.5 lines"))
@@ -1981,16 +1966,15 @@ class Ui_Settings(object):
self.label_41.setText(_translate("Settings", "Spacing:"))
self.spnEditorParaAbove.setSuffix(_translate("Settings", " px"))
self.spnEditorParaBelow.setSuffix(_translate("Settings", " px"))
- self.label_35.setText(_translate("Settings", "Alignment:"))
- self.cmbEditorAlignment.setItemText(0, _translate("Settings", "Left"))
- self.cmbEditorAlignment.setItemText(1, _translate("Settings", "Center"))
- self.cmbEditorAlignment.setItemText(2, _translate("Settings", "Right"))
- self.cmbEditorAlignment.setItemText(3, _translate("Settings", "Justify"))
+ self.groupBox_15.setTitle(_translate("Settings", "Cursor"))
+ self.chkEditorCursorWidth.setText(_translate("Settings", "Use block insertion of"))
+ self.spnEditorCursorWidth.setSuffix(_translate("Settings", " px"))
+ self.chkEditorNoBlinking.setText(_translate("Settings", "Disable blinking"))
self.tabViews.setTabText(self.tabViews.indexOf(self.tab_4), _translate("Settings", "Text editor"))
self.lblTitleLabels.setText(_translate("Settings", "Labels"))
self.btnLabelColor.setShortcut(_translate("Settings", "Ctrl+S"))
self.lblTitleStatus.setText(_translate("Settings", "Status"))
- self.lblTitleStatus_2.setText(_translate("Settings", "Fullscreen"))
+ self.lblTitleFullscreen.setText(_translate("Settings", "Fullscreen"))
self.btnThemeAdd.setText(_translate("Settings", "New"))
self.btnThemeEdit.setText(_translate("Settings", "Edit"))
self.btnThemeRemove.setText(_translate("Settings", "Delete"))
diff --git a/manuskript/ui/settings_ui.ui b/manuskript/ui/settings_ui.ui
index e22e6f3..194161d 100644
--- a/manuskript/ui/settings_ui.ui
+++ b/manuskript/ui/settings_ui.ui
@@ -7,7 +7,7 @@
00658
- 530
+ 632
@@ -54,7 +54,7 @@
- 2
+ 5
@@ -78,15 +78,6 @@
0
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- General settings
@@ -464,15 +455,6 @@ text-align:center;
0
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- Revisions
@@ -775,15 +757,6 @@ text-align:center;
0
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- Views settings
@@ -795,13 +768,12 @@ text-align:center;
- 0
+ 3
-
-
+ ..
Tree
@@ -1070,7 +1042,7 @@ text-align:center;
- Show wordcount
+ Show &wordcount
@@ -1083,7 +1055,7 @@ text-align:center;
- Show progress
+ S&how progress
@@ -1096,7 +1068,7 @@ text-align:center;
- Show summary
+ Show summar&y
@@ -1140,7 +1112,7 @@ text-align:center;
- Show wordcount
+ &Show wordcount
@@ -1153,7 +1125,7 @@ text-align:center;
- Show progress
+ Show p&rogress
@@ -1225,8 +1197,7 @@ text-align:center;
-
-
+ ..
Outline
@@ -1573,8 +1544,7 @@ text-align:center;
-
-
+ ..
Index cards
@@ -1693,7 +1663,7 @@ text-align:center;
- Old style
+ Old st&yle
@@ -1706,7 +1676,7 @@ text-align:center;
- New style
+ Ne&w style
@@ -1976,365 +1946,342 @@ text-align:center;
-
-
+ ..
Text editor
-
+
-
+
-
-
-
-
-
- 75
- true
-
-
-
- Font
-
-
-
-
-
-
- 50
- false
-
-
-
- Color:
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 50
- false
-
-
-
-
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Family:
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 150
- 16777215
-
-
-
-
- 50
- false
-
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Size:
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Misspelled:
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 50
- false
-
-
-
-
-
-
-
-
-
-
-
- 50
- false
-
-
-
- 4
-
-
- 299
-
-
- 10
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Background:
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 50
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 75
- true
-
-
-
- Cursor
-
-
-
-
-
-
- 50
- false
-
-
-
- Use block insertion of
-
-
-
-
-
-
-
- 50
- false
-
-
-
- px
-
-
- 0
-
-
- 99
-
-
- 9
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Disable blinking
-
-
-
-
-
-
-
-
-
-
- 75
- true
-
-
-
- Text area
-
-
-
-
-
-
- 50
- false
-
-
-
- Max width
-
-
-
-
-
-
-
- 50
- false
-
-
-
- px
-
-
- 4096
-
-
- 500
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Left/Right margins:
-
-
-
-
-
-
-
- 50
- false
-
-
-
- px
-
-
- 2048
-
-
-
-
-
-
-
- 50
- false
-
-
-
- Top/Bottom margins:
-
-
-
-
-
-
-
- 50
- false
-
-
-
- px
-
-
- 2048
-
-
-
-
-
-
-
+
+
+
+ 75
+ true
+
+
+
+ Colors
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Background:
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ false
+
+
+
+
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Transparent
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Color:
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ false
+
+
+
+
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Restore defaults
+
+
+
+
+
+
+
+
+
+ 75
+ true
+
+
+
+ Font
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Family:
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 150
+ 16777215
+
+
+
+
+ 50
+ false
+
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Size:
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Misspelled:
+
+
+
+
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ false
+
+
+
+
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ 4
+
+
+ 299
+
+
+ 10
+
+
+
+
+
+
+
+
+
+
+ 75
+ true
+
+
+
+ Text area
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Max width
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ px
+
+
+ 4096
+
+
+ 500
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Left/Right margins:
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ px
+
+
+ 2048
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Top/Bottom margins:
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ px
+
+
+ 2048
+
+
+
+
+
+
+
+
+
+
@@ -2347,6 +2294,65 @@ text-align:center;
Paragraphs
+
+
+
+
+ 50
+ false
+
+
+
+ Alignment:
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+
+ Left
+
+
+
+ ..
+
+
+
+
+ Center
+
+
+
+ ..
+
+
+
+
+ Right
+
+
+
+ ..
+
+
+
+
+ Justify
+
+
+
+ ..
+
+
+
+
@@ -2550,8 +2556,23 @@ text-align:center;
+
+
+
+
+
+
+
+ 75
+ true
+
+
+
+ Cursor
+
+
-
+ 50
@@ -2559,58 +2580,43 @@ text-align:center;
- Alignment:
+ Use block insertion of
-
+ 50false
-
-
- Left
-
-
-
-
-
-
-
-
-
- Center
-
-
-
-
-
-
-
-
-
- Right
-
-
-
-
-
-
-
-
-
- Justify
-
-
-
-
-
-
-
+
+ px
+
+
+ 0
+
+
+ 99
+
+
+ 9
+
+
+
+
+
+
+
+ 50
+ false
+
+
+
+ Disable blinking
+
@@ -2646,15 +2652,6 @@ text-align:center;
0
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- Labels
@@ -2787,15 +2784,6 @@ text-align:center;
0
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- Status
@@ -2863,22 +2851,13 @@ text-align:center;
0
-
+ 00
-
- background-color:lightBlue;
-border:none;
-padding:10px;
-color:darkBlue;
-font-size:16px;
-font-weight:bold;
-text-align:center;
- Fullscreen
@@ -3597,8 +3576,7 @@ text-align:center;
-
-
+ ..
@@ -3607,8 +3585,7 @@ text-align:center;
-
-
+ ..
@@ -3617,8 +3594,7 @@ text-align:center;
-
-
+ ..
@@ -3627,8 +3603,7 @@ text-align:center;
-
-
+ ..
diff --git a/manuskript/ui/style.py b/manuskript/ui/style.py
index ad5c907..ba1d72f 100644
--- a/manuskript/ui/style.py
+++ b/manuskript/ui/style.py
@@ -8,14 +8,40 @@ from PyQt5.QtGui import QColor, QPalette
from PyQt5.QtWidgets import qApp
from manuskript import settings
+from manuskript import functions as F
+# Loading palette colors.
+# Manuskript as to restart to reload
+p = qApp.palette()
# window = "#d6d2d0" #"#eee" / #eff0f1
-window = qApp.palette().color(QPalette.Window).name()
+window = p.color(QPalette.Window).name() # General background
+windowText = p.color(QPalette.WindowText).name() # General foregroung
+base = p.color(QPalette.Base).name() # Other background
+alternateBase = p.color(QPalette.AlternateBase).name() # Other background
+text = p.color(QPalette.Text).name() # Base Text
+brightText = p.color(QPalette.BrightText).name() # Contrast Text
+button = p.color(QPalette.Button).name() # Button background
+buttonText = p.color(QPalette.ButtonText).name() # Button Text
+highlight = p.color(QPalette.Highlight).name() # Other background
+highlightedText = p.color(QPalette.HighlightedText).name() # Base Text
+
+light = p.color(QPalette.Light).name() # Lighter than Button color
+midlight = p.color(QPalette.Midlight).name() # Between Button and Light
+dark = p.color(QPalette.Dark).name() # Darker than Button
+mid = p.color(QPalette.Mid).name() # Between Button and Dark
+shadow = p.color(QPalette.Shadow).name() # A very dark color
+
+highlightLight = F.mixColors(highlight, window, .3)
+highlightedTextDark = F.mixColors(highlight, text, .3)
+highlightedTextLight = F.mixColors(highlight, highlightedText)
+midlighter = F.mixColors(mid, window, .4)
+textLight = F.mixColors(window, text)
+
+
+#from manuskript.ui import style as S
+#QColor(S.highlightedTextDark)
+#QColor(S.highlightLight)
-bgHover = "#ccc"
-bgChecked = "#bbb"
-borderColor = "darkGray"
-blue = "#268bd2"
def mainWindowSS():
return """
@@ -26,13 +52,13 @@ def mainWindowSS():
border: none;
}}
QPushButton:flat:hover, QToolButton:hover{{
- border: 1px solid {borderColor};
+ border: 1px solid {borderHover};
border-radius: 3px;
- background: {bgHover};
+ background: {backgroundHover};
}}
""".format(
- bgHover=bgHover,
- borderColor=borderColor
+ backgroundHover=highlightLight,
+ borderHover=mid
)
def styleMainWindow(mw):
@@ -40,13 +66,34 @@ def styleMainWindow(mw):
mw.lstTabs.verticalScrollBar().setStyleSheet(simpleScrollBarV())
# Custon palette?
- qApp.setPalette(appPalette())
+ #qApp.setPalette(appPalette())
mw.treeRedacOutline.setStyleSheet("""
QTreeView{
background: transparent;
margin-top: 30px;
- }""")
+ }""" + simpleScrollBarV())
+
+ mw.lstTabs.setStyleSheet("""
+ QListView {{
+ show-decoration-selected: 0;
+ outline: none;
+ background-color: transparent;
+ }}
+
+ QListView::item:selected {{
+ background: {highlight};
+ color: {textSelected}
+ }}
+
+ QListView::item:hover {{
+ background: {hover};
+ }}
+ """.format(
+ hover=highlight,
+ highlight=highlightLight,
+ textSelected=text,
+ ))
def appPalette():
@@ -78,104 +125,126 @@ def appPalette():
def collapsibleGroupBoxButton():
s1 = """
- QPushButton{
+ QPushButton{{
background-color: #BBB;
border: none;
padding: 2px;
- }
- QPushButton:checked, QPushButton:hover{
+ }}
+ QPushButton:checked, QPushButton:hover{{
font-style:italic;
- background-color:lightBlue;
- }"""
+ background-color:{bg};
+ }}""".format(bg=highlightLight)
s2 = """
QPushButton{{
background-color: transparent;
border: none;
- border-top: 1px solid darkGray;
+ border-top: 1px solid {border};
padding: 4px 0px;
font-weight: bold;
}}
QPushButton:hover{{
- background-color:{bgHover};
+ background-color:{hover};
}}
""".format(
- bgHover=bgHover,
- bgChecked=bgChecked
+ hover=highlightLight,
+ border=mid,
)
return s2
def mainEditorTabSS():
- return """
- QTabWidget::pane{{
- margin-top: -1px;
- border: 1px solid #999;
- }}
- QTabWidget::tab-bar{{
- left:50px;
- }}
- QTabBar{{
- background: transparent;
- border-radius: 0;
- border: 0px;
- }}
- QTabBar::tab{{
- margin: 3px 0 -3px 0;
- padding: 2px 9px;
- border: 1px solid #999;
- border-bottom: 0px;
- }}
- QTabBar::tab:selected{{
- border: 1px solid #999;
- background: {bgColor};
- border-bottom: 0px;
- margin-top: 0px;
- color: {foreground};
- }}
- QTabBar::tab:!selected:hover{{
- background:#ddd;
- }}
+ if not settings.textEditor["backgroundTransparent"]:
+ SS = """
+ QTabWidget::pane{{
+ margin-top: -1px;
+ border: 1px solid {borderColor};
+ }}
+ QTabWidget::tab-bar{{
+ left:50px;
+ }}
+ QTabBar{{
+ background: transparent;
+ border-radius: 0;
+ border: 0px;
+ }}
+ QTabBar::tab{{
+ padding: 2px 9px;
+ border: 1px solid {borderColor};
+ border-bottom: 0px;
+ }}
+ QTabBar::tab:selected{{
+ border: 1px solid {borderColor};
+ background: {bgColor};
+ border-bottom: 0px;
+ color: {foreground};
+ }}
+ QTabBar::tab:!selected:hover{{
+ background:{highlight};
+ color: {highlightedText};
+ }}
+ """.format(
+ bgColor=settings.textEditor["background"],
+ foreground=settings.textEditor["fontColor"],
+ borderColor=mid,
+ highlight=highlight,
+ highlightedText=highlightedText,
+ )
+ else:
+ # Transparent text view
+ SS = """
+ QTabWidget::pane{{
+ margin-top: -1px;
+ border: none;
+ }}
+ QTabWidget::tab-bar{{
+ left:50px;
+ }}
+ QTabBar{{
+ background: transparent;
+ border: 0px;
+ }}
+ QTabBar::tab{{
+ padding: 2px 9px;
+ border: 1px solid {borderColor};
+ }}
+ QTabBar::tab:selected{{
+ border: 1px solid {borderColor};
+ background: {highlight};
+ color: {highlightedText};
+ }}
+ QTabBar::tab:!selected:hover{{
+ background:{highlight};
+ color: {highlightedText};
+ }}
+ """.format(
+ highlight=highlight,
+ highlightedText=highlightedText,
+ text=text,
+ borderColor=mid,
+ )
+
+ # Add scrollbar
+ SS += simpleScrollBarV(handle=mid, width=10)
+
+ return SS
- QScrollBar:vertical {{
- border: none;
- background: transparent;
- width: 10px;
- }}
- QScrollBar::handle {{
- background: rgba(180, 180, 180, 40%);
- }}
- QScrollBar::add-line:vertical {{
- width:0;
- height: 0;
- border: none;
- background: none;
- }}
-
- QScrollBar::sub-line:vertical {{
- width:0;
- height: 0;
- border: none;
- background: none;
- }}
- """.format(
- bgColor=settings.textEditor["background"],
- foreground=settings.textEditor["fontColor"]
- )
def toolBarSS():
return """
- QToolBar{
+ QToolBar{{
background:transparent;
border: 0;
- border-left: 1px solid darkgray;
+ border-left: 1px solid {border};
spacing: 0px;
- }
- QToolBar:separator{
+ }}
+ QToolBar:separator{{
border: none;
- }
- """
+ }}
+ """.format(
+ border=midlighter,
+ )
def verticalToolButtonSS():
return """
@@ -195,25 +264,27 @@ def verticalToolButtonSS():
background: {bgHover};
}}
""".format(
- borderColor=borderColor,
- bgChecked=bgChecked,
- bgHover=bgHover
+ borderColor=mid,
+ bgChecked=midlighter,
+ bgHover=highlightLight,
)
def dockSS():
+
return """
QDockWidget::title {{
text-align: left; /* align the text to the left */
- background: {bgChecked};
+ background: {header};
padding: 5px;
}}
QDockWidget::close-button, QDockWidget::float-button {{
- background: {bgChecked};
+ background: {header};
}}
""".format(
- bgChecked=bgChecked
+ header=highlightLight,
+ button=button
)
@@ -231,17 +302,17 @@ def lineEditSS():
# return "border-radius: 6px;"
return """QLineEdit{{
border: none;
- border-bottom: 1px solid {checked};
+ border-bottom: 1px solid {line};
background:{window};
}}
QLineEdit:focus{{
- border-bottom: 1px solid {blue};
+ border-bottom: 1px solid {highlight};
}}
""".format(window=window,
- checked=bgChecked,
- blue=blue)
-
-
+ line=mid,
+ highlight=highlight)
+
+
def transparentSS():
return """
QTextEdit{
@@ -249,25 +320,66 @@ def transparentSS():
border:none;
}"""
-def simpleScrollBarV():
+def simpleScrollBarV(handle=None, width=8):
+ # system default is (i think): mid background, dark handle
+
+ default = midlighter
+
+ handle = handle or default
return """
- QScrollBar:vertical {
+ QScrollBar:vertical {{
border: none;
- background: transparent;
- width: 8px;
- }
- QScrollBar::handle {
- background: rgba(180, 180, 180, 60%);
- }
- QScrollBar::add-line:vertical {
+ background: {background};
+ width: {width}px;
+ }}
+ QScrollBar::handle {{
+ background: {handle};
+ }}
+ QScrollBar::add-line:vertical {{
width:0;
height: 0;
border: none;
background: none;
- }
- QScrollBar::sub-line:vertical {
+ }}
+
+ QScrollBar::sub-line:vertical {{
width:0;
height: 0;
border: none;
background: none;
- }"""
\ No newline at end of file
+ }}""".format(
+ background="transparent",
+ handle=handle,
+ width=width)
+
+def toolBoxSS():
+ return """
+ QToolBox::tab{{
+ background-color: {background};
+ padding: 2px;
+ border: none;
+ }}
+
+ QToolBox::tab:selected, QToolBox::tab:hover{{
+ background-color:{backgroundHover};
+ color: {colorHover};
+ }}""".format(
+ background=highlightLight,
+ backgroundHover=highlight,
+ colorHover=highlightedText,
+ )
+
+def titleLabelSS():
+ return """
+ QLabel{{
+ background-color:{bg};
+ border:none;
+ padding:10px;
+ color:{text};
+ font-size:16px;
+ font-weight:bold;
+ text-align:center;
+ }}""".format(
+ bg=highlightLight,
+ text=highlightedTextDark,
+ )
diff --git a/manuskript/ui/tools/splitDialog.py b/manuskript/ui/tools/splitDialog.py
new file mode 100644
index 0000000..da950bb
--- /dev/null
+++ b/manuskript/ui/tools/splitDialog.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+# --!-- coding: utf8 --!--
+from PyQt5.QtWidgets import QInputDialog
+from manuskript.functions import mainWindow
+
+
+class splitDialog(QInputDialog):
+ """
+ Opens a dialog to split indexes.
+ """
+ def __init__(self, parent, indexes, mark=None):
+ """
+ @param parent: a QWidget, for the dialog.
+ @param indexes: a list of QModelIndex in the outlineModel
+ @param default: the default split mark
+ """
+ QInputDialog.__init__(self, parent)
+
+ description = self.tr("""
+
Split selected item(s) at the given mark.
+
+
If one of the selected item is a folder, it will be applied
+ recursively to all of it's children items.
+
+
The split mark can contain folling escape sequences:
+
+
\\n: line break
+
\\t: tab
+
+
+
+
Mark:
+ """)
+
+ if not mark:
+ mark = "\\n---\\n"
+ mark = mark.replace("\n", "\\n")
+ mark = mark.replace("\t", "\\t")
+
+ self.setLabelText(description)
+ self.setTextValue(mark)
+
+ if len(indexes) == 0:
+ return
+ if len(indexes) == 1:
+ idx = indexes[0]
+ self.setWindowTitle(
+ self.tr("Split '{}'").format(self.getItem(idx).title())
+ )
+ else:
+ self.setWindowTitle(self.tr("Split items"))
+
+ r = self.exec()
+
+ mark = self.textValue()
+
+ if r and mark:
+
+ mark = mark.replace("\\n", "\n")
+ mark = mark.replace("\\t", "\t")
+
+ for idx in indexes:
+ item = self.getItem(idx)
+ item.split(mark)
+
+ def getItem(self, index):
+ if index.isValid():
+ return index.internalPointer()
+ else:
+ return mainWindow().mdlOutline.rootItem
diff --git a/manuskript/ui/views/characterTreeView.py b/manuskript/ui/views/characterTreeView.py
index 2bfbe55..a2a5025 100644
--- a/manuskript/ui/views/characterTreeView.py
+++ b/manuskript/ui/views/characterTreeView.py
@@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QTreeWidget, QTreeWidgetItem, QColorDialog
from manuskript.enums import Character
from manuskript.functions import iconColor, mainWindow
+from manuskript.ui import style as S
class characterTreeView(QTreeWidget):
@@ -90,8 +91,8 @@ class characterTreeView(QTreeWidget):
for i in range(3):
# Create category item
cat = QTreeWidgetItem(self, [h[i]])
- cat.setBackground(0, QBrush(QColor(Qt.blue).lighter(190)))
- cat.setForeground(0, QBrush(Qt.darkBlue))
+ cat.setBackground(0, QBrush(QColor(S.highlightLight)))
+ cat.setForeground(0, QBrush(QColor(S.highlightedTextDark)))
cat.setTextAlignment(0, Qt.AlignCenter)
f = cat.font(0)
f.setBold(True)
diff --git a/manuskript/ui/views/cmbOutlineCharacterChoser.py b/manuskript/ui/views/cmbOutlineCharacterChoser.py
index 4185aca..cfe07f8 100644
--- a/manuskript/ui/views/cmbOutlineCharacterChoser.py
+++ b/manuskript/ui/views/cmbOutlineCharacterChoser.py
@@ -6,6 +6,7 @@ from PyQt5.QtWidgets import QComboBox
from manuskript.enums import Outline
from manuskript.functions import toInt
+from manuskript.ui import style as S
class cmbOutlineCharacterChoser(QComboBox):
@@ -36,8 +37,8 @@ class cmbOutlineCharacterChoser(QComboBox):
for importance in range(3):
self.addItem(l[importance])
- self.setItemData(self.count() - 1, QBrush(QColor(Qt.darkBlue)), Qt.ForegroundRole)
- self.setItemData(self.count() - 1, QBrush(QColor(Qt.blue).lighter(190)), Qt.BackgroundRole)
+ self.setItemData(self.count() - 1, QBrush(QColor(S.highlightedTextDark)), Qt.ForegroundRole)
+ self.setItemData(self.count() - 1, QBrush(QColor(S.highlightLight)), Qt.BackgroundRole)
item = self.model().item(self.count() - 1)
item.setFlags(Qt.ItemIsEnabled)
for i in range(self.mdlCharacters.rowCount()):
diff --git a/manuskript/ui/views/corkDelegate.py b/manuskript/ui/views/corkDelegate.py
index ab4e4c5..d3908eb 100644
--- a/manuskript/ui/views/corkDelegate.py
+++ b/manuskript/ui/views/corkDelegate.py
@@ -10,6 +10,7 @@ from manuskript.functions import colorifyPixmap
from manuskript.functions import mainWindow
from manuskript.functions import mixColors
from manuskript.functions import outlineItemColors
+from manuskript.ui import style as S
class corkDelegate(QStyledItemDelegate):
@@ -20,6 +21,8 @@ class corkDelegate(QStyledItemDelegate):
self.editing = None
self.margin = 5
+ self.bgColors = {}
+
def newStyle(self):
return settings.corkStyle == "new"
@@ -42,6 +45,8 @@ class corkDelegate(QStyledItemDelegate):
def createEditor(self, parent, option, index):
self.updateRects(option, index)
+ bgColor = self.bgColors.get(index, "white")
+
if self.mainLineRect.contains(self.lastPos):
# One line summary
self.editing = Outline.summarySentence
@@ -56,6 +61,7 @@ class corkDelegate(QStyledItemDelegate):
edt.setAlignment(Qt.AlignCenter)
edt.setPlaceholderText(self.tr("One line summary"))
edt.setFont(f)
+ edt.setStyleSheet("background: {}; color: black;".format(bgColor))
return edt
elif self.titleRect.contains(self.lastPos):
@@ -71,6 +77,7 @@ class corkDelegate(QStyledItemDelegate):
edt.setAlignment(Qt.AlignCenter)
f.setBold(True)
edt.setFont(f)
+ edt.setStyleSheet("background: {}; color: black;".format(bgColor))
# edt.setGeometry(self.titleRect)
return edt
@@ -85,6 +92,7 @@ class corkDelegate(QStyledItemDelegate):
edt.setPlaceholderText(self.tr("Full summary"))
except AttributeError:
pass
+ edt.setStyleSheet("background: {}; color: black;".format(bgColor))
return edt
def updateEditorGeometry(self, editor, option, index):
@@ -141,14 +149,14 @@ class corkDelegate(QStyledItemDelegate):
iconSize = max(24 * self.factor, 18)
item = index.internalPointer()
fm = QFontMetrics(option.font)
- h = fm.lineSpacing()
-
+ h = fm.lineSpacing()
+
self.itemRect = option.rect.adjusted(margin, margin, -margin, -margin)
-
+
top = 15 * self.factor
self.topRect = QRect(self.itemRect)
self.topRect.setHeight(top)
-
+
self.cardRect = QRect(self.itemRect.topLeft() + QPoint(0, top),
self.itemRect.bottomRight())
self.iconRect = QRect(self.cardRect.topLeft() + QPoint(margin, margin),
@@ -167,7 +175,7 @@ class corkDelegate(QStyledItemDelegate):
self.mainRect.bottomRight())
if not item.data(Outline.summarySentence.value):
self.mainTextRect.setTopLeft(self.mainLineRect.topLeft())
-
+
def updateRects_v1(self, option, index):
margin = self.margin
iconSize = max(16 * self.factor, 12)
@@ -196,7 +204,7 @@ class corkDelegate(QStyledItemDelegate):
self.paint_v2(p, option, index)
else:
self.paint_v1(p, option, index)
-
+
def paint_v2(self, p, option, index):
# QStyledItemDelegate.paint(self, p, option, index)
if not index.isValid():
@@ -212,7 +220,7 @@ class corkDelegate(QStyledItemDelegate):
p.translate(self.mainRect.center())
p.rotate(angle)
p.translate(-self.mainRect.center())
-
+
def drawRect(r):
p.save()
p.setBrush(Qt.gray)
@@ -240,10 +248,15 @@ class corkDelegate(QStyledItemDelegate):
if c == QColor(Qt.transparent):
c = QColor(Qt.white)
col = mixColors(c, QColor(Qt.white), .2)
+ backgroundColor = col
p.setBrush(col)
else:
p.setBrush(Qt.white)
-
+ backgroundColor = QColor(Qt.white)
+
+ # Cache background color
+ self.bgColors[index] = backgroundColor.name()
+
p.setPen(Qt.NoPen)
p.drawRect(self.cardRect)
if item.isFolder():
@@ -273,10 +286,10 @@ class corkDelegate(QStyledItemDelegate):
self.labelRect.bottomRight() + QPointF(1, w / 2),
self.labelRect.bottomRight() + QPointF(1, 1),
])
-
- p.drawPolygon(poly)
+
+ p.drawPolygon(poly)
p.restore()
-
+
if settings.viewSettings["Cork"]["Corner"] == "Nothing" or \
color == Qt.transparent:
# No corner, so title can be full width
@@ -299,12 +312,23 @@ class corkDelegate(QStyledItemDelegate):
# Draw title
p.save()
text = index.data()
-
+
if text:
+ p.setPen(Qt.black)
+ textColor = QColor(Qt.black)
if settings.viewSettings["Cork"]["Text"] != "Nothing":
col = colors[settings.viewSettings["Cork"]["Text"]]
if col == Qt.transparent:
col = Qt.black
+
+ # If title setting is compile, we have to hack the color
+ # Or we won't see anything in some themes
+ if settings.viewSettings["Cork"]["Text"] == "Compile":
+ if item.compile() in [0, "0"]:
+ col = mixColors(QColor(Qt.black), backgroundColor)
+ else:
+ col = Qt.black
+ textColor = col
p.setPen(col)
f = QFont(option.font)
f.setPointSize(f.pointSize() + 4)
@@ -357,6 +381,7 @@ class corkDelegate(QStyledItemDelegate):
f = QFont(option.font)
f.setBold(True)
p.setFont(f)
+ p.setPen(textColor)
fm = QFontMetrics(f)
elidedText = fm.elidedText(lineSummary, Qt.ElideRight, self.mainLineRect.width())
p.drawText(self.mainLineRect, Qt.AlignLeft | Qt.AlignVCenter, elidedText)
@@ -364,8 +389,11 @@ class corkDelegate(QStyledItemDelegate):
# Full summary
if fullSummary:
+ p.save()
p.setFont(option.font)
+ p.setPen(textColor)
p.drawText(self.mainTextRect, Qt.TextWordWrap, fullSummary)
+ p.restore()
def paint_v1(self, p, option, index):
# QStyledItemDelegate.paint(self, p, option, index)
diff --git a/manuskript/ui/views/outlineBasics.py b/manuskript/ui/views/outlineBasics.py
index 7fc6959..ccd655c 100644
--- a/manuskript/ui/views/outlineBasics.py
+++ b/manuskript/ui/views/outlineBasics.py
@@ -1,20 +1,22 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtCore import Qt, QSignalMapper, QSize
-from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QAbstractItemView, qApp, QMenu, QAction
-from PyQt5.QtWidgets import QListWidget, QWidgetAction, QListWidgetItem, QLineEdit
+from PyQt5.QtGui import QIcon, QCursor
+from PyQt5.QtWidgets import QAbstractItemView, qApp, QMenu, QAction, \
+ QListWidget, QWidgetAction, QListWidgetItem, \
+ QLineEdit, QInputDialog, QMessageBox, QCheckBox
from manuskript import settings
from manuskript.enums import Outline
-from manuskript.functions import mainWindow
+from manuskript.functions import mainWindow, statusMessage
from manuskript.functions import toInt, customIcons
from manuskript.models.outlineModel import outlineItem
+from manuskript.ui.tools.splitDialog import splitDialog
class outlineBasics(QAbstractItemView):
def __init__(self, parent=None):
- pass
+ self._indexesToOpen = None
def getSelection(self):
sel = []
@@ -39,14 +41,51 @@ class outlineBasics(QAbstractItemView):
menu = QMenu(self)
- # Add / remove items
- self.actOpen = QAction(QIcon.fromTheme("go-right"), qApp.translate("outlineBasics", "Open Item"), menu)
+ # Get index under cursor
+ pos = self.viewport().mapFromGlobal(QCursor.pos())
+ mouseIndex = self.indexAt(pos)
+
+ # Get index's title
+ if mouseIndex.isValid():
+ title = mouseIndex.internalPointer().title()
+
+ elif self.rootIndex().parent().isValid():
+ # mouseIndex is the background of an item, so we check the parent
+ mouseIndex = self.rootIndex().parent()
+ title = mouseIndex.internalPointer().title()
+
+ else:
+ title = qApp.translate("outlineBasics", "Root")
+
+ if len(title) > 25:
+ title = title[:25] + "…"
+
+ # Open Item action
+ self.actOpen = QAction(QIcon.fromTheme("go-right"),
+ qApp.translate("outlineBasics", "Open {}".format(title)),
+ menu)
self.actOpen.triggered.connect(self.openItem)
menu.addAction(self.actOpen)
+ # Open item(s) in new tab
+ if mouseIndex in sel and len(sel) > 1:
+ actionTitle = qApp.translate("outlineBasics", "Open {} items in new tabs").format(len(sel))
+ self._indexesToOpen = sel
+ else:
+ actionTitle = qApp.translate("outlineBasics", "Open {} in a new tab").format(title)
+ self._indexesToOpen = [mouseIndex]
+
+ self.actNewTab = QAction(QIcon.fromTheme("go-right"), actionTitle, menu)
+ self.actNewTab.triggered.connect(self.openItemsInNewTabs)
+ menu.addAction(self.actNewTab)
+
menu.addSeparator()
- # Add / remove items
+ # Rename / add / remove items
+ self.actRename = QAction(QIcon.fromTheme("edit-rename"), qApp.translate("outlineBasics", "Rename"), menu)
+ self.actRename.triggered.connect(self.rename)
+ menu.addAction(self.actRename)
+
self.actAddFolder = QAction(QIcon.fromTheme("folder-new"), qApp.translate("outlineBasics", "New Folder"), menu)
self.actAddFolder.triggered.connect(self.addFolder)
menu.addAction(self.actAddFolder)
@@ -181,22 +220,38 @@ class outlineBasics(QAbstractItemView):
self.actAddText.setEnabled(False)
if len(sel) == 0:
- self.actOpen.setEnabled(False)
self.actCopy.setEnabled(False)
self.actCut.setEnabled(False)
+ self.actRename.setEnabled(False)
self.actDelete.setEnabled(False)
self.menuPOV.setEnabled(False)
self.menuStatus.setEnabled(False)
self.menuLabel.setEnabled(False)
self.menuCustomIcons.setEnabled(False)
+ if len(sel) > 1:
+ self.actRename.setEnabled(False)
+
return menu
def openItem(self):
- idx = self.currentIndex()
+ #idx = self.currentIndex()
+ idx = self._indexesToOpen[0]
from manuskript.functions import MW
MW.openIndex(idx)
+ def openItemsInNewTabs(self):
+ from manuskript.functions import MW
+ MW.openIndexes(self._indexesToOpen)
+
+ def rename(self):
+ if len(self.getSelection()) == 1:
+ index = self.currentIndex()
+ self.edit(index)
+ elif len(self.getSelection()) > 1:
+ # FIXME: add smart rename
+ pass
+
def addFolder(self):
self.addItem("folder")
@@ -231,8 +286,132 @@ class outlineBasics(QAbstractItemView):
self.delete()
def delete(self):
+ """
+ Shows a warning, and then deletes currently selected indexes.
+ """
+ if not settings.dontShowDeleteWarning:
+ msg = QMessageBox(QMessageBox.Warning,
+ qApp.translate("outlineBasics", "About to remove"),
+ qApp.translate("outlineBasics",
+ "
You're about to delete {} item(s).
Are you sure?
"
+ ).format(len(self.getSelection())),
+ QMessageBox.Yes | QMessageBox.Cancel)
+
+ chk = QCheckBox("&Don't show this warning in the future.")
+ msg.setCheckBox(chk)
+ ret = msg.exec()
+
+ if ret == QMessageBox.Cancel:
+ return
+
+ if chk.isChecked():
+ settings.dontShowDeleteWarning = True
+
self.model().removeIndexes(self.getSelection())
+ def duplicate(self):
+ self.copy()
+ self.paste()
+
+ def move(self, delta=1):
+ """
+ Move selected items up or down.
+ """
+
+ # we store selected indexesret
+ currentID = self.model().ID(self.currentIndex())
+ selIDs = [self.model().ID(i) for i in self.selectedIndexes()]
+
+ # Block signals
+ self.blockSignals(True)
+ self.selectionModel().blockSignals(True)
+
+ # Move each index individually
+ for idx in self.selectedIndexes():
+ self.moveIndex(idx, delta)
+
+ # Done the hardcore way, so inform views
+ self.model().layoutChanged.emit()
+
+ # restore selection
+ selIdx = [self.model().getIndexByID(ID) for ID in selIDs]
+ sm = self.selectionModel()
+ sm.clear()
+ [sm.select(idx, sm.Select) for idx in selIdx]
+ sm.setCurrentIndex(self.model().getIndexByID(currentID), sm.Select)
+ #self.setSmsgBoxelectionModel(sm)
+
+ # Unblock signals
+ self.blockSignals(False)
+ self.selectionModel().blockSignals(False)
+
+ def moveIndex(self, index, delta=1):
+ """
+ Move the item represented by index. +1 means down, -1 means up.
+ """
+
+ if not index.isValid():
+ return
+
+ if index.parent().isValid():
+ parentItem = index.parent().internalPointer()
+ else:
+ parentItem = index.model().rootItem
+
+ parentItem.childItems.insert(index.row() + delta,
+ parentItem.childItems.pop(index.row()))
+ parentItem.updateWordCount(emit=False)
+
+ def moveUp(self): self.move(-1)
+ def moveDown(self): self.move(+1)
+
+ def splitDialog(self):
+ """
+ Opens a dialog to split selected items.
+
+ Call context: if at least one index is selected. Folder or text.
+ """
+
+ indexes = self.getSelection()
+ if len(indexes) == 0:
+ # No selection, we use parent
+ indexes = [self.rootIndex()]
+
+ splitDialog(self, indexes)
+
+ def merge(self):
+ """
+ Merges selected items together.
+
+ Call context: Multiple selection, same parent.
+ """
+
+ # Get selection
+ indexes = self.getSelection()
+ # Get items
+ items = [i.internalPointer() for i in indexes if i.isValid()]
+ # Remove folders
+ items = [i for i in items if not i.isFolder()]
+
+ # Check that we have at least 2 items
+ if len(items) < 2:
+ statusMessage(qApp.translate("outlineBasics",
+ "Select at least two items. Folders are ignored."))
+ return
+
+ # Check that all share the same parent
+ p = items[0].parent()
+ for i in items:
+ if i.parent() != p:
+ statusMessage(qApp.translate("outlineBasics",
+ "All items must be on the same level (share the same parent)."))
+ return
+
+ # Sort items by row
+ items = sorted(items, key=lambda i: i.row())
+
+ items[0].mergeWith(items[1:])
+
def setPOV(self, POV):
for i in self.getSelection():
self.model().setData(i.sibling(i.row(), Outline.POV.value), str(POV))
diff --git a/manuskript/ui/views/outlineDelegates.py b/manuskript/ui/views/outlineDelegates.py
index 818718d..8995ec9 100644
--- a/manuskript/ui/views/outlineDelegates.py
+++ b/manuskript/ui/views/outlineDelegates.py
@@ -8,6 +8,7 @@ from PyQt5.QtWidgets import qApp
from manuskript import settings
from manuskript.enums import Character, Outline
from manuskript.functions import outlineItemColors, mixColors, colorifyPixmap, toInt, toFloat, drawProgress
+from manuskript.ui import style as S
class outlineTitleDelegate(QStyledItemDelegate):
@@ -39,7 +40,7 @@ class outlineTitleDelegate(QStyledItemDelegate):
col = colors[settings.viewSettings["Outline"]["Background"]]
if col != QColor(Qt.transparent):
- col2 = QColor(Qt.white)
+ col2 = QColor(S.base)
if opt.state & QStyle.State_Selected:
col2 = opt.palette.brush(QPalette.Normal, QPalette.Highlight).color()
col = mixColors(col, col2, .2)
@@ -76,10 +77,20 @@ class outlineTitleDelegate(QStyledItemDelegate):
# Text
if opt.text:
painter.save()
+ textColor = QColor(S.text)
+ if option.state & QStyle.State_Selected:
+ col = QColor(S.highlightedText)
+ textColor = col
+ painter.setPen(col)
if settings.viewSettings["Outline"]["Text"] != "Nothing":
col = colors[settings.viewSettings["Outline"]["Text"]]
if col == Qt.transparent:
- col = Qt.black
+ col = textColor
+ # If text color is Compile and item is selected, we have
+ # to change the color
+ if settings.viewSettings["Outline"]["Text"] == "Compile" and \
+ item.compile() in [0, "0"]:
+ col = mixColors(textColor, QColor(S.window))
painter.setPen(col)
f = QFont(opt.font)
painter.setFont(f)
@@ -132,8 +143,8 @@ class outlineCharacterDelegate(QStyledItemDelegate):
l = [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]
for importance in range(3):
editor.addItem(l[importance])
- editor.setItemData(editor.count() - 1, QBrush(Qt.darkBlue), Qt.ForegroundRole)
- editor.setItemData(editor.count() - 1, QBrush(QColor(Qt.blue).lighter(190)), Qt.BackgroundRole)
+ editor.setItemData(editor.count() - 1, QBrush(QColor(S.highlightedTextDark)), Qt.ForegroundRole)
+ editor.setItemData(editor.count() - 1, QBrush(QColor(S.highlightLight)), Qt.BackgroundRole)
item = editor.model().item(editor.count() - 1)
item.setFlags(Qt.ItemIsEnabled)
for i in range(self.mdlCharacter.rowCount()):
diff --git a/manuskript/ui/views/plotTreeView.py b/manuskript/ui/views/plotTreeView.py
index 31d4cfe..460aab1 100644
--- a/manuskript/ui/views/plotTreeView.py
+++ b/manuskript/ui/views/plotTreeView.py
@@ -8,6 +8,7 @@ from lxml import etree as ET
from manuskript import settings
from manuskript.enums import Plot, Outline, PlotStep
from manuskript.models import references as Ref
+from manuskript.ui import style as S
class plotTreeView(QTreeWidget):
@@ -126,8 +127,8 @@ class plotTreeView(QTreeWidget):
h = [self.tr("Main"), self.tr("Secondary"), self.tr("Minor")]
for i in range(3):
cat = QTreeWidgetItem(self, [h[i]])
- cat.setBackground(0, QBrush(QColor(Qt.blue).lighter(190)))
- cat.setForeground(0, QBrush(Qt.darkBlue))
+ cat.setBackground(0, QBrush(QColor(S.highlightLight)))
+ cat.setForeground(0, QBrush(QColor(S.highlightedTextDark)))
cat.setTextAlignment(0, Qt.AlignCenter)
f = cat.font(0)
f.setBold(True)
diff --git a/manuskript/ui/views/propertiesView.py b/manuskript/ui/views/propertiesView.py
index e0fa719..30857ef 100644
--- a/manuskript/ui/views/propertiesView.py
+++ b/manuskript/ui/views/propertiesView.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
# --!-- coding: utf8 --!--
from PyQt5.QtWidgets import QWidget
+from PyQt5.QtGui import QIntValidator
from manuskript.enums import Outline
from manuskript.ui.views.propertiesView_ui import Ui_propertiesView
@@ -19,6 +20,7 @@ class propertiesView(QWidget, Ui_propertiesView):
self.chkCompile.setModel(mdlOutline)
self.txtTitle.setModel(mdlOutline)
self.txtGoal.setModel(mdlOutline)
+ self.txtGoal.setValidator(QIntValidator(0, 9999999))
def getIndexes(self, sourceView):
"""Returns a list of indexes from list of QItemSelectionRange"""
diff --git a/manuskript/ui/views/propertiesView_ui.py b/manuskript/ui/views/propertiesView_ui.py
index 5b8a7e1..e4846fa 100644
--- a/manuskript/ui/views/propertiesView_ui.py
+++ b/manuskript/ui/views/propertiesView_ui.py
@@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'manuskript/ui/views/propertiesView_ui.ui'
#
-# Created by: PyQt5 UI code generator 5.4.2
+# Created by: PyQt5 UI code generator 5.9
#
# WARNING! All changes made in this file will be lost!
diff --git a/manuskript/ui/views/textEditView.py b/manuskript/ui/views/textEditView.py
index d557e4d..ce7cf72 100644
--- a/manuskript/ui/views/textEditView.py
+++ b/manuskript/ui/views/textEditView.py
@@ -2,19 +2,19 @@
# --!-- coding: utf8 --!--
import re
-from PyQt5.QtCore import QTimer, QModelIndex, Qt, QEvent, pyqtSignal, QRegExp
+from PyQt5.QtCore import QTimer, QModelIndex, Qt, QEvent, pyqtSignal, QRegExp, QLocale
from PyQt5.QtGui import QTextBlockFormat, QTextCharFormat, QFont, QColor, QIcon, QMouseEvent, QTextCursor
from PyQt5.QtWidgets import QWidget, QTextEdit, qApp, QAction, QMenu
from manuskript import settings
from manuskript.enums import Outline
-from manuskript.functions import AUC, themeIcon
-from manuskript.functions import toString
+from manuskript import functions as F
from manuskript.models.outlineModel import outlineModel
from manuskript.ui.editors.MDFunctions import MDFormatSelection
from manuskript.ui.editors.MMDHighlighter import MMDHighlighter
from manuskript.ui.editors.basicHighlighter import basicHighlighter
from manuskript.ui.editors.textFormat import textFormat
+from manuskript.ui import style as S
try:
import enchant
@@ -49,7 +49,7 @@ class textEditView(QTextEdit):
self.highligtCS = False
self.defaultFontPointSize = qApp.font().pointSize()
self._dict = None
- # self.document().contentsChanged.connect(self.submit, AUC)
+ # self.document().contentsChanged.connect(self.submit, F.AUC)
# Submit text changed only after 500ms without modifications
self.updateTimer = QTimer()
@@ -59,7 +59,7 @@ class textEditView(QTextEdit):
# self.updateTimer.timeout.connect(lambda: print("Timeout"))
self.updateTimer.stop()
- self.document().contentsChanged.connect(self.updateTimer.start, AUC)
+ self.document().contentsChanged.connect(self.updateTimer.start, F.AUC)
# self.document().contentsChanged.connect(lambda: print("Document changed"))
# self.document().contentsChanged.connect(lambda: print(self.objectName(), "Contents changed"))
@@ -76,7 +76,7 @@ class textEditView(QTextEdit):
# Spellchecking
if enchant and self.spellcheck:
try:
- self._dict = enchant.Dict(self.currentDict if self.currentDict else enchant.get_default_language())
+ self._dict = enchant.Dict(self.currentDict if self.currentDict else self.getDefaultLocale())
except enchant.errors.DictNotFoundError:
self.spellcheck = False
@@ -87,14 +87,23 @@ class textEditView(QTextEdit):
self.highlighter = basicHighlighter(self)
self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
+ def getDefaultLocale(self):
+ default_locale = enchant.get_default_language()
+ if default_locale is None:
+ default_locale = QLocale.system().name()
+ if default_locale is None:
+ default_locale = enchant.list_dicts()[0][0]
+
+ return default_locale
+
def setModel(self, model):
self._model = model
try:
- self._model.dataChanged.connect(self.update, AUC)
+ self._model.dataChanged.connect(self.update, F.AUC)
except TypeError:
pass
try:
- self._model.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC)
+ self._model.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, F.AUC)
except TypeError:
pass
@@ -132,6 +141,21 @@ class textEditView(QTextEdit):
self.setPlainText("")
self.setEnabled(False)
+ def currentIndex(self):
+ """
+ Getter function used to normalized views acces with QAbstractItemViews.
+ """
+ if self._index:
+ return self._index
+ else:
+ return QModelIndex()
+
+ def getSelection(self):
+ """
+ Getter function used to normalized views acces with QAbstractItemViews.
+ """
+ return [self.currentIndex()]
+
def setCurrentModelIndexes(self, indexes):
self._index = None
self._indexes = []
@@ -181,6 +205,8 @@ class textEditView(QTextEdit):
opt = settings.textEditor
f = QFont()
f.fromString(opt["font"])
+ background = opt["background"] if not opt["backgroundTransparent"] else "transparent"
+ foreground = opt["fontColor"] if not opt["backgroundTransparent"] else S.text
# self.setFont(f)
self.setStyleSheet("""QTextEdit{{
background: {bg};
@@ -191,8 +217,8 @@ class textEditView(QTextEdit):
{maxWidth}
}}
""".format(
- bg=opt["background"],
- foreground=opt["fontColor"],
+ bg=background,
+ foreground=foreground,
ff=f.family(),
fs="{}pt".format(str(f.pointSize())),
mTB = opt["marginsTB"],
@@ -213,7 +239,7 @@ class textEditView(QTextEdit):
# We style by name, otherwise all heriting widgets get the same
# colored background, for example context menu.
name=self.parent().objectName(),
- bg=opt["background"],
+ bg=background,
))
cf = QTextCharFormat()
@@ -281,7 +307,7 @@ class textEditView(QTextEdit):
pass
def reconnectDocument(self):
- self.document().contentsChanged.connect(self.updateTimer.start, AUC)
+ self.document().contentsChanged.connect(self.updateTimer.start, F.AUC)
def updateText(self):
if self._updating:
@@ -290,9 +316,9 @@ class textEditView(QTextEdit):
self._updating = True
if self._index:
self.disconnectDocument()
- if self.toPlainText() != toString(self._model.data(self._index)):
+ if self.toPlainText() != F.toString(self._model.data(self._index)):
# print(" Updating plaintext")
- self.document().setPlainText(toString(self._model.data(self._index)))
+ self.document().setPlainText(F.toString(self._model.data(self._index)))
self.reconnectDocument()
elif self._indexes:
@@ -301,7 +327,7 @@ class textEditView(QTextEdit):
same = True
for i in self._indexes:
item = i.internalPointer()
- t.append(toString(item.data(self._column)))
+ t.append(F.toString(item.data(self._column)))
for t2 in t[1:]:
if t2 != t[0]:
@@ -337,7 +363,7 @@ class textEditView(QTextEdit):
self._updating = True
for i in self._indexes:
item = i.internalPointer()
- if self.toPlainText() != toString(item.data(self._column)):
+ if self.toPlainText() != F.toString(item.data(self._column)):
print("Submitting many indexes")
self._model.setData(i, self.toPlainText())
self._updating = False
@@ -384,7 +410,7 @@ class textEditView(QTextEdit):
if enchant and self.spellcheck and not self._dict:
if self.currentDict:
self._dict = enchant.Dict(self.currentDict)
- elif enchant.dict_exists(enchant.get_default_language()):
+ elif enchant.get_default_language() and enchant.dict_exists(enchant.get_default_language()):
self._dict = enchant.Dict(enchant.get_default_language())
else:
self.spellcheck = False
@@ -439,7 +465,7 @@ class textEditView(QTextEdit):
selectedWord = cursor.selectedText()
if not valid:
spell_menu = QMenu(self.tr('Spelling Suggestions'), self)
- spell_menu.setIcon(themeIcon("spelling"))
+ spell_menu.setIcon(F.themeIcon("spelling"))
for word in self._dict.suggest(text):
action = self.SpellAction(word, spell_menu)
action.correct.connect(self.correctWord)
@@ -526,3 +552,21 @@ class textEditView(QTextEdit):
MDFormatSelection(self, 2)
elif _format == "Clear":
MDFormatSelection(self)
+
+ ###############################################################################
+ # KEYBOARD SHORTCUTS
+ ###############################################################################
+
+ def callMainTreeView(self, functionName):
+ """
+ The tree view in mainwindow must have same index as the text
+ edit that has focus. So we can pass it the call for documents
+ edits like: duplicate, move up, etc.
+ """
+ if self._index and self._column == Outline.text.value:
+ function = getattr(F.mainWindow().treeRedacOutline, functionName)
+ function()
+
+ def duplicate(self): self.callMainTreeView("duplicate")
+ def moveUp(self): self.callMainTreeView("moveUp")
+ def moveDown(self): self.callMainTreeView("moveDown")
diff --git a/manuskript/ui/views/treeDelegates.py b/manuskript/ui/views/treeDelegates.py
index a9ee454..9e8179b 100644
--- a/manuskript/ui/views/treeDelegates.py
+++ b/manuskript/ui/views/treeDelegates.py
@@ -9,6 +9,7 @@ from manuskript.enums import Outline
from manuskript.functions import mixColors, colorifyPixmap
from manuskript.functions import outlineItemColors
from manuskript.functions import toFloat
+from manuskript.ui import style as S
class treeTitleDelegate(QStyledItemDelegate):
@@ -44,7 +45,7 @@ class treeTitleDelegate(QStyledItemDelegate):
col = colors[settings.viewSettings["Tree"]["Background"]]
if col != QColor(Qt.transparent):
- col2 = QColor(Qt.white)
+ col2 = QColor(S.window)
if opt.state & QStyle.State_Selected:
col2 = opt.palette.brush(QPalette.Normal, QPalette.Highlight).color()
col = mixColors(col, col2, .2)
@@ -81,10 +82,20 @@ class treeTitleDelegate(QStyledItemDelegate):
# Text
if opt.text:
painter.save()
+ textColor = QColor(S.text)
+ if option.state & QStyle.State_Selected:
+ col = QColor(S.highlightedText)
+ textColor = col
+ painter.setPen(col)
if settings.viewSettings["Tree"]["Text"] != "Nothing":
col = colors[settings.viewSettings["Tree"]["Text"]]
if col == Qt.transparent:
- col = Qt.black
+ col = textColor
+ # If text color is Compile and item is selected, we have
+ # to change the color
+ if settings.viewSettings["Outline"]["Text"] == "Compile" and \
+ item.compile() in [0, "0"]:
+ col = mixColors(textColor, QColor(S.window))
painter.setPen(col)
f = QFont(opt.font)
painter.setFont(f)
@@ -131,8 +142,12 @@ class treeTitleDelegate(QStyledItemDelegate):
f = painter.font()
f.setWeight(QFont.Normal)
painter.setFont(f)
- painter.setPen(Qt.darkGray)
- painter.drawText(r, Qt.AlignLeft | Qt.AlignBottom, extraText)
+ if option.state & QStyle.State_Selected:
+ col = QColor(S.highlightedTextLight)
+ else:
+ col = QColor(S.textLight)
+ painter.setPen(col)
+ painter.drawText(r, Qt.AlignLeft | Qt.AlignVCenter, extraText)
painter.restore()
painter.restore()
diff --git a/manuskript/ui/views/treeView.py b/manuskript/ui/views/treeView.py
index 53a063f..4e28d1c 100644
--- a/manuskript/ui/views/treeView.py
+++ b/manuskript/ui/views/treeView.py
@@ -31,30 +31,13 @@ class treeView(QTreeView, dndView, outlineBasics):
def makePopupMenu(self):
menu = outlineBasics.makePopupMenu(self)
- first = menu.actions()[0]
+ first = menu.actions()[3]
# Open item in new tab
- sel = self.selectedIndexes()
+ #sel = self.selectedIndexes()
pos = self.viewport().mapFromGlobal(QCursor.pos())
mouseIndex = self.indexAt(pos)
- if mouseIndex.isValid():
- mouseTitle = mouseIndex.internalPointer().title()
- else:
- mouseTitle = self.tr("Root")
-
- if mouseIndex in sel and len(sel) > 1:
- actionTitle = self.tr("Open {} items in new tabs").format(len(sel))
- self._indexesToOpen = sel
- else:
- actionTitle = self.tr("Open {} in a new tab").format(mouseTitle)
- self._indexesToOpen = [mouseIndex]
-
- self.actNewTab = QAction(actionTitle, menu)
- self.actNewTab.triggered.connect(self.openNewTab)
- menu.insertAction(first, self.actNewTab)
- menu.insertSeparator(first)
-
# Expand /collapse item
if mouseIndex.isValid():
# index = self.currentIndex()
@@ -83,9 +66,6 @@ class treeView(QTreeView, dndView, outlineBasics):
return menu
- def openNewTab(self):
- mainWindow().mainEditor.openIndexes(self._indexesToOpen, newTab=True)
-
def expandCurrentIndex(self, index=None):
if index is None or type(index) == bool:
index = self._indexesToOpen[0] # self.currentIndex()
diff --git a/manuskript/ui/welcome.py b/manuskript/ui/welcome.py
index c6d4c68..d3858e8 100644
--- a/manuskript/ui/welcome.py
+++ b/manuskript/ui/welcome.py
@@ -20,9 +20,12 @@ from manuskript.models.outlineModel import outlineModel
from manuskript.models.plotModel import plotModel
from manuskript.models.worldModel import worldModel
from manuskript.ui.welcome_ui import Ui_welcome
+from manuskript.ui import style as S
-locale.setlocale(locale.LC_ALL, '')
-
+try:
+ locale.setlocale(locale.LC_ALL, '')
+except:
+ pass
class welcome(QWidget, Ui_welcome):
def __init__(self, parent=None):
@@ -65,8 +68,15 @@ class welcome(QWidget, Ui_welcome):
self.mw.loadProject(last)
def getAutoLoadValues(self):
+ """
+ Reads manuskript system's settings and returns a tupple:
+ - `bool`: whether manuskript should automatically load
+ the last openend project or display the
+ welcome widget.
+ - `str`: the absolute path to the last opened project.
+ """
sttgns = QSettings()
- autoLoad = sttgns.value("autoLoad", type=bool)
+ autoLoad = sttgns.value("autoLoad", defaultValue=False, type=bool)
if autoLoad and sttgns.contains("lastProject"):
last = sttgns.value("lastProject")
else:
@@ -139,6 +149,8 @@ class welcome(QWidget, Ui_welcome):
self.tr("Manuskript project (*.msk)"))[0]
if filename:
+ if filename[-4:] != ".msk":
+ filename += ".msk"
self.appendToRecentFiles(filename)
loadSave.clearSaveCache() # Ensure all file(s) are saved under new filename
self.mw.saveDatas(filename)
@@ -269,6 +281,7 @@ class welcome(QWidget, Ui_welcome):
btn = QPushButton("", self)
btn.setIcon(QIcon.fromTheme("edit-delete"))
btn.setProperty("deleteRow", k)
+ btn.setFlat(True)
btn.clicked.connect(self.deleteTemplateRow)
self.lytTemplate.addWidget(btn, k, 3)
@@ -333,8 +346,8 @@ class welcome(QWidget, Ui_welcome):
def addTopLevelItem(self, name):
item = QTreeWidgetItem(self.tree, [name])
- item.setBackground(0, QBrush(QColor(Qt.blue).lighter(190)))
- item.setForeground(0, QBrush(Qt.darkBlue))
+ item.setBackground(0, QBrush(QColor(S.highlightLight)))
+ item.setForeground(0, QBrush(QColor(S.highlightedTextDark)))
item.setTextAlignment(0, Qt.AlignCenter)
item.setFlags(Qt.ItemIsEnabled)
f = item.font(0)
@@ -372,6 +385,8 @@ class welcome(QWidget, Ui_welcome):
# Empty settings
imp.reload(settings)
+ settings.initDefaultValues()
+
if self.template:
t = [i for i in self.templates() if i[0] == self.template[0]]
if t and t[0][2] == "Non-fiction":
diff --git a/package/create_deb.sh b/package/create_deb.sh
new file mode 100755
index 0000000..f9773f0
--- /dev/null
+++ b/package/create_deb.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Can take two parameters: AppVersion PkgNumber
+# Default values are: 0.5.0 1
+
+# Manuskript Vars
+AppName=manuskript
+AppVersion=${1:-0.5.0}
+PkgNumber=${2:-1}
+PkgVersion=$AppVersion-$PkgNumber
+#PkgSizeInKb # find with: du -sk manuskript-0.5.0-1
+
+# Program vars
+ScriptPath="$( cd "$(dirname "$0")" ; pwd -P )"
+Dest="$ScriptPath/../dist/$AppName-$PkgVersion"
+
+echo Package directory: $Dest
+
+echo -n Creating folder structure
+mkdir -p $Dest/DEBIAN
+mkdir -p $Dest/usr/bin
+mkdir -p $Dest/usr/share/applications
+echo " [✓]"
+
+# Getting manuskript files, by downloading
+# pushd $Dest/usr/share
+# wget https://github.com/olivierkes/manuskript/archive/$AppVersion.tar.gz
+# tar -xvf $AppVersion.tar.gz
+# rm $AppVersion.tar.gz
+# mv manuskript-0.5.0 manuskript
+# popd
+
+# Using the current direction as source
+
+echo -n Copying manuskript content
+rsync -a --exclude=.git --include="*.msk" --exclude-from=.gitignore $ScriptPath/../ $Dest/usr/share/manuskript
+cp $ScriptPath/create_deb/manuskript $Dest/usr/bin/manuskript
+cp $ScriptPath/create_deb/manuskript.desktop $Dest/usr/share/applications/manuskript.desktop
+cp $ScriptPath/create_deb/control $Dest/DEBIAN/control
+
+sed -i "s/{PkgVersion}/$PkgVersion/" $Dest/DEBIAN/control
+PkgSizeInKb=$(du -sk $Dest | cut -f 1)
+sed -i "s/{PkgSizeInKb}/$PkgSizeInKb/" $Dest/DEBIAN/control
+echo " [✓]"
+
+echo -n Setting permissions
+chmod 0755 $Dest/usr/bin/manuskript
+echo " [✓]"
+
+echo Your root password might now be asked to finish setting permissions:
+sudo chown root:root -R $Dest
+
+echo Creating the package…
+dpkg -b $Dest
+
+echo -n Removing build folder
+sudo rm -r $Dest
+echo " [✓]"
+
+echo Done !
diff --git a/package/create_deb/control b/package/create_deb/control
new file mode 100644
index 0000000..a8e3296
--- /dev/null
+++ b/package/create_deb/control
@@ -0,0 +1,19 @@
+Package: manuskript
+Version: {PkgVersion}
+Maintainer: Curtis Gedak
+Description: Manuskript open source tool for writers.
+ Manuskript is an open source tool for writers. It
+ provides a rich environment to help writers create
+ their first draft and then further refine and edit
+ their masterpiece.
+Section: office, text
+Priority: optional
+Installed-Size: {PkgSizeInKb}
+Architecture: all
+Origin: Ubuntu 14.04
+Bugs: https://github.com/olivierkes/manuskript/issues
+Homepage: http://www.theologeek.ch/manuskript/
+Source: https://github.com/olivierkes/manuskript/archive/0.5.0.tar.gz
+Depends: python3, python3-pyqt5, python3-pyqt5.qtwebkit, libqt5svg5,
+ python3-lxml, zlib1g, python3-enchant, python3-markdown, pandoc
+Suggests: texlive-latex-recommended
diff --git a/package/create_deb/manuskript b/package/create_deb/manuskript
new file mode 100644
index 0000000..33e83fc
--- /dev/null
+++ b/package/create_deb/manuskript
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# manuskript - invocation python script
+#
+# Copyright (C) 2017 Olivier Keshavjee
+#
+# This file is part of Manuskript.
+#
+# Manuskript is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Manuskript is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Manuskript. If not, see .
+
+import os
+import sys
+
+sys.path.insert(1, '/usr/share/manuskript/')
+
+from manuskript import main
+
+main.run()
diff --git a/package/create_deb/manuskript.desktop b/package/create_deb/manuskript.desktop
new file mode 100644
index 0000000..c3414f8
--- /dev/null
+++ b/package/create_deb/manuskript.desktop
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Name=Manuskript
+Comment=An open source tool for writers
+Keywords=manuskript;office;write;edit;novel;text;msk
+Exec=/usr/bin/manuskript
+Terminal=false
+Type=Application
+Icon=/usr/share/manuskript/icons/Manuskript/icon-512px.png
+Categories=Office;WordProcessor;
diff --git a/package/dependency_test.py b/package/dependency_test.py
new file mode 100644
index 0000000..9f0d947
--- /dev/null
+++ b/package/dependency_test.py
@@ -0,0 +1,8 @@
+import os
+import sys
+
+realpath = os.path.realpath(__file__)
+
+sys.path.insert(1, os.path.join(os.path.dirname(realpath), '..'))
+
+from manuskript import main
diff --git a/package/prepare_osx.sh b/package/prepare_osx.sh
new file mode 100755
index 0000000..9529460
--- /dev/null
+++ b/package/prepare_osx.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+set -ev
+brew update
+brew install python3 enchant
+sudo pip3 install --upgrade pip setuptools wheel
+pip3 install pyinstaller PyQt5 lxml pyenchant
+brew install qt hunspell
+# fooling PyEnchant as described in the wiki: https://github.com/olivierkes/manuskript/wiki/Package-manuskript-for-OS-X
+sudo touch /usr/local/share/aspell