1#!/usr/bin/env python3 2# vim: ts=8:sts=8:sw=8:noexpandtab 3 4# ReText 5# Copyright 2011-2021 Dmitry Shachnev 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with this program. If not, see <http://www.gnu.org/licenses/>. 19 20import ctypes 21import multiprocessing 22import sys 23import signal 24import markups 25from os import devnull 26from os.path import join 27from ReText import datadirs, settings, globalSettings, app_version 28from ReText import initializeDataDirs 29from ReText.window import ReTextWindow 30 31from PyQt5.QtCore import QCommandLineOption, QCommandLineParser, QFile, \ 32 QFileInfo, QIODevice, QLibraryInfo, QTextStream, QTranslator, Qt 33from PyQt5.QtWidgets import QApplication 34from PyQt5.QtNetwork import QNetworkProxyFactory 35from PyQt5.QtDBus import QDBusConnection, QDBusInterface 36 37def canonicalize(option): 38 if option == '-': 39 return option 40 return QFileInfo(option).canonicalFilePath() 41 42def main(): 43 multiprocessing.set_start_method('spawn') 44 45 if markups.__version_tuple__ < (2, ): 46 sys.exit('Error: ReText needs PyMarkups 2.0 or newer to run.') 47 48 # If we're running on Windows without a console, then discard stdout 49 # and save stderr to a file to facilitate debugging in case of crashes. 50 if sys.executable.endswith('pythonw.exe'): 51 sys.stdout = open(devnull, 'w') 52 sys.stderr = open('stderr.log', 'w') 53 54 try: 55 # See https://github.com/retext-project/retext/issues/399 56 # and https://launchpad.net/bugs/941826 57 ctypes.CDLL('libGL.so.1', ctypes.RTLD_GLOBAL) 58 except OSError: 59 pass 60 61 # Needed for Qt WebEngine on Windows 62 QApplication.setAttribute(Qt.ApplicationAttribute.AA_ShareOpenGLContexts) 63 QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps) 64 app = QApplication(sys.argv) 65 app.setOrganizationName("ReText project") 66 app.setApplicationName("ReText") 67 app.setApplicationDisplayName("ReText") 68 app.setApplicationVersion(app_version) 69 app.setOrganizationDomain('mitya57.me') 70 app.setDesktopFileName('me.mitya57.ReText.desktop') 71 QNetworkProxyFactory.setUseSystemConfiguration(True) 72 73 initializeDataDirs() 74 RtTranslator = QTranslator() 75 for path in datadirs: 76 if RtTranslator.load('retext_' + globalSettings.uiLanguage, 77 join(path, 'locale')): 78 break 79 QtTranslator = QTranslator() 80 QtTranslator.load("qtbase_" + globalSettings.uiLanguage, 81 QLibraryInfo.location(QLibraryInfo.LibraryLocation.TranslationsPath)) 82 app.installTranslator(RtTranslator) 83 app.installTranslator(QtTranslator) 84 85 parser = QCommandLineParser() 86 parser.addHelpOption() 87 parser.addVersionOption() 88 previewOption = QCommandLineOption('preview', 89 QApplication.translate('main', 'Open the files in preview mode')) 90 newWindowOption = QCommandLineOption('new-window', 91 QApplication.translate('main', 'Create a new window even if there is an existing one')) 92 parser.addOption(previewOption) 93 parser.addOption(newWindowOption) 94 parser.addPositionalArgument('files', 95 QApplication.translate('main', 'List of files to open'), 96 '[files...]') 97 98 parser.process(app) 99 filesToOpen = parser.positionalArguments() 100 101 print('Using configuration file:', settings.fileName()) 102 if globalSettings.appStyleSheet: 103 sheetfile = QFile(globalSettings.appStyleSheet) 104 sheetfile.open(QIODevice.OpenModeFlag.ReadOnly) 105 app.setStyleSheet(QTextStream(sheetfile).readAll()) 106 sheetfile.close() 107 window = ReTextWindow() 108 109 openInExistingWindow = (globalSettings.openFilesInExistingWindow 110 and not parser.isSet(newWindowOption)) 111 connection = QDBusConnection.sessionBus() 112 if connection.isConnected() and openInExistingWindow: 113 connection.registerObject('/', window, QDBusConnection.RegisterOption.ExportAllSlots) 114 serviceName = 'me.mitya57.ReText' 115 if not connection.registerService(serviceName) and filesToOpen: 116 print('Opening the file(s) in the existing window of ReText.') 117 iface = QDBusInterface(serviceName, '/', '', connection) 118 for fileName in filesToOpen: 119 iface.call('openFileWrapper', fileName) 120 qWidgetIface = QDBusInterface(serviceName, '/', 'org.qtproject.Qt.QWidget', connection) 121 qWidgetIface.call('raise') 122 sys.exit(0) 123 124 window.show() 125 # ReText can change directory when loading files, so we 126 # need to have a list of canonical names before loading 127 fileNames = list(map(canonicalize, filesToOpen)) 128 readStdIn = False 129 130 if globalSettings.openLastFilesOnStartup: 131 window.restoreLastOpenedFiles() 132 for fileName in fileNames: 133 if QFile.exists(fileName): 134 window.openFileWrapper(fileName) 135 if parser.isSet(previewOption): 136 window.actionPreview.setChecked(True) 137 window.preview(True) 138 elif fileName == '-': 139 readStdIn = True 140 141 inputData = '' 142 if readStdIn and sys.stdin is not None: 143 if sys.stdin.isatty(): 144 print('Reading stdin, press ^D to end...') 145 inputData = sys.stdin.read() 146 if inputData or not window.tabWidget.count(): 147 window.createNew(inputData) 148 signal.signal(signal.SIGINT, lambda sig, frame: window.close()) 149 sys.exit(app.exec()) 150 151if __name__ == '__main__': 152 main() 153