2
# -*- coding: utf-8 -*-
3
# vim: sw=8:ts=8:noexpandtab
6
# Copyright 2011-2012 Dmitry Shachnev
8
# This program is free software; you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation; either version 2 of the License, or
11
# (at your option) any later version.
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
# GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License
19
# along with this program; if not, write to the Free Software
20
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25
from subprocess import Popen
26
from PyQt4.QtCore import *
27
from PyQt4.QtGui import *
32
def readFromSettings(settings, key, keytype):
34
return settings.value(key, type=keytype)
36
# For old PyQt versions
38
return settings.value(key).toString()
40
result, ok = settings.value(key).toInt()
42
print('Warning: cannot covert settings value to int!')
45
return settings.value(key).toBool()
47
def readListFromSettings(settings, key):
48
if not settings.contains(key):
50
value = settings.value(key)
52
return value.toStringList()
55
if isinstance(value, str):
60
def writeListToSettings(settings, key, value):
62
settings.setValue(key, value)
64
settings.setValue(key, value[0])
68
settings = QSettings('ReText project', 'ReText')
77
if settings.contains('mdExtensions'):
78
for ext in readListFromSettings(settings, 'mdExtensions'):
80
md = markdown.Markdown(exts)
82
md = markdown.Markdown()
85
import gdata.docs.data
86
import gdata.docs.client
87
from gdata.data import MediaSource
104
from docutils.core import publish_parts
112
PARSER_DOCUTILS, PARSER_MARKDOWN, PARSER_HTML, PARSER_NA = range(4)
114
if QFileInfo("wpgen/wpgen.py").isExecutable():
116
wpgen = unicode(QFileInfo("wpgen/wpgen.py").canonicalFilePath(), 'utf-8')
119
wpgen = QFileInfo("wpgen/wpgen.py").canonicalFilePath()
120
elif QFileInfo("/usr/bin/wpgen").isExecutable():
121
wpgen = "/usr/bin/wpgen"
126
if settings.contains('editorFont'):
127
monofont.setFamily(readFromSettings(settings, 'editorFont', str))
129
monofont.setFamily('monospace')
130
if settings.contains('editorFontSize'):
131
monofont.setPointSize(readFromSettings(settings, 'editorFontSize', int))
134
from PyQt4.QtWebKit import QWebView, QWebSettings
136
webkit_available = False
138
webkit_available = True
140
class ReTextHighlighter(QSyntaxHighlighter):
141
def highlightBlock(self, text):
143
('<[^<>]*>', Qt.darkMagenta, QFont.Bold), # HTML tags
144
('&[^; ]*;', Qt.darkCyan, QFont.Bold), # HTML symbols
145
('"[^"<]*"(?=[^<]*>)', Qt.darkYellow, QFont.Bold), # Quoted strings inside tags
146
('<!--[^<>]*-->', Qt.gray, QFont.Normal), # HTML comments
147
('(?<!\\*)\\*[^ \\*][^\\*]*\\*', None, QFont.Normal, True), # *Italics*
148
('(?<!_|\\w)_[^_]+_(?!\\w)', None, QFont.Normal, True), # _Italics_
149
('(?<!\\*)\\*\\*((?!\\*\\*).)*\\*\\*', None, QFont.Bold), # **Bold**
150
('(?<!_|\\w)__[^_]+__(?!\\w)', None, QFont.Bold), # __Bold__
151
('\\*{3,3}[^\\*]+\\*{3,3}', None, QFont.Bold, True), # ***BoldItalics***
152
('___[^_]+___', None, QFont.Bold, True), # ___BoldItalics___
153
('^#.+', None, QFont.Black), # Headers
154
('(?<=\\[)[^\\[\\]]*(?=\\])', Qt.blue, QFont.Normal), # Links and images
155
('(?<=\\]\\()[^\\(\\)]*(?=\\))', None, QFont.Normal, True, True) # Link references
157
for pattern in patterns:
158
charFormat = QTextCharFormat()
159
charFormat.setFontWeight(pattern[2])
160
if pattern[1] != None:
161
charFormat.setForeground(pattern[1])
162
if len(pattern) >= 4:
163
charFormat.setFontItalic(pattern[3])
164
if len(pattern) >= 5:
165
charFormat.setFontUnderline(pattern[4])
166
for match in re.finditer(pattern[0], text):
167
self.setFormat(match.start(), match.end() - match.start(), charFormat)
172
# Not necessary for Python 3
174
charFormat = QTextCharFormat()
175
charFormat.setUnderlineColor(Qt.red)
176
charFormat.setUnderlineStyle(QTextCharFormat.SpellCheckUnderline)
177
for match in re.finditer('[^_\\W]+', text, flags=re.UNICODE):
178
finalFormat = QTextCharFormat()
179
finalFormat.merge(charFormat)
180
finalFormat.merge(self.format(match.start()))
181
if not dictionary.check(match.group(0)):
182
self.setFormat(match.start(), match.end() - match.start(), finalFormat)
184
class LogPassDialog(QDialog):
185
def __init__(self, defaultLogin="", defaultPass=""):
186
QDialog.__init__(self)
187
self.setWindowTitle(app_name)
188
self.verticalLayout = QVBoxLayout(self)
189
self.label = QLabel(self)
190
self.label.setText(self.tr("Enter your Google account data"))
191
self.verticalLayout.addWidget(self.label)
192
self.loginEdit = QLineEdit(self)
193
self.loginEdit.setText(defaultLogin)
194
self.verticalLayout.addWidget(self.loginEdit)
195
self.passEdit = QLineEdit(self)
196
self.passEdit.setText(defaultPass)
197
self.passEdit.setEchoMode(QLineEdit.Password)
199
self.loginEdit.setPlaceholderText(self.tr("Username"))
200
self.passEdit.setPlaceholderText(self.tr("Password"))
203
self.verticalLayout.addWidget(self.passEdit)
204
self.buttonBox = QDialogButtonBox(self)
205
self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
206
self.verticalLayout.addWidget(self.buttonBox)
207
self.connect(self.buttonBox, SIGNAL("accepted()"), self.accept)
208
self.connect(self.buttonBox, SIGNAL("rejected()"), self.reject)
210
class HtmlDialog(QDialog):
211
def __init__(self, parent=None):
212
QDialog.__init__(self, parent)
213
self.resize(600, 500)
214
self.verticalLayout = QVBoxLayout(self)
215
self.textEdit = QTextEdit(self)
216
self.textEdit.setReadOnly(True)
217
self.textEdit.setFont(monofont)
218
ReTextHighlighter(self.textEdit.document())
219
self.verticalLayout.addWidget(self.textEdit)
220
self.buttonBox = QDialogButtonBox(self)
221
self.buttonBox.setStandardButtons(QDialogButtonBox.Close)
222
self.connect(self.buttonBox, SIGNAL("clicked(QAbstractButton*)"), self.doClose)
223
self.verticalLayout.addWidget(self.buttonBox)
228
class ReTextWindow(QMainWindow):
229
def __init__(self, parent=None):
230
QMainWindow.__init__(self, parent)
231
self.resize(800, 600)
232
screen = QDesktopWidget().screenGeometry()
233
size = self.geometry()
234
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
235
if settings.contains('iconTheme'):
236
QIcon.setThemeName(readFromSettings(settings, 'iconTheme', str))
237
if settings.contains('font'):
238
self.font = QFont(readFromSettings(settings, 'font', str))
239
if settings.contains('fontSize'):
240
self.font.setPointSize(readFromSettings(settings, 'fontSize', int))
243
if QFile.exists(icon_path+'retext.png'):
244
self.setWindowIcon(QIcon(icon_path+'retext.png'))
246
self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor')))
248
self.previewBoxes = []
249
self.highlighters = []
254
self.gDocsEntries = []
255
self.tabWidget = QTabWidget(self)
256
self.tabWidget.setTabsClosable(True)
257
self.setCentralWidget(self.tabWidget)
258
self.connect(self.tabWidget, SIGNAL('currentChanged(int)'), self.changeIndex)
259
self.connect(self.tabWidget, SIGNAL('tabCloseRequested(int)'), self.closeTab)
260
self.toolBar = QToolBar(self.tr('File toolbar'), self)
261
self.addToolBar(Qt.TopToolBarArea, self.toolBar)
262
self.editBar = QToolBar(self.tr('Edit toolbar'), self)
263
self.addToolBar(Qt.TopToolBarArea, self.editBar)
264
self.searchBar = QToolBar(self.tr('Search toolbar'), self)
265
self.addToolBar(Qt.BottomToolBarArea, self.searchBar)
266
self.actionNew = self.act(self.tr('New'), icon='document-new', shct=QKeySequence.New, trig=self.createNew)
267
self.actionNew.setPriority(QAction.LowPriority)
268
self.actionOpen = self.act(self.tr('Open'), icon='document-open', shct=QKeySequence.Open, trig=self.openFile)
269
self.actionOpen.setPriority(QAction.LowPriority)
270
self.actionSave = self.act(self.tr('Save'), icon='document-save', shct=QKeySequence.Save, trig=self.saveFile)
271
self.actionSave.setEnabled(False)
272
self.actionSave.setPriority(QAction.LowPriority)
273
self.actionSaveAs = self.act(self.tr('Save as'), icon='document-save-as', shct=QKeySequence.SaveAs, \
274
trig=self.saveFileAs)
275
self.actionPrint = self.act(self.tr('Print'), icon='document-print', shct=QKeySequence.Print, trig=self.printFile)
276
self.actionPrint.setPriority(QAction.LowPriority)
277
self.actionPrintPreview = self.act(self.tr('Print preview'), icon='document-print-preview', \
278
trig=self.printPreview)
279
self.actionViewHtml = self.act(self.tr('View HTML code'), icon='text-html', trig=self.viewHtml)
280
self.actionChangeFont = self.act(self.tr('Change default font'), trig=self.changeFont)
281
self.actionSearch = self.act(self.tr('Find text'), icon='edit-find', shct=QKeySequence.Find)
282
self.actionSearch.setCheckable(True)
283
self.connect(self.actionSearch, SIGNAL('triggered(bool)'), self.searchBar, SLOT('setVisible(bool)'))
284
self.connect(self.searchBar, SIGNAL('visibilityChanged(bool)'), self.searchBarVisibilityChanged)
285
self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview)
286
if QIcon.hasThemeIcon('document-preview'):
287
self.actionPreview.setIcon(QIcon.fromTheme('document-preview'))
288
elif QIcon.hasThemeIcon('preview-file'):
289
self.actionPreview.setIcon(QIcon.fromTheme('preview-file'))
290
elif QIcon.hasThemeIcon('x-office-document'):
291
self.actionPreview.setIcon(QIcon.fromTheme('x-office-document'))
293
self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png'))
294
self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.SHIFT+Qt.Key_E, \
295
trigbool=self.enableLivePreview)
296
self.actionFullScreen = self.act(self.tr('Fullscreen mode'), icon='view-fullscreen', shct=Qt.Key_F11, \
297
trigbool=self.enableFullScreen)
298
self.actionPerfectHtml = self.act('HTML', icon='text-html', trig=self.saveFilePerfect)
299
self.actionPdf = self.act('PDF', icon='application-pdf', trig=self.savePdf)
300
self.actionOdf = self.act('ODT', icon='x-office-document', trig=self.saveOdf)
301
settings.beginGroup('Export')
302
if settings.allKeys():
303
self.actionOtherExport = self.act(self.tr('Other formats'), trig=self.otherExport)
308
self.getExportExtensionsList()
309
self.actionQuit = self.act(self.tr('Quit'), icon='application-exit', shct=QKeySequence.Quit)
310
self.actionQuit.setMenuRole(QAction.QuitRole)
311
self.connect(self.actionQuit, SIGNAL('triggered()'), qApp, SLOT('quit()'))
312
self.actionUndo = self.act(self.tr('Undo'), icon='edit-undo', shct=QKeySequence.Undo, \
313
trig=lambda: self.editBoxes[self.ind].undo())
314
self.actionRedo = self.act(self.tr('Redo'), icon='edit-redo', shct=QKeySequence.Redo, \
315
trig=lambda: self.editBoxes[self.ind].redo())
316
self.actionCopy = self.act(self.tr('Copy'), icon='edit-copy', shct=QKeySequence.Copy, \
317
trig=lambda: self.editBoxes[self.ind].copy())
318
self.actionCut = self.act(self.tr('Cut'), icon='edit-cut', shct=QKeySequence.Cut, \
319
trig=lambda: self.editBoxes[self.ind].cut())
320
self.actionPaste = self.act(self.tr('Paste'), icon='edit-paste', shct=QKeySequence.Paste, \
321
trig=lambda: self.editBoxes[self.ind].paste())
322
self.actionUndo.setEnabled(False)
323
self.actionRedo.setEnabled(False)
324
self.actionCopy.setEnabled(False)
325
self.actionCut.setEnabled(False)
326
self.connect(qApp.clipboard(), SIGNAL('dataChanged()'), self.clipboardDataChanged)
327
self.clipboardDataChanged()
330
self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSC)
331
self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale)
332
if settings.contains('spellCheckLocale'):
333
self.sl = str(readFromSettings(settings, 'spellCheckLocale', str))
336
if settings.contains('spellCheck'):
337
if readFromSettings(settings, 'spellCheck', bool):
338
self.actionEnableSC.setChecked(True)
340
self.actionPlainText = self.act(self.tr('Plain text'), trigbool=self.enablePlainText)
342
self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit)
343
self.useWebKit = False
344
if settings.contains('useWebKit'):
345
if readFromSettings(settings, 'useWebKit', bool):
346
self.useWebKit = True
347
self.actionWebKit.setChecked(True)
349
self.actionWpgen = self.act(self.tr('Generate webpages'), trig=self.startWpgen)
350
self.actionShow = self.act(self.tr('Show'), icon='system-file-manager', trig=self.showInDir)
351
self.actionFind = self.act(self.tr('Next'), icon='go-next', shct=QKeySequence.FindNext, trig=self.find)
352
self.actionFindPrev = self.act(self.tr('Previous'), icon='go-previous', shct=QKeySequence.FindPrevious, \
353
trig=lambda: self.find(back=True))
354
self.actionHelp = self.act(self.tr('Get help online'), icon='help-contents', trig=self.openHelp)
356
self.aboutWindowTitle = self.tr('About %s') % app_name
359
self.aboutWindowTitle = self.tr('About %s').replace('%s', '%1').arg(app_name)
360
self.actionAbout = self.act(self.aboutWindowTitle, icon='help-about', trig=self.aboutDialog)
361
self.actionAbout.setMenuRole(QAction.AboutRole)
362
self.actionAboutQt = self.act(self.tr('About Qt'))
363
self.actionAboutQt.setMenuRole(QAction.AboutQtRole)
364
self.connect(self.actionAboutQt, SIGNAL('triggered()'), qApp, SLOT('aboutQt()'))
365
self.chooseGroup = QActionGroup(self)
366
self.useDocUtils = False
367
self.actionUseMarkdown = self.act('Markdown')
368
self.actionUseMarkdown.setCheckable(True)
369
self.actionUseReST = self.act('ReStructuredText')
370
self.actionUseReST.setCheckable(True)
371
if settings.contains('useReST'):
372
if readFromSettings(settings, 'useReST', bool):
374
self.useDocUtils = True
375
self.actionUseReST.setChecked(True)
377
self.actionUseMarkdown.setChecked(True)
379
self.actionUseMarkdown.setChecked(True)
380
self.connect(self.actionUseReST, SIGNAL('toggled(bool)'), self.setDocUtilsDefault)
381
self.chooseGroup.addAction(self.actionUseMarkdown)
382
self.chooseGroup.addAction(self.actionUseReST)
383
self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertChars('**'))
384
self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertChars('*'))
385
self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline, \
386
trig=lambda: self.insertTag(9)) # <u>...</u>
388
self.actionSaveGDocs = self.act(self.tr('Save to Google Docs'), trig=self.saveGDocs)
389
self.actionSaveGDocs.setIcon(QIcon.fromTheme('web-browser', self.actIcon('intenret-web-browser')))
390
self.usefulTags = ('big', 'center', 's', 'small', 'span', 'table', 'td', 'tr', 'u')
391
self.usefulChars = ('deg', 'divide', 'hellip', 'laquo', 'larr', \
392
'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo', \
393
'rarr', 'rsquo', 'times')
394
self.tagsBox = QComboBox(self.editBar)
395
self.tagsBox.addItem(self.tr('Tags'))
396
self.tagsBox.addItems(self.usefulTags)
397
self.connect(self.tagsBox, SIGNAL('activated(int)'), self.insertTag)
398
self.symbolBox = QComboBox(self.editBar)
399
self.symbolBox.addItem(self.tr('Symbols'))
400
self.symbolBox.addItems(self.usefulChars)
401
self.connect(self.symbolBox, SIGNAL('activated(int)'), self.insertSymbol)
402
if settings.contains('styleSheet'):
403
ssname = readFromSettings(settings, 'styleSheet', str)
404
sheetfile = QFile(ssname)
405
sheetfile.open(QIODevice.ReadOnly)
406
self.ss = QTextStream(sheetfile).readAll()
408
webkitsettings = QWebSettings.globalSettings()
409
webkitsettings.setUserStyleSheetUrl(QUrl.fromLocalFile(ssname))
412
if use_md and 'codehilite' in exts:
413
# Load CSS style for codehilite
415
from pygments.formatters import HtmlFormatter
419
self.ss += HtmlFormatter().get_style_defs('.codehilite')
420
self.menubar = QMenuBar(self)
421
self.menubar.setGeometry(QRect(0, 0, 800, 25))
422
self.setMenuBar(self.menubar)
423
self.menuFile = self.menubar.addMenu(self.tr('File'))
424
self.menuEdit = self.menubar.addMenu(self.tr('Edit'))
425
self.menuHelp = self.menubar.addMenu(self.tr('Help'))
426
self.menuFile.addAction(self.actionNew)
427
self.menuFile.addAction(self.actionOpen)
428
self.menuRecentFiles = self.menuFile.addMenu(self.tr('Open recent'))
429
self.connect(self.menuRecentFiles, SIGNAL('aboutToShow()'), self.updateRecentFiles)
430
self.menuFile.addMenu(self.menuRecentFiles)
431
self.menuDir = self.menuFile.addMenu(self.tr('Directory'))
432
self.menuDir.addAction(self.actionShow)
434
self.menuDir.addAction(self.actionWpgen)
435
self.menuFile.addSeparator()
436
self.menuFile.addAction(self.actionSave)
437
self.menuFile.addAction(self.actionSaveAs)
438
self.menuFile.addSeparator()
439
self.menuExport = self.menuFile.addMenu(self.tr('Export'))
440
self.menuExport.addAction(self.actionPerfectHtml)
441
self.menuExport.addAction(self.actionOdf)
442
self.menuExport.addAction(self.actionPdf)
443
if self.extensionActions:
444
self.menuExport.addSeparator()
445
for action, mimetype in self.extensionActions:
446
self.menuExport.addAction(action)
447
self.connect(self.menuRecentFiles, SIGNAL('aboutToShow()'), self.updateExtensionsVisibility)
449
self.menuExport.addAction(self.actionOtherExport)
451
self.menuExport.addSeparator()
452
self.menuExport.addAction(self.actionSaveGDocs)
453
self.menuFile.addAction(self.actionPrint)
454
self.menuFile.addAction(self.actionPrintPreview)
455
self.menuFile.addSeparator()
456
self.menuFile.addAction(self.actionQuit)
457
self.menuEdit.addAction(self.actionUndo)
458
self.menuEdit.addAction(self.actionRedo)
459
self.menuEdit.addSeparator()
460
self.menuEdit.addAction(self.actionCut)
461
self.menuEdit.addAction(self.actionCopy)
462
self.menuEdit.addAction(self.actionPaste)
463
self.menuEdit.addSeparator()
465
self.menuSC = self.menuEdit.addMenu(self.tr('Spell check'))
466
self.menuSC.addAction(self.actionEnableSC)
467
self.menuSC.addAction(self.actionSetLocale)
468
self.menuEdit.addAction(self.actionSearch)
469
self.menuEdit.addAction(self.actionPlainText)
470
self.menuEdit.addAction(self.actionChangeFont)
471
self.menuEdit.addSeparator()
472
if use_docutils and use_md:
473
self.menuMode = self.menuEdit.addMenu(self.tr('Default editing mode'))
474
self.menuMode.addAction(self.actionUseMarkdown)
475
self.menuMode.addAction(self.actionUseReST)
476
self.menuFormat = self.menuEdit.addMenu(self.tr('Formatting'))
477
self.menuFormat.addAction(self.actionBold)
478
self.menuFormat.addAction(self.actionItalic)
479
self.menuFormat.addAction(self.actionUnderline)
481
self.menuEdit.addAction(self.actionWebKit)
482
self.menuEdit.addSeparator()
483
self.menuEdit.addAction(self.actionViewHtml)
484
self.menuEdit.addAction(self.actionLivePreview)
485
self.menuEdit.addAction(self.actionPreview)
486
self.menuEdit.addSeparator()
487
self.menuEdit.addAction(self.actionFullScreen)
488
self.menuHelp.addAction(self.actionHelp)
489
self.menuHelp.addSeparator()
490
self.menuHelp.addAction(self.actionAbout)
491
self.menuHelp.addAction(self.actionAboutQt)
492
self.menubar.addMenu(self.menuFile)
493
self.menubar.addMenu(self.menuEdit)
494
self.menubar.addMenu(self.menuHelp)
495
self.toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
496
self.toolBar.addAction(self.actionNew)
497
self.toolBar.addSeparator()
498
self.toolBar.addAction(self.actionOpen)
499
self.toolBar.addAction(self.actionSave)
500
self.toolBar.addAction(self.actionPrint)
501
self.toolBar.addSeparator()
502
self.toolBar.addAction(self.actionPreview)
503
self.editBar.addAction(self.actionUndo)
504
self.editBar.addAction(self.actionRedo)
505
self.editBar.addSeparator()
506
self.editBar.addAction(self.actionCut)
507
self.editBar.addAction(self.actionCopy)
508
self.editBar.addAction(self.actionPaste)
509
self.editBar.addSeparator()
510
self.editBar.addWidget(self.tagsBox)
511
self.editBar.addWidget(self.symbolBox)
512
self.searchEdit = QLineEdit(self.searchBar)
514
self.searchEdit.setPlaceholderText(self.tr('Search'))
517
self.connect(self.searchEdit, SIGNAL('returnPressed()'), self.find)
518
self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar)
519
self.searchBar.addWidget(self.searchEdit)
520
self.searchBar.addWidget(self.csBox)
521
self.searchBar.addAction(self.actionFindPrev)
522
self.searchBar.addAction(self.actionFind)
523
self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
524
self.searchBar.setVisible(False)
525
self.autoSave = False
526
if settings.contains('autoSave'):
527
if readFromSettings(settings, 'autoSave', bool):
531
self.connect(timer, SIGNAL('timeout()'), self.saveAll)
532
self.restorePreviewState = False
533
self.livePreviewEnabled = False
534
if settings.contains('restorePreviewState'):
535
self.restorePreviewState = readFromSettings(settings, 'restorePreviewState', bool)
536
if settings.contains('previewState'):
537
self.livePreviewEnabled = readFromSettings(settings, 'previewState', bool)
539
self.tabWidget.addTab(self.createTab(""), self.tr('New document'))
540
if not (use_md or use_docutils):
541
QMessageBox.warning(self, app_name, self.tr('You have neither Markdown nor Docutils modules installed!') \
542
+'<br>'+self.tr('Only HTML formatting will be available.'))
544
def act(self, name, icon=None, trig=None, trigbool=None, shct=None):
546
action = QAction(self.actIcon(icon), name, self)
548
action = QAction(name, self)
550
self.connect(action, SIGNAL('triggered()'), trig)
552
action.setCheckable(True)
553
self.connect(action, SIGNAL('triggered(bool)'), trigbool)
555
action.setShortcut(shct)
558
def actIcon(self, name):
559
return QIcon.fromTheme(name, QIcon(icon_path+name+'.png'))
561
def printError(self, error):
562
print('Exception occured while parsing document:')
565
def getSplitter(self, index):
566
splitter = QSplitter(Qt.Horizontal)
567
# Give both boxes a minimum size so the minimumSizeHint will be
568
# ignored when splitter.setSizes is called below
569
for widget in self.editBoxes[index], self.previewBoxes[index]:
570
widget.setMinimumWidth(125)
571
splitter.addWidget(widget)
572
splitter.setSizes((50, 50))
573
splitter.setChildrenCollapsible(False)
576
def createTab(self, fileName):
577
self.previewBlocked = False
578
self.editBoxes.append(QTextEdit())
579
self.highlighters.append(ReTextHighlighter(self.editBoxes[-1].document()))
581
self.previewBoxes.append(QWebView())
583
self.previewBoxes.append(QTextEdit())
584
self.previewBoxes[-1].setReadOnly(True)
585
self.previewBoxes[-1].setVisible(False)
586
self.fileNames.append(fileName)
587
liveMode = self.restorePreviewState and self.livePreviewEnabled
588
self.apc.append(liveMode)
589
self.alpc.append(liveMode)
590
self.aptc.append(False)
591
self.gDocsEntries.append(None)
592
self.editBoxes[-1].setFont(monofont)
593
self.editBoxes[-1].setAcceptRichText(False)
594
self.connect(self.editBoxes[-1], SIGNAL('textChanged()'), self.updateLivePreviewBox)
595
self.connect(self.editBoxes[-1], SIGNAL('undoAvailable(bool)'), self.actionUndo, SLOT('setEnabled(bool)'))
596
self.connect(self.editBoxes[-1], SIGNAL('redoAvailable(bool)'), self.actionRedo, SLOT('setEnabled(bool)'))
597
self.connect(self.editBoxes[-1], SIGNAL('copyAvailable(bool)'), self.enableCopy)
598
self.connect(self.editBoxes[-1].document(), SIGNAL('modificationChanged(bool)'), self.modificationChanged)
599
return self.getSplitter(-1)
601
def closeTab(self, ind):
602
if self.maybeSave(ind):
603
if self.tabWidget.count() == 1:
604
self.tabWidget.addTab(self.createTab(""), self.tr("New document"))
605
del self.editBoxes[ind]
606
del self.previewBoxes[ind]
607
del self.highlighters[ind]
608
del self.fileNames[ind]
612
del self.gDocsEntries[ind]
613
self.tabWidget.removeTab(ind)
615
def changeIndex(self, ind):
617
self.actionPlainText.setChecked(self.aptc[ind])
618
self.enablePlainTextMain(self.aptc[ind])
619
self.actionUndo.setEnabled(self.editBoxes[ind].document().isUndoAvailable())
620
self.actionRedo.setEnabled(self.editBoxes[ind].document().isRedoAvailable())
621
self.actionCopy.setEnabled(self.editBoxes[ind].textCursor().hasSelection())
622
self.actionCut.setEnabled(self.editBoxes[ind].textCursor().hasSelection())
623
self.actionPreview.setChecked(self.apc[ind])
624
self.actionLivePreview.setChecked(self.alpc[ind])
625
self.editBar.setDisabled(self.apc[ind])
627
if self.fileNames[ind]:
628
self.setCurrentFile()
631
self.setWindowTitle(self.tr('New document') + '[*] ' + QChar(0x2014) + ' ' + app_name)
634
self.setWindowTitle(self.tr('New document') + '[*] \u2014 ' + app_name)
635
self.modificationChanged(self.editBoxes[ind].document().isModified())
636
self.livePreviewEnabled = self.alpc[ind]
638
self.enableLivePreview(True)
639
self.editBoxes[self.ind].setFocus(Qt.OtherFocusReason)
641
def changeFont(self):
644
fd = QFontDialog.getFont(self.font, self)
647
self.font.setFamily(fd[0].family())
648
settings.setValue('font', fd[0].family())
649
self.font.setPointSize(fd[0].pointSize())
650
settings.setValue('fontSize', fd[0].pointSize())
651
self.updatePreviewBox()
653
def preview(self, viewmode):
654
self.apc[self.ind] = viewmode
655
if self.actionLivePreview.isChecked():
656
self.actionLivePreview.setChecked(False)
657
return self.enableLivePreview(False)
658
self.editBar.setDisabled(viewmode)
659
self.editBoxes[self.ind].setVisible(not viewmode)
660
self.previewBoxes[self.ind].setVisible(viewmode)
662
self.updatePreviewBox()
664
def enableLivePreview(self, livemode):
665
self.livePreviewEnabled = livemode
666
self.alpc[self.ind] = livemode
667
self.apc[self.ind] = livemode
668
self.actionPreview.setChecked(livemode)
669
self.editBar.setEnabled(True)
670
self.previewBoxes[self.ind].setVisible(livemode)
671
self.editBoxes[self.ind].setVisible(True)
673
self.updatePreviewBox()
675
def enableWebKit(self, enable):
676
self.useWebKit = enable
678
settings.setValue('useWebKit', True)
680
settings.remove('useWebKit')
682
self.tabWidget.clear()
683
for self.ind in range(len(self.editBoxes)):
685
self.previewBoxes[self.ind] = QWebView()
687
self.previewBoxes[self.ind] = QTextEdit()
688
self.previewBoxes[self.ind].setReadOnly(True)
689
splitter = self.getSplitter(self.ind)
690
self.tabWidget.addTab(splitter, self.getDocumentTitle(baseName=True))
691
self.updatePreviewBox()
692
self.previewBoxes[self.ind].setVisible(self.apc[self.ind])
694
self.tabWidget.setCurrentIndex(self.ind)
696
def enableCopy(self, copymode):
697
self.actionCopy.setEnabled(copymode)
698
self.actionCut.setEnabled(copymode)
700
def enableFullScreen(self, yes):
702
self.showFullScreen()
706
def keyPressEvent(self, e):
707
v = not self.menubar.isVisible()
708
if e.key() == Qt.Key_F12 and e.modifiers() & Qt.ShiftModifier:
709
self.menubar.setVisible(v)
710
self.toolBar.setVisible(v)
711
self.editBar.setVisible(v)
712
elif e.key() == Qt.Key_F11:
714
n = not self.actionFullScreen.isChecked()
715
self.actionFullScreen.setChecked(n)
716
self.enableFullScreen(n)
718
def enableSC(self, yes):
723
dictionary = enchant.Dict(self.sl)
724
except Exception as e:
725
QMessageBox.warning(self, app_name, str(e))
726
dictionary = enchant.Dict()
728
dictionary = enchant.Dict()
729
settings.setValue('spellCheck', True)
732
settings.remove('spellCheck')
733
for highlighter in self.highlighters:
734
highlighter.rehighlight()
736
def changeLocale(self):
741
sl, ok = QInputDialog.getText(self, app_name, self.tr('Enter locale name (example: en_US)'), QLineEdit.Normal, text)
749
self.enableSC(self.actionEnableSC.isChecked())
752
self.enableSC(self.actionEnableSC.isChecked())
754
def searchBarVisibilityChanged(self, visible):
755
self.actionSearch.setChecked(visible)
757
self.searchEdit.setFocus(Qt.ShortcutFocusReason)
759
def find(self, back=False):
762
flags = QTextDocument.FindBackward
763
if self.csBox.isChecked():
764
flags = flags | QTextDocument.FindCaseSensitively
765
text = self.searchEdit.text()
766
if not self.findMain(text, flags):
767
if text in self.editBoxes[self.ind].toPlainText():
768
cursor = self.editBoxes[self.ind].textCursor()
770
cursor.movePosition(QTextCursor.End)
772
cursor.movePosition(QTextCursor.Start)
773
self.editBoxes[self.ind].setTextCursor(cursor)
774
self.findMain(text, flags)
776
def findMain(self, text, flags):
778
return self.editBoxes[self.ind].find(text, flags)
780
return self.editBoxes[self.ind].find(text)
782
def updatePreviewBox(self):
783
self.previewBlocked = False
784
pb = self.previewBoxes[self.ind]
785
textedit = isinstance(pb, QTextEdit)
786
if self.ss and textedit:
787
pb.document().setDefaultStyleSheet(self.ss)
788
if self.actionPlainText.isChecked():
790
pb.setPlainText(self.editBoxes[self.ind].toPlainText())
793
td.setPlainText(self.editBoxes[self.ind].toPlainText())
794
pb.setHtml(td.toHtml())
797
pb.setHtml(self.parseText())
798
except Exception as e:
800
if self.font and textedit:
801
pb.document().setDefaultFont(self.font)
803
def updateLivePreviewBox(self):
804
if self.actionLivePreview.isChecked() and self.previewBlocked == False:
805
self.previewBlocked = True
806
QTimer.singleShot(1000, self.updatePreviewBox)
808
def startWpgen(self):
809
if self.fileNames[self.ind] == "":
810
QMessageBox.warning(self, app_name, self.tr("Please, save the file somewhere."))
812
if not (QDir("html").exists() and QFile.exists("template.html")):
813
Popen((wpgen, 'init')).wait()
814
Popen([wpgen, 'updateall']).wait()
815
msgBox = QMessageBox(QMessageBox.Information, app_name, \
816
self.tr("Webpages saved in <code>html</code> directory."), QMessageBox.Ok)
817
showButton = msgBox.addButton(self.tr("Show directory"), QMessageBox.AcceptRole)
819
if msgBox.clickedButton() == showButton:
820
QDesktopServices.openUrl(QUrl.fromLocalFile(QDir('html').absolutePath()))
822
QMessageBox.error(self, app_name, self.tr("Webpages generator is not installed!"))
825
if self.fileNames[self.ind]:
826
QDesktopServices.openUrl(QUrl.fromLocalFile(QFileInfo(self.fileNames[self.ind]).path()))
828
QMessageBox.warning(self, app_name, self.tr("Please, save the file somewhere."))
830
def setCurrentFile(self):
831
self.setWindowTitle("")
832
self.tabWidget.setTabText(self.ind, self.getDocumentTitle(baseName=True))
833
self.setWindowFilePath(self.fileNames[self.ind])
834
files = readListFromSettings(settings, "recentFileList")
836
files.prepend(self.fileNames[self.ind])
837
files.removeDuplicates()
840
while self.fileNames[self.ind] in files:
841
files.remove(self.fileNames[self.ind])
842
files.insert(0, self.fileNames[self.ind])
845
writeListToSettings(settings, "recentFileList", files)
846
QDir.setCurrent(QFileInfo(self.fileNames[self.ind]).dir().path())
849
self.tabWidget.addTab(self.createTab(""), self.tr("New document"))
850
self.ind = self.tabWidget.count()-1
851
self.tabWidget.setCurrentIndex(self.ind)
853
def updateRecentFiles(self):
854
self.menuRecentFiles.clear()
855
self.recentFilesActions = []
856
filesOld = readListFromSettings(settings, "recentFileList")
861
self.recentFilesActions.append(self.act(f, trig=self.openFunction(f)))
862
writeListToSettings(settings, "recentFileList", files)
863
for action in self.recentFilesActions:
864
self.menuRecentFiles.addAction(action)
866
def openFunction(self, fileName):
867
return lambda: self.openFileWrapper(fileName)
869
def extensionFuntion(self, data):
871
self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension'])
873
def getExportExtensionsList(self):
875
for extsprefix in ('/usr', QDir.homePath()+'/.local'):
876
extsdir = QDir(extsprefix+'/share/retext/export-extensions/')
878
for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable):
879
extensions.append(self.readExtension(fileInfo.filePath()))
880
locale = QLocale.system().name()
881
self.extensionActions = []
882
for extension in extensions:
884
if ('Name[%s]' % locale) in extension:
885
name = extension['Name[%s]' % locale]
886
elif ('Name[%s]' % locale.split('_')[0]) in extension:
887
name = extension['Name[%s]' % locale.split('_')[0]]
889
name = extension['Name']
891
for prop in ('FileFilter', 'DefaultExtension', 'Exec'):
892
if 'X-ReText-'+prop in extension:
893
data[prop] = extension['X-ReText-'+prop]
894
elif prop in extension:
895
data[prop] = extension[prop]
898
action = self.act(name, trig=self.extensionFuntion(data))
899
if 'Icon' in extension:
900
action.setIcon(self.actIcon(extension['Icon']))
901
mimetype = extension['MimeType'] if 'MimeType' in extension else None
903
print('Failed to parse extension: Name is required')
905
self.extensionActions.append((action, mimetype))
907
def updateExtensionsVisibility(self):
908
for action in self.extensionActions:
909
if self.actionPlainText.isChecked():
910
action[0].setEnabled(False)
915
elif self.getParser() == PARSER_MARKDOWN:
916
enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown"))
917
elif self.getParser() == PARSER_DOCUTILS:
918
enabled = (mimetype in ("text/x-retext-rst", "text/x-rst"))
919
elif self.getParser() == PARSER_HTML:
920
enabled = (mimetype == "text/html")
923
action[0].setEnabled(enabled)
925
def readExtension(self, fileName):
926
extFile = QFile(fileName)
927
extFile.open(QIODevice.ReadOnly)
929
stream = QTextStream(extFile)
930
while not stream.atEnd():
931
line = stream.readLine()
935
# Not needed for Python 3
938
index = line.index('=')
939
extension[line[:index].rstrip()] = line[index+1:].lstrip()
944
fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), "", \
945
self.tr("Supported files")+" (*.re *.md *.markdown *.mdown *.mkd *.mkdn *.rst *.rest *.txt *.html *.htm);;"+self.tr("All files (*)"))
946
for fileName in fileNames:
947
self.openFileWrapper(fileName)
949
def openFileWrapper(self, fileName):
952
fileName = QFileInfo(fileName).canonicalFilePath()
954
for i in range(self.tabWidget.count()):
955
if self.fileNames[i] == fileName:
959
self.tabWidget.setCurrentIndex(ex)
961
if self.fileNames[self.ind] or self.editBoxes[self.ind].toPlainText() \
962
or self.editBoxes[self.ind].document().isModified():
963
self.tabWidget.addTab(self.createTab(""), "")
964
self.ind = self.tabWidget.count()-1
965
self.tabWidget.setCurrentIndex(self.ind)
966
self.fileNames[self.ind] = fileName
969
def openFileMain(self):
970
if QFile.exists(self.fileNames[self.ind]):
971
openfile = QFile(self.fileNames[self.ind])
972
openfile.open(QIODevice.ReadOnly)
973
html = QTextStream(openfile).readAll()
975
self.editBoxes[self.ind].setPlainText(html)
976
suffix = QFileInfo(self.fileNames[self.ind]).suffix()
977
pt = suffix not in ('re', 'md', 'markdown', 'mdown', 'mkd', 'mkdn', 'rst', 'rest', 'html', 'htm')
978
if settings.contains('autoPlainText'):
979
if not readFromSettings(settings, 'autoPlainText', bool):
981
self.actionPlainText.setChecked(pt)
982
self.enablePlainText(pt)
983
self.setCurrentFile()
984
self.setWindowModified(False)
987
self.saveFileMain(dlg=False)
989
def saveFileAs(self):
990
self.saveFileMain(dlg=True)
994
for self.ind in range(self.tabWidget.count()):
995
if self.fileNames[self.ind] and QFileInfo(self.fileNames[self.ind]).isWritable():
996
self.saveFileWrapper(self.fileNames[self.ind])
997
self.editBoxes[self.ind].document().setModified(False)
1000
def saveFileMain(self, dlg):
1001
if (not self.fileNames[self.ind]) or dlg:
1002
if self.actionPlainText.isChecked():
1003
defaultExt = self.tr("Plain text (*.txt)")
1005
elif self.getParser() == PARSER_DOCUTILS:
1006
defaultExt = self.tr("ReStructuredText files")+" (*.rest *.rst *.txt)"
1008
elif self.getParser() == PARSER_HTML:
1009
defaultExt = self.tr("HTML files")+" (*.html *.htm)"
1012
defaultExt = self.tr("Markdown files")+" (*.re *.md *.markdown *.mdown *.mkd *.mkdn *.txt)"
1014
if settings.contains('defaultExt'):
1015
ext = readFromSettings(settings, 'defaultExt', str)
1016
self.fileNames[self.ind] = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", defaultExt)
1017
if self.fileNames[self.ind] and not QFileInfo(self.fileNames[self.ind]).suffix():
1018
self.fileNames[self.ind] += ext
1019
if QFileInfo(self.fileNames[self.ind]).isWritable() or not QFile.exists(self.fileNames[self.ind]):
1020
if self.fileNames[self.ind]:
1021
self.saveFileWrapper(self.fileNames[self.ind])
1022
self.setCurrentFile()
1023
self.editBoxes[self.ind].document().setModified(False)
1024
self.setWindowModified(False)
1026
self.setWindowModified(self.isWindowModified())
1027
QMessageBox.warning(self, app_name, self.tr("Cannot save to file because it is read-only!"))
1029
def saveFileWrapper(self, fn):
1030
savefile = QFile(fn)
1031
savefile.open(QIODevice.WriteOnly)
1032
savestream = QTextStream(savefile)
1033
savestream << self.editBoxes[self.ind].toPlainText()
1036
def saveHtml(self, fileName):
1037
if not QFileInfo(fileName).suffix():
1040
text = self.parseText()
1041
except Exception as e:
1042
return self.printError(e)
1043
htmlFile = QFile(fileName)
1044
htmlFile.open(QIODevice.WriteOnly)
1045
html = QTextStream(htmlFile)
1046
if self.getParser() == PARSER_HTML:
1047
html << text << "\n"
1050
html << '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">\n'
1051
html << '<html>\n<head>\n'
1052
html << ' <meta http-equiv="content-type" content="text/html; charset=utf-8">\n'
1053
html << ' <meta name="generator" content="%s %s">\n' % (app_name, app_version)
1054
html << ' <title>' + self.getDocumentTitle() + '</title>\n'
1055
html << '</head>\n<body>\n'
1057
html << '\n</body>\n</html>\n'
1060
def textDocument(self):
1061
if not self.actionPlainText.isChecked():
1062
text = self.parseText()
1063
td = QTextDocument()
1064
td.setMetaInformation(QTextDocument.DocumentTitle, self.getDocumentTitle())
1066
td.setDefaultStyleSheet(self.ss)
1067
if self.actionPlainText.isChecked():
1068
td.setPlainText(self.editBoxes[self.ind].toPlainText())
1070
td.setHtml('<html><body>'+text+'</body></html>')
1072
td.setDefaultFont(self.font)
1077
document = self.textDocument()
1078
except Exception as e:
1081
fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), "", self.tr("OpenDocument text files (*.odt)"))
1082
if not QFileInfo(fileName).suffix():
1084
writer = QTextDocumentWriter(fileName)
1085
writer.setFormat("odf")
1086
writer.write(document)
1088
def saveFilePerfect(self):
1090
fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", self.tr("HTML files (*.html *.htm)"))
1092
self.saveHtml(fileName)
1094
def getDocumentForPrint(self):
1096
return self.previewBoxes[self.ind]
1098
return self.textDocument()
1099
except Exception as e:
1103
def standardPrinter(self):
1104
printer = QPrinter(QPrinter.HighResolution)
1105
printer.setDocName(self.getDocumentTitle())
1106
printer.setCreator(app_name+" "+app_version)
1110
document = self.getDocumentForPrint()
1111
if document == None:
1113
fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"), "", self.tr("PDF files (*.pdf)"))
1115
if not QFileInfo(fileName).suffix():
1117
printer = self.standardPrinter()
1118
printer.setOutputFormat(QPrinter.PdfFormat)
1119
printer.setOutputFileName(fileName)
1120
document.print_(printer)
1122
def printFile(self):
1123
document = self.getDocumentForPrint()
1124
if document == None:
1126
printer = self.standardPrinter()
1127
dlg = QPrintDialog(printer, self)
1128
dlg.setWindowTitle(self.tr("Print document"))
1129
if (dlg.exec_() == QDialog.Accepted):
1130
document.print_(printer)
1132
def printPreview(self):
1133
document = self.getDocumentForPrint()
1134
if document == None:
1136
printer = self.standardPrinter()
1137
preview = QPrintPreviewDialog(printer, self)
1138
self.connect(preview, SIGNAL("paintRequested(QPrinter*)"), document.print_)
1141
def runExtensionCommand(self, command, filefilter='', defaultext=''):
1142
of = ('%of' in command)
1143
html = ('%html' in command)
1145
if defaultext and not filefilter:
1146
filefilter = '*'+defaultext
1147
fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)
1150
if defaultext and not QFileInfo(fileName).suffix():
1151
fileName += defaultext
1153
tmpname = '.retext-temp.html'
1154
self.saveHtml(tmpname)
1156
tmpname = '.retext-temp.rst' if self.getParser() == PARSER_DOCUTILS else '.retext-temp.mkd'
1157
self.saveFileWrapper(tmpname)
1158
command = command.replace('%of', 'out'+defaultext)
1159
command = command.replace('%html' if html else '%if', tmpname)
1160
args = str(command).split()
1163
except Exception as error:
1164
errorstr = str(error)
1166
errorstr = QString.fromUtf8(errorstr)
1168
# Not needed for Python 3
1170
QMessageBox.warning(self, app_name, self.tr('Failed to execute the command:') + '\n' + errorstr)
1171
QFile(tmpname).remove()
1173
QFile('out'+defaultext).rename(fileName)
1175
def otherExport(self):
1176
if self.actionPlainText.isChecked():
1177
return QMessageBox.warning(self, app_name, self.tr('This function is not available in Plain text mode!'))
1178
settings.beginGroup('Export')
1179
types = settings.allKeys()
1180
item, ok = QInputDialog.getItem(self, app_name, self.tr('Select type'), types, 0, False)
1182
return settings.endGroup()
1183
command = readFromSettings(settings, item, str)
1185
self.runExtensionCommand(command, defaultext='.'+item)
1187
def getDocumentTitle(self, baseName=False):
1188
"""Ensure that parseText() is called before this function!
1189
If 'baseName' is set to True, file basename will be used."""
1192
text = unicode(self.editBoxes[self.ind].toPlainText())
1195
text = self.editBoxes[self.ind].toPlainText()
1196
if not self.actionPlainText.isChecked():
1197
parser = self.getParser()
1198
if parser == PARSER_DOCUTILS:
1199
realTitle = publish_parts(text, writer_name='html')['title']
1200
elif parser == PARSER_MARKDOWN:
1202
realTitle = str.join(' ', md.Meta['title'])
1204
# Meta extension not installed
1206
if realTitle and not baseName:
1208
elif self.fileNames[self.ind]:
1209
fileinfo = QFileInfo(self.fileNames[self.ind])
1210
basename = fileinfo.completeBaseName()
1211
return (basename if basename else fileinfo.fileName())
1213
return self.tr("New document")
1215
def saveGDocs(self):
1217
if settings.contains('GDocsLogin') and settings.contains('GDocsPasswd'):
1218
login = readFromSettings(settings, 'GDocsLogin', str)
1219
passwd = readFromSettings(settings, 'GDocsPasswd', str)
1220
if self.gDocsEntries[self.ind] == None:
1221
loginDialog = LogPassDialog(login, passwd)
1222
if loginDialog.exec_() == QDialog.Accepted:
1223
login = loginDialog.loginEdit.text()
1224
passwd = loginDialog.passEdit.text()
1227
if self.actionPlainText.isChecked():
1228
tempFile = '.retext-temp.txt'
1229
contentType = 'text/plain'
1230
self.saveFileWrapper(tempFile)
1232
tempFile = '.retext-temp.html'
1233
contentType = 'text/html'
1234
self.saveHtml(tempFile)
1235
gdClient = gdata.docs.client.DocsClient(source=app_name)
1239
gdClient.ClientLogin(unicode(login), unicode(passwd), gdClient.source)
1242
gdClient.ClientLogin(login, passwd, gdClient.source)
1243
except gdata.client.BadAuthentication:
1244
QFile(tempFile).remove()
1245
return QMessageBox.warning(self, app_name, self.tr("Incorrect user name or password!"))
1247
QFile(tempFile).remove()
1248
return QMessageBox.warning(self, app_name, \
1249
self.tr("Authentification failed, please check your internet connection!"))
1250
settings.setValue("GDocsLogin", login)
1251
settings.setValue("GDocsPasswd", passwd)
1253
title = unicode(self.getDocumentTitle())
1256
title = self.getDocumentTitle()
1257
ms = MediaSource(file_path=tempFile, content_type=contentType)
1258
entry = self.gDocsEntries[self.ind]
1260
entry.title.text = title
1261
entry = gdClient.Update(entry, media_source=ms, force=True)
1264
resource = gdata.docs.data.Resource(title=title)
1265
entry = gdClient.CreateResource(resource, media=ms)
1266
except AttributeError:
1267
# For old gdata versions
1268
entry = gdClient.Upload(ms, title)
1269
QDesktopServices.openUrl(QUrl(entry.GetAlternateLink().href))
1270
self.gDocsEntries[self.ind] = entry
1271
QFile(tempFile).remove()
1273
def autoSaveActive(self):
1274
return self.autoSave and self.fileNames[self.ind] and \
1275
QFileInfo(self.fileNames[self.ind]).isWritable()
1277
def modificationChanged(self, changed):
1278
if self.autoSaveActive():
1280
self.actionSave.setEnabled(changed)
1281
self.setWindowModified(changed)
1283
def clipboardDataChanged(self):
1284
self.actionPaste.setEnabled(qApp.clipboard().mimeData().hasText())
1286
def insertChars(self, chars):
1287
tc = self.editBoxes[self.ind].textCursor()
1289
tc.insertText(chars+tc.selectedText()+chars)
1291
tc.insertText(chars)
1293
def insertTag(self, num):
1295
ut = self.usefulTags[num-1]
1296
hc = not ut in ('td', 'tr')
1300
tc = self.editBoxes[self.ind].textCursor()
1302
toinsert = '<'+ut+arg+'>'+tc.selectedText()+'</'+ut+'>'
1303
tc.insertText(toinsert)
1305
tc.insertText('<'+ut+arg+'>'+tc.selectedText())
1306
self.tagsBox.setCurrentIndex(0)
1308
def insertSymbol(self, num):
1310
self.editBoxes[self.ind].insertPlainText('&'+self.usefulChars[num-1]+';')
1311
self.symbolBox.setCurrentIndex(0)
1313
def maybeSave(self, ind):
1314
if self.autoSaveActive():
1315
self.saveFileWrapper(self.fileNames[self.ind])
1317
if not self.editBoxes[ind].document().isModified():
1319
self.tabWidget.setCurrentIndex(ind)
1320
ret = QMessageBox.warning(self, app_name, self.tr("The document has been modified.\nDo you want to save your changes?"), \
1321
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)
1322
if ret == QMessageBox.Save:
1323
self.saveFileMain(False)
1325
elif ret == QMessageBox.Cancel:
1329
def closeEvent(self, closeevent):
1331
for self.ind in range(self.tabWidget.count()):
1332
if not self.maybeSave(self.ind):
1335
if self.restorePreviewState:
1336
if self.livePreviewEnabled:
1337
settings.setValue('previewState', True)
1339
settings.remove('previewState')
1345
HtmlDlg = HtmlDialog(self)
1347
HtmlDlg.textEdit.setPlainText(self.parseText())
1348
except Exception as e:
1351
winTitle = self.tr('New document')
1352
if self.fileNames[self.ind]:
1353
winTitle = QFileInfo(self.fileNames[self.ind]).fileName()
1355
HtmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+") "+QChar(0x2014)+" "+app_name)
1358
HtmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+") \u2014 "+app_name)
1361
HtmlDlg.activateWindow()
1364
QDesktopServices.openUrl(QUrl('http://sourceforge.net/p/retext/home/Help and Support'))
1366
def aboutDialog(self):
1367
QMessageBox.about(self, self.aboutWindowTitle, \
1368
'<p><b>'+app_name+' '+app_version+'</b><br>'+self.tr('Simple but powerful editor for Markdown and ReStructuredText') \
1369
+'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011') \
1370
+'<br><a href="http://sourceforge.net/p/retext/">'+self.tr('Website') \
1371
+'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">'+self.tr('Markdown syntax') \
1372
+'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">' \
1373
+self.tr('ReST syntax')+'</a></p>')
1375
def enablePlainText(self, value):
1376
self.aptc[self.ind] = value
1377
self.enablePlainTextMain(value)
1378
self.updatePreviewBox()
1380
def enablePlainTextMain(self, value):
1381
self.actionPerfectHtml.setDisabled(value)
1382
self.actionViewHtml.setDisabled(value)
1383
self.tagsBox.setDisabled(value)
1384
self.symbolBox.setDisabled(value)
1386
def setDocUtilsDefault(self, yes):
1387
self.useDocUtils = yes
1389
settings.setValue('useReST', True)
1391
settings.remove('useReST')
1392
self.updatePreviewBox()
1394
def getParser(self):
1395
if self.fileNames[self.ind]:
1396
suffix = QFileInfo(self.fileNames[self.ind]).suffix()
1397
if suffix in ('md', 'markdown', 'mdown', 'mkd', 'mkdn', 're'):
1398
return PARSER_MARKDOWN if use_md else PARSER_NA
1399
elif suffix in ('rest', 'rst'):
1400
return PARSER_DOCUTILS if use_docutils else PARSER_NA
1401
elif suffix in ('html', 'htm'):
1403
if not (use_docutils or use_md):
1405
elif use_docutils and (self.useDocUtils or not use_md):
1406
return PARSER_DOCUTILS
1408
return PARSER_MARKDOWN
1410
def parseText(self):
1412
htmltext = unicode(self.editBoxes[self.ind].toPlainText())
1415
htmltext = self.editBoxes[self.ind].toPlainText()
1417
htmltext = htmltext.replace('%HTMLDIR%', 'html')
1418
htmltext = htmltext.replace('%\\', '%')
1419
parser = self.getParser()
1420
if parser == PARSER_HTML:
1422
elif parser == PARSER_DOCUTILS:
1423
return publish_parts(htmltext, writer_name='html')['body']
1424
elif parser == PARSER_MARKDOWN:
1426
return md.convert(htmltext)
1428
return '<p style="color: red">'\
1429
+self.tr('Could not parse file contents, check if you have the necessary module installed!')+'</p>'
1431
def main(fileNames):
1432
app = QApplication(sys.argv)
1433
app.setOrganizationName("ReText project")
1434
app.setApplicationName("ReText")
1435
RtTranslator = QTranslator()
1436
if not RtTranslator.load("retext_"+QLocale.system().name(), "locale"):
1437
if not RtTranslator.load("retext_"+QLocale.system().name(), "/usr/share/retext/locale"):
1438
RtTranslator.load("retext_"+QLocale.system().name(), "/usr/lib/retext")
1439
QtTranslator = QTranslator()
1440
QtTranslator.load("qt_"+QLocale.system().name(), QLibraryInfo.location(QLibraryInfo.TranslationsPath))
1441
app.installTranslator(RtTranslator)
1442
app.installTranslator(QtTranslator)
1443
if settings.contains('appStyleSheet'):
1444
stylename = readFromSettings(settings, 'appStyleSheet', str)
1445
sheetfile = QFile(stylename)
1446
sheetfile.open(QIODevice.ReadOnly)
1447
app.setStyleSheet(QTextStream(sheetfile).readAll())
1449
window = ReTextWindow()
1450
for fileName in fileNames:
1452
fileName = QString.fromUtf8(fileName)
1454
# Not needed for Python 3
1456
if QFile.exists(fileName):
1457
window.openFileWrapper(fileName)
1459
sys.exit(app.exec_())
1461
if __name__ == '__main__':
1462
if len(sys.argv) > 1: