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

Revision 226, 21.8 KB checked in by bretzel_parpg, 10 years ago (diff)

Started on documentation and added an example script. Also made the editor call the right quit function
when you click the x or hit alt+F4

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