content.py 8.9 KB

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