def addContextMenu(self): """ Adds a context menu to the tab bar """ from iep.iepcore.menu import EditorTabContextMenu self._menu = EditorTabContextMenu(self, "EditorTabMenu") self._tabs.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._tabs.customContextMenuRequested.connect(self.contextMenuTriggered)
class EditorTabs(QtGui.QWidget): """ The EditorTabs instance manages the open files and corresponding editors. It does the saving loading etc. """ # Signal to indicate that a breakpoint has changed, emits dict breakPointsChanged = QtCore.Signal(object) # Signal to notify that a different file was selected currentChanged = QtCore.Signal() # Signal to notify that the parser has parsed the text (emit by parser) parserDone = QtCore.Signal() def __init__(self, parent): QtGui.QWidget.__init__(self,parent) # keep a booking of opened directories self._lastpath = '' # keep track of all breakpoints self._breakPoints = {} # create tab widget self._tabs = FileTabWidget(self) self._tabs.tabCloseRequested.connect(self.closeFile) self._tabs.currentChanged.connect(self.onCurrentChanged) # Double clicking a tab saves the file, clicking on the bar opens a new file self._tabs.tabBar().tabDoubleClicked.connect(self.saveFile) self._tabs.tabBar().barDoubleClicked.connect(self.newFile) # Create find/replace widget self._findReplace = FindReplaceWidget(self) # create box layout control and add widgets self._boxLayout = QtGui.QVBoxLayout(self) self._boxLayout.addWidget(self._tabs, 1) self._boxLayout.addWidget(self._findReplace, 0) # spacing of widgets self._boxLayout.setSpacing(0) # apply self.setLayout(self._boxLayout) #self.setAttribute(QtCore.Qt.WA_AlwaysShowToolTips,True) # accept drops self.setAcceptDrops(True) # restore state (call later so that the menu module can bind to the # currentChanged signal first, in order to set tab/indentation # checkmarks appropriately) # todo: Resetting the scrolling would work better if set after # the widgets are properly sized. iep.callLater(self.restoreEditorState) def addContextMenu(self): """ Adds a context menu to the tab bar """ from iep.iepcore.menu import EditorTabContextMenu self._menu = EditorTabContextMenu(self, "EditorTabMenu") self._tabs.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._tabs.customContextMenuRequested.connect(self.contextMenuTriggered) def contextMenuTriggered(self, p): """ Called when context menu is clicked """ # Get index of current tab index = self._tabs.tabBar().tabAt(p) self._menu.setIndex(index) # Show menu if item is available if index >= 0: p = self._tabs.tabBar().tabRect(index).bottomLeft() self._menu.popup(self._tabs.tabBar().mapToGlobal(p)) def onCurrentChanged(self): self.currentChanged.emit() def getCurrentEditor(self): """ Get the currently active editor. """ item = self._tabs.currentItem() if item: return item.editor else: return None def getMainEditor(self): """ Get the editor that represents the main file, or None if there is no main file. """ item = self._tabs.mainItem() if item: return item.editor else: return None def __iter__(self): tmp = [item.editor for item in self._tabs.items()] return tmp.__iter__() def updateBreakPoints(self, editor=None): # Get list of editors to update keypoints for if editor is None: editors = self self._breakPoints = {} # Full reset else: editors = [editor] # Update our keypoints dict for editor in editors: fname = editor._filename or editor._name if not fname: continue linenumbers = editor.breakPoints() if linenumbers: self._breakPoints[fname] = linenumbers else: self._breakPoints.pop(fname, None) # Emit signal so shells can update the kernel self.breakPointsChanged.emit(self._breakPoints) def setDebugLineIndicators(self, *filename_linenr): """ Set the debug line indicator. There is one indicator global to IEP, corresponding to the last shell for which we received the indicator. """ if len(filename_linenr) and filename_linenr[0] is None: filename_linenr = [] # Normalize case filename_linenr = [(os.path.normcase(i[0]), int(i[1])) for i in filename_linenr] for item in self._tabs.items(): # Prepare editor = item._editor fname = editor._filename or editor._name fname = os.path.normcase(fname) # Reset editor.setDebugLineIndicator(None) # Set for filename, linenr in filename_linenr: if fname == filename: active = (filename, linenr) == filename_linenr[-1] editor.setDebugLineIndicator(linenr, active) ## Loading ad saving files def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() def dropEvent(self, event): """ Drop files in the list. """ for qurl in event.mimeData().urls(): path = str( qurl.toLocalFile() ) if os.path.isfile(path): self.loadFile(path) elif os.path.isdir(path): self.loadDir(path) else: pass def newFile(self): """ Create a new (unsaved) file. """ # create editor editor = createEditor(self, None) editor.document().setModified(False) # Start out as OK # add to list item = FileItem(editor) self._tabs.addItem(item) self._tabs.setCurrentItem(item) # set focus to new file editor.setFocus() return item def openFile(self): """ Create a dialog for the user to select a file. """ # determine start dir # todo: better selection of dir, using project manager editor = self.getCurrentEditor() if editor and editor._filename: startdir = os.path.split(editor._filename)[0] else: startdir = self._lastpath if (not startdir) or (not os.path.isdir(startdir)): startdir = '' # show dialog msg = "Select one or more files to open" filter = "Python (*.py *.pyw);;" filter += "Pyrex (*.pyi *.pyx *.pxd);;" filter += "C (*.c *.h *.cpp *.c++);;" #filter += "Py+Cy+C (*.py *.pyw *.pyi *.pyx *.pxd *.c *.h *.cpp);;" filter += "All (*)" if True: filenames = QtGui.QFileDialog.getOpenFileNames(self, msg, startdir, filter) if isinstance(filenames, tuple): # PySide filenames = filenames[0] else: # Example how to preselect files, can be used when the users # opens a file in a project to select all files currently not # loaded. d = QtGui.QFileDialog(self, msg, startdir, filter) d.setFileMode(d.ExistingFiles) d.selectFile('"codeparser.py" "editorStack.py"') d.exec_() if d.result(): filenames = d.selectedFiles() else: filenames = [] # were some selected? if not filenames: return # load for filename in filenames: self.loadFile(filename) def openDir(self): """ Create a dialog for the user to select a directory. """ # determine start dir editor = self.getCurrentEditor() if editor and editor._filename: startdir = os.path.split(editor._filename)[0] else: startdir = self._lastpath if not os.path.isdir(startdir): startdir = '' # show dialog msg = "Select a directory to open" dirname = QtGui.QFileDialog.getExistingDirectory(self, msg, startdir) # was a dir selected? if not dirname: return # load self.loadDir(dirname) def loadFile(self, filename, updateTabs=True): """ Load the specified file. On success returns the item of the file, also if it was already open.""" # Note that by giving the name of a tempfile, we can select that # temp file. # normalize path if filename[0] != '<': filename = normalizePath(filename) if not filename: return None # if the file is already open... for item in self._tabs.items(): if item.id == filename: # id gets _filename or _name for temp files break else: item = None if item: self._tabs.setCurrentItem(item) print("File already open: '{}'".format(filename)) return item # create editor try: editor = createEditor(self, filename) except Exception as err: # Notify in logger print("Error loading file: ", err) # Make sure the user knows m = QtGui.QMessageBox(self) m.setWindowTitle("Error loading file") m.setText(str(err)) m.setIcon(m.Warning) m.exec_() return None # create list item item = FileItem(editor) self._tabs.addItem(item, updateTabs) if updateTabs: self._tabs.setCurrentItem(item) # store the path self._lastpath = os.path.dirname(item.filename) return item def loadDir(self, path): """ Create a project with the dir's name and add all files contained in the directory to it. extensions is a komma separated list of extenstions of files to accept... """ # if the path does not exist, stop path = os.path.abspath(path) if not os.path.isdir(path): print("ERROR loading dir: the specified directory does not exist!") return # get extensions extensions = iep.config.advanced.fileExtensionsToLoadFromDir extensions = extensions.replace(',',' ').replace(';',' ') extensions = ["."+a.lstrip(".").strip() for a in extensions.split(" ")] # init item item = None # open all qualified files... self._tabs.setUpdatesEnabled(False) try: filelist = os.listdir(path) for filename in filelist: filename = os.path.join(path, filename) ext = os.path.splitext(filename)[1] if str(ext) in extensions: item = self.loadFile(filename, False) finally: self._tabs.setUpdatesEnabled(True) self._tabs.updateItems() # return lastopened item return item def saveFileAs(self, editor=None): """ Create a dialog for the user to select a file. returns: True if succesfull, False if fails """ # get editor if editor is None: editor = self.getCurrentEditor() if editor is None: return False # get startdir if editor._filename: startdir = os.path.dirname(editor._filename) else: startdir = self._lastpath # Try the file browser or project manager to suggest a path fileBrowser = iep.toolManager.getTool('iepfilebrowser') projectManager = iep.toolManager.getTool('iepprojectmanager') if fileBrowser: startdir = fileBrowser.getDefaultSavePath() if projectManager and not startdir: startdir = projectManager.getDefaultSavePath() if not os.path.isdir(startdir): startdir = '' # show dialog msg = "Select the file to save to" filter = "Python (*.py *.pyw);;" filter += "Pyrex (*.pyi *.pyx *.pxd);;" filter += "C (*.c *.h *.cpp);;" #filter += "Py+Cy+C (*.py *.pyw *.pyi *.pyx *.pxd *.c *.h *.cpp);;" filter += "All (*.*)" filename = QtGui.QFileDialog.getSaveFileName(self, msg, startdir, filter) if isinstance(filename, tuple): # PySide filename = filename[0] # give python extension if it has no extension head, tail = os.path.split(filename) if tail and '.' not in tail: filename += '.py' # proceed or cancel if filename: return self.saveFile(editor, filename) else: return False # Cancel was pressed def saveFile(self, editor=None, filename=None): """ Save the file. returns: True if succesfull, False if fails """ # get editor if editor is None: editor = self.getCurrentEditor() elif isinstance(editor, int): index = editor editor = None if index>=0: item = self._tabs.items()[index] editor = item.editor if editor is None: return False # get filename if filename is None: filename = editor._filename if not filename: return self.saveFileAs(editor) # let the editor do the low level stuff... try: editor.save(filename) except Exception as err: # Notify in logger print("Error saving file:",err) # Make sure the user knows m = QtGui.QMessageBox(self) m.setWindowTitle("Error saving file") m.setText(str(err)) m.setIcon(m.Warning) m.exec_() # Return now return False # get actual normalized filename filename = editor._filename # notify # TODO: message concerining line endings print("saved file: {} ({})".format(filename, editor.lineEndingsHumanReadable)) self._tabs.updateItems() # todo: this is where we once detected whether the file being saved was a style file. # Notify done return True def saveAllFiles(self): """ Save all files""" for editor in self: self.saveFile(editor) ## Closing files / closing down def askToSaveFileIfDirty(self, editor): """ askToSaveFileIfDirty(editor) If the given file is not saved, pop up a dialog where the user can save the file . Returns 1 if file need not be saved. Returns 2 if file was saved. Returns 3 if user discarded changes. Returns 0 if cancelled. """ # should we ask to save the file? if editor.document().isModified(): # Ask user what to do result = simpleDialog(editor, "Closing", "Save modified file?", ['Discard', 'Cancel', 'Save'], 'Save') result = result.lower() # Get result and act if result == 'save': return 2 if self.saveFile(editor) else 0 elif result == 'discard': return 3 else: # cancel return 0 return 1 def closeFile(self, editor=None): """ Close the selected (or current) editor. Returns same result as askToSaveFileIfDirty() """ # get editor if editor is None: editor = self.getCurrentEditor() item = self._tabs.currentItem() elif isinstance(editor, int): index = editor editor, item = None, None if index>=0: item = self._tabs.items()[index] editor = item.editor else: item = None for i in self._tabs.items(): if i.editor is editor: item = i if editor is None or item is None: return # Ask if dirty result = self.askToSaveFileIfDirty(editor) # Ask if closing pinned file if result and item.pinned: result = simpleDialog(editor, "Closing pinned", "Are you sure you want to close this pinned file?", ['Close', 'Cancel'], 'Cancel') result = result == 'Close' # ok, close... if result: if editor._name.startswith("<tmp"): # Temp file, try to find its index for i in range(len(self._tabs.items())): if self._tabs.getItemAt(i).editor is editor: self._tabs.removeTab(i) break else: self._tabs.removeTab(editor) # Clear any breakpoints that it may have had self.updateBreakPoints() return result def closeAllFiles(self): """Close all files""" for editor in self: self.closeFile(editor) def saveEditorState(self): """ Save the editor's state configuration. """ fr = self._findReplace iep.config.state.find_matchCase = fr._caseCheck.isChecked() iep.config.state.find_regExp = fr._regExp.isChecked() iep.config.state.find_wholeWord = fr._wholeWord.isChecked() iep.config.state.find_show = fr.isVisible() # iep.config.state.editorState2 = self._getCurrentOpenFilesAsSsdfList() def restoreEditorState(self): """ Restore the editor's state configuration. """ # Restore opened editors if iep.config.state.editorState2: self._setCurrentOpenFilesAsSsdfList(iep.config.state.editorState2) else: self.newFile() # The find/replace state is set in the corresponding class during init def _getCurrentOpenFilesAsSsdfList(self): """ Get the state as it currently is as an ssdf list. The state entails all open files and their structure in the projects. The being collapsed of projects and their main files. The position of the cursor in the editors. """ # Init state = [] # Get items for item in self._tabs.items(): # Get editor ed = item.editor if not ed._filename: continue # Init info info = [] # Add filename, line number, and scroll distance info.append(ed._filename) info.append(int(ed.textCursor().position())) info.append(int(ed.verticalScrollBar().value())) # Add whether pinned or main file if item.pinned: info.append('pinned') if item.id == self._tabs._mainFile: info.append('main') # Add to state state.append( tuple(info) ) # Get history history = [item for item in self._tabs._itemHistory] history.reverse() # Last one is current for item in history: if isinstance(item, FileItem): ed = item._editor if ed._filename: state.append( (ed._filename, 'hist') ) # Done return state def _setCurrentOpenFilesAsSsdfList(self, state): """ Set the state of the editor in terms of opened files. The input should be a list object as returned by ._getCurrentOpenFilesAsSsdfList(). """ # Init dict fileItems = {} # Process items for item in state: fname = item[0] if item[1] == 'hist': # select item (to make the history right) if fname in fileItems: self._tabs.setCurrentItem( fileItems[fname] ) elif fname: # a file item, create editor-item and store itm = self.loadFile(fname) fileItems[fname] = itm # set position if itm: try: ed = itm.editor cursor = ed.textCursor() cursor.setPosition(int(item[1])) ed.setTextCursor(cursor) # set scrolling ed.verticalScrollBar().setValue(int(item[2])) #ed.centerCursor() #TODO: this does not work properly yet # set main and/or pinned? if 'main' in item: self._tabs._mainFile = itm.id if 'pinned' in item: itm._pinned = True except Exception as err: print('Could not set position for %s' % fname, err) def closeAll(self): """ Close all files (well technically, we don't really close them, so that they are all stil there when the user presses cancel). Returns False if the user pressed cancel when asked for saving an unsaved file. """ # try closing all editors. for editor in self: result = self.askToSaveFileIfDirty(editor) if not result: return False # we're good to go closing return True
class EditorTabs(QtGui.QWidget): """ The EditorTabs instance manages the open files and corresponding editors. It does the saving loading etc. """ # Signal to notify that a different file was selected currentChanged = QtCore.Signal() # Signal to notify that the parser has parsed the text (emit by parser) parserDone = QtCore.Signal() def __init__(self, parent): QtGui.QWidget.__init__(self,parent) # keep a booking of opened directories self._lastpath = '' # create tab widget self._tabs = FileTabWidget(self) self._tabs.tabCloseRequested.connect(self.closeFile) self._tabs.currentChanged.connect(self.onCurrentChanged) # Double clicking a tab saves the file, clicking on the bar opens a new file self._tabs.tabBar().tabDoubleClicked.connect(self.saveFile) self._tabs.tabBar().barDoubleClicked.connect(self.newFile) # Create find/replace widget self._findReplace = FindReplaceWidget(self) # create box layout control and add widgets self._boxLayout = QtGui.QVBoxLayout(self) self._boxLayout.addWidget(self._tabs, 1) self._boxLayout.addWidget(self._findReplace, 0) # spacing of widgets self._boxLayout.setSpacing(0) # apply self.setLayout(self._boxLayout) #self.setAttribute(QtCore.Qt.WA_AlwaysShowToolTips,True) # accept drops self.setAcceptDrops(True) # restore state (call later so that the menu module can bind to the # currentChanged signal first, in order to set tab/indentation # checkmarks appropriately) # todo: Resetting the scrolling would work better if set after # the widgets are properly sized. iep.callLater(self.restoreEditorState) def addContextMenu(self): """ Adds a context menu to the tab bar """ from iep.iepcore.menu import EditorTabContextMenu self._menu = EditorTabContextMenu(self, "EditorTabMenu") self._tabs.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self._tabs.customContextMenuRequested.connect(self.contextMenuTriggered) def contextMenuTriggered(self, p): """ Called when context menu is clicked """ # Get index of current tab index = self._tabs.tabBar().tabAt(p) self._menu.setIndex(index) # Show menu if item is available if index >= 0: p = self._tabs.tabBar().tabRect(index).bottomLeft() self._menu.popup(self._tabs.tabBar().mapToGlobal(p)) def onCurrentChanged(self): self.currentChanged.emit() def getCurrentEditor(self): """ Get the currently active editor. """ item = self._tabs.currentItem() if item: return item.editor else: return None def getMainEditor(self): """ Get the editor that represents the main file, or None if there is no main file. """ item = self._tabs.mainItem() if item: return item.editor else: return None def __iter__(self): tmp = [item.editor for item in self._tabs.items()] return tmp.__iter__() ## Loading ad saving files def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.acceptProposedAction() def dropEvent(self, event): """ Drop files in the list. """ for qurl in event.mimeData().urls(): path = str( qurl.toLocalFile() ) if os.path.isfile(path): self.loadFile(path) elif os.path.isdir(path): self.loadDir(path) else: pass def newFile(self): """ Create a new (unsaved) file. """ # create editor editor = createEditor(self, None) # add to list item = FileItem(editor) self._tabs.addItem(item) self._tabs.setCurrentItem(item) # set focus to new file editor.setFocus() return item def openFile(self): """ Create a dialog for the user to select a file. """ # determine start dir # todo: better selection of dir, using project manager editor = self.getCurrentEditor() if editor and editor._filename: startdir = os.path.split(editor._filename)[0] else: startdir = self._lastpath if not os.path.isdir(startdir): startdir = '' # show dialog msg = "Select one or more files to open" filter = "Python (*.py *.pyw);;" filter += "Pyrex (*.pyi *.pyx *.pxd);;" filter += "C (*.c *.h *.cpp *.c++);;" #filter += "Py+Cy+C (*.py *.pyw *.pyi *.pyx *.pxd *.c *.h *.cpp);;" filter += "All (*)" if True: filenames = QtGui.QFileDialog.getOpenFileNames(self, msg, startdir, filter) if isinstance(filenames, tuple): # PySide filenames = filenames[0] else: # Example how to preselect files, can be used when the users # opens a file in a project to select all files currently not # loaded. d = QtGui.QFileDialog(self, msg, startdir, filter) d.setFileMode(d.ExistingFiles) d.selectFile('"codeparser.py" "editorStack.py"') d.exec_() if d.result(): filenames = d.selectedFiles() else: filenames = [] # were some selected? if not filenames: return # load for filename in filenames: self.loadFile(filename) def openDir(self): """ Create a dialog for the user to select a directory. """ # determine start dir editor = self.getCurrentEditor() if editor and editor._filename: startdir = os.path.split(editor._filename)[0] else: startdir = self._lastpath if not os.path.isdir(startdir): startdir = '' # show dialog msg = "Select a directory to open" dirname = QtGui.QFileDialog.getExistingDirectory(self, msg, startdir) # was a dir selected? if not dirname: return # load self.loadDir(dirname) def loadFile(self, filename, updateTabs=True): """ Load the specified file. On success returns the item of the file, also if it was already open.""" # Note that by giving the name of a tempfile, we can select that # temp file. # normalize path if filename[0] != '<': filename = normalizePath(filename) if not filename: return None # if the file is already open... for item in self._tabs.items(): if item.id == filename: # id gets _filename or _name for temp files break else: item = None if item: self._tabs.setCurrentItem(item) print("File already open: '{}'".format(filename)) return item # create editor try: editor = createEditor(self, filename) except Exception as err: # Notify in logger print("Error loading file: ", err) # Make sure the user knows m = QtGui.QMessageBox(self) m.setWindowTitle("Error loading file") m.setText(str(err)) m.setIcon(m.Warning) m.exec_() return None # create list item item = FileItem(editor) self._tabs.addItem(item, updateTabs) if updateTabs: self._tabs.setCurrentItem(item) # store the path self._lastpath = os.path.dirname(item.filename) return item def loadDir(self, path): """ Create a project with the dir's name and add all files contained in the directory to it. extensions is a komma separated list of extenstions of files to accept... """ # if the path does not exist, stop path = os.path.abspath(path) if not os.path.isdir(path): print("ERROR loading dir: the specified directory does not exist!") return # get extensions extensions = iep.config.advanced.fileExtensionsToLoadFromDir extensions = extensions.replace(',',' ').replace(';',' ') extensions = ["."+a.lstrip(".").strip() for a in extensions.split(" ")] # init item item = None # open all qualified files... self._tabs.setUpdatesEnabled(False) try: filelist = os.listdir(path) for filename in filelist: filename = os.path.join(path, filename) ext = os.path.splitext(filename)[1] if str(ext) in extensions: item = self.loadFile(filename, False) finally: self._tabs.setUpdatesEnabled(True) self._tabs.updateItems() # return lastopened item return item def saveFileAs(self, editor=None): """ Create a dialog for the user to select a file. returns: True if succesfull, False if fails """ # get editor if editor is None: editor = self.getCurrentEditor() if editor is None: return False # get startdir if editor._filename: startdir = os.path.dirname(editor._filename) else: startdir = self._lastpath # Try the file browser or project manager to suggest a path fileBrowser = iep.toolManager.getTool('iepfilebrowser') projectManager = iep.toolManager.getTool('iepprojectmanager') if fileBrowser: startdir = fileBrowser.getDefaultSavePath() if projectManager and not startdir: startdir = projectManager.getDefaultSavePath() if not os.path.isdir(startdir): startdir = '' # show dialog msg = "Select the file to save to" filter = "Python (*.py *.pyw);;" filter += "Pyrex (*.pyi *.pyx *.pxd);;" filter += "C (*.c *.h *.cpp);;" #filter += "Py+Cy+C (*.py *.pyw *.pyi *.pyx *.pxd *.c *.h *.cpp);;" filter += "All (*.*)" filename = QtGui.QFileDialog.getSaveFileName(self, msg, startdir, filter) if isinstance(filename, tuple): # PySide filename = filename[0] # give python extension if it has no extension head, tail = os.path.split(filename) if tail and '.' not in tail: filename += '.py' # proceed or cancel if filename: return self.saveFile(editor, filename) else: return False # Cancel was pressed def saveFile(self, editor=None, filename=None): """ Save the file. returns: True if succesfull, False if fails """ # get editor if editor is None: editor = self.getCurrentEditor() elif isinstance(editor, int): index = editor editor = None if index>=0: item = self._tabs.items()[index] editor = item.editor if editor is None: return False # get filename if filename is None: filename = editor._filename if not filename: return self.saveFileAs(editor) # let the editor do the low level stuff... try: editor.save(filename) except Exception as err: # Notify in logger print("Error saving file:",err) # Make sure the user knows m = QtGui.QMessageBox(self) m.setWindowTitle("Error saving file") m.setText(str(err)) m.setIcon(m.Warning) m.exec_() # Return now return False # get actual normalized filename filename = editor._filename # notify # TODO: message concerining line endings print("saved file: {} ({})".format(filename, editor.lineEndingsHumanReadable)) self._tabs.updateItems() # todo: this is where we once detected whether the file being saved was a style file. # Notify done return True def saveAllFiles(self): """ Save all files""" for editor in self: self.saveFile(editor) ## Closing files / closing down def askToSaveFileIfDirty(self, editor): """ askToSaveFileIfDirty(editor) If the given file is not saved, pop up a dialog where the user can save the file . Returns 1 if file need not be saved. Returns 2 if file was saved. Returns 3 if user discarded changes. Returns 0 if cancelled. """ # should we ask to save the file? if editor.document().isModified(): # Ask user what to do result = simpleDialog(editor, "Closing", "Save modified file?", ['Discard', 'Cancel', 'Save'], 'Save') result = result.lower() # Get result and act if result == 'save': return 2 if self.saveFile(editor) else 0 elif result == 'discard': return 3 else: # cancel return 0 return 1 def closeFile(self, editor=None): """ Close the selected (or current) editor. Returns same result as askToSaveFileIfDirty() """ # get editor if editor is None: editor = self.getCurrentEditor() item = self._tabs.currentItem() elif isinstance(editor, int): index = editor editor, item = None, None if index>=0: item = self._tabs.items()[index] editor = item.editor else: item = None for i in self._tabs.items(): if i.editor is editor: item = i if editor is None or item is None: return # Ask if dirty result = self.askToSaveFileIfDirty(editor) # Ask if closing pinned file if result and item.pinned: result = simpleDialog(editor, "Closing pinned", "Are you sure you want to close this pinned file?", ['Close', 'Cancel'], 'Cancel') result = result == 'Close' # ok, close... if result: if editor._name.startswith("<tmp"): # Temp file, try to find its index for i in range(len(self._tabs.items())): if self._tabs.getItemAt(i).editor is editor: self._tabs.removeTab(i) break else: self._tabs.removeTab(editor) return result def closeAllFiles(self): """Close all files""" for editor in self: self.closeFile(editor) def saveEditorState(self): """ Save the editor's state configuration. """ fr = self._findReplace iep.config.state.find_matchCase = fr._caseCheck.isChecked() iep.config.state.find_regExp = fr._regExp.isChecked() iep.config.state.find_wholeWord = fr._wholeWord.isChecked() iep.config.state.find_show = fr.isVisible() # iep.config.state.editorState2 = self._getCurrentOpenFilesAsSsdfList() def restoreEditorState(self): """ Restore the editor's state configuration. """ # Restore opened editors if iep.config.state.editorState2: self._setCurrentOpenFilesAsSsdfList(iep.config.state.editorState2) else: self.newFile() # The find/replace state is set in the corresponding class during init def _getCurrentOpenFilesAsSsdfList(self): """ Get the state as it currently is as an ssdf list. The state entails all open files and their structure in the projects. The being collapsed of projects and their main files. The position of the cursor in the editors. """ # Init state = [] # Get items for item in self._tabs.items(): # Get editor ed = item.editor if not ed._filename: continue # Init info info = [] # Add filename, line number, and scroll distance info.append(ed._filename) info.append(int(ed.textCursor().position())) info.append(int(ed.verticalScrollBar().value())) # Add whether pinned or main file if item.pinned: info.append('pinned') if item.id == self._tabs._mainFile: info.append('main') # Add to state state.append( tuple(info) ) # Get history history = [item for item in self._tabs._itemHistory] history.reverse() # Last one is current for item in history: if isinstance(item, FileItem): ed = item._editor if ed._filename: state.append( (ed._filename, 'hist') ) # Done return state def _setCurrentOpenFilesAsSsdfList(self, state): """ Set the state of the editor in terms of opened files. The input should be a list object as returned by ._getCurrentOpenFilesAsSsdfList(). """ # Init dict fileItems = {} # Process items for item in state: fname = item[0] if item[1] == 'hist': # select item (to make the history right) if fname in fileItems: self._tabs.setCurrentItem( fileItems[fname] ) elif fname: # a file item, create editor-item and store itm = self.loadFile(fname) fileItems[fname] = itm # set position if itm: try: ed = itm.editor cursor = ed.textCursor() cursor.setPosition(int(item[1])) ed.setTextCursor(cursor) # set scrolling ed.verticalScrollBar().setValue(int(item[2])) #ed.centerCursor() #TODO: this does not work properly yet # set main and/or pinned? if 'main' in item: self._tabs._mainFile = itm.id if 'pinned' in item: itm._pinned = True except Exception as err: print('Could not set position for %s' % fname, err) def closeAll(self): """ Close all files (well technically, we don't really close them, so that they are all stil there when the user presses cancel). Returns False if the user pressed cancel when asked for saving an unsaved file. """ # try closing all editors. for editor in self: result = self.askToSaveFileIfDirty(editor) if not result: return False # we're good to go closing return True