Example #1
0
 def __init__(self, parent, error_text):
     Gtk.Dialog.__init__(self, _("Make bug report"), parent,
             buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT))
     self.m_error_text = error_text
     self.add_button(_("_Send"), RESPONSE_SEND)
     self.set_default_size(400, 400)
     sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
     l = Gtk.Label(_("Information about the version of GNU Solfege, your operating system and Python version, and the Python traceback (error message) will be sent to the crash database. Your email will not be published or shared, but we might contact you by email if we have further questions about your crash report."))
     l.set_line_wrap(True)
     l.show()
     self.vbox.pack_start(l, False, False, 0)
     self.g_email = Gtk.Entry()
     self.vbox.pack_start(
         gu.hig_label_widget(_("_Email:"), self.g_email, sizegroup),
         False, False, 0)
     self.g_email.set_text(cfg.get_string('user/email'))
     # 140 is max in the solfege.org database
     self.g_description = Gtk.Entry()
     self.g_description.set_max_length(140)
     self.vbox.pack_start(
         gu.hig_label_widget(_("S_hort description:"), self.g_description,
                  sizegroup), False, False, 0)
     label = Gtk.Label(label=_("_Describe how to produce the error message:"))
     label.set_use_underline(True)
     label.set_alignment(0.0, 0.5)
     self.vbox.pack_start(label, False, False, 0)
     self.g_tw = Gtk.TextView()
     self.g_tw.set_wrap_mode(Gtk.WrapMode.WORD)
     self.g_tw.set_border_width(10)
     label.set_mnemonic_widget(self.g_tw)
     self.vbox.pack_start(self.g_tw, True, True, 0)
     self.show_all()
Example #2
0
    def create_user_config(self):
        it, page_vbox = self.new_page_box(None, _("User"))
        box, category_vbox = gu.hig_category_vbox(_("User's Vocal Range"))
        page_vbox.pack_start(box, False, False, 0)
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        self.g_highest_singable = notenamespinbutton.NotenameSpinButton(
            self.get_string('user/highest_pitch'))
        box = gu.hig_label_widget(_("Highest pitch:"),
                                  self.g_highest_singable,
                                  sizegroup)
        category_vbox.pack_start(box, True, True, 0)

        self.g_lowest_singable = notenamespinbutton.NotenameSpinButton(
            self.get_string('user/lowest_pitch'))
        box = gu.hig_label_widget(_("Lowest pitch:"),
                                  self.g_lowest_singable,
                                  sizegroup)
        category_vbox.pack_start(box, True, True, 0)
        notenamespinbutton.nNotenameRangeController(
                  self.g_lowest_singable, self.g_highest_singable,
                  mpd.LOWEST_NOTENAME, mpd.HIGHEST_NOTENAME,
                  'user', 'lowest_pitch', 'highest_pitch')


        box, category_vbox = gu.hig_category_vbox(_("Sex"))
        page_vbox.pack_start(box, False, False, 0)
        self.g_sex_male = Gtk.RadioButton.new_with_mnemonic(None, _("_Male"))
        self.g_sex_male.connect('toggled', lambda w: self.set_string('user/sex', 'male'))
        category_vbox.pack_start(self.g_sex_male, False, False, 0)
        self.g_sex_female = Gtk.RadioButton.new_with_mnemonic_from_widget(self.g_sex_male, _("_Female or child"))
        self.g_sex_female.connect('toggled', lambda w: self.set_string('user/sex', 'female'))
        category_vbox.pack_start(self.g_sex_female, False, False, 0)
        if self.get_string('user/sex') == 'female':
            self.g_sex_female.set_active(True)
Example #3
0
 def __init__(self, parent, error_text):
     gtk.Dialog.__init__(self, _("Make bug report"), parent,
             buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT))
     self.m_error_text = error_text
     self.add_button(_("See complete _report"), RESPONSE_SEE)
     self.add_button(_("_Send"), RESPONSE_SEND)
     self.set_default_size(400, 400)
     sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
     self.g_email = gtk.Entry()
     self.vbox.pack_start(
         gu.hig_label_widget(_("_Email:"), self.g_email, sizegroup),
         False)
     self.g_mangled_email = gtk.Label()
     self.vbox.pack_start(
         gu.hig_label_widget(_("Mangled email address:"), self.g_mangled_email,
             sizegroup), False)
     self.g_email.set_text(cfg.get_string('user/email'))
     self.g_email.connect('changed', self.on_update_mangle)
     self.g_description = gtk.Entry()
     self.vbox.pack_start(
         gu.hig_label_widget(_("S_hort description:"), self.g_description,
                  sizegroup), False)
     label = gtk.Label(_("_Describe how to produce the error message:"))
     label.set_use_underline(True)
     label.set_alignment(0.0, 0.5)
     self.vbox.pack_start(label, False)
     self.g_tw = gtk.TextView()
     self.g_tw.set_wrap_mode(gtk.WRAP_WORD)
     self.g_tw.set_border_width(10)
     # translators, please notice that the word NO_DESCRIPTION must not be
     # translated in this string.
     self.g_tw.get_buffer().insert_at_cursor(_("""Describe as exactly as you can what you did when this error occurred. If you give no description at all, you make it very difficult to track down this bug. You should replace this text with your description, and also remove the "bug-tag" in the bottom of this text so that this bug is not automatically sorted among the bug reports with no description.\n\n(bug-tag: NO_DESCRIPTION)"""))
     label.set_mnemonic_widget(self.g_tw)
     self.vbox.pack_start(self.g_tw)
     self.show_all()
Example #4
0
    def show_path_info(self, w):
        if not self.g_path_info_dlg:
            self.g_path_info_dlg = Gtk.Dialog(_("_File locations").replace("_", ""), self,
                buttons=(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
            sc = Gtk.ScrolledWindow()
            sc.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
            self.g_path_info_dlg.vbox.pack_start(sc, True, True, 0)
            #
            vbox = gu.hig_dlg_vbox()
            sc.add_with_viewport(vbox)

            box1, box2 = gu.hig_category_vbox(_("_File locations").replace("_", ""))
            vbox.pack_start(box1, True, True, 0)
            sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
            # statistics.sqlite
            # win32 solfegerc
            # win32 langenviron.txt
            box2.pack_start(gu.hig_label_widget(_("Solfege application data:"), Gtk.Label(label=filesystem.app_data()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("Solfege user data:"), Gtk.Label(label=filesystem.user_data()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("Solfege config file:"), Gtk.Label(label=filesystem.rcfile()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("Solfege installation directory:"), Gtk.Label(label=os.getcwd()), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget(_("User manual in HTML format:"), Gtk.Label(label=os.path.join(os.getcwd(), "help")), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget("gtk:", Gtk.Label(label=str(Gtk)), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget("pyalsa:", Gtk.Label(label=str(alsaseq)), sizegroup), False, False, 0)
            box2.pack_start(gu.hig_label_widget("PYTHONHOME", Gtk.Label(os.environ.get('PYTHONHOME', 'Not defined')), sizegroup), False, False, 0)
            self.g_path_info_dlg.show_all()

            def f(*w):
                self.g_path_info_dlg.hide()
                return True
            self.g_path_info_dlg.connect('response', f)
            self.g_path_info_dlg.connect('delete-event', f)
            sc.set_size_request(min(vbox.size_request().width + gu.hig.SPACE_LARGE * 2,
                                    Gdk.Screen.width() * 0.9),
                                vbox.size_request().height)
Example #5
0
    def create_midi_config(self):
        it, page_vbox = self.new_page_box(None, _("Instruments"))
        vbox, category_vbox = gu.hig_category_vbox(_("Tempo"))
        page_vbox.pack_start(vbox, False, False, 0)
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        tempo_hbox = Gtk.Box(spacing=6)
        self.g_default_bpm = gu.nSpinButton(
            'config', 'default_bpm',
            Gtk.Adjustment(self.get_int('config/default_bpm'), 10, 500, 1, 10))
        self.g_arpeggio_bpm = gu.nSpinButton(
            'config', 'arpeggio_bpm',
            Gtk.Adjustment(self.get_int('config/arpeggio_bpm'), 10, 500, 1,
                           10))
        for text, widget in [(_("_Default:"), self.g_default_bpm),
                             (_("A_rpeggio:"), self.g_arpeggio_bpm)]:
            label = Gtk.Label(_("BPM"))
            tempo_hbox.pack_start(
                gu.hig_label_widget(text, [widget, label], None), False, False,
                0)
            label.set_tooltip_text(_("Beats per minute"))
        category_vbox.pack_start(tempo_hbox, False, False, 0)

        box, category_vbox = gu.hig_category_vbox(_("Preferred Instrument"))
        page_vbox.pack_start(box, False, False, 0)
        self.g_instrsel = nInstrumentSelector('config', 'preferred_instrument',
                                              None)
        category_vbox.pack_start(self.g_instrsel, False, False, 0)

        box, category_vbox = gu.hig_category_vbox(_("Chord Instruments"))
        page_vbox.pack_start(box, False, False, 0)
        self.g_instrument_configurator  \
              = InstrumentConfigurator("config", 3,
                    _("Use different instruments for chords and harmonic intervals"))
        category_vbox.pack_start(self.g_instrument_configurator, False, False,
                                 0)

        vbox, category_box = gu.hig_category_vbox(_("Percussion Instruments"))
        page_vbox.pack_start(vbox, False, False, 0)
        category_box.pack_start(
            gu.hig_label_widget(
                _("Count in:"),
                gu.PercussionNameButton("config", "countin_perc", "Claves"),
                sizegroup, True, True), True, True, 0)
        category_box.pack_start(
            gu.hig_label_widget(
                _("Rhythm:"),
                gu.PercussionNameButton("config", "rhythm_perc", "Side Stick"),
                sizegroup, True, True), False, False, 0)
Example #6
0
    def create_midi_config(self):
        it, page_vbox = self.new_page_box(None, _("Instruments"))
        vbox, category_vbox = gu.hig_category_vbox(_("Tempo"))
        page_vbox.pack_start(vbox, False)
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        tempo_hbox = gtk.HBox(spacing=6)
        self.g_default_bpm = gu.nSpinButton('config', 'default_bpm',
            gtk.Adjustment(self.get_int('config/default_bpm'), 10, 500, 1, 10))
        self.g_arpeggio_bpm = gu.nSpinButton('config', 'arpeggio_bpm',
            gtk.Adjustment(self.get_int('config/arpeggio_bpm'), 10, 500, 1, 10))
        for text, widget in [(_("_Default:") ,self.g_default_bpm),
                             (_("A_rpeggio:") ,self.g_arpeggio_bpm)]:
            label = gtk.Label(text)
            label.set_alignment(0.0, 0.5)
            label.set_use_underline(True)
            tempo_hbox.pack_start(label, expand=False)
            label.set_mnemonic_widget(widget)
            tempo_hbox.pack_start(widget, expand=False)
            label = gtk.Label(_("BPM"))
            label.set_alignment(0.0, 0.5)
            label.set_tooltip_text(_("Beats per minute"))
            tempo_hbox.pack_start(label, expand=True)
        category_vbox.pack_start(tempo_hbox, False)

        box, category_vbox = gu.hig_category_vbox(_("Preferred Instrument"))
        page_vbox.pack_start(box, False)
        self.g_instrsel = nInstrumentSelector('config',
                        'preferred_instrument', sizegroup)
        category_vbox.pack_start(self.g_instrsel, False)

        box, category_vbox = gu.hig_category_vbox(_("Chord Instruments"))
        page_vbox.pack_start(box, False)
        self.g_instrument_configurator  \
              = InstrumentConfigurator("config", 3,
                    _("Use different instruments for chords and harmonic intervals"))
        category_vbox.pack_start(self.g_instrument_configurator, False)

        vbox, category_box = gu.hig_category_vbox(_("Percussion Instruments"))
        page_vbox.pack_start(vbox, False)
        category_box.pack_start(gu.hig_label_widget(
            _("Count in:"),
            gu.PercussionNameButton("config", "countin_perc", "Claves"),
            sizegroup, True, True))
        category_box.pack_start(gu.hig_label_widget(
            _("Rhythm:"),
            gu.PercussionNameButton("config", "rhythm_perc", "Side Stick"),
            sizegroup, True, True))
Example #7
0
    def create_mainwin_ui(self):
        qbox = gu.hig_dlg_vbox()
        self.g_notebook.append_page(qbox, Gtk.Label(label=_("Questions")))
        gu.bLabel(qbox, _("Enter new chords using the mouse"), False, False)
        hbox = gu.bHBox(qbox, False, False)
        self.g_displayer = mpd.musicdisplayer.ChordEditor()
        self.g_displayer.connect('clicked', self.on_displayer_clicked)
        self.g_displayer.clear(2)
        gu.bLabel(hbox, "")
        hbox.pack_start(self.g_displayer, False)
        gu.bLabel(hbox, "")
        ##
        self.g_question_name = Gtk.Entry()
        qbox.pack_start(gu.hig_label_widget(_("Question title:", True, True, 0), self.g_question_name, None), False)
        self.g_navinfo = Gtk.Label(label="")
        qbox.pack_start(self.g_navinfo, False)

        ##
        self.m_P = EditorLessonfile()
        cvbox = Gtk.VBox()
        self.g_notebook.append_page(cvbox, Gtk.Label(label=_("Lessonfile header")))
        # Header section
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
        self.g_title = Gtk.Entry()
        cvbox.pack_start(gu.hig_label_widget(_("File title:", True, True, 0), self.g_title,
                        sizegroup))
        self.g_content_chord = Gtk.RadioButton(None, "chord")
        self.g_content_chord_voicing = Gtk.RadioButton(self.g_content_chord, "chord-voicing")
        self.g_content_idbyname = Gtk.RadioButton(self.g_content_chord, "id-by-name")
        box = Gtk.HBox()
        box.pack_start(self.g_content_chord, True, True, 0)
        box.pack_start(self.g_content_chord_voicing, True, True, 0)
        box.pack_start(self.g_content_idbyname, True, True, 0)
        cvbox.pack_start(gu.hig_label_widget(_("Content:", True, True, 0), box, sizegroup))
        self.g_random_transpose = Gtk.Entry()
        cvbox.pack_start(gu.hig_label_widget(_("Random transpose:", True, True, 0),
            self.g_random_transpose, sizegroup))
        #
        #self.g_statusbar = Gtk.Statusbar()
        #self.toplevel_vbox.pack_start(self.g_statusbar, False)
        self.update_appwin()
Example #8
0
    def create_user_config(self):
        it, page_vbox = self.new_page_box(None, _("User"))
        box, category_vbox = gu.hig_category_vbox(_("User's Vocal Range"))
        page_vbox.pack_start(box, False, False, 0)
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        self.g_highest_singable = notenamespinbutton.NotenameSpinButton(
            self.get_string('user/highest_pitch'))
        box = gu.hig_label_widget(_("Highest pitch:"), self.g_highest_singable,
                                  sizegroup)
        category_vbox.pack_start(box, True, True, 0)

        self.g_lowest_singable = notenamespinbutton.NotenameSpinButton(
            self.get_string('user/lowest_pitch'))
        box = gu.hig_label_widget(_("Lowest pitch:"), self.g_lowest_singable,
                                  sizegroup)
        category_vbox.pack_start(box, True, True, 0)
        notenamespinbutton.nNotenameRangeController(self.g_lowest_singable,
                                                    self.g_highest_singable,
                                                    mpd.LOWEST_NOTENAME,
                                                    mpd.HIGHEST_NOTENAME,
                                                    'user', 'lowest_pitch',
                                                    'highest_pitch')

        box, category_vbox = gu.hig_category_vbox(_("Sex"))
        page_vbox.pack_start(box, False, False, 0)
        self.g_sex_male = Gtk.RadioButton.new_with_mnemonic(None, _("_Male"))
        self.g_sex_male.connect('toggled',
                                lambda w: self.set_string('user/sex', 'male'))
        category_vbox.pack_start(self.g_sex_male, False, False, 0)
        self.g_sex_female = Gtk.RadioButton.new_with_mnemonic_from_widget(
            self.g_sex_male, _("_Female or child"))
        self.g_sex_female.connect(
            'toggled', lambda w: self.set_string('user/sex', 'female'))
        category_vbox.pack_start(self.g_sex_female, False, False, 0)
        if self.get_string('user/sex') == 'female':
            self.g_sex_female.set_active(True)
Example #9
0
    def __init__(self, filename=None):
        logging.debug("PractiseSheetDialog.__init__")
        Gtk.Window.__init__(self)
        gu.EditorDialogBase.__init__(self, filename)
        self.m_changed = False
        self.set_title(self._get_a_filename())
        self.m_exported_to = None
        self.set_default_size(800, 300)
        # self.vbox contains the toolbar and the vbox with the rest of the
        # window contents
        self.vbox = Gtk.VBox()
        self.add(self.vbox)
        self.setup_toolbar()
        vbox = Gtk.VBox()
        vbox.set_spacing(6)
        self.vbox.pack_start(vbox, True, True, 0)
        vbox.set_border_width(8)
        hbox = Gtk.HBox()
        vbox.pack_start(hbox, False, False, 0)
        hbox.pack_start(Gtk.Label(_("Output format:")), False, False, 0)
        self.g_latex_radio = Gtk.RadioButton.new_with_label(None, "LaTeX")
        hbox.pack_start(self.g_latex_radio, False, False, 0)
        self.g_html_radio = Gtk.RadioButton.new_with_label_from_widget(
            self.g_latex_radio, "HTML")
        hbox.pack_start(self.g_html_radio, False, False, 0)
        #
        hbox = Gtk.HBox()
        hbox.set_spacing(6)
        vbox.pack_start(hbox, False, False, 0)
        hbox.pack_start(Gtk.Label(_("Title:")), False, False, 0)
        self.g_title = Gtk.Entry()
        hbox.pack_start(self.g_title, False, False, 0)
        #
        self.m_sections = []
        self.g_liststore = Gtk.ListStore(
            GObject.TYPE_STRING,  # lesson-file title
            GObject.TYPE_STRING,  # lessonfile filename, hidden column
        )
        self.g_treeview = Gtk.TreeView(self.g_liststore)
        self.g_treeview.set_size_request(400, 100)
        self.g_treeview.set_headers_visible(False)
        self.g_treeview.connect('cursor-changed', self.on_tv_cursor_changed)
        self.g_treeview.connect('unselect-all', self.on_tv_unselect_all)
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add(self.g_treeview)
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC,
                                   Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        #
        renderer = Gtk.CellRendererText()

        def mark_invalid(column,
                         cell_renderer,
                         liststore,
                         iter,
                         user_data=None):
            filename = liststore.get(iter, self.STORE_FILENAME)[0]
            if not filename:
                cell_renderer.props.markup = '<span background="red">%s</span>' % liststore.get_value(
                    iter, self.STORE_TITLE)

        column = Gtk.TreeViewColumn(None, renderer, text=self.STORE_TITLE)
        column.set_cell_data_func(renderer, mark_invalid)
        self.g_treeview.append_column(column)
        column = Gtk.TreeViewColumn(None, renderer, text=self.STORE_FILENAME)
        self.g_treeview.append_column(column)
        #
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
        self.g_lbox = Gtk.VBox()
        self.g_lbox.set_sensitive(False)
        vbox.pack_start(self.g_lbox, True, True, 0)
        self.g_lesson_title = Gtk.Entry()
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Section title:"), self.g_lesson_title,
                                sizegroup), False, False, 0)
        self.g_lesson_title_event_handle =\
            self.g_lesson_title.connect('changed', self.on_lesson_title_changed)
        #
        self.g_qtype = Gtk.ComboBoxText()
        # We should not change the order of music types, as the index
        # of the different types are used in SolfegeApp.on_create_sheet
        self.g_qtype.append_text(_("Name the music"))
        self.g_qtype.append_text(_("Write the music, first tone given"))
        self.g_qtype_event_handler = \
            self.g_qtype.connect('changed', self.on_qtype_changed)
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Type of question:"), self.g_qtype,
                                sizegroup), False, False, 0)
        #
        self.g_intervals = IntervalCheckBox()
        self.g_intervals_box = gu.hig_label_widget(_("Intervals:"),
                                                   self.g_intervals, sizegroup)
        self.g_intervals_event_handler = \
            self.g_intervals.connect('value-changed', self.on_intervals_changed)
        self.g_lbox.pack_start(self.g_intervals_box, False, False, 0)
        #
        self.g_line_len = Gtk.SpinButton(
            adjustment=Gtk.Adjustment(0, 1, 10, 1, 10))
        self.g_line_len_event_handler = \
            self.g_line_len.connect('value-changed', self.on_spin_changed, 'line_len')
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Questions per line:"), self.g_line_len,
                                sizegroup), False, False, 0)
        #
        self.g_count = Gtk.SpinButton(
            adjustment=Gtk.Adjustment(0, 1, 1000, 1, 10))
        self.g_count_event_handler = \
            self.g_count.connect('value-changed', self.on_spin_changed, 'count')
        self.g_lbox.pack_start(
            gu.hig_label_widget(_("Number of questions:"), self.g_count,
                                sizegroup), False, False, 0)
        if filename:
            self.load_file(filename)
        self.add_to_instance_dict()

        # The popupmenu shown to add exercises
        self.g_emenu = self.create_learning_tree_menu()
Example #10
0
    def add_gui_for_external_programs(self, page_vbox):
        box, category_vbox = gu.hig_category_vbox(_("Audio File Players"))
        page_vbox.pack_start(box, False, False, 0)
        format_info = {}
        if sys.platform != 'win32':
            format_info = {'wav': {
                 # testfile is a file in lesson-files/share
                 'testfile': 'fifth-small-220.00.wav',
                 'label': _("WAV:"),
                 # players is a list of tuples. The tuple has to or more
                 # items. The first is the binary, and the rest is possible
                 # sets of command line options that might work.
                 # '/path/to/player', 'comandline', '
                 'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                                       'filesrc location=%s ! wavparse ! alsasink'),
                        ('play', ''),
                        ('aplay', ''),
                        ('esdplay', ''),
                 ],
                }
            }
        format_info['midi'] = {
             'testfile': 'fanfare.midi',
             'label': _("MIDI:"),
             'players': [
                         ('gst-launch', 'playbin uri=file://%s',
                                        'filesrc location=exercises/standard/lesson-files/share/fanfare.midi ! wildmidi ! alsasink'),
                         ('timidity', '-idqq %s'),
                         ('drvmidi', ''),
                         ('playmidi', ''),
             ],
            }
        format_info['mp3'] = {
             'testfile': 'fanfare.mp3',
             'label': _("MP3:"),
             'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                                       'filesrc location=%s ! mad ! alsasink'),
                        ('mpg123', ''),
                        ('alsaplayer', ''),
             ],
            }
        format_info['ogg'] = {
             'testfile': 'fanfare.ogg',
             'label': _("OGG:"),
             'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                                       'filesrc location=%s ! oggdemux ! vorbisdec ! audioconvert ! alsasink'),
                        ('ogg123', ''),
                        ('alsaplayer', ''),
             ],
            }
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
        for formatid, format in format_info.items():
            combo = gu.sComboBox('sound', '%s_player' % formatid, [p[0] for p in format['players']])
            combo.set_tooltip_text(_("Enter the name of the program. An absolute path is required only if the executable is not found on the PATH."))
            combo.opts = gu.sComboBox('sound', '%s_player_options' % formatid, 'NOT YET!')
            combo.opts.set_tooltip_text(_("The command line options required. Write %s where you want the name of the file to be played. Or omit it to have it added to the end of the string."))

            def _changed(widget, formatid):
                if widget.get_active() != -1:
                    model = widget.opts.get_model()
                    model.clear()
                    format = format_info[formatid]
                    for player_options in format['players'][widget.get_active()][1:]:
                        model.append([player_options])
                    widget.opts.get_child().set_text(format['players'][widget.get_active()][1])
                widget.testbutton.set_sensitive(bool(
                    osutils.find_progs((widget.get_child().get_text().decode("utf-8"),))))

            combo.connect('changed', _changed, formatid)
            testbutton = Gtk.Button(_("_Test").replace("_", ""))
            testbutton.set_tooltip_text(_("This button is clickable only if the binary is found."))
            combo.testbutton = testbutton
            testbutton.connect('clicked', self.test_XXX_player,
                           formatid, format['testfile'])
            testbutton.set_sensitive(bool(
                    osutils.find_progs((combo.get_child().get_text().decode("utf-8"),))))
            box = gu.hig_label_widget(format['label'],
                                      [combo, combo.opts, testbutton],
                                      sizegroup, True, True)
            category_vbox.pack_start(box, True, True, 0)
Example #11
0
    def create_external_programs_config(self):
        it, page_vbox = self.new_page_box(None, _("External Programs"))
        box, category_vbox = gu.hig_category_vbox(_("Converters"))
        page_vbox.pack_start(box, False, False, 0)
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        # midi_to_wav
        self.g_wav_convertor = gu.sComboBox(
            'app', 'midi_to_wav_cmd', ['timidity'])
        self.g_wav_convertor_options = gu.sComboBox(
            'app', 'midi_to_wav_cmd_options', ["-Ow %(in)s -o %(out)s"])
        self.g_wav_convertor_options.set_entry_text_column(0)
        category_vbox.pack_start(
            gu.hig_label_widget(_("MIDI to WAV:"),
                (self.g_wav_convertor, self.g_wav_convertor_options),
                 sizegroup),
            False, False, 0)

        # wav_to_mp3
        self.g_mp3_convertor = gu.sComboBox(
            'app', 'wav_to_mp3_cmd', ["lame"])
        self.g_mp3_convertor_options = gu.sComboBox(
            'app', 'wav_to_mp3_cmd_options', ["%(in)s %(out)s"])
        category_vbox.pack_start(
            gu.hig_label_widget(_("WAV to MP3:"),
                (self.g_mp3_convertor, self.g_mp3_convertor_options),
                sizegroup),
            False, False, 0)

        # wav_to_ogg
        self.g_ogg_convertor = gu.sComboBox(
            "app", "wav_to_ogg_cmd", ["oggenc"])
        self.g_ogg_convertor_options = gu.sComboBox(
            "app", "wav_to_ogg_cmd_options", ["%(in)s"])
        category_vbox.pack_start(
            gu.hig_label_widget(_("WAV to OGG:"),
                (self.g_ogg_convertor, self.g_ogg_convertor_options),
                sizegroup),
            False, False, 0)

        self.add_gui_for_external_programs(page_vbox)
        ########
        # Misc #
        ########
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        box, category_vbox = gu.hig_category_vbox(_("Miscellaneous"))
        page_vbox.pack_start(box, False, False, 0)

        # CSound_("Musical MIDI Accompaniment:")
        for binary, label, bins in (
            ("csound", _("CSound:"), osutils.find_csound_executables()),
            ("mma", "MMA:", osutils.find_mma_executables(
                cfg.get_list("app/win32_ignore_drives"))),
            ("lilypond-book", _("Lilypond-book:"),
             osutils.find_progs(("lilypond-book", "lilypond-book.py"))),
            ("latex", "Latex:", osutils.find_progs(("latex",))),
            ("text-editor", _("Text editor:"), osutils.find_progs(("sensible-editor", "gvim", "gedit", "emacs", "notepad.exe"))),
            ):
            combo = gu.sComboBox("programs", binary, bins)
            def binary_changed_cb(widget, binary):
                widget.warning.props.visible = not bool(
                    osutils.find_progs((cfg.get_string('programs/%s' % binary),)))
            combo.warning = Gtk.Image()
            combo.warning.set_tooltip_text(_("Not found. Much of GNU Solfege will run fine without this program. You will get a message when the program is required, and the user manual will explain what you need it for."))
            combo.warning.set_from_stock(Gtk.STOCK_DIALOG_WARNING,
                                              Gtk.IconSize.SMALL_TOOLBAR)
            box = gu.hig_label_widget(label,
                                      [combo, combo.warning],
                                      sizegroup, True, True)
            category_vbox.pack_start(box, True, True, 0)
            combo.warning.props.no_show_all = True
            binary_changed_cb(combo, binary)
            combo.connect('changed', binary_changed_cb, binary)
Example #12
0
    def show_path_info(self, w):
        if not self.g_path_info_dlg:
            self.g_path_info_dlg = Gtk.Dialog(
                _("_File locations").replace("_", ""),
                self,
                buttons=(Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT))
            sc = Gtk.ScrolledWindow()
            sc.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER)
            self.g_path_info_dlg.vbox.pack_start(sc, True, True, 0)
            #
            vbox = gu.hig_dlg_vbox()
            sc.add_with_viewport(vbox)

            box1, box2 = gu.hig_category_vbox(
                _("_File locations").replace("_", ""))
            vbox.pack_start(box1, True, True, 0)
            sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
            # statistics.sqlite
            # win32 solfegerc
            # win32 langenviron.txt
            box2.pack_start(
                gu.hig_label_widget(_("Solfege application data:"),
                                    Gtk.Label(label=filesystem.app_data()),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(_("Solfege user data:"),
                                    Gtk.Label(label=filesystem.user_data()),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(_("Solfege config file:"),
                                    Gtk.Label(label=filesystem.rcfile()),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(_("Solfege installation directory:"),
                                    Gtk.Label(label=os.getcwd()), sizegroup),
                False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(
                    _("User manual in HTML format:"),
                    Gtk.Label(label=os.path.join(os.getcwd(), "help")),
                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget("gtk:", Gtk.Label(label=str(Gtk)),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget("pyalsa:", Gtk.Label(label=str(alsaseq)),
                                    sizegroup), False, False, 0)
            box2.pack_start(
                gu.hig_label_widget(
                    "PYTHONHOME",
                    Gtk.Label(os.environ.get('PYTHONHOME', 'Not defined')),
                    sizegroup), False, False, 0)
            self.g_path_info_dlg.show_all()

            def f(*w):
                self.g_path_info_dlg.hide()
                return True

            self.g_path_info_dlg.connect('response', f)
            self.g_path_info_dlg.connect('delete-event', f)
            sc.set_size_request(
                min(vbox.size_request().width + gu.hig.SPACE_LARGE * 2,
                    Gdk.Screen.width() * 0.9),
                vbox.size_request().height)
Example #13
0
    def __init__(self, filename=None):
        logging.debug("PractiseSheetDialog.__init__")
        Gtk.Window.__init__(self)
        gu.EditorDialogBase.__init__(self, filename)
        self.m_changed = False
        self.set_title(self._get_a_filename())
        self.m_exported_to = None
        self.set_default_size(800, 300)
        # self.vbox contains the toolbar and the vbox with the rest of the
        # window contents
        self.vbox = Gtk.VBox()
        self.add(self.vbox)
        self.setup_toolbar()
        vbox = Gtk.VBox()
        vbox.set_spacing(6)
        self.vbox.pack_start(vbox, True, True, 0)
        vbox.set_border_width(8)
        hbox = Gtk.HBox()
        vbox.pack_start(hbox, False, False, 0)
        hbox.pack_start(Gtk.Label(_("Output format:")), False, False, 0)
        self.g_latex_radio = Gtk.RadioButton.new_with_label(None, "LaTeX")
        hbox.pack_start(self.g_latex_radio, False, False, 0)
        self.g_html_radio = Gtk.RadioButton.new_with_label_from_widget(self.g_latex_radio, "HTML")
        hbox.pack_start(self.g_html_radio, False, False, 0)
        #
        hbox = Gtk.HBox()
        hbox.set_spacing(6)
        vbox.pack_start(hbox, False, False, 0)
        hbox.pack_start(Gtk.Label(_("Title:")), False, False, 0)
        self.g_title = Gtk.Entry()
        hbox.pack_start(self.g_title, False, False, 0)
        #
        self.m_sections = []
        self.g_liststore = Gtk.ListStore(
            GObject.TYPE_STRING, # lesson-file title
            GObject.TYPE_STRING, # lessonfile filename, hidden column
        )
        self.g_treeview = Gtk.TreeView(self.g_liststore)
        self.g_treeview.set_size_request(400, 100)
        self.g_treeview.set_headers_visible(False)
        self.g_treeview.connect('cursor-changed', self.on_tv_cursor_changed)
        self.g_treeview.connect('unselect-all', self.on_tv_unselect_all)
        scrolled_window = Gtk.ScrolledWindow()
        scrolled_window.add(self.g_treeview)
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
        vbox.pack_start(scrolled_window, True, True, 0)
        #
        renderer = Gtk.CellRendererText()

        def mark_invalid(column, cell_renderer, liststore, iter, user_data=None):
            filename = liststore.get(iter, self.STORE_FILENAME)[0]
            if not filename:
                cell_renderer.props.markup = '<span background="red">%s</span>' % liststore.get_value(iter, self.STORE_TITLE)
        column = Gtk.TreeViewColumn(None, renderer, text=self.STORE_TITLE)
        column.set_cell_data_func(renderer, mark_invalid)
        self.g_treeview.append_column(column)
        column = Gtk.TreeViewColumn(None, renderer, text=self.STORE_FILENAME)
        self.g_treeview.append_column(column)
        #
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
        self.g_lbox = Gtk.VBox()
        self.g_lbox.set_sensitive(False)
        vbox.pack_start(self.g_lbox, True, True, 0)
        self.g_lesson_title = Gtk.Entry()
        self.g_lbox.pack_start(gu.hig_label_widget(_("Section title:"),
            self.g_lesson_title, sizegroup), False, False, 0)
        self.g_lesson_title_event_handle =\
            self.g_lesson_title.connect('changed', self.on_lesson_title_changed)
        #
        self.g_qtype = Gtk.ComboBoxText()
        # We should not change the order of music types, as the index
        # of the different types are used in SolfegeApp.on_create_sheet
        self.g_qtype.append_text(_("Name the music"))
        self.g_qtype.append_text(_("Write the music, first tone given"))
        self.g_qtype_event_handler = \
            self.g_qtype.connect('changed', self.on_qtype_changed)
        self.g_lbox.pack_start(gu.hig_label_widget(_("Type of question:"),
            self.g_qtype, sizegroup), False, False, 0)
        #
        self.g_intervals = IntervalCheckBox()
        self.g_intervals_box = gu.hig_label_widget(_("Intervals:"),
            self.g_intervals, sizegroup)
        self.g_intervals_event_handler = \
            self.g_intervals.connect('value-changed', self.on_intervals_changed)
        self.g_lbox.pack_start(self.g_intervals_box, False, False, 0)
        #
        self.g_line_len = Gtk.SpinButton(adjustment=Gtk.Adjustment(0, 1, 10, 1, 10))
        self.g_line_len_event_handler = \
            self.g_line_len.connect('value-changed', self.on_spin_changed, 'line_len')
        self.g_lbox.pack_start(gu.hig_label_widget(_("Questions per line:"),
            self.g_line_len, sizegroup), False, False, 0)
        #
        self.g_count = Gtk.SpinButton(adjustment=Gtk.Adjustment(0, 1, 1000, 1, 10))
        self.g_count_event_handler = \
            self.g_count.connect('value-changed', self.on_spin_changed, 'count')
        self.g_lbox.pack_start(gu.hig_label_widget(_("Number of questions:"),
            self.g_count, sizegroup), False, False, 0)
        if filename:
            self.load_file(filename)
        self.add_to_instance_dict()
Example #14
0
    def create_external_programs_config(self):
        it, page_vbox = self.new_page_box(None, _("External Programs"))
        box, category_vbox = gu.hig_category_vbox(_("Converters"))
        page_vbox.pack_start(box, False)
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        # midi_to_wav
        self.g_wav_convertor = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_wav_convertor.get_model().append(("timidity",))
        #
        self.g_wav_convertor_options = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_wav_convertor_options.get_model().append(("-Ow %(in)s -o %(out)s",))
        #
        category_vbox.pack_start(
            gu.hig_label_widget(_("MIDI to WAV:"),
               (self.g_wav_convertor, self.g_wav_convertor_options),
               sizegroup, True, True))
        #
        self.g_wav_convertor.child.set_text(
            self.get_string("app/midi_to_wav_cmd"))
        self.g_wav_convertor_options.child.set_text(
            self.get_string("app/midi_to_wav_cmd_options"))
        self.g_wav_convertor.connect('changed',
            lambda w: self.set_string('app/midi_to_wav_cmd',
                w.child.get_text()))
        self.g_wav_convertor_options.connect('changed',
            lambda w: self.set_string('app/midi_to_wav_cmd_options',
                w.child.get_text()))
        # wav_to_mp3
        self.g_mp3_convertor = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_mp3_convertor.get_model().append(("lame",))
        #
        self.g_mp3_convertor_options = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_mp3_convertor_options.get_model().append(("%(in)s %(out)s",))
        #
        category_vbox.pack_start(
            gu.hig_label_widget(_("WAV to MP3:"),
            (self.g_mp3_convertor, self.g_mp3_convertor_options),
            sizegroup, True, True))
        #
        self.g_mp3_convertor.child.set_text(
            self.get_string("app/wav_to_mp3_cmd"))
        self.g_mp3_convertor_options.child.set_text(
            self.get_string("app/wav_to_mp3_cmd_options"))
        self.g_mp3_convertor.connect('changed',
            lambda w: self.set_string('app/wav_to_mp3_cmd',
                w.child.get_text()))
        self.g_mp3_convertor_options.connect('changed',
            lambda w: self.set_string('app/wav_to_mp3_cmd_options',
                w.child.get_text()))
        # wav_to_ogg
        self.g_ogg_convertor = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_ogg_convertor.get_model().append(("oggenc",))
        #
        self.g_ogg_convertor_options = gtk.ComboBoxEntry(
            gtk.ListStore(gobject.TYPE_STRING))
        self.g_ogg_convertor_options.get_model().append(("%(in)s",))
        #
        category_vbox.pack_start(
            gu.hig_label_widget(_("WAV to OGG:"),
            (self.g_ogg_convertor, self.g_ogg_convertor_options),
            sizegroup, True, True))
        #
        self.g_ogg_convertor.child.set_text(
            self.get_string("app/wav_to_ogg_cmd"))
        self.g_ogg_convertor_options.child.set_text(
            self.get_string("app/wav_to_ogg_cmd_options"))
        self.g_ogg_convertor.connect('changed',
            lambda w: self.set_string('app/wav_to_ogg_cmd',
                w.child.get_text()))
        self.g_ogg_convertor_options.connect('changed',
            lambda w: self.set_string('app/wav_to_ogg_cmd_options',
                w.child.get_text()))

        self.add_gui_for_external_programs(page_vbox)
        ########
        # Misc #
        ########
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)

        box, category_vbox = gu.hig_category_vbox(_("Miscellaneous"))
        page_vbox.pack_start(box, False)

        # CSound_("Musical MIDI Accompaniment:")
        for bin, label, bins in (
            ("csound", _("CSound:"), osutils.find_csound_executables()),
            ("mma", "MMA:", osutils.find_mma_executables(
                cfg.get_list("app/win32_ignore_drives"))),
            ("lilypond-book", _("Lilypond-book:"),
             osutils.find_progs(("lilypond-book", "lilypond-book.py"))),
            ("latex", "Latex:", osutils.find_progs(("latex",))),
            ("text-editor", "Text editor:", osutils.find_progs(("sensible-editor", "gvim", "gedit", "emacs", "notepad.exe"))),
            ):
            liststore = gtk.ListStore(gobject.TYPE_STRING)
            for p in bins:
                liststore.append((p,))
            cbox_entry = gtk.ComboBoxEntry(liststore)
            cbox_entry.child.set_text(self.get_string("programs/%(bin)s" % locals()))
            def csound_changed_cb(widget, bin):
                self.set_string('programs/%s' % bin, widget.child.get_text())
                widget.warning.props.visible = not bool(
                    osutils.find_progs((cfg.get_string('programs/%s' % bin),)))
            cbox_entry.warning = gtk.Image()
            cbox_entry.warning.set_tooltip_text(_("Not found. Much of GNU Solfege will run fine without this program. You will get a message when the program is required, and the user manual will explain what you need it for."))
            cbox_entry.warning.set_from_stock(gtk.STOCK_DIALOG_WARNING,
                                              gtk.ICON_SIZE_SMALL_TOOLBAR)
            box = gu.hig_label_widget(label,
                                      [cbox_entry, cbox_entry.warning],
                                      sizegroup, True, True)
            category_vbox.pack_start(box)
            cbox_entry.warning.props.no_show_all = True
            csound_changed_cb(cbox_entry, bin)
            cbox_entry.connect('changed', csound_changed_cb, bin)
Example #15
0
    def add_gui_for_external_programs(self, page_vbox):
        box, category_vbox = gu.hig_category_vbox(_("Audio File Players"))
        page_vbox.pack_start(box, False, False, 0)
        format_info = {}
        if sys.platform != 'win32':
            format_info = {
                'wav': {
                    # testfile is a file in lesson-files/share
                    'testfile':
                    'fifth-small-220.00.wav',
                    'label':
                    _("WAV:"),
                    # players is a list of tuples. The tuple has to or more
                    # items. The first is the binary, and the rest is possible
                    # sets of command line options that might work.
                    # '/path/to/player', 'comandline', '
                    'players': [
                        ('gst-launch', 'playbin uri=file://%s',
                         'filesrc location=%s ! wavparse ! alsasink'),
                        ('play', ''),
                        ('aplay', ''),
                        ('esdplay', ''),
                    ],
                }
            }
        format_info['midi'] = {
            'testfile':
            'fanfare.midi',
            'label':
            _("MIDI:"),
            'players': [
                ('gst-launch', 'playbin uri=file://%s',
                 'filesrc location=exercises/standard/lesson-files/share/fanfare.midi ! wildmidi ! alsasink'
                 ),
                ('timidity', '-idqq %s'),
                ('drvmidi', ''),
                ('playmidi', ''),
            ],
        }
        format_info['mp3'] = {
            'testfile':
            'fanfare.mp3',
            'label':
            _("MP3:"),
            'players': [
                ('gst-launch', 'playbin uri=file://%s',
                 'filesrc location=%s ! mad ! alsasink'),
                ('mpg123', ''),
                ('alsaplayer', ''),
            ],
        }
        format_info['ogg'] = {
            'testfile':
            'fanfare.ogg',
            'label':
            _("OGG:"),
            'players': [
                ('gst-launch', 'playbin uri=file://%s',
                 'filesrc location=%s ! oggdemux ! vorbisdec ! audioconvert ! alsasink'
                 ),
                ('ogg123', ''),
                ('alsaplayer', ''),
            ],
        }
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
        for formatid, format in format_info.items():
            combo = gu.sComboBox('sound', '%s_player' % formatid,
                                 [p[0] for p in format['players']])
            combo.set_tooltip_text(
                _("Enter the name of the program. An absolute path is required only if the executable is not found on the PATH."
                  ))
            combo.opts = gu.sComboBox('sound', '%s_player_options' % formatid,
                                      'NOT YET!')
            combo.opts.set_tooltip_text(
                _("The command line options required. Write %s where you want the name of the file to be played. Or omit it to have it added to the end of the string."
                  ))

            def _changed(widget, formatid):
                if widget.get_active() != -1:
                    model = widget.opts.get_model()
                    model.clear()
                    format = format_info[formatid]
                    for player_options in format['players'][
                            widget.get_active()][1:]:
                        model.append([player_options])
                    widget.opts.get_child().set_text(
                        format['players'][widget.get_active()][1])
                widget.testbutton.set_sensitive(
                    bool(
                        osutils.find_progs((
                            widget.get_child().get_text().decode("utf-8"), ))))

            combo.connect('changed', _changed, formatid)
            testbutton = Gtk.Button(_("_Test").replace("_", ""))
            testbutton.set_tooltip_text(
                _("This button is clickable only if the binary is found."))
            combo.testbutton = testbutton
            testbutton.connect('clicked', self.test_XXX_player, formatid,
                               format['testfile'])
            testbutton.set_sensitive(
                bool(
                    osutils.find_progs(
                        (combo.get_child().get_text().decode("utf-8"), ))))
            box = gu.hig_label_widget(format['label'],
                                      [combo, combo.opts, testbutton],
                                      sizegroup, True, True)
            category_vbox.pack_start(box, True, True, 0)
Example #16
0
    def create_external_programs_config(self):
        it, page_vbox = self.new_page_box(None, _("External Programs"))
        box, category_vbox = gu.hig_category_vbox(_("Converters"))
        page_vbox.pack_start(box, False, False, 0)
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        # midi_to_wav
        self.g_wav_convertor = gu.sComboBox('app', 'midi_to_wav_cmd',
                                            ['timidity'])
        self.g_wav_convertor_options = gu.sComboBox('app',
                                                    'midi_to_wav_cmd_options',
                                                    ["-Ow %(in)s -o %(out)s"])
        self.g_wav_convertor_options.set_entry_text_column(0)
        category_vbox.pack_start(
            gu.hig_label_widget(
                _("MIDI to WAV:"),
                (self.g_wav_convertor, self.g_wav_convertor_options),
                sizegroup), False, False, 0)

        # wav_to_mp3
        self.g_mp3_convertor = gu.sComboBox('app', 'wav_to_mp3_cmd', ["lame"])
        self.g_mp3_convertor_options = gu.sComboBox('app',
                                                    'wav_to_mp3_cmd_options',
                                                    ["%(in)s %(out)s"])
        category_vbox.pack_start(
            gu.hig_label_widget(
                _("WAV to MP3:"),
                (self.g_mp3_convertor, self.g_mp3_convertor_options),
                sizegroup), False, False, 0)

        # wav_to_ogg
        self.g_ogg_convertor = gu.sComboBox("app", "wav_to_ogg_cmd",
                                            ["oggenc"])
        self.g_ogg_convertor_options = gu.sComboBox("app",
                                                    "wav_to_ogg_cmd_options",
                                                    ["%(in)s"])
        category_vbox.pack_start(
            gu.hig_label_widget(
                _("WAV to OGG:"),
                (self.g_ogg_convertor, self.g_ogg_convertor_options),
                sizegroup), False, False, 0)

        self.add_gui_for_external_programs(page_vbox)
        ########
        # Misc #
        ########
        sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)

        box, category_vbox = gu.hig_category_vbox(_("Miscellaneous"))
        page_vbox.pack_start(box, False, False, 0)

        # CSound_("Musical MIDI Accompaniment:")
        for binary, label, bins in (
            ("csound", _("CSound:"), osutils.find_csound_executables()),
            ("mma", "MMA:",
             osutils.find_mma_executables(
                 cfg.get_list("app/win32_ignore_drives"))),
            ("lilypond-book", _("Lilypond-book:"),
             osutils.find_progs(("lilypond-book", "lilypond-book.py"))),
            ("latex", "Latex:", osutils.find_progs(("latex", ))),
            ("text-editor", _("Text editor:"),
             osutils.find_progs(("sensible-editor", "gvim", "gedit", "emacs",
                                 "notepad.exe"))),
        ):
            combo = gu.sComboBox("programs", binary, bins)

            def binary_changed_cb(widget, binary):
                widget.warning.props.visible = not bool(
                    osutils.find_progs(
                        (cfg.get_string('programs/%s' % binary), )))

            combo.warning = Gtk.Image()
            combo.warning.set_tooltip_text(
                _("Not found. Much of GNU Solfege will run fine without this program. You will get a message when the program is required, and the user manual will explain what you need it for."
                  ))
            combo.warning.set_from_stock(Gtk.STOCK_DIALOG_WARNING,
                                         Gtk.IconSize.SMALL_TOOLBAR)
            box = gu.hig_label_widget(label, [combo, combo.warning], sizegroup,
                                      True, True)
            category_vbox.pack_start(box, True, True, 0)
            combo.warning.props.no_show_all = True
            binary_changed_cb(combo, binary)
            combo.connect('changed', binary_changed_cb, binary)