#
# THIS FILE IS PART OF THE JOKOSHER PROJECT AND LICENSED UNDER THE GPL. SEE
# THE 'COPYING' FILE FOR DETAILS
#
# TimeLineBar.py
#
# This module is the gtk.Frame which sits above the instruments and
# holds the TImeLine as well as the click track button and the bpm label.
#
#-------------------------------------------------------------------------------
import gtk, gtk.glade
import gobject
import gettext
import os
import TimeLine
import Globals
import ui.EventLaneHSeparator as EventLaneHSeparator
# for the time signature combo
from math import log
_=gettext.gettext
class TimeLineBar(gtk.HBox):
"""
This class contains the TimeLine widget as well as the click track button and the bpm label in a gtk.Frame widget.
"""
#_____________________________________________________________________
def __init__(self, project, mainview):
"""
Creates a new instance of TimeLineBar
Parameters:
project -- reference to Project (Project.py).
mainview -- reference to MainApp (JokosherApp.py).
"""
gtk.HBox.__init__(self)
self.project = project
self.mainview = mainview
self.timeline = TimeLine.TimeLine(self.project, mainview)
self.Updating = False
# add click / bpm / signature box
self.clickbutton = gtk.VolumeButton()
self.clickbutton.set_value(0)
self.clickbutton.set_relief(gtk.RELIEF_NORMAL)
self.clickbutton.set_property("size", gtk.ICON_SIZE_BUTTON)
self.clickbutton_metronome_image = gtk.Image()
self.clickbutton_metronome_image.set_from_file(os.path.join(Globals.IMAGE_PATH, "icon_click.png"))
# use get_child() not get_image here because GtkScaleButton
# uses gtk_container_add() in its internal implementation
self.clickbutton_volume_image = self.clickbutton.get_child()
self.clickbutton.set_image(self.clickbutton_metronome_image)
image_size_group = gtk.SizeGroup(gtk.SIZE_GROUP_BOTH)
image_size_group.add_widget(self.clickbutton_metronome_image)
image_size_group.add_widget(self.clickbutton_volume_image)
self.clickbutton.set_tooltip_text(_("Adjust volume of click track"))
self.bpmeventbox = gtk.EventBox()
self.bpmframe = gtk.Frame()
self.bpmeventbox.set_tooltip_text(_("Beats per minute"))
self.bpmframe.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
self.bpmlabel = gtk.Label()
self.bpmlabel.set_use_markup(True)
self.bpmlabel.set_markup("%s" % (self.project.bpm))
self.bpmlabel.set_padding(5, 5)
self.bpmeventbox.add(self.bpmlabel)
self.bpmframe.add(self.bpmeventbox)
self.bpmedit = gtk.SpinButton()
self.bpmedit.set_range(1, 400)
self.bpmedit.set_increments(1, 5)
self.bpmedit.set_value(self.project.bpm)
self.bpmedit.connect("activate", self.OnAcceptEditBPM)
self.bpmedit.connect("key_press_event", self.OnEditBPMKey)
self.bpmedit.connect_after("button-release-event", self.OnEditBPMClick)
self.sigeventbox = gtk.EventBox()
self.sigeventbox.set_tooltip_text(_("Time signature"))
self.sigframe = gtk.Frame()
self.sigframe.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
self.siglabel = gtk.Label()
self.siglabel.set_use_markup(True)
self.siglabel.set_markup("%d/%d" % (self.project.meter_nom, self.project.meter_denom))
self.siglabel.set_padding(5, 5)
self.sigeventbox.add(self.siglabel)
self.sigframe.add(self.sigeventbox)
self.sigeditPacked = False
# set events
self.clickbutton.connect("value-changed", self.OnClickTrackVolume)
self.clickbutton.connect("enter_notify_event", self.OnClickButtonEnter)
self.clickbutton.connect("leave_notify_event", self.OnClickButtonEnter)
self.bpmeventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK)
self.bpmeventbox.connect("button_release_event", self.OnEditBPM)
self.bpmeventbox.connect("enter_notify_event", self.OnMouseMoveBPM)
self.bpmeventbox.connect("leave_notify_event", self.OnMouseMoveBPM)
self.sigeventbox.set_events(gtk.gdk.BUTTON_PRESS_MASK)
self.sigeventbox.connect("button_press_event", self.OnEditSig)
self.sigeventbox.connect("enter_notify_event", self.OnMouseMoveTimeSig)
self.sigeventbox.connect("leave_notify_event", self.OnMouseMoveTimeSig)
self.project.connect("bpm", self.OnProjectBPMChange)
self.project.connect("time-signature", self.OnProjectSigChange)
self.project.connect("click-track", self.OnProjectClickTrackChange)
self.sigDialog = None
# ###########################################################
self.headerhbox = gtk.HBox()
self.headerhbox.set_border_width(2)
self.headerhbox.set_spacing(5)
self.headerhbox.pack_start(self.clickbutton, True, True)
self.headerhbox.pack_start(self.bpmframe, True, True)
self.headerhbox.pack_start(self.sigframe, True, True)
self.headerVBox = gtk.VBox()
self.headerVBox.pack_start(self.headerhbox, True, True)
self.headerVBox.pack_start(gtk.HSeparator(), False, False)
self.timelineVBox = gtk.VBox()
self.timelineVBox.pack_start(self.timeline, True, True)
separator = EventLaneHSeparator.EventLaneHSeparator(self.project, self.project.transport)
self.timelineVBox.pack_start(separator, False, False)
self.pack_start(self.headerVBox, False, False)
self.pack_start(self.timelineVBox)
#_____________________________________________________________________
def GetHeaderWidget(self):
"""
Returns the widget which is required to be aligned with the instrument headers.
"""
return self.headerVBox
#_____________________________________________________________________
def OnProjectBPMChange(self, project):
"""
Callback for when the BPM of the project changes. This method
will update the contents of the beats per minute box.
Parameters:
project -- The project that send the signal.
"""
#Do this outside the if statement so that it gets updated if someone else changes the bpm
self.bpmlabel.set_use_markup(True)
self.bpmlabel.set_markup("%d" % (self.project.bpm))
#_____________________________________________________________________
def OnProjectSigChange(self, project):
"""
Callback for when the BPM of the project changes. This method
will update the contents of the time signature label.
Parameters:
project -- The project that send the signal.
"""
self.siglabel.set_use_markup(True)
self.siglabel.set_markup("%d/%d" % (self.project.meter_nom, self.project.meter_denom))
#_____________________________________________________________________
def OnProjectClickTrackChange(self, project, value):
"""
Callback for when the click track of the project it turned on
or shut off. This method will update the button and tooltip state.
Parameters:
project -- The project that send the signal.
value -- The new value of the click track volume.
"""
self.Updating = True
self.clickbutton.set_value(value)
self.Updating = False
#_____________________________________________________________________
def OnEditBPM(self, widget, event):
"""
Called when the user clicks the beats per minute box.
This method will show a spin button widget with a value inside which the user can change.
Parameters:
widget -- reserved for GTK callbacks, don't use it explicitly.
event -- reserved for GTK callbacks, don't use it explicitly.
"""
if event.type == gtk.gdk.BUTTON_RELEASE:
startWidth = self.headerhbox.size_request()[0]
if self.bpmeventbox.parent:
self.bpmframe.remove(self.bpmeventbox)
self.bpmframe.add(self.bpmedit)
self.bpmedit.show()
self.bpmedit.grab_add()
self.bpmedit.grab_focus()
#_____________________________________________________________________
def OnAcceptEditBPM(self, widget=None, cancel=False):
"""
Called when the user finishes editing the beats per minute box.
This method then updates the beats per minute value to the value the user
enters.
Parameters:
widget -- reserved for GTK callbacks, don't use it explicitly.
"""
self.bpmedit.grab_remove()
if self.bpmedit.parent:
self.bpmframe.remove(self.bpmedit)
self.bpmframe.add(self.bpmeventbox)
self.bpmframe.show_all()
if not cancel:
newbpm = self.bpmedit.get_value_as_int()
self.project.SetBPM(float(newbpm))
#_____________________________________________________________________
def OnEditBPMClick(self, widget, event):
"""
Handles the button presses while doing a grab on the bpm edit spinbutton.
If we get a mouse event, it means the user has clicked outside the spinbutton.
So then we save what the user edited and go back to a label.
"""
if event.type == gtk.gdk.BUTTON_RELEASE:
self.OnAcceptEditBPM()
return True
#_____________________________________________________________________
def OnEditBPMKey(self, widget, event):
"""
Handles the key presses while editing the instrument name label.
Used to make the escape key save the name and then return to normal mode.
Parameters:
widget -- GTK callback.
event -- GTK callback.
"""
key = gtk.gdk.keyval_name(event.keyval)
if key == "Escape":
self.OnAcceptEditBPM(cancel=True)
return True
#_____________________________________________________________________
def OnEditSig(self, widget, event):
"""
Called when the user clicks the time signature box.
This method will show a dialog for changing the time signature.
Parameters:
widget -- reserved for GTK callbacks, don't use it explicitly.
event -- reserved for GTK callbacks, don't use it explicitly.
"""
if event.type == gtk.gdk.BUTTON_PRESS:
if not self.sigDialog:
self.sigDialogTree = gtk.glade.XML(Globals.GLADE_PATH, "TimeSignatureDialog")
signals = {
"on_spinBeats_value_changed" : self.OnNomValueChange,
"on_comboValue_changed" : self.OnDenomValueChange,
"on_buttonClose_clicked" : self.OnSigDialogClose
}
self.sigDialogTree.signal_autoconnect(signals)
# references to GUI widgets
self.sigDialog = self.sigDialogTree.get_widget("TimeSignatureDialog")
self.spinBeats = self.sigDialogTree.get_widget("spinBeats")
self.comboValue = self.sigDialogTree.get_widget("comboValue")
# set some properties for the widgets
self.sigDialog.set_icon(self.mainview.icon)
self.spinBeats.set_value(self.project.meter_nom)
self.comboValue.set_active(int(log(self.project.meter_denom, 2)))
else:
self.sigDialog.present()
#_____________________________________________________________________
def OnNomValueChange(self, spinButton):
"""
Updates the Project's beats per measure value.
Parameters:
combobox -- beats per measure combobox.
"""
self.project.SetMeter(int(spinButton.get_value()),
self.project.meter_denom)
#_____________________________________________________________________
def OnDenomValueChange(self, combobox):
"""
Updates the Project's beat value.
Parameters:
spinButton -- beat value spin button.
"""
self.project.SetMeter(self.project.meter_nom,
int(combobox.get_active_text()))
#_____________________________________________________________________
def OnSigDialogClose(self, button):
"""
Closes the time signature settings dialog.
Parameters:
button -- reserved for GTK callbacks, don't use it explicitly.
"""
self.sigDialog.destroy()
self.sigDialog = None
#_____________________________________________________________________
def OnMouseMoveBPM(self, widget, event):
"""
Called when the mouse pointer enters or leaves the beats per minute box.
This method changes the type of cursor if the mouse pointer is hovered over the beats per minute box.
Parameters:
widget -- reserved for GTK callbacks, don't use it explicitly.
event -- reserved for GTK callbacks, don't use it explicitly.
"""
if not widget.window:
return
if (event.type == gtk.gdk.ENTER_NOTIFY):
widget.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.XTERM))
else:
widget.window.set_cursor(None)
#_____________________________________________________________________
def OnMouseMoveTimeSig(self, widget, event):
"""
Called when the mouse pointer enters or leaves the beats per minute box.
This method changes the type of cursor if the mouse pointer is hovered over the beats per minute box.
Parameters:
widget -- reserved for GTK callbacks, don't use it explicitly.
event -- reserved for GTK callbacks, don't use it explicitly.
"""
if not widget.window:
return
if (event.type == gtk.gdk.ENTER_NOTIFY):
self.sigeventbox.set_state(gtk.STATE_PRELIGHT)
else:
self.sigeventbox.set_state(gtk.STATE_NORMAL)
#_____________________________________________________________________
def OnClickTrackVolume(self, widget, value):
"""
Called when the click button is clicked. This method will call the project
to set the volume of the click track.
"""
if not self.Updating:
self.project.SetClickTrackVolume(value)
#_____________________________________________________________________
def OnClickButtonEnter(self, widget, event):
if event.type == gtk.gdk.ENTER_NOTIFY:
self.clickbutton.set_image(self.clickbutton_volume_image)
elif event.type == gtk.gdk.LEAVE_NOTIFY:
self.clickbutton.set_image(self.clickbutton_metronome_image)
#_____________________________________________________________________
#=========================================================================