UNIT PatternEditor;

INTERFACE

USES Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,Menus,
     StdCtrls,extctrls,SoundEnginePlayer,comCtrls;

TYPE TPatternEditorProperties=PACKED RECORD
      cBackground:TColor;
      cBackgroundPlay:TColor;
      cBackgroundCursor:TColor;
      cDisabledChannel:TColor;
      cMutedChannel:TColor;
      cText:TColor;
      cTextCursor:TColor;
      cSelection:TColor;
      cTextSelection:TColor;
      cTextPlay:TColor;
      cRow:TColor;
      cSeparator:TColor;
      cRowHilightMinor:TColor;
      cRowHilightMajor:TColor;
      cSelectionDrag:TColor;
      FontName:STRING[255];
      FontSize:BYTE;
      FontStyle:TFontStyles;
     END;

     PPatternEditorUndoItem=^TPatternEditorUndoItem;
     TPatternEditorUndoItem=RECORD
      Pattern:TPattern;
      PatternNr,Zeile,Kanal,SichtKanal,MarkStartZeile,MarkEndeZeile,
      MarkStartKanal,MarkEndeKanal:INTEGER;
      Markierung:BOOLEAN;
     END;

CONST MaxListSize=2147483647 DIV SIZEOF(TPatternEditorUndoItem);

TYPE PPatternEditorUndoItems=^TPatternEditorUndoItems;
     TPatternEditorUndoItems=ARRAY[0..MaxListSize-1] OF TPatternEditorUndoItem;

     TPatternEditorUndoList=CLASS
      PRIVATE
       FList:PPatternEditorUndoItems;
       FCount,FSize:INTEGER;
       FUNCTION GetItem(Index:INTEGER):TPatternEditorUndoItem;
       PROCEDURE SetItem(Index:INTEGER;Value:TPatternEditorUndoItem);
       FUNCTION GetItemPointer(Index:INTEGER):PPatternEditorUndoItem;
      PUBLIC
       CONSTRUCTOR Create;
       DESTRUCTOR Destroy; OVERRIDE;
       PROCEDURE Clear;
       FUNCTION Add(Item:TPatternEditorUndoItem):INTEGER;
       PROCEDURE Insert(Index:INTEGER;Item:TPatternEditorUndoItem);
       PROCEDURE Delete(Index:INTEGER);
       FUNCTION Remove(Item:PPatternEditorUndoItem):INTEGER;
       FUNCTION Find(Item:PPatternEditorUndoItem):INTEGER;
       FUNCTION IndexOf(Item:PPatternEditorUndoItem):INTEGER;
       PROCEDURE Exchange(Index1,Index2:INTEGER);
       PROCEDURE SetCapacity(NewCapacity:INTEGER);
       PROCEDURE SetCount(NewCount:INTEGER);
       PROPERTY Count:INTEGER READ FCount;
       PROPERTY Capacity:INTEGER READ FSize WRITE SetCapacity;
       PROPERTY Item[Index:INTEGER]:TPatternEditorUndoItem READ GetItem WRITE SetItem; DEFAULT;
       PROPERTY Items[Index:INTEGER]:TPatternEditorUndoItem READ GetItem WRITE SetItem;
       PROPERTY PItems[Index:INTEGER]:PPatternEditorUndoItem READ GetItemPointer;
     END;

     TPatternEditorMemoryRow=ARRAY OF TPatternNote;
     TPatternEditorMemoryRows=ARRAY OF TPatternEditorMemoryRow;

     TPatternEditor=CLASS(TCustomControl)
      PRIVATE
       IBB:INTEGER;
       MaxSichtbareZeilen:INTEGER;
       LeftButton:BOOLEAN;
       RightButton:BOOLEAN;
       Ziehen:BOOLEAN;
       ObenKlick:BOOLEAN;
       ObenKlickKanal:INTEGER;
       LX,LY,LZ,LK:INTEGER;
       AltMarkStartZeile,AltMarkEndeZeile,AltMarkStartKanal,
       AltMarkEndeKanal:INTEGER;
       MarkierungAlt:BOOLEAN;
       DragStartZeile:INTEGER;
       DragEndeZeile:INTEGER;
       DragStartKanal:INTEGER;
       DragEndeKanal:INTEGER;
       StartDragMarkStartZeile:INTEGER;
       StartDragMarkEndeZeile:INTEGER;
       StartDragMarkStartKanal:INTEGER;
       StartDragMarkEndeKanal:INTEGER;
       DragMarkStartZeile:INTEGER;
       DragMarkEndeZeile:INTEGER;
       DragMarkStartKanal:INTEGER;
       DragMarkEndeKanal:INTEGER;
       AltDragMarkStartZeile:INTEGER;
       AltDragMarkEndeZeile:INTEGER;
       AltDragMarkStartKanal:INTEGER;
       AltDragMarkEndeKanal:INTEGER;
       OWidth,OHeight:INTEGER;
       UndoList:TPatternEditorUndoList;
       PROCEDURE PaintEx;
      PROTECTED
       PROCEDURE Paint; OVERRIDE;
       PROCEDURE Resize; OVERRIDE;
       PROCEDURE MouseDown(Button:TMouseButton;Shift:TShiftState;X,Y:INTEGER); OVERRIDE;
       PROCEDURE MouseMove(Shift:TShiftState;X,Y:INTEGER); OVERRIDE;
       PROCEDURE MouseUp(Button:TMouseButton;Shift:TShiftState;X,Y:INTEGER); OVERRIDE;
       PROCEDURE DblClick; OVERRIDE;
       PROCEDURE WMEraseBkgnd(VAR Message:TMessage); MESSAGE WM_ERASEBKGND;
       PROCEDURE WMGetDlgCode(VAR Message:TMessage); MESSAGE WM_GetDlgCode;
       PROCEDURE KeyDown(VAR Key:WORD;Shift:TShiftState); OVERRIDE;
       PROCEDURE KeyUp(VAR Key:WORD;Shift:TShiftState); OVERRIDE;
       PROCEDURE OnClear(Sender:TObject);
       PROCEDURE OnDelete(Sender:TObject);
       PROCEDURE OnCopy(Sender:TObject);
       PROCEDURE OnCut(Sender:TObject);
       PROCEDURE OnPaste(Sender:TObject);
       PROCEDURE OnPut(Sender:TObject);
       PROCEDURE OnInstrumentChange(Sender:TObject);
       PROCEDURE OnTransposeM1(Sender:TObject);
       PROCEDURE OnTransposeM12(Sender:TObject);
       PROCEDURE OnTransposeP12(Sender:TObject);
       PROCEDURE OnTransposeP1(Sender:TObject);
       PROCEDURE OnClearEffectA(Sender:TObject);
       PROCEDURE OnInterpolateEffectBA(Sender:TObject);
       PROCEDURE OnClearEffectB(Sender:TObject);
       PROCEDURE OnInterpolateEffectB(Sender:TObject);
       PROCEDURE OnMuteChannel(Sender:TObject);
       PROCEDURE OnUnmuteChannel(Sender:TObject);
       PROCEDURE OnMuteAllExceptThisChannel(Sender:TObject);
       PROCEDURE OnUnmuteAll(Sender:TObject);
       PROCEDURE MouseWheel(Sender:TObject;Shift:TShiftState;WheelDelta:INTEGER;MousePos:TPoint;VAR Handled:BOOLEAN);
      PUBLIC
       Track:TTrack;
       Form:TForm;
       AktPos:INTEGER;
       AktKanal:INTEGER;
       AktSichtKanal:INTEGER;
       AktPattern:BYTE;
       AktZeile:INTEGER;
       CurrentOctave:INTEGER;
       CurrentInstrument:INTEGER;
       RowScroll,ChannelScroll:TScrollBar;
       MaxKaenale,MaxGanzeKaenale,MaxZeilen:INTEGER;
       LastShift:TShiftState;
       LastKey:WORD;
       LShift:TShiftState;
       LKey:WORD;
       ALtMarkierung:BOOLEAN;
       Markierung:BOOLEAN;
       StartMarkierung:BOOLEAN;
       MarkStartZeile,MarkEndeZeile,MarkStartKanal,MarkEndeKanal:INTEGER;
       MarkZeile,MarkKanal:INTEGER;
       SelectionPopupMenu:TPopupMenu;
       MemoryPopupMenu:TPopupMenu;
       ChannelPopupMenu:TPopupMenu;
       HintergrundLoeschen:BOOLEAN;
       SChannel:STRING;
       MaxUndoSteps:INTEGER;
       TB,TH:INTEGER;
       Dauer:DOUBLE;
       CONSTRUCTOR Create(AOwner:TComponent); OVERRIDE;
       DESTRUCTOR Destroy; OVERRIDE;
       FUNCTION KanalBreite:INTEGER;
       PROCEDURE ClearMemory;
       PROCEDURE ClearSelection;
       PROCEDURE DeleteSelection;
       PROCEDURE CopySelection;
       PROCEDURE CutSelection;
       PROCEDURE PasteSelection;
       PROCEDURE PutSelection;
       PROCEDURE InstrumentChangeSelection;
       PROCEDURE TransposeSelection(Faktor:INTEGER);
       PROCEDURE ClearEffectASelection;
       PROCEDURE InterpolateEffectASelection;
       PROCEDURE ClearEffectBSelection;
       PROCEDURE InterpolateEffectBSelection;
       PROCEDURE MuteChannel;
       PROCEDURE UnmuteChannel;
       PROCEDURE MuteAllExceptThisChannel;
       PROCEDURE UnmuteAll;
       PROCEDURE ClearUndoList;
       PROCEDURE NewUndoEntry;
       PROCEDURE DoUndo;
       PROCEDURE PaintExEx;
       PROCEDURE ResizeEx;
       PROCEDURE PositionUpdate;
       PROCEDURE ChannelScrollUpdate;
       PROCEDURE RowScrollUpdate;
       PROCEDURE KeyProcess;
       PROCEDURE AktualisiereStatusBar;
       PROCEDURE InvalidateRowChannel;
       PROCEDURE InvalidateRow;
       PROCEDURE InvalidateSelection;
       PROCEDURE InvalidateSelectionChange;
       PROCEDURE InvalidateDragChange;
       PROCEDURE InvalidateChannel;
      PUBLISHED
     END;

CONST PatternEditorProperties:TPatternEditorProperties=(
       cBackground:$584738;
       cBackgroundPlay:$D7CCB9;
       cBackgroundCursor:$2480DB;
       cDisabledChannel:$0000FF;
       cMutedChannel:$FFFFFF;
       cText:$F7F3D2;
       cTextCursor:$FFFFFF;
       cSelection:$85B3E0;
       cTextSelection:$43362C;
       cTextPlay:$442C1E;
       cRow:$442C1E;
       cSeparator:$63594E;
       cRowHilightMinor:$746047;
       cRowHilightMajor:$917A64;
       cSelectionDrag:$FFFFFF;
       FontName:'Courier';
       FontSize:10;
       FontStyle:[]);

IMPLEMENTATION

USES Main,Globals;

CONST VK_OEM_5=$DC;

CONSTRUCTOR TPatternEditorUndoList.Create;
BEGIN
 INHERITED Create;
 Clear;
END;

DESTRUCTOR TPatternEditorUndoList.Destroy;
BEGIN
 Clear;
 INHERITED Destroy;
END;

PROCEDURE TPatternEditorUndoList.Clear;
BEGIN
 FCount:=0;
 FSize:=0;
 REALLOCMEM(FList,0);
END;

PROCEDURE TPatternEditorUndoList.SetCapacity(NewCapacity:INTEGER);
BEGIN
 IF (NewCapacity>=0) AND (NewCapacity<MaxListSize) THEN BEGIN
  REALLOCMEM(FList,NewCapacity*SIZEOF(TPatternEditorUndoItem));
  FSize:=NewCapacity;
 END;
END;

PROCEDURE TPatternEditorUndoList.SetCount(NewCount:INTEGER);
BEGIN
 IF (NewCount>=0) AND (NewCount<MaxListSize) THEN BEGIN
  IF NewCount<FCount THEN BEGIN
   FCount:=NewCount;
  END ELSE IF NewCount>FCount THEN BEGIN
   IF NewCount>FSize THEN BEGIN
    SetCapacity(NewCount);
   END;
   IF FCount<NewCount THEN BEGIN
    FILLCHAR(FList^[FCount],(NewCount-FCount)*SIZEOF(TPatternEditorUndoItem),0);
   END;
   FCount:=NewCount;
  END;
 END;
END;

FUNCTION TPatternEditorUndoList.Add(Item:TPatternEditorUndoItem):INTEGER;
BEGIN
 IF FCount=FSize THEN BEGIN
  IF FSize>64 THEN BEGIN
   INC(FSize,FSize DIV 4);
  END ELSE IF FSize>8 THEN BEGIN
   INC(FSize,16);
  END ELSE BEGIN
   INC(FSize,4);
  END;
  REALLOCMEM(FList,FSize*SIZEOF(TPatternEditorUndoItem));
 END;
 FList^[FCount]:=Item;
 RESULT:=FCount;
 INC(FCount);
END;

PROCEDURE TPatternEditorUndoList.Insert(Index:INTEGER;Item:TPatternEditorUndoItem);
VAR I:INTEGER;
BEGIN
 IF (Index>=0) AND (Index<FCount) THEN BEGIN
  SetCount(FCount+1);
  I:=FCount-1;
  WHILE I>Index DO BEGIN
   FList^[I]:=FList^[I-1];
   INC(I);
  END;
  FList^[Index]:=Item;
 END ELSE IF Index=FCount THEN BEGIN
  Add(Item);
 END ELSE IF Index>FCount THEN BEGIN
  SetCount(Index);
  Add(Item);
 END;
END;

PROCEDURE TPatternEditorUndoList.Delete(Index:INTEGER);
VAR I,J,K:INTEGER;
BEGIN
 IF (Index>=0) AND (Index<FCount) THEN BEGIN
  K:=FCount-1;
  J:=Index;
  I:=J;
  WHILE I<K DO BEGIN
   FList^[I]:=FList^[I+1];
   INC(I);
  END;
  SetCount(K);
 END;
END;

FUNCTION TPatternEditorUndoList.Remove(Item:PPatternEditorUndoItem):INTEGER;
VAR I,J,K:INTEGER;
BEGIN
 RESULT:=-1;
 K:=FCount;
 J:=-1;
 I:=0;
 WHILE I<K DO BEGIN
  IF @FList^[I]=Item THEN BEGIN
   J:=I;
   BREAK;
  END;
  INC(I);
 END;
 IF J>=0 THEN BEGIN
  DEC(K);
  I:=J;
  WHILE I<K DO BEGIN
   FList^[I]:=FList^[I+1];
   INC(I);
  END;
  SetCount(K);
  RESULT:=J;
 END;
END;

FUNCTION TPatternEditorUndoList.Find(Item:PPatternEditorUndoItem):INTEGER;
VAR I:INTEGER;
BEGIN
 RESULT:=-1;
 I:=0;
 WHILE I<FCount DO BEGIN
  IF @FList^[I]=Item THEN BEGIN
   RESULT:=I;
   EXIT;
  END;
  INC(I);
 END;
END;

FUNCTION TPatternEditorUndoList.IndexOf(Item:PPatternEditorUndoItem):INTEGER;
VAR I:INTEGER;
BEGIN
 RESULT:=-1;
 I:=0;
 WHILE I<FCount DO BEGIN
  IF @FList^[I]=Item THEN BEGIN
   RESULT:=I;
   EXIT;
  END;
  INC(I);
 END;
END;

PROCEDURE TPatternEditorUndoList.Exchange(Index1,Index2:INTEGER);
VAR TempPointer:TPatternEditorUndoItem;
BEGIN
 IF (Index1>=0) AND (Index1<FCount) AND (Index2>=0) AND (Index2<FCount) THEN BEGIN
  TempPointer:=FList^[Index1];
  FList^[Index1]:=FList^[Index2];
  FList^[Index2]:=TempPointer;
 END;
END;

FUNCTION TPatternEditorUndoList.GetItem(Index:INTEGER):TPatternEditorUndoItem;
BEGIN
 FILLCHAR(RESULT,SIZEOF(TPatternEditorUndoItem),#0);
 IF (Index>=0) AND (Index<FCount) THEN RESULT:=FList^[Index];
END;

PROCEDURE TPatternEditorUndoList.SetItem(Index:INTEGER;Value:TPatternEditorUndoItem);
BEGIN
 IF (Index>=0) AND (Index<FCount) THEN FList^[Index]:=Value;
END;

FUNCTION TPatternEditorUndoList.GetItemPointer(Index:INTEGER):PPatternEditorUndoItem;
BEGIN
 RESULT:=NIL;
 IF (Index>=0) AND (Index<FCount) THEN RESULT:=@FList^[Index];
END;

CONSTRUCTOR TPatternEditor.Create(AOwner:TComponent);
VAR Item:TMenuItem;
BEGIN
 INHERITED Create(AOwner);
 ControlStyle:=ControlStyle+[csOpaque];
 LastShift:=[];
 LastKey:=0;
 LShift:=[];
 LKey:=0;
 AltMarkierung:=FALSE;
 Markierung:=FALSE;
 StartMarkierung:=FALSE;
 MarkStartZeile:=0;
 MarkEndeZeile:=0;
 MarkStartKanal:=0;
 MarkEndeKanal:=0;
 AltMarkStartZeile:=-1;
 AltMarkEndeZeile:=-1;
 AltMarkStartKanal:=-1;
 AltMarkEndeKanal:=-1;
 MarkierungAlt:=FALSE;
 AktPos:=0;
 AktKanal:=0;
 AktSichtKanal:=0;
 AktPattern:=0;
 AktZeile:=0;
 MaxSichtbareZeilen:=0;
 CurrentOctave:=4;
 CurrentInstrument:=1;
 TabStop:=TRUE;
 LeftButton:=FALSE;
 RightButton:=FALSE;
 Ziehen:=FALSE;
 ObenKlick:=FALSE;
 ObenKlickKanal:=0;
 DragStartZeile:=-1;
 DragEndeZeile:=-1;
 DragStartKanal:=-1;
 DragEndeKanal:=-1;
 DragMarkStartZeile:=-1;
 DragMarkEndeZeile:=-1;
 DragMarkStartKanal:=-1;
 DragMarkEndeKanal:=-1;
 AltDragMarkStartZeile:=-1;
 AltDragMarkEndeZeile:=-1;
 AltDragMarkStartKanal:=-1;
 AltDragMarkEndeKanal:=-1;
 OnMouseWheel:=MouseWheel;
 OWidth:=-1;
 OHeight:=-1;

 UndoList:=TPatternEditorUndoList.Create;

 HintergrundLoeschen:=TRUE;

 TB:=0;
 TH:=0;

 SelectionPopupMenu:=TPopupMenu.Create(SELF);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='&Copy';
 Item.ShortCut:=ShortCut(WORD('C'),[ssAlt]);
 Item.OnClick:=OnCopy;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='&Paste';
 Item.ShortCut:=ShortCut(WORD('P'),[ssAlt]);
 Item.OnClick:=OnPaste;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='-';
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='C&ut';
 Item.ShortCut:=ShortCut(VK_DELETE,[ssShift]);
 Item.OnClick:=OnCut;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Put (&overwrite)';
 Item.ShortCut:=ShortCut(WORD('O'),[ssAlt]);
 Item.OnClick:=OnPut;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='-';
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='C&lear';
 Item.ShortCut:=ShortCut(VK_SPACE,[]);
 Item.OnClick:=OnClear;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='&Delete';
 Item.ShortCut:=ShortCut(VK_DELETE,[]);
 Item.OnClick:=OnDelete;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='-';
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Transpose -1';
 Item.OnClick:=OnTransposeM1;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Transpose -12';
 Item.OnClick:=OnTransposeM12;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Transpose +12';
 Item.OnClick:=OnTransposeP12;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Transpose +1';
 Item.OnClick:=OnTransposeP1;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='-';
 SelectionPopupMenu.Items.Add(Item);

{Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Change instrument';
 Item.ShortCut:=ShortCut(WORD('I'),[ssAlt]);
 Item.OnClick:=OnInstrumentChange;
 SelectionPopupMenu.Items.Add(Item);}

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='-';
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='&Interpolate effect A column';
 Item.OnClick:=OnInterpolateEffectBA;
 Item.ShortCut:=ShortCut(WORD('K'),[ssAlt]);
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Interpolate &effect B column';
 Item.OnClick:=OnInterpolateEffectB;
 Item.ShortCut:=ShortCut(WORD('X'),[ssAlt]);
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='-';
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Clear effect A col&umn';
 Item.OnClick:=OnClearEffectA;
 SelectionPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(SelectionPopupMenu);
 Item.Caption:='Cle&ar effect B column';
 Item.OnClick:=OnClearEffectB;
 SelectionPopupMenu.Items.Add(Item);

 MemoryPopupMenu:=TPopupMenu.Create(SELF);

 Item:=TMenuItem.Create(MemoryPopupMenu);
 Item.Caption:='&Paste';
 Item.ShortCut:=ShortCut(WORD('P'),[ssAlt]);
 Item.OnClick:=OnPaste;
 MemoryPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(MemoryPopupMenu);
 Item.ShortCut:=ShortCut(WORD('O'),[ssAlt]);
 Item.Caption:='Put (&overwrite)';
 Item.OnClick:=OnPut;
 MemoryPopupMenu.Items.Add(Item);

 ChannelPopupMenu:=TPopupMenu.Create(SELF);

 Item:=TMenuItem.Create(ChannelPopupMenu);
 Item.Caption:='&Mute channel';
 Item.OnClick:=OnMuteChannel;
 ChannelPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(ChannelPopupMenu);
 Item.Caption:='&Unmute channel';
 Item.OnClick:=OnUnmuteChannel;
 ChannelPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(ChannelPopupMenu);
 Item.Caption:='&Mute all except this channel';
 Item.OnClick:=OnMuteAllExceptThisChannel;
 ChannelPopupMenu.Items.Add(Item);

 Item:=TMenuItem.Create(ChannelPopupMenu);
 Item.Caption:='&Unmute all';
 Item.OnClick:=OnUnmuteAll;
 ChannelPopupMenu.Items.Add(Item);

 DoubleBuffered:=FALSE;

 SChannel:='Channel';

 MaxUndoSteps:=24;
END;

DESTRUCTOR TPatternEditor.Destroy;
BEGIN
 ClearUndoList;
 UndoList.Free;
 SelectionPopupMenu.Free;
 MemoryPopupMenu.Free;
 ChannelPopupMenu.Free;
 INHERITED Destroy;
END;

PROCEDURE TPatternEditor.PositionUpdate;
BEGIN
 IF AktKanal<0 THEN AktKanal:=Track.CountOfChannels-1;
 IF AktKanal>=Track.CountOfChannels THEN AktKanal:=0;
 IF AktSichtKanal<AktKanal THEN BEGIN
  WHILE (AktKanal-AktSichtKanal)>=MaxGanzeKaenale DO BEGIN
   INC(AktSichtKanal);
  END;
 END ELSE IF AktSichtKanal>AktKanal THEN BEGIN
  WHILE (AktSichtKanal-AktKanal)>0 DO BEGIN
   DEC(AktSichtKanal);
  END;
 END;
 IF ChannelScroll.Position<>AktSichtKanal THEN BEGIN
  ChannelScrollUpdate;
  ChannelScroll.Position:=AktSichtKanal;
  ChannelScroll.Invalidate;
 END;
END;

FUNCTION TPatternEditor.KanalBreite:INTEGER;
BEGIN
 RESULT:=(LENGTH('NNNIIEPPEPP')*TB)+6;
END;

PROCEDURE TPatternEditor.PaintEx;
CONST Hex:ARRAY[0..15] OF CHAR='0123456789ABCDEF';
VAR R,DR,ZR:TRect;
    I,J,IX,IY,PS,IB,ZX,ZY,MI,NK,NZ:INTEGER;
    S:STRING;
    RowBackGround:TColor;
    OK:BOOLEAN;
    Note:TPatternNote;
    C:CHAR;
 PROCEDURE TextZeichen(CONST S:BYTE);
 VAR C,FC:TColor;
     Cursor:BOOLEAN;
 BEGIN
  IF (ZX>=R.Left) AND (ZX<=(R.Right+TB)) AND (ZY>=R.Top) AND (ZY<=(R.Bottom+TH)) THEN BEGIN
   WITH Canvas DO BEGIN
    Cursor:=(((PS=AktPos) OR ((AktPos=0) AND ((PS=0) OR (PS=1)))) AND (NK=AktKanal) AND (NZ=AktZeile)) AND NOT (Track.Playing AND Track.FollowSong);
    C:=Brush.Color;
    FC:=Font.Color;
    IF Cursor THEN BEGIN
     Brush.Color:=PatternEditorProperties.cBackgroundCursor;
     Brush.Style:=bsSolid;
     Pen.Color:=RowBackGround;
     Pen.Style:=psSolid;
     FillRect(RECT(ZX,ZY,ZX+TB,ZY+TH));
     Font.Color:=PatternEditorProperties.cTextCursor;
    END;
    TextOut(ZX+((TB-TextWidth(CHR(S))) DIV 2),ZY,CHR(S));
    Brush.Color:=C;
    Font.Color:=FC;
   END;
  END;
  INC(ZX,TB);
  INC(PS);
 END;
 PROCEDURE TextEx(CONST S:STRING);
 VAR I:INTEGER;
 BEGIN
  FOR I:=1 TO LENGTH(S) DO TextZeichen(ORD(S[I]));
 END;
 PROCEDURE Text(CONST S:STRING);
 BEGIN
  TextEx(S);
  IF (ZX>=R.Left) AND (ZX<=(R.Right+2)) AND (ZY>=R.Top) AND (ZY<=(R.Bottom+TH)) THEN BEGIN
   WITH Canvas DO BEGIN
    Pen.Color:=PatternEditorProperties.cSeparator;
    INC(ZX);
    MoveTo(ZX,ZY);
    LineTo(ZX,ZY+TH);
    INC(ZX);
   END;
  END ELSE BEGIN
   INC(ZX,2);
  END;
 END;
 PROCEDURE ZeigeSpalte(X,Y:INTEGER;Note:TPatternNote);
 VAR SB:INTEGER;
     C:CHAR;
 BEGIN
  PS:=0;
  ZX:=X;
  ZY:=Y;
  SB:=KanalBreite;
  WITH Canvas DO BEGIN
   Brush.Color:=RowBackGround;
   Brush.Style:=bsSolid;
   Pen.Color:=RowBackGround;
   Pen.Style:=psSolid;
   FillRect(RECT(X,Y,X+SB,Y+TH));
  END;
  Text(Note3C(Note.Note));
  IF Note.Volume<>$FF THEN BEGIN
   Text(Hex2C(Note.Volume));
  END ELSE BEGIN
   Text('..');
  END;
  IF Note.EffectA.Effect<>0 THEN BEGIN
   C:=ABC(Note.EffectA.Effect);
   IF C<>#0 THEN BEGIN
    TextEx(C);
   END ELSE BEGIN
    TextEx('.');
   END;
  END ELSE BEGIN
   TextEx('.');
  END;
  IF (Note.EffectA.Effect<>0) OR (Note.EffectA.Parameter<>0) THEN BEGIN
   Text(Hex2C(Note.EffectA.Parameter));
  END ELSE BEGIN
   Text('..');
  END;
  IF Note.EffectB.Effect<>0 THEN BEGIN
   C:=ABC(Note.EffectB.Effect);
   IF C<>#0 THEN BEGIN
    TextEx(C);
   END ELSE BEGIN
    TextEx('.');
   END;
  END ELSE BEGIN
   TextEx('.');
  END;
  IF (Note.EffectB.Effect<>0) OR (Note.EffectB.Parameter<>0) THEN BEGIN
   TextEx(Hex2C(Note.EffectB.Parameter));
  END ELSE BEGIN
   TextEx('..');
  END;
 END;
BEGIN
 OK:=TRUE;
 IF OK AND NOT ASSIGNED(Track) THEN OK:=FALSE;
 IF AktPattern>=$FE THEN OK:=FALSE;
 IF NOT OK THEN BEGIN
  R:=GetClientRect;
  WITH Canvas DO BEGIN
   Brush.Color:=PatternEditorProperties.cBackground;
   Brush.Style:=bsSolid;
   Pen.Color:=PatternEditorProperties.cBackground;
   Pen.Style:=psSolid;
   FillRect(R);
  END;
// Track.Leave;
  EXIT;
 END;

 IF Track.RowHilightMinor=0 THEN Track.RowHilightMinor:=1;
 IF Track.RowHilightMajor=0 THEN Track.RowHilightMajor:=1;

 MaxZeilen:=64;

 IF MarkStartZeile<0 THEN MarkStartZeile:=0;
 IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
 IF MarkStartKanal<0 THEN MarkStartKanal:=0;
 IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
 IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
 IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
 IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
 IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;

 NK:=-1;
 NZ:=-1;

 RowBackGround:=PatternEditorProperties.cRow;

 R:=GetClientRect;
 WITH Canvas DO BEGIN
  Brush.Color:=PatternEditorProperties.cBackground;
  Brush.Style:=bsSolid;
  Pen.Color:=PatternEditorProperties.cBackground;
  Pen.Style:=psSolid;
  IF HintergrundLoeschen THEN FillRect(R);
  HintergrundLoeschen:=FALSE;

  Font.Name:=PatternEditorProperties.FontName;
  Font.Size:=PatternEditorProperties.FontSize;
  Font.Style:=PatternEditorProperties.FontStyle;
  Font.Color:=PatternEditorProperties.cText;

  IF (TB=0) OR (TH=0) THEN BEGIN
   S:='ABCDEFGHhnal012345789-.#';
   FOR J:=0 TO LENGTH(S) DO BEGIN
    C:=S[J];
    IF TB<TextWidth(C) THEN TB:=TextWidth(C);
   END;
   INC(TB);
   TH:=TextHeight(S)+1;
  END;
 END;

 IB:=KanalBreite+1;
 DR:=R;
 IF (DR.Right-DR.Left)=OWidth THEN BEGIN
  INC(DR.Right,IB);
 END ELSE BEGIN
  DEC(DR.Left);
  INC(DR.Right);
 END;
 IF (DR.Bottom-DR.Top)=OHeight THEN BEGIN
  INC(DR.Bottom,TH);
 END ELSE BEGIN
  DEC(DR.Bottom);
  INC(DR.Bottom);
 END;

 MaxKaenale:=0;
 IF (R.Right-R.Left)=OWidth THEN MaxGanzeKaenale:=0;
 J:=AktSichtKanal;
 IX:=2+TB*2+7;
 IY:=2;
 WHILE IX<DR.Right DO BEGIN
  IF (IX>=DR.Left) AND ((IX+IB)<=DR.Right) AND (IY>=DR.Top) AND ((IY+TH)<=DR.Bottom) THEN BEGIN
   IF J<Track.CountOfChannels THEN BEGIN
    WITH Canvas DO BEGIN
     Brush.Color:=PatternEditorProperties.cRow;
     Brush.Style:=bsSolid;
     Pen.Color:=PatternEditorProperties.cRow;
     Pen.Style:=psSolid;
     FillRect(RECT(IX,IY,IX+IB-1,IY+TH-1));
    END;
    STR(J+1,S);
    S:=SChannel+' '+S;
    ZX:=IX+((IB-(LENGTH(S)*TB)) DIV 2);
    ZY:=IY;
    TextEx(S);
    WITH Canvas DO BEGIN
     IF Track.Channels[J].Mute<>0 THEN BEGIN
      Pen.Width:=2;
      Pen.Color:=PatternEditorProperties.cMutedChannel;
      MoveTo(IX,IY);
      LineTo(IX+IB-1,IY+TH-1);
      MoveTo(IX+IB-1,IY);
      LineTo(IX,IY+TH-1);
      Pen.Width:=1;
     END;
     IF Track.Channels[J].Active=0 THEN BEGIN
      Pen.Width:=2;
      Pen.Color:=PatternEditorProperties.cDisabledChannel;
      MoveTo(IX,IY);
      LineTo(IX+IB-1,IY+TH-1);
      MoveTo(IX+IB-1,IY);
      LineTo(IX,IY+TH-1);
      Pen.Width:=1;
     END;
    END;
    INC(J);
   END ELSE BEGIN
    WITH Canvas DO BEGIN
     Brush.Color:=PatternEditorProperties.cBackground;
     Brush.Style:=bsSolid;
     Pen.Color:=PatternEditorProperties.cBackground;
     Pen.Style:=psSolid;
     FillRect(RECT(IX,IY,IX+IB-1,IY+TH-1));
    END;
   END;
  END;
  INC(IX,IB+4);
  IF (R.Right-R.Left)=OWidth THEN IF IX<R.Right THEN INC(MaxGanzeKaenale);
  INC(MaxKaenale);
 END;

 I:=0;
 IY:=2+TH+4;
 WHILE IY<OHeight DO BEGIN
  INC(I);
  INC(IY,TH+1);
 END;
 MI:=I;
 MaxSichtbareZeilen:=I;

 I:=AktZeile-(MI DIV 2)+1;
 IX:=2;
 IY:=2+TH+4;
 WHILE IY<DR.Bottom DO BEGIN
  IF (IX>=DR.Left) AND ((IX+((TB*2)+7))<=DR.Right) AND (IY>=DR.Top) AND ((IY+TH)<=DR.Bottom) THEN BEGIN
   IF (I>=0) AND (I<MaxZeilen) THEN BEGIN
    RowBackGround:=PatternEditorProperties.cRow;
    Canvas.Font.Color:=PatternEditorProperties.cText;
    IF (I MOD Track.RowHilightMinor)=0 THEN RowBackGround:=PatternEditorProperties.cRowHilightMinor;
    IF (I MOD Track.RowHilightMajor)=0 THEN RowBackGround:=PatternEditorProperties.cRowHilightMajor;
    IF (I=Track.CurrentRow) AND (Track.PatternOrder[Track.CurrentPatternOrder]=AktPattern) AND Track.Playing AND NOT Track.FollowSong THEN BEGIN
     RowBackGround:=PatternEditorProperties.cBackgroundPlay;
     Canvas.Font.Color:=PatternEditorProperties.cTextPlay;
    END ELSE IF I=AktZeile THEN BEGIN
     IF Track.Playing AND Track.FollowSong THEN BEGIN
      RowBackGround:=PatternEditorProperties.cBackgroundPlay;
      Canvas.Font.Color:=PatternEditorProperties.cTextPlay;
     END ELSE BEGIN
      RowBackGround:=PatternEditorProperties.cBackgroundCursor;
      Canvas.Font.Color:=PatternEditorProperties.cTextCursor;
     END;
    END;
    WITH Canvas DO BEGIN
     Brush.Color:=RowBackGround;
     Brush.Style:=bsSolid;
     Pen.Color:=RowBackGround;
     Pen.Style:=psSolid;
     FillRect(RECT(IX,IY,IX+TB*2-1,IY+TH-1));
    END;
    ZX:=IX;
    ZY:=IY;
    S:=Hex2C(I);
    Text(S);
   END ELSE BEGIN
    WITH Canvas DO BEGIN
     Brush.Color:=PatternEditorProperties.cBackground;
     Brush.Style:=bsSolid;
     Pen.Color:=PatternEditorProperties.cBackground;
     Pen.Style:=psSolid;
     FillRect(RECT(IX,IY,IX+TB*2+2,IY+TH));
    END;
   END;
  END;
  INC(I);
  INC(IY,TH+1);
 END;

 IB:=KanalBreite+1;
 I:=AktZeile-(MI DIV 2)+1;
 IY:=2+TH+4;
 WHILE IY<DR.Bottom DO BEGIN
  IF (I>=0) AND (I<MaxZeilen) THEN BEGIN
   J:=AktSichtKanal;
   IX:=2+TB*2+7;
   WHILE IX<DR.Right DO BEGIN
    IF (IX>=DR.Left) AND ((IX+IB)<=DR.Right) AND (IY>=DR.Top) AND ((IY+TH)<=DR.Bottom) THEN BEGIN
     IF J<Track.CountOfChannels THEN BEGIN
      RowBackGround:=PatternEditorProperties.cRow;
      Canvas.Font.Color:=PatternEditorProperties.cText;
      IF (I MOD Track.RowHilightMinor)=0 THEN RowBackGround:=PatternEditorProperties.cRowHilightMinor;
      IF (I MOD Track.RowHilightMajor)=0 THEN RowBackGround:=PatternEditorProperties.cRowHilightMajor;
      IF (I=Track.CurrentRow) AND (Track.PatternOrder[Track.CurrentPatternOrder]=AktPattern) AND Track.Playing AND NOT Track.FollowSong THEN BEGIN
       RowBackGround:=PatternEditorProperties.cBackgroundPlay;
       Canvas.Font.Color:=PatternEditorProperties.cTextPlay;
      END ELSE IF I=AktZeile THEN BEGIN
       IF Track.Playing AND Track.FollowSong THEN BEGIN
        RowBackGround:=PatternEditorProperties.cBackgroundPlay;
        Canvas.Font.Color:=PatternEditorProperties.cTextPlay;
       END;
      END;
      IF Markierung AND (((J)>=MarkStartKanal) AND ((J)<=MarkEndeKanal)) AND ((I>=MarkStartZeile) AND (I<=MarkEndeZeile)) THEN BEGIN
       RowBackGround:=PatternEditorProperties.cSelection;
       Canvas.Font.Color:=PatternEditorProperties.cTextSelection;
      END;
      Note:=Track.Patterns[AktPattern,I AND $3F,J];
      NK:=J;
      NZ:=I;
      ZeigeSpalte(IX,IY,Note);
     END ELSE BEGIN
      WITH Canvas DO BEGIN
       Brush.Color:=PatternEditorProperties.cBackground;
       Brush.Style:=bsSolid;
       Pen.Color:=PatternEditorProperties.cBackground;
       Pen.Style:=psSolid;
       FillRect(RECT(IX,IY,IX+IB,IY+TH));
      END;
     END;
    END;
    INC(J);
    INC(IX,IB+4);
   END;
  END ELSE BEGIN
   IX:=2+TB*2+7;
   IF (IX>=DR.Left) AND (IY>=DR.Top) AND ((IY+TH)<=DR.Bottom) THEN BEGIN
    WITH Canvas DO BEGIN
     Brush.Color:=PatternEditorProperties.cBackground;
     Brush.Style:=bsSolid;
     Pen.Color:=PatternEditorProperties.cBackground;
     Pen.Style:=psSolid;
     FillRect(RECT(IX,IY,OWidth,IY+TH));
    END;
   END;
  END;
  INC(I);
  INC(IY,TH+1);
 END;

 IBB:=KanalBreite+1;
 INC(IBB,4);

 IF Ziehen AND Markierung THEN BEGIN
  ZR.Top:=((TH+1)*(DragMarkStartZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
  ZR.Bottom:=((TH+1)*(DragMarkEndeZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
  ZR.Left:=(2+(TB*2)+7)+(IBB*(DragMarkStartKanal-AktSichtKanal));
  ZR.Right:=(2+(TB*2)+7)+(IBB*(DragMarkEndeKanal-AktSichtKanal))+KanalBreite;
  WITH Canvas DO BEGIN
   Brush.Style:=bsClear;
   Pen.Color:=PatternEditorProperties.cSelectionDrag;
   Pen.Style:=psSolid;
   Rectangle(ZR);
  END;
 END;

//Track.Leave;
END;

PROCEDURE TPatternEditor.PaintExEx;
VAR OK:BOOLEAN;
BEGIN
 PaintEx;
 ChannelScrollUpdate;
 OK:=TRUE;
 IF OK AND NOT ASSIGNED(Track) THEN OK:=FALSE;
 TRY
  RowScroll.Min:=0;
  IF (MaxZeilen>0) AND OK THEN BEGIN
   RowScroll.Max:=$3F;
   RowScroll.Enabled:=TRUE;
  END ELSE BEGIN
   RowScroll.Max:=0;
   RowScroll.Enabled:=FALSE;
  END;
 EXCEPT
 END;
END;

PROCEDURE TPatternEditor.Paint;
BEGIN
 PaintExEx;
END;

PROCEDURE TPatternEditor.ResizeEx;
BEGIN
 OWidth:=ClientWidth;
 OHeight:=ClientHeight;
END;

PROCEDURE TPatternEditor.Resize;
BEGIN
 INHERITED Resize;
 ResizeEx;
END;

PROCEDURE TPatternEditor.ChannelScrollUpdate;
VAR OK:BOOLEAN;
BEGIN
 OK:=TRUE;
 IF OK AND NOT ASSIGNED(Track) THEN OK:=FALSE;
 IF ASSIGNED(Track) THEN BEGIN
  TRY
   ChannelScroll.Min:=0;
   IF ((Track.CountOfChannels-MaxGanzeKaenale)>0) AND OK THEN BEGIN
    ChannelScroll.Max:=Track.CountOfChannels-MaxGanzeKaenale;
    ChannelScroll.Enabled:=TRUE;
   END ELSE BEGIN
    ChannelScroll.Max:=1;
    ChannelScroll.Enabled:=FALSE;
   END;
   AktualisiereStatusBar;
  EXCEPT
  END;
 END;
END;

PROCEDURE TPatternEditor.RowScrollUpdate;
VAR OK:BOOLEAN;
BEGIN
 OK:=TRUE;
 IF OK AND NOT ASSIGNED(Track) THEN OK:=FALSE;
 TRY
  RowScroll.Min:=0;
  IF (MaxZeilen>0) AND OK THEN BEGIN
   RowScroll.Max:=$3F;
   RowScroll.Enabled:=TRUE;
  END ELSE BEGIN
   RowScroll.Max:=0;
   RowScroll.Enabled:=FALSE;
  END;
  IF AktZeile<=$3F THEN BEGIN
   IF RowScroll.Position<>AktZeile THEN BEGIN
    RowScroll.Position:=AktZeile;
    RowScroll.Invalidate;
    AktualisiereStatusBar;
   END;
  END;
 EXCEPT
 END;
END;

PROCEDURE TPatternEditor.WMEraseBkgnd(VAR Message:TMessage);
BEGIN
 Message.Result:=1;
 HintergrundLoeschen:=TRUE;
END;

PROCEDURE TPatternEditor.WMGetDlgCode(VAR Message:TMessage);
BEGIN
 Message.Result:=DLGC_WANTARROWS OR DLGC_WANTCHARS OR DLGC_WANTALLKEYS OR DLGC_WANTTAB;
END;

PROCEDURE TPatternEditor.KeyDown(VAR Key:WORD;Shift:TShiftState);
BEGIN
 INHERITED KeyDown(Key,Shift);
 LastShift:=Shift;
 LastKey:=Key;
 KeyProcess;
 LastShift:=[];
 LastKey:=0;
 Key:=0;
END;

PROCEDURE TPatternEditor.KeyUp(VAR Key:WORD;Shift:TShiftState);
BEGIN
 INHERITED KeyUp(Key,Shift);
 Key:=0;
END;{}

PROCEDURE TPatternEditor.MouseDown(Button:TMouseButton;Shift:TShiftState;X,Y:INTEGER);
VAR Zeile,Kanal,KX:INTEGER;
BEGIN
 IF NOT Focused THEN BEGIN
  SetFocus;
  LastShift:=[];
  LastKey:=0;
 END;
 Ziehen:=FALSE;
 LeftButton:=Button=mbLeft;
 RightButton:=Button=mbRight;
 ObenKlick:=FALSE;
 ObenKlickKanal:=0;
 IF Markierung AND LeftButton AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  IF Y>=TB THEN BEGIN
   Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
   IF X>((TB*2)+9) THEN BEGIN
    Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
    IF (Zeile>=MarkStartZeile) AND (Zeile<=MarkEndeZeile) AND
       (Kanal>=MarkStartKanal) AND (Kanal<=MarkEndeKanal) THEN BEGIN
     Ziehen:=TRUE;
    END;
   END;
  END;
 END;
 IF Ziehen THEN BEGIN
  IF Track.Playing AND Track.FollowSong THEN EXIT;
  IF Markierung THEN BEGIN
   LX:=X;
   LY:=Y;
   IF Y>=TB THEN BEGIN
    Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
    IF X>((TB*2)+9) THEN BEGIN
     Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
    END ELSE BEGIN
     Ziehen:=FALSE;
     LeftButton:=FALSE;
     EXIT;
    END;
    DragStartZeile:=Zeile;
    DragStartKanal:=Kanal;
    DragEndeZeile:=Zeile;
    DragEndeKanal:=Kanal;
    DragMarkStartZeile:=MarkStartZeile;
    DragMarkStartKanal:=MarkStartKanal;
    DragMarkEndeZeile:=MarkEndeZeile;
    DragMarkEndeKanal:=MarkEndeKanal;
    AltDragMarkStartZeile:=MarkStartZeile;
    AltDragMarkStartKanal:=MarkStartKanal;
    AltDragMarkEndeZeile:=MarkEndeZeile;
    AltDragMarkEndeKanal:=MarkEndeKanal;
    StartDragMarkStartZeile:=MarkStartZeile;
    StartDragMarkStartKanal:=MarkStartKanal;
    StartDragMarkEndeZeile:=MarkEndeZeile;
    StartDragMarkEndeKanal:=MarkEndeKanal;
    Ziehen:=TRUE;
    InvalidateDragChange;
   END;
  END ELSE BEGIN
   Ziehen:=FALSE;
   LeftButton:=FALSE;
  END;
  EXIT;
 END ELSE IF LeftButton AND (NOT Ziehen) AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  IF Track.Playing AND Track.FollowSong THEN EXIT;
  LX:=X;
  LY:=Y;
  Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
  IF X>((TB*2)+9) THEN BEGIN
   Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
  END ELSE BEGIN
   EXIT;
  END;
  IF Y<=TB THEN BEGIN
   ObenKlick:=TRUE;
   ObenKlickKanal:=Kanal;
   IF Markierung THEN BEGIN
    Markierung:=FALSE;
    InvalidateSelectionChange;
   END;
   IF (Kanal>=0) AND (Kanal<Track.CountOfChannels) THEN BEGIN
    IF LeftButton THEN BEGIN
     Markierung:=TRUE;
     MarkStartKanal:=Kanal;
     MarkEndeKanal:=Kanal;
     MarkStartZeile:=0;
     MarkEndeZeile:=$3F;
     InvalidateSelection;
    END;
   END;
   EXIT;
  END;
  KX:=(2+(TB*2)+7)+(IBB*(Kanal-AktSichtKanal));
  KX:=X-KX;
  IF KX<(TB*2) THEN BEGIN
   AktPos:=0;
  END ELSE IF KX<(TB*3) THEN BEGIN
   AktPos:=2;
  END ELSE IF KX<((TB*3)+2+TB) THEN BEGIN
   AktPos:=3;
  END ELSE IF KX<((TB*3)+2+(TB*2)) THEN BEGIN
   AktPos:=4;
  END ELSE IF KX<((TB*3)+2+(TB*2)+2+TB) THEN BEGIN
   AktPos:=5;
  END ELSE IF KX<((TB*3)+2+(TB*2)+2+(TB*2)) THEN BEGIN
   AktPos:=6;
  END ELSE IF KX<((TB*3)+2+(TB*2)+2+(TB*3)) THEN BEGIN
   AktPos:=7;
  END ELSE IF KX<((TB*3)+2+(TB*2)+2+(TB*3)+2+TB) THEN BEGIN
   AktPos:=8;
  END ELSE IF KX<((TB*3)+2+(TB*2)+2+(TB*3)+2+(TB*2)) THEN BEGIN
   AktPos:=9;
  END ELSE IF KX<((TB*3)+2+(TB*2)+2+(TB*3)+2+(TB*2)+2+TB) THEN BEGIN
   AktPos:=10;
  END;
  LZ:=Zeile;
  LK:=AktKanal;
  IF Zeile>=0 THEN BEGIN
   AltMarkierung:=Markierung;
   IF NOT Markierung THEN BEGIN
    AltMarkStartZeile:=-1;
    AltMarkEndeZeile:=-1;
    AltMarkStartKanal:=-1;
    AltMarkEndeKanal:=-1;
   END;
   StartMarkierung:=TRUE;
   MarkStartZeile:=Zeile;
   MarkEndeZeile:=Zeile;
   MarkStartKanal:=Kanal;
   MarkEndeKanal:=Kanal;
   MarkZeile:=Zeile;
   MarkKanal:=Kanal;
   IF Kanal=AktKanal THEN InvalidateRow;
  END;
 END ELSE BEGIN
  IF Button=mbRight THEN BEGIN
   IF Y<=TB THEN BEGIN
    IF X>((TB*2)+9) THEN BEGIN
     Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
    END ELSE BEGIN
     EXIT;
    END;
    ObenKlick:=TRUE;
    ObenKlickKanal:=Kanal;
    ChannelPopupMenu.Popup(Mouse.CursorPos.X,Mouse.CursorPos.Y);
   END ELSE IF NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
    IF AktPattern>=$FE THEN EXIT;
    IF Markierung THEN BEGIN
     SelectionPopupMenu.Popup(Mouse.CursorPos.X,Mouse.CursorPos.Y);
    END ELSE IF LENGTH((Form AS TMainForm).PatternEditorMemory)>0 THEN BEGIN
     MemoryPopupMenu.Popup(Mouse.CursorPos.X,Mouse.CursorPos.Y);
    END;
   END;
  END;
 END;
 AktualisiereStatusBar;
END;

PROCEDURE TPatternEditor.MouseMove(Shift:TShiftState;X,Y:INTEGER);
VAR Zeile,Kanal,BisKanal,Tausch:INTEGER;
BEGIN
 IF ObenKlick THEN BEGIN
  Kanal:=ObenKlickKanal;
  BisKanal:=ObenKlickKanal;
  IF X>((TB*2)+9) THEN BEGIN
   BisKanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
  END;
  IF (Kanal>=0) AND (Kanal<Track.CountOfChannels) THEN BEGIN
   IF (X>((TB*2)+9)) AND LeftButton THEN BEGIN
    IF Kanal>BisKanal THEN BEGIN
     Tausch:=BisKanal;
     BisKanal:=Kanal;
     Kanal:=Tausch;
    END;
    Markierung:=TRUE;
    MarkStartKanal:=Kanal;
    MarkEndeKanal:=BisKanal;
    MarkStartZeile:=0;
    MarkEndeZeile:=$3F;
    InvalidateSelectionChange;
   END;
  END;
 END ELSE IF Ziehen AND LeftButton AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  IF X>=(OWidth-TB) THEN BEGIN
   INC(AktSichtKanal);
   IF AktSichtKanal>=(Track.CountOfChannels-MaxGanzeKaenale) THEN BEGIN
    AktSichtKanal:=Track.CountOfChannels-MaxGanzeKaenale;
   END;
   ChannelScrollUpdate;
   Invalidate;
  END ELSE IF X<=2+TB+7+TB THEN BEGIN
   DEC(AktSichtKanal);
   IF AktSichtKanal<0 THEN AktSichtKanal:=0;
   ChannelScrollUpdate;
   Invalidate;
  END;
  IF Y>=(OHeight-TH) THEN BEGIN
   INC(AktZeile);
   IF AktZeile>$3F THEN BEGIN
    AktZeile:=$3F;
   END;
   RowScrollUpdate;
   Invalidate;
  END ELSE IF Y<=TH+TH+4 THEN BEGIN
   DEC(AktZeile);
   IF AktZeile<0 THEN AktZeile:=0;
   RowScrollUpdate;
   Invalidate;
  END;
  IF (LX<>X) OR (LY<>Y) THEN BEGIN
   LX:=X;
   LY:=Y;
   IF Y>=TB THEN BEGIN
    Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
    IF X>((TB*2)+9) THEN BEGIN
     Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
     DragEndeZeile:=Zeile;
     DragEndeKanal:=Kanal;
     DragMarkStartZeile:=MarkStartZeile+(DragEndeZeile-DragStartZeile);
     DragMarkStartKanal:=MarkStartKanal+(DragEndeKanal-DragStartKanal);
     DragMarkEndeZeile:=MarkEndeZeile+(DragEndeZeile-DragStartZeile);
     DragMarkEndeKanal:=MarkEndeKanal+(DragEndeKanal-DragStartKanal);
    END;
   END;
  END;
  InvalidateDragChange;
 END ELSE IF (StartMarkierung OR Markierung) AND LeftButton AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  StartMarkierung:=FALSE;
  Markierung:=TRUE;
  IF X>=(OWidth-TB) THEN BEGIN
   INC(AktSichtKanal);
   IF AktSichtKanal>=(Track.CountOfChannels-MaxGanzeKaenale) THEN BEGIN
    AktSichtKanal:=Track.CountOfChannels-MaxGanzeKaenale;
   END;
   ChannelScrollUpdate;
   Invalidate;
  END ELSE IF X<=2+TB+7+TB THEN BEGIN
   DEC(AktSichtKanal);
   IF AktSichtKanal<0 THEN AktSichtKanal:=0;
   ChannelScrollUpdate;
   Invalidate;
  END;
  IF Y>=(OHeight-TH) THEN BEGIN
   INC(AktZeile);
   IF AktZeile>$3F THEN BEGIN
    AktZeile:=$3F;
   END;
   RowScrollUpdate;
   Invalidate;
  END ELSE IF Y<=TH+TH+4 THEN BEGIN
   DEC(AktZeile);
   IF AktZeile<0 THEN AktZeile:=0;
   RowScrollUpdate;
   Invalidate;
  END;
  IF LX<>X THEN BEGIN
   LX:=X;
   IF X>((TB*2)+9) THEN BEGIN
    Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
   END ELSE BEGIN
    Kanal:=AktSichtKanal;
   END;
   LK:=Kanal;
   IF Kanal<=MarkKanal THEN BEGIN
    MarkStartKanal:=Kanal;
    MarkEndeKanal:=MarkKanal;
   END ELSE IF Kanal>MarkKanal THEN BEGIN
    MarkStartKanal:=MarkKanal;
    MarkEndeKanal:=Kanal;
   END;
   IF MarkEndeKanal<MarkStartKanal THEN BEGIN
    Tausch:=MarkStartKanal;
    MarkStartKanal:=MarkEndeKanal;
    MarkEndeKanal:=Tausch;
   END;
   IF MarkStartZeile<0 THEN MarkStartZeile:=0;
   IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
   IF MarkStartKanal<0 THEN MarkStartKanal:=0;
   IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
   IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
   IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
   IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
   IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  END;
  IF LY<>Y THEN BEGIN
   LY:=Y;
   IF Y<TH THEN BEGIN
    Zeile:=AktZeile-(MaxSichtbareZeilen DIV 2);
   END ELSE BEGIN
    Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
   END;
   IF Zeile<0 THEN BEGIN
    Zeile:=0;
   END ELSE IF Zeile>=64 THEN BEGIN
    Zeile:=64-1;
   END;
   IF Zeile>=0 THEN BEGIN
    IF (LZ<>Zeile) THEN BEGIN
     LZ:=Zeile;
     IF Zeile<=MarkZeile THEN BEGIN
      MarkStartZeile:=Zeile;
      MarkEndeZeile:=MarkZeile;
     END ELSE IF Zeile>MarkZeile THEN BEGIN
      MarkStartZeile:=MarkZeile;
      MarkEndeZeile:=Zeile;
     END;
     IF MarkEndeZeile<MarkStartZeile THEN BEGIN
      Tausch:=MarkStartZeile;
      MarkStartZeile:=MarkEndeZeile;
      MarkEndeZeile:=Tausch;
     END;
    END;
   END;
   IF MarkStartZeile<0 THEN MarkStartZeile:=0;
   IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
   IF MarkStartKanal<0 THEN MarkStartKanal:=0;
   IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
   IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
   IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
   IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
   IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
   InvalidateSelectionChange;
  END;
 END;
 AktualisiereStatusBar;
END;

PROCEDURE TPatternEditor.MouseUp(Button:TMouseButton;Shift:TShiftState;X,Y:INTEGER);
VAR Zeile,Kanal,BisKanal,Tausch:INTEGER;
    Zaehler,SubZaehler,K,L:INTEGER;
    DragPattern:TPattern;
    EineNote:TPatternNote;
BEGIN
 IF ObenKlick THEN BEGIN
  IF Markierung THEN BEGIN
   Markierung:=FALSE;
   InvalidateSelectionChange;
  END;
  ObenKlick:=FALSE;
  Kanal:=ObenKlickKanal;
  BisKanal:=ObenKlickKanal;
  IF X>((TB*2)+9) THEN BEGIN
   BisKanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
  END;
  IF (Kanal>=0) AND (Kanal<Track.CountOfChannels) THEN BEGIN
   IF LeftButton OR (Button=mbLeft) THEN BEGIN
    IF Track.Playing AND Track.FollowSong THEN EXIT;
    IF Kanal>BisKanal THEN BEGIN
     L:=BisKanal;
     BisKanal:=Kanal;
     Kanal:=L;
    END;
    Markierung:=TRUE;
    MarkStartKanal:=Kanal;
    MarkEndeKanal:=BisKanal;
    MarkStartZeile:=0;
    MarkEndeZeile:=$3F;
    InvalidateSelectionChange;
   END;
  END;
  LeftButton:=FALSE;
  RightButton:=FALSE;
 END ELSE IF Ziehen AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  IF Track.Playing AND Track.FollowSong THEN EXIT;
  LeftButton:=FALSE;
  IF (LX<>X) OR (LY<>Y) THEN BEGIN
   LX:=X;
   LY:=Y;
   IF Y>=TB THEN BEGIN
    Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
    IF X>((TB*2)+9) THEN BEGIN
     Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
     DragEndeZeile:=Zeile;
     DragEndeKanal:=Kanal;
     DragMarkStartZeile:=MarkStartZeile+(DragEndeZeile-DragStartZeile);
     DragMarkStartKanal:=MarkStartKanal+(DragEndeKanal-DragStartKanal);
     DragMarkEndeZeile:=MarkEndeZeile+(DragEndeZeile-DragStartZeile);
     DragMarkEndeKanal:=MarkEndeKanal+(DragEndeKanal-DragStartKanal);
    END;
   END;
  END;
  IF (ABS(DragEndeZeile-DragStartZeile)>0) OR (ABS(DragEndeKanal-DragStartKanal)>0) THEN BEGIN
   NewUndoEntry;
   Track.Enter;
   DragPattern:=Track.Patterns[AktPattern];
   IF MarkEndeZeile<MarkStartZeile THEN BEGIN
    Tausch:=MarkStartZeile;
    MarkStartZeile:=MarkEndeZeile;
    MarkEndeZeile:=Tausch;
   END;
   IF MarkEndeKanal<MarkStartKanal THEN BEGIN
    Tausch:=MarkStartKanal;
    MarkStartKanal:=MarkEndeKanal;
    MarkEndeKanal:=Tausch;
   END;
   FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
    FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
     EineNote.Note:=0;
     EineNote.Volume:=$FF;
     EineNote.EffectA.Effect:=0;
     EineNote.EffectA.Parameter:=0;
     EineNote.EffectB.Effect:=0;
     EineNote.EffectB.Parameter:=0;
     Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
    END;
   END;
   FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
    FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
     K:=Zaehler+(DragEndeZeile-DragStartZeile);
     L:=SubZaehler+(DragEndeKanal-DragStartKanal);
     IF (K>=0) AND (K<64) THEN BEGIN
      IF (L>=0) AND (L<Track.CountOfChannels) THEN BEGIN
       EineNote:=DragPattern[Zaehler,SubZaehler];
       Track.Patterns[AktPattern,K,L]:=EineNote;
      END;
     END;
    END;
   END;
   Track.Changed:=TRUE;
   Track.Leave;
  END;
  MarkStartZeile:=DragMarkStartZeile;
  MarkStartKanal:=DragMarkStartKanal;
  MarkEndeZeile:=DragMarkEndeZeile;
  MarkEndeKanal:=DragMarkEndeKanal;
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  InvalidateDragChange;
  Ziehen:=FALSE;
  DragStartZeile:=-1;
  DragStartKanal:=-1;
  DragEndeZeile:=-1;
  DragEndeKanal:=-1;
 END ELSE IF (StartMarkierung OR Markierung) AND LeftButton AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  IF Track.Playing AND Track.FollowSong THEN EXIT;
  StartMarkierung:=FALSE;
  Markierung:=TRUE;
  LeftButton:=FALSE;
  IF LX<>X THEN BEGIN
   LX:=X;
   IF X>((TB*2)+9) THEN BEGIN
    Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
   END ELSE BEGIN
    EXIT;
   END;
   LK:=Kanal;
   IF Kanal<=MarkKanal THEN BEGIN
    MarkStartKanal:=Kanal;
    MarkEndeKanal:=MarkKanal;
   END ELSE IF Kanal>MarkKanal THEN BEGIN
    MarkStartKanal:=MarkKanal;
    MarkEndeKanal:=Kanal;
   END;
   IF MarkEndeKanal<MarkStartKanal THEN BEGIN
    Tausch:=MarkStartKanal;
    MarkStartKanal:=MarkEndeKanal;
    MarkEndeKanal:=Tausch;
   END;
   IF MarkStartZeile<0 THEN MarkStartZeile:=0;
   IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
   IF MarkStartKanal<0 THEN MarkStartKanal:=0;
   IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
   IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
   IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
   IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
   IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  END;
  IF LY<>Y THEN BEGIN
   LY:=Y;
   IF Y<TB THEN EXIT;
   Zeile:=((Y-4) DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
   IF Zeile>=0 THEN BEGIN
    IF (LZ<>Zeile) THEN BEGIN
     LZ:=Zeile;
     IF Zeile<=MarkZeile THEN BEGIN
      MarkStartZeile:=Zeile;
      MarkEndeZeile:=MarkZeile;
     END ELSE IF Zeile>MarkZeile THEN BEGIN
      MarkStartZeile:=MarkZeile;
      MarkEndeZeile:=Zeile;
     END;
     IF MarkEndeZeile<MarkStartZeile THEN BEGIN
      Tausch:=MarkStartZeile;
      MarkStartZeile:=MarkEndeZeile;
      MarkEndeZeile:=Tausch;
     END;
    END;
   END;
   IF MarkStartZeile<0 THEN MarkStartZeile:=0;
   IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
   IF MarkStartKanal<0 THEN MarkStartKanal:=0;
   IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
   IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
   IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
   IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
   IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
   InvalidateSelectionChange;
  END;
  IF (ABS(MarkEndeZeile-MarkStartZeile)=0) AND (ABS(MarkEndeKanal-MarkStartKanal)=0) THEN BEGIN
   Markierung:=FALSE;
   AktZeile:=MarkStartZeile;
   AktKanal:=MarkStartKanal;
   Invalidate;
  END;
 END;
 AktualisiereStatusBar;
 RightButton:=FALSE;
END;

PROCEDURE TPatternEditor.DblClick;
BEGIN
 INHERITED DblClick;
 IF Markierung THEN BEGIN
  Markierung:=FALSE;
  InvalidateSelectionChange;
 END;
END;

PROCEDURE TPatternEditor.ClearUndoList;
BEGIN
 WHILE UndoList.Count>0 DO UndoList.Delete(0);
END;

PROCEDURE TPatternEditor.NewUndoEntry;
VAR Item:TPatternEditorUndoItem;
BEGIN
 IF ASSIGNED(Track) THEN BEGIN
  IF UndoList.Count>=MaxUndoSteps THEN UndoList.Delete(0);
  Item.Pattern:=Track.Patterns[AktPattern];
  Item.PatternNr:=AktPattern;
  Item.Zeile:=AktZeile;
  Item.Kanal:=AktKanal;
  Item.SichtKanal:=AktSichtKanal;
  Item.Markierung:=Markierung;
  Item.MarkStartZeile:=MarkStartZeile;
  Item.MarkEndeZeile:=MarkEndeZeile;
  Item.MarkStartKanal:=MarkStartKanal;
  Item.MarkEndeKanal:=MarkEndeKanal;
  UndoList.Add(Item);
 END;
END;

PROCEDURE TPatternEditor.DoUndo;
VAR Item:TPatternEditorUndoItem;
BEGIN
 IF ASSIGNED(Track) THEN BEGIN
  IF UndoList.Count>0 THEN BEGIN
   Item:=UndoList[UndoList.Count-1];
   UndoList.Delete(UndoList.Count-1);
   AktPattern:=Item.PatternNr;
   Track.Enter;
   Track.Patterns[AktPattern]:=Item.Pattern;
   AktZeile:=Item.Zeile;
   AktKanal:=Item.Kanal;
   AktSichtKanal:=Item.SichtKanal;
   Markierung:=Item.Markierung;
   MarkStartZeile:=Item.MarkStartZeile;
   MarkEndeZeile:=Item.MarkEndeZeile;
   MarkStartKanal:=Item.MarkStartKanal;
   MarkEndeKanal:=Item.MarkEndeKanal;
   Track.Changed:=TRUE;
   Track.Leave;
   Invalidate;
  END;
  AktualisiereStatusBar;
 END;
END;

PROCEDURE TPatternEditor.ClearMemory;
VAR Zaehler:INTEGER;
BEGIN
 FOR Zaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory)-1 DO SETLENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler],0);
 SETLENGTH((Form AS TMainForm).PatternEditorMemory,0);
END;

PROCEDURE TPatternEditor.ClearSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
     EineNote.Note:=0;
     EineNote.Volume:=$FF;
     EineNote.EffectA.Effect:=0;
     EineNote.EffectA.Parameter:=0;
     EineNote.EffectB.Effect:=0;
     EineNote.EffectB.Parameter:=0;
     Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
   END;
  END;
  AktualisiereStatusBar;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.DeleteSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    SubSubSubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    FOR SubSubSubZaehler:=MarkStartZeile TO 64-2 DO BEGIN
     EineNote:=Track.Patterns[AktPattern,SubSubSubZaehler+1,SubZaehler];
     Track.Patterns[AktPattern,SubSubSubZaehler,SubZaehler]:=EineNote;
    END;
    EineNote.Note:=0;
    EineNote.Volume:=$FF;
    EineNote.EffectA.Effect:=0;
    EineNote.EffectA.Parameter:=0;
    EineNote.EffectB.Effect:=0;
    EineNote.EffectB.Parameter:=0;
    Track.Patterns[AktPattern,$3F,SubZaehler]:=EineNote;
   END;
  END;
  AktZeile:=MarkStartZeile;
  AktKanal:=MarkStartKanal;
  Markierung:=FALSE;
  AktualisiereStatusBar;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.CopySelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    Zeilen:INTEGER;
    Kaenale:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  ClearMemory;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  Zeilen:=(MarkEndeZeile-MarkStartZeile)+1;
  Kaenale:=(MarkEndeKanal-MarkStartKanal)+1;
  SETLENGTH((Form AS TMainForm).PatternEditorMemory,Zeilen);
  FOR Zaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory)-1 DO BEGIN
   SETLENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler],Kaenale);
   FOR SubZaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler])-1 DO BEGIN
    EineNote:=Track.Patterns[AktPattern,(MarkStartZeile+Zaehler),MarkStartKanal+SubZaehler];
    (Form AS TMainForm).PatternEditorMemory[Zaehler][SubZaehler]:=EineNote;
   END;
  END;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.CutSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    SubSubZaehler:INTEGER;
    Tausch:INTEGER;
    Zeilen:INTEGER;
    Kaenale:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  ClearMemory;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  Zeilen:=(MarkEndeZeile-MarkStartZeile)+1;
  Kaenale:=(MarkEndeKanal-MarkStartKanal)+1;
  SETLENGTH((Form AS TMainForm).PatternEditorMemory,Zeilen);
  FOR Zaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory)-1 DO BEGIN
   SETLENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler],Kaenale);
   FOR SubZaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler])-1 DO BEGIN
    EineNote:=Track.Patterns[AktPattern,(MarkStartZeile+Zaehler),MarkStartKanal+SubZaehler];
    (Form AS TMainForm).PatternEditorMemory[Zaehler][SubZaehler]:=EineNote;
   END;
  END;
  IF LENGTH((Form AS TMainForm).PatternEditorMemory)>0 THEN BEGIN
   FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
    FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
     FOR SubSubZaehler:=MarkStartZeile TO 64-2 DO BEGIN
      EineNote:=Track.Patterns[AktPattern,SubSubZaehler+1,SubZaehler];
      Track.Patterns[AktPattern,SubSubZaehler,SubZaehler]:=EineNote;
     END;
     EineNote.Note:=0;
     EineNote.Volume:=$FF;
     EineNote.EffectA.Effect:=0;
     EineNote.EffectA.Parameter:=0;
     EineNote.EffectB.Effect:=0;
     EineNote.EffectB.Parameter:=0;
     Track.Patterns[AktPattern,$3F,SubZaehler]:=EineNote;
    END;
   END;
  END;
  AktZeile:=MarkStartZeile;
  AktKanal:=MarkStartKanal;
  Markierung:=FALSE;
  Track.Changed:=TRUE;
  AktualisiereStatusBar;
 END;
END;

PROCEDURE TPatternEditor.PasteSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    SubSubZaehler:INTEGER;
    SubSubSubZaehler:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 NewUndoEntry;
 Markierung:=FALSE;
 FOR Zaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory)-1 DO BEGIN
  FOR SubZaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler])-1 DO BEGIN
   IF Zaehler=0 THEN BEGIN
    FOR SubSubZaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory)-1 DO BEGIN
     FOR SubSubSubZaehler:=63 DOWNTO AktZeile+1 DO BEGIN
      IF SubSubSubZaehler IN [1..63] THEN BEGIN
       EineNote:=Track.Patterns[AktPattern,SubSubSubZaehler-1,AktKanal+SubZaehler];
       Track.Patterns[AktPattern,SubSubSubZaehler,AktKanal+SubZaehler]:=EineNote;
      END;
     END;
    END;
   END;
   IF (AktZeile+Zaehler) IN [0..63] THEN BEGIN
    EineNote:=(Form AS TMainForm).PatternEditorMemory[Zaehler][SubZaehler];
    Track.Patterns[AktPattern,AktZeile+Zaehler,AktKanal+SubZaehler]:=EineNote;
   END;
  END;
 END;
 INC(AktZeile,LENGTH((Form AS TMainForm).PatternEditorMemory));
 IF AktZeile>=$3F THEN AktZeile:=$3F;
 RowScrollUpdate;
 Track.Changed:=TRUE;
 AktualisiereStatusBar;
END;

PROCEDURE TPatternEditor.PutSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 NewUndoEntry;
 Markierung:=FALSE;
 FOR Zaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory)-1 DO BEGIN
  FOR SubZaehler:=0 TO LENGTH((Form AS TMainForm).PatternEditorMemory[Zaehler])-1 DO BEGIN
   EineNote:=(Form AS TMainForm).PatternEditorMemory[Zaehler][SubZaehler];
   Track.Patterns[AktPattern,AktZeile+Zaehler,AktKanal+SubZaehler]:=EineNote;
  END;
 END;
 Track.Changed:=TRUE;
 AktualisiereStatusBar;
END;

PROCEDURE TPatternEditor.InstrumentChangeSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    EineNote:=Track.Patterns[AktPattern,Zaehler,SubZaehler];
    IF CurrentInstrument>0 THEN BEGIN
    END;
    Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
   END;
  END;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.TransposeSelection(Faktor:INTEGER);
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
    Note:INTEGER;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    EineNote:=Track.Patterns[AktPattern,Zaehler,SubZaehler];
    IF (EineNote.Note>0) AND (EineNote.Note<=120) THEN BEGIN
     Note:=EineNote.Note+Faktor;
     IF Note<1 THEN Note:=1;
     IF Note>120 THEN Note:=120;
     EineNote.Note:=Note;
     Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
    END;
   END;
  END;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.ClearEffectASelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    EineNote:=Track.Patterns[AktPattern,Zaehler,SubZaehler];
    EineNote.EffectA.Effect:=0;
    EineNote.EffectA.Parameter:=0;
    Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
   END;
  END;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.InterpolateEffectASelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
    Befehl,Von,Bis,Differenz:INTEGER;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  IF ABS(MarkEndeZeile-MarkStartZeile)>0 THEN BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    EineNote:=Track.Patterns[AktPattern,MarkStartZeile,SubZaehler];
    Befehl:=EineNote.EffectA.Effect;
    Von:=EineNote.EffectA.Parameter;
    EineNote:=Track.Patterns[AktPattern,MarkEndeZeile,SubZaehler];
    Bis:=EineNote.EffectA.Parameter;
    Differenz:=Bis-Von;
    FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
     EineNote:=Track.Patterns[AktPattern,Zaehler,SubZaehler];
     EineNote.EffectA.Effect:=Befehl;
     EineNote.EffectA.Parameter:=Von+((Differenz*(Zaehler-MarkStartZeile)) DIV (MarkEndeZeile-MarkStartZeile));
     Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
    END;
   END;
  END;
  Track.Changed:=TRUE;
 END;
END;

PROCEDURE TPatternEditor.ClearEffectBSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    EineNote:=Track.Patterns[AktPattern,Zaehler,SubZaehler];
    EineNote.EffectB.Effect:=0;
    EineNote.EffectB.Parameter:=0;
    Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
   END;
  END;
  Track.Changed:=TRUE;
  AktualisiereStatusBar;
 END;
END;

PROCEDURE TPatternEditor.InterpolateEffectBSelection;
VAR Zaehler:INTEGER;
    SubZaehler:INTEGER;
    Tausch:INTEGER;
    EineNote:TPatternNote;
    Effekt,Von,Bis,Differenz:INTEGER;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
 IF Markierung THEN BEGIN
  IF MarkStartZeile<0 THEN MarkStartZeile:=0;
  IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
  IF MarkStartKanal<0 THEN MarkStartKanal:=0;
  IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
  IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
  IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
  IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
  IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
  NewUndoEntry;
  IF MarkEndeZeile<MarkStartZeile THEN BEGIN
   Tausch:=MarkStartZeile;
   MarkStartZeile:=MarkEndeZeile;
   MarkEndeZeile:=Tausch;
  END;
  IF MarkEndeKanal<MarkStartKanal THEN BEGIN
   Tausch:=MarkStartKanal;
   MarkStartKanal:=MarkEndeKanal;
   MarkEndeKanal:=Tausch;
  END;
  IF ABS(MarkEndeZeile-MarkStartZeile)>0 THEN BEGIN
   FOR SubZaehler:=MarkStartKanal TO MarkEndeKanal DO BEGIN
    EineNote:=Track.Patterns[AktPattern,MarkStartZeile,SubZaehler];
    Effekt:=EineNote.EffectB.Effect;
    Von:=EineNote.EffectB.Parameter;
    EineNote:=Track.Patterns[AktPattern,MarkEndeZeile,SubZaehler];
    Bis:=EineNote.EffectB.Parameter;
    Differenz:=Bis-Von;
    FOR Zaehler:=MarkStartZeile TO MarkEndeZeile DO BEGIN
     EineNote:=Track.Patterns[AktPattern,Zaehler,SubZaehler];
     EineNote.EffectB.Effect:=Effekt;
     EineNote.EffectB.Parameter:=Von+((Differenz*(Zaehler-MarkStartZeile)) DIV (MarkEndeZeile-MarkStartZeile));
     Track.Patterns[AktPattern,Zaehler,SubZaehler]:=EineNote;
    END;
   END;
  END;
  Track.Changed:=TRUE;
  AktualisiereStatusBar;
 END;
END;

PROCEDURE TPatternEditor.MuteChannel;
BEGIN
 IF (ObenKlickKanal>=0) AND (ObenKlickKanal<Track.CountOfChannels) THEN BEGIN
  Track.Enter;
  Track.Channels[ObenKlickKanal].Mute:=1;
  Track.Changed:=TRUE;
  Track.Leave;
 END;
 Invalidate;
END;

PROCEDURE TPatternEditor.UnmuteChannel;
BEGIN
 IF (ObenKlickKanal>=0) AND (ObenKlickKanal<Track.CountOfChannels) THEN BEGIN
  Track.Enter;
  Track.Channels[ObenKlickKanal].Mute:=0;
  Track.Changed:=TRUE;
  Track.Leave;
 END;
 Invalidate;
END;

PROCEDURE TPatternEditor.MuteAllExceptThisChannel;
VAR I:INTEGER;
BEGIN
 IF (ObenKlickKanal>=0) AND (ObenKlickKanal<Track.CountOfChannels) THEN BEGIN
  Track.Enter;
  FOR I:=0 TO Track.CountOfChannels-1 DO Track.Channels[I].Mute:=1;
  Track.Channels[ObenKlickKanal].Mute:=0;
  Track.Changed:=TRUE;
  Track.Leave;
 END;
 Invalidate;
END;

PROCEDURE TPatternEditor.UnmuteAll;
VAR I:INTEGER;
BEGIN
 Track.Enter;
 FOR I:=0 TO Track.CountOfChannels-1 DO Track.Channels[I].Mute:=0;
 Track.Changed:=TRUE;
 Track.Leave;
 Invalidate;
END;

PROCEDURE TPatternEditor.OnClear(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  ClearSelection;
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnDelete(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  DeleteSelection;
  Invalidate;
 END;
END;

PROCEDURE TPatternEditor.OnCopy(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  CopySelection;
  Invalidate;
 END;
END;

PROCEDURE TPatternEditor.OnCut(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  CutSelection;
  Invalidate;
 END;
END;

PROCEDURE TPatternEditor.OnPaste(Sender:TObject);
BEGIN
 PasteSelection;
 Invalidate;
END;

PROCEDURE TPatternEditor.OnPut(Sender:TObject);
BEGIN
 PutSelection;
 Invalidate;
END;

PROCEDURE TPatternEditor.OnInstrumentChange(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  InstrumentChangeSelection;
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnTransposeM1(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  TransposeSelection(-1);
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnTransposeM12(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  TransposeSelection(-12);
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnTransposeP12(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  TransposeSelection(12);
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnTransposeP1(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  TransposeSelection(1);
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnClearEffectA(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  ClearEffectASelection;
  Markierung:=FALSE;
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnInterpolateEffectBA(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  InterpolateEffectASelection;
  Markierung:=FALSE;
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnClearEffectB(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  ClearEffectBSelection;
  Markierung:=FALSE;
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnInterpolateEffectB(Sender:TObject);
BEGIN
 IF Markierung THEN BEGIN
  InterpolateEffectBSelection;
  Markierung:=FALSE;
  InvalidateSelection;
 END;
END;

PROCEDURE TPatternEditor.OnMuteChannel(Sender:TObject);
BEGIN
 MuteChannel;
END;

PROCEDURE TPatternEditor.OnUnmuteChannel(Sender:TObject);
BEGIN
 UnmuteChannel;
END;

PROCEDURE TPatternEditor.OnMuteAllExceptThisChannel(Sender:TObject);
BEGIN
 MuteAllExceptThisChannel;
END;

PROCEDURE TPatternEditor.OnUnmuteAll(Sender:TObject);
BEGIN
 UnmuteAll;
END;

PROCEDURE TPatternEditor.MouseWheel(Sender:TObject;Shift:TShiftState;WheelDelta:INTEGER;MousePos:TPoint;VAR Handled:BOOLEAN);
VAR Zeile,Kanal,Tausch,X,Y:INTEGER;
BEGIN
 IF TH>0 THEN BEGIN
  IF ABS(WheelDelta)<Height THEN BEGIN
   AktZeile:=AktZeile-(WheelDelta DIV (TH*4));
   IF WheelDelta<0 THEN BEGIN
    WHILE (AktZeile>1) AND (AktZeile>$3F) DO DEC(AktZeile);
    AktZeile:=AktZeile AND $3F;
   END ELSE IF WheelDelta>0 THEN BEGIN
    WHILE AktZeile<0 DO INC(AktZeile);
    AktZeile:=AktZeile AND $3F;
   END;
   RowScrollUpdate;
   Invalidate;
   IF LeftButton THEN BEGIN
    Y:=LY;
    IF Y<TB THEN EXIT;
    Zeile:=(Y DIV (TH+1))-(MaxSichtbareZeilen DIV 2)+AktZeile;
    IF Zeile>=0 THEN BEGIN
     IF (LZ<>Zeile) THEN BEGIN
      LZ:=Zeile;
      IF Zeile<=MarkZeile THEN BEGIN
       MarkStartZeile:=Zeile;
       MarkEndeZeile:=MarkZeile;
      END ELSE IF Zeile>MarkZeile THEN BEGIN
       MarkStartZeile:=MarkZeile;
       MarkEndeZeile:=Zeile;
      END;
      IF MarkEndeZeile<MarkStartZeile THEN BEGIN
       Tausch:=MarkStartZeile;
       MarkStartZeile:=MarkEndeZeile;
       MarkEndeZeile:=Tausch;
      END;
     END;
     IF MarkStartZeile<0 THEN MarkStartZeile:=0;
     IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
     IF MarkStartKanal<0 THEN MarkStartKanal:=0;
     IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
     IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
     IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
     IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
     IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
     Invalidate;
    END;
    X:=LX;
    IF X>((TB*2)+9) THEN BEGIN
     Kanal:=((X-(TB*2)-9) DIV IBB)+AktSichtKanal;
    END ELSE BEGIN
     EXIT;
    END;
    LK:=Kanal;
    IF Kanal<=MarkKanal THEN BEGIN
     MarkStartKanal:=Kanal;
     MarkEndeKanal:=MarkKanal;
    END ELSE IF Kanal>MarkKanal THEN BEGIN
     MarkStartKanal:=MarkKanal;
     MarkEndeKanal:=Kanal;
    END;
    IF MarkEndeKanal<MarkStartKanal THEN BEGIN
     Tausch:=MarkStartKanal;
     MarkStartKanal:=MarkEndeKanal;
     MarkEndeKanal:=Tausch;
    END;
    IF MarkStartZeile<0 THEN MarkStartZeile:=0;
    IF MarkEndeZeile<0 THEN MarkEndeZeile:=0;
    IF MarkStartKanal<0 THEN MarkStartKanal:=0;
    IF MarkEndeKanal<0 THEN MarkEndeKanal:=0;
    IF MarkStartZeile>=64 THEN MarkStartZeile:=64-1;
    IF MarkEndeZeile>=64 THEN MarkEndeZeile:=64-1;
    IF MarkStartKanal>=Track.CountOfChannels THEN MarkStartKanal:=Track.CountOfChannels-1;
    IF MarkEndeKanal>=Track.CountOfChannels THEN MarkEndeKanal:=Track.CountOfChannels-1;
    Invalidate;
   END;
  END;
 END;
 AktualisiereStatusBar;
 Handled:=TRUE;
END;

PROCEDURE TPatternEditor.KeyProcess;
VAR Key:WORD;
    Shift:TShiftState;
    Bearbeiten:BOOLEAN;
    OK:BOOLEAN;
    Zaehler:INTEGER;
    N,O:INTEGER;
    Note:TPatternNote;
    K,KK:BYTE;
    D1,D2,D3:INTEGER;
    AktPos2:INTEGER;
    Tausch:INTEGER;
BEGIN
 IF LastKey=0 THEN EXIT;
 Key:=LastKey;
 Shift:=LastShift;
 OK:=TRUE;
 IF OK AND NOT ASSIGNED(Track) THEN OK:=FALSE;
 IF NOT OK THEN EXIT;
 IF AktPattern>=$FE THEN EXIT;
 Bearbeiten:=TRUE;
 IF Key=VK_OEM_5 THEN Shift:=[];
 IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
  Bearbeiten:=FALSE;
  CASE Key OF
   ORD('Z'):DoUndo;
   ORD('U'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     IF NOT Markierung THEN ClearMemory;
     Markierung:=FALSE;
     InvalidateSelection;
    END;
   END;
   ORD('C'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     CopySelection;
     Invalidate;
    END;
   END;
   ORD('P'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     PasteSelection;
     Invalidate;
    END;
   END;
   ORD('O'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     PutSelection;
     Invalidate;
    END;
   END;
   VK_DELETE:BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     CutSelection;
     Invalidate;
    END;
   END;
   ORD('I'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     InstrumentChangeSelection;
     InvalidateSelection;
    END;
   END;
   ORD('K'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     IF ((ssAlt IN LShift) OR (ssCtrl IN LShift)) AND (LKey=ORD('K')) THEN BEGIN
      ClearEffectASelection;
     END ELSE BEGIN
      InterpolateEffectASelection;
     END;
     InvalidateSelection;
    END;
   END;
   ORD('X'):BEGIN
    IF (ssAlt IN Shift) OR (ssCtrl IN Shift) THEN BEGIN
     IF ((ssAlt IN LShift) OR (ssCtrl IN LShift)) AND (LKey=ORD('X')) THEN BEGIN
      ClearEffectBSelection;
     END ELSE BEGIN
      InterpolateEffectBSelection;
     END;
     InvalidateSelection;
    END;
   END;
   ELSE BEGIN
   END;
  END;
 END ELSE IF ssShift IN Shift THEN BEGIN
  CASE Key OF
   VK_DELETE:BEGIN
    Bearbeiten:=FALSE;
    CutSelection;
    Invalidate;
   END;
   VK_INSERT:BEGIN
    Bearbeiten:=FALSE;
    PasteSelection;
    Invalidate;
   END;
   ELSE BEGIN
   END;
  END;
 END;
 IF Bearbeiten AND NOT (Track.Playing AND Track.FollowSong) THEN BEGIN
  CASE Key OF
   VK_TAB:BEGIN
    IF ssShift IN Shift THEN BEGIN
     IF Markierung THEN BEGIN
      IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
      IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
      IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
      IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
     END;
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
      Markierung:=TRUE;
      MarkZeile:=AktZeile;
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=AktZeile;
      MarkKanal:=AktKanal;
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=AktKanal;
     END;
    END;
    INC(AktKanal);
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktKanal<=MarkKanal THEN BEGIN
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=MarkKanal;
     END ELSE IF AktKanal>MarkKanal THEN BEGIN
      MarkStartKanal:=MarkKanal;
      MarkEndeKanal:=AktKanal;
     END;
     IF MarkEndeKanal<MarkStartKanal THEN BEGIN
      Tausch:=MarkStartKanal;
      MarkStartKanal:=MarkEndeKanal;
      MarkEndeKanal:=Tausch;
     END;
    END;
    InvalidateRow;
   END;
   VK_HOME:BEGIN
    IF ssShift IN Shift THEN BEGIN
     IF Markierung THEN BEGIN
      IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
      IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
      IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
      IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
     END;
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
      Markierung:=TRUE;
      MarkZeile:=AktZeile;
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=AktZeile;
      MarkKanal:=AktKanal;
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=AktKanal;
     END;
    END;
    IF Markierung AND ((AktPos<>0) OR (AktKanal<>0)) THEN BEGIN
     AktKanal:=0;
     AktPos:=0;
     PositionUpdate;
    ENd ELSE IF AktPos>0 THEN BEGIN
     AktPos:=0;
    ENd ELSE IF (AktPos=0) AND (AktKanal=0) THEN BEGIN
     AktZeile:=0;
     IF (ssShift IN Shift) AND Markierung THEN BEGIN
      IF AktZeile<=MarkZeile THEN BEGIN
       MarkStartZeile:=AktZeile;
       MarkEndeZeile:=MarkZeile;
      END ELSE IF AktZeile>MarkZeile THEN BEGIN
       MarkStartZeile:=MarkZeile;
       MarkEndeZeile:=AktZeile;
      END;
      IF MarkEndeZeile<MarkStartZeile THEN BEGIN
       Tausch:=MarkStartZeile;
       MarkStartZeile:=MarkEndeZeile;
       MarkEndeZeile:=Tausch;
      END;
     END;
    ENd ELSE BEGIN
     AktKanal:=0;
     PositionUpdate;
    END;
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktKanal<=MarkKanal THEN BEGIN
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=MarkKanal;
     END ELSE IF AktKanal>MarkKanal THEN BEGIN
      MarkStartKanal:=MarkKanal;
      MarkEndeKanal:=AktKanal;
     END;
     IF MarkEndeKanal<MarkStartKanal THEN BEGIN
      Tausch:=MarkStartKanal;
      MarkStartKanal:=MarkEndeKanal;
      MarkEndeKanal:=Tausch;
     END;
    END;
    RowScrollUpdate;
    PositionUpdate;
    Invalidate;
   END;
   VK_END:BEGIN
    IF ssShift IN Shift THEN BEGIN
     IF Markierung THEN BEGIN
      IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
      IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
      IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
      IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
     END;
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
      Markierung:=TRUE;
      MarkZeile:=AktZeile;
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=AktZeile;
      MarkKanal:=AktKanal;
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=AktKanal;
     END;
    END;
    IF AktKanal=(Track.CountOfChannels-1) THEN BEGIN
     AktZeile:=64-1;
     IF (ssShift IN Shift) AND Markierung THEN BEGIN
      IF AktZeile<=MarkZeile THEN BEGIN
       MarkStartZeile:=AktZeile;
       MarkEndeZeile:=MarkZeile;
      END ELSE IF AktZeile>MarkZeile THEN BEGIN
       MarkStartZeile:=MarkZeile;
       MarkEndeZeile:=AktZeile;
      END;
      IF MarkEndeZeile<MarkStartZeile THEN BEGIN
       Tausch:=MarkStartZeile;
       MarkStartZeile:=MarkEndeZeile;
       MarkEndeZeile:=Tausch;
      END;
     END;
    END ELSE BEGIN
     AktKanal:=Track.CountOfChannels-1;
     PositionUpdate;
    END;
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktKanal<=MarkKanal THEN BEGIN
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=MarkKanal;
     END ELSE IF AktKanal>MarkKanal THEN BEGIN
      MarkStartKanal:=MarkKanal;
      MarkEndeKanal:=AktKanal;
     END;
     IF MarkEndeKanal<MarkStartKanal THEN BEGIN
      Tausch:=MarkStartKanal;
      MarkStartKanal:=MarkEndeKanal;
      MarkEndeKanal:=Tausch;
     END;
    END;
    RowScrollUpdate;
    PositionUpdate;
    Invalidate;
   END;
   VK_LEFT:BEGIN
    AktPos:=AktPos-1;
    AktPos2:=AktPos;
    CASE AktPos2 OF
     1:AktPos2:=0;
     -1:BEGIN
      AktPos2:=10;
      IF ssShift IN Shift THEN BEGIN
       IF Markierung THEN BEGIN
        IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
        IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
        IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
        IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
       END;
       IF NOT Markierung THEN BEGIN
        AltMarkStartZeile:=-1;
        AltMarkEndeZeile:=-1;
        AltMarkStartKanal:=-1;
        AltMarkEndeKanal:=-1;
       END;
       IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
        Markierung:=TRUE;
        MarkZeile:=AktZeile;
        MarkStartZeile:=AktZeile;
        MarkEndeZeile:=AktZeile;
        MarkKanal:=AktKanal;
        MarkStartKanal:=AktKanal;
        MarkEndeKanal:=AktKanal;
       END;
      END;
      DEC(AktKanal);
      IF AktKanal<0 THEN BEGIN
       IF ssShift IN Shift THEN BEGIN
        AktKanal:=0;
        AktPos2:=0;
       END;
      END;
      IF (ssShift IN Shift) AND Markierung THEN BEGIN
       IF AktKanal<=MarkKanal THEN BEGIN
        MarkStartKanal:=AktKanal;
        MarkEndeKanal:=MarkKanal;
       END ELSE IF AktKanal>MarkKanal THEN BEGIN
        MarkStartKanal:=MarkKanal;
        MarkEndeKanal:=AktKanal;
       END;
       IF MarkEndeKanal<MarkStartKanal THEN BEGIN
        Tausch:=MarkStartKanal;
        MarkStartKanal:=MarkEndeKanal;
        MarkEndeKanal:=Tausch;
       END;
      END;
     END;
    END;
    AktPos:=AktPos2;
    InvalidateRow;
   END;
   VK_RIGHT:BEGIN
    AktPos:=AktPos+1;
    AktPos2:=AktPos;
    CASE AktPos2 OF
     1:AktPos2:=2;
     11:BEGIN
      AktPos2:=0;
      IF ssShift IN Shift THEN BEGIN
       IF Markierung THEN BEGIN
        IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
        IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
        IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
        IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
       END;
       IF NOT Markierung THEN BEGIN
        AltMarkStartZeile:=-1;
        AltMarkEndeZeile:=-1;
        AltMarkStartKanal:=-1;
        AltMarkEndeKanal:=-1;
       END;
       IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
        Markierung:=TRUE;
        MarkZeile:=AktZeile;
        MarkStartZeile:=AktZeile;
        MarkEndeZeile:=AktZeile;
        MarkKanal:=AktKanal;
        MarkStartKanal:=AktKanal;
        MarkEndeKanal:=AktKanal;
       END;
      END;
      INC(AktKanal);
      IF AktKanal>=Track.CountOfChannels THEN BEGIN
       IF ssShift IN Shift THEN BEGIN
        AktKanal:=Track.CountOfChannels-1;
        AktPos2:=14;
       END;
      END;
      IF (ssShift IN Shift) AND Markierung THEN BEGIN
       IF AktKanal<=MarkKanal THEN BEGIN
        MarkStartKanal:=AktKanal;
        MarkEndeKanal:=MarkKanal;
       END ELSE IF AktKanal>MarkKanal THEN BEGIN
        MarkStartKanal:=MarkKanal;
        MarkEndeKanal:=AktKanal;
       END;
       IF MarkEndeKanal<MarkStartKanal THEN BEGIN
        Tausch:=MarkStartKanal;
        MarkStartKanal:=MarkEndeKanal;
        MarkEndeKanal:=Tausch;
       END;
      END;
     END;
    END;
    AktPos:=AktPos2;
    InvalidateRow;
   END;
   VK_UP:BEGIN
    IF ssShift IN Shift THEN BEGIN
     IF Markierung THEN BEGIN
      IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
      IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
      IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
      IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
     END;
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
      Markierung:=TRUE;
      MarkZeile:=AktZeile;
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=AktZeile;
      MarkKanal:=AktKanal;
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=AktKanal;
     END;
    END;
    DEC(AktZeile);
    IF AktZeile<0 THEN BEGIN
     IF Markierung THEN BEGIN
      AktZeile:=0;
     END ELSE BEGIN
      AktZeile:=64-1;
     END;
    END;
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktZeile<=MarkZeile THEN BEGIN
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=MarkZeile;
     END ELSE IF AktZeile>MarkZeile THEN BEGIN
      MarkStartZeile:=MarkZeile;
      MarkEndeZeile:=AktZeile;
     END;
     IF MarkEndeZeile<MarkStartZeile THEN BEGIN
      Tausch:=MarkStartZeile;
      MarkStartZeile:=MarkEndeZeile;
      MarkEndeZeile:=Tausch;
     END;
    END;
    RowScrollUpdate;
    Invalidate;
   END;
   VK_DOWN:BEGIN
    IF ssShift IN Shift THEN BEGIN
     IF Markierung THEN BEGIN
      IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
      IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
      IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
      IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
     END;
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
      Markierung:=TRUE;
      MarkZeile:=AktZeile;
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=AktZeile;
      MarkKanal:=AktKanal;
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=AktKanal;
     END;
    END;
    INC(AktZeile);
    IF AktZeile>$3F THEN BEGIN
     IF ssShift IN Shift THEN BEGIN
      AktZeile:=$3F;
     END ELSE BEGIN
      AktZeile:=0;
     END;
    END;
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktZeile<=MarkZeile THEN BEGIN
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=MarkZeile;
     END ELSE IF AktZeile>MarkZeile THEN BEGIN
      MarkStartZeile:=MarkZeile;
      MarkEndeZeile:=AktZeile;
     END;
     IF MarkEndeZeile<MarkStartZeile THEN BEGIN
      Tausch:=MarkStartZeile;
      MarkStartZeile:=MarkEndeZeile;
      MarkEndeZeile:=Tausch;
     END;
    END;
    RowScrollUpdate;
    Invalidate;
   END;
   VK_PRIOR:BEGIN
    IF (ssShift IN Shift) AND NOT Markierung THEN BEGIN
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     Markierung:=TRUE;
     MarkZeile:=AktZeile;
     MarkStartZeile:=AktZeile;
     MarkEndeZeile:=AktZeile;
     MarkKanal:=AktKanal;
     MarkStartKanal:=AktKanal;
     MarkEndeKanal:=AktKanal;
    END;
    FOR Zaehler:=1 TO 4 DO BEGIN
     DEC(AktZeile);
     IF AktZeile<0 THEN BEGIN
      IF ssShift IN Shift THEN BEGIN
       AktZeile:=0;
      END ELSE BEGIN
       AktZeile:=64-1;
      END;
     END;
    END;
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktZeile<=MarkZeile THEN BEGIN
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=MarkZeile;
     END ELSE IF AktZeile>MarkZeile THEN BEGIN
      MarkStartZeile:=MarkZeile;
      MarkEndeZeile:=AktZeile;
     END;
     IF MarkEndeZeile<MarkStartZeile THEN BEGIN
      Tausch:=MarkStartZeile;
      MarkStartZeile:=MarkEndeZeile;
      MarkEndeZeile:=Tausch;
     END;
    END;
    RowScrollUpdate;
    Invalidate;
   END;
   VK_NEXT:BEGIN
    IF ssShift IN Shift THEN BEGIN
     IF Markierung THEN BEGIN
      IF AktKanal<MarkStartKanal THEN Markierung:=FALSE;
      IF AktKanal>MarkEndeKanal THEN Markierung:=FALSE;
      IF AktZeile<MarkStartZeile THEN Markierung:=FALSE;
      IF AktZeile>MarkEndeZeile THEN Markierung:=FALSE;
     END;
     IF NOT Markierung THEN BEGIN
      AltMarkStartZeile:=-1;
      AltMarkEndeZeile:=-1;
      AltMarkStartKanal:=-1;
      AltMarkEndeKanal:=-1;
     END;
     IF (NOT Markierung) OR (Markierung AND ((MarkStartZeile>AktZeile) OR (MarkEndeZeile<AktZeile))) THEN BEGIN
      Markierung:=TRUE;
      MarkZeile:=AktZeile;
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=AktZeile;
      MarkKanal:=AktKanal;
      MarkStartKanal:=AktKanal;
      MarkEndeKanal:=AktKanal;
     END;
    END;
    FOR Zaehler:=1 TO 4 DO BEGIN
     INC(AktZeile);
     IF AktZeile>$3F THEN BEGIN
      IF ssShift IN Shift THEN BEGIN
       AktZeile:=$3F;
      END ELSE BEGIN
       AktZeile:=0;
      END;
     END;
    END;
    IF (ssShift IN Shift) AND Markierung THEN BEGIN
     IF AktZeile<=MarkZeile THEN BEGIN
      MarkStartZeile:=AktZeile;
      MarkEndeZeile:=MarkZeile;
     END ELSE IF AktZeile>MarkZeile THEN BEGIN
      MarkStartZeile:=MarkZeile;
      MarkEndeZeile:=AktZeile;
     END;
     IF MarkEndeZeile<MarkStartZeile THEN BEGIN
      Tausch:=MarkStartZeile;
      MarkStartZeile:=MarkEndeZeile;
      MarkEndeZeile:=Tausch;
     END;
    END;
    RowScrollUpdate;
    Invalidate;
   END;
   ORD('A')..ORD('Z'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9,VK_BACK:BEGIN
    Markierung:=FALSE;
    K:=0;
    CASE Key OF
     VK_NUMPAD0,ORD('0'):K:=0;
     VK_NUMPAD1,ORD('1'):K:=1;
     VK_NUMPAD2,ORD('2'):K:=2;
     VK_NUMPAD3,ORD('3'):K:=3;
     VK_NUMPAD4,ORD('4'):K:=4;
     VK_NUMPAD5,ORD('5'):K:=5;
     VK_NUMPAD6,ORD('6'):K:=6;
     VK_NUMPAD7,ORD('7'):K:=7;
     VK_NUMPAD8,ORD('8'):K:=8;
     VK_NUMPAD9,ORD('9'):K:=9;
     ORD('A'):K:=10;
     ORD('B'):K:=11;
     ORD('C'):K:=12;
     ORD('D'):K:=13;
     ORD('E'):K:=14;
     ORD('F'):K:=15;
    END;
    IF (AktPos=0) AND (Key=ORD('1')) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.Note:=$FE;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.PlayNote(AktPattern,AktZeile,AktKanal);
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF (AktPos=0) AND (Key=VK_BACK) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.Note:=$FF;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.PlayNote(AktPattern,AktZeile,AktKanal);
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF (AktPos=0) AND (Key IN [ORD('A')..ORD('Z'),ORD('0')..ORD('9')]) THEN BEGIN
     NewUndoEntry;
     N:=0;
     O:=CurrentOctave;
     IF Char2Note(UPCASE(CHR(Key)),N,O) THEN BEGIN
      IF (O>=0) AND (O<=9) THEN BEGIN
       Track.Enter;
       Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
       Note.Note:=N+(O*12)+1;
       Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
       Track.PlayNote(AktPattern,AktZeile,AktKanal);
       Track.Changed:=TRUE;
       Track.Leave;
       AktZeile:=(AktZeile+1) AND $3F;
       RowScrollUpdate;
       Invalidate;
      END;
     END;
    END ELSE IF (AktPos=2) AND (Key IN [ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     IF (Note.Note>0) AND (Note.Note<121) THEN BEGIN
      KK:=((Note.Note-1) MOD 12)+(K*12)+1;
      Note.Note:=KK;
     END;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF (AktPos=3) AND (Key IN [ORD('A')..ORD('F'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     IF Note.Volume=$FF THEN Note.Volume:=0;
     KK:=(Note.Volume AND $0F) OR (K SHL 4);
     Note.Volume:=KK;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     INC(AktPos);
//   (Form AS TMainForm).InstrumentListBox.ItemIndex:=KK-1;
     CurrentInstrument:=KK;
     InvalidateRowChannel;
    END ELSE IF (AktPos=4) AND (Key IN [ORD('A')..ORD('F'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     IF Note.Volume=$FF THEN Note.Volume:=0;
     KK:=(Note.Volume AND $F0) OR K;
     Note.Volume:=KK;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     DEC(AktPos);
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
//   (Form AS TMainForm).InstrumentListBox.ItemIndex:=KK-1;
     CurrentInstrument:=KK;
     Invalidate;
    END ELSE IF (AktPos=5) AND (Key IN [ORD('A')..ORD('Z')]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectA.Effect:=Key-ORD('A')+1;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     Invalidate;
    END ELSE IF (AktPos=5) AND (Key IN [ORD('0')..ORD('9')]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectA.Effect:=Key-ORD('0')+27;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     Invalidate;
    END ELSE IF (AktPos=5) AND (Key IN [VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectA.Effect:=Key-VK_NUMPAD0+27;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     Invalidate;
    END ELSE IF (AktPos=6) AND (Key IN [ORD('A')..ORD('F'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     KK:=(Note.EffectA.Parameter AND $0F) OR (K SHL 4);
     Note.EffectA.Parameter:=KK;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     INC(AktPos);
     InvalidateRowChannel;
    END ELSE IF (AktPos=7) AND (Key IN [ORD('A')..ORD('F'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     KK:=(Note.EffectA.Parameter AND $F0) OR K;
     Note.EffectA.Parameter:=KK;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     DEC(AktPos);
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF (AktPos=8) AND (Key IN [ORD('A')..ORD('Z')]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectB.Effect:=Key-ORD('A')+1;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF (AktPos=8) AND (Key IN [ORD('0')..ORD('9')]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectB.Effect:=Key-ORD('0')+27;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     Invalidate;
    END ELSE IF (AktPos=8) AND (Key IN [VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectB.Effect:=Key-VK_NUMPAD0+27;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     Invalidate;
    END ELSE IF (AktPos=9) AND (Key IN [ORD('A')..ORD('F'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     KK:=(Note.EffectB.Parameter AND $0F) OR (K SHL 4);
     Note.EffectB.Parameter:=KK;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     INC(AktPos);
     InvalidateRowChannel;
    END ELSE IF (AktPos=10) AND (Key IN [ORD('A')..ORD('F'),ORD('0')..ORD('9'),VK_NUMPAD0..VK_NUMPAD9]) THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     KK:=(Note.EffectB.Parameter AND $F0) OR K;
     Note.EffectB.Parameter:=KK;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     DEC(AktPos);
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END;
   END;
   VK_SPACE:BEGIN
    IF Markierung THEN BEGIN
     ClearSelection;
     InvalidateSelection;
    END ELSE IF AktPos IN [0,1,2] THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.Note:=0;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF AktPos IN [3,4] THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.Volume:=$FF;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF AktPos=5 THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectA.Effect:=0;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF AktPos IN [6,7] THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectA.Parameter:=0;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF AktPos=8 THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectB.Effect:=0;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END ELSE IF AktPos IN [9,10] THEN BEGIN
     NewUndoEntry;
     Track.Enter;
     Note:=Track.Patterns[AktPattern,AktZeile,AktKanal];
     Note.EffectB.Parameter:=0;
     Track.Patterns[AktPattern,AktZeile,AktKanal]:=Note;
     Track.Changed:=TRUE;
     Track.Leave;
     AktZeile:=(AktZeile+1) AND $3F;
     RowScrollUpdate;
     Invalidate;
    END;
   END;
   VK_INSERT:BEGIN
    IF ssShift IN Shift THEN BEGIN
     PasteSelection;
    END ELSE BEGIN
     NewUndoEntry;
     Markierung:=FALSE;
     D1:=AktZeile;
     D2:=64-1;
     FOR D3:=D2 DOWNTO D1 DO BEGIN
      IF (D3-1)>=D1 THEN BEGIN
       Note:=Track.Patterns[AktPattern,(D3-1) AND $3F,AktKanal];
      END ELSE BEGIN
       Note.Note:=0;
       Note.Volume:=$FF;
       Note.EffectA.Effect:=0;
       Note.EffectA.Parameter:=0;
       Note.EffectB.Effect:=0;
       Note.EffectB.Parameter:=0;
      END;
      Track.Patterns[AktPattern,D3 AND $3F,AktKanal]:=Note;
     END;
    END;
    RowScrollUpdate;
    Invalidate;
   END;
   VK_DELETE:BEGIN
    IF ssShift IN Shift THEN BEGIN
     CutSelection;
    END ELSE IF Markierung THEN BEGIN
     DeleteSelection;
    END ELSE BEGIN
     NewUndoEntry;
     D1:=AktZeile;
     D2:=64-1;
     FOR D3:=D1 TO D2 DO BEGIN
      IF (D3+1)<=D2 THEN BEGIN
       Note:=Track.Patterns[AktPattern,(D3+1) AND $3F,AktKanal];
      END ELSE BEGIN
       Note.Note:=0;
       Note.Volume:=$FF;
       Note.EffectA.Effect:=0;
       Note.EffectA.Parameter:=0;
       Note.EffectB.Effect:=0;
       Note.EffectB.Parameter:=0;
      END;
      Track.Patterns[AktPattern,D3 AND $3F,AktKanal]:=Note;
     END;
    END;
    RowScrollUpdate;
    Invalidate;
   END;
   ELSE BEGIN
   END;
  END;
 END;
 AktualisiereStatusBar;
 LKey:=LastKey;
 LShift:=LastShift;
END;

PROCEDURE TPatternEditor.AktualisiereStatusBar;
BEGIN
 IF NOT ASSIGNED(Track) THEN EXIT;
END;

PROCEDURE TPatternEditor.InvalidateRowChannel;
VAR R:TRect;
    AltSichtKanal:INTEGER;
BEGIN
 AltSichtKanal:=AktSichtKanal;
 PositionUpdate;
 IF AltSichtKanal<>AktSichtKanal THEN BEGIN
  Invalidate;
 END ELSE BEGIN
  IF (MarkierungAlt<>Markierung) OR (AltMarkStartZeile<>MarkStartZeile) OR
     (AltMarkEndeZeile<>MarkEndeZeile) OR (AltMarkStartKanal<>MarkStartKanal) OR
     (AltMarkEndeKanal<>MarkEndeKanal) THEN InvalidateSelectionChange;
  R.Top:=((TH+1)*((MaxSichtbareZeilen DIV 2)))+5;
  R.Bottom:=((TH+1)*((MaxSichtbareZeilen DIV 2)))+TH+5;
  R.Left:=(2+(TB*2)+7)+(IBB*(AktKanal-AktSichtKanal));
  R.Right:=R.Left+IBB;
  InvalidateRect(Handle,@R,FALSE);
 END;
END;

PROCEDURE TPatternEditor.InvalidateRow;
VAR R:TRect;
    AltSichtKanal:INTEGER;
BEGIN
 AltSichtKanal:=AktSichtKanal;
 PositionUpdate;
 IF AltSichtKanal<>AktSichtKanal THEN BEGIN
  Invalidate;
 END ELSE BEGIN
  IF (MarkierungAlt<>Markierung) OR (AltMarkStartZeile<>MarkStartZeile) OR
     (AltMarkEndeZeile<>MarkEndeZeile) OR (AltMarkStartKanal<>MarkStartKanal) OR
     (AltMarkEndeKanal<>MarkEndeKanal) THEN InvalidateSelectionChange;
  R.Top:=((TH+1)*((MaxSichtbareZeilen DIV 2)))+5;
  R.Bottom:=((TH+1)*((MaxSichtbareZeilen DIV 2)))+TH+5;
  R.Left:=(2+(TB*2)+7);
  R.Right:=ClientWidth;
  InvalidateRect(Handle,@R,FALSE);
 END;
END;

PROCEDURE TPatternEditor.InvalidateSelection;
VAR R:TRect;
BEGIN
 IF (MarkierungAlt<>Markierung) OR (AltMarkStartZeile<>MarkStartZeile) OR
    (AltMarkEndeZeile<>MarkEndeZeile) OR (AltMarkStartKanal<>MarkStartKanal) OR
    (AltMarkEndeKanal<>MarkEndeKanal) THEN InvalidateSelectionChange;
 R.Top:=((TH+1)*(MarkStartZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
 R.Bottom:=((TH+1)*(MarkEndeZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
 R.Left:=(2+(TB*2)+7)+(IBB*(MarkStartKanal-AktSichtKanal));
 R.Right:=(2+(TB*2)+7)+(IBB*(MarkEndeKanal-AktSichtKanal))+IBB;
 InvalidateRect(Handle,@R,FALSE);
END;

PROCEDURE TPatternEditor.InvalidateSelectionChange;
VAR R:TRect;
    MinZeile,MaxZeile,MinKanal,MaxKanal:INTEGER;
BEGIN
 IF Markierung THEN BEGIN
  IF MarkierungAlt THEN BEGIN
   MinZeile:=MarkStartZeile;
   MaxZeile:=MarkEndeZeile;
   MinKanal:=MarkStartKanal;
   MaxKanal:=MarkEndeKanal;
   IF AltMarkStartZeile<MinZeile THEN MinZeile:=AltMarkStartZeile;
   IF AltMarkEndeZeile>MaxZeile THEN MaxZeile:=AltMarkEndeZeile;
   IF AltMarkStartKanal<MinKanal THEN MinKanal:=AltMarkStartKanal;
   IF AltMarkEndeKanal>MaxKanal THEN MaxKanal:=AltMarkEndeKanal;
   R.Top:=((TH+1)*(MinZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
   R.Bottom:=((TH+1)*(MaxZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
   R.Left:=(2+(TB*2)+7)+(IBB*(MinKanal-AktSichtKanal));
   R.Right:=(2+(TB*2)+7)+(IBB*(MaxKanal-AktSichtKanal))+IBB;
   InvalidateRect(Handle,@R,FALSE);
  END ELSE BEGIN
   R.Top:=((TH+1)*(MarkStartZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
   R.Bottom:=((TH+1)*(MarkEndeZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
   R.Left:=(2+(TB*2)+7)+(IBB*(MarkStartKanal-AktSichtKanal));
   R.Right:=(2+(TB*2)+7)+(IBB*(MarkEndeKanal-AktSichtKanal))+IBB;
   InvalidateRect(Handle,@R,FALSE);
  END;
 END ELSE BEGIN
  IF MarkierungAlt THEN BEGIN
   MinZeile:=MarkStartZeile;
   MaxZeile:=MarkEndeZeile;
   MinKanal:=MarkStartKanal;
   MaxKanal:=MarkEndeKanal;
   IF AltMarkStartZeile<MinZeile THEN MinZeile:=AltMarkStartZeile;
   IF AltMarkEndeZeile>MaxZeile THEN MaxZeile:=AltMarkEndeZeile;
   IF AltMarkStartKanal<MinKanal THEN MinKanal:=AltMarkStartKanal;
   IF AltMarkEndeKanal>MaxKanal THEN MaxKanal:=AltMarkEndeKanal;
   R.Top:=((TH+1)*(MinZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
   R.Bottom:=((TH+1)*(MaxZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
   R.Left:=(2+(TB*2)+7)+(IBB*(MinKanal-AktSichtKanal));
   R.Right:=(2+(TB*2)+7)+(IBB*(MaxKanal-AktSichtKanal))+IBB;
   InvalidateRect(Handle,@R,FALSE);
  END ELSE BEGIN
   R.Top:=((TH+1)*(MarkStartZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
   R.Bottom:=((TH+1)*(MarkEndeZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
   R.Left:=(2+(TB*2)+7)+(IBB*(MarkStartKanal-AktSichtKanal));
   R.Right:=(2+(TB*2)+7)+(IBB*(MarkEndeKanal-AktSichtKanal))+IBB;
   InvalidateRect(Handle,@R,FALSE);
  END;
 END;
 AltMarkStartZeile:=MarkStartZeile;
 AltMarkEndeZeile:=MarkEndeZeile;
 AltMarkStartKanal:=MarkStartKanal;
 AltMarkEndeKanal:=MarkEndeKanal;
 MarkierungAlt:=Markierung;
END;

PROCEDURE TPatternEditor.InvalidateDragChange;
VAR R:TRect;
    MinZeile,MaxZeile,MinKanal,MaxKanal:INTEGER;
BEGIN
 IF Ziehen THEN BEGIN
  MinZeile:=DragMarkStartZeile;
  MaxZeile:=DragMarkEndeZeile;
  MinKanal:=DragMarkStartKanal;
  MaxKanal:=DragMarkEndeKanal;
  IF AltDragMarkStartZeile<MinZeile THEN MinZeile:=AltDragMarkStartZeile;
  IF AltDragMarkEndeZeile>MaxZeile THEN MaxZeile:=AltDragMarkEndeZeile;
  IF AltDragMarkStartKanal<MinKanal THEN MinKanal:=AltDragMarkStartKanal;
  IF AltDragMarkEndeKanal>MaxKanal THEN MaxKanal:=AltDragMarkEndeKanal;
  IF MarkStartZeile<MinZeile THEN MinZeile:=MarkStartZeile;
  IF MarkEndeZeile>MaxZeile THEN MaxZeile:=MarkEndeZeile;
  IF MarkStartKanal<MinKanal THEN MinKanal:=MarkStartKanal;
  IF MarkEndeKanal>MaxKanal THEN MaxKanal:=MarkEndeKanal;
  IF StartDragMarkStartZeile<MinZeile THEN MinZeile:=StartDragMarkStartZeile;
  IF StartDragMarkEndeZeile>MaxZeile THEN MaxZeile:=StartDragMarkEndeZeile;
  IF StartDragMarkStartKanal<MinKanal THEN MinKanal:=StartDragMarkStartKanal;
  IF StartDragMarkEndeKanal>MaxKanal THEN MaxKanal:=StartDragMarkEndeKanal;
  R.Top:=((TH+1)*(MinZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+5;
  R.Bottom:=((TH+1)*(MaxZeile-(AktZeile-(MaxSichtbareZeilen DIV 2))))+TH+5;
  R.Left:=(2+(TB*2)+7)+(IBB*(MinKanal-AktSichtKanal));
  R.Right:=(2+(TB*2)+7)+(IBB*(MaxKanal-AktSichtKanal))+IBB;
  InvalidateRect(Handle,@R,FALSE);
  AltDragMarkStartZeile:=DragMarkStartZeile;
  AltDragMarkEndeZeile:=DragMarkEndeZeile;
  AltDragMarkStartKanal:=DragMarkStartKanal;
  AltDragMarkEndeKanal:=DragMarkEndeKanal;
  AltMarkStartZeile:=MarkStartZeile;
  AltMarkEndeZeile:=MarkEndeZeile;
  AltMarkStartKanal:=MarkStartKanal;
  AltMarkEndeKanal:=MarkEndeKanal;
 END ELSE BEGIN
  Invalidate;
  AltDragMarkStartZeile:=-1;
  AltDragMarkEndeZeile:=-1;
  AltDragMarkStartKanal:=-1;
  AltDragMarkEndeKanal:=-1;
 END;
END;

PROCEDURE TPatternEditor.InvalidateChannel;
VAR R:TRect;
BEGIN
 IF (MarkierungAlt<>Markierung) OR (AltMarkStartZeile<>MarkStartZeile) OR
    (AltMarkEndeZeile<>MarkEndeZeile) OR (AltMarkStartKanal<>MarkStartKanal) OR
    (AltMarkEndeKanal<>MarkEndeKanal) THEN InvalidateSelectionChange;
 R.Top:=TH;
 R.Bottom:=ClientHeight;
 R.Left:=(2+(TB*2)+7)+(IBB*(AktKanal-AktSichtKanal));
 R.Right:=R.Left+IBB;
 InvalidateRect(Handle,@R,FALSE);
END;

END.
