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

« back to all changes in this revision

Viewing changes to mpd/engravers.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, 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 operator
18
 
 
19
 
import gtk
20
 
import pango
21
 
 
22
 
import const
23
 
import mpdutils
24
 
 
25
 
fetadir = "feta"
26
 
class dim20:
27
 
    linespacing = 8
28
 
    stemlen = linespacing * 3.5
29
 
    xshift = 10
30
 
    col_width = 20
31
 
    first_staff_ypos = 80
32
 
    staff_spacing = 100
33
 
    ledger_left = -4
34
 
    ledger_right = 14
35
 
    accidental_widths = { -2: 12, -1: 7, 0: 5, 1: 8, 2: 8}
36
 
    clef_yoffset = {
37
 
        'G': -38 + 3 * linespacing,
38
 
        'F':  -8 + 3 * linespacing,
39
 
        'C': -15 + 3 * linespacing,
40
 
    }
41
 
 
42
 
dimentions = {20: dim20}
43
 
 
44
 
accidental_y_offset = {const.ACCIDENTAL__2: -7,
45
 
                       const.ACCIDENTAL__1: -7,
46
 
                       const.ACCIDENTAL_0: -5,
47
 
                       const.ACCIDENTAL_1: -6,
48
 
                       const.ACCIDENTAL_2: -2}
49
 
 
50
 
class Engraver:
51
 
    def __init__(self, timepos, fontsize):
52
 
        self.m_fontsize = 20
53
 
        self.m_timepos = timepos
54
 
    def get_width(self):
55
 
        """
56
 
        Get the width of all engravers that does not implement their
57
 
        own get_width function.
58
 
        """
59
 
        return 20
60
 
    def __str__(self):
61
 
        return '(Engraver)'
62
 
 
63
 
 
64
 
class ClefEngraver(Engraver):
65
 
    def __init__(self, timepos, fontsize, clef):
66
 
        Engraver.__init__(self, timepos, fontsize)
67
 
        self.m_clef = clef
68
 
    def set_xpos(self, dict):
69
 
        self.m_xpos = dict[self.m_timepos].m_clef
70
 
    def get_width(self):
71
 
        return 25
72
 
    def engrave(self, widget, gc, staff_yoffset):
73
 
        widget.window.draw_pixbuf(gc,
74
 
            gtk.gdk.pixbuf_new_from_file(fetadir+'/feta%i-clefs-%s.xpm' \
75
 
                % (self.m_fontsize, self.m_clef.get_symbol())),
76
 
            0, 0, self.m_xpos,
77
 
            dimentions[self.m_fontsize].clef_yoffset[self.m_clef.get_symbol()] - self.m_clef.get_stafflinepos() * dimentions[self.m_fontsize].linespacing\
78
 
                + staff_yoffset)
79
 
 
80
 
class TimeSignatureEngraver(Engraver):
81
 
    def __init__(self, timepos, fontsize, timesig):
82
 
        Engraver.__init__(self, timepos, fontsize)
83
 
        self.m_timesig = timesig
84
 
    def set_xpos(self, dict):
85
 
        self.m_xpos = dict[self.m_timepos].m_timesignature
86
 
    def engrave(self, widget, gc, staff_yoffset):
87
 
        x = 0
88
 
        for idx in range(len(str(self.m_timesig.m_num))):
89
 
            n = str(self.m_timesig.m_num)[idx]
90
 
            widget.window.draw_pixbuf(gc,
91
 
                gtk.gdk.pixbuf_new_from_file(fetadir+'/feta%i-number-%s.xpm' \
92
 
                    % (self.m_fontsize, n)),
93
 
                0, 0,
94
 
                self.m_xpos+x, staff_yoffset-12)
95
 
            x  += 10
96
 
        x = 0
97
 
        for idx in range(len(str(self.m_timesig.m_den))):
98
 
            n = str(self.m_timesig.m_den)[idx]
99
 
            widget.window.draw_pixbuf(gc,
100
 
                gtk.gdk.pixbuf_new_from_file(fetadir+'/feta%i-number-%s.xpm' %\
101
 
                    (self.m_fontsize, n)),
102
 
                0, 0,
103
 
                self.m_xpos+x, staff_yoffset+3)
104
 
            x += 10
105
 
    def __str__(self):
106
 
        return '(TimeSignatureEngraver:%i/%i, xpos:%s)' % (self.m_timesig.m_den,
107
 
            self.m_timesig.m_num, self.m_xpos)
108
 
 
109
 
 
110
 
class TieEngraver(Engraver):
111
 
    def __init__(self, fontsize, pos1, pos2, shift1, shift2, ylinepos):
112
 
        Engraver.__init__(self, None, fontsize)
113
 
        self.m_pos1 = pos1
114
 
        self.m_pos2 = pos2
115
 
        self.m_shift1 = shift1
116
 
        self.m_shift2 = shift2
117
 
        self.m_ylinepos = ylinepos
118
 
    def set_xpos(self, dict):
119
 
        dim = dimentions[self.m_fontsize]
120
 
        self.m_xpos = dict[self.m_pos1].m_music
121
 
        self.m_xpos2 = dict[self.m_pos2].m_music
122
 
        self.m_xpos += 3 + (self.m_shift1 + 1) * dim.xshift
123
 
        self.m_xpos2 += - 3 + self.m_shift2 * dim.xshift
124
 
    def engrave(self, widget, gc, staff_yoffset):
125
 
        dim = dimentions[self.m_fontsize]
126
 
        w = self.m_xpos2 - self.m_xpos
127
 
        h = 8
128
 
        widget.window.draw_arc(gc, False,
129
 
              self.m_xpos, staff_yoffset + dim.linespacing*(self.m_ylinepos-1)/2 + 3,
130
 
              w, h, 64 * 180, 64*180)
131
 
 
132
 
 
133
 
class AccidentalsEngraver(Engraver):
134
 
    def __init__(self, timepos, fontsize, accs):
135
 
        Engraver.__init__(self, timepos, fontsize)
136
 
        self.m_accs = accs
137
 
        def f(v, w=dimentions[fontsize].accidental_widths):
138
 
            x = 0
139
 
            for a in v:
140
 
                x += w[a]
141
 
            return x
142
 
        self.m_usize = reduce(operator.__add__, map(f, accs.values())) + len(accs)
143
 
    def set_xpos(self, dict, tpd):
144
 
        self.m_xpos = dict[self.m_timepos].m_accidentals + tpd[self.m_timepos].m_accidentals - self.get_width()
145
 
    def get_width(self):
146
 
        return self.m_usize
147
 
    def engrave(self, widget, gc, staff_yoffset):
148
 
        x = 0
149
 
        for y in self.m_accs:
150
 
            for acc in self.m_accs[y]:
151
 
                widget.window.draw_pixbuf(gc,
152
 
                    gtk.gdk.pixbuf_new_from_file(
153
 
                        fetadir+'/feta%i-accidentals-%i.xpm' % \
154
 
                            (self.m_fontsize, acc)),
155
 
                        0, 0,
156
 
                        self.m_xpos + x,
157
 
                        int(accidental_y_offset[acc] + staff_yoffset
158
 
                         + dimentions[self.m_fontsize].linespacing*y/2
159
 
                         + accidental_y_offset[acc]))
160
 
                x += dimentions[self.m_fontsize].accidental_widths[acc] + 1
161
 
 
162
 
 
163
 
class KeySignatureEngraver(Engraver):
164
 
    def __init__(self, timepos, fontsize, old_key, key, clef):
165
 
        Engraver.__init__(self, timepos, fontsize)
166
 
        self.m_old_key = old_key
167
 
        self.m_key = key
168
 
        self.m_clef = clef
169
 
        self.m_old_accidentals = mpdutils.key_to_accidentals(old_key)
170
 
        self.m_accidentals = mpdutils.key_to_accidentals(key)
171
 
    def set_xpos(self, dict):
172
 
        self.m_xpos = dict[self.m_timepos].m_keysignature
173
 
    def get_width(self):
174
 
        #FIXME this value depends on the fontsize, and on what kind of
175
 
        #accidental we are drawing. A sharp is wider that a flat.
176
 
        return len(self.m_accidentals) * 10 + len(self.m_old_accidentals) * 6 + 8
177
 
    def engrave(self, widget, gc, staff_yoffset):
178
 
        x = 0
179
 
        dolist = []
180
 
        # natural signs
181
 
        for acc in self.m_old_accidentals:
182
 
            if acc in self.m_accidentals:
183
 
                continue
184
 
            ypos = self.m_clef.an_to_ylinepos(acc)
185
 
            dolist.append((0, x, ypos))
186
 
            #FIXME see FIXME msg in .get_width
187
 
            x += 6
188
 
        # accidentals
189
 
        for acc in self.m_accidentals:
190
 
            if acc.endswith('eses'):
191
 
                ktype = -2
192
 
            elif acc.endswith('es'):
193
 
                ktype = -1
194
 
            elif acc.endswith('isis'):
195
 
                ktype = 2
196
 
            else:
197
 
                ktype = 1
198
 
            ypos = self.m_clef.an_to_ylinepos(acc)
199
 
            dolist.append((ktype, x, ypos))
200
 
            #FIXME see FIXME msg in .get_width
201
 
            x += 10
202
 
        for ktype, x, ypos in dolist:
203
 
            widget.window.draw_pixbuf(gc,
204
 
                gtk.gdk.pixbuf_new_from_file(
205
 
                    fetadir+'/feta%i-accidentals-%i.xpm' \
206
 
                    % (self.m_fontsize, ktype)),
207
 
                0, 0,
208
 
                self.m_xpos + x,
209
 
                int(accidental_y_offset[ktype] + staff_yoffset
210
 
                     + dimentions[self.m_fontsize].linespacing*ypos/2
211
 
                     + accidental_y_offset[ktype]))
212
 
 
213
 
 
214
 
class NoteheadEngraver(Engraver):
215
 
    def __init__(self, timepos, fontsize, shift, ypos, head, numdots, midi_int, mgroup):
216
 
        """
217
 
        m_ypos == 0 is the middle line on the staff.
218
 
        Negative value is up, positive is down.
219
 
        The value counts notesteps, not pixels!
220
 
        FIXME, right now it also draws dots, but they should be an own class
221
 
        because the dots has to be handled special with noteheads are xshifted.
222
 
        """
223
 
        Engraver.__init__(self, timepos, fontsize)
224
 
        self.m_head = head
225
 
        self.m_shift = shift
226
 
        self.m_numdots = numdots
227
 
        self.m_ypos = ypos
228
 
        self.m_midi_int = midi_int
229
 
        self.m_mgroup = mgroup
230
 
    def set_xpos(self, dict):
231
 
        self.m_xpos = dict[self.m_timepos].m_music
232
 
    def engrave(self, widget, gc, staff_yoffset):
233
 
        dim = dimentions[self.m_fontsize]
234
 
        # Have to adjust holenotes a little to the left to be on the middle
235
 
        # of the ledger line.
236
 
        if self.m_head == 0:
237
 
            xx = -2
238
 
        else:
239
 
            xx = 0
240
 
        widget.window.draw_pixbuf(gc, 
241
 
            gtk.gdk.pixbuf_new_from_file(fetadir+'/feta%i-noteheads-%i.xpm' \
242
 
                % (self.m_fontsize, self.m_head)),
243
 
            0, 0,
244
 
            self.m_xpos + self.m_shift * dim.xshift + xx,
245
 
            int(staff_yoffset + dim.linespacing*self.m_ypos/2 - 4))
246
 
        for n in range(self.m_numdots):
247
 
            widget.window.draw_pixbuf(gc,
248
 
                gtk.gdk.pixbuf_new_from_file(fetadir+'/feta20-dots-dot.xpm'),
249
 
                0, 0,
250
 
                int(self.m_xpos+dim.xshift*(self.m_shift+1.5+n/2.0)),
251
 
                -3 + staff_yoffset + dim.linespacing*self.m_ypos/2)
252
 
    def __str__(self):
253
 
        return "(NoteheadEngraver: xpos:%i, ypos:%i, head:%i)" % (self.m_xpos,
254
 
                                                                    self.m_ypos,
255
 
                                                                    self.m_head)
256
 
 
257
 
class BarlineEngraver(Engraver):
258
 
    def __init__(self, timepos, fontsize, barline_type):
259
 
        Engraver.__init__(self, timepos, fontsize)
260
 
        self.m_type = barline_type
261
 
    def set_xpos(self, dict):
262
 
        self.m_xpos = dict[self.m_timepos].m_barline
263
 
    def get_width(self):
264
 
        return 8
265
 
    def engrave(self, widget, gc, staff_yoffset):
266
 
        dim = dimentions[self.m_fontsize]
267
 
        widget.window.draw_line(gc, self.m_xpos, staff_yoffset - dim.linespacing*2,
268
 
                                    self.m_xpos, staff_yoffset + dim.linespacing*2)
269
 
 
270
 
class TupletEngraver(Engraver):
271
 
    def __init__(self, fontsize, n):
272
 
        Engraver.__init__(self, None, fontsize)
273
 
        self.m_stems = []
274
 
        self.m_den, self.m_direction = n
275
 
    def set_xpos(self, dict):
276
 
        pass
277
 
    def add_stem(self, stem):
278
 
        self.m_stems.append(stem)
279
 
    def do_layout(self):
280
 
        dim = dimentions[self.m_fontsize]
281
 
        top = []
282
 
        bottom = []
283
 
        for s in self.m_stems:
284
 
            if s.m_mgroup.m_stemdir == const.UP:
285
 
                top.append(min(s.m_yposes)-7)
286
 
                bottom.append(max(s.m_yposes)+1)
287
 
            else:
288
 
                top.append(min(s.m_yposes))
289
 
                bottom.append(max(s.m_yposes)+5)
290
 
        self.m_t = min(top) - 2
291
 
        self.m_b = max(bottom) + 2
292
 
        self.m_xpos1 = self.m_stems[0].m_xpos
293
 
        self.m_xpos2 = self.m_stems[-1].m_xpos + dim.xshift
294
 
    def engrave(self, widget, gc, staff_yoffset):
295
 
        dim = dimentions[self.m_fontsize]
296
 
        m = self.m_xpos1 + (self.m_xpos2 - self.m_xpos1)/2
297
 
        x1 = m - 6
298
 
        x2 = m + 6
299
 
        if self.m_direction == const.UP or self.m_direction == const.BOTH:
300
 
            y = min(staff_yoffset - dim.linespacing * 3,
301
 
                   staff_yoffset + self.m_t * dim.linespacing / 2)
302
 
            d = 1
303
 
        else: # == const.DOWN
304
 
            y = max(staff_yoffset + dim.linespacing * 5,
305
 
                    staff_yoffset + self.m_b * dim.linespacing /2)
306
 
            d = -1
307
 
        widget.window.draw_line(gc, self.m_xpos1, y, x1, y)
308
 
        widget.window.draw_line(gc, x2, y, self.m_xpos2, y)
309
 
        widget.window.draw_line(gc, self.m_xpos1, y, self.m_xpos1, y+5*d)
310
 
        widget.window.draw_line(gc, self.m_xpos2, y, self.m_xpos2, y+5*d)
311
 
        cc = widget.create_pango_context()
312
 
        ll = pango.Layout(cc)
313
 
        ll.set_text(str(self.m_den))
314
 
        ll.set_font_description(pango.FontDescription("Sans 10"))
315
 
        sx, sy = [i / pango.SCALE for i in ll.get_size()]
316
 
        widget.window.draw_layout(gc, m - sx / 2, y - sy / 2, ll)
317
 
 
318
 
class BeamEngraver(Engraver):
319
 
    def __init__(self, fontsize):
320
 
        Engraver.__init__(self, None, fontsize)
321
 
        self.m_stems = []
322
 
    def add_stem(self, stem_engraver):
323
 
        self.m_stems.append(stem_engraver)
324
 
    def set_xpos(self, dict):
325
 
        pass
326
 
    def do_layout(self):
327
 
        self.decide_beam_stemdir()
328
 
        self.set_stemlens(self.find_lowhigh_ypos())
329
 
    def set_stemlens(self, lh):
330
 
        l, h = lh
331
 
        for e in self.m_stems:
332
 
            if self.m_stemdir == const.UP:
333
 
                e.m_beamed_stem_top = l - 6
334
 
            else:
335
 
                assert self.m_stemdir == const.DOWN
336
 
                e.m_beamed_stem_top = h + 6
337
 
    def find_lowhigh_ypos(self):
338
 
        """
339
 
        Find the lowest and highest notehead in the beam.
340
 
        """
341
 
        mn = 1000
342
 
        mx = -1000
343
 
        for se in self.m_stems:
344
 
            for ylinepos in se.m_yposes:
345
 
                if mn > ylinepos:
346
 
                    mn = ylinepos
347
 
                if mx < ylinepos:
348
 
                    mx = ylinepos
349
 
        return mn, mx
350
 
    def decide_beam_stemdir(self):
351
 
        """
352
 
        Decide the direction for the stems in this beam, and set
353
 
        the stemdir for all stems.
354
 
        """
355
 
        v = {const.UP: 0, const.DOWN: 0}
356
 
        for e in self.m_stems:
357
 
            v[e.m_mgroup.m_stemdir] = v[e.m_mgroup.m_stemdir] + 1
358
 
            e.m_stemlen = 50
359
 
        if v[const.UP] > v[const.DOWN]:
360
 
            stemdir = const.UP
361
 
        else:
362
 
            stemdir = const.DOWN
363
 
        for e in self.m_stems:
364
 
            e.m_mgroup.m_stemdir = stemdir
365
 
        self.m_stemdir = stemdir
366
 
        for e in self.m_stems:
367
 
            e.m_mgroup.m_stemdir = stemdir
368
 
    def engrave(self, widget, gc, staff_yoffset):
369
 
        dim = dimentions[self.m_fontsize]
370
 
        d = 0
371
 
        if self.m_stemdir == const.UP:
372
 
            d = 1
373
 
        else:
374
 
            d = -1
375
 
        x1 = self.m_stems[0].m_xpos
376
 
        x2 = self.m_stems[-1].m_xpos
377
 
        if self.m_stems[0].m_beamed_stem_top % 2 == 1:
378
 
            for x in range(len(self.m_stems)):
379
 
                self.m_stems[x].m_beamed_stem_top =\
380
 
                    self.m_stems[x].m_beamed_stem_top - d
381
 
        y1 = self.m_stems[0].m_beamed_stem_top * dim.linespacing/2 + staff_yoffset
382
 
        beamw = 3
383
 
        beaml = 10
384
 
        for y in range(beamw):
385
 
            widget.window.draw_line(gc, x1, y1 + y*d, x2, y1 + y*d)
386
 
        for stem in self.m_stems:
387
 
            stem._beam_done = 8
388
 
        def short_beam(stem, xdir, beamnum, d=d, widget=widget, beamw=beamw, gc=gc, beaml=beaml, y1=y1):
389
 
            for y in range(beamw):
390
 
                widget.window.draw_line(gc, stem.m_xpos, y1 + y + beamnum*d*beamw*2,
391
 
                       stem.m_xpos + xdir*beaml, y1 + y + beamnum*d*beamw*2)
392
 
        for nl, yc in ((16, 0), (32, 1), (64, 2)):
393
 
            for i in range(len(self.m_stems)-1):
394
 
                if self.m_stems[i].m_mgroup.m_duration.m_nh >= nl \
395
 
                        and self.m_stems[i+1].m_mgroup.m_duration.m_nh >= nl:
396
 
                    for y in range(beamw):
397
 
                        widget.window.draw_line(gc,
398
 
                            self.m_stems[i].m_xpos, d*beamw*yc*2 + y1 + y + d*beamw*2,
399
 
                            self.m_stems[i+1].m_xpos, d*beamw*yc*2 + y1 + y + d*beamw*2)
400
 
                    self.m_stems[i]._beam_done = nl
401
 
                    self.m_stems[i+1]._beam_done = nl
402
 
                if self.m_stems[i].m_mgroup.m_duration.m_nh >= nl \
403
 
                      and self.m_stems[i+1].m_mgroup.m_duration.m_nh <= nl/2 \
404
 
                      and self.m_stems[i]._beam_done < nl:
405
 
                    if i == 0:
406
 
                        stemdir = 1
407
 
                    else:
408
 
                        if self.m_stems[i-1].m_mgroup.m_duration.m_nh < \
409
 
                           self.m_stems[i+1].m_mgroup.m_duration.m_nh:
410
 
                            stemdir = 1
411
 
                        else:
412
 
                            stemdir = -1
413
 
                    short_beam(self.m_stems[i], stemdir, yc+1, d)
414
 
                    self.m_stems[i]._beam_done = nl
415
 
            if self.m_stems[-1].m_mgroup.m_duration.m_nh >= nl \
416
 
                    and self.m_stems[-1]._beam_done <= nl/2:
417
 
                short_beam(self.m_stems[-1], -1, yc+1, d)
418
 
 
419
 
 
420
 
class StemEngraver(Engraver):
421
 
    """
422
 
    Every notehead belong to a stem, even if the stem is invisible.
423
 
    """
424
 
    def __init__(self, timepos, fontsize, yposes, mgroup, is_beamed):
425
 
        Engraver.__init__(self, timepos, fontsize)
426
 
        self.m_mgroup = mgroup
427
 
        self.m_yposes = yposes
428
 
        self.m_is_beamed = is_beamed
429
 
        if len(self.m_yposes) == 1:
430
 
            x = self.m_yposes[0]
431
 
        else:
432
 
            x = self.m_yposes[0] + self.m_yposes[-1]
433
 
        if self.m_mgroup.m_stemdir == const.UP \
434
 
            or (self.m_mgroup.m_stemdir == const.BOTH and x >= 0):
435
 
            self.m_mgroup.m_stemdir = const.UP
436
 
        else:
437
 
            self.m_mgroup.m_stemdir = const.DOWN
438
 
        self.m_stemlen = dimentions[self.m_fontsize].stemlen
439
 
    def set_xpos(self, dict):
440
 
        self.m_xpos = dict[self.m_timepos].m_music
441
 
    def calc_xpos(self):
442
 
        if self.m_mgroup.m_stempos == 1 or self.m_mgroup.m_stemdir == const.UP:
443
 
            self.m_xpos = self.m_xpos + dimentions[self.m_fontsize].xshift
444
 
    def engrave(self, widget, gc, staff_yoffset):
445
 
        dim = dimentions[self.m_fontsize]
446
 
        # draw flags
447
 
        if not self.m_is_beamed and self.m_mgroup.m_duration.m_nh > 4:
448
 
            if self.m_mgroup.m_stemdir == const.UP:
449
 
                widget.window.draw_pixbuf(gc,
450
 
                    gtk.gdk.pixbuf_new_from_file(
451
 
                        fetadir+'/feta%i-flags-%s.xpm' % \
452
 
                            (self.m_fontsize, self.get_flag(const.UP))),
453
 
                    0, 0,
454
 
                    self.m_xpos,
455
 
                    int(staff_yoffset - self.m_stemlen + self.m_yposes[0] * dim.linespacing/2))
456
 
            else:
457
 
                widget.window.draw_pixbuf(gc,
458
 
                    gtk.gdk.pixbuf_new_from_file(
459
 
                        fetadir+'/feta%i-flags-%s.xpm' \
460
 
                        % (self.m_fontsize, self.get_flag(const.DOWN))),
461
 
                    0, 0,
462
 
                    self.m_xpos,
463
 
                    int(staff_yoffset + 4 + self.m_yposes[-1] * dim.linespacing/2))
464
 
        # draw stem
465
 
        if self.m_mgroup.m_stemdir == const.DOWN:
466
 
            if self.m_is_beamed:
467
 
                yroot = self.m_beamed_stem_top
468
 
            else:
469
 
                yroot = self.m_yposes[-1] + 6
470
 
            ytop = self.m_yposes[0]
471
 
        else:
472
 
            if self.m_is_beamed:
473
 
                yroot = self.m_beamed_stem_top
474
 
            else:
475
 
                yroot = self.m_yposes[0] - 6
476
 
            ytop = self.m_yposes[-1]
477
 
        widget.window.draw_line(gc,
478
 
                     self.m_xpos,
479
 
                     ytop * dim.linespacing/2 + staff_yoffset,
480
 
                     self.m_xpos,
481
 
                     yroot * dim.linespacing/2 + staff_yoffset)
482
 
    def get_flag(self, stemdir):
483
 
        assert self.m_mgroup.m_duration.m_nh > 4
484
 
        return {8:{const.UP: const.FLAG_8UP,
485
 
                             const.DOWN: const.FLAG_8DOWN},
486
 
                16: {const.UP: const.FLAG_16UP,
487
 
                               const.DOWN: const.FLAG_16DOWN},
488
 
                32: {const.UP: const.FLAG_32UP,
489
 
                               const.DOWN: const.FLAG_32DOWN},
490
 
                64: {const.UP: const.FLAG_64UP,
491
 
                               const.DOWN: const.FLAG_64DOWN}} \
492
 
                              [self.m_mgroup.m_duration.m_nh][stemdir]
493
 
    def __str__(self):
494
 
        return "(StemEngraver)"
495
 
 
496
 
 
497
 
class LedgerLineEngraver(Engraver):
498
 
    def __init__(self, timepos, fontsize, up, down):
499
 
        """
500
 
        up: number of ledger lines above staff
501
 
        down: number of ledger lines below staff
502
 
        """
503
 
        Engraver.__init__(self, timepos, fontsize)
504
 
        self.m_up = up
505
 
        self.m_down = down
506
 
    def set_xpos(self, dict):
507
 
        self.m_xpos = dict[self.m_timepos].m_music
508
 
    def engrave(self, widget, gc, staff_yoffset):
509
 
        dim = dimentions[self.m_fontsize]
510
 
        if self.m_up:
511
 
            for y in range(self.m_up):
512
 
                widget.window.draw_line(gc,
513
 
                    self.m_xpos+dim.ledger_left, (-y-3) * dim.linespacing + staff_yoffset,
514
 
                    self.m_xpos+dim.ledger_right, (-y-3) * dim.linespacing + staff_yoffset)
515
 
        if self.m_down:
516
 
            for y in range(self.m_down):
517
 
                widget.window.draw_line(gc,
518
 
                    self.m_xpos+dim.ledger_left, (y+3) * dim.linespacing + staff_yoffset,
519
 
                    self.m_xpos+dim.ledger_right, (y+3) * dim.linespacing + staff_yoffset)
520
 
    def __str__(self):
521
 
        return "(LedgerLineEngraver xpos:%i, updown%i%i" % (
522
 
            self.m_xpos, self.m_up, self.m_down)
523
 
 
524
 
 
525
 
class RestEngraver(Engraver):
526
 
    def __init__(self, timepos, fontsize, ypos, dur):
527
 
        Engraver.__init__(self, timepos, fontsize)
528
 
        self.m_ypos = ypos
529
 
        self.m_dots = dur.m_dots
530
 
        self.m_type = {1: 0, 2: 1, 4: 2, 8: 3, 16: 4, 32: 5, 64: 6}[dur.m_nh]
531
 
    def set_xpos(self, dict):
532
 
        self.m_xpos = dict[self.m_timepos].m_music
533
 
    def get_width(self):
534
 
        #FIXME write me!
535
 
        return 20
536
 
    def engrave(self, widget, gc, staff_yoffset):
537
 
        dim = dimentions[self.m_fontsize]
538
 
        if self.m_type == 0:
539
 
            my = dim.linespacing/2
540
 
        else:
541
 
            my = 0
542
 
        widget.window.draw_pixbuf(gc,
543
 
            gtk.gdk.pixbuf_new_from_file(fetadir+'/feta%i-rests-%i.xpm' \
544
 
                % (self.m_fontsize, self.m_type)),
545
 
            0, 0,
546
 
            self.m_xpos,
547
 
            int(staff_yoffset - my + dim.linespacing*self.m_ypos/2 - 4))
548
 
        for n in range(self.m_dots):
549
 
            widget.window.draw_pixbuf(gc,
550
 
                gtk.gdk.pixbuf_new_from_file(fetadir+'/feta20-dots-dot.xpm'),
551
 
                0, 0,
552
 
                int(self.m_xpos+dim.xshift*(1.5+n/2.0)),
553
 
                -3 + staff_yoffset + dim.linespacing*self.m_ypos/2)
554
 
    def __str__(self):
555
 
        return "(RestEngraver)"