content.py 9.1 KB

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