def __init__ (self, data_dir):
		gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
		self.preferences = RecordingPreferences (data_dir)
		self.masterer = AudioMastering (self.preferences)
		self.masterer.listeners.append (self)
		g = gtk.glade.XML (os.path.join (self.preferences.data_dir, "serpentine.glade"),
		                   "main_window_container")
		self.add (g.get_widget ("main_window_container"))
		self.set_title ("Serpentine")
		
		# Add a file button
		g.get_widget ("add").connect ("clicked", self.add_file)
		g.get_widget ("add_mni").connect ("activate", self.add_file)
		
		# record button
		g.get_widget("burn").connect ("clicked", self.burn)
		
		# masterer widget
		box = self.get_child()
		self.masterer.show()
		box.add (self.masterer)
		
		# preferences
		g.get_widget ("preferences_mni").connect ('activate', self.__on_preferences)
		
		# setup remove buttons
		self.remove = MapProxy ({'menu': g.get_widget ("remove_mni"),
		                         'button': g.get_widget ("remove")})

		self.remove['menu'].connect ("activate", self.remove_file)
		self.remove['button'].connect ("clicked", self.remove_file)
		self.remove.set_sensitive (False)
		
		# setup record button
		self.burn = g.get_widget ("burn")
		self.burn.set_sensitive (False)
		
		# setup clear buttons
		self.clear = MapProxy ({'menu': g.get_widget ("clear_mni"),
		                        'button': g.get_widget ("clear")})
		self.clear['button'].connect ("clicked", self.clear_files)
		self.clear['menu'].connect ("activate", self.clear_files)
		self.clear.set_sensitive (False)
		
		# setup quit menu item
		g.get_widget ("quit_mni").connect ('activate', self.quit)
		self.connect("destroy", self.quit)
		
		# About dialog
		g.get_widget ("about_mni").connect ('activate', self.__on_about)
		
		self.__last_path = None
		self.__add_file = gtk.FileChooserDialog (buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
		self.__add_file.set_title ('')
		self.__add_file.set_select_multiple (True)
		
		if not self.preferences.drive:
			gtk_util.dialog_warn ("No recording drive found", "No recording drive found on your system, therefore some of Serpentine's functionalities will be disabled.")
			g.get_widget ("preferences_mni").set_sensitive (False)
			self.burn.set_sensitive (False)
    def __init__ (self, application):
        gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
        operations.Operation.__init__ (self)
        Component.__init__ (self, application)
            
        self.__application = application
        self.__masterer = AudioMastering ()
        # Variable related to this being an Operation
        self.__running = False
        self.connect ("show", self.__on_show)
        # We listen for GtkMusicList events
        self.music_list_widget.listeners.append (self)

        glade_file = os.path.join (constants.data_dir, "serpentine.glade")
        g = gtk.glade.XML (glade_file, "main_window_container")
        
        # Send glade to setup subcomponents
        for c in self._components:
            if hasattr (c, "_setup_glade"):
                c._setup_glade (g)

        self.add (g.get_widget ("main_window_container"))
        self.set_title ("Serpentine")
        self.set_default_size (450, 350)
        self.set_icon_name ("gnome-dev-cdrom-audio")
        
        
        # record button
        # setup record button
        self.__write_to_disc = MapProxy (dict(
            button = g.get_widget ("write_to_disc"),
            menu   = g.get_widget ("write_to_disc_mni")
        ))
        
        self.__write_to_disc.set_sensitive (False)
        self.__write_to_disc["button"].connect ("clicked", self.__on_write_files)
        self.__write_to_disc["menu"].connect ("activate", self.__on_write_files)
        
        # masterer widget
        box = self.get_child()
        self.music_list_widget.show()
        box.add (self.music_list_widget)
        
        # preferences
        g.get_widget ("preferences_mni").connect ("activate", self.__on_preferences)
        
        # setup remove buttons
        self.remove = MapProxy ({"menu": g.get_widget ("remove_mni"),
                                 "button": g.get_widget ("remove")})

        self.remove["menu"].connect ("activate", self.__on_remove_file)
        self.remove["button"].connect ("clicked", self.__on_remove_file)
        self.remove.set_sensitive (False)
        
        # setup clear buttons
        self.clear = MapProxy ({"menu": g.get_widget ("clear_mni"),
                                "button": g.get_widget ("clear")})
        self.clear["button"].connect ("clicked", self.clear_files)
        self.clear["menu"].connect ("activate", self.clear_files)
        self.clear.set_sensitive (False)
        
        # setup quit menu item
        g.get_widget ("quit_mni").connect ("activate", self.stop)
        self.connect("delete-event", self.stop)
        
        # About dialog
        g.get_widget ("about_mni").connect ("activate", self.__on_about)
        
        # update buttons
        self.on_contents_changed()
        
        if self.__application.preferences.drive is None:
            gtkutil.dialog_warn (
                _("No recording drive found"),
                _("No recording drive found on your system, therefore some of "
                  "Serpentine's functionalities will be disabled."),
                parent = self
            )
            g.get_widget ("preferences_mni").set_sensitive (False)
            self.__write_to_disc.set_sensitive (False)
    
        # Load internal XSPF playlist
        self.__load_playlist()
class SerpentineWindow (gtk.Window, OperationListener, operations.Operation, Component):
    # TODO: finish up implementing an Operation
    components = (
        AddFileComponent,
        PlaylistComponent,
        SavePlaylistComponent,
        ToolbarComponent
    )
    
    def __init__ (self, application):
        gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
        operations.Operation.__init__ (self)
        Component.__init__ (self, application)
            
        self.__application = application
        self.__masterer = AudioMastering ()
        # Variable related to this being an Operation
        self.__running = False
        self.connect ("show", self.__on_show)
        # We listen for GtkMusicList events
        self.music_list_widget.listeners.append (self)

        glade_file = os.path.join (constants.data_dir, "serpentine.glade")
        g = gtk.glade.XML (glade_file, "main_window_container")
        
        # Send glade to setup subcomponents
        for c in self._components:
            if hasattr (c, "_setup_glade"):
                c._setup_glade (g)

        self.add (g.get_widget ("main_window_container"))
        self.set_title ("Serpentine")
        self.set_default_size (450, 350)
        self.set_icon_name ("gnome-dev-cdrom-audio")
        
        
        # record button
        # setup record button
        self.__write_to_disc = MapProxy (dict(
            button = g.get_widget ("write_to_disc"),
            menu   = g.get_widget ("write_to_disc_mni")
        ))
        
        self.__write_to_disc.set_sensitive (False)
        self.__write_to_disc["button"].connect ("clicked", self.__on_write_files)
        self.__write_to_disc["menu"].connect ("activate", self.__on_write_files)
        
        # masterer widget
        box = self.get_child()
        self.music_list_widget.show()
        box.add (self.music_list_widget)
        
        # preferences
        g.get_widget ("preferences_mni").connect ("activate", self.__on_preferences)
        
        # setup remove buttons
        self.remove = MapProxy ({"menu": g.get_widget ("remove_mni"),
                                 "button": g.get_widget ("remove")})

        self.remove["menu"].connect ("activate", self.__on_remove_file)
        self.remove["button"].connect ("clicked", self.__on_remove_file)
        self.remove.set_sensitive (False)
        
        # setup clear buttons
        self.clear = MapProxy ({"menu": g.get_widget ("clear_mni"),
                                "button": g.get_widget ("clear")})
        self.clear["button"].connect ("clicked", self.clear_files)
        self.clear["menu"].connect ("activate", self.clear_files)
        self.clear.set_sensitive (False)
        
        # setup quit menu item
        g.get_widget ("quit_mni").connect ("activate", self.stop)
        self.connect("delete-event", self.stop)
        
        # About dialog
        g.get_widget ("about_mni").connect ("activate", self.__on_about)
        
        # update buttons
        self.on_contents_changed()
        
        if self.__application.preferences.drive is None:
            gtkutil.dialog_warn (
                _("No recording drive found"),
                _("No recording drive found on your system, therefore some of "
                  "Serpentine's functionalities will be disabled."),
                parent = self
            )
            g.get_widget ("preferences_mni").set_sensitive (False)
            self.__write_to_disc.set_sensitive (False)
    
        # Load internal XSPF playlist
        self.__load_playlist()
        
    music_list_widget = property (lambda self: self.__masterer)
    
    music_list = property (lambda self: self.__masterer.music_list)
    
    can_start = property (lambda self: True)
    # TODO: handle the can_stop property better
    can_stop = property (lambda self: True)
    
    masterer = property (lambda self: self.__masterer)
    
    application = property (lambda self: self.__application)
    
    def __on_show (self, *args):
        self.__running = True
    
    def __load_playlist (self):
        #TODO: move it to SerpentineApplication ?
        """Private method for loading the internal playlist."""
        try:
            self.__application.preferences.loadPlaylist (self.music_list_widget.source)
        except:
            import traceback
            traceback.print_exc()
            
    def on_selection_changed (self, *args):
        self.remove.set_sensitive (self.music_list_widget.count_selected() > 0)
        
    def on_contents_changed (self, *args):
        is_sensitive = len(self.music_list_widget.source) > 0
        self.clear.set_sensitive (is_sensitive)
        # Only set it sentitive if the drive is available and is not recording
        if self.__application.preferences.drive is not None:
            self.__write_to_disc.set_sensitive (is_sensitive)

    def __on_remove_file (self, *args):
        self.music_list_widget.remove_selected()
        
    def clear_files (self, *args):
        self.music_list_widget.source.clear()
    
    def __on_preferences (self, *args):
        # Triggered by preferences menu item
        self.__application.preferences.dialog.run ()
        self.__application.preferences.dialog.hide ()
    
    def __on_about (self, widget, *args):
        # Triggered by the about menu item
        a = gtk.AboutDialog ()
        a.set_name ("Serpentine")
        a.set_version (self.__application.preferences.version)
        a.set_website ("http://s1x.homelinux.net/projects/serpentine")
        a.set_copyright ("2004-2005 Tiago Cogumbreiro")
        a.set_transient_for (self)
        a.run ()
        a.hide()
    
    def __on_write_files (self, *args):
        # TODO: move this to SerpentineApplication.write_files ?
        try:
            # Try to validate music list
            validate_music_list (self.music_list_widget.source, self.application.preferences)
        except SerpentineCacheError, err:
            show_prefs = False
            
            if err.error_id == SerpentineCacheError.INVALID:
                title = _("Cache directory location unavailable")
                show_prefs = True
                
            elif err.error_id == SerpentineCacheError.NO_SPACE:
                title = _("Not enough space on cache directory")
            
            gtkutil.dialog_warn (title, err.error_message, parent = self)
            return

        # TODO: move this to recording module?
        if self.music_list_widget.source.total_duration > self.music_list_widget.disc_size:
            title = _("Do you want to overburn your disc?")
            msg = _("You are about to record a media disc in overburn mode. "
                    "This may not work on all drives and shouldn't give you "
                    "more then a couple of minutes.")
            btn = _("Write to Disc (Overburning)")
            self.__application.preferences.overburn = True
        else:
            title = _("Do you want to record your music?")
            msg = _("You are about to record a media disc. "
                    "Canceling a writing operation will make "
                    "your disc unusable.")
                    
            btn = _("Write to Disc")
            self.__application.preferences.overburn = False
        
        if gtkutil.dialog_ok_cancel (title, msg, parent = self, ok_button = btn) != gtk.RESPONSE_OK:
            return
        
        self.application.write_files ().start ()
    def __init__(self, application):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        operations.Operation.__init__(self)
        Component.__init__(self, application)

        self.__application = application
        self.__masterer = AudioMastering()
        # Variable related to this being an Operation
        self.__running = False
        self.connect("show", self.__on_show)
        # We listen for GtkMusicList events
        self.music_list_widget.listeners.append(self)

        glade_file = os.path.join(constants.data_dir, "serpentine.glade")
        g = gtk.glade.XML(glade_file, "main_window_container")

        # Send glade to setup subcomponents
        for c in self._components:
            if hasattr(c, "_setup_glade"):
                c._setup_glade(g)

        self.add(g.get_widget("main_window_container"))
        self.set_title("Serpentine")
        self.set_default_size(450, 350)
        self.set_icon_name("gnome-dev-cdrom-audio")

        # record button
        # setup record button
        self.__write_to_disc = MapProxy(
            dict(button=g.get_widget("write_to_disc"),
                 menu=g.get_widget("write_to_disc_mni")))

        self.__write_to_disc.set_sensitive(False)
        self.__write_to_disc["button"].connect("clicked",
                                               self.__on_write_files)
        self.__write_to_disc["menu"].connect("activate", self.__on_write_files)

        # masterer widget
        box = self.get_child()
        self.music_list_widget.show()
        box.add(self.music_list_widget)

        # preferences
        g.get_widget("preferences_mni").connect("activate",
                                                self.__on_preferences)

        # setup remove buttons
        self.remove = MapProxy({
            "menu": g.get_widget("remove_mni"),
            "button": g.get_widget("remove")
        })

        self.remove["menu"].connect("activate", self.__on_remove_file)
        self.remove["button"].connect("clicked", self.__on_remove_file)
        self.remove.set_sensitive(False)

        # setup clear buttons
        self.clear = MapProxy({
            "menu": g.get_widget("clear_mni"),
            "button": g.get_widget("clear")
        })
        self.clear["button"].connect("clicked", self.clear_files)
        self.clear["menu"].connect("activate", self.clear_files)
        self.clear.set_sensitive(False)

        # setup quit menu item
        g.get_widget("quit_mni").connect("activate", self.stop)
        self.connect("delete-event", self.stop)

        # About dialog
        g.get_widget("about_mni").connect("activate", self.__on_about)

        # update buttons
        self.on_contents_changed()

        if self.__application.preferences.drive is None:
            gtkutil.dialog_warn(
                _("No recording drive found"),
                _("No recording drive found on your system, therefore some of "
                  "Serpentine's functionalities will be disabled."), self)
            g.get_widget("preferences_mni").set_sensitive(False)
            self.__write_to_disc.set_sensitive(False)

        # Load internal XSPF playlist
        self.__load_playlist()
class SerpentineWindow(gtk.Window, OperationListener, operations.Operation,
                       Component):
    # TODO: finish up implementing an Operation
    components = (AddFileComponent, PlaylistComponent, SavePlaylistComponent,
                  ToolbarComponent)

    def __init__(self, application):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
        operations.Operation.__init__(self)
        Component.__init__(self, application)

        self.__application = application
        self.__masterer = AudioMastering()
        # Variable related to this being an Operation
        self.__running = False
        self.connect("show", self.__on_show)
        # We listen for GtkMusicList events
        self.music_list_widget.listeners.append(self)

        glade_file = os.path.join(constants.data_dir, "serpentine.glade")
        g = gtk.glade.XML(glade_file, "main_window_container")

        # Send glade to setup subcomponents
        for c in self._components:
            if hasattr(c, "_setup_glade"):
                c._setup_glade(g)

        self.add(g.get_widget("main_window_container"))
        self.set_title("Serpentine")
        self.set_default_size(450, 350)
        self.set_icon_name("gnome-dev-cdrom-audio")

        # record button
        # setup record button
        self.__write_to_disc = MapProxy(
            dict(button=g.get_widget("write_to_disc"),
                 menu=g.get_widget("write_to_disc_mni")))

        self.__write_to_disc.set_sensitive(False)
        self.__write_to_disc["button"].connect("clicked",
                                               self.__on_write_files)
        self.__write_to_disc["menu"].connect("activate", self.__on_write_files)

        # masterer widget
        box = self.get_child()
        self.music_list_widget.show()
        box.add(self.music_list_widget)

        # preferences
        g.get_widget("preferences_mni").connect("activate",
                                                self.__on_preferences)

        # setup remove buttons
        self.remove = MapProxy({
            "menu": g.get_widget("remove_mni"),
            "button": g.get_widget("remove")
        })

        self.remove["menu"].connect("activate", self.__on_remove_file)
        self.remove["button"].connect("clicked", self.__on_remove_file)
        self.remove.set_sensitive(False)

        # setup clear buttons
        self.clear = MapProxy({
            "menu": g.get_widget("clear_mni"),
            "button": g.get_widget("clear")
        })
        self.clear["button"].connect("clicked", self.clear_files)
        self.clear["menu"].connect("activate", self.clear_files)
        self.clear.set_sensitive(False)

        # setup quit menu item
        g.get_widget("quit_mni").connect("activate", self.stop)
        self.connect("delete-event", self.stop)

        # About dialog
        g.get_widget("about_mni").connect("activate", self.__on_about)

        # update buttons
        self.on_contents_changed()

        if self.__application.preferences.drive is None:
            gtkutil.dialog_warn(
                _("No recording drive found"),
                _("No recording drive found on your system, therefore some of "
                  "Serpentine's functionalities will be disabled."), self)
            g.get_widget("preferences_mni").set_sensitive(False)
            self.__write_to_disc.set_sensitive(False)

        # Load internal XSPF playlist
        self.__load_playlist()

    music_list_widget = property(lambda self: self.__masterer)

    music_list = property(lambda self: self.__masterer.music_list)

    can_start = property(lambda self: True)
    # TODO: handle the can_stop property better
    can_stop = property(lambda self: True)

    masterer = property(lambda self: self.__masterer)

    application = property(lambda self: self.__application)

    def __on_show(self, *args):
        self.__running = True

    def __load_playlist(self):
        #TODO: move it to SerpentineApplication ?
        """Private method for loading the internal playlist."""
        try:
            self.__application.preferences.loadPlaylist(
                self.music_list_widget.source)
        except:
            import traceback
            traceback.print_exc()

    def on_selection_changed(self, *args):
        self.remove.set_sensitive(self.music_list_widget.count_selected() > 0)

    def on_contents_changed(self, *args):
        is_sensitive = len(self.music_list_widget.source) > 0
        self.clear.set_sensitive(is_sensitive)
        # Only set it sentitive if the drive is available and is not recording
        if self.__application.preferences.drive is not None:
            self.__write_to_disc.set_sensitive(is_sensitive)

    def __on_remove_file(self, *args):
        self.music_list_widget.remove_selected()

    def clear_files(self, *args):
        self.music_list_widget.source.clear()

    def __on_preferences(self, *args):
        # Triggered by preferences menu item
        self.__application.preferences.dialog.run()
        self.__application.preferences.dialog.hide()

    def __on_about(self, widget, *args):
        # Triggered by the about menu item
        a = gtk.AboutDialog()
        a.set_name("Serpentine")
        a.set_version(self.__application.preferences.version)
        a.set_website("http://s1x.homelinux.net/projects/serpentine")
        a.set_copyright("2004-2005 Tiago Cogumbreiro")
        a.set_transient_for(self)
        a.run()
        a.hide()

    def __on_write_files(self, *args):
        # TODO: move this to SerpentineApplication.write_files ?
        try:
            # Try to validate music list
            validate_music_list(self.music_list_widget.source,
                                self.application.preferences)
        except SerpentineCacheError, err:
            show_prefs = False

            if err.error_id == SerpentineCacheError.INVALID:
                title = _("Cache directory location unavailable")
                show_prefs = True

            elif err.error_id == SerpentineCacheError.NO_SPACE:
                title = _("Not enough space on cache directory")

            gtkutil.dialog_warn(title, err.error_message)
            return

        # TODO: move this to recording module?
        if self.music_list_widget.source.total_duration > self.music_list_widget.disc_size:
            title = _("Do you want to overburn your disc?")
            msg = _("You are about to record a media disc in overburn mode. "
                    "This may not work on all drives and shouldn't give you "
                    "more then a couple of minutes.")
            btn = _("Write to Disc (Overburning)")
            self.__application.preferences.overburn = True
        else:
            title = _("Do you want to record your music?")
            msg = _("You are about to record a media disc. "
                    "Canceling a writing operation will make "
                    "your disc unusable.")

            btn = _("Write to Disc")
            self.__application.preferences.overburn = False

        if gtkutil.dialog_ok_cancel(title, msg, self, btn) != gtk.RESPONSE_OK:
            return

        self.application.write_files().start()
	def __init__ (self, application):
		gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
		operations.Operation.__init__ (self)
		self.__application = application
		self.__masterer = AudioMastering ()
		# Variable related to this being an Operation
		self.__running = False
		self.connect ("show", self.__on_show)
		# We listen for GtkMusicList events
		self.music_list_widget.listeners.append (self)

		glade_file = os.path.join (constants.data_dir, "serpentine.glade")
		g = gtk.glade.XML (glade_file, "main_window_container")
		self.add (g.get_widget ("main_window_container"))
		self.set_title ("Serpentine")
		self.set_default_size (450, 350)
		
		# Add a file button
		g.get_widget ("add").connect ("clicked", self.__on_add_file)
		g.get_widget ("add_mni").connect ("activate", self.__on_add_file)
		
		# record button
		g.get_widget("burn").connect ("clicked", self.__on_write_files)
		
		# masterer widget
		box = self.get_child()
		self.music_list_widget.show()
		box.add (self.music_list_widget)
		
		# preferences
		g.get_widget ("preferences_mni").connect ('activate', self.__on_preferences)
		
		# setup remove buttons
		self.remove = MapProxy ({'menu': g.get_widget ("remove_mni"),
		                         'button': g.get_widget ("remove")})

		self.remove["menu"].connect ("activate", self.__on_remove_file)
		self.remove["button"].connect ("clicked", self.__on_remove_file)
		self.remove.set_sensitive (False)
		
		# setup record button
		self.__on_write_files = g.get_widget ("burn")
		self.__on_write_files.set_sensitive (False)
		
		# setup clear buttons
		self.clear = MapProxy ({'menu': g.get_widget ("clear_mni"),
		                        'button': g.get_widget ("clear")})
		self.clear['button'].connect ("clicked", self.clear_files)
		self.clear['menu'].connect ("activate", self.clear_files)
		self.clear.set_sensitive (False)
		
		# setup quit menu item
		g.get_widget ("quit_mni").connect ('activate', self.stop)
		self.connect("delete-event", self.stop)
		
		# About dialog
		g.get_widget ("about_mni").connect ('activate', self.__on_about)
		
		self.__last_path = None
		self.__add_file = gtk.FileChooserDialog (buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
		self.__add_file.set_title ('')
		self.__add_file.set_select_multiple (True)
		
		# update buttons
		self.on_contents_changed()
		
		if self.__application.preferences.drive is None:
			gtkutil.dialog_warn ("No recording drive found", "No recording drive found on your system, therefore some of Serpentine's functionalities will be disabled.", self)
			g.get_widget ("preferences_mni").set_sensitive (False)
			self.__on_write_files.set_sensitive (False)
	
		# Load internal XSPF playlist
		self.__load_playlist()
class SerpentineWindow (gtk.Window, OperationListener, operations.Operation):
	# TODO: finish up implementing an Operation
	def __init__ (self, application):
		gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
		operations.Operation.__init__ (self)
		self.__application = application
		self.__masterer = AudioMastering ()
		# Variable related to this being an Operation
		self.__running = False
		self.connect ("show", self.__on_show)
		# We listen for GtkMusicList events
		self.music_list_widget.listeners.append (self)

		glade_file = os.path.join (constants.data_dir, "serpentine.glade")
		g = gtk.glade.XML (glade_file, "main_window_container")
		self.add (g.get_widget ("main_window_container"))
		self.set_title ("Serpentine")
		self.set_default_size (450, 350)
		
		# Add a file button
		g.get_widget ("add").connect ("clicked", self.__on_add_file)
		g.get_widget ("add_mni").connect ("activate", self.__on_add_file)
		
		# record button
		g.get_widget("burn").connect ("clicked", self.__on_write_files)
		
		# masterer widget
		box = self.get_child()
		self.music_list_widget.show()
		box.add (self.music_list_widget)
		
		# preferences
		g.get_widget ("preferences_mni").connect ('activate', self.__on_preferences)
		
		# setup remove buttons
		self.remove = MapProxy ({'menu': g.get_widget ("remove_mni"),
		                         'button': g.get_widget ("remove")})

		self.remove["menu"].connect ("activate", self.__on_remove_file)
		self.remove["button"].connect ("clicked", self.__on_remove_file)
		self.remove.set_sensitive (False)
		
		# setup record button
		self.__on_write_files = g.get_widget ("burn")
		self.__on_write_files.set_sensitive (False)
		
		# setup clear buttons
		self.clear = MapProxy ({'menu': g.get_widget ("clear_mni"),
		                        'button': g.get_widget ("clear")})
		self.clear['button'].connect ("clicked", self.clear_files)
		self.clear['menu'].connect ("activate", self.clear_files)
		self.clear.set_sensitive (False)
		
		# setup quit menu item
		g.get_widget ("quit_mni").connect ('activate', self.stop)
		self.connect("delete-event", self.stop)
		
		# About dialog
		g.get_widget ("about_mni").connect ('activate', self.__on_about)
		
		self.__last_path = None
		self.__add_file = gtk.FileChooserDialog (buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
		self.__add_file.set_title ('')
		self.__add_file.set_select_multiple (True)
		
		# update buttons
		self.on_contents_changed()
		
		if self.__application.preferences.drive is None:
			gtkutil.dialog_warn ("No recording drive found", "No recording drive found on your system, therefore some of Serpentine's functionalities will be disabled.", self)
			g.get_widget ("preferences_mni").set_sensitive (False)
			self.__on_write_files.set_sensitive (False)
	
		# Load internal XSPF playlist
		self.__load_playlist()
		
	music_list_widget = property (lambda self: self.__masterer)
	
	music_list = property (lambda self: self.__masterer.music_list)
	
	can_start = property (lambda self: True)
	# TODO: handle the can_stop property better
	can_stop = property (lambda self: True)
	
	def __on_show (self, *args):
		self.__running = True
	
	def __load_playlist (self):
		#TODO: move it to SerpentineApplication ?
		"""Private method for loading the internal playlist."""
		try:
			self.__application.preferences.load_playlist (self.music_list_widget.source)
		except ExpatError:
			pass
		except IOError:
			pass
			
	def on_selection_changed (self, *args):
		self.remove.set_sensitive (self.music_list_widget.count_selected() > 0)
		
	def on_contents_changed (self, *args):
		is_sensitive = len(self.music_list_widget.source) > 0
		self.clear.set_sensitive (is_sensitive)
		# Only set it sentitive if the drive is available and is not recording
		if self.__application.preferences.drive is not None:
			self.__on_write_files.set_sensitive (is_sensitive)

	def __on_remove_file (self, *args):
		self.music_list_widget.remove_selected()
		
	def clear_files (self, *args):
		self.music_list_widget.source.clear()
		
	def __on_add_file (self, *args):
		# Triggered by add button
		if self.__add_file.run () == gtk.RESPONSE_OK:
			files = self.__add_file.get_uris()
			hints_list = []
			for uri in files:
				hints_list.append({'location': uri})
			self.music_list_widget.add_files (hints_list)
		self.__add_file.unselect_all()
		self.__add_file.hide()
	
	def __on_preferences (self, *args):
		# Triggered by preferences menu item
		self.__application.preferences.dialog.run ()
		self.__application.preferences.dialog.hide ()
	
	def __on_about (self, widget, *args):
		# Triggered by the about menu item
		a = gtk.AboutDialog ()
		a.set_name ("Serpentine")
		a.set_version (self.__application.preferences.version)
		a.set_website ("http://s1x.homelinux.net/projects/serpentine")
		a.set_copyright ("2004-2005 Tiago Cogumbreiro")
		a.set_transient_for (self)
		a.run ()
		a.hide()
	
	def __on_write_files (self, *args):
		# TODO: move this to SerpentineApplication.write_files ?
		try:
			# Try to validate music list
			validate_music_list (self.music_list_widget.source, self.__application.preferences)
		except SerpentineCacheError, err:
			show_prefs = False
			
			if err.error_id == SerpentineCacheError.INVALID:
				title = "Cache directory location unavailable"
				show_prefs = True
				
			elif err.error_id == SerpentineCacheError.NO_SPACE:
				title = "Not enough space on cache directory"
			
			gtkutil.dialog_warn (title, err.error_message)
			return

		# TODO: move this to recording module?
		if self.music_list_widget.source.total_duration > self.music_list_widget.disc_size:
			title = "Do you want to overburn your disc?"
			msg = "You are about to record a media disc in overburn mode. " \
			      "This may not work on all drives and shouldn't give you " \
			      "more then a couple of minutes."
			btn = "Overburn Disk"
			self.__application.preferences.overburn = True
		else:
			title = "Do you want to record your musics?"
			msg = "You are about to record a media disc. " \
			      "Canceling a writing operation will make your disc unusable."
			btn = "Record Disk"
			self.__application.preferences.overburn = False
		
		if gtkutil.dialog_ok_cancel (title, msg, self, btn) != gtk.RESPONSE_OK:
			return
		
		self.__application.write_files ()
class Serpentine (gtk.Window):
	def __init__ (self, data_dir):
		gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
		self.preferences = RecordingPreferences (data_dir)
		self.masterer = AudioMastering (self.preferences)
		self.masterer.listeners.append (self)
		g = gtk.glade.XML (os.path.join (self.preferences.data_dir, "serpentine.glade"),
		                   "main_window_container")
		self.add (g.get_widget ("main_window_container"))
		self.set_title ("Serpentine")
		
		# Add a file button
		g.get_widget ("add").connect ("clicked", self.add_file)
		g.get_widget ("add_mni").connect ("activate", self.add_file)
		
		# record button
		g.get_widget("burn").connect ("clicked", self.burn)
		
		# masterer widget
		box = self.get_child()
		self.masterer.show()
		box.add (self.masterer)
		
		# preferences
		g.get_widget ("preferences_mni").connect ('activate', self.__on_preferences)
		
		# setup remove buttons
		self.remove = MapProxy ({'menu': g.get_widget ("remove_mni"),
		                         'button': g.get_widget ("remove")})

		self.remove['menu'].connect ("activate", self.remove_file)
		self.remove['button'].connect ("clicked", self.remove_file)
		self.remove.set_sensitive (False)
		
		# setup record button
		self.burn = g.get_widget ("burn")
		self.burn.set_sensitive (False)
		
		# setup clear buttons
		self.clear = MapProxy ({'menu': g.get_widget ("clear_mni"),
		                        'button': g.get_widget ("clear")})
		self.clear['button'].connect ("clicked", self.clear_files)
		self.clear['menu'].connect ("activate", self.clear_files)
		self.clear.set_sensitive (False)
		
		# setup quit menu item
		g.get_widget ("quit_mni").connect ('activate', self.quit)
		self.connect("destroy", self.quit)
		
		# About dialog
		g.get_widget ("about_mni").connect ('activate', self.__on_about)
		
		self.__last_path = None
		self.__add_file = gtk.FileChooserDialog (buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
		self.__add_file.set_title ('')
		self.__add_file.set_select_multiple (True)
		
		if not self.preferences.drive:
			gtk_util.dialog_warn ("No recording drive found", "No recording drive found on your system, therefore some of Serpentine's functionalities will be disabled.")
			g.get_widget ("preferences_mni").set_sensitive (False)
			self.burn.set_sensitive (False)
			
	def burn (self, *args):
		if not self.preferences.temporary_dir_is_ok():
			gtk_util.dialog_warn ("Temporary directory location unavailable", "Please check if the temporary exists and has writable permissions.")
			self.__on_preferences ()
			return
		if gtk_util.dialog_ok_cancel ("Do you want to continue?", "You are about to record a media disk. Canceling a writing operation will make your disk unusable.", self) != gtk.RESPONSE_OK:
			return
		r = RecordingMedia (self.masterer.source, self.preferences, self)
		r.start()
		
	def quit (self, *args):
		self.preferences.pool.clear()
		gtk.main_quit()
		sys.exit (0)
		
	def on_selection_changed (self, *args):
		self.remove.set_sensitive (self.masterer.count_selected() > 0)
		
	def on_contents_changed (self, *args):
		is_sensitive = len(self.masterer.source) > 0
		self.clear.set_sensitive (is_sensitive)
		self.burn.set_sensitive (is_sensitive)

	def remove_file (self, *args):
		self.masterer.remove_selected()
		
	def clear_files (self, *args):
		self.masterer.source.clear()
		
	def add_file (self, *args):
		if self.__add_file.run () == gtk.RESPONSE_OK:
			files = self.__add_file.get_uris()
			self.masterer.add_files (files)
		self.__add_file.unselect_all()
		self.__add_file.hide()
	
	def __on_preferences (self, *args):
		self.preferences.dialog.run ()
		self.preferences.dialog.hide ()
	
	def __on_about (self, *args):
		a = gnome.ui.About ("Serpentine", self.preferences.version, "Copyright 2004 Tiago Cogumbreiro", "Audio CD Recording", ["Tiago Cogumbreiro <*****@*****.**>"], [], "")
		a.run ()
class Serpentine (gtk.Window):
	def __init__ (self, data_dir):
		gtk.Window.__init__ (self, gtk.WINDOW_TOPLEVEL)
		self.preferences = RecordingPreferences (data_dir)
		self.preferences.dialog.set_transient_for (self)
		self.masterer = AudioMastering (self.preferences)
		self.masterer.listeners.append (self)
		g = gtk.glade.XML (os.path.join (self.preferences.data_dir, "serpentine.glade"),
		                   "main_window_container")
		self.add (g.get_widget ("main_window_container"))
		self.set_title ("Serpentine")
		self.set_default_size (450, 350)
		
		# Add a file button
		g.get_widget ("add").connect ("clicked", self.add_file)
		g.get_widget ("add_mni").connect ("activate", self.add_file)
		
		# record button
		g.get_widget("burn").connect ("clicked", self.burn)
		
		# masterer widget
		box = self.get_child()
		self.masterer.show()
		box.add (self.masterer)
		
		# preferences
		g.get_widget ("preferences_mni").connect ('activate', self.__on_preferences)
		
		# setup remove buttons
		self.remove = MapProxy ({'menu': g.get_widget ("remove_mni"),
		                         'button': g.get_widget ("remove")})

		self.remove['menu'].connect ("activate", self.remove_file)
		self.remove['button'].connect ("clicked", self.remove_file)
		self.remove.set_sensitive (False)
		
		# setup record button
		self.burn = g.get_widget ("burn")
		self.burn.set_sensitive (False)
		
		# setup clear buttons
		self.clear = MapProxy ({'menu': g.get_widget ("clear_mni"),
		                        'button': g.get_widget ("clear")})
		self.clear['button'].connect ("clicked", self.clear_files)
		self.clear['menu'].connect ("activate", self.clear_files)
		self.clear.set_sensitive (False)
		
		# setup quit menu item
		g.get_widget ("quit_mni").connect ('activate', self.quit)
		self.connect("destroy", self.quit)
		
		# About dialog
		g.get_widget ("about_mni").connect ('activate', self.__on_about)
		
		self.__last_path = None
		self.__add_file = gtk.FileChooserDialog (buttons = (gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
		self.__add_file.set_title ('')
		self.__add_file.set_select_multiple (True)
		
		if not self.preferences.drive:
			gtk_util.dialog_warn ("No recording drive found", "No recording drive found on your system, therefore some of Serpentine's functionalities will be disabled.", self)
			g.get_widget ("preferences_mni").set_sensitive (False)
			self.burn.set_sensitive (False)
	
	def __hig_bytes (self, bytes):
		hig_desc = [("GByte", "GBytes"),
		            ("MByte", "MBytes"),
		            ("KByte", "KByte" ),
		            ("byte" , "bytes" )]
		value, strings = self.__decompose_bytes (bytes, 30, hig_desc)
		return "%.1f %s" % (value, self.__plural (value, strings))
	
	def __decompose_bytes (self, bytes, offset, hig_desc):
		if bytes == 0:
			return (0.0, hig_desc[-1:])
		if offset == 0:
			return (float (bytes), hig_desc[-1:])
			
		part = bytes >> offset
		if part > 0:
			sub_part = part ^ ((part >> offset) << offset)
			return ((part * 1024 + sub_part) / 1024.0, hig_desc[0])
		else:
			del hig_desc[0]
			return self.__decompose_bytes (bytes, offset - 10, hig_desc)

	def __plural (self, value, strings):
		if value == 1:
			return strings[0]
		else:
			return strings[1]
		
	def burn (self, *args):
		# Check if we have space available in our cache dir
		secs = 0
		for music in self.masterer.source:
			if not self.preferences.pool.is_available (music['uri']):
				secs += music['duration']
		# 44100hz * 16bit * 2channels / 8bits = 176400 bytes per sec
		size_needed = secs * 176400L
		s = os.statvfs (self.preferences.temporary_dir)
		size_avail = s[statvfs.F_BAVAIL] * long(s[statvfs.F_BSIZE])
		if (size_avail - size_needed) < 0:
			
			gtk_util.dialog_error ("Not enough space on cache directory",
				"Remove some music tracks or make sure your cache location location has enough free space (about %s)." % (self.__hig_bytes(size_needed - size_avail)), self)
			return
	
		if not self.preferences.temporary_dir_is_ok():
			gtk_util.dialog_warn ("Cache directory location unavailable", "Please check if the cache location exists and has writable permissions.", self)
			self.__on_preferences ()
			return
		if gtk_util.dialog_ok_cancel ("Do you want to continue?", "You are about to record a media disk. Canceling a writing operation will make your disk unusable.", self) != gtk.RESPONSE_OK:
			return
		r = RecordingMedia (self.masterer.source, self.preferences, self)
		r.start()
		
	def quit (self, *args):
		self.preferences.pool.clear()
		gtk.main_quit()
		sys.exit (0)
		
	def on_selection_changed (self, *args):
		self.remove.set_sensitive (self.masterer.count_selected() > 0)
		
	def on_contents_changed (self, *args):
		is_sensitive = len(self.masterer.source) > 0
		self.clear.set_sensitive (is_sensitive)
		self.burn.set_sensitive (is_sensitive)

	def remove_file (self, *args):
		self.masterer.remove_selected()
		
	def clear_files (self, *args):
		self.masterer.source.clear()
		
	def add_file (self, *args):
		if self.__add_file.run () == gtk.RESPONSE_OK:
			files = self.__add_file.get_uris()
			self.masterer.add_files (files)
		self.__add_file.unselect_all()
		self.__add_file.hide()
	
	def __on_preferences (self, *args):
		self.preferences.dialog.run ()
		self.preferences.dialog.hide ()
	
	def __on_about (self, widget, *args):
		a = gnome.ui.About ("Serpentine", self.preferences.version, "Copyright 2004 Tiago Cogumbreiro", "Audio CD Recording", ["Tiago Cogumbreiro <*****@*****.**>"])
		a.set_transient_for (self)
		a.run ()
		a.destroy ()