| 
							- #!/usr/bin/env python3
 - # -*- coding: utf-8 -*-
 - 
 - # A piano roll viewer/editor
 - # Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
 - # Copyright (C) 2014-2015 Perry Nguyen
 - #
 - # This program is free software; you can redistribute it and/or
 - # modify it under the terms of the GNU General Public License as
 - # published by the Free Software Foundation; either version 2 of
 - # the License, or any later version.
 - #
 - # This program is distributed in the hope that it will be useful,
 - # but WITHOUT ANY WARRANTY; without even the implied warranty of
 - # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 - # GNU General Public License for more details.
 - #
 - # For a full copy of the GNU General Public License see the doc/GPL.txt file.
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Config)
 - 
 - from carla_config import *
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Global)
 - 
 - if config_UseQt5:
 -     from PyQt5.QtCore import Qt, QRectF, QPointF, pyqtSignal
 -     from PyQt5.QtGui import QColor, QFont, QPen, QPainter
 -     from PyQt5.QtWidgets import QGraphicsItem, QGraphicsLineItem, QGraphicsOpacityEffect, QGraphicsRectItem, QGraphicsSimpleTextItem
 -     from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView
 -     from PyQt5.QtWidgets import QWidget, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QStyle
 - else:
 -     from PyQt4.QtCore import Qt, QRectF, QPointF, pyqtSignal
 -     from PyQt4.QtGui import QColor, QFont, QPen, QPainter
 -     from PyQt4.QtGui import QGraphicsItem, QGraphicsLineItem, QGraphicsOpacityEffect, QGraphicsRectItem, QGraphicsSimpleTextItem
 -     from PyQt4.QtGui import QGraphicsScene, QGraphicsView
 -     from PyQt4.QtGui import QWidget, QLabel, QComboBox, QSlider, QHBoxLayout, QVBoxLayout, QStyle
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Imports (Custom)
 - 
 - from carla_shared import *
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # MIDI definitions, copied from CarlaMIDI.h
 - 
 - MAX_MIDI_CHANNELS = 16
 - MAX_MIDI_NOTE     = 128
 - MAX_MIDI_VALUE    = 128
 - MAX_MIDI_CONTROL  = 120 # 0x77
 - 
 - MIDI_STATUS_BIT  = 0xF0
 - MIDI_CHANNEL_BIT = 0x0F
 - 
 - # MIDI Messages List
 - MIDI_STATUS_NOTE_OFF              = 0x80 # note (0-127), velocity (0-127)
 - MIDI_STATUS_NOTE_ON               = 0x90 # note (0-127), velocity (0-127)
 - MIDI_STATUS_POLYPHONIC_AFTERTOUCH = 0xA0 # note (0-127), pressure (0-127)
 - MIDI_STATUS_CONTROL_CHANGE        = 0xB0 # see 'Control Change Messages List'
 - MIDI_STATUS_PROGRAM_CHANGE        = 0xC0 # program (0-127), none
 - MIDI_STATUS_CHANNEL_PRESSURE      = 0xD0 # pressure (0-127), none
 - MIDI_STATUS_PITCH_WHEEL_CONTROL   = 0xE0 # LSB (0-127), MSB (0-127)
 - 
 - # MIDI Message type
 - def MIDI_IS_CHANNEL_MESSAGE(status): return status >= MIDI_STATUS_NOTE_OFF and status <  MIDI_STATUS_BIT
 - def MIDI_IS_SYSTEM_MESSAGE(status):  return status >= MIDI_STATUS_BIT      and status <= 0xFF
 - def MIDI_IS_OSC_MESSAGE(status):     return status == '/'                   or status == '#'
 - 
 - # MIDI Channel message type
 - def MIDI_IS_STATUS_NOTE_OFF(status):              return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_OFF
 - def MIDI_IS_STATUS_NOTE_ON(status):               return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_NOTE_ON
 - def MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status): return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_POLYPHONIC_AFTERTOUCH
 - def MIDI_IS_STATUS_CONTROL_CHANGE(status):        return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CONTROL_CHANGE
 - def MIDI_IS_STATUS_PROGRAM_CHANGE(status):        return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PROGRAM_CHANGE
 - def MIDI_IS_STATUS_CHANNEL_PRESSURE(status):      return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_CHANNEL_PRESSURE
 - def MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status):   return MIDI_IS_CHANNEL_MESSAGE(status) and (status & MIDI_STATUS_BIT) == MIDI_STATUS_PITCH_WHEEL_CONTROL
 - 
 - # MIDI Utils
 - def MIDI_GET_STATUS_FROM_DATA(data):  return data[0] & MIDI_STATUS_BIT  if MIDI_IS_CHANNEL_MESSAGE(data[0]) else data[0]
 - def MIDI_GET_CHANNEL_FROM_DATA(data): return data[0] & MIDI_CHANNEL_BIT if MIDI_IS_CHANNEL_MESSAGE(data[0]) else 0
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - # Graphics Items
 - 
 - class NoteExpander(QGraphicsRectItem):
 -     def __init__(self, length, height, parent):
 -         QGraphicsRectItem.__init__(self, 0, 0, length, height, parent)
 -         self.parent = parent
 -         self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
 -         self.setAcceptHoverEvents(True)
 - 
 -         clearpen = QPen(QColor(0,0,0,0))
 -         self.setPen(clearpen)
 - 
 -         self.orig_brush = QColor(0, 0, 0, 0)
 -         self.hover_brush = QColor(200, 200, 200)
 -         self.stretch = False
 - 
 -     def mousePressEvent(self, event):
 -         QGraphicsRectItem.mousePressEvent(self, event)
 -         self.stretch = True
 - 
 -     def hoverEnterEvent(self, event):
 -         QGraphicsRectItem.hoverEnterEvent(self, event)
 -         if self.parent.isSelected():
 -             self.parent.setBrush(self.parent.select_brush)
 -         else:
 -             self.parent.setBrush(self.parent.orig_brush)
 -         self.setBrush(self.hover_brush)
 - 
 -     def hoverLeaveEvent(self, event):
 -         QGraphicsRectItem.hoverLeaveEvent(self, event)
 -         if self.parent.isSelected():
 -             self.parent.setBrush(self.parent.select_brush)
 -         elif self.parent.hovering:
 -             self.parent.setBrush(self.parent.hover_brush)
 -         else:
 -             self.parent.setBrush(self.parent.orig_brush)
 -         self.setBrush(self.orig_brush)
 - 
 - class NoteItem(QGraphicsRectItem):
 -     '''a note on the pianoroll sequencer'''
 -     def __init__(self, height, length, note_info):
 -         QGraphicsRectItem.__init__(self, 0, 0, length, height)
 - 
 -         self.setFlag(QGraphicsItem.ItemIsMovable)
 -         self.setFlag(QGraphicsItem.ItemIsSelectable)
 -         self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
 -         self.setAcceptHoverEvents(True)
 - 
 -         clearpen = QPen(QColor(0,0,0,0))
 -         self.setPen(clearpen)
 -         self.orig_brush = QColor(note_info[3], 0, 0)
 -         self.hover_brush = QColor(note_info[3] + 100, 200, 100)
 -         self.select_brush = QColor(note_info[3] + 100, 100, 100)
 -         self.setBrush(self.orig_brush)
 - 
 -         self.note = note_info
 -         self.length = length
 -         self.piano = self.scene
 - 
 -         self.pressed = False
 -         self.hovering = False
 -         self.moving_diff = (0,0)
 -         self.expand_diff = 0
 - 
 -         l = 5
 -         self.front = NoteExpander(l, height, self)
 -         self.back = NoteExpander(l, height, self)
 -         self.back.setPos(length - l, 0)
 - 
 -     def paint(self, painter, option, widget=None):
 -         paint_option = option
 -         paint_option.state &= ~QStyle.State_Selected
 -         QGraphicsRectItem.paint(self, painter, paint_option, widget)
 - 
 -     def setSelected(self, boolean):
 -         QGraphicsRectItem.setSelected(self, boolean)
 -         if boolean: self.setBrush(self.select_brush)
 -         else: self.setBrush(self.orig_brush)
 - 
 -     def hoverEnterEvent(self, event):
 -         self.hovering = True
 -         QGraphicsRectItem.hoverEnterEvent(self, event)
 -         if not self.isSelected():
 -             self.setBrush(self.hover_brush)
 - 
 -     def hoverLeaveEvent(self, event):
 -         self.hovering = False
 -         QGraphicsRectItem.hoverLeaveEvent(self, event)
 -         if not self.isSelected():
 -             self.setBrush(self.orig_brush)
 -         elif self.isSelected():
 -             self.setBrush(self.select_brush)
 - 
 -     def mousePressEvent(self, event):
 -         QGraphicsRectItem.mousePressEvent(self, event)
 -         self.setSelected(True)
 -         self.pressed = True
 - 
 -     def mouseMoveEvent(self, event):
 -         pass
 - 
 -     def moveEvent(self, event):
 -         offset = event.scenePos() - event.lastScenePos()
 - 
 -         if self.back.stretch:
 -             self.expand(self.back, offset)
 -         else:
 -             self.move_pos = self.scenePos() + offset \
 -                     + QPointF(self.moving_diff[0],self.moving_diff[1])
 -             pos = self.piano().enforce_bounds(self.move_pos)
 -             pos_x, pos_y = pos.x(), pos.y()
 -             pos_sx, pos_sy = self.piano().snap(pos_x, pos_y)
 -             self.moving_diff = (pos_x-pos_sx, pos_y-pos_sy)
 -             if self.front.stretch:
 -                 right = self.rect().right() - offset.x() + self.expand_diff
 -                 if (self.scenePos().x() == self.piano().piano_width and offset.x() < 0) \
 -                         or right < 10:
 -                     self.expand_diff = 0
 -                     return
 -                 self.expand(self.front, offset)
 -                 self.setPos(pos_sx, self.scenePos().y())
 -             else:
 -                 self.setPos(pos_sx, pos_sy)
 - 
 -     def expand(self, rectItem, offset):
 -         rect = self.rect()
 -         right = rect.right() + self.expand_diff
 -         if rectItem == self.back:
 -             right += offset.x()
 -             if right > self.piano().grid_width:
 -                 right = self.piano().grid_width
 -             elif right < 10:
 -                 right = 10
 -             new_x = self.piano().snap(right)
 -         else:
 -             right -= offset.x()
 -             new_x = self.piano().snap(right+2.75)
 -         if self.piano().snap_value: new_x -= 2.75 # where does this number come from?!
 -         self.expand_diff = right - new_x
 -         self.back.setPos(new_x - 5, 0)
 -         rect.setRight(new_x)
 -         self.setRect(rect)
 - 
 -     def updateNoteInfo(self, pos_x, pos_y):
 -         self.note[0] = self.piano().get_note_num_from_y(pos_y)
 -         self.note[1] = self.piano().get_note_start_from_x(pos_x)
 -         self.note[2] = self.piano().get_note_length_from_x(
 -                 self.rect().right() - self.rect().left())
 -         #print("note: {}".format(self.note))
 - 
 -     def mouseReleaseEvent(self, event):
 -         QGraphicsRectItem.mouseReleaseEvent(self, event)
 -         self.pressed = False
 -         if event.button() == Qt.LeftButton:
 -             self.moving_diff = (0,0)
 -             self.expand_diff = 0
 -             self.back.stretch = False
 -             self.front.stretch = False
 -             (pos_x, pos_y,) = self.piano().snap(self.pos().x(), self.pos().y())
 -             self.setPos(pos_x, pos_y)
 -             self.updateNoteInfo(pos_x, pos_y)
 - 
 -     def updateVelocity(self, event):
 -         offset = event.scenePos().x() - event.lastScenePos().x()
 -         self.note[3] += int(offset/5)
 -         if self.note[3] > 127:
 -             self.note[3] = 127
 -         elif self.note[3] < 0:
 -             self.note[3] = 0
 -         print("new vel: {}".format(self.note[3]))
 -         self.orig_brush = QColor(self.note[3], 0, 0)
 -         self.select_brush = QColor(self.note[3] + 100, 100, 100)
 -         self.setBrush(self.orig_brush)
 - 
 - 
 - class PianoKeyItem(QGraphicsRectItem):
 -     def __init__(self, width, height, parent):
 -         QGraphicsRectItem.__init__(self, 0, 0, width, height, parent)
 -         self.setPen(QPen(QColor(0,0,0,80)))
 -         self.width = width
 -         self.height = height
 -         self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
 -         self.setAcceptHoverEvents(True)
 -         self.hover_brush = QColor(200, 0, 0)
 -         self.click_brush = QColor(255, 100, 100)
 -         self.pressed = False
 - 
 -     def hoverEnterEvent(self, event):
 -         QGraphicsRectItem.hoverEnterEvent(self, event)
 -         self.orig_brush = self.brush()
 -         self.setBrush(self.hover_brush)
 - 
 -     def hoverLeaveEvent(self, event):
 -         if self.pressed:
 -             self.pressed = False
 -             self.setBrush(self.hover_brush)
 -         QGraphicsRectItem.hoverLeaveEvent(self, event)
 -         self.setBrush(self.orig_brush)
 - 
 -     #def mousePressEvent(self, event):
 -     #    self.pressed = True
 -     #    self.setBrush(self.click_brush)
 - 
 -     def mouseMoveEvent(self, event):
 -         """this may eventually do something"""
 -         pass
 - 
 -     def mouseReleaseEvent(self, event):
 -         self.pressed = False
 -         QGraphicsRectItem.mouseReleaseEvent(self, event)
 -         self.setBrush(self.hover_brush)
 - 
 - class PianoRoll(QGraphicsScene):
 -     '''the piano roll'''
 - 
 -     midievent = pyqtSignal(list)
 -     measureupdate = pyqtSignal(int)
 -     modeupdate = pyqtSignal(str)
 - 
 -     def __init__(self, time_sig = '4/4', num_measures = 4, quantize_val = '1/8'):
 -         QGraphicsScene.__init__(self)
 -         self.setBackgroundBrush(QColor(50, 50, 50))
 -         self.mousePos = QPointF()
 - 
 -         self.notes = []
 -         self.selected_notes = []
 -         self.piano_keys = []
 - 
 -         self.marquee_select = False
 -         self.insert_mode = False
 -         self.velocity_mode = False
 -         self.place_ghost = False
 -         self.ghost_note = None
 -         self.default_ghost_vel = 100
 -         self.ghost_vel = self.default_ghost_vel
 - 
 -         ## dimensions
 -         self.padding = 2
 - 
 -         ## piano dimensions
 -         self.note_height = 10
 -         self.start_octave = -2
 -         self.end_octave = 8
 -         self.notes_in_octave = 12
 -         self.total_notes = (self.end_octave - self.start_octave) \
 -                 * self.notes_in_octave + 1
 -         self.piano_height = self.note_height * self.total_notes
 -         self.octave_height = self.notes_in_octave * self.note_height
 - 
 -         self.piano_width = 34
 - 
 -         ## height
 -         self.header_height = 20
 -         self.total_height = self.piano_height - self.note_height + self.header_height
 -         #not sure why note_height is subtracted
 - 
 -         ## width
 -         self.full_note_width = 250 # i.e. a 4/4 note
 -         self.snap_value = None
 -         self.quantize_val = quantize_val
 - 
 -         ### dummy vars that will be changed
 -         self.time_sig = 0
 -         self.measure_width = 0
 -         self.num_measures = 0
 -         self.max_note_length = 0
 -         self.grid_width = 0
 -         self.value_width = 0
 -         self.grid_div = 0
 -         self.piano = None
 -         self.header = None
 -         self.play_head = None
 - 
 -         self.setTimeSig(time_sig)
 -         self.setMeasures(num_measures)
 -         self.setGridDiv()
 -         self.default_length = 1. / self.grid_div
 - 
 - 
 -     # -------------------------------------------------------------------------
 -     # Callbacks
 - 
 -     def movePlayHead(self, transport_info):
 -         # TODO: need conversion between frames and PPQ
 -         x = 105. # works for 120bpm
 -         total_duration = self.time_sig[0] * self.num_measures * x
 -         pos = transport_info['frame'] / x
 -         frac = (pos % total_duration) / total_duration
 -         self.play_head.setPos(QPointF(frac * self.grid_width, 0))
 - 
 -     def setTimeSig(self, time_sig):
 -         try:
 -            new_time_sig = list(map(float, time_sig.split('/')))
 -            if len(new_time_sig)==2:
 -                self.time_sig = new_time_sig
 - 
 -                self.measure_width = self.full_note_width * self.time_sig[0]/self.time_sig[1]
 -                self.max_note_length = self.num_measures * self.time_sig[0]/self.time_sig[1]
 -                self.grid_width = self.measure_width * self.num_measures
 -                self.setGridDiv()
 -         except ValueError:
 -             pass
 - 
 -     def setMeasures(self, measures):
 -         try:
 -             self.num_measures = float(measures)
 -             self.max_note_length = self.num_measures * self.time_sig[0]/self.time_sig[1]
 -             self.grid_width = self.measure_width * self.num_measures
 -             self.refreshScene()
 -         except:
 -             pass
 - 
 -     def setDefaultLength(self, length):
 -         try:
 -             v = list(map(float, length.split('/')))
 -             if len(v) < 3:
 -                 self.default_length = \
 -                         v[0] if len(v)==1 else \
 -                         v[0] / v[1]
 -                 pos = self.enforce_bounds(self.mousePos)
 -                 if self.insert_mode: self.makeGhostNote(pos.x(), pos.y())
 -         except ValueError:
 -             pass
 - 
 -     def setGridDiv(self, div=None):
 -         if not div: div = self.quantize_val
 -         try:
 -             val = list(map(int, div.split('/')))
 -             if len(val) < 3:
 -                 self.quantize_val = div
 -                 self.grid_div = val[0] if len(val)==1 else val[1]
 -                 self.value_width = self.full_note_width / float(self.grid_div) if self.grid_div else None
 -                 self.setQuantize(div)
 - 
 -                 self.refreshScene()
 -         except ValueError:
 -             pass
 - 
 -     def setQuantize(self, value):
 -         try:
 -             val = list(map(float, value.split('/')))
 -             if len(val) == 1:
 -                 self.quantize(val[0])
 -                 self.quantize_val = value
 -             elif len(val) == 2:
 -                 self.quantize(val[0] / val[1])
 -                 self.quantize_val = value
 -         except ValueError:
 -             pass
 - 
 -     # -------------------------------------------------------------------------
 -     # Event Callbacks
 - 
 -     def keyPressEvent(self, event):
 -         QGraphicsScene.keyPressEvent(self, event)
 -         if event.key() == Qt.Key_F:
 -             if not self.insert_mode:
 -                 self.velocity_mode = False
 -                 self.insert_mode = True
 -                 self.makeGhostNote(self.mousePos.x(), self.mousePos.y())
 -                 self.modeupdate.emit('insert_mode')
 -             elif self.insert_mode:
 -                 self.insert_mode = False
 -                 if self.place_ghost: self.place_ghost = False
 -                 self.removeItem(self.ghost_note)
 -                 self.ghost_note = None
 -                 self.modeupdate.emit('')
 -         elif event.key() == Qt.Key_D:
 -             if self.velocity_mode:
 -                 self.velocity_mode = False
 -                 self.modeupdate.emit('')
 -             else:
 -                 if self.insert_mode:
 -                     self.removeItem(self.ghost_note)
 -                 self.ghost_note = None
 -                 self.insert_mode = False
 -                 self.place_ghost = False
 -                 self.velocity_mode = True
 -                 self.modeupdate.emit('velocity_mode')
 -         elif event.key() == Qt.Key_A:
 -             if all((note.isSelected() for note in self.notes)):
 -                 for note in self.notes:
 -                     note.setSelected(False)
 -                 self.selected_notes = []
 -             else:
 -                 for note in self.notes:
 -                     note.setSelected(True)
 -                 self.selected_notes = self.notes[:]
 -         elif event.key() in (Qt.Key_Delete, Qt.Key_Backspace):
 -             self.notes = [note for note in self.notes if note not in self.selected_notes]
 -             for note in self.selected_notes:
 -                 self.removeItem(note)
 -                 self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]])
 -                 del note
 -             self.selected_notes = []
 - 
 -     def mousePressEvent(self, event):
 -         QGraphicsScene.mousePressEvent(self, event)
 -         if not (any(key.pressed for key in self.piano_keys)
 -                 or any(note.pressed for note in self.notes)):
 -             for note in self.selected_notes:
 -                 note.setSelected(False)
 -             self.selected_notes = []
 - 
 -             if event.button() == Qt.LeftButton:
 -                 if self.insert_mode:
 -                     self.place_ghost = True
 -                 else:
 -                     self.marquee_select = True
 -                     self.marquee_rect = QRectF(event.scenePos().x(), event.scenePos().y(), 1, 1)
 -                     self.marquee = QGraphicsRectItem(self.marquee_rect)
 -                     self.marquee.setBrush(QColor(255, 255, 255, 100))
 -                     self.addItem(self.marquee)
 -         else:
 -             for s_note in self.notes:
 -                 if s_note.pressed and s_note in self.selected_notes:
 -                     break
 -                 elif s_note.pressed and s_note not in self.selected_notes:
 -                     for note in self.selected_notes:
 -                         note.setSelected(False)
 -                     self.selected_notes = [s_note]
 -                     break
 -             for note in self.selected_notes:
 -                 if not self.velocity_mode:
 -                     note.mousePressEvent(event)
 - 
 -     def mouseMoveEvent(self, event):
 -         QGraphicsScene.mouseMoveEvent(self, event)
 -         self.mousePos = event.scenePos()
 -         if not (any((key.pressed for key in self.piano_keys))):
 -             m_pos = event.scenePos()
 -             if self.insert_mode and self.place_ghost: #placing a note
 -                 m_width = self.ghost_rect.x() + self.ghost_rect_orig_width
 -                 if m_pos.x() > m_width:
 -                     m_new_x = self.snap(m_pos.x())
 -                     self.ghost_rect.setRight(m_new_x)
 -                     self.ghost_note.setRect(self.ghost_rect)
 -                 #self.adjust_note_vel(event)
 -             else:
 -                 m_pos = self.enforce_bounds(m_pos)
 - 
 -                 if self.insert_mode: #ghostnote follows mouse around
 -                     (m_new_x, m_new_y) = self.snap(m_pos.x(), m_pos.y())
 -                     self.ghost_rect.moveTo(m_new_x, m_new_y)
 -                     try:
 -                         self.ghost_note.setRect(self.ghost_rect)
 -                     except RuntimeError:
 -                         self.ghost_note = None
 -                         self.makeGhostNote(m_new_x, m_new_y)
 - 
 -                 elif self.marquee_select:
 -                     marquee_orig_pos = event.buttonDownScenePos(Qt.LeftButton)
 -                     if marquee_orig_pos.x() < m_pos.x() and marquee_orig_pos.y() < m_pos.y():
 -                         self.marquee_rect.setBottomRight(m_pos)
 -                     elif marquee_orig_pos.x() < m_pos.x() and marquee_orig_pos.y() > m_pos.y():
 -                         self.marquee_rect.setTopRight(m_pos)
 -                     elif marquee_orig_pos.x() > m_pos.x() and marquee_orig_pos.y() < m_pos.y():
 -                         self.marquee_rect.setBottomLeft(m_pos)
 -                     elif marquee_orig_pos.x() > m_pos.x() and marquee_orig_pos.y() > m_pos.y():
 -                         self.marquee_rect.setTopLeft(m_pos)
 -                     self.marquee.setRect(self.marquee_rect)
 -                     self.selected_notes = []
 -                     for item in self.collidingItems(self.marquee):
 -                         if item in self.notes:
 -                             self.selected_notes.append(item)
 - 
 -                     for note in self.notes:
 -                         if note in self.selected_notes: note.setSelected(True)
 -                         else: note.setSelected(False)
 - 
 -                 elif self.velocity_mode:
 -                     if Qt.LeftButton == event.buttons():
 -                         for note in self.selected_notes:
 -                             note.updateVelocity(event)
 - 
 -                 elif not self.marquee_select: #move selected
 -                     if Qt.LeftButton == event.buttons():
 -                         x = y = False
 -                         if any(note.back.stretch for note in self.selected_notes):
 -                             x = True
 -                         elif any(note.front.stretch for note in self.selected_notes):
 -                             y = True
 -                         for note in self.selected_notes:
 -                             note.back.stretch = x
 -                             note.front.stretch = y
 -                             note.moveEvent(event)
 - 
 -     def mouseReleaseEvent(self, event):
 -         if not (any((key.pressed for key in self.piano_keys)) or any((note.pressed for note in self.notes))):
 -             if event.button() == Qt.LeftButton:
 -                 if self.place_ghost and self.insert_mode:
 -                     self.place_ghost = False
 -                     note_start = self.get_note_start_from_x(self.ghost_rect.x())
 -                     note_num = self.get_note_num_from_y(self.ghost_rect.y())
 -                     note_length = self.get_note_length_from_x(self.ghost_rect.width())
 -                     self.drawNote(note_num, note_start, note_length, self.ghost_vel)
 -                     self.midievent.emit(["midievent-add", note_num, note_start, note_length, self.ghost_vel])
 -                     self.makeGhostNote(self.mousePos.x(), self.mousePos.y())
 -                 elif self.marquee_select:
 -                     self.marquee_select = False
 -                     self.removeItem(self.marquee)
 -         elif not self.marquee_select:
 -             for note in self.selected_notes:
 -                 old_info = note.note[:]
 -                 note.mouseReleaseEvent(event)
 -                 if self.velocity_mode:
 -                     note.setSelected(True)
 -                 if not old_info == note.note:
 -                     self.midievent.emit(["midievent-remove", old_info[0], old_info[1], old_info[2], old_info[3]])
 -                     self.midievent.emit(["midievent-add", note.note[0], note.note[1], note.note[2], note.note[3]])
 - 
 -     # -------------------------------------------------------------------------
 -     # Internal Functions
 - 
 -     def drawHeader(self):
 -         self.header = QGraphicsRectItem(0, 0, self.grid_width, self.header_height)
 -         #self.header.setZValue(1.0)
 -         self.header.setPos(self.piano_width, 0)
 -         self.addItem(self.header)
 - 
 -     def drawPiano(self):
 -         piano_keys_width = self.piano_width - self.padding
 -         labels = ('B','Bb','A','Ab','G','Gb','F','E','Eb','D','Db','C')
 -         black_notes = (2,4,6,9,11)
 -         piano_label = QFont()
 -         piano_label.setPointSize(6)
 -         self.piano = QGraphicsRectItem(0, 0, piano_keys_width, self.piano_height)
 -         self.piano.setPos(0, self.header_height)
 -         self.addItem(self.piano)
 - 
 -         key = PianoKeyItem(piano_keys_width, self.note_height, self.piano)
 -         label = QGraphicsSimpleTextItem('C8', key)
 -         label.setPos(18, 1)
 -         label.setFont(piano_label)
 -         key.setBrush(QColor(255, 255, 255))
 -         for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1):
 -             for j in range(self.notes_in_octave, 0, -1):
 -                 if j in black_notes:
 -                     key = PianoKeyItem(piano_keys_width/1.4, self.note_height, self.piano)
 -                     key.setBrush(QColor(0, 0, 0))
 -                     key.setZValue(1.0)
 -                     key.setPos(0, self.note_height * j + self.octave_height * (i - 1))
 -                 elif (j - 1) and (j + 1) in black_notes:
 -                     key = PianoKeyItem(piano_keys_width, self.note_height * 2, self.piano)
 -                     key.setBrush(QColor(255, 255, 255))
 -                     key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.)
 -                 elif (j - 1) in black_notes:
 -                     key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, self.piano)
 -                     key.setBrush(QColor(255, 255, 255))
 -                     key.setPos(0, self.note_height * j + self.octave_height * (i - 1) - self.note_height/2.)
 -                 elif (j + 1) in black_notes:
 -                     key = PianoKeyItem(piano_keys_width, self.note_height * 3./2, self.piano)
 -                     key.setBrush(QColor(255, 255, 255))
 -                     key.setPos(0, self.note_height * j + self.octave_height * (i - 1))
 -                 if j == 12:
 -                     label = QGraphicsSimpleTextItem('{}{}'.format(labels[j - 1], self.end_octave - i), key )
 -                     label.setPos(18, 6)
 -                     label.setFont(piano_label)
 -                 self.piano_keys.append(key)
 - 
 -     def drawGrid(self):
 -         black_notes = [2,4,6,9,11]
 -         scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano)
 -         scale_bar.setPos(self.piano_width, 0)
 -         scale_bar.setBrush(QColor(100,100,100))
 -         clearpen = QPen(QColor(0,0,0,0))
 -         for i in range(self.end_octave - self.start_octave, self.start_octave - self.start_octave, -1):
 -             for j in range(self.notes_in_octave, 0, -1):
 -                 scale_bar = QGraphicsRectItem(0, 0, self.grid_width, self.note_height, self.piano)
 -                 scale_bar.setPos(self.piano_width, self.note_height * j + self.octave_height * (i - 1))
 -                 scale_bar.setPen(clearpen)
 -                 if j not in black_notes:
 -                     scale_bar.setBrush(QColor(120,120,120))
 -                 else:
 -                     scale_bar.setBrush(QColor(100,100,100))
 - 
 -         measure_pen = QPen(QColor(0, 0, 0, 120), 3)
 -         half_measure_pen = QPen(QColor(0, 0, 0, 40), 2)
 -         line_pen = QPen(QColor(0, 0, 0, 40))
 -         for i in range(0, int(self.num_measures) + 1):
 -             measure = QGraphicsLineItem(0, 0, 0, self.piano_height + self.header_height - measure_pen.width(), self.header)
 -             measure.setPos(self.measure_width * i, 0.5 * measure_pen.width())
 -             measure.setPen(measure_pen)
 -             if i < self.num_measures:
 -                 number = QGraphicsSimpleTextItem('%d' % (i + 1), self.header)
 -                 number.setPos(self.measure_width * i + 5, 2)
 -                 number.setBrush(Qt.white)
 -                 for j in self.frange(0, self.time_sig[0]*self.grid_div/self.time_sig[1], 1.):
 -                     line = QGraphicsLineItem(0, 0, 0, self.piano_height, self.header)
 -                     line.setZValue(1.0)
 -                     line.setPos(self.measure_width * i + self.value_width * j, self.header_height)
 -                     if j == self.time_sig[0]*self.grid_div/self.time_sig[1] / 2.0:
 -                         line.setPen(half_measure_pen)
 -                     else:
 -                         line.setPen(line_pen)
 - 
 -     def drawPlayHead(self):
 -         self.play_head = QGraphicsLineItem(self.piano_width, self.header_height, self.piano_width, self.total_height)
 -         self.play_head.setPen(QPen(QColor(255,255,255,50), 2))
 -         self.play_head.setZValue(1.)
 -         self.addItem(self.play_head)
 - 
 -     def refreshScene(self):
 -         list(map(self.removeItem, self.notes))
 -         self.selected_notes = []
 -         self.piano_keys = []
 -         self.clear()
 -         self.drawPiano()
 -         self.drawHeader()
 -         self.drawGrid()
 -         self.drawPlayHead()
 -         for note in self.notes[:]:
 -             if note.note[1] >= (self.num_measures * self.time_sig[0]):
 -                 self.notes.remove(note)
 -                 self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]])
 -             elif note.note[2] > self.max_note_length:
 -                 new_note = note.note[:]
 -                 new_note[2] = self.max_note_length
 -                 self.notes.remove(note)
 -                 self.drawNote(new_note[0], new_note[1], self.max_note_length, new_note[3], False)
 -                 self.midievent.emit(["midievent-remove", note.note[0], note.note[1], note.note[2], note.note[3]])
 -                 self.midievent.emit(["midievent-add", new_note[0], new_note[1], new_note[2], new_note[3]])
 -         list(map(self.addItem, self.notes))
 -         if self.views():
 -             self.views()[0].setSceneRect(self.itemsBoundingRect())
 - 
 -     def clearNotes(self):
 -         self.clear()
 -         self.notes = []
 -         self.selected_notes = []
 -         self.drawPiano()
 -         self.drawHeader()
 -         self.drawGrid()
 - 
 -     def makeGhostNote(self, pos_x, pos_y):
 -         """creates the ghostnote that is placed on the scene before the real one is."""
 -         if self.ghost_note:
 -             self.removeItem(self.ghost_note)
 -         length = self.full_note_width * self.default_length
 -         (start, note) = self.snap(pos_x, pos_y)
 -         self.ghost_vel = self.default_ghost_vel
 -         self.ghost_rect = QRectF(start, note, length, self.note_height)
 -         self.ghost_rect_orig_width = self.ghost_rect.width()
 -         self.ghost_note = QGraphicsRectItem(self.ghost_rect)
 -         self.ghost_note.setBrush(QColor(230, 221, 45, 100))
 -         self.addItem(self.ghost_note)
 - 
 -     def drawNote(self, note_num, note_start=None, note_length=None, note_velocity=None, add=True):
 -         """
 -         note_num: midi number, 0 - 127
 -         note_start: 0 - (num_measures * time_sig[0]) so this is in beats
 -         note_length: 0 - (num_measures  * time_sig[0]/time_sig[1]) this is in measures
 -         note_velocity: 0 - 127
 -         """
 - 
 -         info = [note_num, note_start, note_length, note_velocity]
 - 
 -         if not note_start % (self.num_measures * self.time_sig[0]) == note_start:
 -             #self.midievent.emit(["midievent-remove", note_num, note_start, note_length, note_velocity])
 -             while not note_start % (self.num_measures * self.time_sig[0]) == note_start:
 -                 self.setMeasures(self.num_measures+1)
 -             self.measureupdate.emit(self.num_measures)
 -             self.refreshScene()
 - 
 -         x_start = self.get_note_x_start(note_start)
 -         if note_length > self.max_note_length:
 -             note_length = self.max_note_length + 0.25
 -         x_length = self.get_note_x_length(note_length)
 -         y_pos = self.get_note_y_pos(note_num)
 - 
 -         note = NoteItem(self.note_height, x_length, info)
 -         note.setPos(x_start, y_pos)
 - 
 -         self.notes.append(note)
 -         if add:
 -             self.addItem(note)
 - 
 -     # -------------------------------------------------------------------------
 -     # Helper Functions
 - 
 -     def frange(self, x, y, t):
 -         while x < y:
 -             yield x
 -             x += t
 - 
 -     def quantize(self, value):
 -         self.snap_value = float(self.full_note_width) * value if value else None
 - 
 -     def snap(self, pos_x, pos_y = None):
 -         if self.snap_value:
 -             pos_x = int(round((pos_x - self.piano_width) / self.snap_value)) \
 -                     * self.snap_value + self.piano_width
 -         if pos_y:
 -             pos_y = int((pos_y - self.header_height) / self.note_height) \
 -                     * self.note_height + self.header_height
 -         return (pos_x, pos_y) if pos_y else pos_x
 - 
 -     def adjust_note_vel(self, event):
 -         m_pos = event.scenePos()
 -         #bind velocity to vertical mouse movement
 -         self.ghost_vel += (event.lastScenePos().y() - m_pos.y())/10
 -         if self.ghost_vel < 0:
 -             self.ghost_vel = 0
 -         elif self.ghost_vel > 127:
 -             self.ghost_vel = 127
 - 
 -         m_width = self.ghost_rect.x() + self.ghost_rect_orig_width
 -         if m_pos.x() < m_width:
 -             m_pos.setX(m_width)
 -         m_new_x = self.snap(m_pos.x())
 -         self.ghost_rect.setRight(m_new_x)
 -         self.ghost_note.setRect(self.ghost_rect)
 - 
 - 
 -     def enforce_bounds(self, pos):
 -         if pos.x() < self.piano_width:
 -             pos.setX(self.piano_width)
 -         elif pos.x() > self.grid_width + self.piano_width:
 -             pos.setX(self.grid_width + self.piano_width)
 -         if pos.y() < self.header_height + self.padding:
 -             pos.setY(self.header_height + self.padding)
 -         return pos
 - 
 -     def get_note_start_from_x(self, note_x):
 -         return (note_x - self.piano_width) / (self.grid_width / self.num_measures / self.time_sig[0])
 - 
 - 
 -     def get_note_x_start(self, note_start):
 -         return self.piano_width + \
 -                 (self.grid_width / self.num_measures / self.time_sig[0]) * note_start
 - 
 -     def get_note_x_length(self, note_length):
 -         return float(self.time_sig[1]) / self.time_sig[0] * note_length * self.grid_width / self.num_measures
 - 
 -     def get_note_length_from_x(self, note_x):
 -         return float(self.time_sig[0]) / self.time_sig[1] * self.num_measures / self.grid_width \
 -                 * note_x
 - 
 - 
 -     def get_note_y_pos(self, note_num):
 -         return self.header_height + self.note_height * (self.total_notes - note_num - 1)
 - 
 -     def get_note_num_from_y(self, note_y_pos):
 -         return -(((note_y_pos - self.header_height) / self.note_height) - self.total_notes + 1)
 - 
 - class PianoRollView(QGraphicsView):
 -     def __init__(self, parent, time_sig = '4/4', num_measures = 4, quantize_val = '1/8'):
 -         QGraphicsView.__init__(self, parent)
 -         self.piano = PianoRoll(time_sig, num_measures, quantize_val)
 -         self.setScene(self.piano)
 -         #self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
 - 
 -         x = 0   * self.sceneRect().width() + self.sceneRect().left()
 -         y = 0.4 * self.sceneRect().height() + self.sceneRect().top()
 -         self.centerOn(x, y)
 - 
 -         self.setAlignment(Qt.AlignLeft)
 -         self.o_transform = self.transform()
 -         self.zoom_x = 1
 -         self.zoom_y = 1
 - 
 -     def setZoomX(self, scale_x):
 -         self.setTransform(self.o_transform)
 -         self.zoom_x = 1 + scale_x / float(99) * 2
 -         self.scale(self.zoom_x, self.zoom_y)
 - 
 -     def setZoomY(self, scale_y):
 -         self.setTransform(self.o_transform)
 -         self.zoom_y = 1 + scale_y / float(99)
 -         self.scale(self.zoom_x, self.zoom_y)
 - 
 - # ------------------------------------------------------------------------------------------------------------
 - 
 - class ModeIndicator(QWidget):
 -     def __init__(self, parent):
 -         QWidget.__init__(self, parent)
 -         #self.setGeometry(0, 0, 30, 20)
 -         self.setFixedSize(30,20)
 -         self.mode = None
 - 
 -     def paintEvent(self, event):
 -         painter = QPainter(self)
 -         event.accept()
 - 
 -         painter.begin(self)
 - 
 -         painter.setPen(QPen(QColor(0, 0, 0, 0)))
 -         if self.mode == 'velocity_mode':
 -             painter.setBrush(QColor(127, 0, 0))
 -         elif self.mode == 'insert_mode':
 -             painter.setBrush(QColor(0, 100, 127))
 -         else:
 -             painter.setBrush(QColor(0, 0, 0, 0))
 -         painter.drawRect(0, 0, 30, 20)
 - 
 -         # FIXME
 -         painter.end()
 - 
 -     def changeMode(self, new_mode):
 -         self.mode = new_mode
 -         self.update()
 - 
 - # ------------------------------------------------------------------------------------------------------------
 
 
  |