[jokosher-devel] [PATCH] UI improvements
- From: "Daniel Holt" <daniel holt gmail com>
- To: jokosher-devel-list gnome org
- Subject: [jokosher-devel] [PATCH] UI improvements
- Date: Thu, 27 Mar 2008 21:45:20 -0400
Here are three patches that together make the following improvements to the user interface:
- When starting a new selection within an event, any previous selection now clears correctly
- MMB now starts a selection within an event
- Shift+MMB adds to a selection within an event or moves the selection within an event:
- If done on or before the beginning of the existing selection, stretches the selection from the left
- If done on or after the end of the existing selection, stretches the selection from the right
- If done amid the selection, drags the whole selection
- Currently the drag starts with the end of the selection where the mouse clicks, but the drag should start with the selection exactly where it is
- Ctrl+{L|R}MB now deselects an event if it's already selected
- Shift+{L|R}MB now selects all events from the most recently selected event to the currently clicked event. Can be combined with Ctrl+LMB
Patches are included as attachments, and affect Event.py, EventViewer.py, and Instrument.py
Daniel
Index: /home/daniel/workspace/Jokosher/Jokosher/EventViewer.py
===================================================================
--- /home/daniel/workspace/Jokosher/Jokosher/EventViewer.py (revision 1494)
+++ /home/daniel/workspace/Jokosher/Jokosher/EventViewer.py (working copy)
@@ -104,6 +104,7 @@
# Selections--marking part of the waveform. Don't confuse this with
# self.event.isSelected, which means the whole waveform is selected.
self.isSelecting = False # True if a selection is currently being set
+ self.isDraggingSelection = False# True if a selection is currently being dragged
self.isDraggingFade = False # True if the user is dragging a fade marker
self.lane = lane # The parent lane for this object
self.currentScale = 0 # Tracks if the project viewScale has changed
@@ -108,9 +109,9 @@
self.lane = lane # The parent lane for this object
self.currentScale = 0 # Tracks if the project viewScale has changed
self.redrawWaveform = False # Force redraw the cached waveform on next expose event
- self.drawerAlignToLeft = True #boolean; if the drawer should be at the left of current selection
+ self.drawerAlignToLeft = True #boolean; if the drawer should be at the left of current selection
#otherwise it will be put on the right
- self.fadeMarkers = [100,100] #the values of the right and left fade markers on the selection
+ self.fadeMarkers = [100,100] #the values of the right and left fade markers on the selection
# Set accessibility helpers
self.SetAccessibleName()
@@ -540,6 +541,7 @@
self.highlightCursor = None
+
elif self.isSelecting:
x2 = max(0,min(self.allocation.width,mouse.x))
self.event.selection[1] = self.SecFromPixX(x2)
@@ -556,6 +558,26 @@
#move the drawer to its proper position
self.UpdateDrawerPosition(selection_direction == "rtol")
+ elif self.isDraggingSelection:
+ #temp variable to keep track of length moved
+ selectionDuration = self.PixXFromSec(self.event.selection[1] - self.event.selection[0])
+ x1 = max(selectionDuration,min(self.allocation.width,mouse.x))#(mouse.x - mousedelta1)
+ x0 = x1 - selectionDuration#(mouse.x - mousedelta0)
+ self.event.selection[1] = self.SecFromPixX(x1)
+ self.event.selection[0] = self.SecFromPixX(x0)
+ self.UpdateFadeMarkers()
+
+ selection_direction = "ltor"
+ selection = self.event.selection
+ if selection[0] > selection[1]:
+ selection_direction = "rtol"
+ self.fadeMarkers.reverse()
+
+ #set the drawer align position
+ self.drawerAlignToLeft = (selection_direction == "rtol")
+ #move the drawer to its proper position
+ self.UpdateDrawerPosition(selection_direction == "rtol")
+
else:
self.highlightCursor = mouse.x
@@ -570,8 +592,11 @@
Possible click combinations to capture:
{L|R}MB: deselect all Events, remove any existing selection in
this Event then select this Event and begin moving the Event.
- LMB+shift: remove any existing selection in this Event and begin
- selecting part of this Event.
+ MMB: remove any existing selection in this Event and begin selecting
+ part of this Event.
+ MMB+shift: continue selecting in addition to an already selected part
+ of this Event.
+ {L|R}MB+shift: select everything from last selected Event to this Event
{L|R}MB+ctrl: select this Event without deselecting other Events.
RMB: display a context menu.
LMB double-click: split this Event here.
@@ -592,13 +617,38 @@
# {L|R}MB: deselect all events, select this event, begin moving the event
# {L|R}MB+ctrl: select this event without deselecting other events
- if 'GDK_CONTROL_MASK' not in mouse.state.value_names:
+ # {L|R}MB+shift: select all events from this one to last selected
+ if ('GDK_CONTROL_MASK' not in mouse.state.value_names) & ('GDK_SHIFT_MASK' not in mouse.state.value_names):
self.project.ClearEventSelections()
self.project.SelectInstrument(None)
- self.event.SetSelected(True)
-
+ self.event.SetSelected(True)
+ elif 'GDK_SHIFT_MASK' not in mouse.state.value_names:
+ #deselect previously selected events
+ if self.event.isSelected == True:
+ self.event.SetSelected(False)
+ else:
+ self.event.SetSelected(True)
+ elif 'GDK_SHIFT_MASK' in mouse.state.value_names:
+ #CTRL+SHIFT: add everything from last selected to self to selection
+ #SHIFT: everything from last selected to self becomes the selection
+ if self.event.instrument.orderSelected == []:
+ startSel = self.event.instrument.events.index(self.event) #If no event selected, select clicked
+ else:
+ startSel = self.event.instrument.orderSelected[-1] #Begin shift-selection at last part of orderSelected
+ endSel = self.event.instrument.events.index(self.event) #End shift-selection at event clicked
+ if 'GDK_CONTROL_MASK' not in mouse.state.value_names:
+ self.project.ClearEventSelections()
+ self.project.SelectInstrument(None)
+ #select everything from startSel to endSel
+ if endSel >= startSel:
+ for i in range (startSel, endSel+1):
+ self.event.instrument.events[i].SetSelected(True)
+ else:
+ for i in range (startSel, endSel-1, -1):
+ self.event.instrument.events[i].SetSelected(True)
+
#Don't allow editing while playing back.
- #It must be here to avoid afecting the selection behavior
+ #It must be here to avoid affecting the selection behavior
if self.mainview.isPlaying or self.mainview.isPaused:
return True
@@ -605,6 +655,32 @@
# RMB: context menu
if mouse.button == 3:
self.ContextMenu(mouse)
+
+ # MMB: selecting part of this event
+ elif mouse.button == 2:
+ if 'GDK_SHIFT_MASK' not in mouse.state.value_names:
+ # Remove any existing selection in this event, if
+ # MMB is pressed without shift.
+ self.event.selection[0] = self.SecFromPixX(mouse.x)
+ self.event.selection[1] = self.SecFromPixX(mouse.x)
+ self.isSelecting = True
+ self.fadeMarkers = [100,100]
+ else: #if shift is on
+ #If cursor is on or before selection, drag the leftmost portion of selection
+ if (self.SecFromPixX(mouse.x) <= self.event.selection[0]) & (self.SecFromPixX(mouse.x) <= self.event.selection[1]):
+ self.event.selection[0] = self.event.selection[1]
+ self.isSelecting = True
+ #If cursor is amid selection, move the whole selection
+ elif self.event.selection[0] < self.SecFromPixX(mouse.x) < self.event.selection[1]:
+
+ self.isDraggingSelection = True
+ #If cursor is on or after selection, drag the rightmost portion of selection
+ else:
+ self.isSelecting = True
+
+ if not self.selmessageID:
+ self.selmessageID = self.mainview.SetStatusBar(_("<b>Click</b> the buttons below the selection to do something to that portion of audio."))
+
elif mouse.button == 1:
# check to see if the user clicked on the cancel button
if self.cancelButtonArea.x <= mouse.x <= self.cancelButtonArea.width+self.cancelButtonArea.x \
@@ -613,46 +689,37 @@
self.OnDelete()
return True
- if 'GDK_SHIFT_MASK' in mouse.state.value_names:
- # LMB+shift: remove any existing selection in this event, begin
- # selecting part of this event
- self.isSelecting = True
- self.event.selection[0] = self.SecFromPixX(mouse.x)
- self.fadeMarkers = [100,100]
- if not self.selmessageID:
- self.selmessageID = self.mainview.SetStatusBar(_("<b>Click</b> the buttons below the selection to do something to that portion of audio."))
- else:
- if self.fadeMarkersContext and self.fadeMarkersContext.in_fill(mouse.x, mouse.y):
- # LMB over a fadeMarker: drag that marker
- self.isDraggingFade = True
- if mouse.x > self.PixXFromSec(self.event.selection[1]) - self._PIXX_FADEMARKER_WIDTH - 1:
- self.fadeBeingDragged = 1
- return True
- else:
- self.fadeBeingDragged = 0
- return True
- if mouse.type == gtk.gdk._2BUTTON_PRESS:
- # LMB double-click: split here
- self.mouseAnchor[0] = mouse.x
- if self.event.isLoading == False:
- self.OnSplit(None, mouse.x)
+ if self.fadeMarkersContext and self.fadeMarkersContext.in_fill(mouse.x, mouse.y):
+ # LMB over a fadeMarker: drag that marker
+ self.isDraggingFade = True
+ if mouse.x > self.PixXFromSec(self.event.selection[1]) - self._PIXX_FADEMARKER_WIDTH - 1:
+ self.fadeBeingDragged = 1
+ return True
+ else:
+ self.fadeBeingDragged = 0
return True
+ if mouse.type == gtk.gdk._2BUTTON_PRESS:
+ # LMB double-click: split here
+ self.mouseAnchor[0] = mouse.x
+ if self.event.isLoading == False:
+ self.OnSplit(None, mouse.x)
+ return True
- # remove any existing selection in this event
- self.event.selection = [0,0]
- if self.drawer.parent == self.lane.fixed:
- self.lane.fixed.remove(self.drawer)
- if self.volmessageID: #clesr status bar if not already clear
- self.mainview.ClearStatusBar(self.volmessageID)
- self.volmessageID = None
- if self.selmessageID: #clesr status bar if not already clear
- self.mainview.ClearStatusBar(self.selmessageID)
- self.selmessageID = None
- self.isDragging = True
+ # remove any existing selection in this event
+ self.event.selection = [0,0]
+ if self.drawer.parent == self.lane.fixed:
+ self.lane.fixed.remove(self.drawer)
+ if self.volmessageID: #clesr status bar if not already clear
+ self.mainview.ClearStatusBar(self.volmessageID)
+ self.volmessageID = None
+ if self.selmessageID: #clesr status bar if not already clear
+ self.mainview.ClearStatusBar(self.selmessageID)
+ self.selmessageID = None
+ self.isDragging = True
- self.eventStart = self.event.start
- ptr = gtk.gdk.display_get_default().get_pointer()
- self.mouseAnchor = [ptr[1], ptr[2]]
+ self.eventStart = self.event.start
+ ptr = gtk.gdk.display_get_default().get_pointer()
+ self.mouseAnchor = [ptr[1], ptr[2]]
return True
@@ -957,6 +1024,14 @@
elif self.isSelecting:
self.isSelecting = False
self.ShowDrawer()
+ elif mouse.button == 2:
+ #For MMB selections
+ if self.isSelecting:
+ self.isSelecting = False
+ self.ShowDrawer()
+ elif self.isDraggingSelection:
+ self.isDraggingSelection = False
+ self.ShowDrawer()
#_____________________________________________________________________
Index: /home/daniel/workspace/Jokosher/Jokosher/Instrument.py
===================================================================
--- /home/daniel/workspace/Jokosher/Jokosher/Instrument.py (revision 1494)
+++ /home/daniel/workspace/Jokosher/Jokosher/Instrument.py (working copy)
@@ -110,6 +110,7 @@
self.isSolo = False # True if the instrument is solo'd (only instrument active)
self.isVisible = True # True if the instrument should be displayed in the mixer views
self.isSelected = False # True if the instrument is currently selected
+ self.orderSelected = [] # The order in which events were selected (Used for shift-selection of events)
self.level = 0.0 # Current audio level in range 0..1
self.volume = 1.0 # Gain of the current instrument in range 0..1
Index: /home/daniel/workspace/Jokosher/Jokosher/Event.py
===================================================================
--- /home/daniel/workspace/Jokosher/Jokosher/Event.py (revision 1494)
+++ /home/daniel/workspace/Jokosher/Jokosher/Event.py (working copy)
@@ -840,10 +840,14 @@
# No need to emit a signal when there is no change in selection state
if self.isSelected is not sel:
self.isSelected = sel
+ if sel == True:
+ self.instrument.orderSelected.append(self.instrument.events.index(self))
+ else:
+ self.instrument.orderSelected.remove(self.instrument.events.index(self))
self.emit("selected")
#_____________________________________________________________________
-
+
def MayPlace(self, xpos):
"""
Checks if this event could be placed at xpos without
[Date Prev][
Date Next] [Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]