~ubuntu-branches/ubuntu/saucy/solfege/saucy

« back to all changes in this revision

Viewing changes to src/configwindow.py

  • Committer: Bazaar Package Importer
  • Author(s): Tom Cato Amundsen
  • Date: 2010-03-28 06:34:28 UTC
  • mfrom: (1.1.10 upstream) (2.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100328063428-wg2bqvoce2aq4xfb
Tags: 3.15.9-1
* New upstream release.
* Redo packaging. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# GNU Solfege - free ear training software
2
 
# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008  Tom Cato Amundsen
3
 
#
4
 
# This program is free software: you can redistribute it and/or modify
5
 
# it under the terms of the GNU General Public License as published by
6
 
# the Free Software Foundation, either version 3 of the License, or
7
 
# (at your option) any later version.
8
 
#
9
 
# This program is distributed in the hope that it will be useful,
10
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
# GNU General Public License for more details.
13
 
#
14
 
# You should have received a copy of the GNU General Public License
15
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 
 
17
 
import sys
18
 
import os
19
 
import gobject, gtk
20
 
import gu, mpd
21
 
import buildinfo
22
 
import notenamespinbutton
23
 
from instrumentselector import nInstrumentSelector, InstrumentConfigurator
24
 
import cfg
25
 
import osutils
26
 
import soundcard
27
 
import languages
28
 
import filesystem
29
 
import winlang
30
 
 
31
 
if sys.platform == 'win32':
32
 
    try:
33
 
        from soundcard import winmidi
34
 
    except ImportError, e:
35
 
        print "Loading winmidi.pyd failed:"
36
 
        print e
37
 
        winmidi = None
38
 
 
39
 
 
40
 
class ConfigWindow(gtk.Dialog, cfg.ConfigUtils):
41
 
    def on_destroy(self, widget, e):
42
 
        self.m_app.m_ui.g_config_window.destroy()
43
 
        self.m_app.m_ui.g_config_window = None
44
 
    def __init__(self, app):
45
 
        gtk.Dialog.__init__(self, _("GNU Solfege Preferences"),
46
 
             app.m_ui, 0,
47
 
             (gtk.STOCK_HELP, gtk.RESPONSE_HELP, gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
48
 
        cfg.ConfigUtils.__init__(self, 'configwindow')
49
 
        self.connect('response', self.apply_and_close)
50
 
        # We do this so that the window is only hidden when the
51
 
        # user click on the close button provided by the window manager.
52
 
        self.connect('delete-event', self.on_destroy)#lambda w, e: True)
53
 
        self.set_default_size(400, 400)
54
 
        self.m_app = app
55
 
        self.g_notebook = gtk.Notebook()
56
 
        self.vbox.pack_start(self.g_notebook)
57
 
        
58
 
        ########
59
 
        # midi #
60
 
        ########
61
 
        page_vbox = gu.hig_dlg_vbox()
62
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Instruments")))
63
 
 
64
 
        vbox, category_vbox = gu.hig_category_vbox(_("Tempo"))
65
 
        page_vbox.pack_start(vbox, False)
66
 
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
67
 
 
68
 
        tempo_hbox = gtk.HBox(spacing=6)
69
 
        self.g_default_bpm = gu.nSpinButton('config', 'default_bpm',
70
 
            gtk.Adjustment(self.get_int('config/default_bpm'), 10, 500, 1, 10))
71
 
        self.g_arpeggio_bpm = gu.nSpinButton('config', 'arpeggio_bpm',
72
 
            gtk.Adjustment(self.get_int('config/arpeggio_bpm'), 10, 500, 1, 10))
73
 
        for text, widget in [("_Default:",self.g_default_bpm), ("A_rpeggio:",self.g_arpeggio_bpm)]:
74
 
            label = gtk.Label(text)
75
 
            label.set_alignment(0.0, 0.5)
76
 
            label.set_use_underline(True)
77
 
            tempo_hbox.pack_start(label, expand=False)
78
 
            label.set_mnemonic_widget(widget)
79
 
            tempo_hbox.pack_start(widget, expand=False)
80
 
            label = gtk.Label(_("BPM"))
81
 
            label.set_alignment(0.0, 0.5)
82
 
            label.set_tooltip_text(_("Beats per minute"))
83
 
            tempo_hbox.pack_start(label, expand=True)        
84
 
        category_vbox.pack_start(tempo_hbox, False)
85
 
 
86
 
        box, category_vbox = gu.hig_category_vbox(_("Preferred Instrument"))
87
 
        page_vbox.pack_start(box, False)
88
 
        self.g_instrsel = nInstrumentSelector('config',
89
 
                        'preferred_instrument', sizegroup)
90
 
        category_vbox.pack_start(self.g_instrsel, False)
91
 
 
92
 
        box, category_vbox = gu.hig_category_vbox(_("Chord Instruments"))
93
 
        page_vbox.pack_start(box, False)
94
 
        self.g_instrument_configurator  \
95
 
              = InstrumentConfigurator("config", 3,
96
 
                    _("Use different instruments for chords and harmonic intervals"))
97
 
        category_vbox.pack_start(self.g_instrument_configurator, False)
98
 
 
99
 
        vbox, category_box = gu.hig_category_vbox(_("Percussion Instruments"))
100
 
        page_vbox.pack_start(vbox, False)
101
 
        category_box.pack_start(gu.hig_label_widget(
102
 
            _("Count in:"),
103
 
            gu.PercussionNameButton("config", "countin_perc", "Claves"),
104
 
            sizegroup, True, True))
105
 
        category_box.pack_start(gu.hig_label_widget(
106
 
            _("Rhythm:"),
107
 
            gu.PercussionNameButton("config", "rhythm_perc", "Side Stick"),
108
 
            sizegroup, True, True))
109
 
        ########
110
 
        # user #
111
 
        ########
112
 
        page_vbox = gu.hig_dlg_vbox()
113
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("User")))
114
 
 
115
 
        box, category_vbox = gu.hig_category_vbox(_("User's Vocal Range"))
116
 
        page_vbox.pack_start(box, False)
117
 
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
118
 
 
119
 
        self.g_highest_singable = notenamespinbutton.NotenameSpinButton(
120
 
            self.get_string('user/highest_pitch'))
121
 
        box = gu.hig_label_widget(_("Highest pitch:"),
122
 
                                  self.g_highest_singable,
123
 
                                  sizegroup)
124
 
        category_vbox.pack_start(box)
125
 
 
126
 
        self.g_lowest_singable = notenamespinbutton.NotenameSpinButton(
127
 
            self.get_string('user/lowest_pitch'))
128
 
        box = gu.hig_label_widget(_("Lowest pitch:"),
129
 
                                  self.g_lowest_singable,
130
 
                                  sizegroup)
131
 
        category_vbox.pack_start(box)
132
 
        notenamespinbutton.nNotenameRangeController(
133
 
                  self.g_lowest_singable, self.g_highest_singable,
134
 
                  mpd.LOWEST_NOTENAME, mpd.HIGHEST_NOTENAME,
135
 
                  'user', 'lowest_pitch', 'highest_pitch')
136
 
 
137
 
 
138
 
        box, category_vbox = gu.hig_category_vbox(_("Sex"))
139
 
        page_vbox.pack_start(box, False)
140
 
        self.g_sex_male = gtk.RadioButton(None, _("_Male"))
141
 
        self.g_sex_male.connect('toggled', lambda w: self.set_string('user/sex', 'male'))
142
 
        category_vbox.pack_start(self.g_sex_male, False)
143
 
        self.g_sex_female = gtk.RadioButton(self.g_sex_male, _("_Female or child"))
144
 
        self.g_sex_female.connect('toggled', lambda w: self.set_string('user/sex', 'female'))
145
 
        category_vbox.pack_start(self.g_sex_female, False)
146
 
        if self.get_string('user/sex') == 'female':
147
 
            self.g_sex_female.set_active(True)
148
 
        #####################
149
 
        # external programs #
150
 
        #####################
151
 
        page_vbox = gu.hig_dlg_vbox()
152
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("External Programs")))
153
 
 
154
 
        box, category_vbox = gu.hig_category_vbox(_("Converters"))
155
 
        page_vbox.pack_start(box, False)
156
 
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
157
 
 
158
 
        # midi_to_wav
159
 
        self.g_wav_convertor = gtk.ComboBoxEntry(
160
 
            gtk.ListStore(gobject.TYPE_STRING))
161
 
        self.g_wav_convertor.get_model().append(("timidity",))
162
 
        #
163
 
        self.g_wav_convertor_options = gtk.ComboBoxEntry(
164
 
            gtk.ListStore(gobject.TYPE_STRING))
165
 
        self.g_wav_convertor_options.get_model().append(("-Ow %(in)s -o %(out)s",))
166
 
        #
167
 
        category_vbox.pack_start(
168
 
            gu.hig_label_widget(_("MIDI to WAV:"),
169
 
               (self.g_wav_convertor, self.g_wav_convertor_options),
170
 
               sizegroup, True, True))
171
 
        #
172
 
        self.g_wav_convertor.child.set_text(
173
 
            self.get_string("app/midi_to_wav_cmd"))
174
 
        self.g_wav_convertor_options.child.set_text(
175
 
            self.get_string("app/midi_to_wav_cmd_options"))
176
 
        self.g_wav_convertor.connect('changed',
177
 
            lambda w: self.set_string('app/midi_to_wav_cmd',
178
 
                w.child.get_text()))
179
 
        self.g_wav_convertor_options.connect('changed',
180
 
            lambda w: self.set_string('app/midi_to_wav_cmd_options',
181
 
                w.child.get_text()))
182
 
        # wav_to_mp3
183
 
        self.g_mp3_convertor = gtk.ComboBoxEntry(
184
 
            gtk.ListStore(gobject.TYPE_STRING))
185
 
        self.g_mp3_convertor.get_model().append(("lame",))
186
 
        #
187
 
        self.g_mp3_convertor_options = gtk.ComboBoxEntry(
188
 
            gtk.ListStore(gobject.TYPE_STRING))
189
 
        self.g_mp3_convertor_options.get_model().append(("%(in)s %(out)s",))
190
 
        #
191
 
        category_vbox.pack_start(
192
 
            gu.hig_label_widget(_("WAV to MP3:"),
193
 
            (self.g_mp3_convertor, self.g_mp3_convertor_options),
194
 
            sizegroup, True, True))
195
 
        #
196
 
        self.g_mp3_convertor.child.set_text(
197
 
            self.get_string("app/wav_to_mp3_cmd"))
198
 
        self.g_mp3_convertor_options.child.set_text(
199
 
            self.get_string("app/wav_to_mp3_cmd_options"))
200
 
        self.g_mp3_convertor.connect('changed', 
201
 
            lambda w: self.set_string('app/wav_to_mp3_cmd',
202
 
                w.child.get_text()))
203
 
        self.g_mp3_convertor_options.connect('changed', 
204
 
            lambda w: self.set_string('app/wav_to_mp3_cmd_options',
205
 
                w.child.get_text()))
206
 
        # wav_to_ogg
207
 
        self.g_ogg_convertor = gtk.ComboBoxEntry(
208
 
            gtk.ListStore(gobject.TYPE_STRING))
209
 
        self.g_ogg_convertor.get_model().append(("oggenc",))
210
 
        #
211
 
        self.g_ogg_convertor_options = gtk.ComboBoxEntry(
212
 
            gtk.ListStore(gobject.TYPE_STRING))
213
 
        self.g_ogg_convertor_options.get_model().append(("%(in)s",))
214
 
        #
215
 
        category_vbox.pack_start(
216
 
            gu.hig_label_widget(_("WAV to OGG:"),
217
 
            (self.g_ogg_convertor, self.g_ogg_convertor_options),
218
 
            sizegroup, True, True))
219
 
        #
220
 
        self.g_ogg_convertor.child.set_text(
221
 
            self.get_string("app/wav_to_ogg_cmd"))
222
 
        self.g_ogg_convertor_options.child.set_text(
223
 
            self.get_string("app/wav_to_ogg_cmd_options"))
224
 
        self.g_ogg_convertor.connect('changed', 
225
 
            lambda w: self.set_string('app/wav_to_ogg_cmd',
226
 
                w.child.get_text()))
227
 
        self.g_ogg_convertor_options.connect('changed', 
228
 
            lambda w: self.set_string('app/wav_to_ogg_cmd_options',
229
 
                w.child.get_text()))
230
 
 
231
 
        self.add_gui_for_external_programs(page_vbox)
232
 
        ########
233
 
        # Misc #
234
 
        ########
235
 
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
236
 
 
237
 
        box, category_vbox = gu.hig_category_vbox(_("Miscellaneous"))
238
 
        page_vbox.pack_start(box, False)
239
 
 
240
 
        # CSound_("Musical MIDI Accompaniment:")
241
 
        for bin, label, bins in (
242
 
            ("csound", _("CSound:"), osutils.find_csound_executables()),
243
 
            ("mma", "MMA:", osutils.find_mma_executables(
244
 
                cfg.get_list("app/win32_ignore_drives"))),
245
 
            ("lilypond-book", _("Lilypond-book"),
246
 
             osutils.find_progs(("lilypond-book", "lilypond-book.py"))),
247
 
            ("latex", "Latex:", osutils.find_progs(("latex",))),
248
 
            ):
249
 
            liststore = gtk.ListStore(gobject.TYPE_STRING)
250
 
            for p in bins:
251
 
                liststore.append((p,))
252
 
            cbox_entry = gtk.ComboBoxEntry(liststore)
253
 
            cbox_entry.child.set_text(self.get_string("programs/%(bin)s" % locals()))
254
 
            def csound_changed_cb(widget, bin):
255
 
                self.set_string('programs/%s' % bin, widget.child.get_text())
256
 
                widget.warning.props.visible = not bool(
257
 
                    osutils.find_progs((cfg.get_string('programs/%s' % bin),)))
258
 
            cbox_entry.warning = gtk.Image()
259
 
            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."))
260
 
            cbox_entry.warning.set_from_stock(gtk.STOCK_DIALOG_WARNING,
261
 
                                              gtk.ICON_SIZE_SMALL_TOOLBAR)
262
 
            box = gu.hig_label_widget(label,
263
 
                                      [cbox_entry, cbox_entry.warning],
264
 
                                      sizegroup, True, True)
265
 
            category_vbox.pack_start(box)
266
 
            cbox_entry.warning.props.no_show_all = True
267
 
            csound_changed_cb(cbox_entry, bin)
268
 
            cbox_entry.connect('changed', csound_changed_cb, bin)
269
 
        # Mail program
270
 
        liststore = gtk.ListStore(gobject.TYPE_STRING)
271
 
        for n in osutils.find_progs(('mutt', 'thunderbird', 'evolution')):
272
 
            if os.path.split(n)[1] == 'mutt':
273
 
                n = "xterm -e %s" % n
274
 
            liststore.append((n,))
275
 
        self.g_mail_program = gtk.ComboBoxEntry(liststore)
276
 
        self.g_mail_program.child.set_text(self.get_string("config/mua"))
277
 
        self.g_mail_program.child.connect('changed', lambda w: self.set_string('config/mua', self.g_mail_program.child.get_text()))
278
 
        box = gu.hig_label_widget(_("_Mail:"), self.g_mail_program, sizegroup,
279
 
            True, True)
280
 
        category_vbox.pack_start(box)
281
 
 
282
 
        #######
283
 
        # Gui #
284
 
        #######
285
 
        page_vbox = gu.hig_dlg_vbox()
286
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Interface")))
287
 
 
288
 
        box, category_vbox = gu.hig_category_vbox(_("Interface"))
289
 
        page_vbox.pack_start(box, False)
290
 
 
291
 
        b = gu.nCheckButton('gui', 'web_browser_as_help_browser',
292
 
            _("_Display the user manual in web browser"))
293
 
        category_vbox.pack_start(b, False)
294
 
        self.g_mainwin_user_resizeable = gu.nCheckButton('gui',
295
 
          'mainwin_user_resizeable', _("_Resizeable main window"))
296
 
        category_vbox.pack_start(self.g_mainwin_user_resizeable, False)
297
 
 
298
 
        # Combobox to select language
299
 
        hbox = gtk.HBox()
300
 
        hbox.set_spacing(6)
301
 
        label = gtk.Label()
302
 
        label.set_text_with_mnemonic(_("Select _language:"))
303
 
        hbox.pack_start(label, False)
304
 
        self.g_language = gtk.combo_box_new_text()
305
 
        for n in languages.languages:
306
 
            self.g_language.append_text(n)
307
 
        label.set_mnemonic_widget(self.g_language)
308
 
        if sys.platform == 'win32':
309
 
            lang = winlang.win32_get_langenviron()
310
 
            if lang in languages.languages:
311
 
                idx = languages.languages.index(lang)
312
 
            else:
313
 
                idx = 0
314
 
        else:
315
 
            if cfg.get_string('app/lc_messages') in languages.languages:
316
 
                idx = languages.languages.index(cfg.get_string('app/lc_messages'))
317
 
            else:
318
 
                idx = 0
319
 
        self.g_language.set_active(idx)
320
 
        def f(combobox):
321
 
            cfg.set_string('app/lc_messages', languages.languages[combobox.get_active()])
322
 
            if sys.platform == 'win32':
323
 
                if combobox.get_active():
324
 
                    lang = languages.languages[combobox.get_active()]
325
 
                    winlang.win32_put_langenviron(lang)
326
 
                else:
327
 
                    winlang.win32_put_langenviron(None)
328
 
        self.g_language.connect_after('changed', f)
329
 
        hbox.pack_start(self.g_language, False)
330
 
        category_vbox.pack_start(hbox)
331
 
        l = gtk.Label(_("You have to restart the program for the language change to take effect."))
332
 
        l.set_alignment(0.0, 0.5)
333
 
        category_vbox.pack_start(l)
334
 
 
335
 
        ############
336
 
        # Practise #
337
 
        ############
338
 
        page_vbox = gu.hig_dlg_vbox()
339
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Practise")))
340
 
        box, category_vbox = gu.hig_category_vbox(_("Practise"))
341
 
        page_vbox.pack_start(box, False)
342
 
 
343
 
        self.g_picky_on_new_question = gu.nCheckButton('config', 'picky_on_new_question', _("_Not allow new question before the old is solved"))
344
 
        category_vbox.pack_start(self.g_picky_on_new_question, False)
345
 
 
346
 
        self.g_autorepeat_if_wrong = gu.nCheckButton('config', 'auto_repeat_question_if_wrong_answer', _("_Repeat question if the answer was wrong"))
347
 
        category_vbox.pack_start(self.g_autorepeat_if_wrong, False)
348
 
 
349
 
        self.g_expert_mode = gu.nCheckButton('gui', 
350
 
                'expert_mode', _("E_xpert mode"))
351
 
        self.g_expert_mode.connect('toggled', self.m_app.reset_exercise)
352
 
        category_vbox.pack_start(self.g_expert_mode, False)
353
 
 
354
 
 
355
 
        #########
356
 
        # sound #
357
 
        #########
358
 
        if sys.platform == 'win32':
359
 
            self.create_win32_sound_page()
360
 
        else:
361
 
            self.create_linux_sound_page()
362
 
        self.show_all()
363
 
    def add_gui_for_external_programs(self, page_vbox):
364
 
        box, category_vbox = gu.hig_category_vbox(_("Audio File Players"))
365
 
        page_vbox.pack_start(box, False)
366
 
        format_info = {}
367
 
        if sys.platform != 'win32':
368
 
            format_info = {'wav': {
369
 
                 # testfile is a file in lesson-files/share
370
 
                 'testfile': 'fifth-small-220.00.wav',
371
 
                 'label': _("WAV:"),
372
 
                 # players is a list of tuples. The tuple has to or more 
373
 
                 # items. The first is the binary, and the rest is possible
374
 
                 # sets of command line options that might work.
375
 
                 # '/path/to/player', 'comandline', '
376
 
                 'players': [
377
 
                        ('gst-launch', 'playbin uri=file://%s',
378
 
                                       'filesrc location=%s ! wavparse ! alsasink'),
379
 
                        ('play', ''),
380
 
                        ('aplay', ''),
381
 
                        ('esdplay', ''),
382
 
                 ],
383
 
                }
384
 
            }
385
 
        format_info['midi'] = {
386
 
             'testfile': 'fanfare.midi',
387
 
             'label': _("MIDI:"),
388
 
             'players': [
389
 
                         ('gst-launch', 'playbin uri=file://%s',
390
 
                                        'filesrc location=lesson-files/share/fanfare.midi ! wildmidi ! alsasink'),
391
 
                         ('timidity', '-idqq %s'),
392
 
                         ('drvmidi', ''),
393
 
                         ('playmidi', ''),
394
 
             ],
395
 
            }
396
 
        format_info['mp3'] = {
397
 
             'testfile': 'fanfare.mp3',
398
 
             'label': _("MP3:"),
399
 
             'players': [
400
 
                        ('gst-launch', 'playbin uri=file://%s',
401
 
                                       'filesrc location=%s ! mad ! alsasink'),
402
 
                        ('mpg123', ''),
403
 
                        ('alsaplayer', ''),
404
 
             ],
405
 
            }
406
 
        format_info['ogg'] = {
407
 
             'testfile': 'fanfare.ogg',
408
 
             'label': _("OGG:"),
409
 
             'players': [
410
 
                        ('gst-launch', 'playbin uri=file://%s',
411
 
                                       'filesrc location=%s ! oggdemux ! vorbisdec ! audioconvert ! alsasink'),
412
 
                        ('ogg123', ''), 
413
 
                        ('alsaplayer', ''),
414
 
             ],
415
 
            }
416
 
        sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
417
 
        for formatid, format in format_info.items():
418
 
            cmd_liststore = gtk.ListStore(gobject.TYPE_STRING)
419
 
            for player_data in format['players']:
420
 
                cmd_liststore.append((player_data[0],))
421
 
            cmd_combo = gtk.ComboBoxEntry(cmd_liststore)
422
 
            cmd_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."))
423
 
            cmd_combo.opts = gtk.combo_box_entry_new_text()
424
 
            cmd_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."))
425
 
            cmd_combo.child.set_text(cfg.get_string('sound/%s_player' % formatid))
426
 
            cmd_combo.opts.child.set_text(cfg.get_string('sound/%s_player_options' % formatid))
427
 
            for s in format['players']:
428
 
                if s[0] == cmd_combo.child.get_text():
429
 
                    for o in s[1:]:
430
 
                        cmd_combo.opts.append_text(o)
431
 
            
432
 
            def _changed(widget, formatid):
433
 
                if widget.get_active() != -1:
434
 
                    widget.opts.get_model().clear()
435
 
                    format = format_info[formatid]
436
 
                    for player_options in format['players'][widget.get_active()][1:]:
437
 
                        widget.opts.append_text(player_options)
438
 
                    widget.opts.child.set_text(format['players'][widget.get_active()][1])
439
 
                self.set_string('sound/%s_player' % formatid,
440
 
                                widget.child.get_text())
441
 
                widget.testbutton.set_sensitive(bool(
442
 
                    osutils.find_progs((widget.child.get_text(),))))
443
 
 
444
 
            def _opts_changed(widget, formatid):
445
 
                self.set_string('sound/%s_player_options' % formatid,
446
 
                                widget.child.get_text())
447
 
            cmd_combo.connect('changed', _changed, formatid)
448
 
            cmd_combo.opts.connect('changed', _opts_changed, formatid)
449
 
            testbutton = gtk.Button(_("Test"))
450
 
            testbutton.set_tooltip_text(_("This button is clickable only if the binary is found."))
451
 
            cmd_combo.testbutton = testbutton
452
 
            testbutton.connect('clicked', self.test_XXX_player,
453
 
                           formatid, format['testfile'])
454
 
            testbutton.set_sensitive(bool(
455
 
                    osutils.find_progs((cmd_combo.child.get_text(),))))
456
 
            box = gu.hig_label_widget(format['label'],
457
 
                                      [cmd_combo, cmd_combo.opts, testbutton],
458
 
                                      sizegroup, True, True)
459
 
            category_vbox.pack_start(box)
460
 
    def test_XXX_player(self, w, typeid, testfile):
461
 
        try:
462
 
            soundcard.play_mediafile(typeid, os.path.join('lesson-files/share', testfile))
463
 
        except osutils.BinaryBaseException, e:
464
 
            self.m_app.m_ui.display_error_message2(e.msg1, e.msg2)
465
 
    def create_linux_sound_page(self):
466
 
        page_vbox = gu.hig_dlg_vbox()
467
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("Sound Setup")))
468
 
        #############
469
 
        # midi setup
470
 
        #############
471
 
        box, category_vbox = gu.hig_category_vbox(_("Sound Setup"))
472
 
        page_vbox.pack_start(box)
473
 
 
474
 
        self.g_fakesynth_radio = gu.RadioButton(None, _("_No sound"), None)
475
 
        category_vbox.pack_start(self.g_fakesynth_radio, False)
476
 
 
477
 
        hbox = gu.bHBox(category_vbox, False)
478
 
        self.g_device_radio = gu.RadioButton(self.g_fakesynth_radio,
479
 
              _("Use _device"), None)
480
 
        hbox.pack_start(self.g_device_radio, False)
481
 
 
482
 
        liststore = gtk.ListStore(gobject.TYPE_STRING)
483
 
        for s in ('/dev/sequencer', '/dev/sequencer2', '/dev/music'):
484
 
            liststore.append((s,))
485
 
        self.g_device_file = gtk.ComboBoxEntry(liststore)
486
 
        self.g_device_file.child.set_text(self.get_string('sound/device_file'))
487
 
        self.g_synth_num = gtk.SpinButton(gtk.Adjustment(0, 0, 100, 1, 1),
488
 
                             digits=0)
489
 
        self.g_synth_num.set_value(self.get_int('sound/synth_number'))
490
 
        hbox.pack_start(self.g_device_file, False)
491
 
        hbox.pack_start(self.g_synth_num, False)
492
 
 
493
 
        ###
494
 
        hbox = gu.bHBox(category_vbox, False)
495
 
        self.g_midiplayer_radio = gu.RadioButton(self.g_fakesynth_radio,
496
 
             _("Use _external MIDI player"), None)
497
 
        hbox.pack_start(self.g_midiplayer_radio, False)
498
 
 
499
 
        if self.get_string("sound/type") == "external-midiplayer":
500
 
            self.g_midiplayer_radio.set_active(True)
501
 
        elif self.get_string("sound/type") == "sequencer-device":
502
 
            self.g_device_radio.set_active(True)
503
 
        else:
504
 
            self.g_fakesynth_radio.set_active(True)
505
 
 
506
 
        hbox = gtk.HButtonBox()
507
 
        category_vbox.pack_start(hbox)
508
 
        gu.bButton(category_vbox, _("_Test"), self.on_apply_and_play_test_sound)
509
 
 
510
 
    def create_win32_sound_page(self):
511
 
        page_vbox = gu.hig_dlg_vbox()
512
 
        self.g_notebook.append_page(page_vbox, gtk.Label(_("MIDI Setup")))
513
 
        #############
514
 
        # midi setup
515
 
        #############
516
 
        box, category_vbox = gu.hig_category_vbox(_("Sound Setup"))
517
 
        page_vbox.pack_start(box)
518
 
        txt = gtk.Label(_("""Solfege has two ways to play MIDI files. It is recommended to use Windows multimedia output. An external MIDI player can be useful if your soundcard lacks a hardware synth, in which case you have to use a program like timidity to play the music."""))
519
 
        txt.set_line_wrap(1)
520
 
        txt.set_justify(gtk.JUSTIFY_FILL)
521
 
        txt.set_alignment(0.0, 0.0)
522
 
        category_vbox.pack_start(txt, False)
523
 
 
524
 
        self.g_fakesynth_radio = gu.RadioButton(None, _("_No sound"), None)
525
 
        category_vbox.pack_start(self.g_fakesynth_radio, False)
526
 
 
527
 
        hbox = gu.bHBox(category_vbox, False)
528
 
        self.g_device_radio = gu.RadioButton(self.g_fakesynth_radio,
529
 
              _("_Windows multimedia output:"), None)
530
 
        self.g_synth = gtk.combo_box_new_text()
531
 
        if winmidi:
532
 
            for devname in winmidi.output_devices():
533
 
                #FIXME workaround of the bug
534
 
                # http://code.google.com/p/solfege/issues/detail?id=12
535
 
                if devname is None:
536
 
                    self.g_synth.append_text("FIXME bug #12")
537
 
                else:
538
 
                    self.g_synth.append_text(devname)
539
 
        self.g_synth.set_active(self.get_int('sound/synth_number') + 1)
540
 
        hbox.pack_start(self.g_device_radio, False)
541
 
        hbox.pack_start(self.g_synth, False)
542
 
 
543
 
        hbox = gu.bHBox(category_vbox, False)
544
 
        self.g_midiplayer_radio = gu.RadioButton(self.g_fakesynth_radio,
545
 
             _("Use _external MIDI player"), None)
546
 
        hbox.pack_start(self.g_midiplayer_radio, False)
547
 
 
548
 
        if self.get_string("sound/type") == "external-midiplayer":
549
 
            self.g_midiplayer_radio.set_active(True)
550
 
        elif self.get_string("sound/type") == "winsynth":
551
 
            self.g_device_radio.set_active(True)
552
 
        else:
553
 
            self.g_fakesynth_radio.set_active(True)
554
 
 
555
 
        gu.bButton(category_vbox, _("_Test"), self.on_apply_and_play_test_sound)
556
 
    def set_gui_from_config(self):
557
 
        if self.get_string("sound/type") == "fake-synth":
558
 
            self.g_fakesynth_radio.set_active(True)
559
 
        elif self.get_string("sound/type") == "external-midiplayer":
560
 
            self.g_midiplayer_radio.set_active(True)
561
 
        else:
562
 
            assert self.get_string("sound/type") in ("winsynth", "sequencer-device")
563
 
            self.g_device_radio.set_active(True)
564
 
        return -1
565
 
    def apply_and_close(self, w, response):
566
 
        if response ==  gtk.RESPONSE_DELETE_EVENT:
567
 
            self.set_gui_from_config()
568
 
        elif response == gtk.RESPONSE_HELP:
569
 
            self.m_app.handle_href("preferences-window.html")
570
 
            return
571
 
        else:
572
 
            if self.on_apply() == -1:
573
 
                self.set_gui_from_config()
574
 
                return
575
 
        self.hide()
576
 
    def on_apply_and_play_test_sound(self, *w):
577
 
        if self.on_apply() != -1:
578
 
            self.set_gui_from_config()
579
 
            self.play_midi_test_sound()
580
 
    def play_midi_test_sound(self):
581
 
        try:
582
 
            mpd.play_music(r"""
583
 
            \staff\relative c{
584
 
              c16 e g c e, g c e g, c e g c4
585
 
            }
586
 
            \staff{
587
 
              c4 e g8 e c4
588
 
            }
589
 
            """, 130, 0, 100)
590
 
        # Here we are only cathing exceptions we know the MidiFileSynth
591
 
        # can raise. Maybe we should catch something from the Sequencer
592
 
        # synths too?
593
 
        except osutils.BinaryBaseException, e:
594
 
            self.m_app.m_ui.display_error_message2(e.msg1, e.msg2)
595
 
    def on_apply(self, *v):
596
 
        """Returns -1 if sound init fails."""
597
 
        if soundcard.synth:
598
 
            soundcard.synth.close()
599
 
        if self.g_midiplayer_radio.get_active():
600
 
            soundcard.initialise_external_midiplayer(
601
 
                  cfg.get_string("sound/midi_player"))
602
 
            soundcard.synth.error_report_cb = self.m_app.m_ui.display_error_message
603
 
        elif self.g_device_radio.get_active():
604
 
            try:
605
 
                if sys.platform == 'win32':
606
 
                    soundcard.initialise_winsynth(self.g_synth.get_active() - 1)
607
 
                else:
608
 
                    soundcard.initialise_devicefile(
609
 
                        self.g_device_file.child.get_text(),
610
 
                        self.g_synth_num.get_value_as_int())
611
 
            except (soundcard.SoundInitException, OSError, ImportError), e:
612
 
                self.m_app.display_sound_init_error_message(e)
613
 
                return -1
614
 
        else: # no sound
615
 
            assert self.g_fakesynth_radio.get_active()
616
 
            soundcard.initialise_using_fake_synth(0)
617
 
        if self.g_midiplayer_radio.get_active():
618
 
            self.set_string("sound/type", "external-midiplayer")
619
 
        elif self.g_device_radio.get_active():
620
 
            if sys.platform == "win32":
621
 
                self.set_string("sound/type", "winsynth")
622
 
            else:
623
 
                self.set_string("sound/type", "sequencer-device")
624
 
        else:
625
 
            assert self.g_fakesynth_radio.get_active()
626
 
            self.set_string("sound/type", "fake-synth")
627
 
        if sys.platform != 'win32':
628
 
            self.set_string("sound/device_file", self.g_device_file.child.get_text())
629
 
        if soundcard.synth.m_type_major not in ('Midifile', 'Fake'):
630
 
            self.set_int("sound/synth_number", soundcard.synth.m_devnum)
631
 
            # we set the spin just in case m_devnum was changed by the
632
 
            # soundcard setup code, if it was out of range
633
 
            if sys.platform != 'win32':
634
 
                self.g_synth_num.set_value(soundcard.synth.m_devnum)
635
 
            else:
636
 
                self.g_synth.set_active(soundcard.synth.m_devnum + 1)