// **********************************************
// File: OBJCLASS.CPP
// Musical objects library

#include "muzika.h"
#include "objects.h"
#include <string.h>

// **********************************************
// LoadObject loads an object from a file.
// It reads the object type and constructs the appropriate object,
// letting it to go on and load its data (using the version of the constructor
// with an input stream as a parameter). The type of the object
// (point or continuous) is returned in *type.

MusicalObject *LoadObject(istream &in, void (*LoadError)(), int *type)
{
  // Identify the object type
  switch (in.get()) {
    case O_NOTE:
      *type = POINTOBJECT;
      return new Note(in);

    case O_PAUSE:
      *type = POINTOBJECT;
      return new Pause(in);

    case O_KEY:
      *type = POINTOBJECT;
      return new Key(in);

    case O_BEAT:
      *type = POINTOBJECT;
      return new Beat(in);

    case O_BAR:
      *type = POINTOBJECT;
      return new Bar(in);

    case O_LOUDNESS:
      *type = POINTOBJECT;
      return new Loudness(in);

    case O_CRESCENDO:
      *type = CONTINUOUSOBJECT;
      return new Crescendo(in);

    case O_TEXT:
      *type = POINTOBJECT;
      return new Text(in);
  }

  // Unknown object: call error function
  (*LoadError)();
  return NULL;
}

// **********************************************
// PasteObject creates an object from a clipboard entry.
// It reads the object type and constructs the appropriate object,
// letting it to go on and load its data (using the version of the constructor
// with a far char pointer as a parameter). The type of the object
// (point or continuous) is returned in *type.

MusicalObject *PasteObject(void far *&clipboard, int *type)
{
  // Identify the object type
  switch (*((char far *) clipboard)++) {
    case O_NOTE:
      *type = POINTOBJECT;
      return new Note(clipboard);

    case O_PAUSE:
      *type = POINTOBJECT;
      return new Pause(clipboard);

    case O_KEY:
      *type = POINTOBJECT;
      return new Key(clipboard);

    case O_BEAT:
      *type = POINTOBJECT;
      return new Beat(clipboard);

    case O_BAR:
      *type = POINTOBJECT;
      return new Bar(clipboard);

    case O_LOUDNESS:
      *type = POINTOBJECT;
      return new Loudness(clipboard);

    case O_CRESCENDO:
      *type = CONTINUOUSOBJECT;
      return new Crescendo(clipboard);

    case O_TEXT:
      *type = POINTOBJECT;
      return new Text(clipboard);
  }

  // Unknown object: return a null pointer
  return NULL;
}

// **********************************************
// Following are the member functions of the various musical objects,
// all derived from the MusicalObject base class, either via
// PointObject or ContinuousObject. The following functions
// are defined for each musical object type:
// - A constructor from direct object data
// - A constructor from an input stream (reads the object data from the stream)
// - A constructor from the clipboard (reads the object data from memory)
// - Draw draws the object in the given display context, using the global variables
//   staffX, staffY, staffLoc, and currStaffHeight (from DISPLAY.CPP) when needed.
// - Format sets the object in the given X coordinate during formatting.
// - MIDIPlay writes the MIDI event described by the object to the given file.
// - printOn writes the object data to the given file (when the melody is saved).
// - clipOn writes the object data in the clipboard (when the object is cut or copied).
//
// Any new objects in future extensions should be added here,
// and define their versions of all the above functions.

// **********************************************
// Definitions of the Note class member functions

Note :: Note(int X, int Y, int duration)
{
  // Copy the given parameters to the object variables
  _location = INSTAFF;
  _X = X;
  _Y = (Y >= 0) ? (Y+1)/3*3 : -((1-Y)/3*3);
  _duration = duration;
}

Note :: Note(istream &in)
{
  _location = INSTAFF;

  // Read the object data from the stream
  in.read((char *) &_X, sizeof(_X));
  in.read((char *) &_Y, sizeof(_Y));
  in.read((char *) &_duration, sizeof(_duration));
}

Note :: Note(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = INSTAFF;

  // Read the object data from the clipboard entry
  _X = *iclip++;
  _Y = *iclip++;
  _duration = *iclip++;
}

void Note :: Draw(HDC hDC)
{
  // Draw the staff extension lines if necessary
  if (_Y < 0)
    for (int y = -6; y >= _Y; y -= 6) {
      MoveTo(hDC, _X+staffX-5, staffY+y);
      LineTo(hDC, _X+staffX+6, staffY+y);
    }
  if (_Y > 24)
    for (int y = 30; y <= _Y; y += 6) {
      MoveTo(hDC, _X+staffX-5, staffY+y);
      LineTo(hDC, _X+staffX+6, staffY+y);
    }

  // Load the appropriate bitmap
  HDC hBitmapDC = CreateCompatibleDC(hDC);
  HBITMAP hBitmap;
  int yCenter;
  switch (_duration) {
    case FULLDURATION:
      hBitmap = LoadBitmap(hInst, "B_FULLNOTE");
      yCenter = 10;
      break;

    case HALFDURATION:
      if (_Y > 12) {
        hBitmap = LoadBitmap(hInst, "B_HALFNOTEUP");
        yCenter = 15;
      }
      else
      {
        hBitmap = LoadBitmap(hInst, "B_HALFNOTE");
        yCenter = 4;
      }
      break;

    case QUARTERDURATION:
      if (_Y > 12) {
        hBitmap = LoadBitmap(hInst, "B_QUARTERNOTEUP");
        yCenter = 15;
      }
      else
      {
        hBitmap = LoadBitmap(hInst, "B_QUARTERNOTE");
        yCenter = 4;
      }
      break;

    case EIGHTHDURATION:
      if (_Y > 12) {
        hBitmap = LoadBitmap(hInst, "B_EIGHTHNOTEUP");
        yCenter = 15;
      }
      else
      {
        hBitmap = LoadBitmap(hInst, "B_EIGHTHNOTE");
        yCenter = 4;
      }
      break;

    case SIXTEENTHDURATION:
      if (_Y > 12) {
        hBitmap = LoadBitmap(hInst, "B_SIXTEENTHNOTEUP");
        yCenter = 15;
      }
      else
      {
        hBitmap = LoadBitmap(hInst, "B_SIXTEENTHNOTE");
        yCenter = 4;
      }
      break;
  }

  // Draw the bitmap
  HBITMAP hOldBitmap = SelectObject(hBitmapDC, hBitmap);
  BitBlt(hDC, _X+staffX-16, _Y+staffY-yCenter, 32, 20, hBitmapDC, 0, 0, SRCAND);
  SelectObject(hBitmapDC, hOldBitmap);
  DeleteDC(hBitmapDC);
  DeleteObject(hBitmap);
}

void Note :: Format(int &X)
{
  // No special actions required: just copy the X coordinate
  _X = X;
}

void Note :: MIDIPlay(ostream &out, int part)
{
  // Write a Note On event in the temporary file
  // with the correct frequency and duration
  static const noteNumber[7] = {0, 2, 4, 5, 7, 9, 11};
  out.put(0x90+part);
  int octave = (30-_Y)/21;
  int freq = (30-21*octave-_Y)/3;
  out.write((char *) &_duration, sizeof(int));
  out.put(12*(octave+4)+noteNumber[freq]);
  out.put(0x40);
}

void Note :: printOn(ostream &out) const
{
  // Write the object type ID (O_NOTE) and data to the stream
  out.put(O_NOTE);
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_Y, sizeof(_Y));
  out.write((char *) &_duration, sizeof(_duration));
}

void Note :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_NOTE) and data to the clipboard entry
  *bclip++ = O_NOTE;
  *iclip++ = _X;
  *iclip++ = _Y;
  *iclip++ = _duration;
}

// **********************************************
// Definitions of the Pause class member functions

Pause :: Pause(int X, int duration)
{
  // Copy the given parameters to the object variables
  _location = INSTAFF;
  _X = X;
  _duration = duration;
}

Pause :: Pause(istream &in)
{
  _location = INSTAFF;

  // Read the object data from the stream
  in.read((char *) &_X, sizeof(_X));
  in.read((char *) &_duration, sizeof(_duration));
}

Pause :: Pause(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = INSTAFF;

  // Read the object data from the clipboard entry
  _X = *iclip++;
  _duration = *iclip++;
}

void Pause :: Draw(HDC hDC)
{
  // Draw the pause bitmap according to duration
  switch (_duration) {
    case FULLDURATION:
      Rectangle(hDC, _X+staffX-5, staffY+7, _X+staffX+5, staffY+9);
      break;

    case HALFDURATION:
      Rectangle(hDC, _X+staffX-5, staffY+10, _X+staffX+5, staffY+12);
      break;

    default:
      HDC hBitmapDC = CreateCompatibleDC(hDC);
      HBITMAP hBitmap;
      switch (_duration) {
        case QUARTERDURATION:
          hBitmap = LoadBitmap(hInst, "B_QUARTERPAUSE");
          break;

        case EIGHTHDURATION:
          hBitmap = LoadBitmap(hInst, "B_EIGHTHPAUSE");
          break;

        case SIXTEENTHDURATION:
          hBitmap = LoadBitmap(hInst, "B_SIXTEENTHPAUSE");
          break;
      }
      HBITMAP hOldBitmap = SelectObject(hBitmapDC, hBitmap);
      BitBlt(hDC, _X+staffX-16, staffY+2, 32, 20, hBitmapDC, 0, 0, SRCAND);
      SelectObject(hBitmapDC, hOldBitmap);
      DeleteDC(hBitmapDC);
      DeleteObject(hBitmap);
      break;
  }
}

void Pause :: Format(int &X)
{
  // No special actions required: just copy the X coordinate
  _X = X;
}

void Pause :: MIDIPlay(ostream &out, int part)
{
  // Put a Note Off event with the correct duration in the temporary file
  out.put(0x80+part);
  out.write((char *) &_duration, sizeof(int));
}

void Pause :: printOn(ostream &out) const
{
  // Write the object type ID (O_PAUSE) and data to the stream
  out.put(O_PAUSE);
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_duration, sizeof(_duration));
}

void Pause :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_PAUSE) and data to the clipboard entry
  *bclip++ = O_PAUSE;
  *iclip++ = _X;
  *iclip++ = _duration;
}

// **********************************************
// Definitions of the Key class member functions

Key :: Key(int, int type)
{
  // Copy the given parameters to the object variables
  _location = INSTAFF | ONEPERSTAFF;
  _X = LOCATION;
  _type = type;
}

Key :: Key(istream &in)
{
  _location = INSTAFF | ONEPERSTAFF;
  _X = LOCATION;

  // Read the object data from the stream
  in.read((char *) &_type, sizeof(_type));
}

Key :: Key(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = INSTAFF | ONEPERSTAFF;
  _X = LOCATION;

  // Read the object data from the clipboard entry
  _type = *iclip++;
}

void Key :: Draw(HDC hDC)
{
  HDC hBitmapDC = CreateCompatibleDC(hDC);
  HBITMAP hBitmap;

  // Load the appropriate bitmap
  switch (_type) {
    case KEYSOL:
      hBitmap = LoadBitmap(hInst, "B_KEYSOL");
      break;

    case KEYFA:
      hBitmap = LoadBitmap(hInst, "B_KEYFA");
      break;
  }

  // Draw the bitmap
  HBITMAP hOldBitmap = SelectObject(hBitmapDC, hBitmap);
  BitBlt(hDC, _X+staffX-16, staffY+4, 32, 20, hBitmapDC,
    0, 0, SRCAND);
  SelectObject(hBitmapDC, hOldBitmap);
  DeleteDC(hBitmapDC);
  DeleteObject(hBitmap);
}

void Key :: Format(int &)
{
  // Keys cannot be formatted
}

void Key :: printOn(ostream &out) const
{
  // Write the object type ID (O_KEY) and data to the stream
  out.put(O_KEY);
  out.write((char *) &_type, sizeof(_type));
}

void Key :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_KEY) to the clipboard entry
  *bclip++ = O_KEY;
  *iclip++ = _type;
}

// **********************************************
// Definitions of the Beat class member functions

Beat :: Beat(int X, int type)
{
  // Copy the given parameters to the object variables
  _location = COMMONMULTIPLE;
  _X = X;
  _type = type;
}

Beat :: Beat(istream &in)
{
  _location = COMMONMULTIPLE;

  // Read the object data from the stream
  in.read((char *) &_X, sizeof(_X));
  in.read((char *) &_type, sizeof(_type));
}

Beat :: Beat(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = COMMONMULTIPLE;

  // Read the object data from the clipboard entry
  _X = *iclip++;
  _type = *iclip++;
}

void Beat :: Draw(HDC hDC)
{
  HDC hBitmapDC = CreateCompatibleDC(hDC);
  HBITMAP hBitmap;

  // Load the appropriate bitmap
  switch (_type) {
    case BEATC:
      hBitmap = LoadBitmap(hInst, "B_BEATC");
      break;

    case BEATCBAR:
      hBitmap = LoadBitmap(hInst, "B_BEATCBAR");
      break;

    case BEAT28:
      hBitmap = LoadBitmap(hInst, "B_BEAT28");
      break;

    case BEAT24:
      hBitmap = LoadBitmap(hInst, "B_BEAT24");
      break;

    case BEAT38:
      hBitmap = LoadBitmap(hInst, "B_BEAT38");
      break;

    case BEAT34:
      hBitmap = LoadBitmap(hInst, "B_BEAT34");
      break;

    case BEAT48:
      hBitmap = LoadBitmap(hInst, "B_BEAT48");
      break;

    case BEAT44:
      hBitmap = LoadBitmap(hInst, "B_BEAT44");
      break;

    case BEAT68:
      hBitmap = LoadBitmap(hInst, "B_BEAT68");
      break;

  }

  // Draw the bitmap
  HBITMAP hOldBitmap = SelectObject(hBitmapDC, hBitmap);
  BitBlt(hDC, _X+staffX-16, staffY+4, 32, 20, hBitmapDC,
    0, 0, SRCAND);
  SelectObject(hBitmapDC, hOldBitmap);
  DeleteDC(hBitmapDC);
  DeleteObject(hBitmap);
}

void Beat :: Format(int &X)
{
  // No special actions required: just copy the X coordinate
  _X = X;
}

void Beat :: printOn(ostream &out) const
{
  // Write the object type ID (O_BEAT) and data to the stream
  out.put(O_BEAT);
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_type, sizeof(_type));
}

void Beat :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_BEAT) and data to the clipboard entry
  *bclip++ = O_BEAT;
  *iclip++ = _X;
  *iclip++ = _type;
}

// **********************************************
// Definitions of the Bar class member functions

Bar :: Bar(int X, int type)
{
  // Choose the _location attribute value according to bar type
  switch (_type = type) {
    case STARTBAR:
      _location = COMMONMULTIPLE | ONEPERSTAFF;
      _X = 0;
      break;

    case ENDBAR:
      _location = COMMONMULTIPLE | ONEPERSTAFF;
      _X = melody.GetStaffWidth();
      break;

    default:
      _location = COMMONMULTIPLE;
      _X = X;
      break;
  }
}

Bar :: Bar(istream &in)
{
  _location = COMMONMULTIPLE;

  // Read the object data from the stream
  in.read((char *) &_X, sizeof(_X));
  in.read((char *) &_type, sizeof(_type));

  // Decide about the object _location attribute value according to type
  if (_type == STARTBAR || _type == ENDBAR)
    _location |= ONEPERSTAFF;
}

Bar :: Bar(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = COMMONMULTIPLE;

  // Read the object data from the clipboard entry
  _X = *iclip++;
  _type = *iclip++;

  // Decide about the _location attribute value according to type
  if (_type == STARTBAR || _type == ENDBAR)
    _location |= ONEPERSTAFF;
}

void Bar :: Draw(HDC hDC)
{
  HBRUSH hOldBrush = SelectObject(hDC, GetStockObject(BLACK_BRUSH));
  int Yfrom, Yto;

  // Calculate the bar height limits
  Yfrom = staffY;
  switch (staffLoc) {
    case SINGLESTAFF:
    case LASTSTAFF:
      Yto = staffY+24;
      break;

    case FIRSTSTAFF:
    case MIDSTAFF:
      Yto = staffY+currStaffHeight;
      break;
  }

  // Draw the appropriate bar pattern
  switch (_type) {
    case BAR:
      MoveTo(hDC, _X+staffX, Yfrom);
      LineTo(hDC, _X+staffX, Yto);
      break;

    case DOUBLEBAR:
      MoveTo(hDC, _X+staffX-2, Yfrom);
      LineTo(hDC, _X+staffX-2, Yto);
      MoveTo(hDC, _X+staffX+2, Yfrom);
      LineTo(hDC, _X+staffX+2, Yto);
      break;

    case STARTBAR:
      Rectangle(hDC, _X+staffX, Yfrom, _X+staffX+4, Yto);
      MoveTo(hDC, _X+staffX+6, Yfrom);
      LineTo(hDC, _X+staffX+6, Yto);
      break;

    case ENDBAR:
      Rectangle(hDC, _X+staffX-3, Yfrom, _X+staffX+1, Yto);
      MoveTo(hDC, _X+staffX-6, Yfrom);
      LineTo(hDC, _X+staffX-6, Yto);
      break;
  }

  SelectObject(hDC, hOldBrush);
}

void Bar :: Format(int &X)
{
  // Bar can be moved unless it is fixed at the start or end of the staff
  if (_type != STARTBAR && _type != ENDBAR)
    _X = X;
}

void Bar :: printOn(ostream &out) const
{
  // Write the object type ID (O_BAR) and data to the stream
  out.put(O_BAR);
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_type, sizeof(_type));
}

void Bar :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_BAR) and data to the clipboard entry
  *bclip++ = O_BAR;
  *iclip++ = _X;
  *iclip++ = _type;
}

// **********************************************
// Definitions of the Loudness class member functions

Loudness :: Loudness(int X, int loudness)
{
  // Copy the given parameters to the object variables
  _location = BELOWMULTIPLE;
  _X = X;
  _loudness = loudness;
}

Loudness :: Loudness(istream &in)
{
  _location = BELOWMULTIPLE;

  // Read the object data from the stream
  in.read((char *) &_X, sizeof(_X));
  in.read((char *) &_loudness, sizeof(_loudness));
}

Loudness :: Loudness(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = BELOWMULTIPLE;

  // Read the object data from the clipboard entry
  _X = *iclip++;
  _loudness = *iclip++;
}

void Loudness :: Draw(HDC hDC)
{
  HDC hBitmapDC = CreateCompatibleDC(hDC);
  HBITMAP hBitmap;

  // Load the appropriate bitmap
  switch (_loudness) {
    case FORTE:
      hBitmap = LoadBitmap(hInst, "B_FORTE");
      break;

    case FORTISSIMO:
      hBitmap = LoadBitmap(hInst, "B_FORTISSIMO");
      break;

    case PIANO:
      hBitmap = LoadBitmap(hInst, "B_PIANO");
      break;

    case PIANISSIMO:
      hBitmap = LoadBitmap(hInst, "B_PIANISSIMO");
      break;
  }

  // Draw the bitmap
  HBITMAP hOldBitmap = SelectObject(hBitmapDC, hBitmap);
  BitBlt(hDC, _X+staffX-16, staffY+currStaffHeight/2-8, 32, 20, hBitmapDC,
    0, 0, SRCAND);
  SelectObject(hBitmapDC, hOldBitmap);
  DeleteDC(hBitmapDC);
  DeleteObject(hBitmap);
}

void Loudness :: Format(int &X)
{
  // No special actions required: just copy the X coordinate
  _X = X;
}

void Loudness :: printOn(ostream &out) const
{
  // Write the object type ID (O_LOUDNESS) and data to the stream
  out.put(O_LOUDNESS);
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_loudness, sizeof(_loudness));
}

void Loudness :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_LOUDNESS) and data to the clipboard entry
  *bclip++ = O_LOUDNESS;
  *iclip++ = _X;
  *iclip++ = _loudness;
}

// **********************************************
// Definitions of the Crescendo class member functions

Crescendo :: Crescendo(int Xleft, int Xright, int direction)
{
  // Copy the given parameters to the object variables
  _location = BELOWMULTIPLE;
  _Xleft = Xleft;
  _Xright = Xright;
  _direction = direction;
}

Crescendo :: Crescendo(istream &in)
{
  _location = BELOWMULTIPLE;

  // Read the object data from the stream
  in.read((char *) &_Xleft, sizeof(_Xleft));
  in.read((char *) &_Xright, sizeof(_Xright));
  in.read((char *) &_direction, sizeof(_direction));
}

Crescendo :: Crescendo(void far *&clipboard)
{
  int far *&iclip = (int far *) clipboard;
  _location = BELOWMULTIPLE;

  // Read the object data from the clipboard entry
  _Xleft = *iclip++;
  _Xright = *iclip++;
  _direction = *iclip++;
}

void Crescendo :: Draw(HDC hDC)
{
  // Draw the pair of lines according to direction
  MoveTo(hDC, staffX+((_direction == CRESCENDO) ? _Xright : _Xleft),
    staffY+currStaffHeight/2+4);
  LineTo(hDC, staffX+((_direction == CRESCENDO) ? _Xleft : _Xright),
    staffY+currStaffHeight/2+8);
  LineTo(hDC, staffX+((_direction == CRESCENDO) ? _Xright : _Xleft),
    staffY+currStaffHeight/2+12);
}

void Crescendo :: printOn(ostream &out) const
{
  // Write the object type ID (O_CRESCENDO) and data to the stream
  out.put(O_CRESCENDO);
  out.write((char *) &_Xleft, sizeof(_Xleft));
  out.write((char *) &_Xright, sizeof(_Xright));
  out.write((char *) &_direction, sizeof(_direction));
}

void Crescendo :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_CRESCENDO) and data to the clipboard entry
  *bclip++ = O_CRESCENDO;
  *iclip++ = _Xleft;
  *iclip++ = _Xright;
  *iclip++ = _direction;
}

// **********************************************
// Definitions of the Text class member functions

Text :: Text(int X, int Y, char *string)
{
  // Copy the given parameters to the object variables
  _location = INSTAFF;
  _X = X;
  _Y = (Y > 12) ? 30 : -21;
  strcpy(_text, string);
}

Text :: Text(istream &in)
{
  _location = INSTAFF;

  // Read the object data from the stream
  in.read((char *) &_X, sizeof(_X));
  in.read((char *) &_Y, sizeof(_Y));
  in.get(_text, MAXTEXT+1, 0);
  in.get();
}

Text :: Text(void far *&clipboard)
{
  _location = INSTAFF;
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Read the object data from the clipboard entry
  _X = *iclip++;
  _Y = *iclip++;
  _fstrcpy(_text, bclip);
  bclip += _fstrlen(bclip)+1;
}

void Text :: Draw(HDC hDC)
{
  // Put the text in a transparent fashion (i.e. not erasing the background)
  SetBkMode(hDC, TRANSPARENT);
  TextOut(hDC, _X+staffX, _Y+staffY, _text, strlen(_text));
}

void Text :: Format(int &X)
{
  // No special actions required: just copy the X coordinate
  _X = X;
}

void Text :: printOn(ostream &out) const
{
  // Write the object type ID (O_TEXT) and data to the stream
  out.put(O_TEXT);
  out.write((char *) &_X, sizeof(_X));
  out.write((char *) &_Y, sizeof(_Y));
  out.write(_text, strlen(_text)+1);
}

void Text :: clipOn(void far *&clipboard) const
{
  char far *&bclip = (char far *) clipboard;
  int far *&iclip = (int far *) clipboard;

  // Write the object type ID (O_TEXT) and data to the clipboard entry
  *bclip++ = O_TEXT;
  *iclip++ = _X;
  *iclip++ = _Y;
  _fstrcpy(bclip, _text);
  bclip += strlen(_text)+1;
}
