12 """ Base class for all tab controllers.
14 Tab controllers control the functionality of plugin tabs.
17 TAB_LABEL_MAX_LENGTH = 20
20 QObject.__init__(self)
21 logging.debug(__name__ +
": __init__")
40 """ Static function returning all filetypes the tab controller can handle.
42 Sub classes should reimplement this function. It returns a list with 2-tuples of the following form:
43 ('extension', 'description of file type').
46 staticSupportedFileTypes = staticmethod(staticSupportedFileTypes)
49 """ Returns staticSupportedFileTypes() of the class to which this object belongs.
51 return self.__class__.staticSupportedFileTypes()
54 supportedFileTypes = self.__class__.staticSupportedFileTypes()
55 return ";;".
join([
Filetype(t[0], t[1]).fileDialogFilter()
for t
in supportedFileTypes])
58 """ Returns the plugin reference, set by setPlugin().
80 """ Returns filename of this tab.
85 """ Returns the basename of this tab's filename.
87 Part of filename after last /.
92 """ Sets a flag indicating whether this tab can handle copy and paste events.
94 See also isCopyPasteEnabled(), cut(), copy(), paste().
99 """ Return True if the copyPasteFlag is set.
101 See setCopyPasteEnabled(), cut(), copy(), paste().
106 """Sets a flag indicating whether this tab can handle find requests.
108 See isFindEnabled(), find().
113 """Returns True if findEnabledFlag is set.
115 See setFindEnabled(), find().
120 """ Sets the text of the tab to filename if it is set. If
122 is not an emty string, it is used instead of the filename.
124 Otherwise it is set to 'UNTITLED'. It also evaluates the fileModifiedFlag and indicates changes with an *.
129 ext = os.path.splitext(title)[1].lower().strip(
".")
131 elif titletext ==
"":
141 if self.
tab().tabWidget():
142 self.
tab().tabWidget().setTabText(self.
tab().tabWidget().indexOf(self.
tab()), title)
144 self.
tab().setWindowTitle(title)
147 """ Sets the file Modified flag to True or False.
149 This affects the closing of this tab.
150 It is only possible to set the modification flag to True if the controller is editable (see isEditable()).
160 if self.
tab().mainWindow():
161 self.
plugin().application().updateMenuAndWindowTitle()
163 logging.info(self.__class__.__name__ +
": setModified() - Cannot tell application the modification state: There is no application associated with the tab.")
166 """ Evaluates the file Modified flag. Always returns True if no filename is set.
171 """ Sets the file Editable flag.
175 self.
plugin().application().updateMenu()
178 """ Evaluates the file Editable flag.
183 """ Sets the allowSelectAll flag.
186 self.
plugin().application().updateMenu()
189 """ Evaluates the sllowSelectAll flag.
193 def open(self, filename=None, update=True):
196 logging.debug(self.__class__.__name__ +
": open()")
198 statusMessage = self.
plugin().application().startWorking(
"Opening file " + filename)
204 self.
plugin().application().stopWorking(statusMessage,
"failed")
212 self.
plugin().application().stopWorking(statusMessage)
215 self.
plugin().application().stopWorking(statusMessage,
"failed")
220 This function performs the actual reading of a file. It should be overwritten by any PluginTab which inherits Tab.
221 If the reading was successful True should be returned.
222 The file should be read from the file given in the argument filename not to the one in self._filename.
224 raise NotImplementedError
227 """ Takes care the tab's data will be written to the file given as argument or if this is an empty string to self._filename.
229 Whenever the content of the tab should be saved, this method should be called. If no filename is specified nor already set set it asks the user to set one.
230 Afterwards the writing is initiated by calling writeFile().
238 return self.
plugin().application().saveFileAsDialog()
240 statusMessage = self.
plugin().application().startWorking(
"Saving file " + filename)
246 except Exception
as e:
253 self.
plugin().application().addRecentFile(filename)
254 self.
plugin().application().updateMenuAndWindowTitle()
260 lastSavedStateEvent =
None
263 self.
plugin().application().stopWorking(statusMessage)
266 QMessageBox.critical(self.
tab().mainWindow(),
'Error while saving data',
'Could not write to file ' + filename +
'.'+message)
267 logging.error(self.__class__.__name__ +
": save() : Could not write to file " + filename +
'.'+message)
268 self.
plugin().application().stopWorking(statusMessage,
"failed")
273 messageResult = self.
plugin().application().showMessageBox(
"The document has been modified.",
274 "Do you want to save your changes?",
275 QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
278 if messageResult == QMessageBox.Save:
281 elif messageResult == QMessageBox.Cancel:
286 """ Asks user if he wants to save potentially unsaved data and closes the tab.
288 This function usually does not need to be overwritten by a PluginTab.
292 if self.
tab().tabWidget():
293 self.
tab().tabWidget().removeTab(self.
tab().tabWidget().indexOf(self.
tab()))
304 This function performs the actual writing / saving of a file. It should be overwritten by any PluginTab which inherits Tab.
305 If the writing was successful True should be returned.
306 The file should be written to the file given in the argument filename not to the one in self._filename.
307 These variables may differ in case the user selects "save as..." and picks a new filename on a file which already has a name set.
308 If writing was successful the self._filename variable will then be set to the value of filename.
310 raise NotImplementedError
313 """ Compares the actual modification timestamp of self.filename() to the modification at opening or last save operation.
315 This function is called by Application when the tab associated with this controller was activated.
316 If modification timestamps differ the refresh() method is called.
323 logging.debug(self.__class__.__name__ +
": checkModificationTimestamp() - File was removed.")
325 msgBox = QMessageBox()
326 msgBox.setText(
"The file was removed.")
328 msgBox.setInformativeText(
"Do you want to save the file with your version?")
329 saveButton = msgBox.addButton(
"Save", QMessageBox.ActionRole)
330 ignoreButton = msgBox.addButton(
"Ignore", QMessageBox.RejectRole)
332 ignoreButton = msgBox.addButton(
"OK", QMessageBox.RejectRole)
336 logging.debug(self.__class__.__name__ +
": checkModificationTimestamp() - File was modified.")
337 msgBox = QMessageBox()
338 msgBox.setText(
"The file has been modified.")
340 msgBox.setInformativeText(
"Do you want to overwrite the file with your version or reload the file?")
341 saveButton = msgBox.addButton(
"Overwrite", QMessageBox.ActionRole)
343 msgBox.setInformativeText(
"Do you want to reload the file?")
344 reloadButton = msgBox.addButton(
"Reload", QMessageBox.DestructiveRole)
345 ignoreButton = msgBox.addButton(
"Ignore", QMessageBox.RejectRole)
353 if self.
isEditable()
and msgBox.clickedButton() == saveButton:
355 elif msgBox.clickedButton() == reloadButton:
358 elif msgBox.clickedButton() == ignoreButton
and os.path.exists(self.
_filename):
366 """ Called by application when tab is activated in tabWidget.
368 This function should be overwritten if special treatment on tab selection is required.
373 """ Handle cut event.
375 This function is called if the user selects 'Cut' from menu. PluginTabs should override it if needed.
376 See also setCopyPasteEnabled(), isCopyPasteEnabled().
378 raise NotImplementedError
381 """ Handle copy event.
383 This function is called if the user selects 'Copy' from menu. PluginTabs should override it if needed.
384 See also setCopyPasteEnabled(), isCopyPasteEnabled().
386 raise NotImplementedError
389 """ Handle paste event.
391 This function is called if the user selects 'Paste' from menu. PluginTabs should override it if needed."
392 See also setCopyPasteEnabled(), isCopyPasteEnabled().
394 raise NotImplementedError
397 """ Handle find event.
399 This function is called if the user selects 'Find' from menu. PluginTabs should override it if needed."
400 See also setFindEnabled(), isFindEnabled().
402 raise NotImplementedError
405 """ Handle to perform select all action.
407 This function is called if the user selects 'Select all' from menu. PluginTabs should override it if needed."
408 See also setAllowSelectAll(), allowSelectAll().
410 raise NotImplementedError
413 """ This function has to be implemented by tab controllers who want to use the zoom toolbar.
415 The implementation has to forward the zoom value to the Zoomable object for which the toolbar is set up.
418 raise NotImplementedError
421 """ This function has to be implemented by tab controllers who want to use the zoom toolbar.
423 The implementation should return the zoom value of the Zoomable object for which the toolbar is set up.
426 raise NotImplementedError
429 """ Shows zoom value on main window's status bar.
431 self.
tab().mainWindow().statusBar().showMessage(
"Zoom " + str(round(zoom)) +
" %")
434 """ Sets the zoom button pressed before flag to False.
436 If the flag is set functions handling the zoom toolbar buttons (zoomHundred(), zoomAll()) wont store the last zoom factor. The flag is set to true by these functions.
437 By this mechanism the user can click the zoom buttons several times and will still be able to return to his orignal zoom level by zoomUser().
438 The reset function needs to be called if the user manually sets the zoom level. For instance by connecting this function to the wheelEvent of the workspace scroll area.
443 """ Returns to the manually set zoom factor before zoomHundred() or zoomAll() were called.
445 logging.debug(__name__ +
": zoomUser()")
449 """ Sets zoom factor to 100 %.
451 logging.debug(__name__ +
": zoomHundred()")
458 """ Zooms workspace content to fit optimal.
460 Currently only works if scroll area is used and accessible through self.tab().scrollArea().
462 logging.debug(__name__ +
": zoomAll()")
467 viewportWidth = self.
tab().scrollArea().viewport().
width()
468 viewportHeight = self.
tab().scrollArea().viewport().height()
470 for i
in range(0, 2):
472 workspaceChildrenRect = self.
tab().scrollArea().widget().childrenRect()
473 widthRatio = self.
zoom() * viewportWidth / (workspaceChildrenRect.right())
474 heightRatio = self.
zoom() * viewportHeight / (workspaceChildrenRect.bottom())
476 if widthRatio > heightRatio:
481 self.
setZoom(math.floor(ratio))
484 """ Called after file is loaded.
486 Meant to update to Tab content.
488 raise NotImplementedError
491 """ Reloads file content and refreshes tab.
493 May be implemented by inheriting controllers.
495 statusMessage = self.
plugin().application().startWorking(
"Reopening file")
499 self.
plugin().application().stopWorking(statusMessage)
502 if hasattr(QInputDialog,
"getInteger"):
504 (zoom, ok) = QInputDialog.getInteger(self.
tab(),
"Zoom...",
"Input zoom factor in percent:", self.
zoom(), 0)
507 (zoom, ok) = QInputDialog.getInt(self.
tab(),
"Zoom...",
"Input zoom factor in percent:", self.
zoom(), 0)
513 """ Cancel all operations in tab.
515 This function is called when all current operations in tab shall be canceled.
520 """ Returns True if the this tab controller supports undo history.
522 return self._supportsUndo
525 """ If enable is True this controller enables its undo function.
527 For any tab controller that wants to use this feature, it needs to be made sure the corresponding UndoEvents
528 for actions that should be undoable exists and are added by addUndoEvent().
533 """ Returns list of all registered UndoEvents.
538 """ Returns list of all UndoEvents that have already been undone before.
543 """ Adds event of type UndoEvent to this tab controller's list of undoable events.
545 Undo can be invoked by calling undo().
548 if not isinstance(undoEvent, UndoEvent):
549 logging.error(
"%s: Tried to add non-UndoEvent to list of undo events. Aborting..." % self.__class__.__name__)
555 self._undoEvents.append(undoEvent)
558 self.
plugin().application().updateMenu()
560 logging.warning(
"%s: Tried to add undo event, however undo functionality is not enabled. Aborting..." % self.__class__.__name__)
563 def undo(self, numberOfEvents=1):
564 """ Invokes undo of last stored UndoEvent (see addUndoEvent()), if undo functionality is enabled.
567 logging.warning(self.__class__.__name__ +
": Tried to undo action, however undo functionality is not enabled. Aborting...")
569 logging.debug(self.__class__.__name__ +
": undo("+ str(numberOfEvents) +
")")
572 for i
in range(0, numberOfEvents):
574 lastEvent = self._undoEvents.pop()
576 self._redoEvents.append(lastEvent)
579 if i == (numberOfEvents -1):
580 if lastEvent.isLastSavedState():
584 self.
plugin().application().updateMenu()
588 def redo(self, numberOfEvents=1):
590 logging.warning(self.__class__.__name__ +
": Tried to undo action, however undo functionality is not enabled. Aborting...")
592 logging.debug(self.__class__.__name__ +
": redo("+ str(numberOfEvents) +
")")
595 for i
in range(0, numberOfEvents):
597 lastEvent = self._redoEvents.pop()
599 self._undoEvents.append(lastEvent)
602 if i == (numberOfEvents -1):
604 if undo_events_count > 0:
605 if self.
_redoEvents[undo_events_count-1].isLastSavedState():
616 if event.isLastSavedState():
620 self.
plugin().application().updateMenu()
623 """ Sets last saved state flag of given UndoEvent to True and to False for all other events.
626 if current_event == undoEvent:
627 current_event.setLastSavedState(
True)
629 current_event.setLastSavedState(
False)
def checkModificationTimestamp
def staticSupportedFileTypes
def resetZoomButtonPressedBefore
_showingModifiedMessageFlag
_zoomButtonPressedBeforeFlag
static std::string join(char **cmd)
_fileModifcationTimestamp
def setLastSavedStateEvent