This file is indexed.

/usr/share/gcompris/python/piano_composition.py is in gcompris-data 13.11-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
# -*- coding: utf-8 -*-
#  gcompris - piano_composition.py
#
# Copyright (C) 2012 Beth Hadley
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 3 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# piano_composition activity.


import gobject
import gtk
import gtk.gdk
import gcompris
import gcompris.utils
import gcompris.skin
import goocanvas
import pango
import gcompris.sound
import ConfigParser

from gcompris import gcompris_gettext as _

from gcomprismusic import *

class Gcompris_piano_composition:

    def __init__(self, gcomprisBoard):

        # Save the gcomprisBoard, it defines everything we need
        # to know from the core
        self.gcomprisBoard = gcomprisBoard

        self.gcomprisBoard.level = 1
        self.gcomprisBoard.maxlevel = 7

        # Needed to get key_press
        gcomprisBoard.disable_im_context = True

        self._bachEasterEggDone = False
        self._mozartEasterEggDone = False
        self._bananaEasterEggGone = False
        self._gsoc2012EasterEggGone = False

        self.noClefDescription = False

        self.melodyPageToDisplay = 0

        # Used to skip double clicks
        self.record_click_time = 0

    def start(self):
        # write the navigation bar to bottom left corner
        gcompris.bar_set(gcompris.BAR_LEVEL)
        gcompris.bar_set_level(self.gcomprisBoard)
        gcompris.bar_location(20, -1, 0.6)

        self.saved_policy = gcompris.sound.policy_get()
        gcompris.sound.policy_set(gcompris.sound.PLAY_AND_INTERRUPT)
        gcompris.sound.pause()

        # Set a background image
        gcompris.set_default_background(self.gcomprisBoard.canvas.get_root_item())

        # Create our rootitem. We put each canvas item in it so at the end we
        # only have to kill it. The canvas deletes all the items it contains
        # automaticaly.
        self.rootitem = goocanvas.Group(parent=
                                       self.gcomprisBoard.canvas.get_root_item())

        self.display_level(self.gcomprisBoard.level)

    def display_level(self, level):
        '''
        displays level contents.
        All levels: keyboard, staff, write & erase notes, play composition
        1. treble clef only
        2. bass clef only
        3. note duration choice, treble or bass choice
        4. sharp notes, note duration choice, treble or bass choice
        5. flat notes, note duration choice, treble or bass choice
        6. load and save, only sharp notes, note duration choice, treble or bass choice
        (7. , loads melodies)
        '''
        if hasattr(self,'staff'):
            self.staff.eraseAllNotes()

        if self.rootitem:
            self.rootitem.remove()

        self.rootitem = goocanvas.Group(parent=
                                       self.gcomprisBoard.canvas.get_root_item())
        if level == 8:
            self.displayMelodySelection()
            self.noClefDescription = True
            return


        '''
        create staff instance to manage music data
        treble clef image loaded by default; click button to switch to bass clef
        '''

        if level == 2:
            self.staff = BassStaff(370, 185, self.rootitem, 3)
        else:
            self.staff = TrebleStaff(370, 185, self.rootitem, 3)

        self.staff.drawStaff()
        self.staff.dynamicNoteSpacing = True

        clefDescription = keyboardDescription = not self.noClefDescription
        if level == 1:
            clefText = _("This is the Treble clef staff, for high pitched notes")
            keyboardText = _("These are the 8 \"white\" keys in an octave")
        elif level == 2:
            clefText = _("This is the Bass clef staff, for low pitched notes")
            keyboardText = _("These keys form the C Major scale")
        elif level == 3:
            clefText = _("Click on the note symbols to write different length notes")
            keyboardText = _("Notes can be many types, such as quarter notes, half notes, and whole notes")
        elif level == 4:
            clefText = _("Sharp notes have a # sign")
            keyboardText = _("The black keys are sharp and flat keys")
        elif level == 5:
            clefText = _("Flat notes have a b sign")
            keyboardText = _("Each black key has two names, one with a flat and one with a sharp")
        elif level == 6:
            clefText = _("Now you can load melodies from around the world")
            keyboardDescription = False
        elif level == 7:
            clefText = _("Compose music now! You can now load and save your work")
            keyboardDescription = False

        # CLEF DESCRIPTION
        if clefDescription:
            textBox(clefText, 550, 67, self.rootitem, 240,
                    fill_color_rgba = 0x82B22CFFL)

        # KEYBOARD DESCRIPTION
        if keyboardDescription:
            textBox(keyboardText, 200, 430, self.rootitem, 225,
                    fill_color_rgba = 0x639A0066L)

        # ADD BUTTONS

        bx = 500
        if (level == 7):
            bx = 450
        by = 455

        goocanvas.Rect(parent = self.rootitem,
                       x = 430,
                       y = by - 6,
                       width = 280,
                       height = 61,
                       stroke_color = "black",
                       fill_color_rgba = 0x33333366L,
                       line_width = 2.0,
                       radius_x = 3, radius_y = 3)

        self.eraseAllButton = goocanvas.Image(
            parent=self.rootitem,
            pixbuf=gcompris.utils.load_pixmap('piano_composition/edit-clear.svg'),
            x = bx,
            y = by,
            tooltip =  "\n\n" + _("Erase All Notes")
            )
        self.eraseAllButton.connect("button_press_event", self.askAndEraseStaff)
        gcompris.utils.item_focus_init(self.eraseAllButton, None)


        self.eraseNotesButton = goocanvas.Image(
            parent=self.rootitem,
            pixbuf=gcompris.utils.load_pixmap('piano_composition/edit-undo.svg'),
            x = bx + 50,
            y = by,
            tooltip =  "\n\n" + _("Erase Last Notes")
            )
        self.eraseNotesButton.connect("button_press_event", self.eraseOneNote)
        gcompris.utils.item_focus_init(self.eraseNotesButton, None)

        self.playCompositionButton = goocanvas.Image(
            parent=self.rootitem,
            pixbuf=gcompris.utils.load_pixmap('piano_composition/play.svg'),
            x = bx + 100,
            y = by,
            tooltip =  "\n\n" + _("Play Composition")
            )
        self.playCompositionButton.connect("button_press_event", self.playComposition)
        gcompris.utils.item_focus_init(self.playCompositionButton, None)

        if (level > 2):

            self.changeClefButton = goocanvas.Image(
                parent = self.rootitem,
                pixbuf = gcompris.utils.load_pixmap('piano_composition/bassclef_button.svg'),
                x = 90,
                y = 120,
                tooltip =  "\n\n" + _("Erase and Change Clef"),
            )
            self.changeClefButton.connect("button_press_event", self.change_clef_event)
            gcompris.utils.item_focus_init(self.changeClefButton, None)


        if (level >= 3):
            goocanvas.Text(
                parent=self.rootitem,
                x=210, y=140,
                width=100,
                font = gcompris.skin.get_font("gcompris/board/small"),
                text=_("Change Note Type:"),
                fill_color="black", anchor=gtk.ANCHOR_CENTER,
                alignment=pango.ALIGN_CENTER
                )

            x = 260
            y = 115

            # WRITE NOTE BUTTONS TO SELECT NOTE DURATION

            self.eighthNoteSelectedButton = goocanvas.Group(parent=
                                       self.rootitem)

            goocanvas.Image(
                parent=self.eighthNoteSelectedButton,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/quarterNote.png'),
                x=x,
                y=y,
                height=45,
                width=20,
                # TRANSLATORS: http://en.wikipedia.org/wiki/Eighth_note
                tooltip = "\n\n" + _("Eighth Note")
                )

            goocanvas.Image(
              parent=self.eighthNoteSelectedButton,
              pixbuf=gcompris.utils.load_pixmap("piano_composition/flag.png"),
              x=x + 17,
              y=y + 5 ,
              height=33,
              width=10
              )
            self.eighthNoteSelectedButton.connect("button_press_event", self.updateToEighth)
            gcompris.utils.item_focus_init(self.eighthNoteSelectedButton, None)


            self.quarterNoteSelectedButton = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/quarterNote.png'),
                x=x + 35,
                y=y,
                height=45,
                width=20,
                # TRANSLATORS: http://en.wikipedia.org/wiki/Quarter_note
                tooltip = "\n\n" + _("Quarter Note")
                )
            self.quarterNoteSelectedButton.connect("button_press_event", self.updateToQuarter)
            gcompris.utils.item_focus_init(self.quarterNoteSelectedButton, None)

            self.halfNoteSelected = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/halfNote.png'),
                x=x + 62,
                y=y,
                height=45,
                width=20,
                # TRANSLATORS: http://en.wikipedia.org/wiki/Half_note
                tooltip = "\n\n" + _("Half Note")
                )
            self.halfNoteSelected.connect("button_press_event", self.updateToHalf)
            gcompris.utils.item_focus_init(self.halfNoteSelected, None)

            self.wholeNoteSelected = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/wholeNote.png'),
                x=x + 90,
                y=y,
                height=45,
                width=20,
                # TRANSLATORS: http://en.wikipedia.org/wiki/Whole_note
                tooltip = "\n\n" + _("Whole Note")
                )
            self.wholeNoteSelected.connect("button_press_event", self.updateToWhole)
            gcompris.utils.item_focus_init(self.wholeNoteSelected, None)

            # draw focus rectangle around quarter note duration, the default
            self.staff.drawFocusRect(292, 112)

        if (level in [6, 7]):
            self.makeFlatButton = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/blackflat.png'),
                x=175,
                y=410,
                height=40,
                width=30
                )
            self.makeFlatButton.connect("button_press_event", self.change_accidental_type)
            gcompris.utils.item_focus_init(self.makeFlatButton, None)

            self.makeSharpButton = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/blacksharp.png'),
                x=175,
                y=410,
                height=40,
                width=30
                )
            self.makeSharpButton.connect("button_press_event", self.change_accidental_type)
            gcompris.utils.item_focus_init(self.makeSharpButton, None)
            self.makeSharpButton.props.visibility = goocanvas.ITEM_INVISIBLE

            loadSongsButton = TextButton(290, 480, _("Load Music"),
                                              self.rootitem, 0xE768ABFFL)
            loadSongsButton.getBackground().connect("button_press_event",
                                                    self.load_songs_event)

            textBox(_("Change Accidental Style:"), 100, 430, self.rootitem, width=150, noRect=True)

        if (level == 7):
            self.loadButton = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/open.svg'),
                x = bx + 150,
                y = by,
                tooltip = "\n\n" + "Open Composition"
                )
            self.loadButton.connect("button_press_event", self.load_file_event)
            gcompris.utils.item_focus_init(self.loadButton, None)


            self.saveButton = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/save.svg'),
                x = bx + 200,
                y = by,
                tooltip = "\n\n" + "Save Composition"
                )
            self.saveButton.connect("button_press_event", self.save_file_event)
            gcompris.utils.item_focus_init(self.saveButton, None)


        '''
        create piano keyboard for use on every level
        optionally specify to display the "black keys"
        '''
        self.keyboard = PianoKeyboard(50, 180, self.rootitem)
        self.staff.setPianoKeyboard(self.keyboard)

        if level == 5:
            self.keyboard.sharpNotation = False
            self.keyboard.blackKeys = True
        elif level in [4, 6, 7]:
            self.keyboard.blackKeys = True
            self.keyboard.sharpNotation = True

        self.keyboard.draw(300, 200, self.keyboard_click)

        if not (gcompris.get_properties().fx):
            gcompris.utils.dialog(_("Error: This activity cannot be \
played with the\nsound effects disabled.\nGo to the configuration \
dialogue to\nenable the sound."), None)


    def displayMelodySelection(self):
        '''
        parse the text file melodies.txt and display the melodies for selection
        '''

        self.noClefDescription = True

        item = goocanvas.Text(parent=self.rootitem,
         x=290,
         y=30,
         text= _('Select A Melody to Load'),
         font = gcompris.skin.get_font("gcompris/fontface") + " bold 15000",
         fill_color="black",
         use_markup=True,
         pointer_events="GOO_CANVAS_EVENTS_NONE"
         )
        bg = self.createBgForItem(item, 0x33AA3366L)
        bg.lower(None)

        self.read_data()

        bx = 680
        by = 470
        item = goocanvas.Image(
            parent=self.rootitem,
            pixbuf=gcompris.utils.load_pixmap('piano_composition/previous.svg'),
            x = bx,
            y = by,
            tooltip = "\n\n" + _("Previous Page")
            )
        item.connect("button_press_event", self.previousMelodyPage)
        gcompris.utils.item_focus_init(item, None)

        item = goocanvas.Image(
            parent=self.rootitem,
            pixbuf=gcompris.utils.load_pixmap('piano_composition/next.svg'),
            x = bx + 50,
            y = by,
            tooltip = "\n\n" + _("Next Page")
            )
        item.connect("button_press_event", self.nextMelodyPage)
        gcompris.utils.item_focus_init(item, None)


        self.writeDataToScreen()

    def previousMelodyPage(self, x=None,y=None,z=None):
        self.melodyPageToDisplay -= 1
        self.display_level(8)

    def nextMelodyPage(self, x=None,y=None,z=None):
        self.melodyPageToDisplay += 1
        self.display_level(8)

    def writeDataToScreen(self):

        def displayTitle(section, x, y, bgcolor):
            newRoot = goocanvas.Group(parent=self.rootitem)
            self.text = goocanvas.Text(
                parent=newRoot,
                x=x, y=y,
                font = gcompris.skin.get_font("gcompris/board/small"),
                text= self.data.get(section, 'title'),
                fill_color="black",
                use_markup=True
                )

            self.origin = goocanvas.Text(parent=newRoot,
                 x=x + 30,
                 y=y + 18,
                 width=250,
                 font = gcompris.skin.get_font("gcompris/board/tiny"),
                 # FIXME Should remove the space in the data file instead
                 text= _(" " + self.data.get(section, '_origin')),
                 fill_color="black",
                 use_markup=True
                 )
            bg = self.createBgForItem(newRoot, bgcolor, 380)
            bg.lower(None)

            self.text.connect("button_press_event", self.melodySelected, section)
            self.origin.connect("button_press_event", self.melodySelected, section)
            bg.connect("button_press_event", self.melodySelected, section)
            gcompris.utils.item_focus_init(self.text, bg)
            gcompris.utils.item_focus_init(self.origin, bg)
            gcompris.utils.item_focus_init(bg, None)

        x = 20
        y = 75

        nb_title_by_page = 16
        nb_title = len(self.data.sections())

        # Manage page wrapping
        if self.melodyPageToDisplay < 0:
            self.melodyPageToDisplay = nb_title / nb_title_by_page
        elif self.melodyPageToDisplay >  nb_title / nb_title_by_page:
            self.melodyPageToDisplay = 0


        lower = self.melodyPageToDisplay * nb_title_by_page
        upper = self.melodyPageToDisplay * nb_title_by_page + nb_title_by_page
        index = 0
        bgcolor = None
        for section in self.data.sections()[lower:upper]:
            index += 1
            if index % 2 == 0:
                bgcolor = 0xAA333366L
            else:
                bgcolor = 0x33AAAA66L
            displayTitle(section, x, y, bgcolor)
            if y > 400:
                y = 75
                x += 390
            else:
                y += 48

    def read_data(self):
            '''
            method to read in the data from melodies.desktop.in. Saves this data as
            self.data for reference later.
            '''
            #self.data = ConfigParser.RawConfigParser() # the data that is parsed from
            config = ConfigParser.RawConfigParser()
            filename = gcompris.DATA_DIR + '/' + self.gcomprisBoard.name + '/melodies.desktop.in'
            try:
                gotit = config.read(filename)
                if not gotit:
                    gcompris.utils.dialog(_("Cannot find the file '{filename}'").\
                                        format(filename=filename),
                                    None)
                    return False
            except ConfigParser.Error, error:
                    gcompris.utils.dialog(_("Failed to parse data set '{filename}'"
                                      " with error:\n{error}").\
                                      format(filename=filename, error=error),
                                    None)
                    return False

            self.data = config


    def createBgForItem(self, item, color, width=None):
        '''
        Called with an item, get its bounds and create a grey rectangle around it
        '''
        bounds = item.get_bounds()
        gap = 5

        if not width:
            width = bounds.x2 - bounds.x1 + gap * 2

        return goocanvas.Rect(parent = self.rootitem,
                              x = bounds.x1 - gap,
                              y = bounds.y1 - gap,
                              width = width,
                              height = bounds.y2 - bounds.y1 + gap * 2,
                              stroke_color = "black",
                              fill_color_rgba = color,
                              line_width = 2.0,
                              radius_x = 3, radius_y = 3)


    def melodySelected(self, widget, target, event, section):
        '''
        called once a melody has been selected
        writes the melody to the staff, and displays the title and lyrics
        '''
        self.display_level(self.gcomprisBoard.level)
        self.staff.stringToNotation(self.data.get(section, 'melody'))
        rootitem = self.staff.lyrics_rootitem
        item = goocanvas.Text(parent = rootitem,
                       x=150,
                       y=15,
                       width=280,
                       font = gcompris.skin.get_font("gcompris/board/medium"),
                       text = self.data.get(section, 'title'),
                       fill_color="black",
                       use_markup=True,
                       alignment=pango.ALIGN_CENTER,
                       anchor = gtk.ANCHOR_N,
                       pointer_events="GOO_CANVAS_EVENTS_NONE"
                       )
        bg = self.createBgForItem(item, 0xAA333366L)
        bg.lower(None)

        lyrics = self.data.get(section, 'lyrics').replace('\\n', '\n')
        item = goocanvas.Text(parent = rootitem,
                       x = 400,
                       y = 15,
                       width = 280,
                       text = lyrics,
                       font = gcompris.skin.get_font("gcompris/board/small"),
                       fill_color = "black",
                       pointer_events = "GOO_CANVAS_EVENTS_NONE"
                       )
        bg = self.createBgForItem(item, 0x33AA3366L)
        bg.lower(None)

        item = goocanvas.Text(parent = rootitem,
                       x=150,
                       y=75,
                       width=280,
                       text= _(" " + self.data.get(section, '_origin')),
                       fill_color="black",
                       use_markup=True,
                       alignment=pango.ALIGN_CENTER,
                       anchor = gtk.ANCHOR_N,
                       font = gcompris.skin.get_font("gcompris/board/small"),
                       pointer_events="GOO_CANVAS_EVENTS_NONE"
                       )
        bg = self.createBgForItem(item, 0x3333AA66L)
        bg.lower(None)


    def load_songs_event(self, widget, target, event):
        #self.staff.eraseAllNotes()
        self.display_level(8)

    def save_file_event(self, widget, target, event):
        '''
        method called when 'save' button is pressed
        opens the gcompris file selector to save the music
        calls the general_save function, passes self.staff
        '''
        gcompris.file_selector_save(self.gcomprisBoard,
                                   'comp', '.txt',
                                   general_save, self.staff)
    def load_file_event(self, widget, target, event):
        '''
        method called when 'load' button is pressed
        opens the gcompris file selector to load the music
        calls the general_load function
        '''
        gcompris.file_selector_load(self.gcomprisBoard,
                                           'comp', '.gcmusic',
                                           general_load, self.staff)

    def change_clef_event(self, widget, target, event):
        '''
        method called when button to change clef is pressed
        load in the appropriate new staff image
        re-synchronize all buttons because rootitem changes
        all notes in staff are removed...this can be changed later
        if desired, but could confuse children if more than one clef
        exists in the piece
        '''
        current_note_type = self.staff.currentNoteType
        self.staff.clear()
        if hasattr(self.staff, 'newClef'):
            self.staff.newClef.clear()
        if self.staff.staffName == "trebleClef":
            self.changeClefButton.props.pixbuf = gcompris.utils.load_pixmap('piano_composition/trebbleclef_button.svg')
            self.staff = BassStaff(370, 185, self.rootitem, 3)
            self.staff.drawStaff()
            self.staff.dynamicNoteSpacing = True
        else:
            self.changeClefButton.props.pixbuf = gcompris.utils.load_pixmap('piano_composition/bassclef_button.svg')
            self.staff = TrebleStaff(370, 185, self.rootitem, 3)
            self.staff.drawStaff()
            self.staff.dynamicNoteSpacing = True
        self.staff.currentNoteType = current_note_type


    def eraseOneNote(self, unused1, unused2, unused3):
        self.staff.eraseOneNote()

    def playComposition(self, unused1, unused2, unused3):
        self.staff.playComposition()

    def updateToEighth(self, unused1, unused2, unused3):
        self.staff.updateToEighth()

    def updateToQuarter(self, unused1, unused2, unused3):
        self.staff.updateToQuarter()

    def updateToHalf(self, unused1, unused2, unused3):
        self.staff.updateToHalf()

    def updateToWhole(self, unused1, unused2, unused3):
        self.staff.updateToWhole()

    def askAndEraseStaff(self, unused1=None, unused2=None, unused3=None):
        # @FIXME, should have a dialog asking for confirmation
        self.staff.eraseAllNotes()

    def erase(self,x,y,z):
        #self.staff.eraseAllNotes()
        eraseUserPrompt(None, None, None,self)

    def change_accidental_type(self, widget, target, event):
        self.keyboard.sharpNotation = not self.keyboard.sharpNotation
        self.keyboard.draw(300, 200, self.keyboard_click)
        if self.keyboard.sharpNotation:
            self.makeSharpButton.props.visibility = goocanvas.ITEM_INVISIBLE
            self.makeFlatButton.props.visibility = goocanvas.ITEM_VISIBLE
        else:
            self.makeSharpButton.props.visibility = goocanvas.ITEM_VISIBLE
            self.makeFlatButton.props.visibility = goocanvas.ITEM_INVISIBLE

    def keyboard_click(self, widget, target, event, numID=None):
        '''
        called whenever a key rectangle is pressed; a note object is created
        with a note name, text is output to canvas, the note sound is generated,
        and the note is drawn on the staff
        '''
        # Skip Double clicks
        if event:
            if event.type == gtk.gdk._2BUTTON_PRESS:
                return True
            if event.time - self.record_click_time <= 200:
                self.record_click_time = event.time
                return True
            self.record_click_time = event.time



        if hasattr(self.staff, 'locked') and self.staff.locked:
            return
        if not numID:
            numID = target.numID
        if numID < 0 and self.gcomprisBoard.level < 4:
            return

        self.keyboard.highlightKey(numID)

        if self.staff.currentNoteType == 4:
            n = QuarterNote(numID, self.staff.staffName, self.staff.rootitem, self.keyboard.sharpNotation)
        elif self.staff.currentNoteType == 2:
            n = HalfNote(numID, self.staff.staffName, self.staff.rootitem, self.keyboard.sharpNotation)
        elif self.staff.currentNoteType == 1:
            n = WholeNote(numID, self.staff.staffName, self.staff.rootitem, self.keyboard.sharpNotation)
        elif self.staff.currentNoteType == 8:
            n = EighthNote(numID, self.staff.staffName, self.staff.rootitem, self.keyboard.sharpNotation)

        self.staff.drawNote(n)
        n.play()
        n.enablePlayOnClick()

        self.checkForEasterEgg()
        return False

    def checkForEasterEgg(self):
        '''
        Note From Beth:
        At GUADEC, I was talking to one of the organizers of GNOME-GSOC and he
        said that what he really missed about GNOME was all the fun easter eggs it
        used to have....apparently Cheese had a fun one, and lots of other cool things.
        So he actually encouraged us to put some in ourselves ;-) So here are mine,
        they're pretty rare so I don't expect many people to find them
        but I think they're legitimate and shouldn't be taken out.

        If you're reading this to figure out what the easter eggs are...then you
        obviously have an interest in music and code. why not code a music activyt
        for GCompris? I'll help you...just email me.

        And now, the easter egges are:

        if you enter the notes (on the American keyboard) Bb/A# - A - C - B or (on the
        German keyboard) B - A - C - H, a picture of Bach's statue in front of his
        cathedral in Leipzig will appear. This is a picture I took during the summer
        I was coding this activity, and I had the pleasure to visit Leipzig in Germany

        if you enter the following (note the note name is first, followed by thr rhythm length
        (4=quarter, 8=eighth, 2=half, 1= whole)

        B4 A4 Ab4 A4 Ab4 A4 , a banana will appear to fly across the screen. this is
        a joke from GUADEC...if you're really curious, email bethmhadley@gmail.com ;-)

        if you enter

        G2 E2 F8 A8 G8 F8 E8 E2 F4 D8 F4 D8

        then you'll see picture of the inside of a cathedral, with a girl standing
        on the right. Do you know who it is and where? It's the inside of a cathedral in
        Salzburg, Mozart's birthplace. Mozart served as music master here for a few years
        (because he was forced to leave! ;-) The girl, by the way, is me...I was lucky
        enough to visit Salzurg during the summer I spent coding this activity, and took
        this picture.

        and if you enter

        C4 C8 C8 C E4 E8 E8 E4 G4 G8 G8 G4

        you'll see the Google Summer of Code Banner move across the screen. This is in
        recognition of the wonderful program that enabled me to make this summer project
        a possibility. I really enjoyed working this summer on this code, and I am very
        grateful to my mentor, Bruno Coudoin, for his support and assistance in this process.
        I'm also thankful to the entire GUADEC and GCompris communities for their ideas, support,
        and encouragement.

        Thanks everyone, and enjoy!

        '''
        s = r = ''
        for x in self.staff.noteList:
            s += str(x.numID)
            r += str(x.numID) + str(x.noteType)

        if not self._bachEasterEggDone and '-5617' in s:
            responsePic = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/bach.jpg'),
                x=250,
                y=50
                )
            gobject.timeout_add(2000, lambda: responsePic.remove() )
            self._bachEasterEggDone = True
        if not self._mozartEasterEggDone and '523248685848383244284428' in r:
            responsePic = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/mozart.jpg'),
                x=160,
                y=80
                )
            gobject.timeout_add(2000, lambda: responsePic.remove() )
            self._mozartEasterEggDone = True
        if not self._bananaEasterEggGone and '7464-4464-4464' in r:
            responsePic = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/banana.png'),
                x=200,
                y=10
                )
            responsePic.animate(5, 5, 3.0, 0.0, \
                absolute=False, duration=800, step_time=100, type=goocanvas.ANIMATE_FREEZE)

            gobject.timeout_add(800, lambda: responsePic.remove() )

            self._bananaEasterEggGone = True
        if not self._gsoc2012EasterEggGone and '14181814343838345458585481' in r:
            responsePic = goocanvas.Image(
                parent=self.rootitem,
                pixbuf=gcompris.utils.load_pixmap('piano_composition/gsoc2012.jpg'),
                x= -20,
                y=10
                )
            self.responsePic.animate(0, 400, 1, 0, \
                absolute=False, duration=1000, step_time=100, type=goocanvas.ANIMATE_FREEZE)

            gobject.timeout_add(1000, lambda: responsePic.remove() )
            self._gsoc2012EasterEggGone = True

    def end(self):
        # Remove the root item removes all the others inside it
        self.staff.eraseAllNotes()
        self.rootitem.remove()
        gcompris.sound.policy_set(self.saved_policy)
        gcompris.sound.resume()

    def ok(self):
        pass

    def repeat(self):
        pass

    #mandatory but unused yet
    def config_stop(self):
        pass

    # Configuration function.
    def config_start(self, profile):
        pass

    def key_press(self, keyval, commit_str, preedit_str):

        utf8char = gtk.gdk.keyval_to_unicode(keyval)
        if keyval == gtk.keysyms.BackSpace:
            self.staff.eraseOneNote()
        elif keyval == gtk.keysyms.Delete:
            self.askAndEraseStaff()
        elif keyval == gtk.keysyms.space:
            self.staff.playComposition()
        else:
            pianokeyBindings(keyval, self.keyboard_click)
        return True
    def pause(self, x):
        pass

    def set_level(self, level):
        '''
        updates the level for the game when child clicks on bottom
        left navigation bar to increment level
        '''
        self.noClefDescription = False
        self.gcomprisBoard.level = level
        gcompris.bar_set_level(self.gcomprisBoard)
        self.display_level(self.gcomprisBoard.level)

def general_save(filename, filetype, staffInstance):
    '''
    called after file selection to save
    '''
    staffInstance.staff_to_file(filename)

def general_load(filename, filetype, staffInstance):
    '''
    called after file selection to load
    '''
    staffInstance.file_to_staff(filename)