content.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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. class SummaryActions(QWidget):
  119. def __init__(self,parent):
  120. super(SummaryActions, self).__init__(parent)
  121. self.parent = parent
  122. self.hbox = QHBoxLayout()
  123. self.hbox.setContentsMargins(0,0,0,0)
  124. new = QPushButton("New Page", self)
  125. new.setShortcut('Ctrl+Shift+n')
  126. # new.setIcon(Icon(ico)))
  127. new.clicked.connect(self.onAddPage)
  128. self.hbox.addWidget(new)
  129. delete = QPushButton("Delete Page", self)
  130. delete.setShortcut('Ctrl+Shift+sup')
  131. # delete.setIcon(Icon(ico)))
  132. delete.clicked.connect(self.onDeletePage)
  133. self.hbox.addWidget(delete)
  134. self.setLayout(self.hbox)
  135. def onAddPage(self):
  136. text, ok = QInputDialog.getText(self, 'Input Dialog', 'Page Name:')
  137. if ok:
  138. self.parent.addItem(text)
  139. def onDeletePage(self):
  140. print("onDeletePage")
  141. # TODO: get the current selected page
  142. # TODO: ask for confirmation for deleting the current selecred page
  143. # TODO: call for summary widget to delete the page
  144. # ______ ___ __
  145. # / ____/___/ (_) /_____ _____
  146. # / __/ / __ / / __/ __ \/ ___/
  147. # / /___/ /_/ / / /_/ /_/ / /
  148. # /_____/\__,_/_/\__/\____/_/
  149. class MarkdownEditor(QWidget):
  150. def __init__(self,parent):
  151. super(MarkdownEditor, self).__init__(parent)
  152. self.parent = parent
  153. self.changed = False
  154. self.hbox = QHBoxLayout()
  155. self.hbox.setContentsMargins(0,0,0,0)
  156. self.styles = """
  157. background-color:white;
  158. color:black;
  159. padding:20px;
  160. """
  161. self.editor = QPlainTextEdit(self)
  162. self.editor.setStyleSheet(self.styles)
  163. self.hl=highlighter.Highlighter(self.editor.document(),"md")
  164. self.hbox.addWidget(self.editor)
  165. self.viewer = QTextEdit(self)
  166. self.viewer.setReadOnly(True)
  167. self.viewer.setStyleSheet(self.styles)
  168. # TODO: show all html blocks on viewer
  169. self.hbox.addWidget(self.viewer)
  170. self.setLayout(self.hbox)
  171. self.editor.textChanged.connect(self.onTextChanged)
  172. self.openFile()
  173. self.shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
  174. self.shortcut.activated.connect(self.save)
  175. self.refreshViewer()
  176. def openFile(self, row = 0):
  177. # print("openFile")
  178. sumlist = self.parent.summary.list
  179. item = sumlist.item(row)
  180. if item:
  181. if not self.changed:
  182. self.editor.textChanged.disconnect(self.onTextChanged)
  183. filename = item.data['file']
  184. self.file = os.path.join(self.parent.core.cwd,'contents',filename)
  185. self.editor.clear()
  186. self.editor.insertPlainText(open(self.file, 'r').read())
  187. self.refreshViewer()
  188. self.editor.textChanged.connect(self.onTextChanged)
  189. else:
  190. print("Can't changed file, current id modified, please save first")
  191. # TODO: ask for saving current file
  192. def onTextChanged(self):
  193. self.refreshViewer()
  194. if not self.changed:
  195. self.changed = True
  196. # TODO: show in list that content needs to be saved
  197. # i = self.tabs.currentIndex()
  198. # self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
  199. def refreshViewer(self):
  200. md = self.editor.toPlainText()
  201. html = markdown.markdown(md)
  202. self.viewer.setHtml(html)
  203. def save(self):
  204. if self.changed:
  205. open(self.file, 'w').write(self.editor.toPlainText())
  206. self.changed = False
  207. # i = self.tabs.currentIndex()
  208. # self.tabs.setTabText(i, re.sub(r'^\*\s', '', self.tabs.tabText(i)))
  209. # TODO: how to combine file save and project save
  210. # _____ __ __
  211. # / ___// /_____ ______/ /__
  212. # \__ \/ __/ __ `/ ___/ //_/
  213. # ___/ / /_/ /_/ / /__/ ,<
  214. # /____/\__/\__,_/\___/_/|_|
  215. class ContentStack(QWidget):
  216. def __init__(self, core):
  217. super(ContentStack, self).__init__()
  218. self.core = core
  219. hbox = QHBoxLayout()
  220. hbox.setContentsMargins(0,0,0,0)
  221. self.setLayout(hbox)
  222. self.hsplitter = QSplitter(QtCore.Qt.Horizontal)
  223. self.summary = Summary(self)
  224. # TODO: detect external changes (file changed or new file)
  225. self.hsplitter.addWidget(self.summary)
  226. self.editor = MarkdownEditor(self)
  227. self.hsplitter.addWidget(self.editor)
  228. self.hsplitter.splitterMoved.connect(self.movedSplitter)
  229. hbox.addWidget(self.hsplitter)
  230. self.restorePrefs()
  231. def restorePrefs(self):
  232. settings = QSettings('FiguresLibres', 'Libriis')
  233. vals = settings.value('content/hsplitter/sizes', None)
  234. if vals:
  235. sizes = []
  236. for size in vals: sizes.append(int(size))
  237. self.hsplitter.setSizes(sizes)
  238. def movedSplitter(self):
  239. settings = QSettings('FiguresLibres', 'Libriis')
  240. # print(self.hsplitter.sizes())
  241. settings.setValue('content/hsplitter/sizes', self.hsplitter.sizes())
  242. def refresh(self):
  243. self.summary.reload()
  244. self.editor.openFile()