[jokosher-devel] [PATCH] for ticket #26 (Moving instruments)



Ticket link:
http://jokosher.python-hosting.com/ticket/26

Moving the instruments is undo-able and save-able. When dragging
starts, the default drag-icon is changed into the image of the
instrument that is being dragged.

I had some  problems with those drag-icons: they re-appeared exactly 5
minutes after the drag and drop. This seems to be fixed now though.


Jens "RealNitro" Geiregat
Index: InstrumentViewer.py
===================================================================
--- InstrumentViewer.py	(revision 543)
+++ InstrumentViewer.py	(working copy)
@@ -21,6 +21,11 @@
 	UNSELECTED_COLOUR = None
 	SELECTED_COLOUR = None
 	
+	INSTR_DRAG_TYPE = 83			# Number only to be used inside Jokosher
+	DRAG_TARGETS = [ ( "jokosher_instr_move", 	# A custom name for the instruments
+					   gtk.TARGET_SAME_APP,		# Only move inside Jo
+					   INSTR_DRAG_TYPE )]		# Use the custom number
+	
 	#_____________________________________________________________________
 	
 	def __init__(self, project, instrument, projectview, mainview, small = False):
@@ -42,8 +47,10 @@
 		self.add(self.mainBox)
 		
 		self.headerBox = gtk.VBox()
+		self.headerEventBox = gtk.EventBox()
+		self.headerEventBox.add(self.headerBox)
 		self.headerAlign = gtk.Alignment(0, 0, 1.0, 1.0)
-		self.headerAlign.add(self.headerBox)
+		self.headerAlign.add(self.headerEventBox)
 		self.eventLane = EventLaneViewer(project, instrument, self, mainview, self.small)
 		
 		self.mainBox.pack_start(self.headerAlign, False, False)
@@ -111,6 +118,18 @@
 			self.separator = gtk.HSeparator()
 			self.headerBox.pack_end(self.separator, False, True)
 		self.instrument.isSelected = False
+		
+		# Begin Drag and Drop code
+		self.headerEventBox.drag_dest_set(gtk.DEST_DEFAULT_DROP,
+										  self.DRAG_TARGETS, 
+										  gtk.gdk.ACTION_MOVE)
+		self.headerEventBox.connect('drag_motion', self.OnDragMotion)
+		self.headerEventBox.drag_source_set(gtk.gdk.BUTTON1_MASK, 
+										    self.DRAG_TARGETS, 
+										    gtk.gdk.ACTION_MOVE)
+		# Connect to drag_begin to add a custom icon
+		self.headerEventBox.connect('drag_begin', self.OnDragBegin)
+		self.headerEventBox.connect('drag_drop', self.OnDragDrop)
 
 	#_____________________________________________________________________
 
@@ -195,11 +214,13 @@
 			self.SELECTED_COLOUR = self.get_style().base[3]
 			
 			self.modify_bg(gtk.STATE_NORMAL, self.SELECTED_COLOUR)
+			self.headerEventBox.modify_bg(gtk.STATE_NORMAL, self.SELECTED_COLOUR)
 			self.labeleventbox.modify_bg(gtk.STATE_NORMAL, self.SELECTED_COLOUR)
 			self.eventLane.modify_bg(gtk.STATE_NORMAL, self.SELECTED_COLOUR)
 			
 		else:
 			self.modify_bg(gtk.STATE_NORMAL, self.UNSELECTED_COLOUR)
+			self.headerEventBox.modify_bg(gtk.STATE_NORMAL, self.UNSELECTED_COLOUR)
 			self.labeleventbox.modify_bg(gtk.STATE_NORMAL, self.UNSELECTED_COLOUR)
 			self.eventLane.modify_bg(gtk.STATE_NORMAL, self.UNSELECTED_COLOUR)
 		
@@ -246,6 +267,66 @@
 				a.connect("activate", cb)
 
 		m.popup(None, None, None, mouse.button, mouse.time)
-		
+	
+	#______________________________________________________________________
+	
+	def OnDragMotion(self, widget, context, x, y, time):
+		'''
+			Called each time the user moves his/her mouse while dragging.
+			'if' clause checks if mouse is on an instrument that isn't the
+			source instrument. If so, it swaps that instrument and the
+			source instrument in the GUI. Swapping of the Instrument objects
+			in self.project.instruments happens in OnDragDrop().
+		'''
+		source_header = context.get_source_widget() 	# Will return an EventBox (self.headerEventBox)
+		if widget != source_header: 					# Dont swap with self
+			box = self.GetInstrumentViewVBox()
+			iv_array = box.get_children()				# InstrumentView array
+			index_iv = iv_array.index(self)
+			
+			source_iv = [iv for iv in iv_array if iv.headerEventBox == source_header][0]
+			index_source_iv = iv_array.index(source_iv)
+			
+			box.reorder_child(source_iv, index_iv)		# Immediate visual feedback
+		# Without these lines the icon would fly back to the start of the drag when dropping
+		context.drag_status(gtk.gdk.ACTION_MOVE, time)
+		return True
+
+	#______________________________________________________________________
+	
+	def OnDragBegin(self, widget, context):
+		'''
+			Called at the start of the drag and drop.
+			Displays the instrument icon when dragging.
+		'''
+		widget.drag_source_set_icon_pixbuf(self.instrument.pixbuf)
+		return True
+	
+	#______________________________________________________________________
+	
+	def OnDragDrop(self, widget, context, x, y, time):
+		'''
+			Called when the user releases MOUSE1.
+			Calls MoveInstrument, which moves the dragged
+			instrument to the end position in the
+			self.project.instruments array.
+			MoveInstrument is undo-able.
+		'''
+		id = self.instrument.id
+		box = self.GetInstrumentViewVBox()
+		position = box.get_children().index(self)
+		self.project.MoveInstrument(id, position)
+	
+	#______________________________________________________________________
+
+	def GetInstrumentViewVBox(self):
+		'''
+			Returns the instrumentBox if the current view is a RecordingView.
+			Returns the timebox if the current view is a CompactMixView.
+		'''
+		if hasattr(self.projectview, "instrumentBox"):
+			return self.projectview.instrumentBox
+		else:
+			return self.projectview.timebox
+	
 	#=========================================================================	
-
Index: Project.py
===================================================================
--- Project.py	(revision 543)
+++ Project.py	(working copy)
@@ -806,6 +806,23 @@
 		
 	#_____________________________________________________________________
 	
+	def MoveInstrument(self, id, position):
+		'''
+			Move an instrument in the instrument list.
+			Used for drag and drop ordering of instruments in
+			InstrumentViewer.py
+			
+			undo : MoveInstrument(%(temp)d, %(temp1)d)
+		'''
+		self.temp = id
+		instr = [x for x in self.instruments if x.id == id][0]
+		self.temp1 = self.instruments.index(instr)
+		
+		self.instruments.remove(instr)
+		self.instruments.insert(position, instr)		
+	
+	#_____________________________________________________________________
+	
 	def ClearEventSelections(self):
 		''' Clears the selection of any events '''
 		for instr in self.instruments:
Index: RecordingView.py
===================================================================
--- RecordingView.py	(revision 543)
+++ RecordingView.py	(working copy)
@@ -79,31 +79,40 @@
 	
 
 	def Update(self):
+		# Note: InstrumentViews MUST have the order that the instruments have in
+		#       Project.instruments to keep the drag and drop of InstrumentViews
+		#       consistent!
 		children = self.instrumentBox.get_children()
+		#Remove all instrumentviews, they will be added inside the for loop
+		for child in children:
+			self.instrumentBox.remove(child)
 		for instr in self.project.instruments:
+			#Find the InstrumentView that matches instr:
 			iv = None
 			for ident, instrV in self.views:
 				if instrV.instrument is instr:
 					iv = instrV
 					break
+			#If there is no InstrumentView for instr, create one:
 			if not iv:
 				iv = InstrumentViewer.InstrumentViewer(self.project, instr, self, self.mainview)
 				instr.AddListener(self)
+				#Add it to the views
 				self.views.append((instr.id, iv))
 				iv.headerAlign.connect("size-allocate", self.UpdateSize)
-			
-			if not iv in children:
-				self.instrumentBox.pack_start(iv, False, False)
-			
+			#Add the InstrumentView to the VBox
+			self.instrumentBox.pack_start(iv, False, False)
+			#Make sure the InstrumentView is visible:
 			iv.show()
-		
+		#self.views is up to date now
 		for ident, iv in self.views:
 			#check if instrument has been deleted
 			if not iv.instrument in self.project.instruments and iv in children:
 				self.instrumentBox.remove(iv)
 			else:
-				iv.Update()
+				iv.Update() #Update non-deleted instruments
 		
+		
 		if len(self.views) > 0:
 			self.UpdateSize(None, self.views[0][1].headerAlign.get_allocation())
 		else:


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]