source: trunk/PARPG/tools/Writing Editor/scripts/writingEditor.py @ 227

Revision 227, 20.6 KB checked in by bretzel_parpg, 10 years ago (diff)

Merged help with the editor and help with scripting into one help. Added syntax highlighting

Line 
1#!/usr/bin/python
2
3#   This file is part of PARPG.
4
5#   PARPG is free software: you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation, either version 3 of the License, or
8#   (at your option) any later version.
9
10#   PARPG is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14
15#   You should have received a copy of the GNU General Public License
16#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
17
18import sys, os, codecs
19
20from PyQt4 import QtGui, QtCore
21from ui.editor_ui import Ui_writingEditor
22from ui.popupWindows import *
23from scripts.syntaxHighlight import SyntaxHighlighter
24from scripts.settings import Settings
25
26class WritingEditor(QtGui.QMainWindow):
27    """
28    The main class for the writing editor
29    """
30    def __init__(self, parent=None):
31        """
32        Initialize the editor
33        """
34        QtGui.QWidget.__init__(self, parent)
35        self.ui = Ui_writingEditor()
36        self.ui.setupUi(self)
37        self.syntax = SyntaxHighlighter(self.ui.main_edit.document())
38        self.connectSignals()
39        self.setupMenus()
40
41        self.settings = Settings()
42        self.settings.readSettingsFromFile('data/options.txt')
43        self.resize(int(self.settings.res_width), int(self.settings.res_height))
44
45        self.open_file_name = None
46        self.saveEnabled(False)
47        self.title_asterisk = False
48        self.ui.actionNone.setEnabled(False)
49        self.getRecentItems('data/recent_files.txt')
50
51
52    def setupMenus(self):
53        """
54        Setup the menus:
55        Add the shortcuts to all the menu's and also create the shorcuts
56        Add the images for the icons
57        @return: None
58        """
59        self.ui.actionNew_File.setText('&New File\tCtrl+N')
60        self.ui.actionOpen_File.setText('&Open File\tCtrl+O')
61        self.ui.actionSave.setText('&Save\tCtrl+S')
62        self.ui.actionSave_As.setText('Save &As\tCtrl+Shift+S')
63        self.ui.actionPrint.setText('&Print\tCtrl+P')
64        self.ui.actionExit.setText('&Exit\tCtrl+Q')
65        self.ui.actionUndo.setText('&Undo\tCtrl+Z')
66        self.ui.actionRedo.setText('&Redo\tCtrl+Y')
67        self.ui.actionCopy.setText('&Copy\tCtrl+C')
68        self.ui.actionCut.setText('C&ut\tCtrl+X')
69        self.ui.actionPaste.setText('&Paste\tCtrl+V')
70        self.ui.actionPreferences.setText('P&references\tCtrl+Shift+P')
71        self.ui.actionHelp.setText("&Help\tF1")
72        self.ui.actionAbout.setText('&About\tF2')
73        self.ui.actionNone.setText('None')
74
75        self.ui.actionNew_File.setShortcut(QtGui.QKeySequence('Ctrl+N'))
76        self.ui.actionOpen_File.setShortcut(QtGui.QKeySequence('Ctrl+O'))
77        self.ui.actionSave.setShortcut(QtGui.QKeySequence('Ctrl+S'))
78        self.ui.actionSave_As.setShortcut(QtGui.QKeySequence('Ctrl+Shift+S'))
79        self.ui.actionPrint.setShortcut(QtGui.QKeySequence('Ctrl+P'))
80        self.ui.actionExit.setShortcut(QtGui.QKeySequence('Ctrl+Q'))
81        self.ui.actionUndo.setShortcut(QtGui.QKeySequence('Ctrl+Z'))
82        self.ui.actionRedo.setShortcut(QtGui.QKeySequence('Ctrl+Y'))
83        self.ui.actionCopy.setShortcut(QtGui.QKeySequence('Ctrl+C'))
84        self.ui.actionCut.setShortcut(QtGui.QKeySequence('Ctrl+X'))
85        self.ui.actionPaste.setShortcut(QtGui.QKeySequence('Ctrl+V'))
86        self.ui.actionPreferences.setShortcut(QtGui.QKeySequence('Ctrl+Shift+P'))
87        self.ui.actionHelp.setShortcut(QtGui.QKeySequence('F1'))
88        self.ui.actionAbout.setShortcut(QtGui.QKeySequence('F2'))
89
90        self.ui.actionNew_File.setIcon(self.createIcon('new.png'))
91        self.ui.actionOpen_File.setIcon(self.createIcon('open.png'))
92        self.ui.actionSave.setIcon(self.createIcon('save.png'))
93        self.ui.actionSave_As.setIcon(self.createIcon('save_as.png'))
94        self.ui.actionPrint.setIcon(self.createIcon('printer.png'))
95        self.ui.actionExit.setIcon(self.createIcon('close.png'))
96        self.ui.actionUndo.setIcon(self.createIcon('undo.png'))
97        self.ui.actionRedo.setIcon(self.createIcon('redo.png'))
98        self.ui.actionCopy.setIcon(self.createIcon('copy.png'))
99        self.ui.actionCut.setIcon(self.createIcon('cut.png'))
100        self.ui.actionPaste.setIcon(self.createIcon('paste.png'))
101        self.ui.actionPreferences.setIcon(self.createIcon('preferences.png'))
102        self.ui.actionHelp.setIcon(self.createIcon('help.png'))
103        self.ui.actionAbout.setIcon(self.createIcon('about.png'))
104
105        self.ui.actionNew_File.setStatusTip('Create a new file')
106        self.ui.actionOpen_File.setStatusTip('Open a file')
107        self.ui.actionSave.setStatusTip('Save the open file to disk')
108        self.ui.actionSave_As.setStatusTip('Save the contents of the open file to a new file on the disk')
109        self.ui.actionPrint.setStatusTip('Print the open file')
110        self.ui.actionExit.setStatusTip('Exit the editor')
111        self.ui.actionUndo.setStatusTip('Undo the last action within the text editor')
112        self.ui.actionRedo.setStatusTip('Redo the last action within the text editor')
113        self.ui.actionCopy.setStatusTip('Copy the selected text to the clipboard')
114        self.ui.actionCut.setStatusTip('Delete the selected text and copy it to the clipboard')
115        self.ui.actionPaste.setStatusTip('Paste the text on the clipboard')
116        self.ui.actionPreferences.setStatusTip('Edit preferences with the editor')
117        self.ui.actionHelp.setStatusTip('Help with the editor and scripting language itself')
118        self.ui.actionAbout.setStatusTip('About the editor')
119        self.ui.actionNone.setStatusTip('There are no recent files')
120
121    def connectSignals(self):
122        """
123        Connect all the buttons, widgets, etc to their respective functions
124        @return: None
125        """
126        QtCore.QObject.connect(self.ui.menubar, QtCore.SIGNAL("currentChanged(int)"),
127                               self, QtCore.SIGNAL("enableFunctionsByTabs(int)"))
128        QtCore.QObject.connect(self.ui.main_edit, QtCore.SIGNAL("textChanged()"),
129                               self.onTextChanged)
130
131        QtCore.QObject.connect(self.ui.actionNew_File, QtCore.SIGNAL("triggered()"),
132                               self.newFile)
133        QtCore.QObject.connect(self.ui.actionOpen_File, QtCore.SIGNAL("triggered()"),
134                               self.openFile)
135        QtCore.QObject.connect(self.ui.actionSave, QtCore.SIGNAL("triggered()"),
136                               self.saveFile)
137        QtCore.QObject.connect(self.ui.actionPrint, QtCore.SIGNAL("triggered()"),
138                               self.printFile)
139        QtCore.QObject.connect(self.ui.actionExit, QtCore.SIGNAL("triggered()"),
140                               lambda: self.quit('data/recent_files.txt'))
141
142        QtCore.QObject.connect(self.ui.actionCopy, QtCore.SIGNAL("triggered()"),
143                               self.ui.main_edit.copy)
144        QtCore.QObject.connect(self.ui.actionCut, QtCore.SIGNAL("triggered()"),
145                               self.ui.main_edit.cut)
146        QtCore.QObject.connect(self.ui.actionPaste, QtCore.SIGNAL("triggered()"),
147                               self.ui.main_edit.paste)
148        QtCore.QObject.connect(self.ui.actionRedo, QtCore.SIGNAL("triggered()"),
149                               self.ui.main_edit.redo)
150        QtCore.QObject.connect(self.ui.actionUndo, QtCore.SIGNAL("triggered()"),
151                               self.ui.main_edit.undo)
152        QtCore.QObject.connect(self.ui.actionPreferences, QtCore.SIGNAL("triggered()"),
153                               self.createPrefWindow)
154
155        QtCore.QObject.connect(self.ui.actionAbout, QtCore.SIGNAL("triggered()"),
156                               self.createAboutWindow)
157        QtCore.QObject.connect(self.ui.actionHelp, QtCore.SIGNAL("triggered()"),
158                               self.createHelpWindow)
159
160    def onTextChanged(self):
161        """
162        Function called when text is changed
163        """
164        if (not self.ui.actionSave.isEnabled):
165            self.saveEnabled(True)
166        if (self.windowTitle() == "PARPG Writing Editor - Untitled"):
167            return
168
169        if (self.open_file_name == None):
170            self.setWindowTitle("PARPG Writing Editor - Untitled")
171            return
172
173        if (self.title_asterisk == False):
174            self.setWindowTitle(self.windowTitle() + " *")
175            self.title_asterisk = True
176
177    def enableFunctionsByTabs(self, index):
178        """
179        Check if the tab is the editor or the map viewer and disable/enable actions
180        accordingly
181        @type index: int
182        @param index: The index of the tab
183        @return: None
184        """
185        if (index == 0):
186            self.ui.actionCopy.setEnabled(True)
187            self.ui.actionCut.setEnabled(True)
188            self.ui.actionPaste.setEnabled(True)
189
190        elif (index == 1):
191            self.ui.actionCopy.setEnabled(False)
192            self.ui.actionCut.setEnabled(False)
193            self.ui.actionPaste.setEnabled(False)
194        else:
195            print 'Parameter index should be either 0 or 1. Got %d' % index
196
197    def createIcon(self, name):
198        """
199        Creates a QIcon object from the path name
200        @type name: string
201        @param name: the name of the file
202        @rtype: QtGui.QIcon
203        @return: The QIcon object
204        """
205        path = 'data/images/' + name
206        icon = QtGui.QIcon(path)
207        return icon
208
209    def newFile(self):
210        """
211        Start a new file
212        @return: None
213        """
214        self.open_file_name = None
215        self.ui.main_edit.setText("")
216        self.saveEnabled(False)
217
218    def saveFile(self, filename=None):
219        """
220        Save the contents of self.ui.main_edit to filename
221        If filename is None, then open a save dialog
222        @type filename: string
223        @param filename: the file to save to
224        @return: None
225        """
226        # if no filename argument is specified and there is no open file, open the save dialog
227        if (filename == None and self.open_file_name == None):
228            file_dialog = QtGui.QFileDialog(self)
229            file_dialog.setDefaultSuffix("dialog")
230            file_dialog.setNameFilter("Dialog Files (*.dialog)")
231            self.save_file_name = file_dialog.getSaveFileName()
232            self.open_file_name = self.save_file_name
233        # otherwise just save the file
234        else:
235            self.save_file_name = self.open_file_name
236
237        try:
238            text = self.ui.main_edit.toPlainText()
239            codec_file = codecs.open(self.save_file_name, 'w', 'utf-8')
240            codec_file.write(text)
241            codec_file.close()
242            self.saveEnabled(False)
243            last_slash = self.findLastSlash(self.save_file_name)
244            self.setWindowTitle("PARPG Writing Editor - " + self.save_file_name[last_slash+1:])
245            self.title_asterisk = False
246           
247        except IOError:
248            print 'Unable to save to file: %s' % self.save_file_name
249
250    def saveAs(self):
251        """
252        Open a dialog to save the current file as a new file
253        @return: None
254        """
255        self.saveFile()
256
257    def openFile(self, filename=None):
258        """
259        Open a file
260        @type filename: String
261        @param filename: the file to open
262        @return: None
263        """
264        old_file_name = self.open_file_name
265        if (filename == None):
266            file_dialog = QtGui.QFileDialog(self)
267            self.open_file_name = file_dialog.getOpenFileName()
268        else:
269            self.open_file_name = filename
270        try:
271            codec_file = codecs.open(self.open_file_name, 'r', 'utf-8')
272            codec_contents = codec_file.read()
273            self.ui.main_edit.setText(codec_contents)
274            self.saveEnabled(False)
275
276            new_dict = {}
277
278            try:
279                recent_length = len(self.recent_items)
280                keys = self.recent_items.keys()
281                if (recent_length != 0):
282                    keys.remove(keys[recent_length-1])
283                for key in keys:
284                    value = self.recent_items[key]
285                    new_dict[key] = value
286            except:
287                recent_length = 0
288               
289            last_slash = self.findLastSlash(self.open_file_name)
290            before = self.open_file_name[:last_slash+1]
291            after = self.open_file_name[last_slash+1:]
292            new_dict[after] = before
293               
294            self.recent_items = new_dict
295            self.updateRecentItems()
296
297            slash = self.findLastSlash(self.open_file_name)
298            window_title = 'PARPG Writing Editor - ' + self.open_file_name[slash+1:]
299            self.ui.writingEditor.setWindowTitle(window_title)
300            self.title_asterisk = False
301
302        except IOError:
303            print 'Unable to open file: %s' % self.open_file_name
304            self.open_file_name = old_file_name           
305
306    def printFile(self):
307        """
308        Print the currently open file
309        """
310        qprinter = QtGui.QPrinter()
311        print_dialog = PrintDialog(qprinter)
312        ret = print_dialog.run()
313        if (ret == QtGui.QDialog.Accepted):
314            self.ui.main_edit.document().print_(qprinter)
315
316    def saveEnabled(self, value):
317        """
318        Change whether save is enabled
319        @type value: bool
320        @param value: whether to enable or disable save
321        @return: None
322        """
323        self.ui.actionSave.setEnabled(value)
324        self.ui.actionSave_As.setEnabled(value)
325
326    def createAboutWindow(self):
327        """
328        Create the about the program window
329        @return: None
330        """
331        if (not hasattr(self, "about_window")):
332            self.about_window = AboutWindow()
333        self.about_window.show()
334
335    def createPrefWindow(self):
336        """
337        Create the preferences window
338        @return: None
339        """
340        if (not hasattr(self, "pref_window")):
341            self.pref_window = PrefWindow(self, self.settings)
342        self.pref_window.show()
343        self.pref_window.button_apply.setEnabled(True)
344
345    def createHelpWindow(self):
346        """
347        Create the help window
348        @return: None
349        """
350        if (not hasattr(self, "help_window")):
351            self.help_window = HelpWindow(self.settings)
352        self.help_window.show()
353
354    def getRecentItems(self, filename):
355        """
356        Reads all the filenames from the file filename that contains all the recent files
357        @type filename: string
358        @param filename: the path to the file
359        @return: None
360        """
361        self.recent_items = {}
362        try:
363            recent_files = open(filename, 'r').read().strip()
364           
365            if (recent_files == ""):
366                self.recent_items = None
367                return
368
369            recent_list = recent_files.split('\n')
370
371            for item in recent_list:
372                last_slash = self.findLastSlash(item)                       
373                before = item[:last_slash+1]
374                after = item[last_slash+1:]
375                self.recent_items[after] = before
376
377            self.updateRecentItems()
378            self.ui.menuRecent_Files.removeAction(self.ui.actionNone)
379
380        except IOError:
381            print 'Unable to read the recent files from file: %s\n'\
382                'No recent files will be displayed' % str(filename)
383           
384
385    def updateRecentItems(self):
386        """
387        Make the recent items show up in the gui
388        @return: None
389        """
390        try:
391            self.ui.menuRecent_files.removeAction(self.recent_1)
392        except:
393            print "Cannot remove action self.recent_1"
394        try:
395            self.ui.menuRecent_files.removeAction(self.recent_2)
396        except:
397            print "Cannot remove action self.recent_2"
398        try:
399            self.ui.menuRecent_files.removeAction(self.recent_3)
400        except:
401            print "Cannot remove action self.recent_3"
402        try:
403            self.ui.menuRecent_files.removeAction(self.recent_4)
404        except:
405            print "Cannot remove action self.recent_4"
406
407        recent_keys = []
408        for key in self.recent_items:
409            recent_keys.append(key)
410           
411        try:
412            self.recent_1 = QtGui.QAction(self)
413            self.recent_1.setObjectName(recent_keys[0])
414            self.recent_1.setText(recent_keys[0])
415            self.ui.menuRecent_Files.addAction(self.recent_1)
416            full_path_1 = self.recent_items[recent_keys[0]] + recent_keys[0]
417            QtCore.QObject.connect(self.recent_1, QtCore.SIGNAL('triggered()'),
418                                   lambda: self.openFile(full_path_1))
419           
420        except:
421            print 'Error generating widgets for recent item 1'
422           
423        try:
424            self.recent_2 = QtGui.QAction(self)
425            self.recent_2.setObjectName(recent_keys[1])
426            self.recent_2.setText(recent_keys[1])
427            self.ui.menuRecent_Files.addAction(self.recent_2, 0)
428            full_path_2 = self.recent_items[recent_keys[1]] + recent_keys[1]
429            QtCore.QObject.connect(self.recent_2, QtCore.SIGNAL('triggered()'),
430                                   lambda: self.openFile(full_path_2))
431                   
432        except:
433            print 'Error generating widgets for recent item 2'
434           
435        try:
436            self.recent_3 = QtGui.QAction(self)
437            self.recent_3.setObjectName(recent_keys[2])
438            self.recent_3.setText(recent_keys[2])
439            self.ui.menuRecent_Files.addAction(self.recent_3)
440            full_path_3 = self.recent_items[recent_keys[2]] + recent_keys[2]
441            QtCore.QObject.connect(self.recent_3, QtCore.SIGNAL('triggered()'),
442                                   lambda: self.openFile(full_path_3))
443       
444        except:
445            print 'Error generating widgets for recent item 3'
446
447        try:
448            self.recent_4 = QtGui.QAction(self)
449            self.recent_4.setObjectName(recent_keys[3])
450            self.recent_4.setText(recent_keys[3])
451            self.ui.menuRecent_Files.addAction(self.recent_4)
452            full_path_4 = self.recent_items[recent_keys[3]] + recent_keys[3]
453            QtCore.QObject.connect(self.recent_4, QtCore.SIGNAL('triggered()'),
454                                   lambda: self.openFile(full_path_4))
455       
456        except:
457            print 'Error generating widgets for recent item 4'
458           
459
460    def writeRecentItems(self, filename):
461        """
462        Write the recent items to the file filename
463        @type filename: string
464        @param filename: the file to write to
465        @return: None
466        """
467        if self.recent_items == None:
468            return
469        else:
470            try:
471                file_open = open(filename, 'w')
472                text = ""
473                for key in self.recent_items:
474                    full_path = self.recent_items[key] + key
475                    new_line = full_path + '\n'
476                    text += new_line
477                file_open.write(text)
478                file_open.close()
479                   
480            except IOError:
481                print 'Unable to write the recent files to file: %s\n'\
482                    'No recent files will be written' % str(filename)
483
484    def findLastSlash(self, string):
485        """
486        Find the last slash in string string
487        @type string: string
488        @param string: The string to find the last slash in
489        @return: None
490        """
491        back_num = 1
492        start = len(string)
493        while (True):
494            new_num = start - back_num
495            if (string[new_num] == '/'):
496                last_slash = new_num
497                break
498            else:
499                back_num += 1
500        return last_slash
501
502    def closeEvent(self, event):
503        """
504        Overrides the normal close event so it will ask if you want to save changes etc
505        @type event: QCloseEvent
506        @param event: the event (its provided by the qt system)
507        @return: None
508        """
509        if (self.ui.actionSave.isEnabled()):
510            window = ChangesWindow()
511            ret = window.run()
512            if (ret == QtGui.QMessageBox.Save):
513                self.saveFile()
514                self.writeRecentItems("data/recent_files.txt")
515                event.accept()
516            elif (ret == QtGui.QMessageBox.Discard):
517                self.writeRecentItems("data/recent_files.txt")
518                event.accept()
519            elif (ret == QtGui.QMessageBox.Cancel):
520                event.ignore()
521               
522                       
523    def quit(self, filename):
524        """
525        Quit and then write the recent files to filename and ask about changes
526        @type filename: string
527        @param filename: the file to write to
528        @return: None
529        """
530        self.ui.writingEditor.close()
Note: See TracBrowser for help on using the repository browser.