restructured project folders
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 215 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 121 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,308 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Filename: content.py
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import os, re
|
||||
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QSettings
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QListWidget, QListWidgetItem, QAbstractItemView, QPushButton, QInputDialog, QPlainTextEdit, QTextEdit, QShortcut
|
||||
|
||||
from classes import highlighter
|
||||
|
||||
import markdown
|
||||
import json
|
||||
|
||||
|
||||
# _____
|
||||
# / ___/__ ______ ___ ____ ___ ____ ________ __
|
||||
# \__ \/ / / / __ `__ \/ __ `__ \/ __ `/ ___/ / / /
|
||||
# ___/ / /_/ / / / / / / / / / / / /_/ / / / /_/ /
|
||||
# /____/\__,_/_/ /_/ /_/_/ /_/ /_/\__,_/_/ \__, /
|
||||
# /____/
|
||||
class Summary(QWidget):
|
||||
def __init__(self, parent):
|
||||
super(Summary, self).__init__(parent)
|
||||
self.parent = parent
|
||||
|
||||
self.loadJson()
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
vbox.setContentsMargins(0,0,0,0)
|
||||
|
||||
self.list = SummaryList(self)
|
||||
vbox.addWidget(self.list)
|
||||
|
||||
self.actions = SummaryActions(self)
|
||||
vbox.addWidget(self.actions)
|
||||
|
||||
self.setLayout(vbox)
|
||||
|
||||
def loadJson(self):
|
||||
jsonfilepath = os.path.join(self.parent.core.cwd,'.config/summary.json')
|
||||
sum_json = open(jsonfilepath).read()
|
||||
self.sum = json.loads(sum_json)
|
||||
|
||||
|
||||
def addItem(self, text):
|
||||
# file
|
||||
filename = re.sub(r'\W', "_", text)+".md"
|
||||
# TODO: check if file does not already exists
|
||||
filepath = os.path.join(self.parent.core.cwd,'contents',filename)
|
||||
with open(filepath, 'w') as fp:
|
||||
fp.write('#'+text)
|
||||
# json
|
||||
item = {"title":text,"file":filename}
|
||||
self.sum.append(item)
|
||||
jsonfilepath = os.path.join(self.parent.core.cwd,'.config/summary.json')
|
||||
with open(jsonfilepath, "w") as fp:
|
||||
json.dump(self.sum, fp, ensure_ascii=False, indent="\t")
|
||||
# refresh list
|
||||
self.list.addNewItem(item)
|
||||
# reload content compiler
|
||||
self.parent.core.contentcompiler.reload()
|
||||
|
||||
def recordNewList(self):
|
||||
newdata = []
|
||||
for i in range(0,self.list.count()):
|
||||
# print(self.item(i).item['title'])
|
||||
newdata.append(self.list.item(i).data)
|
||||
|
||||
# print(newdata)
|
||||
self.sum = newdata
|
||||
jsonfilepath = os.path.join(self.parent.core.cwd,'.config/summary.json')
|
||||
with open(jsonfilepath, "w") as fp:
|
||||
json.dump(newdata, fp, ensure_ascii=False, indent="\t")
|
||||
|
||||
# reload content compiler
|
||||
self.parent.core.contentcompiler.reload()
|
||||
|
||||
def reload(self):
|
||||
self.loadJson()
|
||||
self.list.setItems()
|
||||
|
||||
class SummaryList(QListWidget):
|
||||
def __init__(self, parent):
|
||||
super(SummaryList, self).__init__(parent)
|
||||
self.parent = parent
|
||||
# self.sum = sum
|
||||
# print(self.sum)
|
||||
# self.setSortingEnabled(True)
|
||||
self.setDragEnabled(True)
|
||||
self.setSelectionMode(QAbstractItemView.SingleSelection)
|
||||
self.setAcceptDrops(True)
|
||||
self.setDropIndicatorShown(True)
|
||||
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
|
||||
self.model().rowsMoved.connect(self.onRowsMoved)
|
||||
# print(self.model())
|
||||
|
||||
self.itemActivated.connect(self.onItemActivated)
|
||||
|
||||
self.setItems()
|
||||
|
||||
# self.setCurrentRow(0)
|
||||
# self.setCurrentIndex()
|
||||
# self.setCurrentItem()
|
||||
self.item(0).setSelected(True)
|
||||
# TODO: activate first item by default as it will open it with editor
|
||||
|
||||
# TODO: show activated item on the list
|
||||
# TODO: show modifed item on the list
|
||||
|
||||
def setItems(self):
|
||||
self.clear()
|
||||
# add markdown files to the list
|
||||
for itemdata in self.parent.sum:
|
||||
self.addNewItem(itemdata)
|
||||
|
||||
def onRowsMoved(self, model, start, end, dest):
|
||||
# print("onRowsMoved")
|
||||
self.parent.recordNewList()
|
||||
|
||||
def addNewItem(self, item):
|
||||
self.addItem(SummaryListWidgetItem(self,item))
|
||||
|
||||
def onItemActivated(self, item):
|
||||
# print('onItemActivated', item.data)
|
||||
self.parent.parent.editor.openFile(self.currentRow())
|
||||
|
||||
class SummaryListWidgetItem(QListWidgetItem):
|
||||
def __init__(self,parent,data):
|
||||
super(SummaryListWidgetItem, self).__init__(parent)
|
||||
self.parent = parent
|
||||
self.data = data
|
||||
|
||||
self.setText(data['title'])
|
||||
self.setToolTip(data['file'])
|
||||
|
||||
class SummaryActions(QWidget):
|
||||
def __init__(self,parent):
|
||||
super(SummaryActions, self).__init__(parent)
|
||||
|
||||
self.parent = parent
|
||||
|
||||
self.hbox = QHBoxLayout()
|
||||
self.hbox.setContentsMargins(0,0,0,0)
|
||||
|
||||
new = QPushButton("New Page", self)
|
||||
new.setShortcut('Ctrl+Shift+n')
|
||||
# new.setIcon(Icon(ico)))
|
||||
new.clicked.connect(self.onAddPage)
|
||||
self.hbox.addWidget(new)
|
||||
|
||||
delete = QPushButton("Delete Page", self)
|
||||
delete.setShortcut('Ctrl+Shift+sup')
|
||||
# delete.setIcon(Icon(ico)))
|
||||
delete.clicked.connect(self.onDeletePage)
|
||||
self.hbox.addWidget(delete)
|
||||
|
||||
|
||||
self.setLayout(self.hbox)
|
||||
|
||||
def onAddPage(self):
|
||||
text, ok = QInputDialog.getText(self, 'Input Dialog', 'Page Name:')
|
||||
if ok:
|
||||
self.parent.addItem(text)
|
||||
|
||||
def onDeletePage(self):
|
||||
print("onDeletePage")
|
||||
# TODO: get the current selected page
|
||||
# TODO: ask for confirmation for deleting the current selecred page
|
||||
# TODO: call for summary widget to delete the page
|
||||
|
||||
# ______ ___ __
|
||||
# / ____/___/ (_) /_____ _____
|
||||
# / __/ / __ / / __/ __ \/ ___/
|
||||
# / /___/ /_/ / / /_/ /_/ / /
|
||||
# /_____/\__,_/_/\__/\____/_/
|
||||
class MarkdownEditor(QWidget):
|
||||
def __init__(self,parent):
|
||||
super(MarkdownEditor, self).__init__(parent)
|
||||
self.parent = parent
|
||||
self.changed = False
|
||||
|
||||
self.hbox = QHBoxLayout()
|
||||
self.hbox.setContentsMargins(0,0,0,0)
|
||||
|
||||
self.styles = """
|
||||
background-color:white;
|
||||
color:black;
|
||||
padding:20px;
|
||||
"""
|
||||
|
||||
self.editor = QPlainTextEdit(self)
|
||||
self.editor.setStyleSheet(self.styles)
|
||||
|
||||
self.hl=highlighter.Highlighter(self.editor.document(),"md")
|
||||
self.hbox.addWidget(self.editor)
|
||||
|
||||
self.viewer = QTextEdit(self)
|
||||
self.viewer.setReadOnly(True)
|
||||
self.viewer.setStyleSheet(self.styles)
|
||||
# TODO: show all html blocks on viewer
|
||||
self.hbox.addWidget(self.viewer)
|
||||
|
||||
self.setLayout(self.hbox)
|
||||
|
||||
self.editor.textChanged.connect(self.onTextChanged)
|
||||
self.openFile()
|
||||
|
||||
self.shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
|
||||
self.shortcut.activated.connect(self.save)
|
||||
|
||||
self.refreshViewer()
|
||||
|
||||
def openFile(self, row = 0):
|
||||
# print("openFile")
|
||||
sumlist = self.parent.summary.list
|
||||
item = sumlist.item(row)
|
||||
if item:
|
||||
if not self.changed:
|
||||
self.editor.textChanged.disconnect(self.onTextChanged)
|
||||
filename = item.data['file']
|
||||
self.file = os.path.join(self.parent.core.cwd,'contents',filename)
|
||||
self.editor.clear()
|
||||
self.editor.insertPlainText(open(self.file, 'r').read())
|
||||
self.refreshViewer()
|
||||
self.editor.textChanged.connect(self.onTextChanged)
|
||||
else:
|
||||
print("Can't changed file, current id modified, please save first")
|
||||
# TODO: ask for saving current file
|
||||
|
||||
def onTextChanged(self):
|
||||
self.refreshViewer()
|
||||
if not self.changed:
|
||||
self.changed = True
|
||||
# TODO: show in list that content needs to be saved
|
||||
# i = self.tabs.currentIndex()
|
||||
# self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
|
||||
|
||||
def refreshViewer(self):
|
||||
md = self.editor.toPlainText()
|
||||
html = markdown.markdown(md)
|
||||
self.viewer.setHtml(html)
|
||||
|
||||
def save(self):
|
||||
if self.changed:
|
||||
open(self.file, 'w').write(self.editor.toPlainText())
|
||||
self.changed = False
|
||||
# i = self.tabs.currentIndex()
|
||||
# self.tabs.setTabText(i, re.sub(r'^\*\s', '', self.tabs.tabText(i)))
|
||||
# TODO: how to combine file save and project save
|
||||
|
||||
# _____ __ __
|
||||
# / ___// /_____ ______/ /__
|
||||
# \__ \/ __/ __ `/ ___/ //_/
|
||||
# ___/ / /_/ /_/ / /__/ ,<
|
||||
# /____/\__/\__,_/\___/_/|_|
|
||||
class ContentStack(QWidget):
|
||||
def __init__(self, core):
|
||||
super(ContentStack, self).__init__()
|
||||
self.core = core
|
||||
|
||||
hbox = QHBoxLayout()
|
||||
hbox.setContentsMargins(0,0,0,0)
|
||||
self.setLayout(hbox)
|
||||
|
||||
self.hsplitter = QSplitter(QtCore.Qt.Horizontal)
|
||||
|
||||
self.summary = Summary(self)
|
||||
# TODO: detect external changes (file changed or new file)
|
||||
self.hsplitter.addWidget(self.summary)
|
||||
|
||||
self.editor = MarkdownEditor(self)
|
||||
|
||||
self.hsplitter.addWidget(self.editor)
|
||||
|
||||
self.hsplitter.splitterMoved.connect(self.movedSplitter)
|
||||
|
||||
hbox.addWidget(self.hsplitter)
|
||||
|
||||
self.restorePrefs()
|
||||
|
||||
|
||||
def restorePrefs(self):
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
vals = settings.value('content/hsplitter/sizes', None)
|
||||
if vals:
|
||||
sizes = []
|
||||
for size in vals: sizes.append(int(size))
|
||||
self.hsplitter.setSizes(sizes)
|
||||
|
||||
def movedSplitter(self):
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
# print(self.hsplitter.sizes())
|
||||
settings.setValue('content/hsplitter/sizes', self.hsplitter.sizes())
|
||||
|
||||
def refresh(self):
|
||||
self.summary.reload()
|
||||
self.editor.openFile()
|
||||
@@ -0,0 +1,261 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import os, re, shutil, tempfile
|
||||
# sys,
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QSettings, QCoreApplication
|
||||
|
||||
import json
|
||||
# import git
|
||||
# from pygit2 import Repository
|
||||
|
||||
from classes import server, sasscompiler, md2html
|
||||
|
||||
# ______
|
||||
# / ____/___ ________
|
||||
# / / / __ \/ ___/ _ \
|
||||
# / /___/ /_/ / / / __/
|
||||
# \____/\____/_/ \___/
|
||||
class Core():
|
||||
def __init__(self, parent=None):
|
||||
# restore previous preferences
|
||||
self.appcwd = os.getcwd()
|
||||
|
||||
self.restorePreferences()
|
||||
self._mw = False
|
||||
self.temp = tempfile.mkdtemp()
|
||||
# print(self.temp)
|
||||
|
||||
self.tempcwd = False
|
||||
# if ther's not current project folder from restorepref
|
||||
# initaite a new temp project
|
||||
if(self.cwd == None or not os.path.isdir(self.cwd)):
|
||||
self.cwd = os.path.join(self.temp, 'cwd')
|
||||
self.tempcwd = True
|
||||
self.initnewproject()
|
||||
self.initDeamons()
|
||||
else:
|
||||
self.initDeamons()
|
||||
head, tail = os.path.split(self.cwd)
|
||||
print('tail', tail)
|
||||
self.projectname = tail
|
||||
|
||||
self.loadDocSettings()
|
||||
|
||||
|
||||
def initDeamons(self):
|
||||
self.server = server.Server(self)
|
||||
self.sasscompiler = sasscompiler.Compiler(self)
|
||||
self.contentcompiler = md2html.Compiler(self)
|
||||
|
||||
@property
|
||||
def mainwindow(self):
|
||||
return self.mainwindow
|
||||
|
||||
@mainwindow.setter
|
||||
def mainwindow(self, mw):
|
||||
if not self._mw:
|
||||
self._mw = mw
|
||||
if not self.tempcwd:
|
||||
self._mw.setWindowTitle("Cascade – "+self.cwd)
|
||||
|
||||
# ____ ____
|
||||
# / __ \________ / __/____
|
||||
# / /_/ / ___/ _ \/ /_/ ___/
|
||||
# / ____/ / / __/ __(__ )
|
||||
# /_/ /_/ \___/_/ /____/
|
||||
def restorePreferences(self):
|
||||
# print("restorePreferences")
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
# settings.clear()
|
||||
# print(settings.allKeys())
|
||||
|
||||
self.cwd = settings.value('core/cwd', None)
|
||||
self.dialog_path = settings.value('core/dialog_path', os.path.expanduser('~'))
|
||||
|
||||
self.mw_size = settings.value('mainwindow/size', QtCore.QSize(1024, 768))
|
||||
self.mw_pos = settings.value('mainwindow/pos', QtCore.QPoint(0, 0))
|
||||
self.mw_curstack = int(settings.value('mainwindow/curstack', 0))
|
||||
|
||||
def savePreferences(self):
|
||||
# print("savePreferences")
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
# print(settings.allKeys())
|
||||
|
||||
if not self.tempcwd:
|
||||
settings.setValue('core/cwd', self.cwd)
|
||||
|
||||
settings.setValue('core/dialog_path', self.dialog_path)
|
||||
|
||||
settings.setValue('mainwindow/size', self._mw.size())
|
||||
settings.setValue('mainwindow/pos', self._mw.pos())
|
||||
settings.setValue('mainwindow/curstack', self._mw.mainstack.currentIndex())
|
||||
|
||||
# ____ _____ __ __ _
|
||||
# / __ \____ _____ / ___/___ / /_/ /_(_)___ ____ ______
|
||||
# / / / / __ \/ ___/ \__ \/ _ \/ __/ __/ / __ \/ __ `/ ___/
|
||||
# / /_/ / /_/ / /__ ___/ / __/ /_/ /_/ / / / / /_/ (__ )
|
||||
# /_____/\____/\___/ /____/\___/\__/\__/_/_/ /_/\__, /____/
|
||||
# /____/
|
||||
def loadDocSettings(self):
|
||||
self.docsettings = json.loads(open(os.path.join(self.cwd,'.config/docsettings.json')).read())
|
||||
|
||||
def recordDocSettings(self,docsettings):
|
||||
# print("doc settings",docsettings)
|
||||
for key in docsettings:
|
||||
self.docsettings[key] = docsettings[key]
|
||||
jsonfilepath = os.path.join(self.cwd,'.config/docsettings.json')
|
||||
with open(jsonfilepath, "w") as fp:
|
||||
json.dump(self.docsettings, fp, ensure_ascii=False, indent="\t")
|
||||
|
||||
self.updateScss(False)
|
||||
self.updateJs()
|
||||
|
||||
def updateScss(self, reload=True):
|
||||
# print(self.docsettings)
|
||||
sassfilepath = os.path.join(self.cwd,'assets/css/setup.scss')
|
||||
# print(sassfilepath)
|
||||
sass = open(sassfilepath,"r").read()
|
||||
sets = {
|
||||
'pw':'page-width',
|
||||
'ph':'page-height',
|
||||
'mt':'page-margin-top',
|
||||
'mb':'page-margin-bottom',
|
||||
'me':'page-margin-outside',
|
||||
'mi':'page-margin-inside',
|
||||
'cs':'crop-size',
|
||||
'bs':'bleed',
|
||||
'cg':'col-gutter',
|
||||
'rg':'row-gutter',
|
||||
'lh':'line-height'
|
||||
}
|
||||
for s in sets:
|
||||
sass = re.sub(
|
||||
r'\$'+sets[s]+':\smm2pt\([0-9|\.]+\);',
|
||||
'$'+sets[s]+': mm2pt('+self.docsettings[s]+');',
|
||||
sass)
|
||||
|
||||
# $col-number: 9;
|
||||
sass = re.sub(
|
||||
r'\$col-number:\s[0-9|\.]+;',
|
||||
'$col-number: '+self.docsettings['cn']+';',
|
||||
sass)
|
||||
# $row-number: 12;
|
||||
sass = re.sub(
|
||||
r'\$row-number:\s[0-9|\.]+;',
|
||||
'$row-number: '+self.docsettings['rn']+';',
|
||||
sass)
|
||||
#$header-odd: "Cascade, default header";
|
||||
sass = re.sub(
|
||||
r'\$header-odd:\s".+";',
|
||||
'$header-odd: "'+self.docsettings['ho']+'";',
|
||||
sass)
|
||||
# $header-even: "Cascade, default header";
|
||||
sass = re.sub(
|
||||
r'\$header-even:\s".+";',
|
||||
'$header-even: "'+self.docsettings['he']+'";',
|
||||
sass)
|
||||
|
||||
# print('sass', sass)
|
||||
open(sassfilepath,"w").write(sass)
|
||||
|
||||
if reload:
|
||||
self._mw.designstack.webkitview.reload()
|
||||
|
||||
def updateJs(self, reload=True):
|
||||
# print(self.docsettings)
|
||||
jsfilepath = os.path.join(self.cwd,'assets/js/setup.js')
|
||||
# print(jsfilepath)
|
||||
js = open(jsfilepath,"r").read()
|
||||
|
||||
# $row-number: 12;
|
||||
js = re.sub(
|
||||
r'nb_page=[0-9]+;',
|
||||
'nb_page='+str(self.docsettings['np'])+';',
|
||||
js)
|
||||
|
||||
# print('sass', sass)
|
||||
open(jsfilepath,"w").write(js)
|
||||
|
||||
if reload:
|
||||
self._mw.designstack.webkitview.reload()
|
||||
|
||||
def addPage(self):
|
||||
self.docsettings['np'] = int(self.docsettings['np'])+1
|
||||
self.updateJs()
|
||||
|
||||
def rmPage(self):
|
||||
self.docsettings['np'] = int(self.docsettings['np'])-1
|
||||
self.updateJs()
|
||||
|
||||
# ____ _ __
|
||||
# / __ \_________ (_)__ _____/ /_
|
||||
# / /_/ / ___/ __ \ / / _ \/ ___/ __/
|
||||
# / ____/ / / /_/ / / / __/ /__/ /_
|
||||
# /_/ /_/ \____/_/ /\___/\___/\__/
|
||||
# /___/
|
||||
def initnewproject(self, cwd = None):
|
||||
print('initnewproject')
|
||||
if cwd == None :
|
||||
cwd = self.cwd
|
||||
|
||||
shutil.copytree(os.path.join(self.appcwd,'templates/newproject'), cwd)
|
||||
self.changeCWD(cwd)
|
||||
self.loadDocSettings()
|
||||
self.summary = json.loads(open(os.path.join(cwd,'.config/summary.json')).read())
|
||||
# TODO: try python-pygit2 arch package
|
||||
# self.repository = git.Repo.init(cwd)
|
||||
# TODO: set git config user.name & user.email
|
||||
# self.repository
|
||||
# self.repository.index.add(['assets','contents','.config'])
|
||||
# self.repository.index.commit("initial commit")
|
||||
|
||||
|
||||
def saveproject(self, cwd = None):
|
||||
if not cwd == None:
|
||||
shutil.copytree(self.cwd, cwd)
|
||||
self.tempcwd = False
|
||||
self.changeCWD(cwd)
|
||||
|
||||
def openproject(self, cwd=None):
|
||||
if not cwd == None:
|
||||
self.changeCWD(cwd)
|
||||
|
||||
# __ _______ ______
|
||||
# _____/ /_ ____ _____ ____ ____ / ____/ | / / __ \
|
||||
# / ___/ __ \/ __ `/ __ \/ __ `/ _ \/ / | | /| / / / / /
|
||||
# / /__/ / / / /_/ / / / / /_/ / __/ /___ | |/ |/ / /_/ /
|
||||
# \___/_/ /_/\__,_/_/ /_/\__, /\___/\____/ |__/|__/_____/
|
||||
# /____/
|
||||
def changeCWD(self, cwd):
|
||||
if not cwd == self.cwd:
|
||||
self.cwd = cwd
|
||||
self.server.reload()
|
||||
self.sasscompiler.reload()
|
||||
self.contentcompiler.reload()
|
||||
|
||||
if not self.tempcwd:
|
||||
self._mw.setWindowTitle("Cascade – "+self.cwd)
|
||||
head, tail = os.path.split(self.cwd)
|
||||
print('tail', projectname)
|
||||
self.projectname = tail
|
||||
self._mw.designstack.refresh()
|
||||
self._mw.contentstack.refresh()
|
||||
|
||||
# ____ _ __
|
||||
# / __ \__ __(_) /_
|
||||
# / / / / / / / / __/
|
||||
# / /_/ / /_/ / / /_
|
||||
# \___\_\__,_/_/\__/
|
||||
def quit(self):
|
||||
self.savePreferences()
|
||||
shutil.rmtree(self.temp, ignore_errors=True)
|
||||
QCoreApplication.instance().quit()
|
||||
@@ -0,0 +1,492 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Filename: design.py
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import os, re
|
||||
# sys,
|
||||
from PyQt5 import QtCore
|
||||
from PyQt5.QtCore import QUrl, QSettings, QSizeF, Qt
|
||||
from PyQt5.QtGui import QKeySequence, QFont
|
||||
from PyQt5.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QShortcut, QPushButton, QCheckBox, QSpinBox, QLabel
|
||||
from PyQt5.QtWebKit import QWebSettings
|
||||
from PyQt5.QtWebKitWidgets import QWebView, QWebInspector
|
||||
from PyQt5.QtPrintSupport import QPrintPreviewDialog, QPrinter
|
||||
|
||||
from classes import highlighter
|
||||
|
||||
|
||||
# _ __ __ _ ___
|
||||
# | | / /__ / /_| | / (_)__ _ __
|
||||
# | | /| / / _ \/ __ \ | / / / _ \ | /| / /
|
||||
# | |/ |/ / __/ /_/ / |/ / / __/ |/ |/ /
|
||||
# |__/|__/\___/_.___/|___/_/\___/|__/|__/
|
||||
class WebkitView(QWebView):
|
||||
def __init__(self, parent, core):
|
||||
self.parent = parent
|
||||
self.core = core
|
||||
self.port = core.server.port
|
||||
self.view = QWebView.__init__(self, parent)
|
||||
self.setZoomFactor(1)
|
||||
self.loadFinished.connect(self.onLoaded)
|
||||
self.load(QUrl('http://localhost:'+str(self.port)))
|
||||
self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
|
||||
# self.settings().setAttribute(QWebSettings.PluginsEnabled, True)
|
||||
|
||||
self.initPDF()
|
||||
# self.mainframe = self.page.mainFrame()
|
||||
# print(self.mainframe)
|
||||
|
||||
def onLoaded(self):
|
||||
print("WebView : onLoaded")
|
||||
self.parent.webviewtoolbar.onRefresh()
|
||||
|
||||
def initPDF(self):
|
||||
self.printer = QPrinter(QPrinter.HighResolution)
|
||||
self.printer.setFullPage(True)
|
||||
# self.printer.setPageMargins(0,0,0,0,QPrinter.Millimeter)
|
||||
self.printer.setFontEmbeddingEnabled(True)
|
||||
self.printer.setColorMode(QPrinter.Color)
|
||||
# TODO: set the page size and orientation from doc settings
|
||||
# (need to do doc settings before that)
|
||||
# self.printer.setPageSize(QPrinter.A4)
|
||||
self.printer.setPaperSize(QSizeF(210, 300), QPrinter.Millimeter)
|
||||
# self.printer.setOrientation(QPrinter.Portrait)
|
||||
self.printer.setOutputFormat(QPrinter.PdfFormat)
|
||||
self.printer.setCreator('Cascade')
|
||||
self.printer.setDocName(self.core.projectname)
|
||||
self.printer.setOutputFileName(self.core.projectname+".pdf")
|
||||
# self.setFixedWidth(1000)
|
||||
|
||||
def ongenPDF(self):
|
||||
# QPrinter::Custom
|
||||
# dialog = QPrintPreviewDialog(self.printer)
|
||||
# dialog.setWindowState(Qt.WindowMaximized)
|
||||
# dialog.paintRequested.connect(self.print_)
|
||||
# dialog.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint | Qt.WindowContextHelpButtonHint)
|
||||
# dialog.exec()
|
||||
# TODO: open a dialogue to ask where to save the pdf
|
||||
# TODO: reload webview and wait for it before printing
|
||||
# TODO: addd a progress bar
|
||||
# self.webview.
|
||||
self.print_(self.printer)
|
||||
|
||||
def refresh(self):
|
||||
self.initPDF()
|
||||
self.reload()
|
||||
|
||||
def toggleDocClass(self, c="",a=True):
|
||||
if a :
|
||||
togg = "add"
|
||||
else :
|
||||
togg = "remove"
|
||||
command = """document.documentElement.classList."""+togg+"""('"""+c+"""')"""
|
||||
self.evaluateJS(command)
|
||||
|
||||
def zoom(self,z):
|
||||
self.setZoomFactor(z/100)
|
||||
# command = """
|
||||
# var zoomLevel = """+str(z)+""" / 100;
|
||||
# var elt = document.documentElement.querySelector("#pages");
|
||||
# elt.style.webkitTransform = "scale(" + zoomLevel + ")";
|
||||
# elt.style.webkitTransformOrigin = "0 0";
|
||||
# """
|
||||
# self.evaluateJS(command)
|
||||
|
||||
def changePage(self,p=0):
|
||||
command = """
|
||||
var pageNumber = """+str(p-1)+""";
|
||||
var target = document.documentElement.querySelectorAll('.paper')[pageNumber];
|
||||
var offsetTop = target.offsetTop;
|
||||
var offsetLeft = target.offsetLeft;
|
||||
document.documentElement.querySelector('body').scrollTop = offsetTop;
|
||||
document.documentElement.querySelector('body').scrollLeft = offsetLeft;
|
||||
"""
|
||||
self.evaluateJS(command)
|
||||
|
||||
def evaluateJS(self, command):
|
||||
self.page().mainFrame().evaluateJavaScript(command)
|
||||
|
||||
# ____ __
|
||||
# / _/___ _________ ___ _____/ /_____ _____
|
||||
# / // __ \/ ___/ __ \/ _ \/ ___/ __/ __ \/ ___/
|
||||
# _/ // / / (__ ) /_/ / __/ /__/ /_/ /_/ / /
|
||||
# /___/_/ /_/____/ .___/\___/\___/\__/\____/_/
|
||||
# /_/
|
||||
class WebkitInspector(QWebInspector):
|
||||
def __init__(self, parent, webkitview):
|
||||
super(WebkitInspector, self).__init__(parent)
|
||||
self.webkitview = webkitview
|
||||
self.setPage(self.webkitview.page())
|
||||
self.showMaximized()
|
||||
# TODO: webkitinspector is disappearing when chaging tabs
|
||||
|
||||
# ______ ______
|
||||
# /_ __/___ ____ / / __ )____ ______
|
||||
# / / / __ \/ __ \/ / __ / __ `/ ___/
|
||||
# / / / /_/ / /_/ / / /_/ / /_/ / /
|
||||
# /_/ \____/\____/_/_____/\__,_/_/
|
||||
class WebViewToolBar(QWidget):
|
||||
def __init__(self, parent):
|
||||
super(WebViewToolBar, self).__init__(parent)
|
||||
self.parent = parent
|
||||
|
||||
font = QFont()
|
||||
# font.setFamily("Droid Sans Mono")
|
||||
# font.setFixedPitch(True)
|
||||
font.setPointSize(8)
|
||||
self.setFont(font)
|
||||
|
||||
self.hbox = QHBoxLayout()
|
||||
self.hbox.setContentsMargins(0,0,0,0)
|
||||
|
||||
self.preview = QCheckBox('Prev&iew', self)
|
||||
self.preview.stateChanged.connect(self.onPreview)
|
||||
self.hbox.addWidget(self.preview)
|
||||
|
||||
self.debug = QCheckBox('Deb&ug', self)
|
||||
self.debug.stateChanged.connect(self.onDebug)
|
||||
self.hbox.addWidget(self.debug)
|
||||
|
||||
self.grid = QCheckBox('&Grid', self)
|
||||
self.grid.stateChanged.connect(self.onGrid)
|
||||
self.hbox.addWidget(self.grid)
|
||||
|
||||
self.spread = QCheckBox('&Spread', self)
|
||||
self.spread.stateChanged.connect(self.onSpread)
|
||||
self.hbox.addWidget(self.spread)
|
||||
|
||||
self.facing = QCheckBox('Fa&cing', self)
|
||||
self.facing.stateChanged.connect(self.onFacing)
|
||||
self.hbox.addWidget(self.facing)
|
||||
#
|
||||
self.hbox.addStretch()
|
||||
#
|
||||
# zoom
|
||||
self.hbox.addWidget(QLabel("Zoom:"))
|
||||
self.zoom = QSpinBox(self)
|
||||
self.zoom.setMinimum(-100)
|
||||
self.zoom.setMaximum(200)
|
||||
self.zoom.setSingleStep(10)
|
||||
self.zoom.setValue(90)
|
||||
self.zoom.valueChanged.connect(self.onZoomChanged)
|
||||
self.hbox.addWidget(self.zoom)
|
||||
|
||||
# page
|
||||
self.gotopage = QLabel("Go to Page: /"+self.parent.core.docsettings['np'])
|
||||
self.hbox.addWidget(self.gotopage)
|
||||
self.page = QSpinBox(self)
|
||||
self.page.setMinimum(1)
|
||||
self.page.setMaximum(int(self.parent.core.docsettings['np']))
|
||||
self.page.valueChanged.connect(self.onChangePage)
|
||||
self.hbox.addWidget(self.page)
|
||||
|
||||
self.addpage = QPushButton("&Add Page", self)
|
||||
self.addpage.clicked.connect(self.onAddPage)
|
||||
self.hbox.addWidget(self.addpage)
|
||||
self.rmpage = QPushButton("Re&move Page", self)
|
||||
self.rmpage.clicked.connect(self.onRmPage)
|
||||
self.hbox.addWidget(self.rmpage)
|
||||
|
||||
#
|
||||
self.hbox.addStretch()
|
||||
#
|
||||
self.reload = QPushButton("&Reload", self)
|
||||
# self.reload.setShortcut('Ctrl+Shift+r')
|
||||
# TODO: how to define same shortcut in different places
|
||||
# self.reload.setIcon(Icon(ico)))
|
||||
self.reload.clicked.connect(self.onReload)
|
||||
self.hbox.addWidget(self.reload)
|
||||
|
||||
self.genpdf = QPushButton("&PDF", self)
|
||||
# self.genpdf.setShortcut('Ctrl+Shift+r')
|
||||
# TODO: how to define same shortcut in different places
|
||||
# self.genpdf.setIcon(Icon(ico)))
|
||||
self.genpdf.clicked.connect(self.onGenPDF)
|
||||
self.hbox.addWidget(self.genpdf)
|
||||
|
||||
self.setLayout(self.hbox)
|
||||
|
||||
# def onCheckboxAction(self, box):
|
||||
# self.parent.webkitview.toggleDocClass(box, self[box].isChecked())
|
||||
# self.recToolbarState(box, self[box].isChecked())
|
||||
|
||||
def onPreview(self):
|
||||
print('Toolbar : onPreview', self.preview.isChecked())
|
||||
self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked())
|
||||
self.recToolbarState('preview', self.preview.isChecked())
|
||||
|
||||
def onDebug(self):
|
||||
self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked())
|
||||
self.recToolbarState('debug', self.debug.isChecked())
|
||||
|
||||
def onGrid(self):
|
||||
self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked())
|
||||
self.recToolbarState('grid', self.grid.isChecked())
|
||||
|
||||
def onSpread(self):
|
||||
self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked())
|
||||
self.recToolbarState('spread', self.spread.isChecked())
|
||||
|
||||
def onFacing(self):
|
||||
self.parent.webkitview.toggleDocClass('facing', self.facing.isChecked())
|
||||
self.recToolbarState('facing', self.facing.isChecked())
|
||||
|
||||
def onZoomChanged(self,i):
|
||||
# print("onZoomChanged : "+str(i))
|
||||
self.parent.webkitview.zoom(i)
|
||||
|
||||
def onZoomOn(self):
|
||||
# print("onZoomOn")
|
||||
self.zoom.setValue(self.zoom.value()+self.zoom.singleStep())
|
||||
|
||||
def onZoomOut(self):
|
||||
# print("onZoomOut")
|
||||
self.zoom.setValue(self.zoom.value()-self.zoom.singleStep())
|
||||
|
||||
def onChangePage(self, i):
|
||||
# print("onChangePage : "+str(i))
|
||||
self.parent.webkitview.changePage(i)
|
||||
|
||||
def onNextPage(self):
|
||||
# print('onNextPage')
|
||||
self.page.setValue(self.page.value()+self.page.singleStep())
|
||||
|
||||
def onPrevPage(self):
|
||||
# print('onPrevPage')
|
||||
self.page.setValue(self.page.value()-self.page.singleStep())
|
||||
|
||||
def onAddPage(self):
|
||||
# print("onAddPage")
|
||||
self.parent.core.addPage()
|
||||
|
||||
def onRmPage(self):
|
||||
# print("onAddPage")
|
||||
self.parent.core.rmPage()
|
||||
|
||||
def onReload(self):
|
||||
# print("onReload")
|
||||
self.parent.webkitview.reload()
|
||||
|
||||
def onGenPDF(self):
|
||||
print("onGenPDF")
|
||||
self.parent.webkitview.ongenPDF()
|
||||
|
||||
def recToolbarState(self, prop, val):
|
||||
# print('recToolbarState : '+prop, val)
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
settings.setValue('design/toolbar/'+prop, val)
|
||||
# print('recToolbarState after : '+prop, settings.value('design/toolbar/'+prop))
|
||||
|
||||
def onRefresh(self):
|
||||
# apply precedent toolbar state
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
self.preview.setChecked(bool(settings.value('design/toolbar/preview', False, type=bool)))
|
||||
self.debug.setChecked(bool(settings.value('design/toolbar/debug', False, type=bool)))
|
||||
self.grid.setChecked(bool(settings.value('design/toolbar/grid', False, type=bool)))
|
||||
self.spread.setChecked(bool(settings.value('design/toolbar/spread', False, type=bool)))
|
||||
self.facing.setChecked(bool(settings.value('design/toolbar/facing', False, type=bool)))
|
||||
# trigger webview changes
|
||||
self.parent.webkitview.toggleDocClass('preview', self.preview.isChecked())
|
||||
self.parent.webkitview.toggleDocClass('debug', self.debug.isChecked())
|
||||
self.parent.webkitview.toggleDocClass('grid', self.grid.isChecked())
|
||||
self.parent.webkitview.toggleDocClass('spread', self.spread.isChecked())
|
||||
|
||||
self.gotopage.setText("Go to Page: /"+str(self.parent.core.docsettings['np']))
|
||||
self.page.setMaximum(int(self.parent.core.docsettings['np']))
|
||||
self.parent.webkitview.changePage(self.page.value())
|
||||
# ______ ___ __
|
||||
# / ____/___/ (_) /_____ _____
|
||||
# / __/ / __ / / __/ __ \/ ___/
|
||||
# / /___/ /_/ / / /_/ /_/ / /
|
||||
# /_____/\__,_/_/\__/\____/_/
|
||||
class CodeEditor(QPlainTextEdit):
|
||||
def __init__(self, parent, core, tabs, file, mode):
|
||||
super(CodeEditor, self).__init__()
|
||||
self.parent = parent
|
||||
self.core = core
|
||||
self.tabs = tabs
|
||||
self.file = file
|
||||
self.hl= highlighter.Highlighter(self.document(),mode)
|
||||
self.setText()
|
||||
self.setTabStopWidth(15)
|
||||
|
||||
self.textChanged.connect(self.onTextChanged)
|
||||
|
||||
self.save_shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
|
||||
self.save_shortcut.activated.connect(self.save)
|
||||
|
||||
def setText(self):
|
||||
# try:
|
||||
# self.textChanged.disconnect(self.onTextChanged)
|
||||
# except Exception as e:
|
||||
# print(e)
|
||||
|
||||
self.filepath = os.path.join(self.core.cwd,self.file)
|
||||
self.clear()
|
||||
self.insertPlainText(open(self.filepath, 'r').read())
|
||||
self.changed = False
|
||||
|
||||
font = QFont()
|
||||
font.setFamily("Droid Sans Mono")
|
||||
font.setFixedPitch(True)
|
||||
font.setPointSize(12)
|
||||
self.setFont(font)
|
||||
|
||||
|
||||
def onTextChanged(self):
|
||||
print('textChanged')
|
||||
# print(self.toPlainText())
|
||||
# open(self.filepath, 'w').write(self.toPlainText())
|
||||
if not self.changed:
|
||||
self.changed = True
|
||||
i = self.tabs.currentIndex()
|
||||
# self.tabs.setTabText(i, re.sub(r'^\**\s', '', self.tabs.tabText(i)))
|
||||
self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
|
||||
# TODO: indicate that webview needs to be reloaded
|
||||
|
||||
def save(self):
|
||||
if self.changed:
|
||||
open(self.filepath, 'w').write(self.toPlainText())
|
||||
i = self.tabs.currentIndex()
|
||||
self.tabs.setTabText(i, re.sub(r'^\**\s', '', self.tabs.tabText(i)))
|
||||
self.parent.reloadView()
|
||||
self.changed = False
|
||||
# TODO: how to combine file save and project save
|
||||
|
||||
class Editor(QWidget):
|
||||
def __init__(self, parent):
|
||||
super(Editor, self).__init__()
|
||||
self.parent = parent
|
||||
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setContentsMargins(0,0,0,0)
|
||||
|
||||
# Initialize tab screen
|
||||
self.tabs = QTabWidget()
|
||||
|
||||
self.scsstab = CodeEditor(self, self.parent.core, self.tabs, 'assets/css/styles.scss', "sass")
|
||||
self.jstab = CodeEditor(self, self.parent.core, self.tabs, 'assets/js/script.js', 'js')
|
||||
|
||||
# Add tabs
|
||||
self.tabs.addTab(self.scsstab,"scss")
|
||||
self.tabs.addTab(self.jstab,"js")
|
||||
|
||||
# Add tabs to widget
|
||||
self.layout.addWidget(self.tabs)
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def refresh(self):
|
||||
self.scsstab.setText()
|
||||
self.jstab.setText()
|
||||
|
||||
def reloadView(self):
|
||||
self.parent.webkitview.reload()
|
||||
|
||||
# _____ __ __
|
||||
# / ___// /_____ ______/ /__
|
||||
# \__ \/ __/ __ `/ ___/ //_/
|
||||
# ___/ / /_/ /_/ / /__/ ,<
|
||||
# /____/\__/\__,_/\___/_/|_|
|
||||
class DesignStack(QWidget):
|
||||
def __init__(self, core):
|
||||
super(DesignStack, self).__init__()
|
||||
self.core = core
|
||||
|
||||
# self.grid = QGridLayout()
|
||||
self.hbox = QHBoxLayout()
|
||||
self.hbox.setContentsMargins(0,0,0,0)
|
||||
self.setLayout(self.hbox)
|
||||
|
||||
|
||||
self.webview = QWidget()
|
||||
self.webview.vbox = QVBoxLayout()
|
||||
self.webview.setLayout(self.webview.vbox)
|
||||
self.webview.vbox.setContentsMargins(0,0,0,0)
|
||||
|
||||
# toolbar
|
||||
self.webviewtoolbar = WebViewToolBar(self)
|
||||
self.webview.vbox.addWidget(self.webviewtoolbar)
|
||||
|
||||
# webkitview
|
||||
self.webkitview = WebkitView(self, core)
|
||||
|
||||
# webkitinspector
|
||||
self.webkitinspector = WebkitInspector(self, self.webkitview)
|
||||
|
||||
# V layout
|
||||
self.vsplitter = QSplitter(QtCore.Qt.Vertical)
|
||||
self.vsplitter.addWidget(self.webkitview)
|
||||
self.vsplitter.addWidget(self.webkitinspector)
|
||||
self.vsplitter.splitterMoved.connect(self.movedSplitter)
|
||||
|
||||
self.webview.vbox.addWidget(self.vsplitter)
|
||||
|
||||
# H layout
|
||||
self.hsplitter = QSplitter(QtCore.Qt.Horizontal)
|
||||
self.hsplitter.addWidget(self.webview)
|
||||
|
||||
# editor
|
||||
self.editor = Editor(self)
|
||||
self.hsplitter.addWidget(self.editor)
|
||||
|
||||
self.hsplitter.splitterMoved.connect(self.movedSplitter)
|
||||
|
||||
self.hbox.addWidget(self.hsplitter)
|
||||
|
||||
self.restorePrefs()
|
||||
|
||||
def toggleInspector(self):
|
||||
self.webkitinspector.setVisible(not self.webkitinspector.isVisible())
|
||||
|
||||
def initShortcuts(self):
|
||||
# inspector
|
||||
shortcut = QShortcut(self)
|
||||
shortcut.setKey("F12")
|
||||
shortcut.activated.connect(self.toggleInspector)
|
||||
self.webkitinspector.setVisible(False)
|
||||
|
||||
# pages
|
||||
# self.pagenext_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.ShiftModifier+Qt.Key_Right), self)
|
||||
# self.pagenext_shortcut.activated.connect(self.webviewtoolbar.onNextPage)
|
||||
# self.pageprev_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.ShiftModifier+Qt.Key_Left), self)
|
||||
# self.pageprev_shortcut.activated.connect(self.webviewtoolbar.onPrevPage)
|
||||
|
||||
# zoom
|
||||
# self.zoomon_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.Key_Plus), self)
|
||||
# self.zoomon_shortcut.activated.connect(self.webviewtoolbar.onZoomOn)
|
||||
# self.zoomout_shortcut = QShortcut(QKeySequence(Qt.ControlModifier+Qt.Key_Minus), self)
|
||||
# self.zoomout_shortcut.activated.connect(self.webviewtoolbar.onZoomOut)
|
||||
|
||||
|
||||
|
||||
def restorePrefs(self):
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
print(settings.value('design/vsplitter/sizes', self.vsplitter.sizes()))
|
||||
vals = settings.value('design/vsplitter/sizes', None)
|
||||
if vals:
|
||||
sizes = []
|
||||
for size in vals: sizes.append(int(size))
|
||||
self.vsplitter.setSizes(sizes)
|
||||
|
||||
vals = settings.value('design/hsplitter/sizes', None)
|
||||
if vals:
|
||||
sizes = []
|
||||
for size in vals: sizes.append(int(size))
|
||||
self.hsplitter.setSizes(sizes)
|
||||
|
||||
def movedSplitter(self):
|
||||
settings = QSettings('FiguresLibres', 'Cascade')
|
||||
# print(self.vsplitter.sizes())
|
||||
settings.setValue('design/vsplitter/sizes', self.vsplitter.sizes())
|
||||
settings.setValue('design/hsplitter/sizes', self.hsplitter.sizes())
|
||||
|
||||
def refresh(self):
|
||||
self.editor.refresh()
|
||||
self.webkitview.refresh()
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import os
|
||||
# from PyQt5.QtCore import QLine
|
||||
from PyQt5.QtGui import QIcon, QIntValidator
|
||||
from PyQt5.QtWidgets import QWidget, QLabel, QDialog, QGroupBox, QDialogButtonBox, QVBoxLayout, QHBoxLayout, QFormLayout, QLineEdit, QComboBox, QSpinBox, QFrame
|
||||
|
||||
class DocsetDialog(QDialog):
|
||||
def __init__(self, parent):
|
||||
super(DocsetDialog, self).__init__(parent)
|
||||
self.parent = parent
|
||||
self.createFormGroupBox()
|
||||
|
||||
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
buttonBox.accepted.connect(self.accept)
|
||||
buttonBox.rejected.connect(self.reject)
|
||||
|
||||
mainLayout = QVBoxLayout()
|
||||
mainLayout.addWidget(self.formGroupBox)
|
||||
mainLayout.addWidget(buttonBox)
|
||||
self.setLayout(mainLayout)
|
||||
|
||||
self.setWindowTitle("Document settings")
|
||||
|
||||
def createFormGroupBox(self):
|
||||
ds = self.parent.core.docsettings
|
||||
|
||||
self.formGroupBox = QWidget()
|
||||
vbox = QVBoxLayout()
|
||||
vbox.setContentsMargins(0,0,0,0)
|
||||
self.formGroupBox.setLayout(vbox)
|
||||
|
||||
topGroupBox = QWidget()
|
||||
topformlayout = QFormLayout()
|
||||
topformlayout.setContentsMargins(0,0,0,0)
|
||||
topGroupBox.setLayout(topformlayout)
|
||||
vbox.addWidget(topGroupBox)
|
||||
|
||||
colsGroupBox = QWidget()
|
||||
hbox = QHBoxLayout()
|
||||
hbox.setContentsMargins(0,0,0,0)
|
||||
colsGroupBox.setLayout(hbox)
|
||||
vbox.addWidget(colsGroupBox)
|
||||
|
||||
leftGroupBox = QGroupBox()
|
||||
leftformlayout = QFormLayout()
|
||||
# leftformlayout.setContentsMargins(0,0,0,0)
|
||||
leftGroupBox.setLayout(leftformlayout)
|
||||
hbox.addWidget(leftGroupBox)
|
||||
|
||||
rightGroupBox = QGroupBox()
|
||||
rightformlayout = QFormLayout()
|
||||
# rightformlayout.setContentsMargins(0,0,0,0)
|
||||
rightGroupBox.setLayout(rightformlayout)
|
||||
hbox.addWidget(rightGroupBox)
|
||||
|
||||
# headers
|
||||
self.headerOdd = QLineEdit(str(ds['ho']))
|
||||
topformlayout.addRow(QLabel("Header odd:"), self.headerOdd)
|
||||
self.headerEven = QLineEdit(str(ds['he']))
|
||||
topformlayout.addRow(QLabel("Header even:"), self.headerEven)
|
||||
|
||||
self.np = NumLineEdit(str(ds['np']),3,True)
|
||||
leftformlayout.addRow(QLabel("Numbers of Pages:"), self.np)
|
||||
#
|
||||
leftformlayout.addRow(Line(self))
|
||||
#
|
||||
self.pw = NumLineEdit(str(ds['pw']))
|
||||
leftformlayout.addRow(QLabel("Page Width (mm):"), self.pw)
|
||||
self.ph = NumLineEdit(str(ds['ph']))
|
||||
leftformlayout.addRow(QLabel("Page Height (mm):"), self.ph)
|
||||
#
|
||||
leftformlayout.addRow(Line(self))
|
||||
#
|
||||
self.mt = NumLineEdit(str(ds['mt']),3)
|
||||
leftformlayout.addRow(QLabel("Margin Top (mm):"), self.mt)
|
||||
self.mb = NumLineEdit(str(ds['mb']),3)
|
||||
leftformlayout.addRow(QLabel("Margin Bottom (mm):"), self.mb)
|
||||
self.mi = NumLineEdit(str(ds['mi']),3)
|
||||
leftformlayout.addRow(QLabel("Margin inner (mm):"), self.mi)
|
||||
self.me = NumLineEdit(str(ds['me']),3)
|
||||
leftformlayout.addRow(QLabel("Margin external (mm):"), self.me)
|
||||
|
||||
#
|
||||
self.cs = NumLineEdit(str(ds['cs']),3)
|
||||
rightformlayout.addRow(QLabel("Crop size (mm):"), self.cs)
|
||||
self.bs = NumLineEdit(str(ds['bs']),3)
|
||||
rightformlayout.addRow(QLabel("Bleed size (mm):"), self.bs)
|
||||
#
|
||||
rightformlayout.addRow(Line(self))
|
||||
#
|
||||
self.cn = NumLineEdit(str(ds['cn']),2,True)
|
||||
rightformlayout.addRow(QLabel("Columns number:"), self.cn)
|
||||
self.cg = NumLineEdit(str(ds['cg']),2)
|
||||
rightformlayout.addRow(QLabel("Columns gutters:"), self.cg)
|
||||
#
|
||||
self.rn = NumLineEdit(str(ds['rn']),2,True)
|
||||
rightformlayout.addRow(QLabel("Rows number:"), self.rn)
|
||||
self.rg = NumLineEdit(str(ds['rg']),2)
|
||||
rightformlayout.addRow(QLabel("Rows gutters:"), self.rg)
|
||||
#
|
||||
rightformlayout.addRow(Line(self))
|
||||
#
|
||||
self.lh = NumLineEdit(str(ds['lh']),2,True)
|
||||
rightformlayout.addRow(QLabel("Line height:"), self.lh)
|
||||
|
||||
# self.formGroupBox.setLayout(layout)
|
||||
|
||||
class NumLineEdit(QLineEdit):
|
||||
def __init__(self, parent, ml=5, int=False, mw=60):
|
||||
super(NumLineEdit, self).__init__(parent)
|
||||
self.setFixedWidth(mw)
|
||||
self.setMaxLength(ml)
|
||||
if int:
|
||||
self.setValidator(QIntValidator())
|
||||
else:
|
||||
# TODO: set float validator
|
||||
self.setValidator(QIntValidator())
|
||||
|
||||
class Line(QFrame):
|
||||
def __init__(self, parent):
|
||||
super(Line, self).__init__(parent)
|
||||
self.setFrameShape(QFrame.HLine)
|
||||
self.setFrameShadow(QFrame.Sunken)
|
||||
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 02-06-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Filename: highlighter.py
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
# based on code from :
|
||||
# Copyright (C) 2008 Christophe Kibleur <kib2@free.fr>
|
||||
|
||||
# TODO : see pyQode for more features https://github.com/pyQode
|
||||
|
||||
import sys
|
||||
import re
|
||||
# from PyQt5 import QtCore
|
||||
from PyQt5.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter
|
||||
from pygments import highlight
|
||||
from pygments.lexers import *
|
||||
from pygments.formatter import Formatter
|
||||
from pygments.styles import get_all_styles, get_style_by_name
|
||||
# import time
|
||||
|
||||
|
||||
def hex2QColor(c):
|
||||
r=int(c[0:2],16)
|
||||
g=int(c[2:4],16)
|
||||
b=int(c[4:6],16)
|
||||
return QColor(r,g,b)
|
||||
|
||||
|
||||
class QFormatter(Formatter):
|
||||
def __init__(self, linenos=True, style="default"): #, style
|
||||
Formatter.__init__(self)
|
||||
self.data=[]
|
||||
self.style = get_style_by_name(style)
|
||||
# self.linenos = linenos
|
||||
|
||||
# Create a dictionary of text styles, indexed
|
||||
# by pygments token names, containing QTextCharFormat
|
||||
# instances according to pygments' description
|
||||
# of each style
|
||||
|
||||
self.styles={}
|
||||
for token, style in self.style:
|
||||
qtf=QTextCharFormat()
|
||||
|
||||
if style['color']:
|
||||
qtf.setForeground(hex2QColor(style['color']))
|
||||
if style['bgcolor']:
|
||||
qtf.setBackground(hex2QColor(style['bgcolor']))
|
||||
if style['bold']:
|
||||
qtf.setFontWeight(QFont.Bold)
|
||||
if style['italic']:
|
||||
qtf.setFontItalic(True)
|
||||
if style['underline']:
|
||||
qtf.setFontUnderline(True)
|
||||
|
||||
self.styles[str(token)]=qtf
|
||||
|
||||
def format(self, tokensource, outfile):
|
||||
global styles
|
||||
# We ignore outfile, keep output in a buffer
|
||||
self.data=[]
|
||||
|
||||
# Just store a list of styles, one for each character
|
||||
# in the input. Obviously a smarter thing with
|
||||
# offsets and lengths is a good idea!
|
||||
for ttype, value in tokensource:
|
||||
l=len(value)
|
||||
t=str(ttype)
|
||||
self.data.extend([self.styles[t],]*l)
|
||||
|
||||
|
||||
class Highlighter(QSyntaxHighlighter):
|
||||
|
||||
def __init__(self, parent, mode):
|
||||
QSyntaxHighlighter.__init__(self, parent)
|
||||
|
||||
# styles = list(get_all_styles())
|
||||
# print(styles)
|
||||
|
||||
# Keep the formatter and lexer, initializing them
|
||||
# may be costly.
|
||||
if not mode == "md":
|
||||
self.formatter=QFormatter(linenos=True, style="monokai")
|
||||
else:
|
||||
self.formatter=QFormatter(linenos=False, style="github")
|
||||
|
||||
self.lexer=get_lexer_by_name(mode)
|
||||
|
||||
def highlightBlock(self, text):
|
||||
"""Takes a block, applies format to the document.
|
||||
according to what's in it.
|
||||
"""
|
||||
|
||||
# I need to know where in the document we are,
|
||||
# because our formatting info is global to
|
||||
# the document
|
||||
cb = self.currentBlock()
|
||||
p = cb.position()
|
||||
|
||||
# The \n is not really needed, but sometimes
|
||||
# you are in an empty last block, so your position is
|
||||
# **after** the end of the document.
|
||||
text=str(self.document().toPlainText())+'\n'
|
||||
|
||||
# Yes, re-highlight the whole document.
|
||||
# There **must** be some optimizacion possibilities
|
||||
# but it seems fast enough.
|
||||
highlight(text,self.lexer,self.formatter)
|
||||
|
||||
# Just apply the formatting to this block.
|
||||
# For titles, it may be necessary to backtrack
|
||||
# and format a couple of blocks **earlier**.
|
||||
for i in range(len(str(text))):
|
||||
try:
|
||||
self.setFormat(i,1,self.formatter.data[p+i])
|
||||
except IndexError:
|
||||
pass
|
||||
@@ -0,0 +1,272 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import os
|
||||
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QMainWindow, QAction, QWidget, QLabel, QStackedWidget, QFileDialog, QMessageBox
|
||||
|
||||
from classes import design, content, docsetdialog
|
||||
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self, core):
|
||||
super(MainWindow, self).__init__()
|
||||
|
||||
# load core class
|
||||
self.core = core
|
||||
|
||||
self.setWindowTitle("Cascade")
|
||||
self.setWindowIcon(QIcon(os.path.join(self.core.appcwd,'assets/images/icon.png')))
|
||||
|
||||
self.resize(self.core.mw_size)
|
||||
self.move(self.core.mw_pos)
|
||||
|
||||
self.initMenuBar()
|
||||
|
||||
self.initMainStack()
|
||||
|
||||
self.show()
|
||||
|
||||
# __ ___ ____
|
||||
# / |/ /__ ____ __ __/ __ )____ ______
|
||||
# / /|_/ / _ \/ __ \/ / / / __ / __ `/ ___/
|
||||
# / / / / __/ / / / /_/ / /_/ / /_/ / /
|
||||
# /_/ /_/\___/_/ /_/\__,_/_____/\__,_/_/
|
||||
def initMenuBar(self):
|
||||
# menu bar
|
||||
bar = self.menuBar()
|
||||
file = bar.addMenu("&File")
|
||||
|
||||
new = QAction("&New Project",self)
|
||||
new.setShortcut("Ctrl+n")
|
||||
file.addAction(new)
|
||||
|
||||
open = QAction("&Open",self)
|
||||
open.setShortcut("Ctrl+o")
|
||||
file.addAction(open)
|
||||
|
||||
self.save_action = QAction("&Save Project as",self)
|
||||
self.save_action.setShortcut("Ctrl+Shift+s")
|
||||
file.addAction(self.save_action)
|
||||
|
||||
self.save_action = QAction("&Save Project as",self)
|
||||
self.save_action.setShortcut("Ctrl+Shift+s")
|
||||
file.addAction(self.save_action)
|
||||
|
||||
self.docset_action = QAction("&Document Settings",self)
|
||||
self.docset_action.setShortcut("Ctrl+d")
|
||||
file.addAction(self.docset_action)
|
||||
|
||||
self.pdf_action = QAction("&PDF",self)
|
||||
self.pdf_action.setShortcut("Ctrl+p")
|
||||
file.addAction(self.pdf_action)
|
||||
|
||||
self.quit_action = QAction("&Quit",self)
|
||||
self.quit_action.setShortcut("Ctrl+q")
|
||||
file.addAction(self.quit_action)
|
||||
|
||||
file.triggered[QAction].connect(self.onfilemenutrigger)
|
||||
|
||||
# edit menu
|
||||
edit = bar.addMenu("&Edit")
|
||||
# edit.addAction("©")
|
||||
# edit.addAction("&paste")
|
||||
edit.addAction("&build")
|
||||
|
||||
self.reload_action = QAction("&Reload",self)
|
||||
self.reload_action.setShortcut("Ctrl+r")
|
||||
edit.addAction(self.reload_action)
|
||||
|
||||
edit.addAction("&preferences")
|
||||
|
||||
edit.triggered[QAction].connect(self.oneditmenutrigger)
|
||||
|
||||
|
||||
# view menu
|
||||
view = bar.addMenu("&View")
|
||||
|
||||
designview = QAction("&Design",self)
|
||||
designview.setShortcut("F1")
|
||||
view.addAction(designview)
|
||||
|
||||
contentview = QAction("&Content",self)
|
||||
contentview.setShortcut("F2")
|
||||
view.addAction(contentview)
|
||||
|
||||
versionview = QAction("&Version",self)
|
||||
versionview.setShortcut("F3")
|
||||
view.addAction(versionview)
|
||||
|
||||
view.triggered[QAction].connect(self.onviewmenutrigger)
|
||||
|
||||
# about menu
|
||||
about = bar.addMenu("About")
|
||||
about.addAction("&Website")
|
||||
|
||||
def onfilemenutrigger(self, q):
|
||||
print(q.text()+" is triggered")
|
||||
if q.text() == "&New Project":
|
||||
self.newprojectdialogue()
|
||||
elif q.text() == "&Open":
|
||||
self.openprojectdialogue()
|
||||
elif q.text() == "&Save Project as":
|
||||
self.saveprojectdialogue()
|
||||
elif q.text() == "&Document Settings":
|
||||
self.onDocSettings()
|
||||
elif q.text() == "&PDF":
|
||||
self.genPDF()
|
||||
elif q.text() == "&Quit":
|
||||
self.quit()
|
||||
|
||||
def openprojectdialogue(self):
|
||||
print("open")
|
||||
dialog = QFileDialog()
|
||||
dialog.setFileMode(QFileDialog.Directory)
|
||||
dialog.setAcceptMode(QFileDialog.AcceptOpen)
|
||||
options = QFileDialog.DontResolveSymlinks | QFileDialog.ShowDirsOnly
|
||||
folder = dialog.getExistingDirectory(
|
||||
self,
|
||||
'Open Project',
|
||||
self.core.dialog_path,
|
||||
options
|
||||
)
|
||||
try:
|
||||
head, tail = os.path.split(folder)
|
||||
self.core.dialog_path = head
|
||||
# TODO: check if is cascade folder
|
||||
print(folder)
|
||||
if os.path.isdir(folder):
|
||||
self.core.openproject(folder)
|
||||
else:
|
||||
print("folder doesn't exists")
|
||||
except Exception as e:
|
||||
print('Exception', e)
|
||||
pass
|
||||
|
||||
def newprojectdialogue(self):
|
||||
dialog = QFileDialog()
|
||||
dialog.setFileMode(QFileDialog.Directory)
|
||||
dialog.setAcceptMode(QFileDialog.AcceptOpen)
|
||||
projectname = dialog.getSaveFileName(
|
||||
self,
|
||||
'New Project',
|
||||
self.core.dialog_path
|
||||
)[0]
|
||||
# TODO: no file type
|
||||
try:
|
||||
head, tail = os.path.split(projectname)
|
||||
self.core.dialog_path = head
|
||||
if not os.path.isdir(projectname):
|
||||
self.core.initnewproject(projectname)
|
||||
else:
|
||||
print("folder already exists")
|
||||
# TODO: check if is cascade folder
|
||||
except Exception as e:
|
||||
print('Exception', e)
|
||||
pass
|
||||
|
||||
def saveprojectdialogue(self, quit=False):
|
||||
dialog = QFileDialog()
|
||||
dialog.setFileMode(QFileDialog.Directory)
|
||||
dialog.setAcceptMode(QFileDialog.AcceptOpen)
|
||||
projectname = dialog.getSaveFileName(
|
||||
self,
|
||||
'Save Project',
|
||||
self.core.dialog_path
|
||||
)[0]
|
||||
# TODO: no file type
|
||||
try:
|
||||
head, tail = os.path.split(projectname)
|
||||
self.core.dialog_path = head
|
||||
if not os.path.isdir(projectname):
|
||||
self.core.saveproject(projectname)
|
||||
if quit:
|
||||
self.quit()
|
||||
else:
|
||||
print("folder already exists")
|
||||
# TODO: check if is cascade folder
|
||||
except Exception as e:
|
||||
print('Exception', e)
|
||||
pass
|
||||
|
||||
def onDocSettings(self):
|
||||
d = docsetdialog.DocsetDialog(self)
|
||||
d.exec_()
|
||||
self.core.recordDocSettings({
|
||||
"ho":d.headerOdd.text(),
|
||||
"he":d.headerEven.text(),
|
||||
"np":d.np.text(),
|
||||
"pw":d.pw.text(),
|
||||
"ph":d.ph.text(),
|
||||
"mt":d.mt.text(),
|
||||
"mb":d.mb.text(),
|
||||
"me":d.me.text(),
|
||||
"mi":d.mi.text(),
|
||||
"cs":d.cs.text(),
|
||||
"bs":d.bs.text(),
|
||||
"cn":d.cn.text(),
|
||||
"cg":d.cg.text(),
|
||||
"rn":d.rn.text(),
|
||||
"rg":d.rg.text(),
|
||||
"lh":d.lh.text()
|
||||
})
|
||||
|
||||
def genPDF(self):
|
||||
print('PDF')
|
||||
self.designstack.webkitview.ongenPDF()
|
||||
|
||||
def quit(self):
|
||||
print("Quit")
|
||||
if self.core.tempcwd:
|
||||
buttonReply = QMessageBox.question(self, 'Project Not Saved', "Do you want to save your current project before quiting?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.Cancel)
|
||||
if buttonReply == QMessageBox.Yes:
|
||||
self.saveprojectdialogue(quit=True)
|
||||
if buttonReply == QMessageBox.No:
|
||||
self.core.quit()
|
||||
else:
|
||||
self.core.quit()
|
||||
|
||||
def oneditmenutrigger(self, q):
|
||||
print(q.text()+" is triggered")
|
||||
if q.text() == "&Reload":
|
||||
self.designstack.webkitview.reload()
|
||||
|
||||
def onviewmenutrigger(self, q):
|
||||
print(q.text()+" is triggered")
|
||||
if q.text() == "&Design":
|
||||
self.mainstack.setCurrentIndex(0)
|
||||
elif q.text() == "&Content":
|
||||
self.mainstack.setCurrentIndex(1)
|
||||
elif q.text() == "&Version":
|
||||
self.mainstack.setCurrentIndex(2)
|
||||
|
||||
# __ ___ _ _____ __ __
|
||||
# / |/ /___ _(_)___ / ___// /_____ ______/ /__
|
||||
# / /|_/ / __ `/ / __ \\__ \/ __/ __ `/ ___/ //_/
|
||||
# / / / / /_/ / / / / /__/ / /_/ /_/ / /__/ ,<
|
||||
# /_/ /_/\__,_/_/_/ /_/____/\__/\__,_/\___/_/|_|
|
||||
def initMainStack(self):
|
||||
self.mainstack = QStackedWidget()
|
||||
|
||||
self.designstack = design.DesignStack(self.core)
|
||||
self.contentstack = content.ContentStack(self.core)
|
||||
self.versionstack = QLabel("Version (git).")
|
||||
|
||||
self.mainstack.addWidget(self.designstack)
|
||||
self.mainstack.addWidget(self.contentstack)
|
||||
self.mainstack.addWidget(self.versionstack)
|
||||
|
||||
self.mainstack.setCurrentIndex(self.core.mw_curstack)
|
||||
|
||||
# TODO: add an app console window (show sass compilation errors for example)
|
||||
|
||||
self.setCentralWidget(self.mainstack)
|
||||
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Filename: md2html.py
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import os
|
||||
import re
|
||||
from PyQt5.QtCore import QFileSystemWatcher
|
||||
import json
|
||||
from bs4 import BeautifulSoup
|
||||
import pypandoc
|
||||
|
||||
|
||||
class Compiler():
|
||||
def __init__(self,core):
|
||||
self.core = core
|
||||
self.initWatching()
|
||||
self.compileContents()
|
||||
|
||||
def initWatching(self):
|
||||
self.refreshPaths()
|
||||
self.fs_watcher = QFileSystemWatcher(self.paths)
|
||||
# self.fs_watcher.directoryChanged.connect(self.directory_changed)
|
||||
self.fs_watcher.fileChanged.connect(self.onMdFileChanged)
|
||||
|
||||
def onMdFileChanged(self):
|
||||
print("onMdFileChanged")
|
||||
try:
|
||||
self.compileContents()
|
||||
except Exception as e:
|
||||
print("Error compiling MD files", e)
|
||||
pass
|
||||
|
||||
def refreshPaths(self):
|
||||
jsonfilepath = os.path.join(self.core.cwd,'.config/summary.json')
|
||||
sum_json = open(jsonfilepath).read()
|
||||
self.sum = json.loads(sum_json)
|
||||
|
||||
self.paths = [os.path.join(self.core.cwd,'contents')]
|
||||
for item in self.sum:
|
||||
self.paths.append(os.path.join(self.core.cwd,'contents',item['file']))
|
||||
|
||||
def reload(self):
|
||||
self.fs_watcher.removePaths(self.paths)
|
||||
self.refreshPaths()
|
||||
self.fs_watcher.addPaths(self.paths)
|
||||
self.compileContents()
|
||||
|
||||
def compileContents(self):
|
||||
print('Compiling md')
|
||||
|
||||
# create main html dom from template
|
||||
template_f = open(os.path.join(self.core.appcwd,"templates/main.tpl.html"), "r")
|
||||
template_html = template_f.read()
|
||||
template_dom = BeautifulSoup(template_html, 'html.parser')
|
||||
|
||||
# get story div
|
||||
story_dom = template_dom.find('div', {"id":"flow-main"})
|
||||
|
||||
pi = 0
|
||||
for p in self.sum:
|
||||
# print(toc[p])
|
||||
pagename = p['title']
|
||||
pageid = re.sub('[^a-z0-9]+', '-', pagename.lower())
|
||||
print(pageid)
|
||||
|
||||
# files
|
||||
in_f = os.path.join(self.core.cwd, "contents", p['file'])
|
||||
if not os.path.isfile(in_f):
|
||||
print("Source path is not a file, can't generate html : "+in_f)
|
||||
continue
|
||||
# print('in_f : '+in_f)
|
||||
|
||||
pdoc_args = ['--mathjax',
|
||||
'--smart']
|
||||
|
||||
pdoc_filters = []
|
||||
|
||||
output = pypandoc.convert_file(in_f,
|
||||
to='html5',
|
||||
format='markdown+header_attributes+link_attributes+bracketed_spans',
|
||||
extra_args=pdoc_args,
|
||||
filters=pdoc_filters)
|
||||
|
||||
output_dom = BeautifulSoup(output, 'html.parser')
|
||||
|
||||
# TODO: hyphenate paragraph
|
||||
|
||||
# append html story page to template_dom
|
||||
story_page = BeautifulSoup(
|
||||
'<div class="story-page story-page-'+str(pi)+'" id="'+pageid+'"></div>',
|
||||
'html.parser'
|
||||
)
|
||||
story_page.div.append(output_dom)
|
||||
story_dom.append(story_page)
|
||||
|
||||
|
||||
pi = pi+1
|
||||
|
||||
# create main html file from filled template html dom
|
||||
book_html_f = os.path.join(self.core.cwd,'index.html')
|
||||
with open(book_html_f, 'w') as fp:
|
||||
fp.write(template_dom.prettify())
|
||||
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 30-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Filename: sasscompiler.py
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
|
||||
import os
|
||||
from PyQt5.QtCore import QFileSystemWatcher
|
||||
import sass
|
||||
|
||||
class Compiler():
|
||||
def __init__(self,parent):
|
||||
self.parent = parent
|
||||
self.initWatching()
|
||||
self.compile_scss()
|
||||
# def directory_changed(path):
|
||||
# print("Directory changed : %s" % path)
|
||||
|
||||
def initWatching(self):
|
||||
self.refreshPaths()
|
||||
self.fs_watcher = QFileSystemWatcher(self.paths)
|
||||
# self.fs_watcher.directoryChanged.connect(self.directory_changed)
|
||||
self.fs_watcher.fileChanged.connect(self.compile_scss)
|
||||
|
||||
def compile_scss(self):
|
||||
print("compiling sass")
|
||||
try:
|
||||
scss = sass.compile_file(str.encode(os.path.join(self.parent.cwd,'assets/css/main.scss')))
|
||||
with open(os.path.join(self.parent.cwd,'assets/css/main.css'), 'w') as fp:
|
||||
fp.write(scss.decode('utf8'))
|
||||
except Exception as e:
|
||||
print("Error compiling Sass", e)
|
||||
pass
|
||||
|
||||
|
||||
def refreshPaths(self):
|
||||
self.paths = [
|
||||
os.path.join(self.parent.cwd,'assets'),
|
||||
os.path.join(self.parent.cwd,'assets/css')
|
||||
]
|
||||
# os.path.join(self.parent.cwd,'assets/css/styles.scss')
|
||||
for f in os.listdir(os.path.join(self.parent.cwd,'assets/css')):
|
||||
if f.endswith("scss"):
|
||||
self.paths.append(os.path.join(self.parent.cwd,'assets/css',f))
|
||||
|
||||
def reload(self):
|
||||
print('Reload sass compiler')
|
||||
self.fs_watcher.removePaths(self.paths)
|
||||
self.refreshPaths()
|
||||
print('paths', self.paths)
|
||||
self.fs_watcher.addPaths(self.paths)
|
||||
print('files', self.fs_watcher.files())
|
||||
self.compile_scss()
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Filename: server.py
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import sys, os
|
||||
from socket import socket
|
||||
import socketserver
|
||||
import http.server
|
||||
import threading
|
||||
|
||||
class Server():
|
||||
def __init__(self, parent):
|
||||
|
||||
self.parent = parent
|
||||
# find free port
|
||||
sock = socket()
|
||||
sock.bind(('', 0))
|
||||
|
||||
self._port = sock.getsockname()[1]
|
||||
sock.close()
|
||||
|
||||
# self.initial_cwd = os.getcwd()
|
||||
self.thread = threading.Thread(target=self.webserver)
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
# os.chdir(self.initial_cwd)
|
||||
|
||||
def webserver(self):
|
||||
os.chdir(self.parent.cwd)
|
||||
self.httpd = http.server.HTTPServer(('', self.port), http.server.SimpleHTTPRequestHandler)
|
||||
self.httpd.serve_forever()
|
||||
print("serving at port", self._port)
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._port
|
||||
|
||||
def reload(self):
|
||||
# self.thread.shutdown()
|
||||
os.chdir(self.parent.cwd)
|
||||
# self.thread.start()
|
||||
# os.chdir(self.initial_cwd)
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# @Author: Bachir Soussi Chiadmi <bach>
|
||||
# @Date: 23-05-2017
|
||||
# @Email: bachir@figureslibres.io
|
||||
# @Last modified by: bach
|
||||
# @Last modified time: 03-06-2017
|
||||
# @License: GPL-V3
|
||||
|
||||
import sys
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from classes import core, mainwindow
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
app.setOrganizationName('figli')
|
||||
app.setApplicationName('Cascade')
|
||||
mainappcore = core.Core()
|
||||
mainappwindow = mainwindow.MainWindow(mainappcore)
|
||||
mainappcore.mainwindow = mainappwindow
|
||||
sys.exit(app.exec_())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="normal" lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Cascade</title>
|
||||
|
||||
<link rel="stylesheet" href="/assets/css/main.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<!-- PAGES -->
|
||||
<div id="pages">
|
||||
|
||||
<div id="master-page" class="paper">
|
||||
<div class="page">
|
||||
<div class="body">
|
||||
<div class="bloc x0 y0 w9 h12 flow-main" style="margin-bottom:0pt;"></div>
|
||||
<!-- <div class="bloc x0 w1 flow-fn" style="bottom: 115pt;"></div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="stories">
|
||||
<div id="flow-main">
|
||||
|
||||
</div>
|
||||
<!-- // my-story -->
|
||||
</div>
|
||||
<!-- // stories -->
|
||||
|
||||
<script src="/assets/lib/jquery.min.js" charset="utf-8"></script>
|
||||
<script src="/assets/js/setup.js" charset="utf-8"></script>
|
||||
<script src="/assets/js/html2print.js" charset="utf-8"></script>
|
||||
<script src="/assets/js/script.js" charset="utf-8"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"git.user.name":"Cascade",
|
||||
"git.user.email":"cascade@figureslibres.io",
|
||||
"ho": "Cascade, default header odd",
|
||||
"he": "Cascade, default header even",
|
||||
"pw":210,
|
||||
"ph":297,
|
||||
"mt":15,
|
||||
"mb":15,
|
||||
"me":10,
|
||||
"mi":15,
|
||||
"cs":5,
|
||||
"bs":5,
|
||||
"cn": 6,
|
||||
"cg": 5,
|
||||
"rn": 12,
|
||||
"rg": 4,
|
||||
"lh": 3
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"title":"Default page",
|
||||
"file":"default.md",
|
||||
"index":0
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* DEBUG STYLES
|
||||
*/
|
||||
|
||||
.debug{
|
||||
|
||||
.body { outline: 1px solid purple; }
|
||||
|
||||
.body:before,
|
||||
.body:after { outline: 1px solid green; }
|
||||
|
||||
|
||||
.region-break {
|
||||
border-top: 1px dashed blue;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
img { outline: 1px solid blue; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
$body-width: $page-width - $page-margin-inside - $page-margin-outside;
|
||||
$body-height: $page-height - $page-margin-top - $page-margin-bottom;
|
||||
|
||||
$col-width: ($body-width - (($col-number - 1) * $col-gutter)) / $col-number;
|
||||
$row-height: ($body-height - (($row-number - 1) * $row-gutter)) / $row-number;
|
||||
|
||||
$col-gutter-width: $col-width + $col-gutter;
|
||||
$row-gutter-height: $row-height + $row-gutter;
|
||||
|
||||
|
||||
/* x classes */
|
||||
// .create-x-classes($i:0) when($i < $col-number) {
|
||||
// .x#{i}{
|
||||
// left: ($i * $col-width) + ($i * $col-gutter);
|
||||
// }
|
||||
// .create-x-classes($i + 1);
|
||||
// }
|
||||
// .create-x-classes();
|
||||
@for $i from 0 through $col-number - 1{
|
||||
.x#{$i}{
|
||||
left: ($i * $col-width) + ($i * $col-gutter);
|
||||
}
|
||||
}
|
||||
|
||||
/* y classes */
|
||||
// .create-y-classes($i:0) when($i < $row-number) {
|
||||
// .y#{i}{
|
||||
// top: ($i * $row-height) + ($i * $row-gutter);
|
||||
// }
|
||||
// .create-y-classes($i + 1);
|
||||
// }
|
||||
// .create-y-classes();
|
||||
@for $i from 0 through $row-number - 1 {
|
||||
.y#{$i}{
|
||||
top: ($i * $row-height) + ($i * $row-gutter);
|
||||
}
|
||||
}
|
||||
|
||||
/* width classes */
|
||||
// .create-w-classes($i:1) when($i <= $col-number) {
|
||||
// .w#{i}{
|
||||
// width: ($col-width * $i) + ($col-gutter * ($i - 1));
|
||||
// }
|
||||
// .create-w-classes($i + 1);
|
||||
// }
|
||||
// .create-w-classes();
|
||||
@for $i from 1 through $col-number {
|
||||
.w#{$i}{
|
||||
width: ($col-width * $i) + ($col-gutter * ($i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
/* height classes */
|
||||
// .create-h-classes($i:1) when($i <= $row-number) {
|
||||
// .h#{i}{
|
||||
// height: ($row-height * $i) + ($row-gutter * ($i - 1));
|
||||
// }
|
||||
// .create-h-classes($i + 1);
|
||||
// }
|
||||
// .create-h-classes();
|
||||
@for $i from 1 through $row-number {
|
||||
.h#{$i}{
|
||||
height: ($row-height * $i) + ($row-gutter * ($i - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// .create-grid-classes($prop: left, $class-name: x, $max-size: 100, $i: 0, $offset: 0, $odd: true) when ($offset <= $max-size) {
|
||||
// .#{class-name}#{i} {
|
||||
// #{prop}: $offset;
|
||||
// };
|
||||
//
|
||||
// & when ($odd) {
|
||||
// .create-grid-classes($prop, $class-name, $max-size, $i + 1, $offset + $col-width, false)
|
||||
// };
|
||||
//
|
||||
// & when not ($odd) {
|
||||
// .create-grid-classes($prop, $class-name, $max-size, $i + 1, $offset + $col-gutter, true)
|
||||
// };
|
||||
// }
|
||||
@mixin create-grid-classes($prop: left, $class-name: x, $max-size: 100, $i: 0, $offset: 0, $odd: true){
|
||||
$i:0;
|
||||
|
||||
.#{$class-name}#{$i} {
|
||||
#{$prop}: $offset;
|
||||
};
|
||||
|
||||
@while $offset<=$max-size {
|
||||
$i: $i+1;
|
||||
@if $odd{
|
||||
$offset:$offset+$col-width;
|
||||
$odd:false;
|
||||
}@else{
|
||||
$offset:$offset+$col-gutter;
|
||||
$odd:true;
|
||||
}
|
||||
.#{$class-name}#{$i} {
|
||||
#{$prop}: $offset;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
html:not(.facing) .paper:nth-child(odd) .l-1,
|
||||
html.facing .paper:nth-child(even) .l-1 { left: -( $page-margin-inside + 14pt); }
|
||||
|
||||
html:not(.facing) .paper:nth-child(even) .l-1,
|
||||
html.facing .paper:nth-child(odd) .l-1 { left: -( $page-margin-outside + 14pt); }
|
||||
|
||||
// .create-grid-classes(left, l, $body-width);
|
||||
@include create-grid-classes(left, l, $body-width);
|
||||
|
||||
html:not(.facing) .paper:nth-child(odd) .r-1,
|
||||
html.facing .paper:nth-child(even) .r-1 { right: -( $page-margin-outside + 14pt); }
|
||||
|
||||
html:not(.facing) .paper:nth-child(even) .r-1,
|
||||
html.facing .paper:nth-child(odd) .r-1 { right: -( $page-margin-inside + 14pt); }
|
||||
|
||||
// .create-grid-classes(right, r, $body-width);
|
||||
@include create-grid-classes(right, r, $body-width);
|
||||
|
||||
.t-1 { top: -( $page-margin-top + 14pt); }
|
||||
// .create-grid-classes(top, t, $body-height);
|
||||
@include create-grid-classes(top, t, $body-height);
|
||||
|
||||
.b-1 { bottom: -( $page-margin-bottom + 14pt); }
|
||||
// .create-grid-classes(bottom, b, $body-height);
|
||||
@include create-grid-classes(bottom, b, $body-height);
|
||||
|
||||
|
||||
.grid .body {
|
||||
outline: 1px solid $grid-color;
|
||||
background-attachment: local;
|
||||
background-size: 100% $row-gutter-height,
|
||||
$col-gutter-width 100%,
|
||||
100% $row-gutter-height,
|
||||
$col-gutter-width 100%,
|
||||
100% 12pt;
|
||||
background-position: -1px -1px,
|
||||
-1px -1px,
|
||||
-($col-gutter + 0) -($row-gutter + 0),
|
||||
-($col-gutter + 0) -($row-gutter + 0),
|
||||
0 -1px;
|
||||
background-image: -webkit-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-webkit-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-webkit-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-webkit-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-webkit-linear-gradient(top, $baseline-grid-color 1px, transparent 1px);
|
||||
background-image: -moz-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-moz-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-moz-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-moz-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-moz-linear-gradient(top, $baseline-grid-color 1px, transparent 1px);
|
||||
background-image: -ms-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-ms-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-ms-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-ms-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-ms-linear-gradient(top, $baseline-grid-color 1px, transparent 1px);
|
||||
background-image: -o-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-o-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-o-linear-gradient(top, $grid-color 1px, transparent 1px),
|
||||
-o-linear-gradient(left, $grid-color 1px, transparent 1px),
|
||||
-o-linear-gradient(top, $baseline-grid-color 1px, transparent 1px);
|
||||
background-image: linear-gradient(to bottom, $grid-color 1px, transparent 1px),
|
||||
linear-gradient(to right, $grid-color 1px, transparent 1px),
|
||||
linear-gradient(to bottom, $grid-color 1px, transparent 1px),
|
||||
linear-gradient(to right, $grid-color 1px, transparent 1px),
|
||||
linear-gradient(to bottom, $baseline-grid-color 1px, transparent 1px);
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* This file is part of HTML2print.
|
||||
*
|
||||
* HTML2print is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* HTML2print 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 Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with HTML2print. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Computation
|
||||
*/
|
||||
|
||||
|
||||
/* computes the edge size of the paper, which is the sum of the bleed and the
|
||||
* crop sizes */
|
||||
$edge: $crop-size + $bleed;
|
||||
|
||||
/* Computes the size of the paper sheet */
|
||||
$paper-width: $page-width + ( $edge * 2 );
|
||||
$paper-height: $page-height + ( $edge * 2 );
|
||||
|
||||
/**
|
||||
* DEFINITION OF THE PAPER SHEET
|
||||
*/
|
||||
|
||||
/**
|
||||
* The $page CSS at-rule is used to define some properties of printed
|
||||
* documents. We make it the size of the elements with the .paper class and
|
||||
* remove any margins so they don't add up with margins specifed in elements
|
||||
* with the .page class (or it's children, like .header, .body and .footer)
|
||||
*
|
||||
* We add 2pt to circumvent a rounding number bug in some browsers that make
|
||||
* them include extra pages or shifts.
|
||||
*/
|
||||
@page {
|
||||
size: $paper-width $paper-height + 2pt;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CANVAS
|
||||
*/
|
||||
|
||||
@media all {
|
||||
body {
|
||||
margin: 0;
|
||||
|
||||
/* Activate opentype features and kernings */
|
||||
-webkit-font-feature-settings: "liga", "dlig", "clig", "kern";
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
.paper {
|
||||
width: $paper-width;
|
||||
height: $paper-height;
|
||||
box-sizing: border-box;
|
||||
|
||||
/* defines a named counter and increments it every page, so we can use
|
||||
* it to compute the page number */
|
||||
counter-increment: folio;
|
||||
|
||||
/* makes sure that pages aren't cut because of pootential unprecise unit
|
||||
* conversion at printing time */
|
||||
page-break-inside: avoid;
|
||||
page-break-after: always;
|
||||
|
||||
/* clips the content if it goes out of the page, so it doesn't increase
|
||||
* the format */
|
||||
overflow: hidden;
|
||||
|
||||
/* Crop marks */
|
||||
padding: $edge;
|
||||
position: relative;
|
||||
|
||||
/* Crop marks */
|
||||
background-image:
|
||||
-webkit-linear-gradient(90deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(0deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(90deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(0deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(90deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(0deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(90deg, black 0, black 100%),
|
||||
-webkit-linear-gradient(0deg, black 0, black 100%)
|
||||
;
|
||||
background-size:
|
||||
$crop-size 1px,
|
||||
1px $crop-size,
|
||||
$crop-size 1px,
|
||||
1px $crop-size,
|
||||
$crop-size 1px,
|
||||
1px $crop-size,
|
||||
$crop-size 1px,
|
||||
1px $crop-size
|
||||
;
|
||||
background-position:
|
||||
left $edge,
|
||||
$edge top,
|
||||
right $edge,
|
||||
($paper-width - $edge) top,
|
||||
right ($paper-height - $edge),
|
||||
($paper-width - $edge) bottom,
|
||||
left ($paper-height - $edge),
|
||||
$edge bottom
|
||||
;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.page {
|
||||
/* defines the page size */
|
||||
width: $page-width;
|
||||
height: $page-height;
|
||||
|
||||
/* allows for absolutely positioned elements as settings the position
|
||||
* property to relative as the side effect of making this elements
|
||||
* top-left corner the reference point */
|
||||
/*position: relative;*/
|
||||
position: absolute; // FIXME: test it for printing issues
|
||||
}
|
||||
|
||||
// TODO: changer le format du papier en spread pour pouvoir imprimer en planche
|
||||
.spread .paper { float: left; }
|
||||
|
||||
.spread:not(.facing) .paper:nth-child(odd) { margin-left: -$edge; }
|
||||
.spread:not(.facing) .paper:nth-child(even) { margin-right: -$edge; }
|
||||
.spread:not(.facing) .paper:first-child { margin-left: $page-width; }
|
||||
|
||||
.spread.facing .paper:nth-child(even) { margin-right: initial; margin-left: -$edge; }
|
||||
.spread.facing .paper:nth-child(odd) { margin-left: initial; margin-right: -$edge; }
|
||||
.spread.facing .paper:first-child { margin-left: 0; }
|
||||
}
|
||||
|
||||
@media screen {
|
||||
/* defines the background color of the workspace */
|
||||
/* UI */
|
||||
body { background-color: #F0F0F0; }
|
||||
|
||||
#pages {
|
||||
width: $paper-width;
|
||||
height: $paper-height;
|
||||
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
/* FIXME: allows for printing spreads as well */
|
||||
.spread #pages {
|
||||
width: $paper-width * 2;
|
||||
height: $paper-height * 2;
|
||||
}
|
||||
.paper {
|
||||
/* centrer la page à l'écran */
|
||||
/* UI */
|
||||
background-color: white;
|
||||
/* UI */
|
||||
margin-top: 1em;
|
||||
/* UI */
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
/* UI */
|
||||
.normal .page { outline: 1px dotted lightsalmon; }
|
||||
/* UI */
|
||||
.preview .paper { background: transparent; }
|
||||
/* UI */
|
||||
.preview .page {
|
||||
outline: 1px solid lightgray;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media print {
|
||||
html { width: $paper-width; }
|
||||
body {
|
||||
/* Allows for background colors printing */
|
||||
background-color: transparent;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
.region-break {
|
||||
/* Apply this class to an element to put it on a new region.
|
||||
* Hint:
|
||||
* You can also use an empty <div class="page-break"></div>
|
||||
* if you want to put manual page breaks without attaching it to an HTML element
|
||||
*/
|
||||
-webkit-region-break-before: always;
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Macro-structure
|
||||
* ===============
|
||||
*/
|
||||
|
||||
.body,
|
||||
.body:before,
|
||||
.body:after { position: absolute; }
|
||||
|
||||
.body:before,
|
||||
.body:after { z-index: 500; }
|
||||
|
||||
|
||||
/**
|
||||
* Zone de composition principale
|
||||
* ------------------------------
|
||||
*/
|
||||
|
||||
.body {
|
||||
top: $page-margin-top;
|
||||
bottom: $page-margin-bottom;
|
||||
}
|
||||
|
||||
/* TODO: move into grid.less? */
|
||||
.bloc { position: absolute; z-index: 500; }
|
||||
// .debug .header,
|
||||
// .debug .footer,
|
||||
// .debug .body,
|
||||
// .debug .bloc { outline: 1px solid purple; }
|
||||
|
||||
/**
|
||||
* Pieds de page
|
||||
* -------------
|
||||
*/
|
||||
|
||||
.body:before,
|
||||
.body:after {
|
||||
display: block;
|
||||
font-family: sans-serif;
|
||||
font-size: 6pt;
|
||||
line-height: $line-height;
|
||||
letter-spacing: 0.25pt;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.body:before{
|
||||
top:-2em;
|
||||
width:100%;
|
||||
text-align: center;
|
||||
}
|
||||
.body:after{
|
||||
bottom:-2em;
|
||||
width:100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/*gauche*/
|
||||
.paper:nth-child(odd) .body:before { content: $header-odd; }
|
||||
/*droite*/
|
||||
.paper:nth-child(even) .body:before { content: $header-even; }
|
||||
|
||||
.body:after { content: counter(folio); z-index: 499;}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Miroir
|
||||
* -------
|
||||
*/
|
||||
|
||||
/**
|
||||
* Placement en miroir des éléments en fonction de si ils se trouvent sur une
|
||||
* page paire ou une page impaire, en utilisant le pseudo-sélecteur `nth-child`
|
||||
*/
|
||||
|
||||
html:not(.facing) .paper:nth-child(odd) .body,
|
||||
html.facing .paper:nth-child(even) .body {
|
||||
right: $page-margin-outside;
|
||||
left: $page-margin-inside;
|
||||
}
|
||||
|
||||
html:not(.facing) .paper:nth-child(even) .body,
|
||||
html.facing .paper:nth-child(odd) .body {
|
||||
left: $page-margin-outside;
|
||||
right: $page-margin-inside;
|
||||
}
|
||||
|
||||
// html:not(.facing) .paper:nth-child(odd) .body:before,
|
||||
// html.facing .paper:nth-child(even) .body:before {
|
||||
// @extend .x1;
|
||||
// @extend .w4;
|
||||
// }
|
||||
// html:not(.facing) .paper:nth-child(even) .body:before,
|
||||
// html.facing .paper:nth-child(odd) .body:before {
|
||||
// @extend .x5;
|
||||
// @extend .w4;
|
||||
// }
|
||||
// html:not(.facing) .paper:nth-child(odd) .body:after,
|
||||
// html.facing .paper:nth-child(even) .body:after {
|
||||
// @extend .x5;
|
||||
// @extend .w1;
|
||||
// text-align: left;
|
||||
// }
|
||||
// html:not(.facing) .paper:nth-child(even) .body:after,
|
||||
// html.facing .paper:nth-child(odd) .body:after {
|
||||
// @extend .x1;
|
||||
// @extend .w1;
|
||||
// text-align: left;
|
||||
// }
|
||||
|
||||
|
||||
#flow-main {
|
||||
position: relative;
|
||||
-webkit-flow-into: flow-main;
|
||||
flow-into: flow-main;
|
||||
}
|
||||
|
||||
.flow-main {
|
||||
-webkit-flow-from: flow-main;
|
||||
flow-from: flow-main;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* This file is part of HTML2print.
|
||||
*
|
||||
* HTML2print is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* HTML2print 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 Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with HTML2print. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The less CSS is splitted accross different files for a better organisation.
|
||||
*
|
||||
* This is the main less css file that defines custom values and requires all
|
||||
* the the neccessary dependencies.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* THE CODE BELOW IS REQUIRED TO MAKE HTML2PRINT WORK.
|
||||
*/
|
||||
@function mm2pt($mm){
|
||||
// @return $mm*2.8346pt;
|
||||
@return $mm*1mm;
|
||||
}
|
||||
|
||||
@import "setup.scss"
|
||||
|
||||
@import "html2print.scss";
|
||||
|
||||
@import "grid.scss";
|
||||
|
||||
@import "debug.scss";
|
||||
|
||||
@import "mixins.scss";
|
||||
|
||||
@import "layout.scss";
|
||||
|
||||
// @import "fonts.scss";
|
||||
|
||||
// @import "colors.scss";
|
||||
|
||||
@import "styles.scss";
|
||||
|
||||
/*@import "export.scss";*/
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Mixins
|
||||
* ======
|
||||
*/
|
||||
|
||||
@mixin flow-into($flow) {
|
||||
-webkit-flow-into: $flow;
|
||||
flow-into: $flow;
|
||||
}
|
||||
|
||||
@mixin flow-from($flow) {
|
||||
-webkit-flow-from: $flow;
|
||||
flow-from: $flow;
|
||||
}
|
||||
|
||||
// Pas de footer sur ces pages
|
||||
@mixin no-footer($page) {
|
||||
.paper:nth-of-type(#{$page}) .body:before,
|
||||
.paper:nth-of-type(#{$page}) .body:after { content: "" }
|
||||
}
|
||||
|
||||
@mixin trans($rotate, $skew, $originY, $originX) {
|
||||
-webkit-transform: skewY($skew) rotate($rotate);
|
||||
transform: skewY($skew) rotate($rotate);
|
||||
-webkit-transform-origin: $originY $originX;
|
||||
transform-origin: $originY $originX;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
/**
|
||||
* Defines and loads the rules that set the geometry of the page and its
|
||||
* representation on screen. This is the core of html2print.
|
||||
*
|
||||
* Customize the variables to your needs.
|
||||
*/
|
||||
|
||||
/* the geometry of the page */
|
||||
$page-width: mm2pt(200);
|
||||
$page-height: mm2pt(287);
|
||||
|
||||
/* the size of the crop marks based on scribus defaults */
|
||||
$crop-size: mm2pt(2);
|
||||
|
||||
/* the size of bleed */
|
||||
$bleed: mm2pt(3);
|
||||
|
||||
/**
|
||||
* THE CODE BELOW IS NOT REQUIRED TO MAKE HTML2PRINT WORK, ALTHOUGH IT PROVES
|
||||
* USEFUL IN MANY SITUATIONS.
|
||||
*
|
||||
* YOU ARE ENCOURAGED TO CUSTOMIZE IT TO YOU NEEDS.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines and generate helper css rules to absolutly position elements on a
|
||||
* grid.
|
||||
*
|
||||
* Customize the variables to your needs.
|
||||
*/
|
||||
|
||||
$col-number: 9;
|
||||
$col-gutter: mm2pt(3);
|
||||
|
||||
//$row-number: 13;
|
||||
$row-number: 12;
|
||||
$row-gutter: mm2pt(4);
|
||||
|
||||
$grid-color: rgba(255, 0, 0, 0.25);
|
||||
$baseline-grid-color: rgba(0, 255, 255, 0.15);
|
||||
|
||||
|
||||
/**
|
||||
* Defines and loads the rules that set the base layout of the document
|
||||
* (header, body, footer)
|
||||
*
|
||||
* Customize the variables to your needs.
|
||||
*/
|
||||
|
||||
// FIXME: make a case for single page layouts
|
||||
$page-margin-inside: mm2pt(7.5);
|
||||
$page-margin-outside: mm2pt(15);
|
||||
$page-margin-top: mm2pt(10);
|
||||
$page-margin-bottom: mm2pt(10);
|
||||
|
||||
|
||||
$line-height: mm2pt(4);
|
||||
|
||||
$header-odd: "Cascade, default header";
|
||||
$header-even: "Cascade, default header";
|
||||
@@ -0,0 +1,11 @@
|
||||
// your styles here
|
||||
|
||||
|
||||
h1{
|
||||
font-size:32pt;
|
||||
margin:5mm 0 0;
|
||||
}
|
||||
|
||||
.story-page{
|
||||
-webkit-region-break-after:always;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
$(function() {
|
||||
|
||||
// Cloning the master page
|
||||
for (i = 0; i < nb_page; i++){
|
||||
$("#master-page").clone().attr("id","page-"+i).insertBefore($("#master-page"));
|
||||
}
|
||||
$("#master-page").attr("data-width", $(".paper:first-child").width()).hide();
|
||||
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
jQuery(document).ready(function($) {
|
||||
|
||||
console.log("Hello Cascade");
|
||||
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
nb_page=2;
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,5 @@
|
||||
# Default Page
|
||||
|
||||
Welcome message
|
||||
|
||||
insert her an image as example
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Cascade</title>
|
||||
<link rel="stylesheet" href="assets/css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Cascade</h1>
|
||||
<h2>A Markup Cascading Printing software</h2>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user