~mitya57/+junk/master

« back to all changes in this revision

Viewing changes to ReText/window.py

  • Committer: Dmitry Shachnev
  • Date: 2012-07-25 11:07:35 UTC
  • Revision ID: git-v1:368af5ce05f41e27ab464025bcb1de918335adb7
Split ReText by classes, install into "ReText" directory

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from ReText import *
 
2
from ReText.htmldialog import HtmlDialog
 
3
from ReText.highlighter import ReTextHighlighter
 
4
 
 
5
class ReTextWindow(QMainWindow):
 
6
        def __init__(self, parent=None):
 
7
                QMainWindow.__init__(self, parent)
 
8
                self.resize(800, 600)
 
9
                screen = QDesktopWidget().screenGeometry()
 
10
                size = self.geometry()
 
11
                self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
 
12
                if settings.contains('iconTheme'):
 
13
                        QIcon.setThemeName(readFromSettings(settings, 'iconTheme', str))
 
14
                if QIcon.themeName() in ('', 'hicolor'):
 
15
                        try:
 
16
                                gconf = Popen(['gconftool-2', '--get', '/desktop/gnome/interface/icon_theme'],
 
17
                                stdout=PIPE)
 
18
                        except: pass
 
19
                        else:
 
20
                                iconTheme = gconf.stdout.read().rstrip()
 
21
                                if iconTheme: QIcon.setThemeName(iconTheme.decode())
 
22
                if settings.contains('font'):
 
23
                        self.font = QFont(readFromSettings(settings, 'font', str))
 
24
                        if settings.contains('fontSize'):
 
25
                                self.font.setPointSize(readFromSettings(settings, 'fontSize', int))
 
26
                else:
 
27
                        self.font = None
 
28
                if settings.contains('tabWidth'):
 
29
                        self.tabWidth = readFromSettings(settings, 'tabWidth', int)
 
30
                else:
 
31
                        self.tabWidth = 4
 
32
                if settings.contains('tabInsertsSpaces'):
 
33
                        self.tabInsertsSpaces = readFromSettings(settings, 'tabInsertsSpaces', bool)
 
34
                else:
 
35
                        self.tabInsertsSpaces = False
 
36
                if QFile.exists(icon_path+'retext.png'):
 
37
                        self.setWindowIcon(QIcon(icon_path+'retext.png'))
 
38
                else:
 
39
                        self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor')))
 
40
                self.editBoxes = []
 
41
                self.previewBoxes = []
 
42
                self.highlighters = []
 
43
                self.markups = []
 
44
                self.fileNames = []
 
45
                self.apc = []
 
46
                self.alpc = []
 
47
                self.aptc = []
 
48
                self.tabWidget = QTabWidget(self)
 
49
                self.tabWidget.setTabsClosable(True)
 
50
                self.setCentralWidget(self.tabWidget)
 
51
                self.connect(self.tabWidget, SIGNAL('currentChanged(int)'), self.changeIndex)
 
52
                self.connect(self.tabWidget, SIGNAL('tabCloseRequested(int)'), self.closeTab)
 
53
                self.toolBar = QToolBar(self.tr('File toolbar'), self)
 
54
                self.addToolBar(Qt.TopToolBarArea, self.toolBar)
 
55
                self.editBar = QToolBar(self.tr('Edit toolbar'), self)
 
56
                self.addToolBar(Qt.TopToolBarArea, self.editBar)
 
57
                self.searchBar = QToolBar(self.tr('Search toolbar'), self)
 
58
                self.addToolBar(Qt.BottomToolBarArea, self.searchBar)
 
59
                self.actionNew = self.act(self.tr('New'), icon='document-new', shct=QKeySequence.New, trig=self.createNew)
 
60
                self.actionNew.setPriority(QAction.LowPriority)
 
61
                self.actionOpen = self.act(self.tr('Open'), icon='document-open', shct=QKeySequence.Open, trig=self.openFile)
 
62
                self.actionOpen.setPriority(QAction.LowPriority)
 
63
                self.actionSave = self.act(self.tr('Save'), icon='document-save', shct=QKeySequence.Save, trig=self.saveFile)
 
64
                self.actionSave.setEnabled(False)
 
65
                self.actionSave.setPriority(QAction.LowPriority)
 
66
                self.actionSaveAs = self.act(self.tr('Save as'), icon='document-save-as', shct=QKeySequence.SaveAs,
 
67
                trig=self.saveFileAs)
 
68
                self.actionPrint = self.act(self.tr('Print'), icon='document-print', shct=QKeySequence.Print, trig=self.printFile)
 
69
                self.actionPrint.setPriority(QAction.LowPriority)
 
70
                self.actionPrintPreview = self.act(self.tr('Print preview'), icon='document-print-preview',
 
71
                trig=self.printPreview)
 
72
                self.actionViewHtml = self.act(self.tr('View HTML code'), icon='text-html', trig=self.viewHtml)
 
73
                self.actionChangeFont = self.act(self.tr('Change default font'), trig=self.changeFont)
 
74
                self.actionSearch = self.act(self.tr('Find text'), icon='edit-find', shct=QKeySequence.Find)
 
75
                self.actionSearch.setCheckable(True)
 
76
                self.connect(self.actionSearch, SIGNAL('triggered(bool)'), self.searchBar, SLOT('setVisible(bool)'))
 
77
                self.connect(self.searchBar, SIGNAL('visibilityChanged(bool)'), self.searchBarVisibilityChanged)
 
78
                self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview)
 
79
                if QIcon.hasThemeIcon('document-preview'):
 
80
                        self.actionPreview.setIcon(QIcon.fromTheme('document-preview'))
 
81
                elif QIcon.hasThemeIcon('preview-file'):
 
82
                        self.actionPreview.setIcon(QIcon.fromTheme('preview-file'))
 
83
                elif QIcon.hasThemeIcon('x-office-document'):
 
84
                        self.actionPreview.setIcon(QIcon.fromTheme('x-office-document'))
 
85
                else:
 
86
                        self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png'))
 
87
                self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L,
 
88
                trigbool=self.enableLivePreview)
 
89
                self.actionFullScreen = self.act(self.tr('Fullscreen mode'), icon='view-fullscreen', shct=Qt.Key_F11,
 
90
                trigbool=self.enableFullScreen)
 
91
                self.actionPerfectHtml = self.act('HTML', icon='text-html', trig=self.saveFilePerfect)
 
92
                self.actionPdf = self.act('PDF', icon='application-pdf', trig=self.savePdf)
 
93
                self.actionOdf = self.act('ODT', icon='x-office-document', trig=self.saveOdf)
 
94
                self.getExportExtensionsList()
 
95
                self.actionQuit = self.act(self.tr('Quit'), icon='application-exit', shct=QKeySequence.Quit)
 
96
                self.actionQuit.setMenuRole(QAction.QuitRole)
 
97
                self.connect(self.actionQuit, SIGNAL('triggered()'), self.close)
 
98
                self.actionUndo = self.act(self.tr('Undo'), icon='edit-undo', shct=QKeySequence.Undo,
 
99
                trig=lambda: self.editBoxes[self.ind].undo())
 
100
                self.actionRedo = self.act(self.tr('Redo'), icon='edit-redo', shct=QKeySequence.Redo,
 
101
                trig=lambda: self.editBoxes[self.ind].redo())
 
102
                self.actionCopy = self.act(self.tr('Copy'), icon='edit-copy', shct=QKeySequence.Copy,
 
103
                trig=lambda: self.editBoxes[self.ind].copy())
 
104
                self.actionCut = self.act(self.tr('Cut'), icon='edit-cut', shct=QKeySequence.Cut,
 
105
                trig=lambda: self.editBoxes[self.ind].cut())
 
106
                self.actionPaste = self.act(self.tr('Paste'), icon='edit-paste', shct=QKeySequence.Paste,
 
107
                trig=lambda: self.editBoxes[self.ind].paste())
 
108
                self.actionUndo.setEnabled(False)
 
109
                self.actionRedo.setEnabled(False)
 
110
                self.actionCopy.setEnabled(False)
 
111
                self.actionCut.setEnabled(False)
 
112
                self.connect(qApp.clipboard(), SIGNAL('dataChanged()'), self.clipboardDataChanged)
 
113
                self.clipboardDataChanged()
 
114
                if enchant_available:
 
115
                        self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSC)
 
116
                        self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale)
 
117
                self.actionPlainText = self.act(self.tr('Plain text'), trigbool=self.enablePlainText)
 
118
                if webkit_available:
 
119
                        self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit)
 
120
                        self.useWebKit = False
 
121
                        if settings.contains('useWebKit'):
 
122
                                if readFromSettings(settings, 'useWebKit', bool):
 
123
                                        self.useWebKit = True
 
124
                                        self.actionWebKit.setChecked(True)
 
125
                if wpgen:
 
126
                        self.actionWpgen = self.act(self.tr('Generate webpages'), trig=self.startWpgen)
 
127
                self.actionShow = self.act(self.tr('Show'), icon='system-file-manager', trig=self.showInDir)
 
128
                self.actionFind = self.act(self.tr('Next'), icon='go-next', shct=QKeySequence.FindNext, trig=self.find)
 
129
                self.actionFindPrev = self.act(self.tr('Previous'), icon='go-previous', shct=QKeySequence.FindPrevious,
 
130
                trig=lambda: self.find(back=True))
 
131
                self.actionHelp = self.act(self.tr('Get help online'), icon='help-contents', trig=self.openHelp)
 
132
                try:
 
133
                        self.aboutWindowTitle = self.tr('About %s') % app_name
 
134
                except:
 
135
                        # For Python 2
 
136
                        self.aboutWindowTitle = self.tr('About %s').replace('%s', '%1').arg(app_name)
 
137
                self.actionAbout = self.act(self.aboutWindowTitle, icon='help-about', trig=self.aboutDialog)
 
138
                self.actionAbout.setMenuRole(QAction.AboutRole)
 
139
                self.actionAboutQt = self.act(self.tr('About Qt'))
 
140
                self.actionAboutQt.setMenuRole(QAction.AboutQtRole)
 
141
                self.connect(self.actionAboutQt, SIGNAL('triggered()'), qApp, SLOT('aboutQt()'))
 
142
                availableMarkups = markups.get_available_markups()
 
143
                if not availableMarkups:
 
144
                        print('Warning: no markups are available!')
 
145
                self.defaultMarkup = availableMarkups[0] if availableMarkups else None
 
146
                if settings.contains('defaultMarkup'):
 
147
                        dm = str(readFromSettings(settings, 'defaultMarkup', str))
 
148
                        mc = markups.find_markup_class_by_name(dm)
 
149
                        if mc and mc.available():
 
150
                                self.defaultMarkup = mc
 
151
                if len(availableMarkups) > 1:
 
152
                        self.chooseGroup = QActionGroup(self)
 
153
                        markupActions = []
 
154
                        for markup in availableMarkups:
 
155
                                markupAction = self.act(markup.name, trigbool=self.markupFunction(markup))
 
156
                                if markup == self.defaultMarkup:
 
157
                                        markupAction.setChecked(True)
 
158
                                self.chooseGroup.addAction(markupAction)
 
159
                                markupActions.append(markupAction)
 
160
                self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertChars('**'))
 
161
                self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertChars('*'))
 
162
                self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline,
 
163
                trig=lambda: self.insertTag(9)) # <u>...</u>
 
164
                self.usefulTags = ('big', 'center', 's', 'small', 'span', 'table', 'td', 'tr', 'u')
 
165
                self.usefulChars = ('deg', 'divide', 'hellip', 'laquo', 'larr',
 
166
                        'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo',
 
167
                        'rarr', 'rsquo', 'times')
 
168
                self.tagsBox = QComboBox(self.editBar)
 
169
                self.tagsBox.addItem(self.tr('Tags'))
 
170
                self.tagsBox.addItems(self.usefulTags)
 
171
                self.connect(self.tagsBox, SIGNAL('activated(int)'), self.insertTag)
 
172
                self.symbolBox = QComboBox(self.editBar)
 
173
                self.symbolBox.addItem(self.tr('Symbols'))
 
174
                self.symbolBox.addItems(self.usefulChars)
 
175
                self.connect(self.symbolBox, SIGNAL('activated(int)'), self.insertSymbol)
 
176
                if settings.contains('styleSheet'):
 
177
                        ssname = readFromSettings(settings, 'styleSheet', str)
 
178
                        sheetfile = QFile(ssname)
 
179
                        sheetfile.open(QIODevice.ReadOnly)
 
180
                        self.ss = QTextStream(sheetfile).readAll()
 
181
                        sheetfile.close()
 
182
                else:
 
183
                        self.ss = ''
 
184
                self.menubar = QMenuBar(self)
 
185
                self.menubar.setGeometry(QRect(0, 0, 800, 25))
 
186
                self.setMenuBar(self.menubar)
 
187
                self.menuFile = self.menubar.addMenu(self.tr('File'))
 
188
                self.menuEdit = self.menubar.addMenu(self.tr('Edit'))
 
189
                self.menuHelp = self.menubar.addMenu(self.tr('Help'))
 
190
                self.menuFile.addAction(self.actionNew)
 
191
                self.menuFile.addAction(self.actionOpen)
 
192
                self.menuRecentFiles = self.menuFile.addMenu(self.tr('Open recent'))
 
193
                self.connect(self.menuRecentFiles, SIGNAL('aboutToShow()'), self.updateRecentFiles)
 
194
                self.menuFile.addMenu(self.menuRecentFiles)
 
195
                self.menuDir = self.menuFile.addMenu(self.tr('Directory'))
 
196
                self.menuDir.addAction(self.actionShow)
 
197
                if wpgen:
 
198
                        self.menuDir.addAction(self.actionWpgen)
 
199
                self.menuFile.addSeparator()
 
200
                self.menuFile.addAction(self.actionSave)
 
201
                self.menuFile.addAction(self.actionSaveAs)
 
202
                self.menuFile.addSeparator()
 
203
                self.menuExport = self.menuFile.addMenu(self.tr('Export'))
 
204
                self.menuExport.addAction(self.actionPerfectHtml)
 
205
                self.menuExport.addAction(self.actionOdf)
 
206
                self.menuExport.addAction(self.actionPdf)
 
207
                if self.extensionActions:
 
208
                        self.menuExport.addSeparator()
 
209
                        for action, mimetype in self.extensionActions:
 
210
                                self.menuExport.addAction(action)
 
211
                        self.connect(self.menuRecentFiles, SIGNAL('aboutToShow()'), self.updateExtensionsVisibility)
 
212
                self.menuFile.addAction(self.actionPrint)
 
213
                self.menuFile.addAction(self.actionPrintPreview)
 
214
                self.menuFile.addSeparator()
 
215
                self.menuFile.addAction(self.actionQuit)
 
216
                self.menuEdit.addAction(self.actionUndo)
 
217
                self.menuEdit.addAction(self.actionRedo)
 
218
                self.menuEdit.addSeparator()
 
219
                self.menuEdit.addAction(self.actionCut)
 
220
                self.menuEdit.addAction(self.actionCopy)
 
221
                self.menuEdit.addAction(self.actionPaste)
 
222
                self.menuEdit.addSeparator()
 
223
                self.sc = False
 
224
                if enchant_available:
 
225
                        self.menuSC = self.menuEdit.addMenu(self.tr('Spell check'))
 
226
                        self.menuSC.addAction(self.actionEnableSC)
 
227
                        self.menuSC.addAction(self.actionSetLocale)
 
228
                self.menuEdit.addAction(self.actionSearch)
 
229
                self.menuEdit.addAction(self.actionPlainText)
 
230
                self.menuEdit.addAction(self.actionChangeFont)
 
231
                self.menuEdit.addSeparator()
 
232
                if len(availableMarkups) > 1:
 
233
                        self.menuMode = self.menuEdit.addMenu(self.tr('Default editing mode'))
 
234
                        for markupAction in markupActions:
 
235
                                self.menuMode.addAction(markupAction)
 
236
                self.menuFormat = self.menuEdit.addMenu(self.tr('Formatting'))
 
237
                self.menuFormat.addAction(self.actionBold)
 
238
                self.menuFormat.addAction(self.actionItalic)
 
239
                self.menuFormat.addAction(self.actionUnderline)
 
240
                if webkit_available:
 
241
                        self.menuEdit.addAction(self.actionWebKit)
 
242
                self.menuEdit.addSeparator()
 
243
                self.menuEdit.addAction(self.actionViewHtml)
 
244
                self.menuEdit.addAction(self.actionLivePreview)
 
245
                self.menuEdit.addAction(self.actionPreview)
 
246
                self.menuEdit.addSeparator()
 
247
                self.menuEdit.addAction(self.actionFullScreen)
 
248
                self.menuHelp.addAction(self.actionHelp)
 
249
                self.menuHelp.addSeparator()
 
250
                self.menuHelp.addAction(self.actionAbout)
 
251
                self.menuHelp.addAction(self.actionAboutQt)
 
252
                self.menubar.addMenu(self.menuFile)
 
253
                self.menubar.addMenu(self.menuEdit)
 
254
                self.menubar.addMenu(self.menuHelp)
 
255
                self.toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
 
256
                self.toolBar.addAction(self.actionNew)
 
257
                self.toolBar.addSeparator()
 
258
                self.toolBar.addAction(self.actionOpen)
 
259
                self.toolBar.addAction(self.actionSave)
 
260
                self.toolBar.addAction(self.actionPrint)
 
261
                self.toolBar.addSeparator()
 
262
                self.toolBar.addAction(self.actionPreview)
 
263
                self.editBar.addAction(self.actionUndo)
 
264
                self.editBar.addAction(self.actionRedo)
 
265
                self.editBar.addSeparator()
 
266
                self.editBar.addAction(self.actionCut)
 
267
                self.editBar.addAction(self.actionCopy)
 
268
                self.editBar.addAction(self.actionPaste)
 
269
                self.editBar.addSeparator()
 
270
                self.editBar.addWidget(self.tagsBox)
 
271
                self.editBar.addWidget(self.symbolBox)
 
272
                self.searchEdit = QLineEdit(self.searchBar)
 
273
                try:
 
274
                        self.searchEdit.setPlaceholderText(self.tr('Search'))
 
275
                except: pass
 
276
                self.connect(self.searchEdit, SIGNAL('returnPressed()'), self.find)
 
277
                self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar)
 
278
                self.searchBar.addWidget(self.searchEdit)
 
279
                self.searchBar.addWidget(self.csBox)
 
280
                self.searchBar.addAction(self.actionFindPrev)
 
281
                self.searchBar.addAction(self.actionFind)
 
282
                self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
 
283
                self.searchBar.setVisible(False)
 
284
                self.autoSave = False
 
285
                if settings.contains('autoSave'):
 
286
                        if readFromSettings(settings, 'autoSave', bool):
 
287
                                self.autoSave = True
 
288
                                timer = QTimer(self)
 
289
                                timer.start(60000)
 
290
                                self.connect(timer, SIGNAL('timeout()'), self.saveAll)
 
291
                self.restorePreviewState = False
 
292
                self.livePreviewEnabled = False
 
293
                if settings.contains('restorePreviewState'):
 
294
                        self.restorePreviewState = readFromSettings(settings, 'restorePreviewState', bool)
 
295
                if settings.contains('previewState'):
 
296
                        self.livePreviewEnabled = readFromSettings(settings, 'previewState', bool)
 
297
                self.ind = 0
 
298
                self.tabWidget.addTab(self.createTab(""), self.tr('New document'))
 
299
                if enchant_available:
 
300
                        self.sl = None
 
301
                        if settings.contains('spellCheckLocale'):
 
302
                                try:
 
303
                                        self.sl = str(readFromSettings(settings, 'spellCheckLocale', str))
 
304
                                        enchant.Dict(self.sl)
 
305
                                except Exception as e:
 
306
                                        print(e)
 
307
                                        self.sl = None
 
308
                        if settings.contains('spellCheck'):
 
309
                                if readFromSettings(settings, 'spellCheck', bool):
 
310
                                        self.actionEnableSC.setChecked(True)
 
311
                                        self.enableSC(True)
 
312
        
 
313
        def act(self, name, icon=None, trig=None, trigbool=None, shct=None):
 
314
                if icon:
 
315
                        action = QAction(self.actIcon(icon), name, self)
 
316
                else:
 
317
                        action = QAction(name, self)
 
318
                if trig:
 
319
                        self.connect(action, SIGNAL('triggered()'), trig)
 
320
                elif trigbool:
 
321
                        action.setCheckable(True)
 
322
                        self.connect(action, SIGNAL('triggered(bool)'), trigbool)
 
323
                if shct:
 
324
                        action.setShortcut(shct)
 
325
                return action
 
326
        
 
327
        def actIcon(self, name):
 
328
                return QIcon.fromTheme(name, QIcon(icon_path+name+'.png'))
 
329
        
 
330
        def printError(self, error):
 
331
                print('Exception occured while parsing document:')
 
332
                print(error)
 
333
        
 
334
        def getSplitter(self, index):
 
335
                splitter = QSplitter(Qt.Horizontal)
 
336
                # Give both boxes a minimum size so the minimumSizeHint will be
 
337
                # ignored when splitter.setSizes is called below
 
338
                for widget in self.editBoxes[index], self.previewBoxes[index]:
 
339
                        widget.setMinimumWidth(125)
 
340
                        splitter.addWidget(widget)
 
341
                splitter.setSizes((50, 50))
 
342
                splitter.setChildrenCollapsible(False)
 
343
                return splitter
 
344
        
 
345
        def createTab(self, fileName):
 
346
                self.previewBlocked = False
 
347
                self.editBoxes.append(QTextEdit())
 
348
                self.highlighters.append(ReTextHighlighter(self.editBoxes[-1].document()))
 
349
                if enchant_available and self.actionEnableSC.isChecked():
 
350
                        self.highlighters[-1].dictionary = \
 
351
                        enchant.Dict(self.sl) if self.sl else enchant.Dict()
 
352
                        self.highlighters[-1].rehighlight()
 
353
                if self.useWebKit:
 
354
                        self.previewBoxes.append(QWebView())
 
355
                else:
 
356
                        self.previewBoxes.append(QTextEdit())
 
357
                        self.previewBoxes[-1].setReadOnly(True)
 
358
                self.editBoxes[-1].contextMenuEvent = self.editBoxMenuEvent
 
359
                if self.tabInsertsSpaces:
 
360
                        self.editBoxes[-1].keyPressEvent = self.editBoxKeyPressEvent
 
361
                self.previewBoxes[-1].setVisible(False)
 
362
                self.fileNames.append(fileName)
 
363
                markupClass = self.getMarkupClass(fileName)
 
364
                self.markups.append(self.getMarkup(fileName))
 
365
                self.highlighters[-1].docType = (markupClass.name if markupClass else '')
 
366
                liveMode = self.restorePreviewState and self.livePreviewEnabled
 
367
                self.apc.append(liveMode)
 
368
                self.alpc.append(liveMode)
 
369
                self.aptc.append(False)
 
370
                self.editBoxes[-1].setFont(monofont)
 
371
                metrics = QFontMetrics(self.editBoxes[-1].font())
 
372
                self.editBoxes[-1].setTabStopWidth(self.tabWidth*metrics.width(' '))
 
373
                self.editBoxes[-1].setAcceptRichText(False)
 
374
                self.connect(self.editBoxes[-1], SIGNAL('textChanged()'), self.updateLivePreviewBox)
 
375
                self.connect(self.editBoxes[-1], SIGNAL('undoAvailable(bool)'), self.actionUndo, SLOT('setEnabled(bool)'))
 
376
                self.connect(self.editBoxes[-1], SIGNAL('redoAvailable(bool)'), self.actionRedo, SLOT('setEnabled(bool)'))
 
377
                self.connect(self.editBoxes[-1], SIGNAL('copyAvailable(bool)'), self.enableCopy)
 
378
                self.connect(self.editBoxes[-1].document(), SIGNAL('modificationChanged(bool)'), self.modificationChanged)
 
379
                return self.getSplitter(-1)
 
380
        
 
381
        def editBoxMenuEvent(self, event):
 
382
                editBox = self.editBoxes[self.ind]
 
383
                text = editBox.toPlainText()
 
384
                dictionary = self.highlighters[self.ind].dictionary
 
385
                if dictionary is None or not text:
 
386
                        return QTextEdit.contextMenuEvent(editBox, event)
 
387
                oldcursor = editBox.textCursor()
 
388
                cursor = editBox.cursorForPosition(event.pos())
 
389
                pos = cursor.positionInBlock()
 
390
                if pos == len(text): pos -= 1
 
391
                try:
 
392
                        curchar = text[pos]
 
393
                        isalpha = curchar.isalpha()
 
394
                except AttributeError:
 
395
                        # For Python 2
 
396
                        curchar = text.at(pos)
 
397
                        isalpha = curchar.isLetter()
 
398
                if not isalpha:
 
399
                        return QTextEdit.contextMenuEvent(editBox, event)
 
400
                cursor.select(QTextCursor.WordUnderCursor)
 
401
                editBox.setTextCursor(cursor)
 
402
                word = convertToUnicode(cursor.selectedText())
 
403
                if not word or dictionary.check(word):
 
404
                        editBox.setTextCursor(oldcursor)
 
405
                        return QTextEdit.contextMenuEvent(editBox, event)
 
406
                suggestions = dictionary.suggest(word)
 
407
                actions = [self.act(sug, trig=self.fixWord(sug)) for sug in suggestions]
 
408
                menu = editBox.createStandardContextMenu()
 
409
                menu.insertSeparator(menu.actions()[0])
 
410
                for action in actions[::-1]:
 
411
                        menu.insertAction(menu.actions()[0], action) 
 
412
                menu.exec_(event.globalPos())
 
413
        
 
414
        def fixWord(self, correctword):
 
415
                return lambda: self.editBoxes[self.ind].insertPlainText(correctword)
 
416
        
 
417
        def editBoxKeyPressEvent(self, event):
 
418
                key = event.key()
 
419
                if key == Qt.Key_Tab:
 
420
                        self.editBoxIndentMore(self.editBoxes[self.ind])
 
421
                elif key == Qt.Key_Backtab:
 
422
                        self.editBoxIndentLess(self.editBoxes[self.ind])
 
423
                else:
 
424
                        QTextEdit.keyPressEvent(self.editBoxes[self.ind], event)
 
425
        
 
426
        def editBoxIndentMore(self, editBox):
 
427
                cursor = editBox.textCursor()
 
428
                if cursor.hasSelection():
 
429
                        block = editBox.document().findBlock(cursor.selectionStart())
 
430
                        end = editBox.document().findBlock(cursor.selectionEnd()).next()
 
431
                        cursor.beginEditBlock()
 
432
                        while block != end:
 
433
                                cursor.setPosition(block.position())
 
434
                                cursor.insertText(' ' * self.tabWidth)
 
435
                                block = block.next()
 
436
                        cursor.endEditBlock()
 
437
                else:
 
438
                        indent = self.tabWidth - (cursor.positionInBlock() % self.tabWidth)
 
439
                        cursor.insertText(' ' * indent)
 
440
        
 
441
        def editBoxIndentLess(self, editBox):
 
442
                cursor = editBox.textCursor()
 
443
                if cursor.hasSelection():
 
444
                        block = editBox.document().findBlock(cursor.selectionStart())
 
445
                        end = editBox.document().findBlock(cursor.selectionEnd()).next()
 
446
                else:
 
447
                        block = editBox.document().findBlock(cursor.position())
 
448
                        end = block.next()
 
449
                cursor.beginEditBlock()
 
450
                while block != end:
 
451
                        cursor.setPosition(block.position())
 
452
                        pos = 0
 
453
                        while editBox.document().characterAt(cursor.position()) == ' ' \
 
454
                        and pos < self.tabWidth:
 
455
                                pos += 1
 
456
                                cursor.deleteChar()
 
457
                        block = block.next()
 
458
                cursor.endEditBlock()
 
459
        
 
460
        def closeTab(self, ind):
 
461
                if self.maybeSave(ind):
 
462
                        if self.tabWidget.count() == 1:
 
463
                                self.tabWidget.addTab(self.createTab(""), self.tr("New document"))
 
464
                        del self.editBoxes[ind]
 
465
                        del self.previewBoxes[ind]
 
466
                        del self.highlighters[ind]
 
467
                        del self.markups[ind]
 
468
                        del self.fileNames[ind]
 
469
                        del self.apc[ind]
 
470
                        del self.alpc[ind]
 
471
                        del self.aptc[ind]
 
472
                        self.tabWidget.removeTab(ind)
 
473
        
 
474
        def getMarkupClass(self, fileName=None):
 
475
                if fileName is None:
 
476
                        fileName = self.fileNames[self.ind]
 
477
                fileName = convertToUnicode(fileName)
 
478
                if self.actionPlainText.isChecked():
 
479
                        return
 
480
                if fileName:
 
481
                        markupClass = markups.get_markup_for_file_name(
 
482
                                fileName, return_class=True)
 
483
                        if markupClass:
 
484
                                return markupClass
 
485
                return self.defaultMarkup
 
486
        
 
487
        def getMarkup(self, fileName=None):
 
488
                if fileName is None:
 
489
                        fileName = self.fileNames[self.ind]
 
490
                fileName = convertToUnicode(fileName)
 
491
                markupClass = self.getMarkupClass(fileName=fileName)
 
492
                if markupClass and markupClass.available():
 
493
                        return markupClass(filename=fileName)
 
494
        
 
495
        def docTypeChanged(self):
 
496
                self.markups[self.ind] = self.getMarkup()
 
497
                oldType = self.highlighters[self.ind].docType
 
498
                markupClass = self.getMarkupClass()
 
499
                newType = markupClass.name if markupClass else ''
 
500
                if oldType != newType:
 
501
                        self.updatePreviewBox()
 
502
                        self.highlighters[self.ind].docType = newType
 
503
                        self.highlighters[self.ind].rehighlight()
 
504
                dtMarkdown = (newType == DOCTYPE_MARKDOWN)
 
505
                dtMkdOrReST = (newType in (DOCTYPE_MARKDOWN, DOCTYPE_REST))
 
506
                self.tagsBox.setEnabled(dtMarkdown)
 
507
                self.symbolBox.setEnabled(dtMarkdown)
 
508
                self.actionUnderline.setEnabled(dtMarkdown)
 
509
                self.actionBold.setEnabled(dtMkdOrReST)
 
510
                self.actionItalic.setEnabled(dtMkdOrReST)
 
511
        
 
512
        def changeIndex(self, ind):
 
513
                if ind > -1:
 
514
                        self.actionPlainText.setChecked(self.aptc[ind])
 
515
                        self.actionPerfectHtml.setDisabled(self.aptc[ind])
 
516
                        self.actionViewHtml.setDisabled(self.aptc[ind])
 
517
                        self.actionUndo.setEnabled(self.editBoxes[ind].document().isUndoAvailable())
 
518
                        self.actionRedo.setEnabled(self.editBoxes[ind].document().isRedoAvailable())
 
519
                        self.actionCopy.setEnabled(self.editBoxes[ind].textCursor().hasSelection())
 
520
                        self.actionCut.setEnabled(self.editBoxes[ind].textCursor().hasSelection())
 
521
                        self.actionPreview.setChecked(self.apc[ind])
 
522
                        self.actionLivePreview.setChecked(self.alpc[ind])
 
523
                        self.editBar.setDisabled(self.apc[ind])
 
524
                self.ind = ind
 
525
                if self.fileNames[ind]:
 
526
                        self.setCurrentFile()
 
527
                else:
 
528
                        try:
 
529
                                self.setWindowTitle(self.tr('New document') + '[*] ' + QChar(0x2014) + ' ' + app_name)
 
530
                        except:
 
531
                                # For Python 3
 
532
                                self.setWindowTitle(self.tr('New document') + '[*] \u2014 ' + app_name)
 
533
                self.modificationChanged(self.editBoxes[ind].document().isModified())
 
534
                self.livePreviewEnabled = self.alpc[ind]
 
535
                if self.alpc[ind]:
 
536
                        self.enableLivePreview(True)
 
537
                self.editBoxes[self.ind].setFocus(Qt.OtherFocusReason)
 
538
        
 
539
        def changeFont(self):
 
540
                if not self.font:
 
541
                        self.font = QFont()
 
542
                fd = QFontDialog.getFont(self.font, self)
 
543
                if fd[1]:
 
544
                        self.font = QFont()
 
545
                        self.font.setFamily(fd[0].family())
 
546
                        settings.setValue('font', fd[0].family())
 
547
                        self.font.setPointSize(fd[0].pointSize())
 
548
                        settings.setValue('fontSize', fd[0].pointSize())
 
549
                        self.updatePreviewBox()
 
550
        
 
551
        def preview(self, viewmode):
 
552
                self.apc[self.ind] = viewmode
 
553
                if self.actionLivePreview.isChecked():
 
554
                        self.actionLivePreview.setChecked(False)
 
555
                        return self.enableLivePreview(False)
 
556
                self.editBar.setDisabled(viewmode)
 
557
                self.editBoxes[self.ind].setVisible(not viewmode)
 
558
                self.previewBoxes[self.ind].setVisible(viewmode)
 
559
                if viewmode:
 
560
                        self.updatePreviewBox()
 
561
        
 
562
        def enableLivePreview(self, livemode):
 
563
                self.livePreviewEnabled = livemode
 
564
                self.alpc[self.ind] = livemode
 
565
                self.apc[self.ind] = livemode
 
566
                self.actionPreview.setChecked(livemode)
 
567
                self.editBar.setEnabled(True)
 
568
                self.previewBoxes[self.ind].setVisible(livemode)
 
569
                self.editBoxes[self.ind].setVisible(True)
 
570
                if livemode:
 
571
                        self.updatePreviewBox()
 
572
        
 
573
        def enableWebKit(self, enable):
 
574
                self.useWebKit = enable
 
575
                if enable:
 
576
                        settings.setValue('useWebKit', True)
 
577
                else:
 
578
                        settings.remove('useWebKit')
 
579
                oldind = self.ind
 
580
                self.tabWidget.clear()
 
581
                for self.ind in range(len(self.editBoxes)):
 
582
                        if enable:
 
583
                                self.previewBoxes[self.ind] = QWebView()
 
584
                        else:
 
585
                                self.previewBoxes[self.ind] = QTextEdit()
 
586
                                self.previewBoxes[self.ind].setReadOnly(True)
 
587
                        splitter = self.getSplitter(self.ind)
 
588
                        self.tabWidget.addTab(splitter, self.getDocumentTitle(baseName=True))
 
589
                        self.updatePreviewBox()
 
590
                        self.previewBoxes[self.ind].setVisible(self.apc[self.ind])
 
591
                self.ind = oldind
 
592
                self.tabWidget.setCurrentIndex(self.ind)
 
593
        
 
594
        def enableCopy(self, copymode):
 
595
                self.actionCopy.setEnabled(copymode)
 
596
                self.actionCut.setEnabled(copymode)
 
597
        
 
598
        def enableFullScreen(self, yes):
 
599
                if yes:
 
600
                        self.showFullScreen()
 
601
                else:
 
602
                        self.showNormal()
 
603
        
 
604
        def enableSC(self, yes):
 
605
                if yes:
 
606
                        if self.sl:
 
607
                                self.setAllDictionaries(enchant.Dict(self.sl))
 
608
                        else:
 
609
                                self.setAllDictionaries(enchant.Dict())
 
610
                        settings.setValue('spellCheck', True)
 
611
                else:
 
612
                        self.setAllDictionaries(None)
 
613
                        settings.remove('spellCheck')
 
614
        
 
615
        def setAllDictionaries(self, dictionary):
 
616
                for hl in self.highlighters:
 
617
                        hl.dictionary = dictionary
 
618
                        hl.rehighlight()
 
619
        
 
620
        def changeLocale(self):
 
621
                if self.sl == None:
 
622
                        text = ""
 
623
                else:
 
624
                        text = self.sl
 
625
                sl, ok = QInputDialog.getText(self, app_name, self.tr('Enter locale name (example: en_US)'), QLineEdit.Normal, text)
 
626
                if ok and sl:
 
627
                        try:
 
628
                                sl = str(sl)
 
629
                                enchant.Dict(sl)
 
630
                        except Exception as e:
 
631
                                QMessageBox.warning(self, app_name, str(e))
 
632
                        else:
 
633
                                self.sl = sl
 
634
                                self.enableSC(self.actionEnableSC.isChecked())
 
635
                elif not sl:
 
636
                        self.sl = None
 
637
                        self.enableSC(self.actionEnableSC.isChecked())
 
638
        
 
639
        def searchBarVisibilityChanged(self, visible):
 
640
                self.actionSearch.setChecked(visible)
 
641
                if visible:
 
642
                        self.searchEdit.setFocus(Qt.ShortcutFocusReason)
 
643
        
 
644
        def find(self, back=False):
 
645
                flags = 0
 
646
                if back:
 
647
                        flags = QTextDocument.FindBackward
 
648
                if self.csBox.isChecked():
 
649
                        flags = flags | QTextDocument.FindCaseSensitively
 
650
                text = self.searchEdit.text()
 
651
                if not self.findMain(text, flags):
 
652
                        if text in self.editBoxes[self.ind].toPlainText():
 
653
                                cursor = self.editBoxes[self.ind].textCursor()
 
654
                                if back:
 
655
                                        cursor.movePosition(QTextCursor.End)
 
656
                                else:
 
657
                                        cursor.movePosition(QTextCursor.Start)
 
658
                                self.editBoxes[self.ind].setTextCursor(cursor)
 
659
                                self.findMain(text, flags)
 
660
        
 
661
        def findMain(self, text, flags):
 
662
                if flags:
 
663
                        return self.editBoxes[self.ind].find(text, flags)
 
664
                else:
 
665
                        return self.editBoxes[self.ind].find(text)
 
666
        
 
667
        def getHtml(self, includeStyleSheet=True, includeTitle=True,
 
668
                    includeMeta=False, styleForWebKit=False):
 
669
                if self.markups[self.ind] is None:
 
670
                        return '<p style="color: red">'\
 
671
                        +self.tr('Could not parse file contents, check if you have the necessary module installed!')+'</p>'
 
672
                text = convertToUnicode(self.editBoxes[self.ind].toPlainText())
 
673
                # WpGen directives
 
674
                text = text.replace('%HTMLDIR%', 'html')
 
675
                text = text.replace('%\\', '%')
 
676
                headers = ''
 
677
                if includeStyleSheet:
 
678
                        fontline = ''
 
679
                        if styleForWebKit:
 
680
                                fontname = self.font.family() if self.font else 'Sans'
 
681
                                fontsize = (self.font if self.font else QFont()).pointSize()
 
682
                                fontline = 'body { font-family: %s; font-size: %spt }\n' % \
 
683
                                        (fontname, fontsize)
 
684
                        headers += '<style type="text/css">\n' + fontline + self.ss + '</style>\n'
 
685
                if includeMeta:
 
686
                        headers += '<meta name="generator" content="%s %s">\n' % \
 
687
                        (app_name, app_version)
 
688
                fallbackTitle = self.getDocumentTitle() if includeTitle else ''
 
689
                if includeStyleSheet:
 
690
                        return self.markups[self.ind].get_whole_html(text,
 
691
                                custom_headers=headers, fallback_title=fallbackTitle)
 
692
                else:
 
693
                        return self.markups[self.ind].get_whole_html(text,
 
694
                                custom_headers=headers, include_stylesheet=False,
 
695
                                fallback_title=fallbackTitle)
 
696
        
 
697
        def updatePreviewBox(self):
 
698
                self.previewBlocked = False
 
699
                pb = self.previewBoxes[self.ind]
 
700
                textedit = isinstance(pb, QTextEdit)
 
701
                if textedit:
 
702
                        scrollbar = pb.verticalScrollBar()
 
703
                        scrollpos = scrollbar.value()
 
704
                else:
 
705
                        frame = pb.page().mainFrame()
 
706
                        scrollpos = frame.scrollPosition()
 
707
                if self.actionPlainText.isChecked():
 
708
                        if textedit:
 
709
                                pb.setPlainText(self.editBoxes[self.ind].toPlainText())
 
710
                        else:
 
711
                                td = QTextDocument()
 
712
                                td.setPlainText(self.editBoxes[self.ind].toPlainText())
 
713
                                pb.setHtml(td.toHtml())
 
714
                else:
 
715
                        try:
 
716
                                html = self.getHtml(styleForWebKit=(not textedit))
 
717
                        except Exception as e:
 
718
                                return self.printError(e)
 
719
                        if not textedit and '<script ' in html:
 
720
                                # Work-around a bug in QtWebKit
 
721
                                # by saving the html locally
 
722
                                tempFile = QTemporaryFile('retext-XXXXXX.html')
 
723
                                tempFile.setAutoRemove(False)
 
724
                                tempFile.open(QIODevice.WriteOnly)
 
725
                                stream = QTextStream(tempFile)
 
726
                                stream << html
 
727
                                tempFile.close()
 
728
                                self.connect(pb, SIGNAL('loadFinished(bool)'),
 
729
                                        lambda ok: tempFile.remove())
 
730
                                pb.load(QUrl.fromLocalFile(tempFile.fileName()))
 
731
                        else:
 
732
                                pb.setHtml(html)
 
733
                if self.font and textedit:
 
734
                        pb.document().setDefaultFont(self.font)
 
735
                if textedit:
 
736
                        scrollbar.setValue(scrollpos)
 
737
                else:
 
738
                        frame.setScrollPosition(scrollpos)
 
739
        
 
740
        def updateLivePreviewBox(self):
 
741
                if self.actionLivePreview.isChecked() and self.previewBlocked == False:
 
742
                        self.previewBlocked = True
 
743
                        QTimer.singleShot(1000, self.updatePreviewBox)
 
744
        
 
745
        def startWpgen(self):
 
746
                if self.fileNames[self.ind] == "":
 
747
                        QMessageBox.warning(self, app_name, self.tr("Please, save the file somewhere."))
 
748
                elif wpgen:
 
749
                        if not (QDir("html").exists() and QFile.exists("template.html")):
 
750
                                Popen((wpgen, 'init')).wait()
 
751
                        Popen([wpgen, 'updateall']).wait()
 
752
                        msgBox = QMessageBox(QMessageBox.Information, app_name,
 
753
                        self.tr("Webpages saved in <code>html</code> directory."), QMessageBox.Ok)
 
754
                        showButton = msgBox.addButton(self.tr("Show directory"), QMessageBox.AcceptRole)
 
755
                        msgBox.exec_()
 
756
                        if msgBox.clickedButton() == showButton:
 
757
                                QDesktopServices.openUrl(QUrl.fromLocalFile(QDir('html').absolutePath()))
 
758
                else:
 
759
                        QMessageBox.error(self, app_name, self.tr("Webpages generator is not installed!"))
 
760
        
 
761
        def showInDir(self):
 
762
                if self.fileNames[self.ind]:
 
763
                        QDesktopServices.openUrl(QUrl.fromLocalFile(QFileInfo(self.fileNames[self.ind]).path()))
 
764
                else:
 
765
                        QMessageBox.warning(self, app_name, self.tr("Please, save the file somewhere."))
 
766
        
 
767
        def setCurrentFile(self):
 
768
                self.setWindowTitle("")
 
769
                self.tabWidget.setTabText(self.ind, self.getDocumentTitle(baseName=True))
 
770
                self.setWindowFilePath(self.fileNames[self.ind])
 
771
                files = readListFromSettings(settings, "recentFileList")
 
772
                try:
 
773
                        files.prepend(self.fileNames[self.ind])
 
774
                        files.removeDuplicates()
 
775
                except:
 
776
                        # For Python 3
 
777
                        while self.fileNames[self.ind] in files:
 
778
                                files.remove(self.fileNames[self.ind])
 
779
                        files.insert(0, self.fileNames[self.ind])
 
780
                if len(files) > 10:
 
781
                        del files[10:]
 
782
                writeListToSettings(settings, "recentFileList", files)
 
783
                QDir.setCurrent(QFileInfo(self.fileNames[self.ind]).dir().path())
 
784
                self.docTypeChanged()
 
785
        
 
786
        def createNew(self):
 
787
                self.tabWidget.addTab(self.createTab(""), self.tr("New document"))
 
788
                self.ind = self.tabWidget.count()-1
 
789
                self.tabWidget.setCurrentIndex(self.ind)
 
790
        
 
791
        def updateRecentFiles(self):
 
792
                self.menuRecentFiles.clear()
 
793
                self.recentFilesActions = []
 
794
                filesOld = readListFromSettings(settings, "recentFileList")
 
795
                files = []
 
796
                for f in filesOld:
 
797
                        if QFile.exists(f):
 
798
                                files.append(f)
 
799
                                self.recentFilesActions.append(self.act(f, trig=self.openFunction(f)))
 
800
                writeListToSettings(settings, "recentFileList", files)
 
801
                for action in self.recentFilesActions:
 
802
                        self.menuRecentFiles.addAction(action)
 
803
        
 
804
        def markupFunction(self, markup):
 
805
                return lambda: self.setDefaultMarkup(markup)
 
806
        
 
807
        def openFunction(self, fileName):
 
808
                return lambda: self.openFileWrapper(fileName)
 
809
        
 
810
        def extensionFuntion(self, data):
 
811
                return lambda: \
 
812
                self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension'])
 
813
        
 
814
        def getExportExtensionsList(self):
 
815
                extensions = []
 
816
                for extsprefix in ('/usr', QDir.homePath()+'/.local'):
 
817
                        extsdir = QDir(extsprefix+'/share/retext/export-extensions/')
 
818
                        if extsdir.exists():
 
819
                                for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable):
 
820
                                        extensions.append(self.readExtension(fileInfo.filePath()))
 
821
                locale = QLocale.system().name()
 
822
                self.extensionActions = []
 
823
                for extension in extensions:
 
824
                        try:
 
825
                                if ('Name[%s]' % locale) in extension:
 
826
                                        name = extension['Name[%s]' % locale]
 
827
                                elif ('Name[%s]' % locale.split('_')[0]) in extension:
 
828
                                        name = extension['Name[%s]' % locale.split('_')[0]]
 
829
                                else:
 
830
                                        name = extension['Name']
 
831
                                data = {}
 
832
                                for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
 
833
                                        if 'X-ReText-'+prop in extension:
 
834
                                                data[prop] = extension['X-ReText-'+prop]
 
835
                                        elif prop in extension:
 
836
                                                data[prop] = extension[prop]
 
837
                                        else:
 
838
                                                data[prop] = ''
 
839
                                action = self.act(name, trig=self.extensionFuntion(data))
 
840
                                if 'Icon' in extension:
 
841
                                        action.setIcon(self.actIcon(extension['Icon']))
 
842
                                mimetype = extension['MimeType'] if 'MimeType' in extension else None
 
843
                        except KeyError:
 
844
                                print('Failed to parse extension: Name is required')
 
845
                        else:
 
846
                                self.extensionActions.append((action, mimetype))
 
847
        
 
848
        def updateExtensionsVisibility(self):
 
849
                markupClass = self.getMarkupClass()
 
850
                for action in self.extensionActions:
 
851
                        if markupClass is None:
 
852
                                action[0].setEnabled(False)
 
853
                                continue
 
854
                        mimetype = action[1]
 
855
                        if mimetype == None:
 
856
                                enabled = True
 
857
                        elif markupClass == markups.MarkdownMarkup:
 
858
                                enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown"))
 
859
                        elif markupClass == markups.ReStructuredTextMarkup:
 
860
                                enabled = (mimetype in ("text/x-retext-rst", "text/x-rst"))
 
861
                        else:
 
862
                                enabled = False
 
863
                        action[0].setEnabled(enabled)
 
864
        
 
865
        def readExtension(self, fileName):
 
866
                extFile = QFile(fileName)
 
867
                extFile.open(QIODevice.ReadOnly)
 
868
                extension = {}
 
869
                stream = QTextStream(extFile)
 
870
                while not stream.atEnd():
 
871
                        line = convertToUnicode(stream.readLine())
 
872
                        if '=' in line:
 
873
                                index = line.index('=')
 
874
                                extension[line[:index].rstrip()] = line[index+1:].lstrip()
 
875
                extFile.close()
 
876
                return extension
 
877
        
 
878
        def openFile(self):
 
879
                supportedExtensions = ['.txt']
 
880
                for markup in markups.known_markups:
 
881
                        supportedExtensions = supportedExtensions + list(markup.file_extensions)
 
882
                fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;'
 
883
                fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), "",
 
884
                self.tr("Supported files") + fileFilter + self.tr("All files (*)"))
 
885
                for fileName in fileNames:
 
886
                        self.openFileWrapper(fileName)
 
887
        
 
888
        def openFileWrapper(self, fileName):
 
889
                if not fileName:
 
890
                        return
 
891
                fileName = QFileInfo(fileName).canonicalFilePath()
 
892
                exists = False
 
893
                for i in range(self.tabWidget.count()):
 
894
                        if self.fileNames[i] == fileName:
 
895
                                exists = True
 
896
                                ex = i
 
897
                if exists:
 
898
                        self.tabWidget.setCurrentIndex(ex)
 
899
                elif QFile.exists(fileName):
 
900
                        if self.fileNames[self.ind] or self.editBoxes[self.ind].toPlainText() \
 
901
                        or self.editBoxes[self.ind].document().isModified():
 
902
                                self.tabWidget.addTab(self.createTab(""), "")
 
903
                                self.ind = self.tabWidget.count()-1
 
904
                                self.tabWidget.setCurrentIndex(self.ind)
 
905
                        self.fileNames[self.ind] = fileName
 
906
                        self.openFileMain()
 
907
        
 
908
        def openFileMain(self):
 
909
                openfile = QFile(self.fileNames[self.ind])
 
910
                openfile.open(QIODevice.ReadOnly)
 
911
                html = QTextStream(openfile).readAll()
 
912
                openfile.close()
 
913
                self.editBoxes[self.ind].setPlainText(html)
 
914
                suffix = QFileInfo(self.fileNames[self.ind]).suffix()
 
915
                pt = suffix not in ('re', 'md', 'markdown', 'mdown', 'mkd', 'mkdn', 'rst', 'rest')
 
916
                if settings.contains('autoPlainText'):
 
917
                        if not readFromSettings(settings, 'autoPlainText', bool):
 
918
                                pt = False
 
919
                self.actionPlainText.setChecked(pt)
 
920
                self.enablePlainText(pt)
 
921
                self.setCurrentFile()
 
922
                self.setWindowModified(False)
 
923
        
 
924
        def saveFile(self):
 
925
                self.saveFileMain(dlg=False)
 
926
        
 
927
        def saveFileAs(self):
 
928
                self.saveFileMain(dlg=True)
 
929
        
 
930
        def saveAll(self):
 
931
                oldind = self.ind
 
932
                for self.ind in range(self.tabWidget.count()):
 
933
                        if self.fileNames[self.ind] and QFileInfo(self.fileNames[self.ind]).isWritable():
 
934
                                self.saveFileCore(self.fileNames[self.ind])
 
935
                                self.editBoxes[self.ind].document().setModified(False)
 
936
                self.ind = oldind
 
937
        
 
938
        def saveFileMain(self, dlg):
 
939
                if (not self.fileNames[self.ind]) or dlg:
 
940
                        markupClass = self.getMarkupClass()
 
941
                        if markupClass is None:
 
942
                                defaultExt = self.tr("Plain text (*.txt)")
 
943
                                ext = ".txt"
 
944
                        else:
 
945
                                defaultExt = self.tr('%s files') % markupClass.name + ' (' \
 
946
                                        + str.join(' ', ['*'+ext for ext in markupClass.file_extensions]) + ')'
 
947
                                ext = markupClass.default_extension
 
948
                        self.fileNames[self.ind] = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", defaultExt)
 
949
                        if self.fileNames[self.ind] and not QFileInfo(self.fileNames[self.ind]).suffix():
 
950
                                self.fileNames[self.ind] += ext
 
951
                if self.fileNames[self.ind]:
 
952
                        result = self.saveFileCore(self.fileNames[self.ind])
 
953
                        if result:
 
954
                                self.setCurrentFile()
 
955
                                self.editBoxes[self.ind].document().setModified(False)
 
956
                                self.setWindowModified(False)
 
957
                                return True
 
958
                        else:
 
959
                                QMessageBox.warning(self, app_name, self.tr("Cannot save to file because it is read-only!"))
 
960
                return False
 
961
        
 
962
        def saveFileCore(self, fn):
 
963
                savefile = QFile(fn)
 
964
                result = savefile.open(QIODevice.WriteOnly)
 
965
                if result:
 
966
                        savestream = QTextStream(savefile)
 
967
                        savestream << self.editBoxes[self.ind].toPlainText()
 
968
                        savefile.close()
 
969
                return result
 
970
        
 
971
        def saveHtml(self, fileName):
 
972
                if not QFileInfo(fileName).suffix():
 
973
                        fileName += ".html"
 
974
                try:
 
975
                        htmltext = self.getHtml(includeStyleSheet=False, includeMeta=True)
 
976
                except Exception as e:
 
977
                        return self.printError(e)
 
978
                htmlFile = QFile(fileName)
 
979
                htmlFile.open(QIODevice.WriteOnly)
 
980
                html = QTextStream(htmlFile)
 
981
                html << htmltext
 
982
                htmlFile.close()
 
983
        
 
984
        def textDocument(self):
 
985
                td = QTextDocument()
 
986
                td.setMetaInformation(QTextDocument.DocumentTitle, self.getDocumentTitle())
 
987
                if self.ss:
 
988
                        td.setDefaultStyleSheet(self.ss)
 
989
                if self.actionPlainText.isChecked():
 
990
                        td.setPlainText(self.editBoxes[self.ind].toPlainText())
 
991
                else:
 
992
                        td.setHtml(self.getHtml())
 
993
                if self.font:
 
994
                        td.setDefaultFont(self.font)
 
995
                return td
 
996
        
 
997
        def saveOdf(self):
 
998
                try:
 
999
                        document = self.textDocument()
 
1000
                except Exception as e:
 
1001
                        self.printError(e)
 
1002
                        return
 
1003
                fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), "", self.tr("OpenDocument text files (*.odt)"))
 
1004
                if not QFileInfo(fileName).suffix():
 
1005
                        fileName += ".odt"
 
1006
                writer = QTextDocumentWriter(fileName)
 
1007
                writer.setFormat("odf")
 
1008
                writer.write(document)
 
1009
        
 
1010
        def saveFilePerfect(self):
 
1011
                fileName = None
 
1012
                fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", self.tr("HTML files (*.html *.htm)"))
 
1013
                if fileName:
 
1014
                        self.saveHtml(fileName)
 
1015
        
 
1016
        def getDocumentForPrint(self):
 
1017
                if self.useWebKit:
 
1018
                        return self.previewBoxes[self.ind]
 
1019
                try:
 
1020
                        return self.textDocument()
 
1021
                except Exception as e:
 
1022
                        self.printError(e)
 
1023
        
 
1024
        def standardPrinter(self):
 
1025
                printer = QPrinter(QPrinter.HighResolution)
 
1026
                printer.setDocName(self.getDocumentTitle())
 
1027
                printer.setCreator(app_name+" "+app_version)
 
1028
                return printer
 
1029
        
 
1030
        def savePdf(self):
 
1031
                self.updatePreviewBox()
 
1032
                fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"),
 
1033
                        "", self.tr("PDF files (*.pdf)"))
 
1034
                if fileName:
 
1035
                        if not QFileInfo(fileName).suffix():
 
1036
                                fileName += ".pdf"
 
1037
                        printer = self.standardPrinter()
 
1038
                        printer.setOutputFormat(QPrinter.PdfFormat)
 
1039
                        printer.setOutputFileName(fileName)
 
1040
                        document = self.getDocumentForPrint()
 
1041
                        if document != None:
 
1042
                                document.print_(printer)
 
1043
        
 
1044
        def printFile(self):
 
1045
                self.updatePreviewBox()
 
1046
                printer = self.standardPrinter()
 
1047
                dlg = QPrintDialog(printer, self)
 
1048
                dlg.setWindowTitle(self.tr("Print document"))
 
1049
                if (dlg.exec_() == QDialog.Accepted):
 
1050
                        document = self.getDocumentForPrint()
 
1051
                        if document != None:
 
1052
                                document.print_(printer)
 
1053
        
 
1054
        def printPreview(self):
 
1055
                document = self.getDocumentForPrint()
 
1056
                if document == None:
 
1057
                        return
 
1058
                printer = self.standardPrinter()
 
1059
                preview = QPrintPreviewDialog(printer, self)
 
1060
                self.connect(preview, SIGNAL("paintRequested(QPrinter*)"), document.print_)
 
1061
                preview.exec_()
 
1062
        
 
1063
        def runExtensionCommand(self, command, filefilter, defaultext):
 
1064
                of = ('%of' in command)
 
1065
                html = ('%html' in command)
 
1066
                if of:
 
1067
                        if defaultext and not filefilter:
 
1068
                                filefilter = '*'+defaultext
 
1069
                        fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)
 
1070
                        if not fileName:
 
1071
                                return
 
1072
                        if defaultext and not QFileInfo(fileName).suffix():
 
1073
                                fileName += defaultext
 
1074
                if html:
 
1075
                        tmpname = '.retext-temp.html'
 
1076
                        self.saveHtml(tmpname)
 
1077
                else:
 
1078
                        tmpname = '.retext-temp' + self.getMarkupClass().default_extension
 
1079
                        self.saveFileCore(tmpname)
 
1080
                command = command.replace('%of', 'out'+defaultext)
 
1081
                command = command.replace('%html' if html else '%if', tmpname)
 
1082
                args = str(command).split()
 
1083
                try:
 
1084
                        Popen(args).wait()
 
1085
                except Exception as error:
 
1086
                        errorstr = str(error)
 
1087
                        try:
 
1088
                                errorstr = QString.fromUtf8(errorstr)
 
1089
                        except:
 
1090
                                # Not needed for Python 3
 
1091
                                pass
 
1092
                        QMessageBox.warning(self, app_name, self.tr('Failed to execute the command:') + '\n' + errorstr)
 
1093
                QFile(tmpname).remove()
 
1094
                if of:
 
1095
                        QFile('out'+defaultext).rename(fileName)
 
1096
        
 
1097
        def getDocumentTitle(self, baseName=False):
 
1098
                text = convertToUnicode(self.editBoxes[self.ind].toPlainText())
 
1099
                markup = self.markups[self.ind]
 
1100
                realTitle = markup.get_document_title(text) if markup else ''
 
1101
                if realTitle and not baseName:
 
1102
                        return realTitle
 
1103
                elif self.fileNames[self.ind]:
 
1104
                        fileinfo = QFileInfo(self.fileNames[self.ind])
 
1105
                        basename = fileinfo.completeBaseName()
 
1106
                        return (basename if basename else fileinfo.fileName())
 
1107
                return self.tr("New document")
 
1108
        
 
1109
        def autoSaveActive(self):
 
1110
                return self.autoSave and self.fileNames[self.ind] and \
 
1111
                QFileInfo(self.fileNames[self.ind]).isWritable()
 
1112
        
 
1113
        def modificationChanged(self, changed):
 
1114
                if self.autoSaveActive():
 
1115
                        changed = False
 
1116
                self.actionSave.setEnabled(changed)
 
1117
                self.setWindowModified(changed)
 
1118
        
 
1119
        def clipboardDataChanged(self):
 
1120
                self.actionPaste.setEnabled(qApp.clipboard().mimeData().hasText())
 
1121
        
 
1122
        def insertChars(self, chars):
 
1123
                tc = self.editBoxes[self.ind].textCursor()
 
1124
                if tc.hasSelection():
 
1125
                        selection = convertToUnicode(tc.selectedText())
 
1126
                        if selection.startswith(chars) and selection.endswith(chars):
 
1127
                                if len(selection) > 2*len(chars):
 
1128
                                        selection = selection[len(chars):-len(chars)]
 
1129
                                        tc.insertText(selection)
 
1130
                        else:
 
1131
                                tc.insertText(chars+tc.selectedText()+chars)
 
1132
                else:
 
1133
                        tc.insertText(chars)
 
1134
        
 
1135
        def insertTag(self, num):
 
1136
                if num:
 
1137
                        ut = self.usefulTags[num-1]
 
1138
                        arg = ' style=""' if ut == 'span' else '' 
 
1139
                        tc = self.editBoxes[self.ind].textCursor()
 
1140
                        toinsert = '<'+ut+arg+'>'+tc.selectedText()+'</'+ut+'>'
 
1141
                        tc.insertText(toinsert)
 
1142
                self.tagsBox.setCurrentIndex(0)
 
1143
        
 
1144
        def insertSymbol(self, num):
 
1145
                if num:
 
1146
                        self.editBoxes[self.ind].insertPlainText('&'+self.usefulChars[num-1]+';')
 
1147
                self.symbolBox.setCurrentIndex(0)
 
1148
        
 
1149
        def maybeSave(self, ind):
 
1150
                if self.autoSaveActive():
 
1151
                        self.saveFileCore(self.fileNames[self.ind])
 
1152
                        return True
 
1153
                if not self.editBoxes[ind].document().isModified():
 
1154
                        return True
 
1155
                self.tabWidget.setCurrentIndex(ind)
 
1156
                ret = QMessageBox.warning(self, app_name,
 
1157
                        self.tr("The document has been modified.\nDo you want to save your changes?"),
 
1158
                        QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
 
1159
                if ret == QMessageBox.Save:
 
1160
                        return self.saveFileMain(False)
 
1161
                elif ret == QMessageBox.Cancel:
 
1162
                        return False
 
1163
                return True
 
1164
        
 
1165
        def closeEvent(self, closeevent):
 
1166
                for self.ind in range(self.tabWidget.count()):
 
1167
                        if not self.maybeSave(self.ind):
 
1168
                                return closeevent.ignore()
 
1169
                if self.restorePreviewState:
 
1170
                        if self.livePreviewEnabled:
 
1171
                                settings.setValue('previewState', True)
 
1172
                        else:
 
1173
                                settings.remove('previewState')
 
1174
                closeevent.accept()
 
1175
        
 
1176
        def viewHtml(self):
 
1177
                HtmlDlg = HtmlDialog(self)
 
1178
                try:
 
1179
                        htmltext = self.getHtml(includeStyleSheet=False, includeTitle=False)
 
1180
                except Exception as e:
 
1181
                        return self.printError(e)
 
1182
                winTitle = self.getDocumentTitle(baseName=True)
 
1183
                try:
 
1184
                        HtmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+") "+QChar(0x2014)+" "+app_name)
 
1185
                except:
 
1186
                        # For Python 3
 
1187
                        HtmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+") \u2014 "+app_name)
 
1188
                HtmlDlg.textEdit.setPlainText(htmltext)
 
1189
                HtmlDlg.show()
 
1190
                HtmlDlg.raise_()
 
1191
                HtmlDlg.activateWindow()
 
1192
        
 
1193
        def openHelp(self):
 
1194
                QDesktopServices.openUrl(QUrl('http://sourceforge.net/p/retext/home/Help and Support'))
 
1195
        
 
1196
        def aboutDialog(self):
 
1197
                QMessageBox.about(self, self.aboutWindowTitle,
 
1198
                '<p><b>'+app_name+' '+app_version+'</b><br>'+self.tr('Simple but powerful editor for Markdown and ReStructuredText')
 
1199
                +'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011')
 
1200
                +'<br><a href="http://sourceforge.net/p/retext/">'+self.tr('Website')
 
1201
                +'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">'+self.tr('Markdown syntax')
 
1202
                +'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">'
 
1203
                +self.tr('ReST syntax')+'</a></p>')
 
1204
        
 
1205
        def enablePlainText(self, value):
 
1206
                self.aptc[self.ind] = value
 
1207
                self.actionPerfectHtml.setDisabled(value)
 
1208
                self.actionViewHtml.setDisabled(value)
 
1209
                self.updatePreviewBox()
 
1210
                self.docTypeChanged()
 
1211
        
 
1212
        def setDefaultMarkup(self, markup):
 
1213
                self.defaultMarkup = markup
 
1214
                settings.setValue('defaultMarkup', markup.name)
 
1215
                oldind = self.ind
 
1216
                for self.ind in range(len(self.previewBoxes)):
 
1217
                        self.docTypeChanged()
 
1218
                self.ind = oldind