1 //=============================================================================
2 //  MuseScore
3 //  Music Composition & Notation
4 //
5 //  Copyright (C) 2017 Werner Schweer
6 //
7 //  This program is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License version 2
9 //  as published by the Free Software Foundation and appearing in
10 //  the file LICENCE.GPL
11 //=============================================================================
12 
13 #ifndef __SCORE_ELEMENT_H__
14 #define __SCORE_ELEMENT_H__
15 
16 #include "types.h"
17 #include "style.h"
18 
19 namespace Ms {
20 
21 class ScoreElement;
22 class MasterScore;
23 class XmlWriter;
24 class ConnectorInfoReader;
25 class Measure;
26 class Staff;
27 class Part;
28 class Score;
29 class Sym;
30 class MuseScoreView;
31 class Segment;
32 class Element;
33 class BarLine;
34 class Articulation;
35 class Marker;
36 class Clef;
37 class KeySig;
38 class TimeSig;
39 class TempoText;
40 class Breath;
41 class Box;
42 class HBox;
43 class VBox;
44 class TBox;
45 class FBox;
46 class ChordRest;
47 class Slur;
48 class Tie;
49 class Glissando;
50 class GlissandoSegment;
51 class SystemDivider;
52 class RehearsalMark;
53 class Harmony;
54 class Volta;
55 class Jump;
56 class StaffText;
57 class Ottava;
58 class Note;
59 class Chord;
60 class Rest;
61 class LayoutBreak;
62 class Tremolo;
63 class System;
64 class Sticking;
65 class Lyrics;
66 class LyricsLine;
67 class LyricsLineSegment;
68 class Stem;
69 class SlurSegment;
70 class TieSegment;
71 class OttavaSegment;
72 class Beam;
73 class Hook;
74 class StemSlash;
75 class Spacer;
76 class StaffLines;
77 class Ambitus;
78 class Bracket;
79 class InstrumentChange;
80 class Text;
81 class TextBase;
82 class Hairpin;
83 class HairpinSegment;
84 class Bend;
85 class TremoloBar;
86 class RepeatMeasure;
87 class Tuplet;
88 class NoteDot;
89 class Dynamic;
90 class InstrumentName;
91 class DurationElement;
92 class Accidental;
93 class TextLine;
94 class TextLineSegment;
95 class Pedal;
96 class PedalSegment;
97 class LedgerLine;
98 class Icon;
99 class VoltaSegment;
100 class NoteLine;
101 class Trill;
102 class TrillSegment;
103 class Symbol;
104 class FSymbol;
105 class Fingering;
106 class NoteHead;
107 class FiguredBass;
108 class StaffState;
109 class Arpeggio;
110 class Image;
111 class ChordLine;
112 class SlurTieSegment;
113 class FretDiagram;
114 class StaffTypeChange;
115 class MeasureBase;
116 class Page;
117 class SystemText;
118 class BracketItem;
119 class Spanner;
120 class SpannerSegment;
121 class Lasso;
122 class BagpipeEmbellishment;
123 class LineSegment;
124 class BSymbol;
125 class TextLineBase;
126 class Fermata;
127 
128 class LetRing;
129 class LetRingSegment;
130 class Vibrato;
131 class VibratoSegment;
132 class PalmMute;
133 class PalmMuteSegment;
134 class MeasureNumber;
135 class MMRestRange;
136 
137 class StaffTextBase;
138 
139 enum class Pid : int;
140 enum class PropertyFlags : char;
141 
142 //---------------------------------------------------------
143 //   LinkedElements
144 //---------------------------------------------------------
145 
146 class LinkedElements : public QList<ScoreElement*> {
147       int _lid;         // unique id for every linked list
148 
149    public:
150       LinkedElements(Score*);
151       LinkedElements(Score*, int id);
152 
153       void setLid(Score*, int val);
lid()154       int lid() const   { return _lid;    }
155 
156       ScoreElement* mainElement();
157       };
158 
159 //---------------------------------------------------------
160 //   ElementName
161 //---------------------------------------------------------
162 
163 struct ElementName {
164       ElementType type;
165       const char* name;
166       const char* userName;
167       };
168 
169 //---------------------------------------------------------
170 //   ScoreElement
171 //---------------------------------------------------------
172 
173 class ScoreElement {
174       Score* _score;
175       static ElementStyle const emptyStyle;
176 
177    protected:
178       const ElementStyle* _elementStyle { &emptyStyle };
179       PropertyFlags* _propertyFlagsList { 0 };
180       LinkedElements* _links            { 0 };
181       virtual int getPropertyFlagsIdx(Pid id) const;
182 
183    public:
ScoreElement(Score * s)184       ScoreElement(Score* s) : _score(s)   {}
185       ScoreElement(const ScoreElement& se);
186 
187       virtual ~ScoreElement();
188 
score()189       Score* score() const                 { return _score;      }
190       MasterScore* masterScore() const;
setScore(Score * s)191       virtual void setScore(Score* s)      { _score = s;         }
192       const char* name() const;
193       virtual QString userName() const;
194       virtual ElementType type() const = 0;
195 
196       static ElementType name2type(const QStringRef&, bool silent = false);
name2type(const QString & s)197       static ElementType name2type(const QString& s) { return name2type(QStringRef(&s)); }
198       static const char* name(ElementType);
199 
200       virtual QVariant getProperty(Pid) const = 0;
201       virtual bool setProperty(Pid, const QVariant&) = 0;
202       virtual QVariant propertyDefault(Pid) const;
203       virtual void resetProperty(Pid id);
204       QVariant propertyDefault(Pid pid, Tid tid) const;
sizeIsSpatiumDependent()205       virtual bool sizeIsSpatiumDependent() const { return true; }
offsetIsSpatiumDependent()206       virtual bool offsetIsSpatiumDependent() const { return true; }
207 
208       virtual void reset();                     // reset all properties & position to default
209 
210       virtual Pid propertyId(const QStringRef& xmlName) const;
211       virtual QString propertyUserValue(Pid) const;
212 
213       virtual void initElementStyle(const ElementStyle*);
styledProperties()214       virtual const ElementStyle* styledProperties() const   { return _elementStyle; }
215 
propertyFlagsList()216       virtual PropertyFlags* propertyFlagsList() const   { return _propertyFlagsList; }
217       virtual PropertyFlags propertyFlags(Pid) const;
218       bool isStyled(Pid pid) const;
219       QVariant styleValue(Pid, Sid) const;
220 
221       void setPropertyFlags(Pid, PropertyFlags);
222 
223       virtual Sid getPropertyStyle(Pid) const;
224       bool readProperty(const QStringRef&, XmlReader&, Pid);
225       void readProperty(XmlReader&, Pid);
226       bool readStyledProperty(XmlReader& e, const QStringRef& tag);
227 
228       virtual void readAddConnector(ConnectorInfoReader* info, bool pasteMode);
229 
230       virtual void styleChanged();
231 
232       virtual void undoChangeProperty(Pid id, const QVariant&, PropertyFlags ps);
233       void undoChangeProperty(Pid id, const QVariant&);
234       void undoResetProperty(Pid id);
235 
236       void undoPushProperty(Pid);
237       void writeProperty(XmlWriter& xml, Pid id) const;
238       void writeStyledProperties(XmlWriter&) const;
239 
240       QList<ScoreElement*> linkList() const;
241 
242       void linkTo(ScoreElement*);
243       void unlink();
244       bool isLinked(ScoreElement*);
245 
246       virtual void undoUnlink();
lid()247       int lid() const                         { return _links ? _links->lid() : 0; }
links()248       LinkedElements* links() const           { return _links;      }
setLinks(LinkedElements * le)249       void setLinks(LinkedElements* le)       { _links = le;        }
250 
251       //---------------------------------------------------
252       // check type
253       //
254       // Example for ChordRest:
255       //
256       //    bool             isChordRest()
257       //---------------------------------------------------
258 
259 
260 #define CONVERT(a,b) \
261       bool is##a() const { return type() == ElementType::b; }
262 
CONVERT(Note,NOTE)263       CONVERT(Note,          NOTE)
264       CONVERT(Rest,          REST)
265       CONVERT(Chord,         CHORD)
266       CONVERT(BarLine,       BAR_LINE)
267       CONVERT(Articulation,  ARTICULATION)
268       CONVERT(Fermata,       FERMATA)
269       CONVERT(Marker,        MARKER)
270       CONVERT(Clef,          CLEF)
271       CONVERT(KeySig,        KEYSIG)
272       CONVERT(TimeSig,       TIMESIG)
273       CONVERT(Measure,       MEASURE)
274       CONVERT(TempoText,     TEMPO_TEXT)
275       CONVERT(Breath,        BREATH)
276       CONVERT(HBox,          HBOX)
277       CONVERT(VBox,          VBOX)
278       CONVERT(TBox,          TBOX)
279       CONVERT(FBox,          FBOX)
280       CONVERT(Tie,           TIE)
281       CONVERT(Slur,          SLUR)
282       CONVERT(Glissando,     GLISSANDO)
283       CONVERT(GlissandoSegment,     GLISSANDO_SEGMENT)
284       CONVERT(SystemDivider, SYSTEM_DIVIDER)
285       CONVERT(RehearsalMark, REHEARSAL_MARK)
286       CONVERT(Harmony,       HARMONY)
287       CONVERT(Volta,         VOLTA)
288       CONVERT(Jump,          JUMP)
289       CONVERT(Ottava,        OTTAVA)
290       CONVERT(LayoutBreak,   LAYOUT_BREAK)
291       CONVERT(Segment,       SEGMENT)
292       CONVERT(Tremolo,       TREMOLO)
293       CONVERT(System,        SYSTEM)
294       CONVERT(Lyrics,        LYRICS)
295       CONVERT(Stem,          STEM)
296       CONVERT(Beam,          BEAM)
297       CONVERT(Hook,          HOOK)
298       CONVERT(StemSlash,     STEM_SLASH)
299       CONVERT(SlurSegment,   SLUR_SEGMENT)
300       CONVERT(TieSegment,    TIE_SEGMENT)
301       CONVERT(Spacer,        SPACER)
302       CONVERT(StaffLines,    STAFF_LINES)
303       CONVERT(Ambitus,       AMBITUS)
304       CONVERT(Bracket,       BRACKET)
305       CONVERT(InstrumentChange, INSTRUMENT_CHANGE)
306       CONVERT(StaffTypeChange, STAFFTYPE_CHANGE)
307       CONVERT(Hairpin,       HAIRPIN)
308       CONVERT(HairpinSegment,HAIRPIN_SEGMENT)
309       CONVERT(Bend,          BEND)
310       CONVERT(TremoloBar,    TREMOLOBAR)
311       CONVERT(RepeatMeasure, REPEAT_MEASURE)
312       CONVERT(Tuplet,        TUPLET)
313       CONVERT(NoteDot,       NOTEDOT)
314       CONVERT(Dynamic,       DYNAMIC)
315       CONVERT(InstrumentName, INSTRUMENT_NAME)
316       CONVERT(Accidental,    ACCIDENTAL)
317       CONVERT(TextLine,      TEXTLINE)
318       CONVERT(TextLineSegment,      TEXTLINE_SEGMENT)
319       CONVERT(Pedal,         PEDAL)
320       CONVERT(PedalSegment,  PEDAL_SEGMENT)
321       CONVERT(OttavaSegment, OTTAVA_SEGMENT)
322       CONVERT(LedgerLine,    LEDGER_LINE)
323       CONVERT(Icon,          ICON)
324       CONVERT(VoltaSegment,  VOLTA_SEGMENT)
325       CONVERT(NoteLine,      NOTELINE)
326       CONVERT(Trill,         TRILL)
327       CONVERT(TrillSegment,  TRILL_SEGMENT)
328       CONVERT(LetRing,       LET_RING)
329       CONVERT(LetRingSegment, LET_RING_SEGMENT)
330       CONVERT(Vibrato,       VIBRATO)
331       CONVERT(PalmMute,      PALM_MUTE)
332       CONVERT(PalmMuteSegment, PALM_MUTE_SEGMENT)
333       CONVERT(VibratoSegment,  VIBRATO_SEGMENT)
334       CONVERT(Symbol,        SYMBOL)
335       CONVERT(FSymbol,       FSYMBOL)
336       CONVERT(Fingering,     FINGERING)
337       CONVERT(NoteHead,      NOTEHEAD)
338       CONVERT(LyricsLine,    LYRICSLINE)
339       CONVERT(LyricsLineSegment, LYRICSLINE_SEGMENT)
340       CONVERT(FiguredBass,   FIGURED_BASS)
341       CONVERT(StaffState,    STAFF_STATE)
342       CONVERT(Arpeggio,      ARPEGGIO)
343       CONVERT(Image,         IMAGE)
344       CONVERT(ChordLine,     CHORDLINE)
345       CONVERT(FretDiagram,   FRET_DIAGRAM)
346       CONVERT(Page,          PAGE)
347       CONVERT(Text,          TEXT)
348       CONVERT(MeasureNumber, MEASURE_NUMBER)
349       CONVERT(MMRestRange,   MMREST_RANGE)
350       CONVERT(StaffText,     STAFF_TEXT)
351       CONVERT(SystemText,    SYSTEM_TEXT)
352       CONVERT(BracketItem,   BRACKET_ITEM)
353       CONVERT(Score,         SCORE)
354       CONVERT(Staff,         STAFF)
355       CONVERT(Part,          PART)
356       CONVERT(BagpipeEmbellishment, BAGPIPE_EMBELLISHMENT)
357       CONVERT(Lasso,         LASSO)
358       CONVERT(Sticking,      STICKING)
359 #undef CONVERT
360 
361       virtual bool isElement() const { return false; } // overridden in element.h
isChordRest()362       bool isChordRest() const       { return isRest() || isChord() || isRepeatMeasure(); }
isDurationElement()363       bool isDurationElement() const { return isChordRest() || isTuplet(); }
isSlurTieSegment()364       bool isSlurTieSegment() const  { return isSlurSegment() || isTieSegment(); }
365       bool isSLineSegment() const;
isBox()366       bool isBox() const { return isVBox() || isHBox() || isTBox() || isFBox(); }
isVBoxBase()367       bool isVBoxBase() const { return isVBox() || isTBox() || isFBox(); }
isMeasureBase()368       bool isMeasureBase() const { return isMeasure() || isBox(); }
369       bool isTextBase() const;
isTextLineBaseSegment()370       bool isTextLineBaseSegment() const {
371          return isHairpinSegment()
372          || isLetRingSegment()
373          || isTextLineSegment()
374          || isOttavaSegment()
375          || isPalmMuteSegment()
376          || isPedalSegment()
377          || isVoltaSegment()
378          ;
379          }
isLineSegment()380       bool isLineSegment() const {
381          return isGlissandoSegment()
382          || isLyricsLineSegment()
383          || isTextLineBaseSegment()
384          || isTrillSegment()
385          || isVibratoSegment()
386          ;
387          }
isSpannerSegment()388       bool isSpannerSegment() const { return isLineSegment() || isTextLineBaseSegment() || isSlurSegment() || isTieSegment(); }
isBSymbol()389       bool isBSymbol() const { return isImage() || isSymbol(); }
isTextLineBase()390       bool isTextLineBase() const {
391             return isHairpin()
392             || isLetRing()
393             || isNoteLine()
394             || isOttava()
395             || isPalmMute()
396             || isPedal()
397             || isTextLine()
398             || isVolta()
399             ;
400             }
isSLine()401       bool isSLine() const {
402             return isTextLineBase() || isTrill() || isGlissando() || isVibrato();
403             }
404 
isSpanner()405       bool isSpanner() const {
406          return isSlur()
407          || isTie()
408          || isGlissando()
409          || isLyricsLine()
410          || isTextLineBase()
411          || isSLine()
412          ;
413          }
isStaffTextBase()414       bool isStaffTextBase() const {
415             return isStaffText() || isSystemText();
416             }
417       };
418 
419 //---------------------------------------------------
420 // safe casting of ScoreElement
421 //
422 // Example for ChordRest:
423 //
424 //    ChordRest* toChordRest(Element* e)
425 //---------------------------------------------------
426 
toChordRest(ScoreElement * e)427 static inline ChordRest* toChordRest(ScoreElement* e) {
428       Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
429          || e->type() == ElementType::REPEAT_MEASURE);
430       return (ChordRest*)e;
431       }
toChordRest(const ScoreElement * e)432 static inline const ChordRest* toChordRest(const ScoreElement* e) {
433       Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
434          || e->type() == ElementType::REPEAT_MEASURE);
435       return (const ChordRest*)e;
436       }
toDurationElement(ScoreElement * e)437 static inline DurationElement* toDurationElement(ScoreElement* e) {
438       Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
439          || e->type() == ElementType::REPEAT_MEASURE || e->type() == ElementType::TUPLET);
440       return (DurationElement*)e;
441       }
toDurationElement(const ScoreElement * e)442 static inline const DurationElement* toDurationElement(const ScoreElement* e) {
443       Q_ASSERT(e == 0 || e->type() == ElementType::CHORD || e->type() == ElementType::REST
444          || e->type() == ElementType::REPEAT_MEASURE || e->type() == ElementType::TUPLET);
445       return (const DurationElement*)e;
446       }
toRest(ScoreElement * e)447 static inline Rest* toRest(ScoreElement* e) {
448       Q_ASSERT(!e || e->isRest() || e->isRepeatMeasure());
449       return (Rest*)e;
450       }
toRest(const ScoreElement * e)451 static inline const Rest* toRest(const ScoreElement* e) {
452       Q_ASSERT(!e || e->isRest() || e->isRepeatMeasure());
453       return (const Rest*)e;
454       }
toSlurTieSegment(ScoreElement * e)455 static inline SlurTieSegment* toSlurTieSegment(ScoreElement* e) {
456       Q_ASSERT(e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT);
457       return (SlurTieSegment*)e;
458       }
toSlurTieSegment(const ScoreElement * e)459 static inline const SlurTieSegment* toSlurTieSegment(const ScoreElement* e) {
460       Q_ASSERT(e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT);
461       return (const SlurTieSegment*)e;
462       }
toMeasureBase(const ScoreElement * e)463 static inline const MeasureBase* toMeasureBase(const ScoreElement* e) {
464      Q_ASSERT(e == 0 || e->isMeasure() || e->isVBox() || e->isHBox() || e->isTBox() || e->isFBox());
465       return (const MeasureBase*)e;
466       }
toMeasureBase(ScoreElement * e)467 static inline MeasureBase* toMeasureBase(ScoreElement* e) {
468      Q_ASSERT(e == 0 || e->isMeasureBase());
469       return (MeasureBase*)e;
470       }
toBox(ScoreElement * e)471 static inline Box* toBox(ScoreElement* e) {
472      Q_ASSERT(e == 0 || e->isBox());
473       return (Box*)e;
474       }
toSpannerSegment(ScoreElement * e)475 static inline SpannerSegment* toSpannerSegment(ScoreElement* e) {
476       Q_ASSERT(e == 0 || e->isSpannerSegment());
477       return (SpannerSegment*)e;
478       }
toSpannerSegment(const ScoreElement * e)479 static inline const SpannerSegment* toSpannerSegment(const ScoreElement* e) {
480       Q_ASSERT(e == 0 || e->isSpannerSegment());
481       return (const SpannerSegment*)e;
482       }
toBSymbol(ScoreElement * e)483 static inline BSymbol* toBSymbol(ScoreElement* e) {
484       Q_ASSERT(e == 0 || e->isBSymbol());
485       return (BSymbol*)e;
486       }
toTextLineBase(ScoreElement * e)487 static inline TextLineBase* toTextLineBase(ScoreElement* e) {
488       Q_ASSERT(e == 0 || e->isTextLineBase());
489       return (TextLineBase*)e;
490       }
toTextBase(ScoreElement * e)491 static inline TextBase* toTextBase(ScoreElement* e) {
492       Q_ASSERT(e == 0 || e->isTextBase());
493       return (TextBase*)e;
494       }
toTextBase(const ScoreElement * e)495 static inline const TextBase* toTextBase(const ScoreElement* e) {
496       Q_ASSERT(e == 0 || e->isTextBase());
497       return (const TextBase*)e;
498       }
toStaffTextBase(ScoreElement * e)499 static inline StaffTextBase* toStaffTextBase(ScoreElement* e) {
500       Q_ASSERT(e == 0 || e->isStaffTextBase());
501       return (StaffTextBase*)e;
502       }
toStaffTextBase(const ScoreElement * e)503 static inline const StaffTextBase* toStaffTextBase(const ScoreElement* e) {
504       Q_ASSERT(e == 0 || e->isStaffTextBase());
505       return (const StaffTextBase*)e;
506       }
507 
508 #define CONVERT(a)  \
509 static inline a* to##a(ScoreElement* e)             { Q_ASSERT(e == 0 || e->is##a()); return (a*)e; } \
510 static inline const a* to##a(const ScoreElement* e) { Q_ASSERT(e == 0 || e->is##a()); return (const a*)e; }
511 
512       CONVERT(Element)
513       CONVERT(Note)
514       CONVERT(Chord)
515       CONVERT(BarLine)
516       CONVERT(Articulation)
517       CONVERT(Fermata)
518       CONVERT(Marker)
519       CONVERT(Clef)
520       CONVERT(KeySig)
521       CONVERT(TimeSig)
522       CONVERT(Measure)
523       CONVERT(TempoText)
524       CONVERT(Breath)
525       CONVERT(HBox)
526       CONVERT(VBox)
527       CONVERT(TBox)
528       CONVERT(FBox)
529       CONVERT(Spanner)
530       CONVERT(Tie)
531       CONVERT(Slur)
532       CONVERT(Glissando)
533       CONVERT(GlissandoSegment)
534       CONVERT(SystemDivider)
535       CONVERT(RehearsalMark)
536       CONVERT(Harmony)
537       CONVERT(Volta)
538       CONVERT(Jump)
539       CONVERT(StaffText)
540       CONVERT(Ottava)
541       CONVERT(LayoutBreak)
542       CONVERT(Segment)
543       CONVERT(Tremolo)
544       CONVERT(System)
545       CONVERT(Lyrics)
546       CONVERT(Stem)
547       CONVERT(Beam)
548       CONVERT(Hook)
549       CONVERT(StemSlash)
550       CONVERT(LineSegment)
551       CONVERT(SlurSegment)
552       CONVERT(TieSegment)
553       CONVERT(Spacer)
554       CONVERT(StaffLines)
555       CONVERT(Ambitus)
556       CONVERT(Bracket)
557       CONVERT(InstrumentChange)
558       CONVERT(StaffTypeChange)
559       CONVERT(Text)
560       CONVERT(MeasureNumber)
561       CONVERT(MMRestRange)
562       CONVERT(Hairpin)
563       CONVERT(HairpinSegment)
564       CONVERT(Bend)
565       CONVERT(TremoloBar)
566       CONVERT(RepeatMeasure)
567       CONVERT(Tuplet)
568       CONVERT(NoteDot)
569       CONVERT(Dynamic)
570       CONVERT(InstrumentName)
571       CONVERT(Accidental)
572       CONVERT(TextLine)
573       CONVERT(TextLineSegment)
574       CONVERT(Pedal)
575       CONVERT(PedalSegment)
576       CONVERT(OttavaSegment)
577       CONVERT(LedgerLine)
578       CONVERT(Icon)
579       CONVERT(VoltaSegment)
580       CONVERT(NoteLine)
581       CONVERT(Trill)
582       CONVERT(TrillSegment)
583       CONVERT(LetRing)
584       CONVERT(LetRingSegment)
585       CONVERT(Vibrato)
586       CONVERT(VibratoSegment)
587       CONVERT(PalmMute)
588       CONVERT(PalmMuteSegment)
589       CONVERT(Symbol)
590       CONVERT(FSymbol)
591       CONVERT(Fingering)
592       CONVERT(NoteHead)
593       CONVERT(LyricsLine)
594       CONVERT(LyricsLineSegment)
595       CONVERT(FiguredBass)
596       CONVERT(StaffState)
597       CONVERT(Arpeggio)
598       CONVERT(Image)
599       CONVERT(ChordLine)
600       CONVERT(FretDiagram)
601       CONVERT(Page)
602       CONVERT(SystemText)
603       CONVERT(BracketItem)
604       CONVERT(Staff)
605       CONVERT(Part)
606       CONVERT(Lasso)
607       CONVERT(BagpipeEmbellishment)
608       CONVERT(Sticking)
609 #undef CONVERT
610 
611 }
612 
613 #endif
614