content.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # @Author: Bachir Soussi Chiadmi <bach>
  4. # @Date: 23-05-2017
  5. # @Email: bachir@figureslibres.io
  6. # @Filename: content.py
  7. # @Last modified by: bach
  8. # @Last modified time: 03-06-2017
  9. # @License: GPL-V3
  10. from __future__ import absolute_import, print_function, division, unicode_literals
  11. import os, re
  12. from PyQt5 import QtCore
  13. from PyQt5.QtCore import QSettings
  14. from PyQt5.QtGui import QKeySequence
  15. from PyQt5.QtWidgets import (QWidget, QLabel, QHBoxLayout, QVBoxLayout, QSplitter,
  16. QListWidget, QListWidgetItem, QAbstractItemView, QPushButton, QInputDialog,
  17. QPlainTextEdit, QTextEdit, QShortcut)
  18. from . import highlighter
  19. import markdown
  20. import json
  21. # _____
  22. # / ___/__ ______ ___ ____ ___ ____ ________ __
  23. # \__ \/ / / / __ `__ \/ __ `__ \/ __ `/ ___/ / / /
  24. # ___/ / /_/ / / / / / / / / / / / /_/ / / / /_/ /
  25. # /____/\__,_/_/ /_/ /_/_/ /_/ /_/\__,_/_/ \__, /
  26. # /____/
  27. class Summary(QWidget):
  28. def __init__(self, parent):
  29. super(Summary, self).__init__(parent)
  30. self.parent = parent
  31. self.loadJson()
  32. vbox = QVBoxLayout()
  33. vbox.setContentsMargins(0,0,0,0)
  34. self.list = SummaryList(self)
  35. vbox.addWidget(self.list)
  36. self.actions = SummaryActions(self)
  37. vbox.addWidget(self.actions)
  38. self.setLayout(vbox)
  39. def loadJson(self):
  40. jsonfilepath = os.path.join(self.parent.core.cwd,'.config/summary.json')
  41. sum_json = open(jsonfilepath).read()
  42. self.sum = json.loads(sum_json)
  43. def addItem(self, text):
  44. # file
  45. filename = re.sub(r'\W', "_", text)+".md"
  46. # TODO: check if file does not already exists
  47. filepath = os.path.join(self.parent.core.cwd,'contents',filename)
  48. with open(filepath, 'w') as fp:
  49. fp.write('#'+text)
  50. # json
  51. item = {"title":text,"file":filename}
  52. self.sum.append(item)
  53. jsonfilepath = os.path.join(self.parent.core.cwd,'.config/summary.json')
  54. with open(jsonfilepath, "w") as fp:
  55. json.dump(self.sum, fp, ensure_ascii=False, indent="\t")
  56. # refresh list
  57. self.list.addNewItem(item)
  58. # reload content compiler
  59. self.parent.core.contentcompiler.reload()
  60. def recordNewList(self):
  61. newdata = []
  62. for i in range(0,self.list.count()):
  63. # print(self.item(i).item['title'])
  64. newdata.append(self.list.item(i).data)
  65. # print(newdata)
  66. self.sum = newdata
  67. jsonfilepath = os.path.join(self.parent.core.cwd,'.config/summary.json')
  68. with open(jsonfilepath, "w") as fp:
  69. json.dump(newdata, fp, ensure_ascii=False, indent="\t")
  70. # reload content compiler
  71. self.parent.core.contentcompiler.reload()
  72. def reload(self):
  73. self.loadJson()
  74. self.list.setItems()
  75. class SummaryList(QListWidget):
  76. def __init__(self, parent):
  77. super(SummaryList, self).__init__(parent)
  78. self.parent = parent
  79. # self.sum = sum
  80. # print(self.sum)
  81. # self.setSortingEnabled(True)
  82. self.setDragEnabled(True)
  83. self.setSelectionMode(QAbstractItemView.SingleSelection)
  84. self.setAcceptDrops(True)
  85. self.setDropIndicatorShown(True)
  86. self.setDragDropMode(QAbstractItemView.InternalMove)
  87. self.model().rowsMoved.connect(self.onRowsMoved)
  88. # print(self.model())
  89. self.itemActivated.connect(self.onItemActivated)
  90. self.setItems()
  91. # self.setCurrentRow(0)
  92. # self.setCurrentIndex()
  93. # self.setCurrentItem()
  94. self.item(0).setSelected(True)
  95. # TODO: activate first item by default as it will open it with editor
  96. # TODO: show activated item on the list
  97. # TODO: show modifed item on the list
  98. def setItems(self):
  99. self.clear()
  100. # add markdown files to the list
  101. for itemdata in self.parent.sum:
  102. self.addNewItem(itemdata)
  103. def onRowsMoved(self, model, start, end, dest):
  104. # print("onRowsMoved")
  105. self.parent.recordNewList()
  106. def addNewItem(self, item):
  107. self.addItem(SummaryListWidgetItem(self,item))
  108. def onItemActivated(self, item):
  109. # print('onItemActivated', item.data)
  110. self.parent.parent.editor.openFile(self.currentRow())
  111. class SummaryListWidgetItem(QListWidgetItem):
  112. def __init__(self,parent,data):
  113. super(SummaryListWidgetItem, self).__init__(parent)
  114. self.parent = parent
  115. self.data = data
  116. self.setText(data['title'])
  117. self.setToolTip(data['file'])
  118. # self.setStyleSheet('padding:5px;')
  119. class SummaryActions(QWidget):
  120. def __init__(self,parent):
  121. super(SummaryActions, self).__init__(parent)
  122. self.parent = parent
  123. self.hbox = QHBoxLayout()
  124. self.hbox.setContentsMargins(0,0,0,0)
  125. new = QPushButton("New Page", self)
  126. new.setShortcut('Ctrl+Shift+n')
  127. # new.setIcon(Icon(ico)))
  128. new.clicked.connect(self.onAddPage)
  129. self.hbox.addWidget(new)
  130. delete = QPushButton("Delete Page", self)
  131. delete.setShortcut('Ctrl+Shift+sup')
  132. # delete.setIcon(Icon(ico)))
  133. delete.clicked.connect(self.onDeletePage)
  134. self.hbox.addWidget(delete)
  135. self.setLayout(self.hbox)
  136. def onAddPage(self):
  137. text, ok = QInputDialog.getText(self, 'Input Dialog', 'Page Name:')
  138. if ok:
  139. self.parent.addItem(text)
  140. def onDeletePage(self):
  141. print("onDeletePage")
  142. # TODO: get the current selected page
  143. # TODO: ask for confirmation for deleting the current selecred page
  144. # TODO: call for summary widget to delete the page
  145. # ______ ___ __
  146. # / ____/___/ (_) /_____ _____
  147. # / __/ / __ / / __/ __ \/ ___/
  148. # / /___/ /_/ / / /_/ /_/ / /
  149. # /_____/\__,_/_/\__/\____/_/
  150. class MarkdownEditor(QWidget):
  151. def __init__(self,parent):
  152. super(MarkdownEditor, self).__init__(parent)
  153. self.parent = parent
  154. self.changed = False
  155. self.hbox = QHBoxLayout()
  156. self.hbox.setContentsMargins(0,0,0,0)
  157. self.styles = """
  158. background-color:white;
  159. color:black;
  160. padding:20px;
  161. """
  162. self.editor = QPlainTextEdit(self)
  163. self.editor.setStyleSheet(self.styles)
  164. self.hl=highlighter.Highlighter(self.editor.document(),"md")
  165. self.hbox.addWidget(self.editor)
  166. self.viewer = QTextEdit(self)
  167. self.viewer.setReadOnly(True)
  168. self.viewer.setStyleSheet(self.styles)
  169. # TODO: show all html blocks on viewer
  170. self.hbox.addWidget(self.viewer)
  171. self.setLayout(self.hbox)
  172. self.editor.textChanged.connect(self.onTextChanged)
  173. self.openFile()
  174. self.shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
  175. self.shortcut.activated.connect(self.save)
  176. self.refreshViewer()
  177. def openFile(self, row = 0):
  178. # print("openFile")
  179. sumlist = self.parent.summary.list
  180. item = sumlist.item(row)
  181. if item:
  182. if not self.changed:
  183. self.editor.textChanged.disconnect(self.onTextChanged)
  184. filename = item.data['file']
  185. self.file = os.path.join(self.parent.core.cwd,'contents',filename)
  186. self.editor.clear()
  187. self.editor.insertPlainText(open(self.file, 'r').read())
  188. self.refreshViewer()
  189. self.editor.textChanged.connect(self.onTextChanged)
  190. else:
  191. print("Can't changed file, current id modified, please save first")
  192. # TODO: ask for saving current file
  193. def onTextChanged(self):
  194. self.refreshViewer()
  195. if not self.changed:
  196. self.changed = True
  197. # TODO: show in list that content needs to be saved
  198. # i = self.tabs.currentIndex()
  199. # self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
  200. def refreshViewer(self):
  201. md = self.editor.toPlainText()
  202. html = markdown.markdown(md)
  203. self.viewer.setHtml(html)
  204. def save(self):
  205. if self.changed:
  206. open(self.file, 'w').write(self.editor.toPlainText())
  207. self.changed = False
  208. # i = self.tabs.currentIndex()
  209. # self.tabs.setTabText(i, re.sub(r'^\*\s', '', self.tabs.tabText(i)))
  210. # TODO: how to combine file save and project save
  211. # _____ __ __
  212. # / ___// /_____ ______/ /__
  213. # \__ \/ __/ __ `/ ___/ //_/
  214. # ___/ / /_/ /_/ / /__/ ,<
  215. # /____/\__/\__,_/\___/_/|_|
  216. class ContentStack(QWidget):
  217. def __init__(self, core):
  218. super(ContentStack, self).__init__()
  219. self.core = core
  220. hbox = QHBoxLayout()
  221. hbox.setContentsMargins(0,0,0,0)
  222. self.setLayout(hbox)
  223. self.hsplitter = QSplitter(QtCore.Qt.Horizontal)
  224. self.summary = Summary(self)
  225. # TODO: detect external changes (file changed or new file)
  226. self.hsplitter.addWidget(self.summary)
  227. self.editor = MarkdownEditor(self)
  228. self.hsplitter.addWidget(self.editor)
  229. self.hsplitter.splitterMoved.connect(self.movedSplitter)
  230. hbox.addWidget(self.hsplitter)
  231. self.restorePrefs()
  232. def restorePrefs(self):
  233. settings = QSettings('FiguresLibres', 'Libriis')
  234. vals = settings.value('content/hsplitter/sizes', None)
  235. if vals:
  236. sizes = []
  237. for size in vals: sizes.append(int(size))
  238. self.hsplitter.setSizes(sizes)
  239. def movedSplitter(self):
  240. settings = QSettings('FiguresLibres', 'Libriis')
  241. # print(self.hsplitter.sizes())
  242. settings.setValue('content/hsplitter/sizes', self.hsplitter.sizes())
  243. def refresh(self):
  244. self.summary.reload()
  245. self.editor.openFile()