Пример #1
0
    def __init__(self, monochrome=False, *a, **k):
        super(ModeSelector, self).__init__(*a, **k)
        is_momentary = True
        self._monochrome = monochrome
        self._scene_mode = SceneMode(0)
        self._clip_mode = StudioClipMode(1)
        self._pad_mode = PadMode(2, monochrome)
        self._drum_mode = DrumMode(2, monochrome)
        self._control_mode = ControlMode(2, monochrome)
        self._pad_mode.set_alternate_mode(self._drum_mode)
        self._drum_mode.set_alternate_mode(self._pad_mode)
        self._tracks_assign = TrackAssign()
        self._arm_mode = TrackArmMode(self._tracks_assign)
        self._solo_mode = TrackSoloMode(self._tracks_assign)
        self._stop_mode = TrackStopMode(self._tracks_assign)
        self._mute_mode = TrackMuteMode(self._tracks_assign)
        self._select_mode = TrackSelectMode(self._tracks_assign)
        self._xfade_mode = TrackXFadeMode(self._tracks_assign)
        self._color_select_mode = MaschineColorSelectMode(5)
        self._mode = self._clip_mode
        self._return_mode = None

        def create_button(ccval, channel=0):
            return StateButton(is_momentary, MIDI_CC_TYPE, channel, ccval)

        self._scene_mode_button = create_button(112)
        self._clip_mode_button = create_button(113)
        self._pad_mode_button = create_button(114)
        self._select_button = create_button(117)
        self._solo_button = create_button(118)
        self._mute_button = create_button(119)
        self._xfade_button = self.canonical_parent.create_gated_button(84, 5)
        self._stop_button = self.canonical_parent.create_gated_button(94, 16)
        self._arm_button = self.canonical_parent.create_gated_button(83, 0)
        self._solo_button = StateButton(False, MIDI_CC_TYPE, 0, 118)
        self._clip_mode_button.send_value(127, True)
        self._select_clip_mode.subject = self._clip_mode_button
        self._select_scene_mode.subject = self._scene_mode_button
        self._select_pad_mode.subject = self._pad_mode_button
        self._select_arm.subject = self._arm_button
        self._select_solo.subject = self._solo_button
        self._select_stop.subject = self._stop_button
        self._select_xfade.subject = self._xfade_button
        self._select_mute.subject = self._mute_button
        self._select_select.subject = self._select_button
        self._arm_exclusive_button = create_button(51, 2)
        self._action_arm_exclusive.subject = self._arm_exclusive_button
        self._solo_exclusive_button = create_button(52, 2)
        self._action_solo_exclusive.subject = self._solo_exclusive_button
        self._scene_mode_button.send_value(0, True)
        self._clip_mode_button.send_value(1, True)
        self._pad_mode_button.send_value(0, True)
        self._mode._active = True
        return
Пример #2
0
class ModeSelector(CompoundComponent):
    """
    Class Handling the switch between Modes.
    """
    __module__ = __name__
    mikro_shift = False
    _md_select_mode = None
    _md_solo_mode = None
    _md_mute_mode = None
    _knob_section = None
    _clip_mode_down = False
    _monochrome = False

    def __init__(self, monochrome=False, *a, **k):
        super(ModeSelector, self).__init__(*a, **k)
        is_momentary = True
        self._monochrome = monochrome
        self._scene_mode = SceneMode(0)
        self._clip_mode = StudioClipMode(1)
        self._pad_mode = PadMode(2, monochrome)
        self._drum_mode = DrumMode(2, monochrome)
        self._control_mode = ControlMode(2, monochrome)
        self._pad_mode.set_alternate_mode(self._drum_mode)
        self._drum_mode.set_alternate_mode(self._pad_mode)
        self._tracks_assign = TrackAssign()
        self._arm_mode = TrackArmMode(self._tracks_assign)
        self._solo_mode = TrackSoloMode(self._tracks_assign)
        self._stop_mode = TrackStopMode(self._tracks_assign)
        self._mute_mode = TrackMuteMode(self._tracks_assign)
        self._select_mode = TrackSelectMode(self._tracks_assign)
        self._xfade_mode = TrackXFadeMode(self._tracks_assign)
        self._color_select_mode = MaschineColorSelectMode(5)
        self._mode = self._clip_mode
        self._return_mode = None

        def create_button(ccval, channel=0):
            return StateButton(is_momentary, MIDI_CC_TYPE, channel, ccval)

        self._scene_mode_button = create_button(112)
        self._clip_mode_button = create_button(113)
        self._pad_mode_button = create_button(114)
        self._select_button = create_button(117)
        self._solo_button = create_button(118)
        self._mute_button = create_button(119)
        self._xfade_button = self.canonical_parent.create_gated_button(84, 5)
        self._stop_button = self.canonical_parent.create_gated_button(94, 16)
        self._arm_button = self.canonical_parent.create_gated_button(83, 0)
        self._solo_button = StateButton(False, MIDI_CC_TYPE, 0, 118)
        self._clip_mode_button.send_value(127, True)
        self._select_clip_mode.subject = self._clip_mode_button
        self._select_scene_mode.subject = self._scene_mode_button
        self._select_pad_mode.subject = self._pad_mode_button
        self._select_arm.subject = self._arm_button
        self._select_solo.subject = self._solo_button
        self._select_stop.subject = self._stop_button
        self._select_xfade.subject = self._xfade_button
        self._select_mute.subject = self._mute_button
        self._select_select.subject = self._select_button
        self._arm_exclusive_button = create_button(51, 2)
        self._action_arm_exclusive.subject = self._arm_exclusive_button
        self._solo_exclusive_button = create_button(52, 2)
        self._action_solo_exclusive.subject = self._solo_exclusive_button
        self._scene_mode_button.send_value(0, True)
        self._clip_mode_button.send_value(1, True)
        self._pad_mode_button.send_value(0, True)
        self._mode._active = True
        return

    def mode(self):
        return self._mode

    def update(self):
        pass

    def get_color_manager(self):
        return self._color_select_mode

    def get_color(self, value, column_index, row_index):
        return self._mode.get_color(value, column_index, row_index)

    def connect_main_knob(self, knobsection):
        self._control_mode.connect_main_knob(knobsection)

    def assign_edit_section(self, editsection):
        self._scene_mode.set_edit_mode(editsection)
        self._pad_mode.set_edit_mode(editsection)
        self._drum_mode.set_edit_mode(editsection)

    def navigate(self, dir, modifier, alt_modifier=False):
        self._mode.navigate(dir, modifier, alt_modifier)

    def notify(self, blink_state):
        self._mode.notify(blink_state)
        if self._mode == self._clip_mode or self._mode == self._color_select_mode:
            if self.canonical_parent._editsection.is_color_edit():
                if self._color_select_mode.in_track_scene_mode():
                    bval = blink_state % 2
                else:
                    bval = blink_state / 2
                self._clip_mode_button.send_value(bval)
            else:
                self._clip_mode_button.send_value(1)

    def notify_mono(self, blink_state):
        self._mode.notify_mono(blink_state)

    def enter_edit_mode(self, type):
        self._mode.enter_edit_mode(type)

    def exit_edit_mode(self, type):
        self._mode.exit_edit_mode(type)

    def enter_clear_state(self):
        self._mode.enter_clear_state()

    def exit_clear_state(self):
        self._mode.exit_clear_state()

    def _device_changed(self):
        self._mode._device_changed()

    def refresh(self):
        self._mode.refresh()
        self._stop_button.activate()
        self._arm_button.activate()
        self._xfade_button.activate()

    @contextmanager
    def rebuild(self):
        self.canonical_parent._set_suppress_rebuild_requests(True)
        yield
        self.canonical_parent._set_suppress_rebuild_requests(False)
        self.canonical_parent.request_rebuild_midi_map()

    def _light_button(self, which):
        self._scene_mode_button.send_value(which == 0 and 1 or 0, True)
        self._clip_mode_button.send_value(which == 1 and 1 or 0, True)
        self._pad_mode_button.send_value(which == 2 and 1 or 0, True)

    def change_mode(self, nextmode):
        return self._mode != nextmode and (self._mode == None or self._mode.is_lock_mode())

    def set_shift_state(self, state):
        self.mikro_shift = state

    def show_messsage(self, msg):
        self.canonical_parent.show_message(msg)

    def handle_push(self, value):
        if self._mode:
            self._mode.handle_push(value)

    def pick_color(self, cscomponent):
        shift_down = self.canonical_parent.isShiftDown()

        def set_color(rgb_value):
            if self.canonical_parent.isShiftDown():
                if shift_down:
                    track = cscomponent._clip_slot.canonical_parent
                    scenes = self.song().scenes
                    index = vindexof(track.clip_slots, cscomponent._clip_slot)
                    scenes[index].color = rgb_value
                else:
                    track = cscomponent._clip_slot.canonical_parent
                    for cs in track.clip_slots:
                        if cs.has_clip:
                            cs.clip.color = rgb_value

            else:
                if shift_down:
                    track = cscomponent._clip_slot.canonical_parent
                    track.color = rgb_value
                else:
                    if cscomponent._clip_slot.clip != None:
                        cscomponent._clip_slot.clip.color = rgb_value
            return

        def color_picked(color_rgb):
            set_color(color_rgb)
            if self._mode == self._color_select_mode:
                with self.rebuild():
                    self._mode.exit()
                    self._mode = self._clip_mode
                    self._mode.enter()

        self._color_select_mode.set_pick_callback(color_picked, shift_down)
        self._into_mode(self._color_select_mode, 'Color Chooser')

    def _into_mode(self, mode, info):
        if self._mode != None and self._mode != mode and not self._mode.is_lock_mode():
            return
        with self.rebuild():
            self._return_mode = self._mode
            self._mode.exit()
            self._mode = mode
            self._mode.enter()
        return

    @subject_slot('value')
    def _select_scene_mode(self, value):
        if value > 0:
            if self.mikro_shift:
                if self.change_mode(self._control_mode):
                    with self.rebuild():
                        self._mode.exit()
                        self._mode = self._control_mode
                        self._light_button(0)
                        self._mode.enter()
            elif self.change_mode(self._scene_mode):
                with self.rebuild():
                    self._mode.exit()
                    self._mode = self._scene_mode
                    self._light_button(0)
                    self._mode.enter()

    @subject_slot('value')
    def _select_clip_mode(self, value):
        click = value != 0
        if click and self._mode == self._color_select_mode:
            self._into_mode(self._clip_mode, 'CLIP MODE')
            return
        if click:
            if self.change_mode(self._clip_mode):
                with self.rebuild():
                    self._mode.exit()
                    self._mode = self._clip_mode
                    self._light_button(1)
                    self._mode.enter()
            if not self._monochrome:
                if self.canonical_parent.isShiftDown() or self.mikro_shift:
                    self.canonical_parent.to_color_edit_mode(True)
                    self._clip_mode_button.send_value(1, True)
                else:
                    self.canonical_parent.to_color_edit_mode(False)
        self._clip_mode_down = click
        return

    @subject_slot('value')
    def _select_pad_mode(self, value):
        if value > 0 and self.change_mode(self._pad_mode):
            track = self.song().view.selected_track
            newmode = self._pad_mode.fitting_mode(track)
            with self.rebuild():
                self._mode.exit()
                self._mode = newmode
                self._light_button(2)
                self._mode.enter()

    def _select_drum_mode(self, value):
        if value > 0 and self.change_mode(self._drum_mode):
            with self.rebuild():
                self._mode.exit()
                self._mode = self._drum_mode
                self._light_button(2)
                self._mode.enter()

    def _into_hold_mode(self, value, button, mode, info):
        if self._mode != None and self._mode != mode and not self._mode.is_lock_mode():
            return
        button.send_value(value, True)
        if value > 0 and self._mode != mode:
            with self.rebuild():
                self._mode.exit()
                self._return_mode = self._mode
                self._mode = mode
                self._mode.enter()
        else:
            if value == 0 and self._mode == mode:
                with self.rebuild():
                    self._mode.exit()
                    self._mode = self._return_mode
                    self._return_mode = None
                    self._mode.enter()
        return

    @subject_slot('value')
    def _select_arm(self, value):
        if value != 0:
            self.show_messsage('Current Mode : Track ARM')
        self._into_hold_mode(value, self._arm_button, self._arm_mode, 'ARM')

    @subject_slot('value')
    def _select_stop(self, value):
        if value != 0:
            self.show_messsage('Current Mode : Track STOP')
        self._into_hold_mode(value, self._stop_button, self._stop_mode, 'STOP')

    @subject_slot('value')
    def _select_solo(self, value):
        if value != 0:
            if self.mikro_shift:
                self._into_hold_mode(value, self._solo_button, self._stop_mode, 'STOP')
                self._md_solo_mode = self._stop_mode
                self.show_messsage('Current Mode : Track STOP')
            else:
                self._into_hold_mode(value, self._solo_button, self._solo_mode, 'SOLO')
                self._md_solo_mode = self._solo_mode
                self.show_messsage('Current Mode : Track SOLO')
        else:
            self._into_hold_mode(value, self._solo_button, self._md_solo_mode, 'SOLO')

    @subject_slot('value')
    def _select_mute(self, value):
        if value != 0:
            if self.mikro_shift:
                self._into_hold_mode(value, self._mute_button, self._arm_mode, 'ARM')
                self.show_messsage('Current Mode : Track ARM')
                self._md_mute_mode = self._arm_mode
            else:
                self._into_hold_mode(value, self._mute_button, self._mute_mode, 'Mute')
                self.show_messsage('Current Mode : Track MUTE')
                self._md_mute_mode = self._mute_mode
        else:
            self._into_hold_mode(value, self._mute_button, self._md_mute_mode, 'SOLO')

    @subject_slot('value')
    def _select_select(self, value):
        if value != 0:
            if self.mikro_shift:
                self._md_select_mode = self._xfade_mode
                self._into_hold_mode(value, self._select_button, self._xfade_mode, 'XFADE')
                self.show_messsage('Current Mode : Track Crossfader Assign')
            else:
                self._md_select_mode = self._select_mode
                self._into_hold_mode(value, self._select_button, self._select_mode, 'Select')
                self.show_messsage('Current Mode : Track SELECT')
        else:
            self._into_hold_mode(value, self._select_button, self._md_select_mode, 'Select')

    @subject_slot('value')
    def _select_xfade(self, value):
        if value != 0:
            self.show_messsage('Current Mode : Track Crossfader Assign')
        self._into_hold_mode(value, self._xfade_button, self._xfade_mode, 'XFADE')

    def _action_octave(self, value):
        pass

    def _action_scale(self, value):
        pass

    def _action_base_note(self, value):
        pass

    def isClipDown(self):
        return self._clip_mode_down

    def _action_key_color(self, value):
        pass

    def is_solo_exclusive(self):
        return self._solo_mode.exclusive

    def is_arm_exclusive(self):
        return self._arm_mode.exclusive

    def set_solo_exclusive(self, value):
        self._solo_mode.exclusive = value
        self._solo_exclusive_button.send_value(value > 0 and 127 or 0, True)

    def set_arm_exclusive(self, value):
        self._arm_mode.exclusive = value
        self._arm_exclusive_button.send_value(value > 0 and 127 or 0, True)

    @subject_slot('value')
    def _action_arm_exclusive(self, value):
        if value > 0:
            self.set_arm_exclusive(not self._arm_mode.exclusive)

    @subject_slot('value')
    def _action_solo_exclusive(self, value):
        if value > 0:
            self.set_solo_exclusive(not self._solo_mode.exclusive)

    def on_selected_scene_changed(self):
        pass

    def on_selected_track_changed(self):
        track = self.song().view.selected_track
        newmode = self._mode.fitting_mode(track)
        if track and newmode != self._mode:
            with self.rebuild():
                self._light_button(newmode.button_index)
                self._mode.exit()
                self._mode = newmode
                self._mode.enter()

    def disconnect(self):
        self._clip_mode.unbind()
        self._drum_mode.unbind()
        self._scene_mode.unbind()
        self._pad_mode.unbind()
        super(ModeSelector, self).disconnect()

    def on_selected_scene_changed(self):
        pass
    def __init__(self, note_repeat=None, *a, **k):
        super(ModeHandler, self).__init__(*a, **k)
        self._note_repeat = note_repeat
        self.nr_index = 6
        self._note_repeat.repeat_rate = 1.0 / CFG_REPEAT_FREQUENCIES[
            self.nr_index] * 4.0

        def create_button(ccval, channel=0, cname=None):
            button = StateButton(True,
                                 MIDI_CC_TYPE,
                                 channel,
                                 ccval,
                                 name=cname)
            button.last_value = 0
            return button

        self._note_repeat_button = create_button(CC_NOTE_REPEAT_BUTTON, 0,
                                                 'Note_Repeat_Button')
        self._do_note_repeat.subject = self._note_repeat_button
        self._scene_mode_button = create_button(CC_SCENE_BUTTON, 0,
                                                'Scene_Mode_Button')
        self._clip_mode_button = create_button(CC_PATTERN_BUTTON, 0,
                                               'Pattern_Mode_Button')
        self._pad_mode_button = create_button(CC_PAD_BUTTON, 0,
                                              'Pad_Mode_Button')
        self._keyboard_mode_button = create_button(CC_KEYBOARD_BUTTON, 0,
                                                   'Keyboard_Mode_Button')
        self._track_mode_button = create_button(CC_MIKRO_GROUP_BUTTON, 0,
                                                'Track_Mode_Button')
        self._level_mode_button = create_button(120, 0, 'Level_Mode_Button')
        self._select_clip_mode.subject = self._clip_mode_button
        self._select_scene_mode.subject = self._scene_mode_button
        self._select_pad_mode.subject = self._pad_mode_button
        self._select_keyboard_mode.subject = self._keyboard_mode_button
        self._select_track_mode.subject = self._track_mode_button
        self._select_level_mode.subject = self._level_mode_button
        self._solo_button = create_button(CC_SOLO_BUTTON, 0, 'Solo_Button')
        self._mute_button = create_button(CC_MUTE_BUTTON, 0, 'Mute_Button')
        self._select_button = create_button(CC_SELECT_BUTTON, 0,
                                            'Select_Button')
        self._handle_solo_button.subject = self._solo_button
        self._handle_mute_button.subject = self._mute_button
        self._handle_select_button.subject = self._select_button
        self._clip_mode = SessionMode()
        self._scene_mode = SceneMode()
        self._pad_mode = PadMode()
        self._drum_mode = DrumMode()
        self._track_mode = TrackMode()
        self._level_mode = LevelIndicatorMode()
        self._pad_mode.fitting_mode = self.fitting_mode
        self._drum_mode.fitting_mode = self.fitting_mode
        self._mode_group = MG_CLIP
        self._clip_mode.button_index = CLIP_MODE
        self._scene_mode.button_index = SCENE_MODE
        self._pad_mode.button_index = PAD_MODE
        self._drum_mode.button_index = PAD_MODE
        self._track_mode.button_index = TRACK_MODE
        self._level_mode.button_index = LEVEL_MODE
        self._clip_mode.enter_action = self.enter_clip_mode
        self._scene_mode.enter_action = self.enter_scene_mode
        self._pad_mode.enter_action = self.enter_pad_mode
        self._track_mode_button.send_value(7)
        self._mode = self._clip_mode
        self._selected_track_change.subject = self.song().view
        self._detail_clip_changed.subject = self.song().view
        self._defer_action = None
        self.__selected_track = self.song().view.selected_track
        if self.__selected_track:
            self.__on_track_color_changed.subject = self.__selected_track
        else:
            self.__on_track_color_changed.subject = None
        self.nr_frq = CTRL_TO_FREQ[4][1]
        self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0
        self._track_color = toHSB(self.song().view.selected_track.color)
        return
class ModeHandler(CompoundComponent):
    """
    Class Handling the switch between Modes.
    """
    __module__ = __name__
    __jogwheel_down = False
    __knob_action = None
    __modifier = None
    __step_down = False
    __pad_down = False
    __lock_map = {'step': False}
    __note_repeat_changed = False
    __prev_to_user_mode = None
    __return_mode = None
    _clip_mode_down = False

    def __init__(self, note_repeat=None, *a, **k):
        super(ModeHandler, self).__init__(*a, **k)
        self._note_repeat = note_repeat
        self.nr_index = 6
        self._note_repeat.repeat_rate = 1.0 / CFG_REPEAT_FREQUENCIES[
            self.nr_index] * 4.0

        def create_button(ccval, channel=0, cname=None):
            button = StateButton(True,
                                 MIDI_CC_TYPE,
                                 channel,
                                 ccval,
                                 name=cname)
            button.last_value = 0
            return button

        self._note_repeat_button = create_button(CC_NOTE_REPEAT_BUTTON, 0,
                                                 'Note_Repeat_Button')
        self._do_note_repeat.subject = self._note_repeat_button
        self._scene_mode_button = create_button(CC_SCENE_BUTTON, 0,
                                                'Scene_Mode_Button')
        self._clip_mode_button = create_button(CC_PATTERN_BUTTON, 0,
                                               'Pattern_Mode_Button')
        self._pad_mode_button = create_button(CC_PAD_BUTTON, 0,
                                              'Pad_Mode_Button')
        self._keyboard_mode_button = create_button(CC_KEYBOARD_BUTTON, 0,
                                                   'Keyboard_Mode_Button')
        self._track_mode_button = create_button(CC_MIKRO_GROUP_BUTTON, 0,
                                                'Track_Mode_Button')
        self._level_mode_button = create_button(120, 0, 'Level_Mode_Button')
        self._select_clip_mode.subject = self._clip_mode_button
        self._select_scene_mode.subject = self._scene_mode_button
        self._select_pad_mode.subject = self._pad_mode_button
        self._select_keyboard_mode.subject = self._keyboard_mode_button
        self._select_track_mode.subject = self._track_mode_button
        self._select_level_mode.subject = self._level_mode_button
        self._solo_button = create_button(CC_SOLO_BUTTON, 0, 'Solo_Button')
        self._mute_button = create_button(CC_MUTE_BUTTON, 0, 'Mute_Button')
        self._select_button = create_button(CC_SELECT_BUTTON, 0,
                                            'Select_Button')
        self._handle_solo_button.subject = self._solo_button
        self._handle_mute_button.subject = self._mute_button
        self._handle_select_button.subject = self._select_button
        self._clip_mode = SessionMode()
        self._scene_mode = SceneMode()
        self._pad_mode = PadMode()
        self._drum_mode = DrumMode()
        self._track_mode = TrackMode()
        self._level_mode = LevelIndicatorMode()
        self._pad_mode.fitting_mode = self.fitting_mode
        self._drum_mode.fitting_mode = self.fitting_mode
        self._mode_group = MG_CLIP
        self._clip_mode.button_index = CLIP_MODE
        self._scene_mode.button_index = SCENE_MODE
        self._pad_mode.button_index = PAD_MODE
        self._drum_mode.button_index = PAD_MODE
        self._track_mode.button_index = TRACK_MODE
        self._level_mode.button_index = LEVEL_MODE
        self._clip_mode.enter_action = self.enter_clip_mode
        self._scene_mode.enter_action = self.enter_scene_mode
        self._pad_mode.enter_action = self.enter_pad_mode
        self._track_mode_button.send_value(7)
        self._mode = self._clip_mode
        self._selected_track_change.subject = self.song().view
        self._detail_clip_changed.subject = self.song().view
        self._defer_action = None
        self.__selected_track = self.song().view.selected_track
        if self.__selected_track:
            self.__on_track_color_changed.subject = self.__selected_track
        else:
            self.__on_track_color_changed.subject = None
        self.nr_frq = CTRL_TO_FREQ[4][1]
        self._note_repeat.repeat_rate = 1.0 / self.nr_frq * 4.0
        self._track_color = toHSB(self.song().view.selected_track.color)
        return

    def refresh_state(self):
        self.__encoder_control.update_mode()

    def init_shift_handler(self):
        pass

    def _light_button(self, which):
        self._scene_mode_button.send_value(which == SCENE_MODE and 1 or 0,
                                           True)
        self._clip_mode_button.send_value(which == CLIP_MODE and 1 or 0, True)
        if self.canonical_parent.has_separate_pad_mode_buttons():
            if self._mode == self._drum_mode:
                self._pad_mode_button.send_value(which == PAD_MODE and 1 or 0,
                                                 True)
                self._keyboard_mode_button.send_value(0, True)
            else:
                self._keyboard_mode_button.send_value(
                    which == PAD_MODE and 1 or 0, True)
                self._pad_mode_button.send_value(0, True)
        else:
            self._pad_mode_button.send_value(which == PAD_MODE and 1 or 0,
                                             True)
        self._level_mode_button.send_value(which == LEVEL_MODE and 1 or 0,
                                           True)
        self._track_mode_button.send_value(
            which == TRACK_MODE and self._track_color[0]
            or self._track_color[1], True)

    def init_elements(self):
        self._light_button(CLIP_MODE)
        self._track_mode.init_elements()
        self._level_mode.init_elements()
        self._detect_devices_changed.subject = self.song().view.selected_track

    def navigate(self, direction, modifier, alt_modifier=False):
        self._mode.navigate(direction, modifier, alt_modifier)

    def bind_modify_component(self, modifier_component):
        pass

    def is_shift_down(self):
        return self.canonical_parent.is_shift_down()

    def get_modifier_state(self):
        return self.canonical_parent.get_modifier_state()

    def handle_modifier(self, value):
        if self.canonical_parent.use_shift_matrix():
            self._mode.handle_shift(value & 1 != 0)

    def handle_edit(self, clipslotcomp, value):
        if value == 0:
            return
        if self._mode == self._clip_mode and self.canonical_parent.is_duplicate_down(
        ):
            if self.canonical_parent.is_shift_down():
                self.double_clipslot(clipslotcomp._clip_slot)
            else:
                self.__handle_duplicate(clipslotcomp)
        else:
            if self._mode == self._clip_mode and self.canonical_parent.is_delete_down(
            ):
                self.__handle_delete(clipslotcomp)
            else:
                if self.canonical_parent.is_shift_down():
                    if self.canonical_parent.use_shift_matrix():
                        self.canonical_parent.handle_edit_action(
                            clipslotcomp.get_index())

    def double_clipslot(self, clip_slot):
        song = self.song()
        track = clip_slot.canonical_parent
        if clip_slot.clip is not None and track.has_midi_input:
            clip = clip_slot.clip
            if clip.length <= 2048.0:
                clip.duplicate_loop()
                self.canonical_parent.show_message('Double Loop : ' +
                                                   str(int(clip.length / 4)) +
                                                   ' Bars')
                song.view.detail_clip = clip
                self.application().view.focus_view('Detail/Clip')
            else:
                self.canonical_parent.show_message(
                    'Clip is to long to Duplicate')
        return

    def __handle_delete(self, clipslotcomp):
        if clipslotcomp._clip_slot is None:
            return
        if self.is_shift_down():
            pass
        else:
            clipslotcomp._do_delete_clip()
        return

    def __handle_duplicate(self, clipslotcomp):
        if clipslotcomp._clip_slot is None:
            return
        if self.is_shift_down():
            pass
        else:
            self.duplicate_clip_slot(clipslotcomp._clip_slot)
        return

    def duplicate_clip_slot(self, clip_slot):
        if clip_slot.has_clip:
            try:
                track = clip_slot.canonical_parent
                index = list(track.clip_slots).index(clip_slot)
                track.duplicate_clip_slot(index)
                self.canonical_parent.show_message('Duplicate Clip ' +
                                                   clip_slot.clip.name)
                select_clip_slot(self.song(), track.clip_slots[index + 1])
            except Live.Base.LimitationError:
                pass
            except RuntimeError:
                pass

    def get_color(self, value, column_index, row_index):
        return self._mode.get_color(value, column_index, row_index)

    def change_mode(self, nextmode):
        return self._mode != nextmode and (self._mode == None
                                           or self._mode.is_lock_mode())

    @subject_slot('value')
    def _select_clip_mode(self, value):
        click = value != 0
        if click:
            if self.change_mode(self._clip_mode):
                with self.rebuild():
                    self._mode.exit()
                    self._mode = self._clip_mode
                    self._light_button(CLIP_MODE)
                    self._mode.enter()
        self._clip_mode_down = click

    def in_pad_mode(self):
        return self._mode == self._pad_mode or self._mode == self._drum_mode

    @subject_slot('value')
    def _do_note_repeat(self, value):
        if value == 0:
            self._note_repeat_button.send_value(0)
            self._note_repeat.enabled = False
            if self.__return_mode != None:
                self._enter_mode(self.__return_mode)
                self.__return_mode = None
        else:
            self._note_repeat_button.send_value(127)
            self._note_repeat.enabled = True
            if not self.in_pad_mode():
                self.__return_mode = self._mode
                self._select_pad_mode(1)
        return

    def _return_to_mode(self):
        if self.__return_mode:
            self._enter_mode(self.__return_mode)
            self.__return_mode = None
        return

    def _enter_mode(self, mode):
        if self.change_mode(mode):
            with self.rebuild():
                self._mode.exit()
                self._mode = mode.fitting_mode(self.song().view.selected_track)
                self._light_button(self._mode.button_index)
                self._mode.enter()

    @subject_slot('value')
    def _select_scene_mode(self, value):
        if value != 0:
            if self.change_mode(self._scene_mode):
                with self.rebuild():
                    self._mode.exit()
                    self._mode = self._scene_mode
                    self._light_button(SCENE_MODE)
                    self._mode.enter()

    @subject_slot('value')
    def _select_keyboard_mode(self, value):
        if value != 0:
            if self.change_mode(self._pad_mode):
                track = self.song().view.selected_track
                newmode = self.fitting_mode(track)
                with self.rebuild():
                    self._mode.exit()
                    self._mode = newmode
                    self._light_button(PAD_MODE)
                    self._mode.enter()

    @subject_slot('value')
    def _select_pad_mode(self, value):
        if value != 0:
            if self.change_mode(self._pad_mode):
                track = self.song().view.selected_track
                newmode = self.fitting_mode(track)
                with self.rebuild():
                    self._mode.exit()
                    self._mode = newmode
                    self._light_button(PAD_MODE)
                    self._mode.enter()

    @subject_slot('value')
    def _select_level_mode(self, value):
        if value == 0:
            self._return_to_mode()
        else:
            self.__return_mode = self._mode
            self._enter_mode(self._level_mode)

    @subject_slot('value')
    def _select_track_mode(self, value):
        if value == 0:
            self._return_to_mode()
        else:
            self.__return_mode = self._mode
            self._enter_mode(self._track_mode)

    def enter_scene_mode(self, show_info=True):
        pass

    def enter_pad_mode(self, show_info=True):
        pass

    def enter_clip_mode(self, show_info=True):
        if self.change_mode(self._clip_mode):
            if show_info:
                try:
                    self._show_msg_callback('SESSION VIEW Mode')
                except:
                    pass

            with self.rebuild():
                self._mode.exit()
                self._mode.spec_unbind()
                self._mode = self._clip_mode
                self._light_button(0)
                self._mode.enter()

    def fitting_mode(self, track):
        if not track:
            return self._pad_mode
        drum_device = find_drum_device(track)
        if drum_device != None:
            return self._drum_mode
        return self._pad_mode

    @subject_slot('color')
    def __on_track_color_changed(self):
        if self._mode == self._pad_mode:
            self._pad_mode.update_color()

    @subject_slot('selected_track')
    def _selected_track_change(self):
        selected_track = self.song().view.selected_track
        if selected_track:
            self._track_color = toHSB(selected_track.color)
            self._track_mode_button.send_value(
                self._mode.get_mode_id() == TRACK_MODE and self._track_color[0]
                or self._track_color[1], True)
        if self._mode == self._pad_mode or self._mode == self._drum_mode:
            newmode = self.fitting_mode(selected_track)
            if newmode != self._mode:
                with self.rebuild():
                    self._mode.exit()
                    self._mode = newmode
                    self._light_button(PAD_MODE)
                    self._mode.enter()
            else:
                self._mode.update_selected_track()

    @subject_slot('detail_clip')
    def _detail_clip_changed(self):
        newclip = self.song().view.detail_clip
        if newclip and newclip.is_midi_clip:
            pass

    @subject_slot('devices')
    def _detect_devices_changed(self):
        pass

    @subject_slot('value')
    def _handle_solo_button(self, value):
        if self.is_shift_down():
            pass
        else:
            self.canonical_parent.get_track_buttons().trigger_solo(
                self._solo_button, value)

    @subject_slot('value')
    def _handle_mute_button(self, value):
        if self.is_shift_down():
            pass
        else:
            self.canonical_parent.get_track_buttons().trigger_mute(
                self._mute_button, value)

    @subject_slot('value')
    def _handle_select_button(self, value):
        self._select_button.send_value(value, True)
        if value == 0:
            self.canonical_parent.get_track_buttons().trigger_to_prev()
        else:
            self.canonical_parent.get_track_buttons().trigger_stop()

    @contextmanager
    def rebuild(self):
        self.canonical_parent._set_suppress_rebuild_requests(True)
        yield
        self.canonical_parent._set_suppress_rebuild_requests(False)
        self.canonical_parent.request_rebuild_midi_map()

    def notify(self, blink_state):
        self._mode.notify(blink_state)
    def __init__(self, note_repeat=None, *a, **k):
        super(JamModes, self).__init__(*a, **k)
        self._note_repeat = note_repeat
        self.nr_index = 6
        self._note_repeat.repeat_rate = 1.0 / CFG_REPEAT_FREQUENCIES[self.nr_index] * 4.0

        def create_button(ccval, channel=0, cname=None):
            button = StateButton(True, MIDI_CC_TYPE, channel, ccval, name=cname)
            button.last_value = 0
            return button

        self.__main_knob = GrabEncoder(MIDI_CC_TYPE, 0, 86, name='Browse_Control')
        self.__push_button = TouchButton(MIDI_CC_TYPE, 0, 87, name='Browse_Push')
        self.__touch_main = TouchButton(MIDI_CC_TYPE, 0, 88, name='Browse_Touch')
        self._do_main_knob.subject = self.__main_knob
        self._do_push_button.subject = self.__push_button
        self._knob_action = None
        self.__instance_button = create_button(62, 0, 'Instance_1_Button')
        self.__perform_button = create_button(45, 0, 'Perform_Button')
        self.__tune_button = create_button(48, 0, 'Tune_Button')
        self.__swing_button = create_button(49, 0, 'Swing_Button')
        self.__browse_button = create_button(44, 0, 'Browse_Mode_Button')
        self.__do_browse_button.subject = self.__browse_button
        self._arrange_button = create_button(30, 0, 'Song_Mode_Button')
        self._arrange_button.view_mode = VM_SESSION
        self._pad_mode_button = create_button(32, 0, 'Pad_Mode_Button')
        self._step_mode_button = create_button(31, 0, 'Step_Mode_Button')
        self._solo_button = create_button(111, 0, 'Solo_Button')
        self._mute_button = create_button(112, 0, 'Mute_Button')
        self._grid_button = create_button(113, 0, 'Grid_Button')
        self.__notes_button = create_button(46, 0, 'Notes_Button')
        master_button = create_button(60, 0, 'Master_Button')
        group_button = create_button(61, 0, 'Group_Button')
        cue_button = create_button(63, 0, 'Cue_Button')
        self._tempo_button = create_button(110, 0, 'Tempo_Button')
        self._note_repeat_button = create_button(94, 0, 'Note_Repeat_Button')
        self._handle_master_button.subject = master_button
        self._handle_group_button.subject = group_button
        self._handle_cue_button.subject = cue_button
        self._handle_tempo_button.subject = self._tempo_button
        self._tempo_button.selected = False
        self._handle_solo_button.subject = self._solo_button
        self._handle_mute_button.subject = self._mute_button
        self._handle_grid_button.subject = self._grid_button
        self._handle_note_repeat.subject = self._note_repeat_button
        self.__encoder_control = MainEncoderControl(self, master_button, group_button, cue_button, self._grid_button, self._tempo_button, self._note_repeat_button, self._solo_button)
        self.__encoder_control.set_selected_track(self.song().view.selected_track)
        self._clip_mode = SessionMode(0)
        self._pad_mode = PadMode(1)
        self._drum_pad_mode = DrumMode(1)
        self._step_mode = StepMode(2, self._pad_mode)
        self._user_mode = UserMode(4)
        self._drum_step_mode = DrumStepMode(3)
        self._mode_group = MG_CLIP
        self._clip_mode.enter_action = self.enter_clip_mode
        self._pad_mode.enter_action = self.enter_pad_mode
        self._drum_pad_mode.enter_action = self.enter_pad_mode
        self._step_mode.enter_action = self.enter_step_mode
        self._user_mode.enter_action = self.enter_user_mode
        self._do_arrange_button.subject = self._arrange_button
        self._do_pad_mode.subject = self._pad_mode_button
        self._do_step_mode.subject = self._step_mode_button
        self._do_midi_capture.subject = self.__notes_button
        self._mode = self._clip_mode
        self._selected_track_change.subject = self.song().view
        self._detail_clip_changed.subject = self.song().view
        self._capture_changed.subject = self.song()
        self.update_arrange_button()
        self._defer_action = None
        return
class JamModes(CompoundComponent):
    """
    Class Handling the switch between Modes.
    """
    __module__ = __name__
    __track_buttons = None
    __scene_buttons = None
    __jogwheel_down = False
    __knob_action = None
    __modifier = None
    __step_down = False
    __pad_down = False
    __lock_map = {'step': False}
    __note_repeat_changed = False
    __prev_to_user_mode = None

    def __init__(self, note_repeat=None, *a, **k):
        super(JamModes, self).__init__(*a, **k)
        self._note_repeat = note_repeat
        self.nr_index = 6
        self._note_repeat.repeat_rate = 1.0 / CFG_REPEAT_FREQUENCIES[self.nr_index] * 4.0

        def create_button(ccval, channel=0, cname=None):
            button = StateButton(True, MIDI_CC_TYPE, channel, ccval, name=cname)
            button.last_value = 0
            return button

        self.__main_knob = GrabEncoder(MIDI_CC_TYPE, 0, 86, name='Browse_Control')
        self.__push_button = TouchButton(MIDI_CC_TYPE, 0, 87, name='Browse_Push')
        self.__touch_main = TouchButton(MIDI_CC_TYPE, 0, 88, name='Browse_Touch')
        self._do_main_knob.subject = self.__main_knob
        self._do_push_button.subject = self.__push_button
        self._knob_action = None
        self.__instance_button = create_button(62, 0, 'Instance_1_Button')
        self.__perform_button = create_button(45, 0, 'Perform_Button')
        self.__tune_button = create_button(48, 0, 'Tune_Button')
        self.__swing_button = create_button(49, 0, 'Swing_Button')
        self.__browse_button = create_button(44, 0, 'Browse_Mode_Button')
        self.__do_browse_button.subject = self.__browse_button
        self._arrange_button = create_button(30, 0, 'Song_Mode_Button')
        self._arrange_button.view_mode = VM_SESSION
        self._pad_mode_button = create_button(32, 0, 'Pad_Mode_Button')
        self._step_mode_button = create_button(31, 0, 'Step_Mode_Button')
        self._solo_button = create_button(111, 0, 'Solo_Button')
        self._mute_button = create_button(112, 0, 'Mute_Button')
        self._grid_button = create_button(113, 0, 'Grid_Button')
        self.__notes_button = create_button(46, 0, 'Notes_Button')
        master_button = create_button(60, 0, 'Master_Button')
        group_button = create_button(61, 0, 'Group_Button')
        cue_button = create_button(63, 0, 'Cue_Button')
        self._tempo_button = create_button(110, 0, 'Tempo_Button')
        self._note_repeat_button = create_button(94, 0, 'Note_Repeat_Button')
        self._handle_master_button.subject = master_button
        self._handle_group_button.subject = group_button
        self._handle_cue_button.subject = cue_button
        self._handle_tempo_button.subject = self._tempo_button
        self._tempo_button.selected = False
        self._handle_solo_button.subject = self._solo_button
        self._handle_mute_button.subject = self._mute_button
        self._handle_grid_button.subject = self._grid_button
        self._handle_note_repeat.subject = self._note_repeat_button
        self.__encoder_control = MainEncoderControl(self, master_button, group_button, cue_button, self._grid_button, self._tempo_button, self._note_repeat_button, self._solo_button)
        self.__encoder_control.set_selected_track(self.song().view.selected_track)
        self._clip_mode = SessionMode(0)
        self._pad_mode = PadMode(1)
        self._drum_pad_mode = DrumMode(1)
        self._step_mode = StepMode(2, self._pad_mode)
        self._user_mode = UserMode(4)
        self._drum_step_mode = DrumStepMode(3)
        self._mode_group = MG_CLIP
        self._clip_mode.enter_action = self.enter_clip_mode
        self._pad_mode.enter_action = self.enter_pad_mode
        self._drum_pad_mode.enter_action = self.enter_pad_mode
        self._step_mode.enter_action = self.enter_step_mode
        self._user_mode.enter_action = self.enter_user_mode
        self._do_arrange_button.subject = self._arrange_button
        self._do_pad_mode.subject = self._pad_mode_button
        self._do_step_mode.subject = self._step_mode_button
        self._do_midi_capture.subject = self.__notes_button
        self._mode = self._clip_mode
        self._selected_track_change.subject = self.song().view
        self._detail_clip_changed.subject = self.song().view
        self._capture_changed.subject = self.song()
        self.update_arrange_button()
        self._defer_action = None
        return

    def refresh_state(self):
        self.__encoder_control.update_mode()
        self.__scene_buttons.refresh_state()
        self.__track_buttons.refresh_state()

    def init_elements(self):
        self._light_button(0)
        self.__encoder_control.update_mode()
        self._detect_devices_changed.subject = self.song().view.selected_track
        self.__notes_button.set_display_value(self.song().can_capture_midi and 127 or 0)

    def bind_session(self, matrix_state):
        session = self.canonical_parent.get_session()
        self.__track_buttons = TrackControlComponent(session, self, name='Track_Select_Button_Matrix')
        self.__track_buttons.assign_buttons()
        self.__scene_buttons = SceneButtonComponent(session, name='Scene_Launch_Button_Matrix')
        self.__scene_buttons.assign()
        matrix_state.register_matrix(self.__track_buttons._bmatrix)
        matrix_state.register_matrix(self.__scene_buttons._bmatrix)
        self._step_mode.set_page_handler(self.__scene_buttons)
        self._drum_step_mode.set_page_handler(self.__scene_buttons)
        session.set_track_offset_listener(self.__update_tracks)

    def bind_modify_component(self, modifier_component):
        self.__scene_buttons.bind_modify_component(modifier_component)
        self.__modifier = modifier_component
        self.__modifier.register_shift_listener(self)
        self._pad_mode.set_modifier_component(modifier_component)
        self._drum_pad_mode.set_modifier_component(modifier_component)

    def _light_button(self, which):
        self._pad_mode_button.set_display_value(which == 1 and 127 or 0, True)
        self._step_mode_button.set_display_value(which == 2 and 127 or 0, True)
        self._solo_button.set_display_value(0, True)
        self._mute_button.set_display_value(0, True)
        self._grid_button.set_display_value(0, True)

    @property
    def selected_mode(self):
        return self._mode.ext_name()

    @selected_mode.setter
    def selected_mode(self, value):
        if value == self._mode.ext_name():
            pass
        else:
            if value == 'session_mode':
                self.enter_clip_mode(False)
            else:
                if value == 'pad_mode':
                    self.enter_pad_mode(False)
                else:
                    if value == 'step_mode':
                        self.enter_step_mode(False)
                    else:
                        if value == 'user_mode':
                            self.enter_user_mode(False)

    def handle_track_select(self, track):
        if not track:
            return
        modmask = self.__modifier.modifier_mask()
        if modmask & MASK_CLEAR:
            index = vindexof(self.song().tracks, track)
            self.song().delete_track(index)
        else:
            if modmask & MASK_DUPLICATE:
                index = vindexof(self.song().tracks, track)
                self.song().duplicate_track(index)
            else:
                if modmask & MASK_SELECT:
                    if track.is_foldable:
                        track.fold_state = track.fold_state == 0 and 1 or 0
                else:
                    if modmask & MASK_SHIFT:
                        self.song().view.selected_track = track
                    else:
                        self.song().view.selected_track = track
                        arm_exclusive(self.song(), track)

    def change_mode(self, nextmode):
        return self._mode != nextmode and (self._mode is None or self._mode.is_lock_mode())

    def get_color(self, value, column_index, row_index):
        return self._mode.get_color(value, column_index, row_index)

    def notify(self, blink_state):
        self.__track_buttons.notify(blink_state)
        self._mode.notify(blink_state)
        self.__scene_buttons.notify(blink_state)
        if blink_state == 0:
            self.update_arrange_button()
        if self._defer_action:
            self._defer_action()
            self._defer_action = None
        return

    @subject_slot('selected_track')
    def _selected_track_change(self):
        selected_track = self.song().view.selected_track
        self.__encoder_control.set_selected_track(self.song().view.selected_track)
        self._detect_devices_changed.subject = selected_track
        if self._mode.device_dependent():
            self.__handle_possible_instrument_change()

    @subject_slot('devices')
    def _detect_devices_changed(self):
        self.__handle_possible_instrument_change()

    def __handle_possible_instrument_change(self):
        drum_device = find_drum_device(self.song().view.selected_track)
        if drum_device:
            if self._mode == self._pad_mode:
                self.enter_pad_mode()
            elif self._mode == self._step_mode:
                self.enter_step_mode(False, True)
        else:
            if self._mode == self._drum_pad_mode:
                self.enter_pad_mode()
            else:
                if self._mode == self._drum_step_mode:
                    self.enter_step_mode(False, True)

    @subject_slot('can_capture_midi')
    def _capture_changed(self):
        self.__notes_button.set_display_value(self.song().can_capture_midi and 127 or 0)

    @subject_slot('detail_clip')
    def _detail_clip_changed(self):
        newclip = self.song().view.detail_clip
        if newclip and newclip.is_midi_clip:
            if self._mode == self._step_mode or self._mode == self._drum_step_mode:
                self.enter_step_mode()

    def update_arrange_button(self):
        appv = self.application().view
        if appv.is_view_visible('Arranger'):
            if self._arrange_button.view_mode == VM_SESSION:
                self._arrange_button.set_display_value(127, True)
                self._arrange_button.view_mode = VM_ARRANGE
        else:
            if self._arrange_button.view_mode == VM_ARRANGE:
                self._arrange_button.set_display_value(0, True)
                self._arrange_button.view_mode = VM_SESSION

    @subject_slot('value', identify_sender=True)
    def _do_arrange_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        appv = self.application().view
        if appv.is_view_visible('Arranger'):
            self.application().view.show_view('Session')
        else:
            self.application().view.show_view('Arranger')
        self.update_arrange_button()

    def enter_user_mode(self):
        if self.change_mode(self._user_mode):
            self.__prev_to_user_mode = self._mode
            with self.rebuild():
                self._mode.exit()
                self._mode.spec_unbind()
                self._mode = self._user_mode
                self._light_button(4)
                self._mode.enter()

    def exit_user_mode(self):
        if self._mode == self._user_mode and self.__prev_to_user_mode:
            self.__prev_to_user_mode.enter_action(False)

    def handle_user_mode_removed(self):
        self.exit_user_mode()

    def ensure_reset_note_repeat_mode(self):
        if self._note_repeat.enabled:
            self._note_repeat.enabled = False
            self.__encoder_control.update_note_repeat(0)
            self.__encoder_control.reset_mode()

    def enter_clip_mode(self, show_info=True):
        self.ensure_reset_note_repeat_mode()
        if self.change_mode(self._clip_mode):
            if show_info:
                try:
                    self._show_msg_callback('SESSION VIEW Mode')
                except:
                    pass

            with self.rebuild():
                self._mode.exit()
                self._mode.spec_unbind()
                self._mode = self._clip_mode
                self._light_button(0)
                self._mode.enter()

    def notify_shift(self, shift_value):
        if self._mode == self._pad_mode:
            self._pad_mode.handle_shift(shift_value)
        else:
            if self._mode == self._drum_step_mode:
                self._drum_step_mode.handle_shift(shift_value)
        self.__scene_buttons.notify_shift(shift_value)
        self.__encoder_control.update_mode()

    def is_shift_down(self):
        return self.__modifier.is_shift_down()

    def in_track_mode(self, mode):
        return self.__track_buttons.mode == mode

    def enter_pad_mode(self, show_info=True, check_drum_device=0):
        if show_info:
            self._show_msg_callback('PAD Mode')
        selected_track = self.song().view.selected_track
        if not selected_track or not selected_track.has_midi_input:
            return
        drum_device = find_drum_device(selected_track)
        if drum_device:
            with self.rebuild():
                self._mode.exit()
                self._mode.spec_unbind()
                self._mode = self._drum_pad_mode
                self._light_button(1)
                self._mode.enter()
        else:
            with self.rebuild():
                self._mode.exit()
                self._mode.spec_unbind()
                self._mode = self._pad_mode
                self._light_button(1)
                self._mode.enter()
        self._mode_group = MG_PAD

    def create_clip_on_track(self):
        track = self.song().view.selected_track
        if not track or not track.has_midi_input:
            return
        higlighted_clip_slot = self.song().view.highlighted_clip_slot
        if not higlighted_clip_slot.has_clip:
            higlighted_clip_slot.create_clip(4.0)
        self.song().view.detail_clip = higlighted_clip_slot.clip
        self.application().view.focus_view('Detail/Clip')
        return higlighted_clip_slot.clip

    def enter_step_mode(self, show_info=True, block_clip_creation=False):
        clip = self.song().view.detail_clip
        if not clip and not block_clip_creation:
            clip = self.create_clip_on_track()
        if not clip or not clip.is_midi_clip:
            return
        selected_track = self.song().view.selected_track
        if not selected_track or not selected_track.has_midi_input:
            return
        self.ensure_reset_note_repeat_mode()
        self._light_button(2)
        selected_track = clip.canonical_parent.canonical_parent
        drum_device = find_drum_device(selected_track)
        if drum_device:
            show_info and self._show_msg_callback('Drum Step Mode')
            with self.rebuild():
                if self._mode != self._drum_step_mode:
                    self._mode.exit()
                    self._mode = self._drum_step_mode
                self._light_button(2)
                self._mode.enter()
                self._drum_step_mode.register_page_handler()
                self._defer_action = lambda : self._drum_step_mode.arm_to_clip()
        else:
            show_info and self._show_msg_callback('Step Mode')
            with self.rebuild():
                if self._mode != self._step_mode:
                    self._mode.exit()
                    self._mode = self._step_mode
                self._light_button(2)
                self._mode.enter()
                self._step_mode.register_page_handler()
        self._mode_group = MG_STEP

    @subject_slot('value', identify_sender=True)
    def _do_pad_mode(self, value, sender):
        if sender.grabbed:
            return
        if self.__pad_down and value == 0:
            if self._mode_group != MG_PAD:
                self.enter_pad_mode()
            else:
                self.enter_clip_mode()
                self._mode_group = MG_CLIP
        self.__pad_down = value != 0

    def notify_state(self, state, value):
        if state == 'step' and self._mode_group == MG_STEP:
            self.__lock_map[state] = True

    @subject_slot('value', identify_sender=True)
    def _do_midi_capture(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if self.song().can_capture_midi:
            self.song().capture_midi()

    @subject_slot('value', identify_sender=True)
    def _do_step_mode(self, value, sender):
        if sender.grabbed:
            return
        if self.__step_down and value == 0:
            if self._mode_group != MG_STEP:
                self.enter_step_mode()
            elif not self.__lock_map['step']:
                self.enter_clip_mode()
                self._mode_group = MG_CLIP
        self.__step_down = value != 0
        if value == 0:
            self.__lock_map['step'] = False
        if self._mode_group == MG_STEP and self.__step_down:
            self.__encoder_control.trigger_mode(ME_STEP_LEN)
        else:
            self.__encoder_control.reset_mode()

    @subject_slot('value', identify_sender=True)
    def _handle_solo_button(self, value, sender):
        if value != 0 or sender.grabbed:
            return
        if self.__modifier.is_shift_down():
            self.__encoder_control.trigger_mode(ME_PAT_LEN)
        else:
            self.__track_buttons.trigger_solo(self._solo_button)

    @subject_slot('value', identify_sender=True)
    def _handle_mute_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        self.__track_buttons.trigger_mute(self._mute_button)

    @subject_slot('value', identify_sender=True)
    def _handle_grid_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if self.__modifier.is_shift_down():
            self.__track_buttons.trigger_arm(self._grid_button)
        else:
            self.__encoder_control.trigger_mode(ME_GRID)

    def __get_enter_action(self):
        if self._mode_group == MG_CLIP:
            return self.enter_clip_mode
        if self._mode_group == MG_PAD:
            return self.enter_pad_mode
        return self.enter_step_mode

    def change_pattern_length(self, incval, modifier):
        clip = self.song().view.detail_clip
        if clip != None and clip.is_midi_clip:
            newlen = clip.loop_end + incval * (modifier and 1.0 or clip.signature_numerator)
            if newlen > 1.0 and newlen < 128.0:
                clip.loop_end = newlen
                clip.end_marker = newlen
                self.application().view.focus_view('Detail/Clip')
                self.canonical_parent.show_message(' Clip Length ' + str(clip.length))
        return

    def set_nr_value(self, incval, push_down):
        if push_down:
            inc_spec = self.nr_index % 2 == 1 and incval or incval * 2
            self.nr_index = min(max(0, self.nr_index + inc_spec), len(CFG_REPEAT_FREQUENCIES) - 1)
            self._note_repeat.repeat_rate = 1.0 / CFG_REPEAT_FREQUENCIES[self.nr_index] * 4.0
            self.canonical_parent.show_message(' NOTE REPEAT ' + nr_string(self.nr_index, True))
        else:
            self.nr_index = min(max(0, self.nr_index + incval), len(CFG_REPEAT_FREQUENCIES) - 1)
            self._note_repeat.repeat_rate = 1.0 / CFG_REPEAT_FREQUENCIES[self.nr_index] * 4.0
            self.canonical_parent.show_message(' NOTE REPEAT ' + nr_string(self.nr_index))
        self.__note_repeat_changed = True

    @subject_slot('value', identify_sender=True)
    def _handle_note_repeat(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if not self._note_repeat.enabled:
            self.__encoder_control.trigger_mode(ME_NOTE_REPEAT)
            self.__note_repeat_changed = False
            if self._mode_group != MG_PAD:
                self.__back_action = self.__get_enter_action()
                self.__back_mode = self._mode_group
                self.enter_pad_mode()
            else:
                self.__back_action = None
            if not self._note_repeat.enabled:
                self._note_repeat.enabled = True
            self.canonical_parent.show_message(' NOTE REPEAT ' + nr_string(self.nr_index))
            self.__encoder_control.update_note_repeat(127)
        else:
            self._note_repeat.enabled = False
            self.__encoder_control.reset_mode()
            self.__encoder_control.update_note_repeat(0)
            if self.__back_action:
                self.__back_action()
                self._mode_group = self.__back_mode
        return

    @subject_slot('value', identify_sender=True)
    def _handle_tempo_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        if self.__modifier.is_shift_down():
            self.song().tap_tempo()
        else:
            self.__encoder_control.trigger_mode(ME_TEMPO)

    @subject_slot('value', identify_sender=True)
    def _handle_master_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        self.__encoder_control.trigger_mode(ME_MASTER)

    @subject_slot('value', identify_sender=True)
    def _handle_cue_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        self.__encoder_control.trigger_mode(ME_CUE)

    @subject_slot('value', identify_sender=True)
    def _handle_group_button(self, value, sender):
        if value == 0 or sender.grabbed:
            return
        self.__encoder_control.trigger_mode(ME_GROUP)

    @subject_slot('value')
    def _do_main_knob(self, value):
        if not self.__main_knob.is_grabbed:
            self.__encoder_control.handle_encoder(value == 127 and -1 or 1, self.__jogwheel_down)

    @subject_slot('value', identify_sender=True)
    def _do_push_button(self, value, sender):
        if not sender.grabbed:
            self.__jogwheel_down = value > 0

    @subject_slot('value', identify_sender=True)
    def __do_browse_button(self, value, sender):
        if sender.grabbed:
            return
        browsedown = value != 0
        self.__browse_button.set_display_value(value)
        self.__modifier.set_browse_down(browsedown)
        modmask = self.__modifier.modifier_mask()
        if browsedown and modmask & MASK_SHIFT:
            self.song().stop_all_clips(1)
        else:
            if browsedown:
                self.__track_buttons.trigger_stop()
            else:
                self.__track_buttons.trigger_to_prev()

    @contextmanager
    def rebuild(self):
        self.canonical_parent._set_suppress_rebuild_requests(True)
        yield
        self.canonical_parent._set_suppress_rebuild_requests(False)
        self.canonical_parent.request_rebuild_midi_map()

    def __update_tracks(self, track_offset):
        self.__track_buttons.assign_buttons()
        self.canonical_parent._encoder_modes.update_encoders()

    def navigate(self, direction, modifier, alt_modifier=False, nav_source=NAV_SRC_ENCODER):
        self._mode.navigate(direction, modifier, alt_modifier, nav_source)
        self.__scene_buttons.assign()
        if self._mode_group == MG_CLIP:
            return True
        self.__track_buttons.assign_buttons()
        return False