Пример #1
0
class IcarusTouchWidget(Widget):
    app = ObjectProperty(None)
    float_layout = ObjectProperty(None)
    pitch_lock_button = ObjectProperty(None)
    y_axis_volume_button = ObjectProperty(None)
    setting_button = ObjectProperty(None)
    look_button = ObjectProperty(None)
    feedback_wall = ObjectProperty(None)
    scale_keywidth_touch_positions = {}
    version = StringProperty(VERSION)
    '''
    ####################################
    ##
    ##   Class Initialisation
    ##
    ####################################
    '''
    def __init__(self, **kwargs):
        super(IcarusTouchWidget,
              self).__init__(**kwargs)  # don't know if this is necessary?

        # add background image (and add it in the BACKGROUND! --> index modification)
        self.background = Background(
            source=self.app.config.get('Graphics', 'Background'))
        self.float_layout.add_widget(self.background,
                                     index=len(self.float_layout.children))

        # add feedback wall image
        self.feedback_wall = Feedback(source='images/feedbackwall.png',
                                      transparency=0)
        self.float_layout.add_widget(self.feedback_wall)

        # add the keyboard itself
        my_key_width = KEY_WIDTH
        self.keyboard = Keyboard(
            source=self.app.config.get('Graphics', 'Keyboard'),
            pos=(-540, 366),
            size=(
                12 * 5 * my_key_width, 468
            ),  # optimization for small screens (e.g. smartphones): 468 if self.get_parent_window().height > (468 + self.get_parent_window().height * 0.3) else self.get_parent_window().height * 0.7
            border_width=BORDER_WIDTH,
            key_width=my_key_width)
        self.add_widget(self.keyboard)

        # initialize the midi device
        pygame.midi.init()
        self.set_midi_device()

        # initialize the settings_panel (I'm doing this here, otherwise opening it in real-time takes ages...)
        self.my_settings_panel = MySettingsPanel()

    '''
    ####################################
    ##
    ##   On Touch Down
    ##
    ####################################
    '''

    def on_touch_down(self, touch):
        ud = touch.ud

        # find out the touchs "function":
        if self.my_settings_panel.is_open == True:
            # if the settingspanel is opened, it has total focus!
            # so if the user clicked on one of the two panels, don't dispatch the touch here but feed it to the panel
            if self.my_settings_panel.keyboard_scroll_view_box.collide_point(*touch.pos) or \
                self.my_settings_panel.background_scroll_view_grid.collide_point(*touch.pos):
                ud['settingspanel'] = True
                return super(IcarusTouchWidget, self).on_touch_down(touch)
            else:
                # user has clicked aside - he wants to quit the settings.
                self.close_my_settings_panel()
                return True

        elif self.keyboard.collide_point(*touch.pos):
            # if the user touched on the keyboard, feed the touch to the keyboard.
            return super(IcarusTouchWidget, self).on_touch_down(touch)

        elif self.pitch_lock_button.collide_point(*touch.pos) or \
            self.y_axis_volume_button.collide_point(*touch.pos) or \
            self.look_button.collide_point(*touch.pos) or \
            self.settings_button.collide_point(*touch.pos):
            # if the user touched one of the buttons, feed the touch to the buttons
            self.create_circle(touch)
            return super(IcarusTouchWidget, self).on_touch_down(touch)

        ##########################
        # X-Axis Key width scaling
        ##########################
        '''
        # if it wasn't a button nor a key touch nor a settings touch, its a scroll touch.
        for search_touch in EventLoop.touches[:]:
            # but if there is already another touch in scroll mode (and not already in 'scale' mode), maybe the desired action is 'scale' not 'scroll'
            if 'scroll' in search_touch.ud and not self.scale_keywidth_touch_positions:
                # remember their actual positions
                self.scale_keywidth_touch_positions = {'initial_first': search_touch.x, 'initial_second': touch.x, 'first_touch': search_touch, 'second_touch': touch}
                print 'multiple saving position data'
        '''

        # assign the scroll-tag not until here. Thus we just get the other existing scroll-touches in the search-routine above.
        ud['scroll'] = True

        # if there was an keyboard "spring back" animation in progress, stop it.
        self.keyboard.keyboard_x_animation.stop(self.keyboard)

    '''
    ####################################
    ##
    ##   On Touch Move
    ##
    ####################################
    '''

    def on_touch_move(self, touch):
        ud = touch.ud

        ##########################
        # X-Axis Key width scaling
        ##########################
        '''
        # if there are two fingers in scroll mode (thus the user dictionary key 'scale_keywidth_touch_positions' isn't empty) - maybe he wants to scale the keywidth?   
        scalepos = self.scale_keywidth_touch_positions
        if scalepos:
            delta_first_touch = scalepos['first_touch'].x - scalepos['initial_first']
            delta_second_touch = scalepos['second_touch'].x - scalepos['initial_second']
            
            #print 'move: delta_first = %s, delta_second = %s' % (delta_first_touch, delta_second_touch)
            
            # if the user dragged both touches towards each other or vice versa
            if (delta_first_touch > SCALE_KEYWIDTH_THRESHOLD and delta_second_touch > -SCALE_KEYWIDTH_THRESHOLD) or \
                (delta_first_touch > -SCALE_KEYWIDTH_THRESHOLD and delta_second_touch > SCALE_KEYWIDTH_THRESHOLD):
                print 'scale mode!'
                
                # if the first touch was on the left and the user moved it right OR it was right and moved to the left, we want to make the keywidth smaller
                if (scalepos['initial_first'] < scalepos['initial_second'] and delta_first_touch > 0) or \
                    (scalepos['initial_first'] > scalepos['initial_second'] and delta_first_touch < 0):
                    print 'zoom out'
                    # perform a ZOOM OUT with a factor applied to the delta_touch_distance:
                    delta_touch_distance = abs(delta_first_touch + (-delta_second_touch))
                    self.keyboard.key_width -= int(delta_touch_distance / SCALE_KEYWIDTH_FACTOR)
                    self.keyboard.width = 12*5*self.keyboard.key_width
                    
                # if the first touch was on the left and the user moved it left OR it was right and moved to the right, we want to make the keywidth smaller
                elif (scalepos['initial_first'] < scalepos['initial_second'] and delta_first_touch < 0) or \
                    (scalepos['initial_first'] > scalepos['initial_second'] and delta_first_touch > 0):
                    print 'zoom in'
                    # perform a ZOOM IN in with a factor applied to the delta_touch_distance:
                    delta_touch_distance = abs(delta_first_touch + (-delta_second_touch))
                    self.keyboard.key_width += int(delta_touch_distance / SCALE_KEYWIDTH_FACTOR)
                    self.keyboard.width = 12*5*self.keyboard.key_width
        '''

        if 'scroll' in ud:
            # move keyboard left or right.
            # nice side effect: if there is more than 1 touch in 'scroll' mode, scrolling is faster!
            scroll_factor = 1

            # if the end of the keyboard is visible, scroll slower!
            if self.keyboard.x > 0 or self.keyboard.x < (
                    self.get_parent_window().width - self.keyboard.width):
                counter = 0
                for touch in EventLoop.touches[:]:
                    if 'scroll' in touch.ud:
                        counter += 1  # how many touches are in 'scroll' mode?

                # if there is more than one touch in scroll mode, i don't want the factor to grow!
                scroll_factor = SCROLLING_STICKY_FACTOR / counter

            # apply the scroll action
            self.keyboard.x += touch.dx * scroll_factor
            return

        # if the touch was started on the keyboard
        if 'initial_key' in ud or 'settingspanel' in ud:
            return super(IcarusTouchWidget, self).on_touch_move(touch)

    '''
    ####################################
    ##
    ##   On Touch Up
    ##
    ####################################
    '''

    def on_touch_up(self, touch):
        ud = touch.ud

        #@@@@@@@@@@@@@@@@
        # Scroll Touch
        #@@@@@@@@@@@@@@@@

        # if there has been scale mode active or perhaps happening: (better would be: "and there aren't multiple 'scroll' mode touches active anymore
        if self.scale_keywidth_touch_positions:
            # delete all scale mode variables
            #print 'deleting the position data'
            self.scale_keywidth_touch_positions = {}

        if 'scroll' in ud:
            win = self.get_parent_window()
            # if keyboard end is visible and the touch is released, "jump" on old place:
            if self.keyboard.x > 0:
                self.keyboard.keyboard_x_animation = Animation(
                    x=0, t='out_cubic', duration=KEYBOARD_ANIMATION_X_DURATION)
                self.keyboard.keyboard_x_animation.start(self.keyboard)
            elif self.keyboard.x < (win.width - self.keyboard.width):
                self.keyboard.keyboard_x_animation = Animation(
                    x=win.width - self.keyboard.width,
                    t='out_cubic',
                    duration=KEYBOARD_ANIMATION_X_DURATION)
                self.keyboard.keyboard_x_animation.start(self.keyboard)

        if 'initial_key' in ud:
            return super(IcarusTouchWidget, self).on_touch_up(touch)

    '''
    ####################################
    ##
    ##   Other GUI Events
    ##
    ####################################
    '''

    def on_y_axis_volume_button_press(self):
        # apply the visible button-state also to the application settings
        if self.y_axis_volume_button.state == 'down':
            # Y axis => volume
            self.app.config.set('General', 'YAxis', 'Volume')
            # lock aftertouch to 0:
            self.midi_out.write_short(0xD0, 0)
        else:
            # Y axis => aftertouch
            self.app.config.set('General', 'YAxis', 'Aftertouch')
            # lock Y-modulation to 127
            self.midi_out.write_short(0xB0, 1, 127)
        self.app.config.write()

    def on_pitch_lock_button_press(self):
        # apply the visible button-state also to the application settings
        if self.pitch_lock_button.state == 'down':
            self.app.config.set('General', 'PitchLock', 'On')
        else:
            self.app.config.set('General', 'PitchLock', 'Off')
        self.app.config.write()

    def open_settings(self):
        # is called from the rightmost button (the "setup" button") --> function call binding in the .kv file
        self.app.open_settings()

    '''
    ####################################
    ##
    ##   Graphical Functions
    ##
    ####################################
    '''

    def create_circle(self, touch):
        # create the circle image
        circle = Image(source=self.app.config.get('Advanced', 'CircleImage'),
                       color=CIRCLE_IMAGE_COLOR,
                       allow_stretch=True,
                       size=(self.app.config.getint('Advanced', 'CircleSize'),
                             self.app.config.getint('Advanced', 'CircleSize')))

        # center the circle on the finger position
        circle.x = touch.x - circle.size[0] / 2
        circle.y = touch.y - circle.size[1] / 2

        self.add_widget(circle)

        # and just right fade it out after having displayed it
        animation = Animation(
            color=CIRCLE_IMAGE_COLOR_TRANSPARENT,
            size=(self.app.config.getint('Advanced', 'CircleSize') * 2,
                  self.app.config.getint('Advanced', 'CircleSize') * 2),
            x=circle.pos[0] -
            (self.app.config.getint('Advanced', 'CircleSize') /
             2),  # workaround for centering the image during resizing
            y=circle.pos[1] -
            (self.app.config.getint('Advanced', 'CircleSize') /
             2),  # workaround for centering the image during resizing
            t='out_expo',
            duration=CIRCLE_IMAGE_FADEOUT_TIME)

        animation.start(circle)
        animation.bind(on_complete=self.circle_fadeout_complete)

    def circle_fadeout_complete(self, animation, widget):
        self.remove_widget(widget)

    '''
    ####################################
    ##
    ##   Settings Functions
    ##
    ####################################
    '''

    def set_midi_device(self):
        # take the midi device of the settings file and try to connect to it.
        # If there isn't such a device, connect to the default one.

        c = pygame.midi.get_count()
        id_device_from_settings = -1
        #print '%s midi devices found' % c
        for i in range(c):
            #print '%s name: %s input: %s output: %s opened: %s' % (pygame.midi.get_device_info(i))
            if pygame.midi.get_device_info(i)[1] == self.app.config.get(
                    'MIDI', 'Device'):
                # if the device from the settings exists in the computers list, take that!
                id_device_from_settings = i

        #print 'Default is %s' % pygame.midi.get_device_info(pygame.midi.get_default_output_id())[1]

        if id_device_from_settings <> -1:
            self.midi_device = id_device_from_settings
            print 'MIDI device "%s" found. Connecting.' % pygame.midi.get_device_info(
                id_device_from_settings)[1]
        else:
            # if it was not in the list, take the default one
            self.midi_device = pygame.midi.get_default_output_id()
            print 'Warning: No MIDI device named "%s" found. Choosing the system default ("%s").' % (
                self.app.config.get('MIDI', 'Device'),
                pygame.midi.get_device_info(self.midi_device)[1])

        if pygame.midi.get_device_info(self.midi_device)[4] == 1:
            print 'Error: Can' 't open the MIDI device - It' 's already opened!'

        self.midi_out = pygame.midi.Output(self.midi_device)

    def open_my_settings_panel(self):
        self.add_widget(self.my_settings_panel)

        # bind the background images to function
        for child in self.my_settings_panel.background_scroll_view_grid.children:
            child.bind(on_press=self.background_image_change_request)

        # bind the keyboard images to function
        for child in self.my_settings_panel.keyboard_scroll_view_box.children:
            child.bind(on_press=self.keyboard_image_change_request)

        self.my_settings_panel.is_open = True

    def close_my_settings_panel(self):
        self.remove_widget(self.my_settings_panel)
        self.my_settings_panel.is_open = False

    def background_image_change_request(self, dispatcher):
        self.background_image_change(dispatcher.background_normal)

    def keyboard_image_change_request(self, dispatcher):
        self.keyboard_image_change(dispatcher.background_normal)

    def background_image_change(self, value):
        # first set the new background into the application settings:
        self.app.config.set('Graphics', 'Background', value)
        self.app.config.write()

        old_background_instance = self.background

        # if the last background change is still in progress, stop it and start the new one.
        if old_background_instance.background_is_animated == True:
            old_background_instance.new_background_change_animation.stop(
                old_background_instance.new_background_instance)
            old_background_instance.old_background_change_animation.stop(
                old_background_instance)

            self.float_layout.remove_widget(old_background_instance)
            old_background_instance.new_background_instance.color = (1, 1, 1,
                                                                     1)
            old_background_instance = old_background_instance.new_background_instance

        # create the new image instance
        old_background_instance.new_background_instance = Background(
            source=value, color=(1, 1, 1, 0))
        # add the new instance to the root widget. BUT add it on the bottom (therefore I set the index myself)
        self.float_layout.add_widget(
            old_background_instance.new_background_instance,
            index=len(self.float_layout.children))

        # keep the settings panel on the front
        self.remove_widget(self.my_settings_panel)
        self.add_widget(self.my_settings_panel)

        # let the old image fade out while the new one fades in.
        old_background_instance.new_background_change_animation = Animation(
            color=(1, 1, 1, 1), duration=BACKGROUND_CHANGE_DURATION)
        old_background_instance.old_background_change_animation = Animation(
            color=(1, 1, 1, 0), duration=BACKGROUND_CHANGE_DURATION)

        old_background_instance.new_background_change_animation.start(
            old_background_instance.new_background_instance)
        old_background_instance.old_background_change_animation.start(
            old_background_instance)

        old_background_instance.background_is_animated = True
        old_background_instance.new_background_change_animation.bind(
            on_complete=self.background_change_complete)

    def background_change_complete(self, animation, widget):
        # first remove the old background from the widget tree
        self.remove_widget(self.background)

        # then make the self.background reference refer to the new background
        self.background = widget

        self.background.background_is_animated = False

    def keyboard_image_change(self, value):
        # first set the new keyboard into the application settings:
        self.app.config.set('Graphics', 'Keyboard', value)
        self.app.config.write()

        win = self.get_parent_window()
        old_keyboard_instance = self.keyboard

        # if the last keyboard change is stil in progress, stop it and start the new.
        if old_keyboard_instance.keyboard_is_animated == True:
            old_keyboard_instance.new_keyboard_change_animation.stop(
                old_keyboard_instance.new_keyboard_instance)
            old_keyboard_instance.old_keyboard_change_animation.stop(
                old_keyboard_instance)

            self.remove_widget(old_keyboard_instance)
            old_keyboard_instance.new_keyboard_instance.y = 366
            old_keyboard_instance = old_keyboard_instance.new_keyboard_instance

        # create the new image instance
        old_keyboard_instance.new_keyboard_instance = Keyboard(
            source=value,
            pos=(old_keyboard_instance.x, win.height + BORDER_WIDTH),
            size=(self.keyboard.width, old_keyboard_instance.height),
            key_width=old_keyboard_instance.key_width,
            border_width=BORDER_WIDTH)
        self.add_widget(old_keyboard_instance.new_keyboard_instance)

        # keep the settings panel on the front
        self.remove_widget(self.my_settings_panel)
        self.add_widget(self.my_settings_panel)

        # let the old image fade out while the new one fades in.
        old_keyboard_instance.new_keyboard_change_animation = Animation(
            y=366, t='out_back', duration=KEYBOARD_CHAGE_DURATION)
        old_keyboard_instance.old_keyboard_change_animation = Animation(
            y=-(self.keyboard.height + BORDER_WIDTH),
            t='out_back',
            duration=KEYBOARD_CHAGE_DURATION)

        old_keyboard_instance.new_keyboard_change_animation.start(
            old_keyboard_instance.new_keyboard_instance)
        old_keyboard_instance.old_keyboard_change_animation.start(
            old_keyboard_instance)

        old_keyboard_instance.keyboard_is_animated = True
        old_keyboard_instance.new_keyboard_change_animation.bind(
            on_complete=self.keyboard_change_complete)

    def keyboard_change_complete(self, animation, widget):
        # first remove the old keyboard from the widget tree
        self.remove_widget(self.keyboard)

        # then make the self.keyboard reference refer to the new keyboard
        self.keyboard = widget

        self.keyboard.keyboard_is_animated = False
Пример #2
0
class IcarusTouchWidget(Widget):
    app = ObjectProperty(None)
    float_layout = ObjectProperty(None)
    pitch_lock_button = ObjectProperty(None)
    y_axis_volume_button = ObjectProperty(None)
    setting_button = ObjectProperty(None)
    look_button = ObjectProperty(None)
    feedback_wall = ObjectProperty(None)
    scale_keywidth_touch_positions = {}
    version = StringProperty(VERSION)
    
    
    '''
    ####################################
    ##
    ##   Class Initialisation
    ##
    ####################################
    '''
    def __init__(self, **kwargs):
        super(IcarusTouchWidget, self).__init__(**kwargs) # don't know if this is necessary?
        
        # add background image (and add it in the BACKGROUND! --> index modification)
        self.background = Background(source=self.app.config.get('Graphics', 'Background'))
        self.float_layout.add_widget(self.background, index=len(self.float_layout.children))
        
        # add feedback wall image
        self.feedback_wall = Feedback(
            source='images/feedbackwall.png',
            transparency=0)
        self.float_layout.add_widget(self.feedback_wall)
        
        # add the keyboard itself
        my_key_width = KEY_WIDTH
        self.keyboard = Keyboard(
            source=self.app.config.get('Graphics', 'Keyboard'),
            pos=(-540, 230), # 366
            size=(12*5*my_key_width, 468), # optimization for small screens (e.g. smartphones): 468 if self.get_parent_window().height > (468 + self.get_parent_window().height * 0.3) else self.get_parent_window().height * 0.7
            border_width=BORDER_WIDTH,
            key_width=my_key_width)
        self.add_widget(self.keyboard)
        
        # initialize the midi device
        pygame.midi.init()
        self.set_midi_device()
        
        # initialize the settings_panel (I'm doing this here, otherwise opening it in real-time takes ages...)
        self.my_settings_panel = MySettingsPanel()
    
    
    '''
    ####################################
    ##
    ##   On Touch Down
    ##
    ####################################
    '''
    def on_touch_down(self, touch):
        ud = touch.ud
        
        # find out the touchs "function":  
        if self.my_settings_panel.is_open == True:
            # if the settingspanel is opened, it has total focus!
            # so if the user clicked on one of the two panels, don't dispatch the touch here but feed it to the panel
            if self.my_settings_panel.keyboard_scroll_view_box.collide_point(*touch.pos) or \
                self.my_settings_panel.background_scroll_view_grid.collide_point(*touch.pos):
                ud['settingspanel'] = True
                return super(IcarusTouchWidget, self).on_touch_down(touch)
            else:
                # user has clicked aside - he wants to quit the settings.
                self.close_my_settings_panel()
                return True
            
        elif self.keyboard.collide_point(*touch.pos):
            # if the user touched on the keyboard, feed the touch to the keyboard.
            return super(IcarusTouchWidget, self).on_touch_down(touch)
        
        elif self.pitch_lock_button.collide_point(*touch.pos) or \
            self.y_axis_volume_button.collide_point(*touch.pos) or \
            self.look_button.collide_point(*touch.pos) or \
            self.settings_button.collide_point(*touch.pos):
            # if the user touched one of the buttons, feed the touch to the buttons
            self.create_circle(touch)
            return super(IcarusTouchWidget, self).on_touch_down(touch)
        
        ##########################
        # X-Axis Key width scaling
        ##########################
        
        '''
        # if it wasn't a button nor a key touch nor a settings touch, its a scroll touch.
        for search_touch in EventLoop.touches[:]:
            # but if there is already another touch in scroll mode (and not already in 'scale' mode), maybe the desired action is 'scale' not 'scroll'
            if 'scroll' in search_touch.ud and not self.scale_keywidth_touch_positions:
                # remember their actual positions
                self.scale_keywidth_touch_positions = {'initial_first': search_touch.x, 'initial_second': touch.x, 'first_touch': search_touch, 'second_touch': touch}
                print 'multiple saving position data'
        '''
        
        # assign the scroll-tag not until here. Thus we just get the other existing scroll-touches in the search-routine above.
        ud['scroll'] = True
        
        # if there was an keyboard "spring back" animation in progress, stop it.
        self.keyboard.keyboard_x_animation.stop(self.keyboard)
    
    
    '''
    ####################################
    ##
    ##   On Touch Move
    ##
    ####################################
    '''
    def on_touch_move(self, touch):
        ud = touch.ud
        
        ##########################
        # X-Axis Key width scaling
        ##########################
        
        '''
        # if there are two fingers in scroll mode (thus the user dictionary key 'scale_keywidth_touch_positions' isn't empty) - maybe he wants to scale the keywidth?   
        scalepos = self.scale_keywidth_touch_positions
        if scalepos:
            delta_first_touch = scalepos['first_touch'].x - scalepos['initial_first']
            delta_second_touch = scalepos['second_touch'].x - scalepos['initial_second']
            
            #print 'move: delta_first = %s, delta_second = %s' % (delta_first_touch, delta_second_touch)
            
            # if the user dragged both touches towards each other or vice versa
            if (delta_first_touch > SCALE_KEYWIDTH_THRESHOLD and delta_second_touch > -SCALE_KEYWIDTH_THRESHOLD) or \
                (delta_first_touch > -SCALE_KEYWIDTH_THRESHOLD and delta_second_touch > SCALE_KEYWIDTH_THRESHOLD):
                print 'scale mode!'
                
                # if the first touch was on the left and the user moved it right OR it was right and moved to the left, we want to make the keywidth smaller
                if (scalepos['initial_first'] < scalepos['initial_second'] and delta_first_touch > 0) or \
                    (scalepos['initial_first'] > scalepos['initial_second'] and delta_first_touch < 0):
                    print 'zoom out'
                    # perform a ZOOM OUT with a factor applied to the delta_touch_distance:
                    delta_touch_distance = abs(delta_first_touch + (-delta_second_touch))
                    self.keyboard.key_width -= int(delta_touch_distance / SCALE_KEYWIDTH_FACTOR)
                    self.keyboard.width = 12*5*self.keyboard.key_width
                    
                # if the first touch was on the left and the user moved it left OR it was right and moved to the right, we want to make the keywidth smaller
                elif (scalepos['initial_first'] < scalepos['initial_second'] and delta_first_touch < 0) or \
                    (scalepos['initial_first'] > scalepos['initial_second'] and delta_first_touch > 0):
                    print 'zoom in'
                    # perform a ZOOM IN in with a factor applied to the delta_touch_distance:
                    delta_touch_distance = abs(delta_first_touch + (-delta_second_touch))
                    self.keyboard.key_width += int(delta_touch_distance / SCALE_KEYWIDTH_FACTOR)
                    self.keyboard.width = 12*5*self.keyboard.key_width
        '''
                
        if 'scroll' in ud:
            # move keyboard left or right.
            # nice side effect: if there is more than 1 touch in 'scroll' mode, scrolling is faster!
            scroll_factor = 1
            
            # if the end of the keyboard is visible, scroll slower!
            if self.keyboard.x > 0 or self.keyboard.x < (self.get_parent_window().width - self.keyboard.width):
                counter = 0
                for touch in EventLoop.touches[:]:
                    if 'scroll' in touch.ud:
                        counter += 1 # how many touches are in 'scroll' mode?
                
                # if there is more than one touch in scroll mode, i don't want the factor to grow!
                scroll_factor = SCROLLING_STICKY_FACTOR / counter
            
            # apply the scroll action
            self.keyboard.x += touch.dx * scroll_factor
            return
        
        
        # if the touch was started on the keyboard
        if 'initial_key' in ud or 'settingspanel' in ud:
            return super(IcarusTouchWidget, self).on_touch_move(touch)
    
    
    '''
    ####################################
    ##
    ##   On Touch Up
    ##
    ####################################
    '''
    def on_touch_up(self, touch):
        ud = touch.ud
        
        #@@@@@@@@@@@@@@@@
        # Scroll Touch
        #@@@@@@@@@@@@@@@@
        
        # if there has been scale mode active or perhaps happening: (better would be: "and there aren't multiple 'scroll' mode touches active anymore
        if self.scale_keywidth_touch_positions:
            # delete all scale mode variables
            #print 'deleting the position data'
            self.scale_keywidth_touch_positions = {}
        
        if 'scroll' in ud:
            win = self.get_parent_window()
            # if keyboard end is visible and the touch is released, "jump" on old place:
            if self.keyboard.x > 0:
                self.keyboard.keyboard_x_animation = Animation(x=0, t='out_cubic', duration=KEYBOARD_ANIMATION_X_DURATION)
                self.keyboard.keyboard_x_animation.start(self.keyboard)
            elif self.keyboard.x < (win.width - self.keyboard.width):
                self.keyboard.keyboard_x_animation = Animation(x=win.width - self.keyboard.width, t='out_cubic', duration=KEYBOARD_ANIMATION_X_DURATION)
                self.keyboard.keyboard_x_animation.start(self.keyboard)
        
        if 'initial_key' in ud:
            return super(IcarusTouchWidget, self).on_touch_up(touch)
    
    
    '''
    ####################################
    ##
    ##   Other GUI Events
    ##
    ####################################
    '''
    def on_y_axis_volume_button_press(self):
        # apply the visible button-state also to the application settings
        if self.y_axis_volume_button.state == 'down':
            # Y axis => volume
            self.app.config.set('General', 'YAxis', 'Volume')
            # lock aftertouch to 0:
            self.midi_out.write_short(0xD0, 0)
        else:
            # Y axis => aftertouch
            self.app.config.set('General', 'YAxis', 'Aftertouch')
            # lock Y-modulation to 127
            self.midi_out.write_short(0xB0, 1, 127)
        self.app.config.write()
    
    def on_pitch_lock_button_press(self):
        # apply the visible button-state also to the application settings
        if self.pitch_lock_button.state == 'down':
            self.app.config.set('General', 'PitchLock', 'On')
        else:
            self.app.config.set('General', 'PitchLock', 'Off')
        self.app.config.write()
    
    def open_settings(self):
        # is called from the rightmost button (the "setup" button") --> function call binding in the .kv file
        self.app.open_settings()
    
    
    '''
    ####################################
    ##
    ##   Graphical Functions
    ##
    ####################################
    '''
    def create_circle(self, touch):
        # create the circle image
        circle = Image(
            source=self.app.config.get('Advanced', 'CircleImage'),
            color=CIRCLE_IMAGE_COLOR,
            allow_stretch=True,
            size=(self.app.config.getint('Advanced', 'CircleSize'), self.app.config.getint('Advanced', 'CircleSize')))
        
        # center the circle on the finger position
        circle.x = touch.x - circle.size[0] / 2
        circle.y = touch.y - circle.size[1] / 2
        
        self.add_widget(circle)
        
        # and just right fade it out after having displayed it
        animation = Animation(
            color=CIRCLE_IMAGE_COLOR_TRANSPARENT,
            size=(self.app.config.getint('Advanced', 'CircleSize') * 2, self.app.config.getint('Advanced', 'CircleSize') * 2),
            x=circle.pos[0] - (self.app.config.getint('Advanced', 'CircleSize')/2), # workaround for centering the image during resizing
            y=circle.pos[1] - (self.app.config.getint('Advanced', 'CircleSize')/2), # workaround for centering the image during resizing
            t='out_expo', duration=CIRCLE_IMAGE_FADEOUT_TIME)
        
        animation.start(circle)
        animation.bind(on_complete=self.circle_fadeout_complete)
    
    def circle_fadeout_complete(self, animation, widget):
        self.remove_widget(widget)
    
    
    '''
    ####################################
    ##
    ##   Settings Functions
    ##
    ####################################
    '''
    def set_midi_device(self):
        # take the midi device of the settings file and try to connect to it.
        # If there isn't such a device, connect to the default one.
        
        c = pygame.midi.get_count()
        id_device_from_settings = -1
        #print '%s midi devices found' % c
        for i in range(c):
            #print '%s name: %s input: %s output: %s opened: %s' % (pygame.midi.get_device_info(i))
            if pygame.midi.get_device_info(i)[1] == self.app.config.get('MIDI', 'Device'):
                # if the device from the settings exists in the computers list, take that!
                id_device_from_settings = i
        
        #print 'Default is %s' % pygame.midi.get_device_info(pygame.midi.get_default_output_id())[1]
        
        if id_device_from_settings <> -1:
            self.midi_device = id_device_from_settings
            print 'MIDI device "%s" found. Connecting.' % pygame.midi.get_device_info(id_device_from_settings)[1]
        else:
            # if it was not in the list, take the default one
            self.midi_device = pygame.midi.get_default_output_id()
            print 'Warning: No MIDI device named "%s" found. Choosing the system default ("%s").' % (self.app.config.get('MIDI', 'Device'), pygame.midi.get_device_info(self.midi_device)[1])
        
        if pygame.midi.get_device_info(self.midi_device)[4] == 1:
            print 'Error: Can''t open the MIDI device - It''s already opened!'
            
        self.midi_out = pygame.midi.Output(self.midi_device)
    
    
    def open_my_settings_panel(self):
        self.add_widget(self.my_settings_panel)
        
        # bind the background images to function
        for child in self.my_settings_panel.background_scroll_view_grid.children:
            child.bind(on_press=self.background_image_change_request)
        
        # bind the keyboard images to function
        for child in self.my_settings_panel.keyboard_scroll_view_box.children:
            child.bind(on_press=self.keyboard_image_change_request)
        
        self.my_settings_panel.is_open = True  
    
    
    def close_my_settings_panel(self):
        self.remove_widget(self.my_settings_panel)
        self.my_settings_panel.is_open = False
    
    
    def background_image_change_request(self, dispatcher):
        self.background_image_change(dispatcher.background_normal)
    
    
    def keyboard_image_change_request(self, dispatcher):
        self.keyboard_image_change(dispatcher.background_normal)
    
    
    def background_image_change(self, value):
        # first set the new background into the application settings:
        self.app.config.set('Graphics', 'Background', value)
        self.app.config.write()
        
        old_background_instance = self.background
        
        # if the last background change is still in progress, stop it and start the new one.
        if old_background_instance.background_is_animated == True:
            old_background_instance.new_background_change_animation.stop(old_background_instance.new_background_instance)
            old_background_instance.old_background_change_animation.stop(old_background_instance)
            
            self.float_layout.remove_widget(old_background_instance)
            old_background_instance.new_background_instance.color = (1, 1, 1, 1)
            old_background_instance = old_background_instance.new_background_instance
        
        # create the new image instance
        old_background_instance.new_background_instance = Background(
            source=value,
            color=(1, 1, 1, 0))
        # add the new instance to the root widget. BUT add it on the bottom (therefore I set the index myself)
        self.float_layout.add_widget(old_background_instance.new_background_instance, index=len(self.float_layout.children))
        
        # keep the settings panel on the front
        self.remove_widget(self.my_settings_panel)
        self.add_widget(self.my_settings_panel)
        
        # let the old image fade out while the new one fades in.
        old_background_instance.new_background_change_animation = Animation(color=(1, 1, 1, 1), duration=BACKGROUND_CHANGE_DURATION)
        old_background_instance.old_background_change_animation = Animation(color=(1, 1, 1, 0), duration=BACKGROUND_CHANGE_DURATION)
        
        old_background_instance.new_background_change_animation.start(old_background_instance.new_background_instance)
        old_background_instance.old_background_change_animation.start(old_background_instance)
        
        old_background_instance.background_is_animated = True
        old_background_instance.new_background_change_animation.bind(on_complete=self.background_change_complete)
    
    
    def background_change_complete(self, animation, widget):
        # first remove the old background from the widget tree
        self.remove_widget(self.background)
        
        # then make the self.background reference refer to the new background
        self.background = widget
        
        self.background.background_is_animated = False
    
    
    def keyboard_image_change(self, value):
        # first set the new keyboard into the application settings:
        self.app.config.set('Graphics', 'Keyboard', value)
        self.app.config.write()
        
        win = self.get_parent_window()
        old_keyboard_instance = self.keyboard
        
        # if the last keyboard change is stil in progress, stop it and start the new.
        if old_keyboard_instance.keyboard_is_animated == True:
            old_keyboard_instance.new_keyboard_change_animation.stop(old_keyboard_instance.new_keyboard_instance)
            old_keyboard_instance.old_keyboard_change_animation.stop(old_keyboard_instance)
            
            self.remove_widget(old_keyboard_instance)
            old_keyboard_instance.new_keyboard_instance.y = 366
            old_keyboard_instance = old_keyboard_instance.new_keyboard_instance
        
        # create the new image instance
        old_keyboard_instance.new_keyboard_instance = Keyboard(
            source=value,
            pos=(old_keyboard_instance.x, win.height + BORDER_WIDTH),
            size=(self.keyboard.width, old_keyboard_instance.height),
            key_width=old_keyboard_instance.key_width,
            border_width=BORDER_WIDTH)
        self.add_widget(old_keyboard_instance.new_keyboard_instance)
        
        # keep the settings panel on the front
        self.remove_widget(self.my_settings_panel)
        self.add_widget(self.my_settings_panel)
        
        # let the old image fade out while the new one fades in.
        old_keyboard_instance.new_keyboard_change_animation = Animation(y=366, t='out_back', duration=KEYBOARD_CHAGE_DURATION)
        old_keyboard_instance.old_keyboard_change_animation = Animation(y=-(self.keyboard.height + BORDER_WIDTH), t='out_back', duration=KEYBOARD_CHAGE_DURATION)
        
        old_keyboard_instance.new_keyboard_change_animation.start(old_keyboard_instance.new_keyboard_instance)
        old_keyboard_instance.old_keyboard_change_animation.start(old_keyboard_instance)
        
        old_keyboard_instance.keyboard_is_animated = True
        old_keyboard_instance.new_keyboard_change_animation.bind(on_complete=self.keyboard_change_complete)
    
    
    def keyboard_change_complete(self, animation, widget):
        # first remove the old keyboard from the widget tree
        self.remove_widget(self.keyboard)
        
        # then make the self.keyboard reference refer to the new keyboard
        self.keyboard = widget
        
        self.keyboard.keyboard_is_animated = False