content.py 8.8 KB

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