Example #1
0
 def __init__(self, gui):
     """Connect up signals to slots and initialize the editor.
     """
     self.db = None          # indicates 'not initialized'
     self.gui = gui
     self.syncHandler = None # holds a GuiSync object, if used
     slot("classChanged", self.slot_classChanged)
     slot("subjectChanged", self.slot_subjectChanged)
     slot("pupilChanged", self.slot_pupilChanged)
     slot("previous", self.slot_previous)
     slot("next", self.slot_next)
     slot("copy", self.slot_copy)
     slot("cut", self.slot_cut)
     slot("paste", self.slot_paste)
     slot("style", self.slot_style)
     slot("indent", self.slot_indent)
     slot("openDB", self.slot_open)
     slot("sync", self.slot_sync)
     slot("undo", self.slot_undo)
     slot("redo", self.slot_redo)
     slot("textChanged", self.slot_clearRedo)
     slot("requestCheck", self.slot_requestCheck)
     slot("print", self.slot_printSubject)
     slot("checkSpelling", self.slot_checkSpelling)
     slot("endCheckSpelling", self.slot_endCheckSpelling)
     slot("nextUnfinished", self.slot_nextUnfinished)
     slot("currentStyle", self.gui.setStyleButton)
     self.canvas = gui.getGView()
     self.editor = Editor(self.canvas)
     spellCheck.spellInit()
     if (self.gui.settings.getSetting("autospellcheck") != "0"):
         self.gui.setAutoSpellCheck()
         __builtin__.autoSpellCheck = True
     slot("autospellcheck", self.slot_autospellcheck)
Example #2
0
class EditorView:
    """There may be only one instance of this class, because of the
    slot declarations. At present there is no possibility of closing
    an editor and reopening it in the same process - also guiEditor.py
    and edit.py would need modifying.
    """
    def __init__(self, gui):
        """Connect up signals to slots and initialize the editor.
        """
        self.db = None          # indicates 'not initialized'
        self.gui = gui
        self.syncHandler = None # holds a GuiSync object, if used
        slot("classChanged", self.slot_classChanged)
        slot("subjectChanged", self.slot_subjectChanged)
        slot("pupilChanged", self.slot_pupilChanged)
        slot("previous", self.slot_previous)
        slot("next", self.slot_next)
        slot("copy", self.slot_copy)
        slot("cut", self.slot_cut)
        slot("paste", self.slot_paste)
        slot("style", self.slot_style)
        slot("indent", self.slot_indent)
        slot("openDB", self.slot_open)
        slot("sync", self.slot_sync)
        slot("undo", self.slot_undo)
        slot("redo", self.slot_redo)
        slot("textChanged", self.slot_clearRedo)
        slot("requestCheck", self.slot_requestCheck)
        slot("print", self.slot_printSubject)
        slot("checkSpelling", self.slot_checkSpelling)
        slot("endCheckSpelling", self.slot_endCheckSpelling)
        slot("nextUnfinished", self.slot_nextUnfinished)
        slot("currentStyle", self.gui.setStyleButton)
        self.canvas = gui.getGView()
        self.editor = Editor(self.canvas)
        spellCheck.spellInit()
        if (self.gui.settings.getSetting("autospellcheck") != "0"):
            self.gui.setAutoSpellCheck()
            __builtin__.autoSpellCheck = True
        slot("autospellcheck", self.slot_autospellcheck)

    def slot_sync(self, arg):
        """Synchronize the present database file with the master
        database.
        """
        if not self.db:
            warning(_("No database file open"))
            return

        self.slot_pupilChanged(-1)      # save current report
        filename = self.db.descriptor
        self.db.close()

        if self.syncHandler:
            self.syncHandler.init(filename)
        else:
            self.syncHandler = GuiSync("sync", filename)
        # The sync dialog needs to be modal!
        self.syncHandler.run()

        #When finished:
        self.slot_open(False)

    def slot_open(self, force):
        """Open a database file. If 'force' is not True try to get the
        last used one from the settings mechanism.
        Return True if successful.
        """
        if self.db:
            self.slot_pupilChanged(-1)      # save current report

        while True:
            db = selectDBFile(self.gui.settings, force)
            if db:
                # Clear revisions dictionary (for undo/redo)
                self.revisionDict = {}
                self.db = db
                try:
                    if self.initialize():
                        self.gui.showDbFile(self.db.descriptor)
                        # Show teacher's name
                        self.gui.showTeacher(self.db.getTeacherName(
                                self.db.owner))
                        return True
                except:
                    warning(_("Database file '%s' contains invalid data")
                            % self.db.descriptor)
                    force = True
                    self.db = None
                    continue
            break
        if self.db:
            return False
        signal("quit")

    def initialize(self):
        """Return True if successfully initialized.
        """
        # Initially there is no RSubject object (current report text)
        self.rsubject = None

        if not self.db.owner:
            warning(_("This is a master database - editing is not permitted"))
            return False

        # Adjust characters for spell-checking etc.
        extraChars = self.db.baseDict[u"extraChars"]
        if not (u"." in extraChars):
            extraChars = u"."
        extraLetters = extraChars.split(u"ยท")[0]
        checkSpelling.setReP(self.db.baseDict[u"dictionary"], extraLetters)
        self.gui.setExtraChars(extraChars.split(u".")[1])

        # Get lists of pupils for each of the subjects for each of
        # the classes - a nested dictionary structure
        self.reportData = self.db.getReportData()

        # Get a list of class object relevant to the 'owner'
        # First get a list of the class tags
        tclasses =  self.reportData.keys()
        # But that may be in the wrong order, so use self.db.classes
        # to sort them:
        self.classes = [c for c in self.db.classes if c.classTag in tclasses]

        # Set the entries in the class combo box
        # A list of class names is needed
        classNames = [c.className for c in self.classes]

        lastClass = self.db.getConfig(u"class")
        try:
            x = classNames.index(lastClass)
        except:
            x = 0
        self.disable = True
        self.gui.setClasses(classNames)
        # That should cause a classChanged signal to be emitted (0 index),
        # which will be ignored (self.disable)

        self.disable = False
        self.gui.setClass(x)
        # That should cause a classChanged signal to be emitted (index x)
        return True

    def slot_classChanged(self, i):
        """Act upon change of class.

        It renews the list of subjects in the subject comboBox,
        which causes a subjectChanged signal to be emitted.
        The config variable currentClass is set to the new name.

        If there was already a subject selected, try to return to
        the same one, otherwise the first in the list.
        """
        if self.disable or (i < 0): return
        self.slot_pupilChanged(-1)      # save current report

        # Set the current class
        self.currentClassObj = self.classes[i]
        self.db.setClass(self.currentClassObj)

        # Get subjects available for current class
        self.subjectsDict = self.reportData[self.currentClassObj.classTag]

        # Make an ordered list of subjects
        self.subjects = [u"%s (%s)" % (self.db.getSubjectName(s), s)
                for s in self.subjectsDict.keys()]
        self.subjects.sort()

        # Set the entries in the subject combo box
        try:
            x = self.subjects.index(self.db.getConfig(u"subject"))
        except:
            x = 0
        self.disable = True
        self.gui.setSubjects(self.subjects)
        # That should cause a subjectChanged signal to be emitted (0 index),
        # which will be ignored (self.disable)

        self.disable = False
        self.gui.setSubject(x)
        # That should cause a subjectChanged signal to be emitted (index x)

    def slot_subjectChanged(self, i):
        """Act upon change of subject.

        It renews the list of pupils in the comboBox_pupil, which causes
        a pupilChanged signal to be emitted.
        The config variable currentSubject is set to the new name.

        If there was already a pupil selected, try to return to the same one,
        otherwise the first in the list.
        """
        if self.disable or (i < 0): return
        self.slot_pupilChanged(-1)      # save current report

        self.currentSubjectNameId = self.subjects[i]

        # Extract the subject name and tag from self.currentSubjectNameId
        sName, sTag = self.currentSubjectNameId.rsplit(None, 1)
        self.subject = sTag.strip(u"()")

        # Get a list of pupils who take this subject
        pupiltags = self.subjectsDict[self.subject]

        # Order the list and get the names of the pupils for the display
        self.pupils = []        # a list of pupil tags
        self.pupilList = []     # a list of pupil names, for the gui
        for pname, ptag in self.currentClassObj.orderedPupilList:
            if ptag in pupiltags:
                self.pupils.append(ptag)
                self.pupilList.append(pname)

        # See if previously selected pupil is in list
        try:
            x = self.pupils.index(self.db.getConfig(u"pupilId"))
        except:
            x = 0

        # set pupil combobox list:
        self.disable = True
        self.gui.setPupils(self.pupilList)
        # That should cause a pupilChanged signal to be emitted (0 index),
        # which will be ignored (self.disable)

        self.disable = False
        self.gui.setPupil(x)
        # That should cause a pupilChanged signal to be emitted (index ix)

    def slot_pupilChanged(self, i):
        """Act upon change of pupil.

        If the edit-text has been modified, that is saved.
        The edit window is then cleared and the text for the new pupil
        is added.
        The config variable currentPupilId is set to the new id.
        """

        # Save old report text, if it has been modified!
        if self.rsubject:
            self.save()
            self.rsubject = None

        if (i < 0) or self.disable: return

        # Remember the index of the current pupil in the combobox for
        # the previous/next buttons
        self.pupilIx = i

        self.currentPupilId = self.pupils[i]
        self.db.setConfig(u"class", self.currentClassObj.className)
        self.db.setConfig(u"subject", self.currentSubjectNameId)
        self.db.setConfig(u"pupilId", self.currentPupilId)

        # Get an RSubject object for this report
        self.rsubject = RSubject(self.currentPupilId, self.subject,
                self.db, self.revisionDict)

        # Set up the editor
        self.editor.init(self.rsubject)

        # set the 'finished' state
        self.setFinishedButton(self.rsubject.isFinished())

        # initialize the undo/redo facility
        self.initUndo()

        # Give 'focus' to the editor, so that it receives keyboard input.
        self.gui.focusEditor()

    def save(self):
        """If the report has been changed, save the new version to
        the database.
        """
        self.editor.saveText()

    def slot_previous(self, arg):
        """Handler for 'previous pupil' button
        """
        if (self.pupilIx == 0):
            # Give 'focus' back to the editor.
            self.gui.focusEditor()
            return
        self.gui.setPupil(self.pupilIx - 1)
        # That should cause a pupilChanged signal to be emitted

    def slot_next(self, arg):
        """Handler for 'next pupil' button
        """
        ix = self.pupilIx + 1
        if (ix >= len(self.pupils)):
            # Give 'focus' back to the editor.
            self.gui.focusEditor()
            return
        self.gui.setPupil(ix)

    def slot_copy(self, arg):
        """Handler for 'copy' action
        """
        text = self.editor.getMarked()
        ###print "COPY $%s$" % text
        self.gui.setClipboard(text)

    def slot_cut(self, arg):
        """Handler for 'cut' action
        """
        self.slot_copy(None)
        self.editor.delete()

    def slot_paste(self, arg):
        """Handler for 'paste' action
        """
        # Get text and insert as block
        self.editor.insertBlock(self.gui.getClipboard())

    def slot_style(self, style):
        """Handler for 'style' action - change style:
        u"n"/normal, u"l"/special, u"r"/special/right-aligned
        """
        selection = self.editor.selection
        if selection.isSelection():
            sm1, sm2 = selection.order()
            tl = sm1[0].tline   # first selected line
            self.rsubject.restyle(tl, sm2[0].tline.para, style)
            selection.markSelection()
        else:
            word, cx = self.editor.edCursor.getPos()
            tl =  word.tline
            self.rsubject.restyle(tl, tl.para, style)
            self.editor.edCursor.setPos(word, cx)

    def slot_indent(self, arg):
        """Handler for 'indent' action - only valid in special style.
        Change the indentation of a 'special' line.
        If there is a selection and the first line is 'special, all
        selected lines with the same alignement will get the same
        indentation as the first.
        If the first line is not special, the command will be ignored.
        """
        if arg:
            step = IndentStep
        else:
            step = -IndentStep

        mainWidth = self.rsubject.db.layoutInfo.mainWidth
        selection = self.editor.selection
        if selection.isSelection():
            sm1, sm2 = selection.order()
            # First selected paragraph
            tl = sm1[0].tline
            p = tl.para
            if (p.align == u"n"): return
            if (step > 0) and (p.indent >= mainWidth/3): return
            p.indent += step
            if (p.indent < 0.0): p.indent = 0.0

            # Repeat for all selected lines
            while (tl != sm2[0].tline):
                tl = self.rsubject.nextLine(tl)
                tl.y = None         # to ensure re-rendering
                if (tl.para.align == p.align):
                    tl.para.indent = p.indent
            self.rsubject.linify(sm1[0].tline)
            selection.markSelection()
        else:
            word, cx = self.editor.edCursor.getPos()
            p = word.tline.para
            if (p.align == u"n"): return
            if (step > 0) and (p.indent >= mainWidth/3): return
            p.indent += step
            if (p.indent < 0.0): p.indent = 0.0
            self.rsubject.linify(word.tline)
            self.editor.edCursor.setPos(word, cx)

    def slot_undo(self, arg):
        if (self.undoLevel == 0):
            # Save the current text
            self.save()
        self.undoLevel += 1
        self.setFinishedButton(self.rsubject.undo(self.undoLevel))
        self.editor.initCurSel()
        if (self.rsubject.nVersions() <= (self.undoLevel+1)):
            self.gui.enableUndo(False)
        self.gui.enableRedo(True)

    def slot_redo(self, arg):
        self.undoLevel -= 1
        if (self.undoLevel == 0):
            self.gui.enableRedo(False)
        self.setFinishedButton(self.rsubject.undo(self.undoLevel))
        self.editor.initCurSel()
        self.gui.enableUndo(True)

    def initUndo(self):
        """Initialize the undo/redo mechanism.
        """
        self.undoLevel = 0
        self.gui.enableRedo(False)
        self.gui.enableUndo(self.rsubject.nVersions() > 1)
        # Counter for delete operations:
        self.editor.deleteCount = 0

    def slot_clearRedo(self, arg):
        """Called when the text has been altered, thus making a 'redo'
        impossible.
        """
        if (self.undoLevel != 0):
            self.undoLevel = 0
            self.gui.enableRedo(False)
        self.gui.enableUndo(True)

    def setFinishedButton(self, down):
        # A flag (self.finishedButtonDisable) is kept to stop a vicious circle!
        self.finishedButtonDisable = True
        self.gui.activateFinished(down)
        self.finishedButtonDisable = False
        if down:
            self.checkFlag = "on"
        else:
            self.checkFlag = "off"
        self.gui.setFinished(self.checkFlag)

    def slot_requestCheck(self, down):
        if self.finishedButtonDisable: return

        self.setFinishedButton(down)
        self.rsubject.setFinished(down, self.editor.edCursor.getPos())

    def slot_printSubject(self, arg):
        """Use class LayoutUnits to format the reports for all pupils
        in the present class taking this subject, then present a
        print dialog.
        """
        # Save current report
        self.slot_pupilChanged(-1)
        # Restore current report
        self.slot_pupilChanged(self.pupilIx)

        asc = autoSpellCheck
        if asc:
            __builtin__.autoSpellCheck = False

        if arg:
            plist = [self.currentPupilId]
        else:
            plist = self.pupils

        lu = LayoutUnits(self.db, self.subject, plist)

        if asc:
            __builtin__.autoSpellCheck = True

        npages = len(lu.pages)
        if not npages:
            warning(_("No pages to print"))
            return
        dlg = printDialog(npages)
        if not dlg: return

        start = dlg.start()
        end = dlg.end()
        if (start > end): return

        even = dlg.even()
        odd = dlg.odd()

        pages = []
        for i in range(start, end+1):
            if (i % 2):
                if odd:
                    pages.append(i)
            elif even:
                pages.append(i)

        if dlg.reverse():
            pages.reverse()

        if dlg.toPdf():
            pdfFile = getFile(_("Print to pdf-file"),
                    startFile=u"print.pdf",
                    defaultSuffix=u"pdf",
                    filter=(_("pdf Files"), (u"*.pdf",)),
                    create=True)
            if pdfFile:
                lu.printout(pdfFile, pages)
        else:
            lu.printout(None, pages)

    def slot_autospellcheck(self, on):
        __builtin__.autoSpellCheck = on
        if on:
            val = "1"
        else:
            val = "0"
        self.gui.settings.setSetting("autospellcheck", val)
        # spellcheck all words
        if self.rsubject.tlines:
            for tl in self.rsubject.tlines:
                for w in tl.twords:
                    if on:
                        w.spellCheck()
                    else:
                        w.spellUncheck()

    def slot_checkSpelling(self, arg):
        checkSpell.start(self.rsubject.tlines)

    def slot_endCheckSpelling(self, arg):
        if checkSpell.altered:
            self.rsubject.setText(checkSpell.text)
            self.rsubject.cursor = (0, 0, 0)
            self.editor.initCurSel()
            signal("textChanged")

    def slot_nextUnfinished(self, arg):
        ix = self.pupilIx
        while True:
            ix += 1
            if (ix >= len(self.pupils)):
                ix = 0
            if (ix == self.pupilIx):
                # None found
                return

            pupilId = self.pupils[ix]
            key = u"%s+%s" % (pupilId, self.subject)
            revisions = self.revisionDict.get(key)

            if revisions:
                report, cursor = revisions[-1]
            else:
                report = self.db.getReport(pupilId, self.subject)
                cursor = (0,0,0)
                revisions = [(report, cursor)]
                self.revisionDict[key] = revisions

            if not getReportVersion(report).endswith(u"$"):
                break

        self.gui.setPupil(ix)