[Syncropated-commits] r161 - trunk/src

zimmerle at garage.maemo.org zimmerle at garage.maemo.org
Tue Jan 30 01:44:05 EET 2007


Author: zimmerle
Date: 2007-01-30 01:44:04 +0200 (Tue, 30 Jan 2007)
New Revision: 161

Added:
   trunk/src/LocalMusicManager.py
Log:
LocalMusicManager first import

Added: trunk/src/LocalMusicManager.py
===================================================================
--- trunk/src/LocalMusicManager.py	2007-01-29 22:21:49 UTC (rev 160)
+++ trunk/src/LocalMusicManager.py	2007-01-29 23:44:04 UTC (rev 161)
@@ -0,0 +1,752 @@
+# LocalMusicManager
+#
+#  Copyright (c) 2006 INdT (Instituto Nokia de Technologia)
+#
+#  Author: Felipe Zimmerle <felipe at zimmerle.org>
+#          Kenneth Rohde Christiansen <kenneth.christiansen at gmail.com>
+#
+#  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 (at your option) 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.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+#  USA
+
+import gobject
+import gtk
+import eyeD3
+import mutagen
+import mutagen.easyid3
+import os
+import sys
+import re
+from Logger import Logger
+from Configuration import Configuration
+from Icons import Icons
+from Utils import Utils
+from ElementMusic import ElementMusic
+
+log = Logger()
+config = Configuration()
+icons = Icons()
+utils = Utils()
+
+class LocalMusicManager(gobject.GObject):
+	"""
+	Manager the music files, reading the id3 tags, indexing it and also displaying in a correct
+	format according to filters criteria
+
+	"""
+
+	__gsignals__ = {
+			'media_content_change'  : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+				(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING,
+				gobject.TYPE_PYOBJECT))
+	}
+
+	(
+		ICON,
+		TITLE,
+		TRACKNUMBER,
+		ARTIST,
+		ALBUM,
+		DATE,
+		BITRATE,
+		PLAY_TIME,
+		GENRE,
+		PLAYCOUNT,
+		SIZE,
+		PATH,
+		FILE_PATH,
+		OBJECT
+	) = range(14)
+
+	# Configuration key, label, value function, editable, indexable, #
+	OPTIONAL_TREEVIEW_COLUMNS = [('audio_view_track_number_check', 'Track Number',
+					'tracknumber', True, False, TRACKNUMBER), 
+				     ('audio_view_artist_check', 'Artist', 
+				     	'artist', True, True, ARTIST),
+				     ('audio_view_album_check', 'Album', 
+				     	'album', True, True, ALBUM), 
+				     ('audio_view_year_check', 'Year', 
+				     	'date', True, False, DATE),
+				     ('audio_view_quality_check', 'Quality', 
+				     	'getBitRateString', False, False, BITRATE),
+				     ('audio_view_time_check', 'Time', 
+				     	'getPlayTimeString', False, False, PLAY_TIME), 
+				     ('audio_view_genre_check', 'Genre', 
+				     	'genre', True, True, GENRE),
+				     ('audio_view_play_count_check', 'Play Counter', 
+				     	'getPlayCount', True, False, PLAYCOUNT),
+				     ('audio_view_file_size_check', 'File Size', 
+				     	'getSize', False, False, SIZE),
+				     ('audio_view_file_path_check', 'File Path', 
+				     	'getPath', False, True, FILE_PATH)]
+
+
+
+
+	TYPE = 'audio'
+
+	__model = None
+	__tree_view = None
+	__root_node = None
+	__total_loaded_files = 0
+	__filter_key = None
+
+	__search_box_entry = None
+	__search_box_timeout = 0
+	
+
+
+	def __init__(self):
+		"""
+		Constructor
+
+		"""
+		gobject.GObject.__init__(self)
+		log.info('LocalMusicManager :: Starting ...')
+		config.connect('configuration_changed', self.cb_configuration_changed)
+
+		self.__filter_count_elements = {}
+		self.__element_count_timeout = 0
+
+		self.__content_filter_artist = {}
+		self.__content_filter_album = {}
+		self.__content_filter_genre = {}
+
+		self.filter_nodes = {}
+		self.master_filter_key = None
+
+		self.master_filter_column = LocalMusicManager.ARTIST
+
+
+	def cb_configuration_changed(self, emitent, key, value):
+		"""
+		Callback called when a configuration changed, 
+		used show the correct columns at the music treeview.
+
+		"""
+		log.debug('LocalMusicManager :: Configuration changed: ' + str(key))
+		for i in self.OPTIONAL_TREEVIEW_COLUMNS:
+			if i[0] == key:
+				self.__reconfigure_columns(key, value)
+				return
+
+
+	def __reconfigure_columns(self, key, value):
+		"""
+		Used to reconfigure the music tree view.
+
+		"""
+		log.debug('LocalMusicManager :: I Got the configuration key: ' +
+			key + ' in the TreeView.')
+		title = None
+		for i in self.OPTIONAL_TREEVIEW_COLUMNS:
+			if i[0] == key:
+				title = i[1]
+
+		if title == None:
+			return
+
+		for i in self.__tree_view.get_columns():
+			if i.get_widget() == None:
+				continue
+
+			log.debug('LocalMusicManager :: Looping inside the tree views' +
+				' columns ... key: ' + key + ' tree v.: ' + 
+				str(i.get_widget().get_text()) + ' - value: ' + value +
+				'.')
+
+			if str(i.get_widget().get_text()) == title:
+				if value == str(True):
+					i.set_visible(True)
+				else:
+					i.set_visible(False)
+
+
+	def content_verify_func(self, file):
+		"""
+		Function called to check if the file is know and supported.
+
+		"""
+		if not os.path.isfile(file):
+			return False
+		# Faster but not that right way:
+		return eyeD3.isMp3File(file)
+
+
+	def __get_header_menu(self):
+		"""
+		Return the menu used when the user clicked with the 3rd button in the 
+		item treeview header.
+
+		"""
+		menu = gtk.Menu()
+
+		for i in self.OPTIONAL_TREEVIEW_COLUMNS:
+			new_bnt = gtk.CheckMenuItem(i[1])
+			new_bnt.set_active(config.getValue(i[0]) == str(True))
+			new_bnt.connect("toggled", self.__header_menu_set_value, i[0])
+			menu.append(new_bnt)
+
+		return menu
+
+
+	def __header_menu_set_value(self, bnt, prop):
+		"""
+		Called when the user enable or disable the visualisation
+		of a column, at the header 3rd button menu.
+
+		"""
+		config.setValue(prop, str(bnt.get_active()))
+			
+
+	def __get_menu(self, path):
+		"""
+		Return the 3rd button menu, when the focous is an item.
+
+		"""
+		iter = self.__model.get_iter(path)
+		value = self.__model.get_value(iter, LocalMusicManager.FILE_PATH)
+
+		menu = gtk.Menu()
+
+		if value != '':
+			play_in_amarok_menu_item = gtk.ImageMenuItem("Play in amarok")
+			play_in_amarok_menu_item.connect("activate",
+				self.cb_tree_view_play_in_amarok_button_pressed, iter)
+			play_in_amarok_menu_item.set_image(
+				icons.load_icon_as_image('media-playback-start', 12))
+			menu.append(play_in_amarok_menu_item)
+
+
+		if log.getLevel() == 'debug':
+			debug_menu_item = gtk.ImageMenuItem("Debug")
+			debug_menu_item.connect("activate",
+				self.cb_tree_view_debug_button_pressed, iter)
+			debug_menu_item.set_image(
+				icons.load_icon_as_image('gtk-info', 12))
+			menu.append(debug_menu_item)
+
+
+		return menu
+
+
+	def cb_tree_view_play_in_amarok_button_pressed(self, menu_item, iter):
+		"""
+		To play a song at amarok
+
+		"""
+		log.info('Playing ' + str(iter) + ' in amarok...')
+		value = self.__model.get_value(iter, LocalMusicManager.FILE_PATH)
+		os.system('dcop amarok playlist playMedia "' + value + '"')
+
+
+	def cb_tree_view_debug_button_pressed(self, menu_item, iter):
+		"""
+		Just print the iter values at the console (when in debug mode)
+
+		"""
+		log.debug('LocalMusicManager :: Debug button pressed. (iter: ' +
+			str(iter) + '.')
+		value = self.__model.get_value(iter, LocalMusicManager.FILE_PATH)
+		log.debug('LocalMusicManager :: Item: ' + str(value))
+
+
+	def cb_tree_view_button_pressed(self, tree_view, event):
+		"""
+		Capture the button pressed event and decide which function
+		will handle it.
+
+		"""
+		log.debug('LocalMusicManager :: Tree view clicked: ' + str(tree_view))
+		if event.button == 3:
+			x = int(event.x)
+			y = int(event.y)
+			time = event.time
+			pthinfo = tree_view.get_path_at_pos(x, y)
+			if pthinfo != None:
+				path, col, cellx, celly = pthinfo
+				tree_view.grab_focus()
+				tree_view.set_cursor(path, col, 0)
+
+				menu = self.__get_menu(path)
+				menu.show_all()
+
+				menu.popup( None, None, None, event.button, time)
+				return True
+
+
+	def cb_tree_view_header_button_pressed(self, tree_view, event):
+		"""
+		Capture the button pressed event and decide which function
+		will handle it.
+
+		"""
+		log.debug('LocalMusicManager :: Tree view header clicked: ' +
+			str(tree_view))
+		if event.button == 3:
+			x = int(event.x)
+			y = int(event.y)
+			time = event.time
+			menu = self.__get_header_menu()
+			menu.show_all()
+			menu.popup( None, None, None, event.button, time)
+			return True
+
+
+	def cb_visible_items(self, model, iter):
+		"""
+		Decide which item is visible or not. Called by gtkTreeView
+
+		"""
+		key = self.__filter_key
+		master_key = self.master_filter_key
+		value = model.get_value(iter, LocalMusicManager.TITLE)
+		value_master_filter = model.get_value(iter,
+				int(self.master_filter_column))
+
+
+		if value == None:
+			return True
+
+		log.debug('LocalMusicManager :: master value : ' + str(value_master_filter))
+
+		if (value_master_filter != None and master_key != None) and value_master_filter != master_key:
+			return False
+			
+
+
+		if (key == None or value.find(key) != -1):
+			log.debug('LocalMusicManager :: visible_items :: Count: ' + 
+				str(len(self.__filter_count_elements)))
+
+			play_time = model.get_value(iter, LocalMusicManager.PLAY_TIME)
+			if play_time == None:
+				play_time = "0:00"
+	
+			file_size = model.get_value(iter, LocalMusicManager.SIZE)
+			self.__filter_count_elements[value] = (int(play_time.split(':')[0])*60 +
+						int(play_time.split(':')[1]), file_size)
+
+			if self.__element_count_timeout != 0: 
+			 	gobject.source_remove (self.__element_count_timeout) 
+				self.__element_count_timeout = 0 
+
+			self.__element_count_timeout = gobject.timeout_add(300, self.update_ui)
+
+			return True
+
+		return False
+
+
+	def update_ui(self):
+		"""
+		Update the status widgets.
+		"""
+		
+		total_time = 0
+		total_size= 0
+		while gtk.events_pending():
+			gtk.main_iteration(False)
+
+		for i in self.__filter_count_elements.values():
+
+			log.debug('LocalMusicManager :: visible_items ::         Total size: ' +
+					str(i[1]) )
+
+			total_time += int(i[0])
+
+			try:
+				total_size += int(i[1])
+			except:
+				pass
+
+			log.debug('LocalMusicManager :: visible_items ::         Total time: ' +
+					str(total_time))
+			log.debug('LocalMusicManager :: visible_items ::         Total size: ' +
+					str(total_size))
+
+		self.__stats_size.set_label('Total of ' + 
+				str(len(self.__filter_count_elements.values())) + 
+				' files, ' + str(utils.get_friendly_size(int(total_size))))
+
+		self.__stats_play_time.set_label(str(utils.get_friendly_time(total_time)) + ' ')
+
+
+	def cb_text_changed(self, text):
+		"""
+		Callback called when the user change the text in search_box_entry. If
+		the user don't type anything more in 500 ms, call the model_refilter()
+
+		"""
+		text = text.get_text()
+	        self.__filter_key = text
+
+		if self.__search_box_timeout != 0:
+			gobject.source_remove (self.__search_box_timeout)
+			self.__search_box_timeout = 0
+
+		self.__filter_count_elements = {}
+		self.__model_filter.clear_cache()
+		self.__search_box_timeout = gobject.timeout_add(500, self.__model_filter.refilter)
+		self.update_ui()
+
+		return False
+
+	
+	def attach_stats_bars(self, stats_size, stats_play_time):
+		"""
+		Attach the stats bars widgets
+
+		"""
+		self.__stats_size = stats_size
+		self.__stats_play_time = stats_play_time
+
+
+	def attach_search_box_entry(self, search_box_entry):
+		"""
+		Attach a text entry to be the new search_box
+
+		"""
+		self.__search_box_entry = search_box_entry
+		self.__search_box_entry.connect('changed', self.cb_text_changed)
+		return True
+
+
+	def get_attached_tree_view(self):
+		"""
+		Return the attached tree view
+		
+		"""
+		return self.__tree_view
+
+
+	def cb_filter_tree_view_button_pressed(self, tree_view, event):
+		"""
+		Capture the button pressed event and decide which function
+		will handle it.
+
+		"""
+		log.debug('LocalMusicManager :: Tree view clicked: ' + str(tree_view))
+		if event.button == 1:
+			x = int(event.x)
+			y = int(event.y)
+			time = event.time
+			pthinfo = tree_view.get_path_at_pos(x, y)
+			if pthinfo != None:
+				path, col, cellx, celly = pthinfo
+				tree_view.grab_focus()
+				tree_view.set_cursor(path, col, 0)
+
+				if len(path) < 3:
+					self.master_filter_key = None
+					self.__model_filter.refilter()
+					return
+
+				self.__filter_count_elements = {}
+				self.__model_filter.clear_cache()
+				self.update_ui()
+
+				iter = self.__model_tree_view_filter.get_iter(path)
+				value = self.__model_tree_view_filter.get_value(iter,
+								LocalMusicManager.TITLE)
+
+				if value == 'Unknow':
+					value = ''
+
+				parent = self.__model_tree_view_filter.get_iter(path[:2])
+				self.master_filter_column = self.__model_tree_view_filter.get_value(
+								parent, 2)
+
+				self.master_filter_key = value
+				log.debug('LocalMusicManager :: Master filter key, changed to: ' + 
+						str(self.master_filter_key)  + ' Path: ' + 
+						str(path) + ' - Parent: ' + str(parent) + 
+						' Column to search: ' + str(self.master_filter_column))
+
+				self.__model_filter.refilter()
+
+
+
+	def attach_filter_tree_view(self, filter_tree_view):
+		"""
+		Attach the filter tree view. It configure the filter_tree_view 
+		with the correct options. 
+
+		"""
+		log.debug('LocalMusicManager :: Attaching a filter TreeView...')
+		self.__model_tree_view_filter = gtk.TreeStore(gtk.gdk.Pixbuf,
+							gobject.TYPE_STRING,
+							gobject.TYPE_STRING)
+
+		# Icon
+		col = gtk.TreeViewColumn()
+		renderer = gtk.CellRendererPixbuf()
+		col.pack_start(renderer, expand=False)
+		col.set_attributes(renderer, pixbuf=0)
+		col.set_alignment(0)
+		filter_tree_view.append_column(col)
+
+		# Filter Name
+		renderer = gtk.CellRendererText()
+		renderer.set_property('editable', False);
+		col.pack_start(renderer, expand=True)
+		col.set_attributes(renderer, text=1)
+		col.set_resizable(0)
+		col.set_sort_column_id(1)
+		filter_tree_view.append_column(col)
+
+		filter_tree_view.set_model(self.__model_tree_view_filter)
+
+		filter_tree_view.connect('button_press_event', self.cb_filter_tree_view_button_pressed)
+
+		tags = []
+		tags.append(icons.load_icon('sound_folder_icon', 64))
+		tags.append('Filters')
+		tags.append(1)
+		x = self.__model_tree_view_filter.append(None, tags)
+
+		for i in LocalMusicManager.OPTIONAL_TREEVIEW_COLUMNS:	
+			if i[4] == True:
+				tags = []
+				tags.append(icons.load_icon('sound_folder_icon', 64))
+				tags.append(i[1])
+				tags.append(i[5])
+				self.filter_nodes[i[1]] = self.__model_tree_view_filter.append(x, tags)
+		filter_tree_view.expand_all()
+
+
+	def attach_tree_view(self, tree_view):
+		"""
+		Attach the tree view. It configure the tree_view 
+		with the correct options. 
+
+		"""
+		log.debug('LocalMusicManager :: Attaching TreeView...')
+
+
+		tree_view.set_enable_search(True)
+
+		tree_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
+
+		tree_view.get_parent().set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+
+		self.__model = gtk.TreeStore(gtk.gdk.Pixbuf, gobject.TYPE_STRING, gobject.TYPE_STRING,
+							gobject.TYPE_STRING, gobject.TYPE_STRING,
+							gobject.TYPE_STRING, gobject.TYPE_STRING, 
+							gobject.TYPE_STRING, gobject.TYPE_STRING, 
+							gobject.TYPE_STRING, gobject.TYPE_STRING,
+							gobject.TYPE_STRING, gobject.TYPE_STRING,
+							gobject.TYPE_OBJECT)
+
+		self.__model_filter = self.__model.filter_new()
+		self.__model_filter.set_visible_func(self.cb_visible_items)
+		
+		self.__model_sort = gtk.TreeModelSort(self.__model_filter)
+		
+
+		tree_view.set_model(self.__model_sort)
+		tree_view.connect('button_press_event', self.cb_tree_view_button_pressed)
+		tree_view.set_rules_hint(1)
+
+		# Icon
+		col = gtk.TreeViewColumn()
+		renderer = gtk.CellRendererPixbuf()
+		col.pack_start(renderer, False)
+		col.set_attributes(renderer, pixbuf=0)
+		col.set_alignment(0)
+		tree_view.append_column(col)
+
+		# Text (Title)
+		renderer = gtk.CellRendererText()
+		renderer.set_property('editable', True);
+		col.pack_start(renderer, False)
+		col.set_attributes(renderer, text=1)
+		col.set_resizable(1)
+		renderer.connect('edited', self.__edit_done, 'title')
+
+		header_label = gtk.Label('Title')
+		col.set_widget(header_label)
+		header_label.show()
+
+		# The TreeView header the 3rd parent of the header label is the a button.
+		button_widget = col.get_widget().get_parent().get_parent().get_parent()
+		button_widget.connect('button_press_event', self.cb_tree_view_header_button_pressed)
+
+		col.set_sort_column_id(1)
+		tree_view.append_column(col)
+
+		j = 2
+		for i in self.OPTIONAL_TREEVIEW_COLUMNS:
+			col = gtk.TreeViewColumn()
+			renderer = gtk.CellRendererText()
+			renderer.set_property('xalign', 1)
+			renderer.set_property('editable', i[3]);
+			renderer.connect('edited', self.__edit_done, i[2])
+
+			header_label = gtk.Label(i[1])
+			col.set_widget(header_label)
+			header_label.show()
+
+			col.pack_start(renderer, True)
+			col.set_attributes(renderer, markup=j)
+			col.set_sort_column_id(j)
+			col.set_alignment(0.5)
+			col.set_resizable(1)
+
+			if config.getValue(i[0]) != str(True):
+				col.set_visible(False)
+	
+			tree_view.append_column(col)
+			j+=1
+			
+			# The TreeView header the 3rd parent of the header label is the a button.
+			button_widget = col.get_widget().get_parent().get_parent().get_parent()
+			button_widget.connect('button_press_event', 
+					self.cb_tree_view_header_button_pressed)
+
+
+
+		tree_view.set_headers_clickable(True)
+		self.__tree_view = tree_view
+		#self.__root_node = self.__model.append(None, [icons.load_icon('folder-open')
+		#, 'Syncropated\'s directories watch', '', '', '', '', '', '', '', ''])
+
+
+
+	def del_registrys(self, uri, obj):
+		"""
+		Used to del registrys from the tree view.
+
+		"""
+		mp3 = self.__model.get_value(obj, LocalMusicManager.OBJECT)
+		log.debug('LocalMusicManager :: content changed :: ' + 
+		 	  'Removing items in: ' + uri + ' obj: ' + str(obj) +
+			  ' mp3: ' + str(mp3))
+
+		for i in self.OPTIONAL_TREEVIEW_COLUMNS:
+			if i[4] == True:
+				self.recalculate_ref_count(self.filter_nodes[i[1]],
+					mp3.get_property(i[2]), True)
+
+		self.__model.remove(obj)
+
+
+	def add_registry(self, uri, gtks):
+		"""
+		Used to add registrys from the tree view.
+
+		"""
+		log.debug('LocalMusicManager :: content changed :: Adding item: ' + uri)
+		if gtks != None:
+			file = gtks[0]
+			files_loaded_label = gtks[1]
+			progress_bar = gtks[2]
+			lenght = gtks[3]
+
+			file_label = os.path.split(uri)[1]
+			file_label = utils.unicodify(file_label)
+			file_label = utils.resize_string(file_label, 40)
+			file.set_text(file_label)
+
+			files_loaded_label.set_text(str(self.__total_loaded_files) +
+					' files loaded.')
+
+			log.debug('LocalMusicManager :: progress_bar ' + 
+				': Total of loaded files: ' + str(self.__total_loaded_files))
+			log.debug('LocalMusicManager :: progress_bar ' + 
+				': Total of files: ' + str(lenght))
+
+			#progress_bar.set_fraction(float(float(self.__total_loaded_files)
+			#/float(lenght-2)))
+
+	 	if not os.path.isfile(uri):
+			return;
+
+		music = ElementMusic(uri)
+		tags = []
+		tags.append(icons.load_icon('sound_icon', 64))
+		tags.append(music.get_property('title'))
+
+		for i in self.OPTIONAL_TREEVIEW_COLUMNS:
+			tags.append(music.get_property(i[2]))
+			if i[4] == True:
+				self.recalculate_ref_count(self.filter_nodes[i[1]],
+					music.get_property(i[2]))
+
+		tags.append(music.get_property('filename'))
+		tags.append(music)
+
+		self.__total_loaded_files += 1
+
+		return self.__model.append(None, tags)
+
+
+	def recalculate_ref_count(self, node, property, reduce=False):
+		"""
+		Recalculate the references counts of the id3s tags. If no
+		reference exist for a specific tag, it should be removed from
+		the filter tree view.
+
+		"""
+		log.debug('LocalMusicManager :: recalculate ref counts ...')
+		i = 0
+		if property == '':
+			property = 'Unknow'
+		while i < self.__model_tree_view_filter.iter_n_children(node):
+			log.debug('LocalMusicManager :: recalculate ref counts :: ' +
+				str(i))
+			iter = self.__model_tree_view_filter.iter_nth_child(node, i)
+			if property == self.__model_tree_view_filter.get_value(iter, 
+						LocalMusicManager.TITLE):
+				num = int(self.__model_tree_view_filter.get_value(iter,
+						2))
+				if reduce:
+					num -= 1
+					log.debug('LocalMusicManager :: recalculate ref ' + 
+						'counts :: reducing ref count of: ' + 
+						str(self.__model_tree_view_filter.get_value(iter, 
+							LocalMusicManager.TITLE)) + ' to: ' +
+							str(num) )
+				else:
+					num += 1
+
+				if num <= 0:
+					log.debug('LocalMusicManager :: recalculate ref ' + 
+						'counts :: removing iter....')
+					self.__model_tree_view_filter.remove(iter)
+					log.debug('LocalMusicManager :: removed!') 
+				else:
+					# Set the new refcount value
+					self.__model_tree_view_filter.set_value(iter, 2, num)
+
+				return True
+
+			i += 1
+		self.__model_tree_view_filter.append(node, (None, property, 1))
+
+		return
+
+
+	def __edit_done(self, cel, path, new_text, col):
+		"""
+		Callback called on the user finish to edit an mp3 information.
+
+		"""
+		iter = self.__model_sort.get_iter_from_string(path)
+		music = self.__model_sort.get_value(iter, 13)
+		music.set_property(col, new_text)
+
+
+
+



More information about the Syncropated-commits mailing list