Index: ProjectTemplate.py =================================================================== --- ProjectTemplate.py (revision 0) +++ ProjectTemplate.py (revision 0) @@ -0,0 +1,139 @@ +# +# THIS FILE IS PART OF THE JOKOSHER PROJECT AND LICENSED UNDER THE GPL. SEE +# THE 'COPYING' FILE FOR DETAILS +# +# ProjectTemplate.py +# +# This module handles the saving and loading of project templates. +# +# +#------------------------------------------------------------------------------- + +import os +import Globals +import xml.dom.minidom as xml +from Utils import LoadListFromXML, StoreListToXML +from Monitored import Monitored +import gettext +_ = gettext.gettext + +#========================================================================= + +class ProjectTemplate(Monitored): + """ + This class saves and loads templates and template information to disk. + """ + + #_____________________________________________________________________ + + def __init__(self): + """ + Creates a new instance of ProjectTemplate. + """ + Monitored.__init__(self) + + #_____________________________________________________________________ + + def SaveTemplateFileToDisk(self, name, instrlist): + """ + This method will write a template file to ~/.jokosher/templates/. + instrlist is a list containing a list of instruments, e.g. [["audiofile", "Audio File", "path_to_image"], + ["acousticguitar", "Acoustic Guitar", "path_to_image"]]. + Note that we do not save the pixbuf image to xml, we insert it at the beginning of the + instrument list instead so that our template instrument model can have nice shiny icons. + + Parameters: + name -- the name of the template file. + instrlist -- a list containing a list of instruments + """ + doc = xml.Document() + head = doc.createElement("template") + doc.appendChild(head) + + for items in instrlist: + # create instrument tags for every instrument in the list + instrtag = doc.createElement("instrument") + head.appendChild(instrtag) + StoreListToXML(doc, instrtag, items, "item") + + if not name.endswith(".template"): + name += ".template" + + filename = open(Globals.TEMPLATES_PATH + name, "w") + filename.write(doc.toprettyxml()) + filename.close() + self.StateChanged("template-update") + + #_____________________________________________________________________ + + def DeleteTemplateFileFromDisk(self, name): + """ + This method will delete a template file. + + Parameters: + name -- the name of the template file which will be deleted. + """ + if not name.endswith(".template"): + name += ".template" + + try: + os.remove(Globals.TEMPLATES_PATH + name) + except OSError, e: + raise _("Cannot remove template %s") % path, e + + self.StateChanged("template-update") + + #_____________________________________________________________________ + + def __LoadInstrumentsFromTemplateFile(self, name): + """ + This method will return a list containing a list of instruments which are in the given template filename. + + Parameters: + name -- the name of the template. + + Returns: + instrlist -- a list containing a list of instruments, e.g. [["audiofile", "Audio File", "path_to_image"]]. + """ + if not name.endswith(".template"): + name += ".template" + + if os.path.exists(Globals.TEMPLATES_PATH + name): + filename = open(Globals.TEMPLATES_PATH + name, "r") + doc = xml.parse(filename) + instrlist = [] + for item in doc.getElementsByTagName("instrument"): + instrlist.append(LoadListFromXML(item)) + return instrlist + else: + raise OSError, _("The template %s does not exist") % Globals.TEMPLATES_PATH + name + + #_____________________________________________________________________ + + def __GetTemplateList(self): + """ + This method will return a list of template files in the template directory (~/.jokosher/templates/") + + Returns: + filenames -- a list containing the names of file in the template directory. + """ + templist = [] + for files in os.listdir(Globals.TEMPLATES_PATH): + filenames = files.split(".")[0] + templist.append(filenames) + return templist + + #_____________________________________________________________________ + + def LoadTemplateDictionaryInstruments(self): + """ + This method will return a dictionary containing the the template file name (key) and their + associated instruments (value). + e.g. {"Rock" : [["electricguitar", "Electric Guitar", "path_to_image"]]} + """ + instrdict = {} + for i in self.__GetTemplateList(): + instrdict[i] = self.__LoadInstrumentsFromTemplateFile(i) + return instrdict + +#========================================================================= Index: ProjectTemplateDialog.py =================================================================== --- ProjectTemplateDialog.py (revision 0) +++ ProjectTemplateDialog.py (revision 0) @@ -0,0 +1,243 @@ +# +# THIS FILE IS PART OF THE JOKOSHER PROJECT AND LICENSED UNDER THE GPL. SEE +# THE 'COPYING' FILE FOR DETAILS +# +# ProjectTemplateDialog.py +# +# This module is used to present a dialog which allows the user to create, modify and +# remove project templates. +# +#------------------------------------------------------------------------------- + +import gtk +import gtk.glade +import Globals +import ProjectTemplate +import gettext +_ = gettext.gettext + +#========================================================================= + +class ProjectTemplateDialog: + """ + This class allows the user to modify, save and delete project templates. + """ + + #_____________________________________________________________________ + + def __init__(self, newprojectdlg, template): + """ + Creates a new instance of ProjectTemplateDialog. + + Parameters: + newprojectdlg -- reference to NewProjectDialog. + template -- reference to ProjectTemplate. + """ + self.newprojectdlg = newprojectdlg + self.template = template + + self.wTree = gtk.glade.XML(Globals.GLADE_PATH, "ProjectTemplateDialog") + + + signals = { + "on_save_button_clicked" : self.OnSaveButtonClicked, + "on_delete_button_clicked" : self.OnDeleteButtonClicked, + "on_add_button_clicked" : self.OnAddButtonClicked, + "on_remove_button_clicked": self.OnRemoveButtonClicked, + "on_close_button_clicked" : self.OnCloseButtonClicked, + } + self.wTree.signal_autoconnect(signals) + + self.window = self.wTree.get_widget("ProjectTemplateDialog") + self.window.set_transient_for(self.newprojectdlg.dlg) + self.window.set_icon(self.newprojectdlg.dlg.get_icon()) + + self.tempcombo = self.wTree.get_widget("template_comboentry") + self.tempcombo.child.set_activates_default(True) + + self.instrtree = self.wTree.get_widget("instrument_treeview") + self.addinstrbtn = self.wTree.get_widget("add_button") + self.removetempinstr = self.wTree.get_widget("remove_button") + self.temptree = self.wTree.get_widget("template_treeview") + + self.savebtn = self.wTree.get_widget("save_button") + self.savebtn.set_flags(gtk.CAN_DEFAULT) + self.savebtn.grab_default() + self.deletebtn = self.wTree.get_widget("delete_button") + + self.temptreemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str) + self.instrtreemodel = gtk.ListStore(gtk.gdk.Pixbuf, str, str, str) + + # the template combo entry uses the same model as the template combo in new project dialog + self.tempcombo.set_model(self.newprojectdlg.templatemodel) + self.instrtree.set_model(self.instrtreemodel) + self.temptree.set_model(self.temptreemodel) + + self.tempcombo.clear() + textrend = gtk.CellRendererText() + self.tempcombo.pack_start(textrend) + self.tempcombo.add_attribute(textrend, "text", 0) + + self.instrtree.get_selection().set_mode(gtk.SELECTION_MULTIPLE) + self.instrtree.append_column(gtk.TreeViewColumn(_("Type"), gtk.CellRendererPixbuf(), pixbuf=0)) + self.instrtree.append_column(gtk.TreeViewColumn(_("Instrument Name"), gtk.CellRendererText(), text=2)) + + self.temptree.get_selection().set_mode(gtk.SELECTION_MULTIPLE) + self.temptree.append_column(gtk.TreeViewColumn(_("Type"), gtk.CellRendererPixbuf(), pixbuf=0)) + self.temptree.append_column(gtk.TreeViewColumn(_("Instrument Name"), gtk.CellRendererText(), text=2)) + + self.tempcombo.connect("changed", self.OnTemplateEntryChanged) + + self.PopulateJokosherInstrumentsModel() + self.tempcombo.set_active(self.newprojectdlg.templatecombo.get_active()) + self.template.AddListener(self) + self.window.show_all() + + #_____________________________________________________________________ + + def OnTemplateEntryChanged(self, widget): + """ + Called when the template combo entry contents change. + + Parameters: + widget -- reserved for GTK callbacks. Don't use explicitly. + """ + if self.tempcombo.child.get_text(): + self.savebtn.set_sensitive(True) + self.deletebtn.set_sensitive(True) + self.Update() + else: + self.savebtn.set_sensitive(False) + self.deletebtn.set_sensitive(False) + + #_____________________________________________________________________ + + def Update(self): + """ + Called when the template combo entry contents change. + Loads instruments from a template file and appends them to the template instruments model. + """ + for i in self.template.LoadTemplateDictionaryInstruments().keys(): + if i == self.tempcombo.child.get_text(): + self.temptreemodel.clear() + for instr in self.template.LoadTemplateDictionaryInstruments()[i]: + items = (gtk.gdk.pixbuf_new_from_file(instr[2]).scale_simple(22, 22, gtk.gdk.INTERP_BILINEAR), instr[0], instr[1], instr[2]) + self.temptreemodel.append(items) + + #_____________________________________________________________________ + + def OnAddButtonClicked(self, widget): + """ + Called when the add button is clicked. + Adds selected instruments to the template instruments model. + + Parameters: + widget -- reserved for GTK callbacks. Don't use explicitly. + """ + selection = self.instrtree.get_selection() + if selection: + model, selected = selection.get_selected_rows() + iters = [model.get_iter(path) for path in selected] + for iter in iters: + self.temptreemodel.append((model[iter][0], model[iter][1], model[iter][2], model[iter][3])) + else: + return + + #_____________________________________________________________________ + + def OnRemoveButtonClicked(self, widget): + """ + Called when the remove button is clicked. + Removes any selected instruments from the template instruments model. + + Parameters: + widget -- reserved for GTK callbacks. Don't use explicitly. + """ + selection = self.temptree.get_selection() + if selection: + model, selected = selection.get_selected_rows() + iters = [model.get_iter(path) for path in selected] + for iter in iters: + model.remove(iter) + else: + return + + #_____________________________________________________________________ + + def OnSaveButtonClicked(self, widget): + """ + Called when the save button is clicked. + Saves added template instruments to a template file. + + Parameters: + widget -- reserved for GTK callbacks. Don't use explicitly. + """ + instruments = [] + for row in self.temptreemodel: + instruments.append([row[1], row[2], row[3]]) + self.template.SaveTemplateFileToDisk(self.tempcombo.child.get_text(), instruments) + + #_____________________________________________________________________ + + def OnDeleteButtonClicked(self, widget): + """ + Called when the delete button is clicked. + Removes the selected template file from disk. + + Parameters: + widget -- reserved for GTK callbacks. Don't use explicitly. + """ + self.template.DeleteTemplateFileFromDisk(self.tempcombo.child.get_text()) + + #_____________________________________________________________________ + + def OnCloseButtonClicked(self, widget): + """ + Called when the close button is clicked. + Destroys the project templates window. + + Parameters: + widget -- reserved for GTK callbacks. Don't use explicitly. + """ + self.window.destroy() + + #_____________________________________________________________________ + + def OnStateChanged(self, obj, change=None, *extra): + """ + Called when a change of state is signalled by any of the + objects this view is 'listening' to. + + Parameters: + obj -- object changing state. *CHECK* + change -- the change which has occured. + extra -- extra parameters passed by the caller. + """ + if change == "template-update": + self.UpdateTemplateModel() + + #_____________________________________________________________________ + + def PopulateJokosherInstrumentsModel(self): + """ + Called when the jokosher instruments model needs to be populated. + """ + instrlist = [(x[0], x[1], x[2].copy(), x[3]) for x in Globals.getCachedInstruments()] + for instr in instrlist: + self.instrtreemodel.append((instr[2].scale_simple(22, 22, gtk.gdk.INTERP_BILINEAR), instr[1], instr[0], instr[3])) + + #_____________________________________________________________________ + + def UpdateTemplateModel(self): + """ + Called when the template model needs updating. + """ + self.newprojectdlg.templatemodel.clear() + for item in self.template.LoadTemplateDictionaryInstruments().keys(): + self.newprojectdlg.templatemodel.append((item,)) + self.newprojectdlg.templatecombo.show_all() + self.newprojectdlg.templatecombo.set_active(0) + + #_____________________________________________________________________ + +#========================================================================= Index: NewProjectDialog.py =================================================================== --- NewProjectDialog.py (revision 1346) +++ NewProjectDialog.py (working copy) @@ -16,6 +16,8 @@ import Globals import gettext _ = gettext.gettext +import ProjectTemplate +import ProjectTemplateDialog class NewProjectDialog: """ @@ -39,6 +41,9 @@ self.signals = { "on_OK_clicked" : self.OnOK, "on_Cancel_clicked" : self.OnCancel, + "on_editbutton_clicked" : self.OnEdit, + "on_notemp_radio_toggled" : self.NoTempRadioToggled, + "on_temp_radio_toggled" : self.TempRadioToggled } self.res.signal_autoconnect(self.signals) @@ -54,6 +59,26 @@ self.author = self.res.get_widget("author") self.author.set_activates_default(True) + self.prjtmp = ProjectTemplate.ProjectTemplate() + self.notempradio = self.res.get_widget("notemp_radio") + self.notempradio.set_active(True) + self.tempradio = self.res.get_widget("temp_radio") + self.temphbox = self.res.get_widget("temp_hbox") + self.temphbox.set_sensitive(False) + self.templatecombo = self.res.get_widget("template") + self.templatecombo.clear() + + self.templatemodel = gtk.ListStore(str) + for files in self.prjtmp.LoadTemplateDictionaryInstruments().keys(): + self.templatemodel.append((files,)) + self.templatecombo.set_model(self.templatemodel) + + text = gtk.CellRendererText() + self.templatecombo.pack_start(text) + self.templatecombo.add_attribute(text, "text", 0) + + self.templatecombo.set_active(0) + # Default author to name of currently logged in user try: # Try to get the full name if it exists @@ -75,7 +100,31 @@ self.dlg.resize(350, 300) self.dlg.set_icon(self.parent.icon) self.dlg.set_transient_for(self.parent.window) + + #_____________________________________________________________________ + + def NoTempRadioToggled(self, widget): + """ + Called when the no template radio button is activated. + The template hbox becomes active if the no template radio button is activated. + + Parameters: + widget -- reserved for GTK callbacks, don't use it explicitly. + """ + self.temphbox.set_sensitive(False) + + #_____________________________________________________________________ + def TempRadioToggled(self, widget): + """ + Called when the template radio button is activated. + The template hbox becomes inactive if the template radio button is activated. + + Parameters: + widget -- reserved for GTK callbacks, don't use it explicitly. + """ + self.temphbox.set_sensitive(True) + #_____________________________________________________________________ def OnOK(self, button): @@ -101,9 +150,9 @@ Globals.settings.write() if not folder: folder = "~" - + try: - project = ProjectManager.CreateNewProject(folder,name, author) + project = ProjectManager.CreateNewProject(folder, name, author) except ProjectManager.CreateProjectError, e: if e.errno == 1: message = _("Could not initialize project.") @@ -129,6 +178,8 @@ else: self.parent.SetProject(project) self.dlg.destroy() + + self.AddProjectTemplate() #_____________________________________________________________________ @@ -141,4 +192,48 @@ """ self.dlg.destroy() - #_____________________________________________________________________ + #_____________________________________________________________________ + + def OnEdit(self, button): + """ + Displays the ProjectTemplate dialog, allowing the user to create, delete and modify project + templates. + + Parameters: + button -- reserved for GTK callbacks, don't use it explicitly. + """ + ProjectTemplateDialog.ProjectTemplateDialog(self, self.prjtmp) + + #_____________________________________________________________________ + + def ReturnProjectInstrumentTuples(self, name): + """ + Ths method will return a tuple containing the name, type and pixbuf of the instruments in a given template file. + + Parameters: + name -- the name of the template to use. + + Returns: + instruments -- a list of instruments containing tuples required for project.AddInstruments() + """ + instruments = [] + for i in self.prjtmp.LoadTemplateDictionaryInstruments()[name]: + items = ((i[1], i[0], gtk.gdk.pixbuf_new_from_file(i[2]), i[2])) + instruments.append(items) + return instruments + + #_____________________________________________________________________ + + def AddProjectTemplate(self): + """ + Adds template instruments to the user's project. + """ + if self.tempradio.get_active(): + if self.parent.project: + active = self.templatecombo.get_model()[self.templatecombo.get_active()][0] + self.parent.project.AddInstruments(self.ReturnProjectInstrumentTuples(active)) + self.parent.UpdateDisplay() + + #_____________________________________________________________________ + +#=========================================================================