home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Audio 4.94 - Over 11,000 Files
/
audio-11000.iso
/
win3
/
edit
/
muzika
/
edit.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-21
|
16KB
|
465 lines
// **********************************************
// File: EDIT.CPP
// Edit window input module
#include "muzika.h"
#include <stdlib.h>
#include <values.h>
// **********************************************
// InsertEmptyStaff inserts a new empty staff in the specified list
// given the staff Y location and the part multiplicity.
// The list is kept sorted by the Y locations in ascending order.
void InsertEmptyStaff(IndexedList &staffList, unsigned Y, int multiplicity)
{
// Find the index to insert at
for (int index = 0;
index < staffList.number() && ((Staff *) &staffList[index])->Y() < Y;
index += multiplicity);
if (index > staffList.number())
index = staffList.number();
// Update coordinates of other staves and of the marked block
for (int other = index; other < staffList.number(); ++other)
((Staff *) &staffList[other])->Y() += pixelsPerStaff;
if (markBeginStaff >= index)
++markBeginStaff;
if (markEndStaff >= index)
++markEndStaff;
// Adjust Y to a multiple of pixelsPerStaff
if (index)
Y = ((Staff *) &staffList[index-1])->Y()+pixelsPerStaff;
else
Y = pixelsPerStaff;
// Insert the staff
staffList.insertAt(*new Staff(Y), index);
}
// **********************************************
// NewMultipleStaff creates a new multiple staff, after the user
// has clicked the pencil-on-staff symbol.
// The staff is inserted in the list, and the scroll bar range
// is readjusted.
void NewMultipleStaff(int Y)
{
Part &p = *((Part *) &melody.part[displayedPart]);
// Insert a new staff group in the database and refresh screen
for (int i = 0; i < p.multiplicity(); ++i)
InsertEmptyStaff(p.staff, Y+p.GetPartY(), p.multiplicity());
melodyModified = TRUE;
SetScrollRange(hEditWnd, SB_VERT, 0,
((Staff *) &p.staff[p.staff.number()-1])->Y(), TRUE);
InvalidateRect(hEditWnd, NULL, TRUE);
}
// **********************************************
// IdentifyStaff finds the index of the staff to which the Y coordinate
// (obtained from the current cursor position) is closest.
int IdentifyStaff(IndexedList &staffList, unsigned Y)
{
unsigned minDistance = MAXINT;
int minIndex;
int staffY;
// Find a staff that is closest to the clicked point,
// by minimizing the distance from different staves
for (int index = 0; index < staffList.number(); ++index) {
// If point actually on the staff, return it
if ((staffY = ((Staff *) &staffList[index])->Y()) <= Y &&
staffY+24 >= Y)
return index;
int distance = (staffY > Y) ? staffY-Y : Y-staffY-25;
if (distance < minDistance) {
minDistance = distance;
minIndex = index;
}
}
// Return a staff only if within reasonable distance
return (minDistance < (pixelsPerStaff-25)/2) ? minIndex : -1;
}
// **********************************************
// DeleteMultipleStaff deletes the multiple staff identified
// by a Y coordinate (obtained from the current cursor position).
// If the staff contains any objects, the user is requested
// to confirm the operation.
void DeleteMultipleStaff(int Y)
{
// Obtain a pointer to the staff to delete
Part &p = *((Part *) &melody.part[displayedPart]);
int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
if (staffIndex < 0) return;
staffIndex = staffIndex/p.multiplicity()*p.multiplicity();
Staff *s = (Staff *) &p.staff[staffIndex];
// Check if the multiple staff contains any objects
for (int i = staffIndex;
i/p.multiplicity() == staffIndex/p.multiplicity(); ++i) {
Staff *s = (Staff *) &p.staff[i];
if (s->pointObject.number() || s->continuousObject.number())
if (MessageBox(hEditWnd, "Staff is not empty. Erase anyway?", "WARNING",
MB_ICONEXCLAMATION | MB_YESNOCANCEL) == IDYES)
break;
else return;
}
// Erase the staff
int staffY = s->Y();
for (i = 0; i < p.multiplicity(); ++i)
p.staff.destroyAt(staffIndex);
if (markBeginStaff > staffIndex)
markBeginStaff -= p.multiplicity();
if (markEndStaff >= staffIndex)
markEndStaff -= p.multiplicity();
if (markEndStaff < markBeginStaff)
UnmarkBlock();
if (p.staff.number() > staffIndex) {
int staffDiff = ((Staff *) &p.staff[staffIndex])->Y()-staffY;
for (; staffIndex < p.staff.number(); ++staffIndex)
((Staff *) &p.staff[staffIndex])->Y() -= staffDiff;
}
// Mark the melody as modified and readjust the scroll bar range
melodyModified = TRUE;
SetScrollRange(hEditWnd, SB_VERT, 0,
p.staff.number() ? ((Staff *) &p.staff[p.staff.number()-1])->Y() : 0, TRUE);
InvalidateRect(hEditWnd, NULL, TRUE);
}
// **********************************************
// NewPointObject creates a point object corresponding to the
// active symbol at the given coordinate (obtained from the current
// cursor position), and inserts it in the staff according
// to the object _location attribute.
void NewPointObject(SymbolClass *symbol, int X, int Y)
{
// Obtain the staff to insert the object into
Part &p = *((Part *) &melody.part[displayedPart]);
int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
if (!s || X < s->X() || X >= s->X()+s->width()) return;
// Create the object and decide where to insert it
int first, last;
MusicalObject *obj = symbol->CreateObject(staffIndex, WidthRound(X-s->X()), Y -= s->Y());
if (obj) {
X = ((PointObject *) obj)->X();
// Check the object _location attribute
switch (obj->location() & ~ONEPERSTAFF) {
case INSTAFF:
case ABOVESTAFF:
case BELOWSTAFF:
first = last = staffIndex;
break;
case ABOVEMULTIPLE:
first = last = staffIndex/p.multiplicity()*p.multiplicity();
break;
case BELOWMULTIPLE:
first = last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
break;
case COMMONMULTIPLE:
first = staffIndex/p.multiplicity()*p.multiplicity();
last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
break;
}
// Insert the objects in the required staff or staves
for (staffIndex = first; staffIndex <= last; ++staffIndex) {
s = (Staff *) &p.staff[staffIndex];
IndexedList &list = s->pointObject;
PointObject *obj1;
for (int index = 0;
index < list.number() && (obj1 = (PointObject *) &list[index])->X() <= X;
++index)
if (obj1->location() & ONEPERSTAFF && obj1->X() == X) {
MessageBox(hEditWnd, "Only one such symbol is allowed per staff", NULL,
MB_ICONEXCLAMATION | MB_OK);
return;
}
list.insertAt(
(staffIndex == first) ? *obj : *symbol->CreateObject(staffIndex, X, Y), index);
}
}
melodyModified = TRUE;
// Refresh screen
InvalidateRect(hEditWnd, NULL, !obj);
}
// **********************************************
// NewContinuousObject creates a continuous object corresponding to the
// active symbol at the given coordinate (obtained from the current
// cursor position), and inserts it in the staff according
// to the object _location attribute.
void NewContinuousObject(SymbolClass *symbol, int Xleft, int Xright, int Y)
{
// Obtain the staff to insert the object into
Part &p = *((Part *) &melody.part[displayedPart]);
int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
if (!s || Xleft < s->X() || Xright >= s->X()+s->width()) return;
// Create the object and decide where to insert it
int first, last;
MusicalObject *obj =
symbol->CreateObject(staffIndex, WidthRound(Xleft-s->X()), WidthRound(Xright-s->X()));
if (obj) {
Xleft = ((ContinuousObject *) obj)->Xleft();
Xright = ((ContinuousObject *) obj)->Xright();
// Check the object _location attribute
switch (obj->location() & ~ONEPERSTAFF) {
case INSTAFF:
case ABOVESTAFF:
case BELOWSTAFF:
first = last = staffIndex;
break;
case ABOVEMULTIPLE:
first = last = staffIndex/p.multiplicity()*p.multiplicity();
break;
case BELOWMULTIPLE:
first = last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
break;
case COMMONMULTIPLE:
first = staffIndex/p.multiplicity()*p.multiplicity();
last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
break;
}
// Insert the object in the appropriate staff or staves
for (staffIndex = first; staffIndex <= last; ++staffIndex) {
s = (Staff *) &p.staff[staffIndex];
IndexedList &list = s->continuousObject;
ContinuousObject *obj1;
for (int index = 0;
index < list.number() &&
(obj1 = (ContinuousObject *) &list[index])->Xleft() <= Xleft;
++index)
if (obj1->location() & ONEPERSTAFF && obj1->Xleft() == Xleft) {
MessageBox(hEditWnd, "Only one such symbol is allowed per staff", NULL,
MB_ICONEXCLAMATION | MB_OK);
return;
}
list.insertAt((staffIndex == first) ? *obj :
*symbol->CreateObject(staffIndex, Xleft, Xright), index);
}
}
melodyModified = TRUE;
// Refresh screen
InvalidateRect(hEditWnd, NULL, !obj);
}
// **********************************************
// DeleteMusicalObject deletes the objects that are within
// (pixelsPerObject/2) pixels away from the given coordinate
// (obtained from the current cursor position).
void DeleteMusicalObject(int X, int Y)
{
// Obtain the staff to delete the objects from
Part &p = *((Part *) &melody.part[displayedPart]);
int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
if (!s || X < s->X() || X >= s->X()+s->width()) return;
// Scan the point objects list and delete any objects in range
X -= s->X();
Y -= s->Y();
for (int index = 0; index < s->pointObject.number(); ++index) {
PointObject *obj = (PointObject *) &s->pointObject[index];
if (abs(obj->X()-X) < pixelsPerObject/2)
// Check the object _location attribute to see if
// any special treatment is required
switch (obj->location() & ~ONEPERSTAFF) {
case ABOVESTAFF:
case ABOVEMULTIPLE:
if (Y < 0) {
s->pointObject.destroyAt(index);
--index;
}
break;
case BELOWSTAFF:
case BELOWMULTIPLE:
if (Y > 24) {
s->pointObject.destroyAt(index);
--index;
}
break;
case INSTAFF:
s->pointObject.destroyAt(index);
--index;
break;
}
}
// Scan the continuous objects list and delete any objects in range
for (index = 0; index < s->continuousObject.number(); ++index) {
ContinuousObject *obj = (ContinuousObject *) &s->continuousObject[index];
if (abs(obj->Xleft()-X) < pixelsPerObject/2)
switch (obj->location() & ~ONEPERSTAFF) {
case ABOVESTAFF:
case ABOVEMULTIPLE:
if (Y < 0) {
s->continuousObject.destroyAt(index);
--index;
melodyModified = TRUE;
}
break;
case BELOWSTAFF:
case BELOWMULTIPLE:
if (Y > 24) {
s->continuousObject.destroyAt(index);
--index;
melodyModified = TRUE;
}
break;
case INSTAFF:
s->continuousObject.destroyAt(index);
--index;
melodyModified = TRUE;
break;
}
}
// Scan all staves in group and delete COMMONMULTIPLE objects
int first = staffIndex/p.multiplicity()*p.multiplicity();
int last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
for (staffIndex = first; staffIndex <= last; ++staffIndex) {
// Delete COMMONMULTIPLE point objects
s = (Staff *) &p.staff[staffIndex];
for (index = 0; index < s->pointObject.number(); ++index) {
PointObject *obj = (PointObject *) &s->pointObject[index];
if (abs(obj->X()-X) < pixelsPerObject/2 &&
(obj->location() & ~ONEPERSTAFF) == COMMONMULTIPLE) {
s->pointObject.destroyAt(index);
--index;
melodyModified = TRUE;
}
}
for (index = 0; index < s->continuousObject.number(); ++index) {
// Delete COMMONMULTIPLE continuous objects
ContinuousObject *obj = (ContinuousObject *) &s->continuousObject[index];
if (abs(obj->Xleft()-X) < pixelsPerObject/2 &&
(obj->location() & ~ONEPERSTAFF) == COMMONMULTIPLE) {
s->continuousObject.destroyAt(index);
--index;
melodyModified = TRUE;
}
}
}
// Refresh screen
InvalidateRect(hEditWnd, NULL, TRUE);
}
// **********************************************
// MoveStaff moves a staff from Yfrom to Yto, both coordinates
// obtained from the mouse cursor. Before actually moving,
// the destination is checked to be free from other staves.
void MoveStaff(int Yfrom, int Yto)
{
// Obtain a pointer to the staff to move
Part &p = *((Part *) &melody.part[displayedPart]);
int staffIndex = IdentifyStaff(p.staff, (Yfrom += p.GetPartY()));
if (staffIndex < 0) return;
staffIndex = staffIndex/p.multiplicity()*p.multiplicity();
Staff *s = (Staff *) &p.staff[staffIndex];
int lastMoved = staffIndex+p.multiplicity()-1;
// Verify that the destination is not occupied
Yto += p.GetPartY();
for (int indexTo = 0; indexTo < p.staff.number(); indexTo += p.multiplicity())
if (indexTo != staffIndex) {
Staff &firstTo = *((Staff *) &p.staff[indexTo]);
Staff &lastTo = *((Staff *) &p.staff[indexTo+p.multiplicity()-1]);
if (Yto <= lastTo.Y()+24 &&
((Staff *) &p.staff[lastMoved])->Y()-s->Y()+Yto+24 >= firstTo.Y()) {
// The destination is occupied:
// display an error message
MessageBox(hEditWnd, "Cannot move onto another staff", NULL,
MB_ICONEXCLAMATION | MB_OK);
return;
}
}
// Set the staff new coordinates and move it
int distance = Yto-s->Y();
for (; staffIndex <= lastMoved; ++staffIndex)
((Staff *) &p.staff[staffIndex])->Y() += distance;
staffIndex -= p.multiplicity();
for (indexTo = 0;
indexTo < p.staff.number() && ((Staff *) &p.staff[indexTo])->Y() < Yto;
indexTo += p.multiplicity());
while (staffIndex <= lastMoved) {
s = (Staff *) &p.staff[staffIndex];
// Detach and re-insert staves whose index is between
// the old and new indexes of the moved staff
p.staff.detachAt(staffIndex);
if (indexTo > staffIndex) {
--lastMoved;
--indexTo;
}
p.staff.insertAt(*s, indexTo);
if (staffIndex >= indexTo) {
++staffIndex;
++indexTo;
}
}
// Refresh the display
UnmarkBlock();
melodyModified = TRUE;
InvalidateRect(hEditWnd, NULL, TRUE);
SetScrollRange(hEditWnd, SB_VERT, 0,
((Staff *) &p.staff[p.staff.number()-1])->Y(), TRUE);
}
// **********************************************
// MoveMusicalObject moves a musical object by using
// the CutBlock and PasteBlock functions in BLOCK.CPP.
void MoveMusicalObject(int Xfrom, int Yfrom, int Xto, int Yto)
{
// Verify both points are on the same staff
Part &p = *((Part *) &melody.part[displayedPart]);
int staffFrom = IdentifyStaff(p.staff, Yfrom+p.GetPartY());
int staffTo = IdentifyStaff(p.staff, Yto+p.GetPartY());
if (staffFrom < 0 || staffTo < 0)
return;
staffFrom = staffFrom/p.multiplicity()*p.multiplicity();
staffTo = staffTo/p.multiplicity()*p.multiplicity();
int staffX = ((Staff *) &p.staff[staffFrom])->X();
// Use the cut and paste functions to move the objects
MarkBlock(staffFrom, WidthRound(Xfrom-staffX),
staffFrom, WidthRound(Xfrom-staffX));
CutBlock();
PasteBlock(staffTo, WidthRound(Xto-staffX));
InvalidateRect(hEditWnd, NULL, TRUE);
}