#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net )

This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),

Laborejo2 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import logging; logger = logging.getLogger(__name__); logger.info("import")

#Standard Library Modules
import os.path
from pathlib import Path

#Third Party Modules
from PyQt5 import QtCore, QtGui, QtWidgets
translate = QtCore.QCoreApplication.translate

#Template Modules


#Our modules
import engine.api as api
from engine.midiinput.stepmidiinput import stepMidiInput  #singleton instance
from .constantsAndConfigs import constantsAndConfigs
from .submenus import SecondaryClefMenu, SecondaryKeySignatureMenu, SecondaryDynamicsMenu, SecondaryMetricalInstructionMenu, SecondaryTempoChangeMenu, SecondaryTemporaryTempoChangeMenu, SecondarySplitMenu, TransposeMenu, pedalNoteChooser, SecondaryProperties, SecondaryProgramChangeMenu, SecondaryChannelChangeMenu, ChooseOne, forwardText

class ModalKeys(object):
    def __init__(self):
        pass

    def keyRest(self):
        if stepMidiInput.midiInIsActive:
            baseDuration = api.session.data.cursor.prevailingBaseDuration
            api.insertRest(baseDuration)
        #else: deactivated.

    def key1(self):
        if stepMidiInput.midiInIsActive: api.prevailing1()
        else: api.insert1()

    def key2(self):
        if stepMidiInput.midiInIsActive: api.prevailing2()
        else: api.insert2()

    def key3(self):
        if stepMidiInput.midiInIsActive: api.prevailing4()
        else: api.insert4()

    def key4(self):
        if stepMidiInput.midiInIsActive: api.prevailing8()
        else: api.insert8()

    def key5(self):
        if stepMidiInput.midiInIsActive: api.prevailing16()
        else: api.insert16()

    def key6(self):
        if stepMidiInput.midiInIsActive: api.prevailing32()
        else: api.insert32()

    def key7(self):
        if stepMidiInput.midiInIsActive: api.prevailingBrevis()
        else: api.insertBrevis()

    def key8(self):
        if stepMidiInput.midiInIsActive: api.prevailingLonga()
        else: api.insertLonga()

    def key9(self):
        pass

    def key0(self):
        pass

class MenuActionDatabase(object):
    """There are different edit and view modes in Laborejo-Qt:

    Main Program
        ScoreView
            NoteEdit
                Noteheads
                Rectangles
            BlockEdit
                Transparent Block Handles
            CCEdit
                CC 0
                CC 1
                ...
                CC 127
        DataEditor


    Each level has shorcuts associated with them which depend on the context.
    To implement those we use the qt shortcut contexts. See QAction docs.

    Additionaly there is midi-in and the modal number keys, which are
    connected to each other. Without midi in the modal keys insert notes,
    in midi mode they choose the duration for the next midi key.
    """

    def __init__(self, mainWindow):
        self.mainWindow = mainWindow
        self.modalKeys = ModalKeys()

        modes = QtWidgets.QActionGroup(self.mainWindow)
        modes.addAction(self.mainWindow.ui.actionNotation_Mode)
        modes.addAction(self.mainWindow.ui.actionBlock_Mode)
        modes.addAction(self.mainWindow.ui.actionCC_Mode)
        self.mainWindow.ui.actionNotation_Mode.setChecked(True)

        self.rememberMidi = stepMidiInput.midiInIsActive

        api.callbacks.historyChanged.append(self.renameUndoRedoByHistory)
        api.callbacks.metronomeChanged.append(lambda v: self.mainWindow.ui.actionMetronome_Enabled.setChecked(v["enabled"])) #accompanied by a normal menu action down below

        self.actionsWithoutMenu = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. but have no menu entry.
            #Of course these actions have shortcuts, they are in a menu, visible and editable in qt-Designer, which gets hidden on program start.
            self.mainWindow.ui.actionLeft : api.left,
            self.mainWindow.ui.actionRight : api.right,
            self.mainWindow.ui.actionSelectLeft : api.selectLeft,
            self.mainWindow.ui.actionSelectRight : api.selectRight,

            self.mainWindow.ui.actionMeasureLeft : api.measureLeft,
            self.mainWindow.ui.actionMeasureRight : api.measureRight,
            self.mainWindow.ui.actionSelectMeasureLeft : api.selectMeasureLeft,
            self.mainWindow.ui.actionSelectMeasureRight : api.selectMeasureRight,


            self.mainWindow.ui.actionBlockLeft : api.blockLeft,
            self.mainWindow.ui.actionBlockRight : api.blockRight,
            self.mainWindow.ui.actionSelectBlockLeft : api.selectBlockLeft,
            self.mainWindow.ui.actionSelectBlockRight : api.selectBlockRight,

            self.mainWindow.ui.actionHead : api.head,
            self.mainWindow.ui.actionTail : api.tail,
            self.mainWindow.ui.actionSelectHead : api.selectHead,
            self.mainWindow.ui.actionSelectTail : api.selectTail,

            self.mainWindow.ui.actionTrackUp : api.trackUp,
            self.mainWindow.ui.actionTrackDown : api.trackDown,
            self.mainWindow.ui.actionTrackFirst : api.trackFirst,
            self.mainWindow.ui.actionTrackLast : api.trackLast,
            self.mainWindow.ui.actionSelectTrackUp : api.selectTrackUp,
            self.mainWindow.ui.actionSelectTrackDown : api.selectTrackDown,
            self.mainWindow.ui.actionSelectTrackFirst : api.selectTrackFirst,
            self.mainWindow.ui.actionSelectTrackLast : api.selectTrackLast,

            self.mainWindow.ui.actionUp : api.up,
            self.mainWindow.ui.actionDown : api.down,
            self.mainWindow.ui.actionUpOctave : api.upOctave,
            self.mainWindow.ui.actionDownOctave : api.downOctave,
            self.mainWindow.ui.actionSelectUp : api.up, #these are the same as up and down. But for a good user experience they should work if shift is pressed down as well.
            self.mainWindow.ui.actionSelectDown : api.down,
            self.mainWindow.ui.actionSelectUpOctave : api.upOctave,
            self.mainWindow.ui.actionSelectDownOctave : api.downOctave,


            self.mainWindow.ui.actionSelectAllTracks : api.selectAllTracks,
            self.mainWindow.ui.actionSelectTrack : api.selectTrack,
            self.mainWindow.ui.actionSelectMeasureColumn : api.selectMeasureColumn,

            #CC Edit Actions. Share the same shortcuts as some of the actions above. But only one of these is enabled at any time.
            #Don't forget to add them to the list below: self.ccEditActions
            #...
            }


        self.actions = { #always available in the main window. It doesn't matter if you change to CC mode or Note Edit etc.
            self.mainWindow.ui.actionToggle_Notehead_Rectangles : self.mainWindow.scoreView.toggleNoteheadsRectangles,
            self.mainWindow.ui.actionData_Editor : self.mainWindow.toggleMainView,
            self.mainWindow.ui.actionProperties : lambda: SecondaryProperties(self.mainWindow),
            self.mainWindow.ui.actionFollow_Playhead : self.mainWindow.scoreView.toggleFollowPlayhead,
            #Modes Submenu
            self.mainWindow.ui.actionNotation_Mode : self.mainWindow.scoreView.updateMode,
            self.mainWindow.ui.actionBlock_Mode : self.mainWindow.scoreView.updateMode,
            self.mainWindow.ui.actionCC_Mode : self.mainWindow.scoreView.updateMode,

            self.mainWindow.ui.actionChange_Grid_Rhythm : self.mainWindow.scoreView.changeGridRhythm,
            self.mainWindow.ui.actionAdd_Track : api.newEmptyTrack,
            self.mainWindow.ui.actionDelete_Current_Track : api.deleteCurrentTrack,
            self.mainWindow.ui.actionUse_Current_Track_as_Metronome : api.useCurrentTrackAsMetronome,
            self.mainWindow.ui.actionMidi_In_is_Active : self.toggleMidiInIsActive,
            self.mainWindow.ui.actionZoom_In_Score_View : self.mainWindow.zoomIn,
            self.mainWindow.ui.actionZoom_Out_Score_View : self.mainWindow.zoomOut,
            self.mainWindow.ui.actionWiden_Score_View : self.mainWindow.widen,
            self.mainWindow.ui.actionShrink_Score_View : self.mainWindow.shrinken,
            self.mainWindow.ui.actionSave : api.save,
            self.mainWindow.ui.actionQuit : self.mainWindow.nsmClient.serverSendExitToSelf,

            self.mainWindow.ui.actionShow_PDF : api.showPDF,
            self.mainWindow.ui.actionExport_to_Ly : self.exportLy,

            self.mainWindow.ui.actionPlayFromBeginning : api.playFromStart,
            self.mainWindow.ui.actionPlayPause : api.playPause,
            self.mainWindow.ui.actionPlayFromEditCursor : api.playFromCursor,
            self.mainWindow.ui.actionPlay_from_Block : api.playFromBlockStart,
            self.mainWindow.ui.actionMetronome_Enabled : api.toggleMetronome, #toggle is enough. The callback makes sure all the checkboxes have the correct value.
            }

        self.noteEditActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc. #all xxxEditActions are mutually exclusive.
            self.mainWindow.ui.actionBeam : api.toggleBeam,
            self.mainWindow.ui.actionRemoveBeams : api.removeBeam,

            self.mainWindow.ui.actionLegatoSlur : api.insertLegatoSlur,
            self.mainWindow.ui.actionClef : SecondaryClefMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus.
            self.mainWindow.ui.actionKey_Signature : SecondaryKeySignatureMenu(self.mainWindow),
            self.mainWindow.ui.actionDynamics : SecondaryDynamicsMenu(self.mainWindow),
            self.mainWindow.ui.actionMulti_Measure_Rest : lambda: api.insertMultiMeasureRest(1),
            self.mainWindow.ui.actionMetrical_Instruction : SecondaryMetricalInstructionMenu(self.mainWindow),


            self.mainWindow.ui.actionTempo_Change : lambda: SecondaryTempoChangeMenu(self.mainWindow),
            self.mainWindow.ui.actionDelete_Tempo_Change : api.removeCurrentTempoItem,
            self.mainWindow.ui.actionTemporary_Tempo_Change : SecondaryTemporaryTempoChangeMenu(self.mainWindow),

            self.mainWindow.ui.actionSharpen_Note : api.sharpenNote,
            self.mainWindow.ui.actionFlatten_Note : api.flattenNote,
            self.mainWindow.ui.actionStep_Up : api.stepUp,
            self.mainWindow.ui.actionStep_Down : api.stepDown,
            self.mainWindow.ui.actionOctave_Up : api.stepUpOctave,
            self.mainWindow.ui.actionOctave_Down : api.stepDownOctave,
            self.mainWindow.ui.actionTransposeChord : lambda: TransposeMenu(self.mainWindow, "item"),


            self.mainWindow.ui.actionVelocityMore : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.moreVelocity),
            self.mainWindow.ui.actionVelocityLess : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.lessVelocity),
            self.mainWindow.ui.actionDurationModMore : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.moreDuration),
            self.mainWindow.ui.actionDurationModLess : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.lessDuration),
            self.mainWindow.ui.actionReset_Velocity_Duration_Mod : lambda: self.mainWindow.scoreView._switchToRectanglesForFunction(api.resetDurationVelocity),

            self.mainWindow.ui.actionAugment : api.augment,
            self.mainWindow.ui.actionDiminish : api.diminish,
            self.mainWindow.ui.actionDots : api.dot,
            self.mainWindow.ui.actionTriplet : api.triplet,
            #self.mainWindow.ui.actionCustomTuplet : something with a GUI that allows any type and number of nested tuplets, #TODO
            self.mainWindow.ui.actionStaccato : api.staccato,
            self.mainWindow.ui.actionTenuto : api.tenuto,
            self.mainWindow.ui.actionTie : api.tie,
            self.mainWindow.ui.actionSplit_in_2 : lambda: api.split(2),
            self.mainWindow.ui.actionSplit_in_3 : lambda: api.split(3),
            self.mainWindow.ui.actionCustom_Split : SecondarySplitMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus.

            self.mainWindow.ui.actionMIDI_Channel_Plus : api.midiRelativeChannelPlus,
            self.mainWindow.ui.actionMIDI_Channel_Minus : api.midiRelativeChannelMinus,
            self.mainWindow.ui.actionMIDI_Channel_Reset : api.midiRelativeChannelReset,

            self.mainWindow.ui.actionAppend_Block : api.appendBlock,
            self.mainWindow.ui.actionSplit_Current_Block : api.splitBlock,
            self.mainWindow.ui.actionJoin_with_next_Block : api.joinBlock,
            self.mainWindow.ui.actionDuplicate : api.duplicateCurrentBlock,
            self.mainWindow.ui.actionCreate_Linked_Copy : api.duplicateContentLinkCurrentBlock,
            self.mainWindow.ui.actionUnlink_Current_Block : api.unlinkCurrentBlock,
            self.mainWindow.ui.actionDelete_Current_Block : api.deleteCurrentBlock,

            self.mainWindow.ui.actionTranspose_Score : lambda: TransposeMenu(self.mainWindow, "score"),
            self.mainWindow.ui.actionDelete_All_Empty_Blocks : api.deleteEmptyBlocks,

            self.mainWindow.ui.actionDelete : api.delete,
            self.mainWindow.ui.actionBackspace : api.backspace,
            self.mainWindow.ui.actionAddCursorNoteToChord : api.addCursorNoteToChord,
            self.mainWindow.ui.actionDeleteCursorNoteFromChord : api.deleteCursorNoteFromChord,
            self.mainWindow.ui.actionCut : api.cutObjects,
            self.mainWindow.ui.actionCopy : api.copyObjects,
            self.mainWindow.ui.actionPaste : api.pasteObjects,
            self.mainWindow.ui.actionDuplicateItem : api.duplicate,
            self.mainWindow.ui.actionUndo : api.undo,
            self.mainWindow.ui.actionRedo : api.redo,

            self.mainWindow.ui.actionRedrawAllTracks : api.updateCallbackAllTracks,

            #Toolbox
                #Note Generation
            self.mainWindow.ui.actionPedalNotes : lambda: pedalNoteChooser(self.mainWindow),
            self.mainWindow.ui.actionRandom_chromatic_in_clef_range: api.insertRandomChromaticInClefRange,
            self.mainWindow.ui.actionRandom_in_scale_in_clef_range: api.insertRandomFromScaleInClefRange,
            self.mainWindow.ui.actionRandom_pitch_from_clipboard: api.insertRandomFromClipboard,
            self.mainWindow.ui.actionRandom_in_scale_in_octave_around_cursor: api.insertRandomFromScaleHypoModeCursor,
            self.mainWindow.ui.actionRandom_in_scale_in_cursor_plus_octave: api.insertRandomFromScaleAuthenticModeCursor,
            self.mainWindow.ui.actionMirror_around_Cursor: api.mirrorAroundCursor,

                #Note Sorting
            self.mainWindow.ui.actionRandom: api.reorderShuffle,
            self.mainWindow.ui.actionReverse: api.reorderReverse,
            self.mainWindow.ui.actionAscending: api.reorderAscending,
            self.mainWindow.ui.actionDescending: api.reoderdDescending,

                #Midi
            self.mainWindow.ui.actionInstrument_Change: SecondaryProgramChangeMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus.
            self.mainWindow.ui.actionChannel_Change: SecondaryChannelChangeMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus.

            #Lilypond
            #Print and Export is in self.actions
            self.mainWindow.ui.actionLyBarline: ChooseOne(self.mainWindow, translate("menu", "Choose a Barline"), api.getLilypondBarlineList()),
            self.mainWindow.ui.actionLyRepeat: ChooseOne(self.mainWindow, translate("menu", "Choose a Repeat"), api.getLilypondRepeatList()),
            self.mainWindow.ui.actionLyFree_Instruction: lambda: forwardText(self.mainWindow, translate("menu", "Enter Instruction"), api.lilypondText),
        }

        self.modalActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc.
            self.mainWindow.ui.actionModal1 : self.modalKeys.key1,
            self.mainWindow.ui.actionModal2 : self.modalKeys.key2,
            self.mainWindow.ui.actionModal3 : self.modalKeys.key3,
            self.mainWindow.ui.actionModal4 : self.modalKeys.key4,
            self.mainWindow.ui.actionModal5 : self.modalKeys.key5,
            self.mainWindow.ui.actionModal6 : self.modalKeys.key6,
            self.mainWindow.ui.actionModal7 : self.modalKeys.key7, #brevis
            self.mainWindow.ui.actionModal8 : self.modalKeys.key8,  #longa

            self.mainWindow.ui.actionPrevailingRest : self.modalKeys.keyRest,

            self.mainWindow.ui.actionShift_modal1 : api.insertRest1,
            self.mainWindow.ui.actionShift_modal2 : api.insertRest2,
            self.mainWindow.ui.actionShift_modal3 : api.insertRest4,
            self.mainWindow.ui.actionShift_modal4 : api.insertRest8,
            self.mainWindow.ui.actionShift_modal5 : api.insertRest16,
            self.mainWindow.ui.actionShift_modal6 : api.insertRest32,
            #self.mainWindow.ui.actionShift_modal7 : api.insertRestBrevis,
            #self.mainWindow.ui.actionShift_modal8 : api.insertRestLonga,
            #self.mainWindow.ui.actionShift_modal7 : api.insertRest64,
            #self.mainWindow.ui.actionShift_modal8 : api.insertRest128,
        }

        self.nonEditingMusicActions = [ #These are available in CC Edit Mode, Note Edit mode etc.
            self.mainWindow.ui.actionPlayFromBeginning,
            self.mainWindow.ui.actionPlayPause,
            self.mainWindow.ui.actionPlayFromEditCursor,
            self.mainWindow.ui.actionToggle_Notehead_Rectangles,
            self.mainWindow.ui.actionData_Editor,
            self.mainWindow.ui.actionFollow_Playhead,
            self.mainWindow.ui.actionNotation_Mode,
            self.mainWindow.ui.actionBlock_Mode,
            self.mainWindow.ui.actionCC_Mode,
            self.mainWindow.ui.actionChange_Grid_Rhythm,
            self.mainWindow.ui.actionAdd_Track,
            self.mainWindow.ui.actionUndo,
            self.mainWindow.ui.actionRedo,
            #self.mainWindow.ui.actionZoom_In_Score,
            #self.mainWindow.ui.actionZoom_Out_Score,
            self.mainWindow.ui.actionWiden_Score_View,
            self.mainWindow.ui.actionShrink_Score_View,
            self.mainWindow.ui.actionSave,
            self.mainWindow.ui.actionQuit,

        ]

        self.ccEditActions = [ #only in CC edit, when editing user points. #all xxxEditActions are mutually exclusive. These are defined in non-menu actions above.
        #    self.mainWindow.ui.CCactionDelete,
        ]

        #Prepare non-designer widgets. Designer can't put normal widgets in a toolbar, but Qt can.
        #Eventhough this is a dict, which has no order, the CREATION has an order. So the first item in the dict will be the first item in the toolBar
        self.extraToolBarWidgets = {
            "snapToGrid" : self.mainWindow.ui.toolBar.addWidget(ToolBarSnapToGrid(mainWindow=self.mainWindow)),
            "metronome" : self.mainWindow.ui.toolBar.addWidget(ToolBarMetronome(mainWindow=self.mainWindow)),
            "playbackSpeed" : self.mainWindow.ui.toolBar.addWidget(ToolBarPlaybackSpeed(mainWindow=self.mainWindow)),
            "ccType" : self.mainWindow.ui.toolBar.addWidget(ToolBarCCType(mainWindow=self.mainWindow)), #keep this at the end of the toolbar because it toggles visibility
            }

        self.toolbarContexts = {
            "notation" : [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ],
            "cc" : [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"],  self.extraToolBarWidgets["ccType"], self.extraToolBarWidgets["playbackSpeed"]],
            "block": [self.extraToolBarWidgets["snapToGrid"], self.extraToolBarWidgets["metronome"], self.extraToolBarWidgets["playbackSpeed"], ],
            }

        #Now connect all actions to functions

        for action, function in self.actions.items():
            action.triggered.connect(function)

        #all xxxEditActions are mutually exclusive.
        for action, function in self.noteEditActions.items():
            action.triggered.connect(function)

        for action in self.ccEditActions: # a list.
            action.setEnabled(False) #These are the same keys as noteEditActions, so we need to disable them for now.

        for action, function in self.modalActions.items():
            self.mainWindow.ui.centralwidget.addAction(action) #no actions without a widget
            action.triggered.connect(function)

        #Do not connect modalActionsPrevailing yet

        for action, function in self.actionsWithoutMenu.items():
            self.mainWindow.ui.centralwidget.addAction(action) #no actions without a widget
            action.triggered.connect(function)

        self.mainWindow.ui.menubar.removeAction(self.mainWindow.ui.menuGeneric.menuAction()) #thats why we added the actions to the centralwidget above.
        self.loadToolbarContext("notation")

    def toggleMidiInIsActive(self):
        """That is: "toggle midiInIsActive", not "toggleMidiIn is active?" """
        stepMidiInput.toggleMidiIn()
        self.mainWindow.ui.actionPrevailingRest.setEnabled(stepMidiInput.midiInIsActive)
        self.mainWindow.ui.actionMidi_In_is_Active.setChecked(stepMidiInput.midiInIsActive)

    def setMusicEditingEnabled(self, boolean):
        """This is the "reset to standard" functions for shortcuts.
        If you want to enable other keys, such as CC editing keys,
        you have to call another function after this one"""
        allActions = list(self.actions.keys()) + list(self.modalActions.keys()) + list(self.actionsWithoutMenu.keys()) +  list(self.noteEditActions.keys())

        for x in self.nonEditingMusicActions:
            allActions.remove(x)

        for editAction in allActions:
            editAction.setEnabled(boolean)

        for action in self.ccEditActions: # Call this after enabling stuff in the for-loop above.
            action.setEnabled(False) #These are the same keys as noteEditActions, so we need to disable them for now. Maybe they are enabled later again, after this function.

        self.mainWindow.ui.actionPrevailingRest.setEnabled(boolean and stepMidiInput.midiInIsActive)

    def noteEditMode(self):
        self.setMusicEditingEnabled(True)

    def ccEditMode(self):
        self.setMusicEditingEnabled(False)
        for action in self.ccEditActions: # a list.
            action.setEnabled(True) #These are the same keys as noteEditActions, which are disabled above.

    def writeProtection(self, boolean):
        """Artificial read only mode that was designed for the mouse-only track editor etc.
        When zoomed out the user should be able to use the mouse but the cursor is hidden and
        """

        if boolean:
            #Switch off midi input
            if stepMidiInput.midiInIsActive:
                self.rememberMidi = stepMidiInput.midiInIsActive
                stepMidiInput.toggleMidiIn()
                assert not stepMidiInput.midiInIsActive
            self.mainWindow.ui.actionPrevailingRest.setEnabled(stepMidiInput.midiInIsActive)
            self.mainWindow.ui.actionMidi_In_is_Active.setChecked(stepMidiInput.midiInIsActive)
            #Deactivate Note Editing Actions
            self.setMusicEditingEnabled(False)
            self.mainWindow.scoreView.scoreScene.cursor.hide() #the cursor which can be used to edit notes. Since the cursor shortcuts get disabled it cannot be changed invisbly.
        else:
            self.setMusicEditingEnabled(True)
            self.mainWindow.scoreView.scoreScene.cursor.show()
            if self.rememberMidi and not stepMidiInput.midiInIsActive:
                self.toggleMidiInIsActive()
            self.rememberMidi = None

    def loadToolbarContext(self, nameAsString):
        """Hide all toolbar actions and only load those
        which are in the current context, defined as list"""

        assert nameAsString in constantsAndConfigs.availableEditModes

        for action in self.mainWindow.ui.toolBar.actions():
            action.setVisible(False)

        for action in self.toolbarContexts[nameAsString]:
            action.setVisible(True)

    def renameUndoRedoByHistory(self, undoList, redoList):
        if undoList:
            self.mainWindow.ui.actionUndo.setText(translate("menu", "Undo: {}").format(undoList[-1]))
            self.mainWindow.ui.actionUndo.setEnabled(True)
        else:
            self.mainWindow.ui.actionUndo.setText(translate("menu", "Undo"))
            self.mainWindow.ui.actionUndo.setEnabled(False)


        if redoList:
            self.mainWindow.ui.actionRedo.setText(translate("menu", "Redo: {}").format(redoList[-1]))
            self.mainWindow.ui.actionRedo.setEnabled(True)
        else:
            self.mainWindow.ui.actionRedo.setText(translate("menu", "Redo"))
            self.mainWindow.ui.actionRedo.setEnabled(False)

    def exportLy(self):
        lastExportDirectory = api.session.guiSharedDataToSave["lastExportDirectory"] if "lastExportDirectory" in api.session.guiSharedDataToSave else str(Path.home())
        filename = QtWidgets.QFileDialog.getSaveFileName(self.mainWindow, translate("menu", "Export Lilypond Source File"), lastExportDirectory, translate("menu", "Lilypond Source (*.ly)"))
        filename = filename[0] #(path, filter)
        if filename:
            if not os.path.splitext(filename)[1]: #no file extension given?
                filename = filename + ".ly"
            api.session.guiSharedDataToSave["lastExportDirectory"] = os.path.dirname(filename)
            api.exportLilypond(filename)

class ToolBarCCType(QtWidgets.QSpinBox):
    def __init__(self, mainWindow):
        super().__init__()
        self.mainWindow = mainWindow
        self.setRange(0,127)
        self.setPrefix("CC ")

        self.valueChanged.connect(self.changed)

        #self.addItems([str(i).zfill(3) for i in range(128) ])

    def changed(self):
        constantsAndConfigs.ccViewValue = self.value()
        self.mainWindow.scoreView.updateMode() #gets the new value itself from constantsAndConfigs

class ToolBarSnapToGrid(QtWidgets.QCheckBox):
    def __init__(self, mainWindow):
        super().__init__(translate("menu", "Snap to Grid"))
        self.setStatus()
        self.stateChanged.connect(self.changed)

        constantsAndConfigs.snapToGridCallbacks.append(self.updateFromCallback)

    def setStatus(self):
        if constantsAndConfigs.snapToGrid:
            self.setCheckState(QtCore.Qt.Checked)
        else:
            self.setCheckState(QtCore.Qt.Unchecked)

    def isChecked(self):
        if self.checkState() == QtCore.Qt.Checked:
            return True
        else:
            return False

    def updateFromCallback(self):
        self.blockSignals(True)
        self.setStatus()
        self.blockSignals(False)

    def changed(self):
        constantsAndConfigs.snapToGrid = self.isChecked() #this sends a call back to all Snap to Grid in the whole program


class ToolBarMetronome(QtWidgets.QCheckBox):
    """Contrary to SnapToGrid this reflects a backend state, not a pure GUI one.
    There is also the menu actionMetronome_Enabled , itself a normal checkbox. It's callback and
    state are handled in the menuDatabase classes init."""
    def __init__(self, mainWindow):
        super().__init__(translate("menu", "Metronome"))
        self.setStatus()
        self.stateChanged.connect(self.changed)
        api.callbacks.metronomeChanged.append(self.updateFromCallback)

    def setStatus(self):
        if api.isMetronomeEnabled():
            self.setCheckState(QtCore.Qt.Checked)
        else:
            self.setCheckState(QtCore.Qt.Unchecked)

    def isChecked(self):
        if self.checkState() == QtCore.Qt.Checked:
            assert api.isMetronomeEnabled(), api.isMetronomeEnabled()
            return True
        else:
            assert not api.isMetronomeEnabled(), api.isMetronomeEnabled()
            return False

    def updateFromCallback(self, exportDict):
        """Gets the metronome dict"""
        self.blockSignals(True)
        self.setStatus()
        assert self.isChecked() == exportDict["enabled"], (self.isChecked(), exportDict["enabled"])
        self.setText(translate("menu", "Metronome:") + " " + exportDict["label"])
        self.blockSignals(False)

    def changed(self, value):
        api.enableMetronome(value) #triggers callback


class ToolBarPlaybackSpeed(QtWidgets.QDoubleSpinBox):
    def __init__(self, mainWindow):
        super().__init__()
        self.setRange(0.1,3.0)
        self.setDecimals(1)
        self.setValue(api.currentTempoScalingFactor())
        self.setSingleStep(0.1)
        self.setPrefix(translate("menu", "Tempo ×")) #yes, this is the unicode multiplication sign × and not an x
        api.callbacks.tempoScalingChanged.append(self.updateFromCallback)
        self.valueChanged.connect(self.changed)
        self.setLineEdit(ToolBarPlaybackSpeed.CustomLineEdit())

    class CustomLineEdit(QtWidgets.QLineEdit):
        def mousePressEvent(self, event):
            if event.button() == 4:
                self.parentWidget().setValue(1.0)
                self.parentWidget().changed()
            else:
                event.ignore()
                super().mousePressEvent(event)

    def mousePressEvent(self, event):
        if event.button() == 4:
            self.setValue(1.0)
            self.changed()
        else:
            event.ignore()
            super().mousePressEvent(event)

    def updateFromCallback(self, newValue):
        self.blockSignals(True)
        self.setValue(newValue)
        self.blockSignals(False)

    def changed(self):
        api.changeTempoScaling(self.value())
