Initial commit.
[amiga/xmodule.git] / Gadgets / PattEditClass.c
1 /*
2 **      PattEditClass.c
3 **
4 **      Copyright (C) 1995,96,97,98 Bernardo Innocenti
5 **
6 **      Pattern Editor gadget class
7 */
8
9 #include <exec/memory.h>
10 #include <exec/ports.h>
11 #include <utility/tagitem.h>
12 #include <utility/hooks.h>
13 #include <devices/inputevent.h>
14 #include <libraries/SongClass.h>
15 #include <libraries/PattEditClass.h>
16 #include <libraries/xmodule.h>
17
18 #include <intuition/intuition.h>
19 #include <intuition/intuitionbase.h>
20 #include <intuition/classes.h>
21 #include <intuition/classusr.h>
22 #include <intuition/gadgetclass.h>
23 #include <intuition/imageclass.h>
24 #include <graphics/gfxbase.h>
25 #include <graphics/gfxmacros.h>
26 #include <graphics/text.h>
27
28 #include <proto/exec.h>
29 #include <proto/intuition.h>
30 #include <proto/graphics.h>
31 #include <proto/utility.h>
32 #include <proto/keymap.h>
33
34
35 #include "CompilerSpecific.h"
36 #include "Debug.h"
37 #include "ListMacros.h"
38
39 #ifdef OS30_ONLY
40         #define WANTED_LIBVER   39
41 #else
42         #define WANTED_LIBVER   37
43 #endif
44
45
46 /* Some handy definitions missing in <devices/inputevent.h> */
47 #define IEQUALIFIER_SHIFT       (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
48 #define IEQUALIFIER_ALT         (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
49 #define IEQUALIFIER_COMMAND     (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND)
50
51
52
53 /* Scrolling control */
54 #define SCROLLED_VERT   1
55 #define SCROLLED_HORIZ  2
56
57
58
59 /* Private class instance data */
60
61 struct PattEditData
62 {
63         struct Pattern  *Patt;
64         struct TextFont *EditorFont;
65
66         /* These are the actual Gadget position and size,
67          * regardless of any GREL_#? flag.
68          */
69         struct IBox              GBounds;
70
71         /* These are the actual editing area position and size. */
72         struct IBox              TBounds;
73
74         UWORD                    FontXSize;
75         UWORD                    FontYSize;
76
77         /* Pens used to draw various pattern editor elements */
78         ULONG                    BGPen;
79         ULONG                    TextPen;
80         ULONG                    LinesPen;
81         ULONG                    TinyLinesPen;
82
83         ULONG                    Flags;                 /* See definitions in <PattEditClass.h>                 */
84
85         /* Routine used to convert a note into its ASCII representation
86          * This routine is passed the pointer to the note and a buffer
87          * with <TrackChars> characters to fill in.
88          */
89         ASMCALL void    (*Note2ASCIIFunc)(REG(a1, UBYTE *s), REG(a2, struct Note *note));
90
91         UWORD                   TrackWidth;             /* Width of a track in pixels   */
92         UWORD                   TrackChars;             /* Width of a track in chars    */
93
94
95         /* How many tracks and lines we can fit in the gadget bounds */
96         UWORD                    DisplayTracks;
97         UWORD                    DisplayLines;
98
99         WORD                     LeftTrack;
100         WORD                     TopLine;
101
102         /* Current cursor position */
103         WORD                     Track;
104         WORD                     Line;
105         WORD                     Column;
106
107         UWORD                    CurrentInst;
108
109         UWORD                    CursState;             /* IDS_NORMAL, IDS_DISABLED or IDS_BUSY */
110         UWORD                    CursLinePos;   /* Cursor line position (0 = not drawn) */
111         struct Rectangle CursRect;              /* Cursor Position to erase it quickly. */
112
113         /* Cursor advancement */
114         WORD                     AdvanceTracks;
115         WORD                     AdvanceLines;
116
117         /* Range marking info */
118         UWORD                    RangeStartTrack;
119         UWORD                    RangeStartLine;
120         UWORD                    RangeEndTrack;
121         UWORD                    RangeEndLine;
122
123         /* Backup cursor position for mouse right button undo operation */
124         WORD                     BackupTrack;
125         WORD                     BackupLine;
126         WORD                     BackupColumn;
127
128         /* This variable holds a counter for the optimized scroller update.
129          * When the pattern is scrolling, updates are only sent after
130          * a specific amount of scrolling operations have occurred.
131          */
132         UWORD                    SliderCounter;
133
134         /* The following two are for track resizing.  ResizeXStart
135          * is the horizontal mouse position when the resize operation
136          * was started. ResizeChars is the last number of chars set.
137          */
138         UWORD                    ResizeXStart;
139         UWORD                    ResizeChars;
140
141         struct Rectangle RangeRect;             /* Backup of range rect to erase it quickly. */
142
143         /* Undo/Redo support */
144         ULONG                    Changes;
145         ULONG                    UndoCount;
146         ULONG                    UndoMem;
147         ULONG                    MaxUndoLevels;
148         ULONG                    MaxUndoMem;
149         struct MinList   UndoList;
150         struct MinList   RedoList;
151
152         /* For testing double/triple click */
153         ULONG                    DoubleClickSeconds;
154         ULONG                    DoubleClickMicros;
155         ULONG                    TripleClickSeconds;
156         ULONG                    TripleClickMicros;
157
158         /* Experimental: timer.device stuff */
159 //      struct timerequest      TimerIO;
160 //      struct MsgPort          TimerPort;
161 //      struct Interrupt        TimerInt;
162 };
163
164
165 /* This structure holds an entry for the Undo/Redo buffer. */
166
167 struct UndoNode
168 {
169         struct MinNode          Link;
170         UWORD                           Line,
171                                                 Track;
172         struct Note                     OldNote;
173 };
174
175
176
177 /* Function prototypes */
178
179 static ULONG HOOKCALL PattEditDispatcher (
180         REG(a0, Class *cl),
181         REG(a2, struct ExtGadget *g),
182         REG(a1, Msg msg));
183 static void             GetGadgetBox    (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect);
184 static BOOL             CalcDisplaySize (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gpi);
185 static void             SaveUndo                (struct PattEditData *ped);
186 static BOOL             UndoChange              (struct PattEditData *ped);
187 static BOOL             RedoChange              (struct PattEditData *ped);
188 static void             FreeUndoBuffers (struct PattEditData *ped, BOOL freeall);
189 static BOOL             MoveCursor              (struct PattEditData *ped, WORD x, WORD y);
190 static void             EraseCursor             (struct RastPort *rp, struct PattEditData *ped);
191 static UWORD    DrawCursor              (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g);
192 static void             DrawRange               (struct RastPort *rp, struct PattEditData *ped);
193 static void             ClearRange              (struct RastPort *rp, struct PattEditData *ped);
194 static void             RedrawAll               (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g);
195 static void             RedrawPattern   (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g);
196 static void             DrawTrackSeparators (struct RastPort *rp, struct PattEditData *ped, UWORD width);
197 static void             DrawTrackNumbers(struct RastPort *rp, struct PattEditData *ped);
198 static void             DrawPatternLines(struct RastPort *rp, struct PattEditData *ped, UWORD min, UWORD max);
199 static void             DrawNote                (struct RastPort *rp, struct PattEditData *ped);
200 void ASMCALL Note2ASCII                 (REG(a1, UBYTE *s), REG(a2, struct Note *note));
201 void ASMCALL Note2ASCIIBlank0   (REG(a1, UBYTE *s), REG(a2, struct Note *note));
202 static UWORD    ScrollPattern   (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g,
203                                                                 UWORD lefttrack, UWORD topline);
204 static void             NotifyCursor    (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
205 static void             NotifyVSlider   (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
206 static void             NotifyHSlider   (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
207 static void             NotifySliders   (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
208
209 //static void ASMCALL TimerIntServer (register REG(a1, struct ExtGadget *g));
210
211 HOOKCALL struct ClassLibrary    *_UserLibInit           (REG(a6, struct ClassLibrary *mybase));
212 HOOKCALL struct ClassLibrary    *_UserLibCleanup        (REG(a6, struct ClassLibrary *mybase));
213 HOOKCALL Class                                  *_GetEngine                     (REG(a6, struct ClassLibrary *mybase));
214
215
216
217 /* Library data */
218
219 const UBYTE LibName[] = LIBNAME;
220 const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
221 const UBYTE LibId[] = LIBNAME " 2.1 (8.4.97) " BUILDMODE " Â© 1995,96,97 by Bernardo Innocenti\n";
222
223
224
225 /* Local data */
226
227 #ifdef PORTABLE
228 static const ULONG TextNotes[MAXNOTES] =
229 {
230         ' -  ',
231         'C-0 ', 'C#0 ', 'D-0 ', 'D#0 ', 'E-0 ', 'F-0 ',
232         'F#0 ', 'G-0 ', 'G#0 ', 'A-0 ', 'A#0 ', 'B-0 ',
233
234         'C-1 ', 'C#1 ', 'D-1 ', 'D#1 ', 'E-1 ', 'F-1 ',
235         'F#1 ', 'G-1 ', 'G#1 ', 'A-1 ', 'A#1 ', 'B-1 ',
236
237         'C-2 ', 'C#2 ', 'D-2 ', 'D#2 ', 'E-2 ', 'F-2 ',
238         'F#2 ', 'G-2 ', 'G#2 ', 'A-2 ', 'A#2 ', 'B-2 ',
239
240         'C-3 ', 'C#3 ', 'D-3 ', 'D#3 ', 'E-3 ', 'F-3 ',
241         'F#3 ', 'G-3 ', 'G#3 ', 'A-3 ', 'A#3 ', 'B-3 ',
242
243         'C-4 ', 'C#4 ', 'D-4 ', 'D#4 ', 'E-4 ', 'F-4 ',
244         'F#4 ', 'G-4 ', 'G#4 ', 'A-4 ', 'A#4 ', 'B-4 ',
245
246         'C-5 ', 'C#5 ', 'D-5 ', 'D#5 ', 'E-5 ', 'F-5 ',
247         'F#5 ', 'G-5 ', 'G#5 ', 'A-5 ', 'A#5 ', 'B-5 '
248 };
249 #endif /* PORTABLE */
250
251
252 /* Keyboard rawkeys -> notes conversion table */
253
254 static const UBYTE KeyNotes0[10] = { 0, 26, 28,  0, 31, 33, 35,  0, 38, 40};    /* (1..0) */
255 static const UBYTE KeyNotes1[10] = {25, 27, 29, 30, 32, 34, 36, 37, 39, 41};    /* (Q..]) */
256 static const UBYTE KeyNotes2[10] = { 0, 14, 16,  0, 19, 21, 23,  0, 26, 28};    /* (A..') */
257 static const UBYTE KeyNotes3[10] = {13, 15, 17, 18, 20, 22, 24, 25, 27, 29};    /* (Z../) */
258
259 static const UBYTE HexValues[20]        = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', 'G', 'H', 'I', 'J'};
260 static const UBYTE HexValuesNo0[20]     = {' ','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F', 'G', 'H', 'I', 'J'};
261
262
263
264 /* Cursor patterns - Used in EraseCursor() and DrawCursor() */
265
266 static UWORD GhostPattern[]     = { 0xAAAA, 0x5555 };
267 static UWORD MarkPattern[]      = { 0xFFFF, 0x0000 };
268
269
270
271 /*****************/
272 /* Library bases */
273 /*****************/
274
275 struct ExecBase                 *SysBase                = NULL;
276 struct IntuitionBase    *IntuitionBase  = NULL;
277 struct GfxBase                  *GfxBase                = NULL;
278 struct UtilityBase              *UtilityBase    = NULL;
279 struct Library                  *KeymapBase             = NULL;
280
281
282 static ULONG HOOKCALL PattEditDispatcher (
283         REG(a0, Class *cl),
284         REG(a2, struct ExtGadget *g),
285         REG(a1, Msg msg))
286
287 /* PattEdit class dispatcher entrypoint. Handle BOOPSI messages.
288  */
289 {
290         struct RastPort         *rp;
291         struct PattEditData     *ped;
292         struct TagItem          *ti;
293         ULONG result = 0;
294
295
296         switch (msg->MethodID)
297         {
298                 case GM_GOACTIVE:
299
300                         ped = INST_DATA (cl, g);
301
302                         if (!ped->Patt)
303                         {
304                                 result = GMR_NOREUSE;
305                                 break;
306                         }
307
308                         g->Flags |= GFLG_SELECTED;
309
310                         /* Render active cursor */
311                         if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
312                         {
313                                 DrawCursor (rp, ped, g);
314                                 ReleaseGIRPort (rp);
315                         }
316
317                         /* Do not process InputEvent when the gadget has been
318                          * activated by ActivateGadget().
319                          */
320                         if (!((struct gpInput *)msg)->gpi_IEvent)
321                         {
322                                 result = GMR_MEACTIVE;
323                                 break;
324                         }
325
326                         /* Note: The input event that triggered the gadget
327                          * activation (usually a mouse click) should be passed
328                          * to the GM_HANDLEINPUT method, so we fall down to it.
329                          */
330
331                 case GM_HANDLEINPUT:
332                 {
333                         struct InputEvent *ie = ((struct gpInput *)msg)->gpi_IEvent;
334                         UWORD topline, lefttrack;
335                         WORD    MouseX, MouseY;         /* Mouse coordinates relative to editing area bounds */
336                         BOOL    moved           = FALSE,
337                                         change_note     = FALSE,
338                                         scroll_pattern = FALSE;
339                         UWORD   scrolled        = 0;
340
341                         result = GMR_MEACTIVE;
342                         ped = INST_DATA (cl, g);
343
344                         /* MouseX and MouseY represent the mouse position inside the
345                          * editing area of the gadget.
346                          */
347                         MouseX = ((struct gpInput *)msg)->gpi_Mouse.X + ped->GBounds.Left - ped->TBounds.Left;
348                         MouseY = ((struct gpInput *)msg)->gpi_Mouse.Y + ped->GBounds.Top - ped->TBounds.Top;
349
350                         switch (ie->ie_Class)
351                         {
352                                 case IECLASS_TIMER:
353
354                                         /* Timer events are used to keep scrolling
355                                          * when the mouse is outside the bounds of the
356                                          * gadget.  When a timer event is recevied and
357                                          * the mouse button is pressed, we scroll the
358                                          * cursor.
359                                          */
360                                                 if (ped->Flags & PEF_DRAGGING)
361                                                         moved = MoveCursor (ped, MouseX, MouseY);
362
363                                         break;
364
365                                 case IECLASS_RAWMOUSE:
366                                 {
367                                         BOOL double_click = FALSE, triple_click;
368                                         BOOL outside =
369                                                 (MouseX < 0) || (MouseX >= ped->TBounds.Width) ||
370                                                 (MouseY < 0) || (MouseY >= ped->TBounds.Height);
371
372                                         switch (ie->ie_Code)
373                                         {
374                                                 case MENUDOWN:
375
376                                                         /* Abort cursor dragging operation and restore
377                                                          * old cursor position.
378                                                          */
379                                                         if (ped->Flags & PEF_DRAGGING)
380                                                         {
381                                                                 ped->Line       = ped->BackupLine;
382                                                                 ped->Track      = ped->BackupTrack;
383                                                                 ped->Column     = ped->BackupColumn;
384                                                                 moved = TRUE;
385                                                                 ped->Flags &= ~(PEF_DRAGGING | PEF_SCROLLING);
386                                                         }
387                                                         else if (ped->Flags & PEF_RESIZETRACKS)
388                                                         {
389                                                                 if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
390                                                                 {
391                                                                         SetDrMd (rp, COMPLEMENT);
392                                                                         DrawTrackSeparators (rp, ped, ped->ResizeChars * ped->FontXSize);
393                                                                         ReleaseGIRPort (rp);
394                                                                 }
395                                                                 ped->Flags &= ~PEF_RESIZETRACKS;
396                                                         }
397                                                         else result = GMR_REUSE;
398
399                                                         break;
400
401                                                 case SELECTUP:
402                                                         /* Send final update to sliders */
403                                                         if (ped->Flags & PEF_SCROLLING)
404                                                                 scrolled = SCROLLED_VERT | SCROLLED_HORIZ;
405
406                                                         if (ped->Flags & PEF_RESIZETRACKS)
407                                                         {
408                                                                 ped->TrackChars = ped->ResizeChars;
409                                                                 ped->TrackWidth = ped->TrackChars * ped->FontXSize;
410                                                                 CalcDisplaySize(ped, g, ((struct gpInput *)msg)->gpi_GInfo);
411                                                                 if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
412                                                                 {
413                                                                         //SetDrMd (rp, COMPLEMENT);
414                                                                         //DrawTrackSeparators (rp, ped, ped->ResizeChars * ped->FontXSize);
415
416                                                                         DoMethod ((Object *)g, GM_RENDER, ((struct gpInput *)msg)->gpi_GInfo, rp,
417                                                                                 GREDRAW_REDRAW);
418
419                                                                         ReleaseGIRPort (rp);
420                                                                 }
421                                                         }
422
423                                                         /* Stop scrolling, dragging, resizing tracks and
424                                                          * adjustisting the scroll box.
425                                                          */
426                                                         ped->Flags &= ~(PEF_DRAGGING | PEF_SCROLLING |
427                                                                 PEF_RESIZETRACKS | PEF_ADJUSTSCROLLBOX);
428
429                                                         break;
430
431                                                 case SELECTDOWN:
432
433                                                         /* Check if mouse click is still over the gadget */
434
435                                                         if (outside)
436                                                         {
437                                                                 /* Click outside editing area box:
438                                                                  * Check if click is really outside gadget box.
439                                                                  * If it is, deactivate the gadget and reuse the event.
440                                                                  * Notify application if it is interested in
441                                                                  * hearing IDCMP_GADGETUP codes.
442                                                                  */
443                                                                 MouseX = ((struct gpInput *)msg)->gpi_Mouse.X;
444                                                                 MouseY = ((struct gpInput *)msg)->gpi_Mouse.Y;
445
446                                                                 if      ((MouseX < 0) || (MouseX >= ped->GBounds.Width) ||
447                                                                         (MouseY < 0) || (MouseY >= ped->GBounds.Height))
448                                                                         result = GMR_REUSE | GMR_VERIFY;
449                                                                 break;
450                                                         }
451                                                         else
452                                                         {
453                                                                 if (MouseX % ped->TrackWidth >= ped->TrackWidth - 2)
454                                                                 {
455                                                                         /* Start track resizing mode */
456                                                                         ped->Flags |= PEF_RESIZETRACKS;
457                                                                         ped->ResizeXStart = MouseX;
458                                                                         ped->ResizeChars = 0;
459                                                                 }
460                                                                 else
461                                                                 {
462                                                                         /* Backup cursor position for undo feature */
463                                                                         ped->BackupLine         = ped->Line;
464                                                                         ped->BackupTrack        = ped->Track;
465                                                                         ped->BackupColumn       = ped->Column;
466
467                                                                         /* Start cursor drag mode */
468                                                                         ped->Flags |= PEF_DRAGGING;
469                                                                 }
470                                                         }
471
472                                                         /* Check for double click */
473
474                                                         if (DoubleClick (ped->DoubleClickSeconds, ped->DoubleClickMicros,
475                                                                 ie->ie_TimeStamp.tv_secs, ie->ie_TimeStamp.tv_micro))
476                                                         {
477                                                                 double_click = TRUE;
478
479                                                                 if (DoubleClick (ped->TripleClickSeconds, ped->TripleClickMicros,
480                                                                         ped->DoubleClickSeconds, ped->DoubleClickMicros))
481                                                                         triple_click = TRUE;
482                                                                 else
483                                                                         triple_click = FALSE;
484
485                                                                 ped->TripleClickSeconds = ped->DoubleClickSeconds;
486                                                                 ped->TripleClickMicros  = ped->DoubleClickMicros;
487                                                         }
488
489                                                         ped->DoubleClickSeconds = ie->ie_TimeStamp.tv_secs;
490                                                         ped->DoubleClickMicros  = ie->ie_TimeStamp.tv_micro;
491
492                                                         /* NOTE: I'm falling through here! */
493
494                                                 default:
495
496                                                         if ((ped->Flags & PEF_DRAGGING))
497                                                         {
498                                                                 if (outside) ped->Flags |= PEF_SCROLLING;
499                                                                 else
500                                                                 {
501                                                                         if (ped->Flags & PEF_SCROLLING)
502                                                                         {
503                                                                                 /* Last update to sync sliders */
504                                                                                 scrolled = SCROLLED_VERT | SCROLLED_HORIZ;
505                                                                                 ped->Flags &= ~PEF_SCROLLING;
506                                                                         }
507                                                                         moved = MoveCursor (ped, MouseX, MouseY);
508                                                                 }
509
510                                                         /*      if ((outside) && (ped->Flags & PEF_DRAGGING) && !(ped->Flags & PEF_SCROLLING))
511                                                                 {
512                                                                         ped->Flags |= PEF_SCROLLING;
513
514                                                                         ped->TimerIO.tr_node.io_Command = TR_ADDREQUEST;
515                                                                         ped->TimerIO.tr_time.tv_micro = 100000;
516                                                                         BeginIO ((struct IORequest *)&ped->TimerIO);
517                                                                 }       */
518                                                         }
519                                                         else if (ped->Flags & PEF_RESIZETRACKS)
520                                                         {
521                                                                 UWORD chars = ped->TrackChars +
522                                                                         (((MouseX - ped->ResizeXStart) / ped->FontXSize)
523                                                                         / ((ped->ResizeXStart / ped->TrackWidth) + 1));
524
525                                                                 if (chars < MINTRACKCHARS) chars = MINTRACKCHARS;
526                                                                 if (chars > MAXTRACKCHARS) chars = MAXTRACKCHARS;
527
528                                                                 if (chars != ped->ResizeChars)
529                                                                 {
530                                                                         if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
531                                                                         {
532                                                                                 SetDrMd (rp, COMPLEMENT);
533
534                                                                                 /* Erase old lines      */
535                                                                                 DrawTrackSeparators (rp, ped, ped->ResizeChars * ped->FontXSize);
536
537                                                                                 /* Redraw lines         */
538                                                                                 DrawTrackSeparators (rp, ped, chars * ped->FontXSize);
539                                                                                 ped->ResizeChars = chars;
540
541                                                                                 ReleaseGIRPort (rp);
542                                                                         }
543                                                                 }
544                                                         }
545
546                                                         if (!moved && double_click)
547                                                         {
548                                                                 if (ped->Flags & PEF_MARKING)
549                                                                 {
550                                                                         if (triple_click && !(ped->Flags & PEF_MARKFULLTRACKS)
551                                                                                 && (ped->RangeStartLine == ped->RangeEndLine))
552                                                                         {
553                                                                                 /* Start full track marking mode */
554                                                                                 ped->Flags |= PEF_MARKFULLTRACKS;
555                                                                                 ped->RangeStartLine = 0;
556                                                                                 ped->RangeEndLine = ped->Patt->Lines - 1;
557                                                                                 moved = TRUE;
558                                                                         }
559                                                                         else
560                                                                                 /* Stop marking mode */
561                                                                                 ped->Flags &= ~(PEF_MARKING | PEF_MARKFULLTRACKS);
562                                                                 }
563                                                                 else
564                                                                 {
565                                                                         /* Start normal marking mode */
566
567                                                                         ped->Flags |= PEF_MARKING;
568                                                                         ped->RangeStartLine = ped->Line;
569                                                                         ped->RangeStartTrack = ped->Track;
570                                                                         moved = TRUE;
571                                                                 }
572
573                                                                 if (!(ped->Flags & PEF_MARKING))
574                                                                 {
575                                                                         if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
576                                                                         {
577                                                                                 ClearRange (rp, ped);
578                                                                                 ReleaseGIRPort (rp);
579                                                                         }
580                                                                         moved = TRUE;
581                                                                 }
582                                                         }
583
584                                                         break;
585
586                                         }       /* End switch (ie->ie_Code) */
587
588                                         break;
589                                 }
590
591                                 case IECLASS_RAWKEY:
592
593                                         if (ie->ie_Code & IECODE_UP_PREFIX)
594                                         {
595                                                 /* Send final update to slider */
596
597                                                 if ((ie->ie_Code == (IECODE_UP_PREFIX | CURSORUP))
598                                                         || (ie->ie_Code == (IECODE_UP_PREFIX | CURSORDOWN)))
599                                                         scrolled = SCROLLED_VERT;
600
601                                                 else if ((ie->ie_Code == (IECODE_UP_PREFIX | CURSORLEFT))
602                                                         || (ie->ie_Code == (IECODE_UP_PREFIX | CURSORRIGHT)))
603                                                         scrolled = SCROLLED_HORIZ;
604                                         }
605                                         else if ((ie->ie_Qualifier & IEQUALIFIER_COMMAND) && (ie->ie_Code != 0x67))
606                                                 result = GMR_REUSE;
607                                         else switch (ie->ie_Code)
608                                         {
609                                                 case CURSORUP:
610
611                                                         if (ped->Line)
612                                                         {
613                                                                 if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
614                                                                 {
615                                                                         if (ped->Line > ped->TopLine)
616                                                                                 ped->Line = ped->TopLine;
617                                                                         else
618                                                                                 if (ped->Line >= ped->DisplayLines - 1)
619                                                                                         ped->Line -= ped->DisplayLines - 1;
620                                                                                 else ped->Line = 0;
621                                                                 }
622                                                                 else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
623                                                                         ped->Line = 0;
624                                                                 else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
625                                                                 {
626                                                                         if (ped->TopLine + ped->DisplayLines < ped->Patt->Lines)
627                                                                         {
628                                                                                 topline = ped->TopLine + 1;
629                                                                                 lefttrack = ped->LeftTrack;
630                                                                                 scroll_pattern = TRUE;
631                                                                                 if (ped->Line < topline)
632                                                                                         ped->Line++;
633                                                                         }
634                                                                 }
635                                                                 else ped->Line--;       /* Cursor key without qualifiers */
636
637                                                                 moved = TRUE;
638                                                         }
639                                                         else if (ped->Flags & PEF_VWRAP)
640                                                         {
641                                                                 ped->Line = ped->Patt->Lines - 1;
642                                                                 moved = TRUE;
643                                                         }
644
645                                                         break;
646
647
648                                                 case CURSORDOWN:
649
650                                                         if (ped->Line < ped->Patt->Lines - 1)
651                                                         {
652                                                                 if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
653                                                                 {
654                                                                         if (ped->Line < ped->TopLine + ped->DisplayLines - 1)
655                                                                                 ped->Line = ped->TopLine + ped->DisplayLines - 1;
656                                                                         else
657                                                                         {
658                                                                                 ped->Line += ped->DisplayLines - 1;
659                                                                                 if (ped->Line > ped->Patt->Lines - 1)
660                                                                                         ped->Line = ped->Patt->Lines - 1;
661                                                                         }
662                                                                 }
663                                                                 else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
664                                                                         ped->Line = ped->Patt->Lines - 1;
665                                                                 else if (ie->ie_Qualifier & IEQUALIFIER_CONTROL)
666                                                                 {
667                                                                         if (ped->TopLine > 1)
668                                                                         {
669                                                                                 topline = ped->TopLine - 1;
670                                                                                 lefttrack = ped->LeftTrack;
671                                                                                 scroll_pattern = TRUE;
672                                                                                 if (ped->Line >= (topline + ped->DisplayLines))
673                                                                                         ped->Line--;
674                                                                         }
675                                                                 }
676                                                                 else ped->Line++;       /* Cursor key without qualifiers */
677
678                                                                 moved = TRUE;
679                                                         }
680                                                         else if (ped->Flags & PEF_VWRAP)
681                                                         {
682                                                                 ped->Line = 0;
683                                                                 moved = TRUE;
684                                                         }
685
686                                                         break;
687
688
689                                                 case CURSORLEFT:
690
691                                                         if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
692                                                         {
693                                                                 if (ped->Track)
694                                                                 {
695                                                                         ped->Track--;
696                                                                         moved = TRUE;
697                                                                 }
698                                                                 else if (ped->Column)
699                                                                 {
700                                                                         ped->Column = 0;
701                                                                         moved = TRUE;
702                                                                 }
703                                                         }
704                                                         else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
705                                                         {
706                                                                 if (ped->Track)
707                                                                 {
708                                                                         ped->Track = 0;
709                                                                         moved = TRUE;
710                                                                 }
711                                                                 else if (ped->Column)
712                                                                 {
713                                                                         ped->Column = 0;
714                                                                         moved = TRUE;
715                                                                 }
716                                                         }
717                                                         else
718                                                         {
719                                                                 if (ped->Column)
720                                                                 {
721                                                                         ped->Column--;
722                                                                         moved = TRUE;
723                                                                 }
724                                                                 else if (ped->Track)
725                                                                 {
726                                                                         ped->Track--;
727                                                                         ped->Column = COL_COUNT-1;
728                                                                         moved = TRUE;
729                                                                 }
730                                                         }
731
732                                                         if (!moved && (ped->Flags & PEF_HWRAP))
733                                                         {
734                                                                 ped->Track = ped->Patt->Tracks - 1;
735                                                                 ped->Column = COL_COUNT-1;
736                                                                 moved = TRUE;
737                                                         }
738
739                                                         break;
740
741
742                                                 case CURSORRIGHT:
743
744                                                         if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
745                                                         {
746                                                                 if (ped->Track < ped->Patt->Tracks - 1)
747                                                                 {
748                                                                         ped->Track++;
749                                                                         moved = TRUE;
750                                                                 }
751                                                                 else if (ped->Column != COL_COUNT-1)
752                                                                 {
753                                                                         ped->Column = COL_COUNT-1;
754                                                                         moved = TRUE;
755                                                                 }
756                                                         }
757                                                         else if (ie->ie_Qualifier & IEQUALIFIER_ALT)
758                                                         {
759                                                                 if (ped->Track != ped->Patt->Tracks - 1)
760                                                                 {
761                                                                         ped->Track = ped->Patt->Tracks - 1;
762                                                                         moved = TRUE;
763                                                                 }
764                                                                 else if (ped->Column != COL_COUNT-1)
765                                                                 {
766                                                                         ped->Column = COL_COUNT-1;
767                                                                         moved = TRUE;
768                                                                 }
769                                                         }
770                                                         else
771                                                         {
772                                                                 if (ped->Column < COL_COUNT-1)
773                                                                 {
774                                                                         ped->Column++;
775                                                                         moved = TRUE;
776                                                                 }
777                                                                 else if (ped->Track < ped->Patt->Tracks - 1)
778                                                                 {
779                                                                         ped->Track++;
780                                                                         ped->Column = 0;
781                                                                         moved = TRUE;
782                                                                 }
783                                                         }
784
785                                                         if (!moved && (ped->Flags & PEF_HWRAP))
786                                                         {
787                                                                 ped->Track = 0;
788                                                                 ped->Column = 0;
789                                                                 moved = TRUE;
790                                                         }
791
792                                                         break;
793
794
795                                                 case 0x00:      /* ESC  */
796                                                 case 0x5F:      /* HELP */
797                                                         result = GMR_REUSE;
798                                                         break;
799
800
801                                                 case 0x42:      /* TAB */
802
803                                                         if (ie->ie_Qualifier & IEQUALIFIER_ALT)
804                                                                 /* Deactivate gadget on ALT+TAB to allow
805                                                                  * window cycling in the application.
806                                                                  */
807                                                                 result = GMR_REUSE;
808                                                         else
809                                                         {
810                                                                 if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
811                                                                 {
812                                                                          if (ped->Track > 0)
813                                                                                 ped->Track--;
814                                                                         else
815                                                                                 ped->Track = ped->Patt->Tracks - 1;
816                                                                 }
817                                                                 else
818                                                                 {
819                                                                          if (ped->Track < ped->Patt->Tracks - 1)
820                                                                                 ped->Track++;
821                                                                         else
822                                                                                 ped->Track = 0;
823                                                                 }
824
825                                                                 ped->Column = COL_NOTE;
826                                                                 moved = TRUE;
827                                                         }
828
829                                                         break;
830
831
832                                                 case 0x0D:      /* RETURN */
833                                                         ped->Column = COL_NOTE;
834                                                         if (ped->Line < ped->Patt->Lines - 1)
835                                                                 ped->Line++;
836                                                         else if (ped->Flags & PEF_VWRAP)
837                                                                 ped->Line = 0;
838                                                         moved = TRUE;
839                                                         break;
840
841
842                                                 case 0x46:      /* DEL */
843                                                 {
844                                                         struct Note *note = &ped->Patt->Notes[ped->Track][ped->Line];
845
846                                                         SaveUndo (ped);
847                                                         change_note = TRUE;
848
849                                                         if (ie->ie_Qualifier & IEQUALIFIER_SHIFT)
850                                                                 memset (note, 0, sizeof (struct Note));
851                                                         else switch (ped->Column)
852                                                         {
853                                                                 case COL_NOTE:
854                                                                         note->Note = 0;
855                                                                         note->Inst = 0;
856                                                                         break;
857
858                                                                 case COL_INSTH:
859                                                                         note->Inst &= 0x0F;
860                                                                         break;
861
862                                                                 case COL_INSTL:
863                                                                         note->Inst &= 0xF0;
864                                                                         break;
865
866                                                                 case COL_EFF:
867                                                                         note->EffNum = EFF_NULL;
868                                                                         break;
869
870                                                                 case COL_VALH:
871                                                                         note->EffVal &= 0x0F;
872                                                                         break;
873
874                                                                 case COL_VALL:
875                                                                         note->EffVal &= 0xF0;
876                                                                         break;
877                                                         }
878                                                         break;
879                                                 }
880
881                                                 default:
882                                                 {
883                                                         struct Note *note = &ped->Patt->Notes[ped->Track][ped->Line];
884                                                         UBYTE tmp = 0, keycode = 1;
885
886                                                         /* Convert to hex number */
887
888                                                         if (ped->Column != COL_NOTE)
889                                                         {
890                                                                 if (MapRawKey (ie, &keycode, 1, NULL) == -1)
891                                                                         keycode = 0;
892                                                                 else
893                                                                 {
894                                                                         if (keycode >= '0' && keycode <= '9')
895                                                                                 tmp = keycode - '0';
896                                                                         else if (keycode >= 'a' && keycode <= ((ped->Column == COL_EFF) ? 'j' : 'f'))
897                                                                                 tmp = keycode - ('a' - 10);
898                                                                         else
899                                                                                 keycode = 0;
900                                                                 }
901                                                         }
902
903                                                         if (keycode) switch (ped->Column)
904                                                         {
905                                                                 case COL_NOTE:
906
907                                                                         /* Insert note */
908
909                                                                         if (ie->ie_Code >= 0x1 && ie->ie_Code <= 0x0A)
910                                                                                 tmp = KeyNotes0[ie->ie_Code - 0x1];
911                                                                         else if (ie->ie_Code >= 0x10 && ie->ie_Code <= 0x19)
912                                                                                 tmp = KeyNotes1[ie->ie_Code - 0x10];
913                                                                         else if (ie->ie_Code >= 0x20 && ie->ie_Code <= 0x29)
914                                                                                 tmp = KeyNotes2[ie->ie_Code - 0x20];
915                                                                         else if (ie->ie_Code >= 0x31 && ie->ie_Code <= 0x39)
916                                                                                 tmp = KeyNotes3[ie->ie_Code - 0x31];
917
918                                                                         if (tmp)
919                                                                         {
920                                                                                 SaveUndo (ped);
921                                                                                 change_note = TRUE;
922                                                                                 note->Note = tmp;
923                                                                                 note->Inst = ped->CurrentInst;
924                                                                         }
925                                                                         break;
926
927                                                                 case COL_INSTL:
928                                                                         SaveUndo (ped);
929                                                                         change_note = TRUE;
930                                                                         note->Inst = (note->Inst & 0xF0) | tmp;
931                                                                         break;
932
933                                                                 case COL_INSTH:
934                                                                         if (tmp < MAXINSTRUMENTS>>4)
935                                                                         {
936                                                                                 SaveUndo (ped);
937                                                                                 change_note = TRUE;
938                                                                                 note->Inst = (note->Inst & 0x0F) | (tmp<<4);
939                                                                         }
940                                                                         break;
941
942                                                                 case COL_EFF:
943                                                                         SaveUndo (ped);
944                                                                         change_note = TRUE;
945                                                                         note->EffNum = tmp;
946                                                                         break;
947
948                                                                 case COL_VALL:
949                                                                         SaveUndo (ped);
950                                                                         change_note = TRUE;
951                                                                         note->EffVal = (note->EffVal & 0xF0) | tmp;
952                                                                         break;
953
954                                                                 case COL_VALH:
955                                                                         SaveUndo (ped);
956                                                                         change_note = TRUE;
957                                                                         note->EffVal = (note->EffVal & 0x0F) | (tmp<<4);
958                                                                         break;
959                                                         }
960                                                         break;
961                                                 }
962
963                                         } /* End switch (ie->ie_Code) */
964
965                                         break;
966
967                                 default:
968                                         break;
969
970                         }       /* End switch (ie->ie_Class) */
971
972                         if (moved || change_note || scroll_pattern)
973                         {
974                                 if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
975                                 {
976                                         if (scroll_pattern)
977                                                 scrolled |= ScrollPattern (rp, ped, g, lefttrack, topline);
978
979                                         if (change_note)
980                                         {
981                                                 EraseCursor (rp, ped);
982                                                 DrawNote (rp, ped);
983
984                                                 /* Advance cursor */
985
986                                                 ped->Track      += ped->AdvanceTracks;
987                                                 ped->Line       += ped->AdvanceLines;
988
989                                                 if (ped->Flags & PEF_HWRAP)
990                                                         ped->Track = ped->Track % ped->Patt->Tracks;
991                                                 else
992                                                 {
993                                                         if (ped->Track < 0)
994                                                                 ped->Track = 0;
995                                                         else if (ped->Track > ped->Patt->Tracks - 1)
996                                                                 ped->Track = ped->Patt->Tracks - 1;
997                                                 }
998
999                                                 if (ped->Flags & PEF_VWRAP)
1000                                                         ped->Line = ped->Line % ped->Patt->Lines;
1001                                                 else
1002                                                 {
1003                                                         if (ped->Line < 0)
1004                                                                 ped->Line = 0;
1005                                                         else if (ped->Line > ped->Patt->Lines - 1)
1006                                                                 ped->Line = ped->Patt->Lines - 1;
1007                                                 }
1008                                         }
1009
1010                                         scrolled |= DrawCursor (rp, ped, g);
1011                                         ReleaseGIRPort (rp);
1012                                 }
1013
1014                                 /* Broadcast notification to our target object. */
1015                                 NotifyCursor (ped, g, ((struct gpInput *)msg)->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
1016                         }
1017
1018                         if (scrolled & SCROLLED_VERT)
1019                                 NotifyVSlider (ped, g, ((struct gpInput *)msg)->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
1020
1021                         if (scrolled & SCROLLED_HORIZ)
1022                                 NotifyHSlider (ped, g, ((struct gpInput *)msg)->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
1023
1024                         break;
1025                 }
1026
1027
1028                 case GM_RENDER:
1029
1030                         ped = INST_DATA (cl, g);
1031
1032                         /* We do not support GREDRAW_TOGGLE */
1033
1034                         switch (((struct gpRender *)msg)->gpr_Redraw)
1035                         {
1036                                 case GREDRAW_REDRAW:
1037 #ifndef OS30_ONLY
1038                                         /* Recalculate the display size only on V37.
1039                                          * As of V39, Intuition supports GM_LAYOUT, which
1040                                          * allows a more optimized way to handle dynamic resizing.
1041                                          */
1042                                         if (IntuitionBase->LibNode.lib_Version < 39)
1043                                                 if (CalcDisplaySize (ped, g, ((struct gpRender *)msg)->gpr_GInfo))
1044                                                         NotifySliders (ped, g, ((struct gpRender *)msg)->gpr_GInfo, 0);
1045 #endif /* OS30_ONLY */
1046
1047                                         RedrawAll (((struct gpRender *)msg)->gpr_RPort, ped, g);
1048                                         break;
1049
1050                                 case GREDRAW_UPDATE:
1051                                         /* Just redraw the notes, not the lines, etc. */
1052                                         RedrawPattern (((struct gpRender *)msg)->gpr_RPort, ped, g);
1053                                         break;
1054                         }
1055
1056                         break;
1057
1058
1059                 case GM_HITTEST:
1060
1061                         /* As we are rectangular shaped, we are always hit */
1062                         result = GMR_GADGETHIT;
1063                         break;
1064
1065                 case GM_HELPTEST:
1066                         result = GMR_HELPHIT;
1067                         break;
1068
1069                 case GM_GOINACTIVE:
1070
1071                         ped = INST_DATA (cl, g);
1072
1073                         g->Flags &= ~GFLG_SELECTED;
1074
1075                         if (ped->Patt)
1076                                 /* Render disabled cursor */
1077                                 if (rp = ObtainGIRPort (((struct gpGoInactive *)msg)->gpgi_GInfo))
1078                                 {
1079                                         DrawCursor (rp, ped, g);
1080                                         ReleaseGIRPort (rp);
1081                                 }
1082                         break;
1083
1084
1085                 case GM_LAYOUT:
1086
1087                         ped = INST_DATA (cl, g);
1088
1089                         if (CalcDisplaySize (ped, g, ((struct gpLayout *)msg)->gpl_GInfo))
1090                                 NotifySliders (ped, g, ((struct gpLayout *)msg)->gpl_GInfo, 0);
1091
1092                         break;
1093
1094
1095                 case OM_UPDATE:
1096                 case OM_SET:
1097                 {
1098                         struct TagItem *tstate = ((struct opSet *)msg)->ops_AttrList;
1099                         BOOL    redraw_all              = FALSE,
1100                                         redraw_pattern  = FALSE,
1101                                         move_cursor             = FALSE,
1102                                         scroll_pattern  = FALSE,
1103                                         change_note             = FALSE,
1104                                         do_super_method = FALSE;
1105                         WORD    lefttrack, topline;
1106                         WORD    line, track, column;
1107
1108                         ped = INST_DATA (cl, g);
1109
1110                         lefttrack       = ped->LeftTrack;
1111                         topline         = ped->TopLine;
1112                         line            = ped->Line;
1113                         track           = ped->Track;
1114                         column          = ped->Column;
1115
1116
1117                         while (ti = NextTagItem(&tstate))
1118                         {
1119                                 switch (ti->ti_Tag)
1120                                 {
1121                                         case PEA_CursTrack:
1122                                                 track = ti->ti_Data;
1123                                                 break;
1124
1125                                         case PEA_CursColumn:
1126                                                 column = ti->ti_Data;
1127                                                 break;
1128
1129                                         case PEA_CursLine:
1130                                                 line = ti->ti_Data;
1131                                                 break;
1132
1133                                         case PEA_LeftTrack:
1134                                                 if (lefttrack != ti->ti_Data)
1135                                                 {
1136                                                         lefttrack = ti->ti_Data;
1137                                                         track = lefttrack + (ped->DisplayTracks / 2);
1138                                                         scroll_pattern = TRUE;
1139                                                 }
1140                                                 break;
1141
1142                                         case PEA_TopLine:
1143                                                 if (topline != ti->ti_Data)
1144                                                 {
1145                                                         topline = ti->ti_Data;
1146                                                         line = topline + (ped->DisplayLines / 2);
1147                                                         scroll_pattern = TRUE;
1148                                                 }
1149                                                 break;
1150
1151                                         case PEA_Left:
1152                                                 if (lefttrack)
1153                                                         track = lefttrack - 1;
1154                                                 break;
1155
1156                                         case PEA_Right:
1157                                                 if (ped->Patt && (lefttrack + ped->DisplayTracks < ped->Patt->Tracks))
1158                                                         track = lefttrack + ped->DisplayTracks;
1159                                                 break;
1160
1161                                         case PEA_Up:
1162                                                 if (topline)
1163                                                         line = topline - 1;
1164                                                 break;
1165
1166                                         case PEA_Down:
1167                                                 if (ped->Patt && (topline + ped->DisplayLines < ped->Patt->Lines))
1168                                                         line = topline + ped->DisplayLines;
1169                                                 break;
1170
1171                                         case PEA_CursLeft:
1172                                                 if (track)
1173                                                         track--;
1174                                                 else if (ped->Flags & PEF_VWRAP)
1175                                                         track = ped->Patt->Tracks - 1;
1176
1177                                                 break;
1178
1179                                         case PEA_CursRight:
1180                                                 if (ped->Patt && (track < ped->Patt->Tracks - 1))
1181                                                         track++;
1182                                                 else if (ped->Flags & PEF_HWRAP)
1183                                                         track = 0;
1184
1185                                                 break;
1186
1187                                         case PEA_CursUp:
1188                                                 if (line)
1189                                                         line--;
1190                                                 else if (ped->Flags & PEF_VWRAP)
1191                                                         line = ped->Patt->Lines - 1;
1192
1193                                                 break;
1194
1195                                         case PEA_CursDown:
1196                                                 if (ped->Patt && line < ped->Patt->Lines - 1)
1197                                                         line++;
1198                                                 else if (ped->Flags & PEF_VWRAP)
1199                                                         line = 0;
1200
1201                                                 break;
1202
1203                                         case PEA_UndoChange:
1204                                                 if (((LONG)ti->ti_Data) < 0)
1205                                                         change_note |= RedoChange (ped);
1206                                                 else
1207                                                         change_note |= UndoChange (ped);
1208                                                 line = ped->Line;
1209                                                 track = ped->Track;
1210
1211                                                 break;
1212
1213                                         case PEA_Changes:
1214                                                 ped->Changes = ti->ti_Data;
1215                                                 break;
1216
1217                                         case PEA_MarkRegion:
1218                                         {
1219                                                 struct Rectangle *region = (struct Rectangle *)ti->ti_Data;
1220
1221                                                 if (!region)                            /* End mark mode */
1222
1223                                                         ped->Flags &= ~(PEF_MARKING | PEF_MARKFULLTRACKS);
1224
1225                                                 else if (region == (struct Rectangle *)-1)      /* Toggle mark mode */
1226                                                 {
1227                                                         if (ped->Flags & PEF_MARKFULLTRACKS)
1228                                                                 ped->Flags &= ~(PEF_MARKING | PEF_MARKFULLTRACKS);
1229                                                         else if (ped->Flags & PEF_MARKING)
1230                                                         {
1231                                                                 if (ped->RangeStartLine == ped->RangeEndLine)
1232                                                                 {
1233                                                                         ped->Flags |= PEF_MARKFULLTRACKS;
1234                                                                         ped->RangeStartLine             = 0;
1235                                                                         ped->RangeEndLine               = ped->Patt->Lines - 1;
1236                                                                 }
1237                                                                 else
1238                                                                         ped->Flags &= ~PEF_MARKING;
1239                                                         }
1240                                                         else
1241                                                         {
1242                                                                 ped->Flags |= PEF_MARKING;
1243                                                                 ped->RangeStartTrack    = track;
1244                                                                 ped->RangeStartLine             = line;
1245                                                         }
1246                                                 }
1247                                                 else                                            /* Start mark mode */
1248                                                 {
1249                                                         memcpy (&ped->RangeStartTrack, region, sizeof (struct Rectangle));
1250                                                         track   = ped->Track    = region->MaxX;
1251                                                         line    = ped->Line             = region->MaxY;
1252                                                         ped->Flags |= PEF_MARKING;
1253                                                 }
1254
1255                                                 if (rp = ObtainGIRPort (((struct opSet *)msg)->ops_GInfo))
1256                                                 {
1257                                                         if (!(ped->Flags & PEF_MARKING))
1258                                                                 ClearRange (rp, ped);
1259
1260                                                         DrawCursor (rp, ped, g);
1261
1262                                                         ReleaseGIRPort (rp);
1263                                                 }
1264
1265                                                 break;
1266                                         }
1267
1268                                         case PEA_Flags:
1269                                         {
1270                                                 ULONG   oldflags = ped->Flags;
1271                                                 ped->Flags = (ped->Flags & 0xFFFF0000) | ti->ti_Data;
1272
1273                                                 if ((oldflags & (PEF_HEXMODE | PEF_BLANKZERO | PEF_INVERSETEXT | PEF_DOTINYLINES)) !=
1274                                                         (ped->Flags & (PEF_HEXMODE | PEF_BLANKZERO | PEF_INVERSETEXT | PEF_DOTINYLINES)))
1275                                                 {
1276                                                         /* Select Note2ASCII func */
1277                                                         if (ped->Flags & PEF_BLANKZERO)
1278                                                                 ped->Note2ASCIIFunc = Note2ASCIIBlank0;
1279                                                         else
1280                                                                 ped->Note2ASCIIFunc = Note2ASCII;
1281
1282                                                         if (!(ped->Flags & PEF_DOCURSORRULER))
1283                                                                 ped->CursLinePos = 0;
1284
1285                                                         /* Cause complete radraw */
1286                                                         redraw_all = TRUE;
1287                                                 }
1288
1289                                                 if ((oldflags & PEF_DOCURSORRULER) !=
1290                                                         (ped->Flags & PEF_DOCURSORRULER))
1291                                                                 move_cursor = TRUE;
1292
1293                                                 break;
1294                                         }
1295
1296                                         case PEA_Pattern:
1297                                                 ped->Patt = (struct Pattern *) ti->ti_Data;
1298                                                 redraw_pattern = TRUE;
1299                                                 FreeUndoBuffers (ped, TRUE);
1300
1301                                                 if (ped->Patt == NULL)
1302                                                 {
1303                                                         static LONG tags[] = { GA_Disabled, TRUE, TAG_DONE };
1304
1305                                                         lefttrack = topline = ped->DisplayTracks = ped->DisplayLines = 0;
1306                                                         redraw_all = TRUE;
1307
1308                                                         DoMethod ((Object *)g, OM_UPDATE, tags, ((struct opUpdate *)msg)->opu_GInfo, 0);
1309                                                 }
1310                                                 else
1311                                                 {
1312                                                         /* Recalculate pattern dimensions */
1313                                                         if (CalcDisplaySize (ped, g, ((struct opSet *)msg)->ops_GInfo))
1314                                                         {
1315                                                                 NotifySliders (ped, g, ((struct opSet *)msg)->ops_GInfo, 0);
1316                                                                 redraw_all = TRUE;
1317                                                         }
1318
1319                                                         /* Force cursor inside pattern */
1320
1321                                                         if (line >= ped->Patt->Lines)
1322                                                         {
1323                                                                 line = ped->Patt->Lines - 1;
1324                                                                 move_cursor = TRUE;
1325                                                         }
1326
1327                                                         if (track >= ped->Patt->Tracks)
1328                                                         {
1329                                                                 track = ped->Patt->Tracks - 1;
1330                                                                 move_cursor = TRUE;
1331                                                         }
1332
1333                                                         if (lefttrack + ped->DisplayTracks > ped->Patt->Tracks)
1334                                                         {
1335                                                                 lefttrack = ped->Patt->Tracks - ped->DisplayTracks;
1336                                                                 scroll_pattern = TRUE;
1337                                                         }
1338
1339                                                         if (topline + ped->DisplayLines > ped->Patt->Lines)
1340                                                         {
1341                                                                 topline = ped->Patt->Lines - ped->DisplayLines;
1342                                                                 scroll_pattern = TRUE;
1343                                                         }
1344
1345                                                         if (g->Flags & GFLG_DISABLED)
1346                                                         {
1347                                                                 static ULONG tags[] = { GA_Disabled, FALSE, TAG_DONE };
1348                                                                 DoMethod ((Object *)g, OM_UPDATE, tags, ((struct opUpdate *)msg)->opu_GInfo, 0);
1349                                                         }
1350                                                 }
1351                                                 break;
1352
1353                                         case PEA_CurrentInst:
1354                                                 ped->CurrentInst = ti->ti_Data;
1355                                                 break;
1356
1357                                         case PEA_MaxUndoLevels:
1358                                                 ped->MaxUndoLevels = ti->ti_Data;
1359                                                 FreeUndoBuffers (ped, FALSE);
1360                                                 break;
1361
1362                                         case PEA_MaxUndoMem:
1363                                                 ped->MaxUndoMem = ti->ti_Data;
1364
1365                                                 /* Unlimited undo memory */
1366                                                 if (ped->MaxUndoMem == 0) ped->MaxUndoMem = ~0;
1367
1368                                                 FreeUndoBuffers (ped, FALSE);
1369
1370                                                 break;
1371
1372                                         case PEA_AdvanceCurs:
1373                                                 ped->AdvanceLines = ti->ti_Data & 0xFFFF;
1374                                                 ped->AdvanceTracks = ti->ti_Data >> 16;
1375                                                 break;
1376
1377                                         case PEA_CursWrap:
1378                                                 ped->Flags = (ped->Flags & (PEF_HWRAP | PEF_VWRAP)) | (ti->ti_Data & (PEF_HWRAP | PEF_VWRAP));
1379                                                 break;
1380
1381                                         case PEA_BGPen:
1382                                                 if (ped->BGPen != ti->ti_Data)
1383                                                 {
1384                                                         ped->BGPen = ti->ti_Data;
1385                                                         redraw_all = TRUE;
1386                                                 }
1387                                                 break;
1388
1389                                         case PEA_TextPen:
1390                                                 if (ped->TextPen != ti->ti_Data)
1391                                                 {
1392                                                         ped->TextPen = ti->ti_Data;
1393                                                         redraw_all = TRUE;
1394                                                 }
1395                                                 break;
1396
1397                                         case PEA_LinesPen:
1398                                                 if (ped->LinesPen != ti->ti_Data)
1399                                                 {
1400                                                         ped->LinesPen = ti->ti_Data;
1401                                                         redraw_all = TRUE;
1402                                                 }
1403                                                 break;
1404
1405                                         case PEA_TinyLinesPen:
1406                                                 if (ped->TinyLinesPen != ti->ti_Data)
1407                                                 {
1408                                                         ped->TinyLinesPen = ti->ti_Data;
1409                                                         redraw_all = TRUE;
1410                                                 }
1411                                                 break;
1412
1413                                         default:
1414                                                 /* This little optimization avoids forwarding the
1415                                                  * OM_SET method to our superclass then there are
1416                                                  * no unknown tags.
1417                                                  */
1418                                                 do_super_method = TRUE;
1419                                                 break;
1420                                 }
1421
1422                         }       /* End while (NextTagItem()) */
1423
1424
1425                         if (!move_cursor)
1426                                 move_cursor = ((line != ped->Line) ||
1427                                         (track != ped->Track) ||
1428                                         (column != ped->Column));
1429
1430                         if (do_super_method)
1431                                 result = DoSuperMethodA (cl, (Object *)g, msg);
1432                         else
1433                                 result = TRUE;
1434
1435                         if (redraw_all || redraw_pattern || scroll_pattern || move_cursor || change_note)
1436                         {
1437                                 WORD scrolled = 0;
1438
1439                                 if (rp = ObtainGIRPort (((struct opSet *)msg)->ops_GInfo))
1440                                 {
1441                                         if (redraw_all || redraw_pattern)
1442                                         {
1443                                                 ped->LeftTrack  = lefttrack;
1444                                                 ped->TopLine    = topline;
1445                                                 ped->Line               = line;
1446                                                 ped->Track              = track;
1447                                                 ped->Column             = column;
1448
1449                                                 DoMethod ((Object *)g, GM_RENDER, ((struct opSet *)msg)->ops_GInfo, rp,
1450                                                         (redraw_all ? GREDRAW_REDRAW : GREDRAW_UPDATE));
1451                                                 scrolled = SCROLLED_VERT | SCROLLED_HORIZ;
1452                                         }
1453                                         else if (scroll_pattern)
1454                                         {
1455                                                 ped->Line       = line;
1456                                                 ped->Track      = track;
1457                                                 scrolled = ScrollPattern (rp, ped, g, lefttrack, topline);
1458                                         }
1459                                         else if (move_cursor || change_note)
1460                                         {
1461                                                 ped->Line       = line;
1462                                                 ped->Track      = track;
1463                                                 ped->Column     = column;
1464
1465                                                 if (change_note)
1466                                                 {
1467                                                         EraseCursor (rp, ped);
1468                                                         DrawNote (rp, ped);
1469                                                 }
1470
1471                                                 scrolled |= DrawCursor (rp, ped, g);
1472                                         }
1473
1474                                         ReleaseGIRPort (rp);
1475                                 }
1476
1477                                 /* TODO: avoid sending back updates to the sliders */
1478
1479                                 if (scrolled & SCROLLED_VERT)
1480                                         NotifyVSlider (ped, g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
1481                                 if (scrolled & SCROLLED_HORIZ)
1482                                         NotifyHSlider (ped, g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
1483
1484                                 if (scrolled || move_cursor)
1485                                         NotifyCursor (ped, g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
1486                         }
1487
1488                         break;
1489                 }
1490
1491
1492                 case OM_GET:
1493
1494                         ped = INST_DATA (cl, g);
1495                         result = TRUE;
1496
1497                         switch (((struct opGet *) msg)->opg_AttrID)
1498                         {
1499                                 case PEA_CursTrack:
1500                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Track;
1501                                         break;
1502
1503                                 case PEA_CursColumn:
1504                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Column;
1505                                         break;
1506
1507                                 case PEA_CursLine:
1508                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Line;
1509                                         break;
1510
1511                                 case PEA_LeftTrack:
1512                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->LeftTrack;
1513                                         break;
1514
1515                                 case PEA_TopLine:
1516                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->TopLine;
1517                                         break;
1518
1519                                 case PEA_Changes:
1520                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Changes;
1521                                         break;
1522
1523                                 case PEA_MarkRegion:
1524                                 {
1525                                         struct Rectangle *region = (struct Rectangle *) (((struct opGet *) msg)->opg_Storage);
1526
1527                                         region->MinX = min (ped->RangeStartTrack, ped->RangeEndTrack);
1528                                         region->MaxX = max (ped->RangeStartTrack, ped->RangeEndTrack);
1529                                         region->MinY = min (ped->RangeStartLine, ped->RangeEndLine);
1530                                         region->MaxY = max (ped->RangeStartLine, ped->RangeEndLine);
1531                                         break;
1532                                 }
1533
1534                                 case PEA_Flags:
1535                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Flags;
1536                                         break;
1537
1538                                 case PEA_DisplayTracks:
1539                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->DisplayTracks;
1540                                         break;
1541
1542                                 case PEA_DisplayLines:
1543                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->DisplayLines;
1544                                         break;
1545
1546                                 case PEA_Pattern:
1547                                         *(((struct opGet *) msg)->opg_Storage) = (ULONG) ped->Patt;
1548                                         break;
1549
1550                                 default:
1551                                         result = DoSuperMethodA (cl, (Object *)g, msg);
1552                                         break;
1553                         }
1554
1555                         break;
1556
1557
1558                 case OM_NEW:
1559
1560                         if (result = DoSuperMethodA (cl, (Object *)g, msg))
1561                         {
1562                                 ULONG tmp;
1563
1564                                 g = (struct ExtGadget *) result;
1565
1566                                 ped = INST_DATA (cl, g);        /* Get pointer to instance data */
1567                                 memset (ped, 0, sizeof (struct PattEditData));
1568
1569                                 /* We are going to use ScrollRaster() in this gadget... */
1570
1571                                 if (g->Flags & GFLG_EXTENDED)
1572                                         g->MoreFlags |= GMORE_SCROLLRASTER;
1573
1574                                 g->Flags |= GFLG_TABCYCLE | GFLG_RELSPECIAL;
1575
1576                                 /* Initialize our lists */
1577                                 NEWLIST ((struct List *)&ped->UndoList);
1578                                 NEWLIST ((struct List *)&ped->RedoList);
1579
1580
1581                                 /* Open the timer.device */
1582 /*
1583                                 NEWLIST (&ped->TimerPort.mp_MsgList);
1584                                 ped->TimerPort.mp_Flags = PA_SOFTINT;
1585                                 ped->TimerPort.mp_SoftInt = &ped->TimerInt;
1586                                 ped->TimerInt.is_Node.ln_Type = NT_INTERRUPT;
1587                                 ped->TimerInt.is_Data = g;
1588                                 ped->TimerInt.is_Code = (void (*)())TimerIntServer;
1589                                 ped->TimerIO.tr_node.io_Message.mn_ReplyPort = &ped->TimerPort;
1590
1591                                 if (OpenDevice (TIMERNAME, UNIT_VBLANK, (struct IORequest *)&ped->TimerIO, 0))
1592                                 {
1593                                         CoerceMethod (cl, g, OM_DISPOSE);
1594                                         result = NULL;
1595                                         break;
1596                                 }
1597 */
1598
1599                                 /* Initialize attributes */
1600
1601                                 ped->Patt                       = (struct Pattern *)    GetTagData (PEA_Pattern,        NULL, ((struct opSet *)msg)->ops_AttrList);
1602                                 ped->CurrentInst        = GetTagData (PEA_CurrentInst,  NULL, ((struct opSet *)msg)->ops_AttrList);
1603                                 ped->EditorFont         = (struct TextFont *)   GetTagData (PEA_TextFont,       (ULONG)GfxBase->DefaultFont, ((struct opSet *)msg)->ops_AttrList);
1604                                 ped->BGPen                      = GetTagData (PEA_BGPen,                0, ((struct opSet *)msg)->ops_AttrList);
1605                                 ped->TextPen            = GetTagData (PEA_TextPen,              1, ((struct opSet *)msg)->ops_AttrList);
1606                                 ped->LinesPen           = GetTagData (PEA_LinesPen,             2, ((struct opSet *)msg)->ops_AttrList);
1607                                 ped->TinyLinesPen       = GetTagData (PEA_TinyLinesPen, 2, ((struct opSet *)msg)->ops_AttrList);
1608                                 ped->MaxUndoLevels      = GetTagData (PEA_MaxUndoLevels,        32, ((struct opSet *)msg)->ops_AttrList);
1609                                 ped->MaxUndoMem         = GetTagData (PEA_MaxUndoMem,           8192, ((struct opSet *)msg)->ops_AttrList);
1610                                 ped->Flags                      = GetTagData (PEA_Flags,                        0, ((struct opSet *)msg)->ops_AttrList);
1611                                 ped->Flags                      |= GetTagData (PEA_CursWrap,            ped->Flags, ((struct opSet *)msg)->ops_AttrList) & (PEF_HWRAP | PEF_VWRAP);
1612
1613                                 tmp = GetTagData (PEA_AdvanceCurs, 1, ((struct opSet *)msg)->ops_AttrList);
1614                                 ped->AdvanceTracks      = tmp << 16;
1615                                 ped->AdvanceLines       = tmp & 0xFFFF;
1616
1617                                 ped->Line       = GetTagData (PEA_CursLine,     0, ((struct opSet *)msg)->ops_AttrList);
1618                                 ped->Track      = GetTagData (PEA_CursTrack,    0, ((struct opSet *)msg)->ops_AttrList);
1619                                 ped->Column     = GetTagData (PEA_CursColumn,   0, ((struct opSet *)msg)->ops_AttrList);
1620
1621                                 ped->FontXSize          = ped->EditorFont->tf_XSize;
1622                                 ped->FontYSize          = ped->EditorFont->tf_YSize;
1623                                 ped->TrackChars         = 10;
1624                                 ped->TrackWidth         = ped->FontXSize * ped->TrackChars;
1625
1626                                 /* Select Note2ASCII func */
1627                                 if (ped->Flags & PEF_BLANKZERO)
1628                                         ped->Note2ASCIIFunc = Note2ASCIIBlank0;
1629                                 else
1630                                         ped->Note2ASCIIFunc = Note2ASCII;
1631
1632                                 /* Unlimited undo memory */
1633                                 if (ped->MaxUndoMem == 0) ped->MaxUndoMem = ~0;
1634
1635                                 if (ped->Patt == NULL)
1636                                 {
1637                                         static LONG tags[] = { GA_Disabled, TRUE, TAG_DONE };
1638                                         DoMethod ((Object *)g, OM_UPDATE, tags, NULL, 0);
1639                                 }
1640                         }
1641                         break;
1642
1643
1644                 case OM_DISPOSE:
1645                 {
1646                         ped = INST_DATA (cl, g);
1647
1648                         FreeUndoBuffers (ped, TRUE);
1649 //                      CloseDevice ((struct IORequest *)&ped->TimerIO);
1650
1651                         /* NOTE: I'm falling through here! */
1652                 }
1653
1654
1655                 default:
1656
1657                         /* Unsupported method: let our superclass's
1658                          * dispatcher take a look at it.
1659                          */
1660                         result = DoSuperMethodA (cl, (Object *)g, msg);
1661                         break;
1662         }
1663
1664         return (result);
1665 }
1666
1667
1668
1669 static void SaveUndo (struct PattEditData *ped)
1670 {
1671         struct UndoNode *undo;
1672
1673         ped->Changes++;
1674
1675         /* Is undo feature disabled ? */
1676         if (!ped->MaxUndoLevels) return;
1677
1678         /* Empty redo list */
1679         while (undo = (struct UndoNode *) REMHEAD((struct List *)&ped->RedoList))
1680                 FreeMem (undo, sizeof (struct UndoNode));
1681
1682         FreeUndoBuffers (ped, FALSE);
1683
1684         while (ped->UndoCount >= ped->MaxUndoLevels || ped->UndoMem >= ped->MaxUndoMem)
1685                 if (undo = (struct UndoNode *) REMTAIL ((struct List *)&ped->UndoList))
1686                 {
1687                         FreeMem (undo, sizeof (struct UndoNode));
1688                         ped->UndoCount--;
1689                         ped->UndoMem -= sizeof (struct UndoNode);
1690                 }
1691
1692         /* Allocate a new undo buffer and save current note */
1693         if (undo = AllocMem (sizeof (struct UndoNode), MEMF_ANY))
1694         {
1695                 undo->Track     = ped->Track;
1696                 undo->Line      = ped->Line;
1697
1698                 memcpy (&undo->OldNote, &ped->Patt->Notes[ped->Track][ped->Line],
1699                         sizeof (struct Note));
1700
1701                 ADDHEAD ((struct List *)&ped->UndoList, (struct Node *)undo);
1702                 ped->UndoCount++;
1703                 ped->UndoMem += sizeof (struct UndoNode);
1704         }
1705 }
1706
1707
1708
1709 static BOOL UndoChange (struct PattEditData *ped)
1710 {
1711         struct UndoNode *undo;
1712         struct Note             *note;
1713         struct Note              tmp_note;
1714
1715         if (undo = (struct UndoNode *) REMHEAD ((struct List *) &ped->UndoList))
1716         {
1717                 ped->UndoCount--;
1718                 ped->UndoMem -= sizeof (struct UndoNode);
1719                 ped->Changes--;
1720
1721                 ped->Track      = undo->Track;
1722                 ped->Line       = undo->Line;
1723
1724                 note = &ped->Patt->Notes[ped->Track][ped->Line];
1725
1726                 /* Swap undo buffer with note */
1727                 memcpy (&tmp_note, &undo->OldNote, sizeof (struct Note));
1728                 memcpy (&undo->OldNote, note, sizeof (struct Note));
1729                 memcpy (note, &tmp_note, sizeof (struct Note));
1730
1731
1732                 /* Move this node to the redo buffer */
1733                 ADDHEAD ((struct List *)&ped->RedoList, (struct Node *)undo);
1734
1735                 return TRUE;
1736         }
1737
1738         return FALSE;
1739 }
1740
1741
1742
1743 static BOOL RedoChange (struct PattEditData *ped)
1744 {
1745         struct UndoNode *undo;
1746         struct Note     *note;
1747         struct Note      tmp_note;
1748
1749         if (undo = (struct UndoNode *) REMHEAD ((struct List *) &ped->RedoList))
1750         {
1751                 ped->Track      = undo->Track;
1752                 ped->Line       = undo->Line;
1753
1754                 note = &ped->Patt->Notes[ped->Track][ped->Line];
1755
1756                 /* Swap undo buffer and note */
1757                 memcpy (&tmp_note, &undo->OldNote, sizeof (struct Note));
1758                 memcpy (&undo->OldNote, note, sizeof (struct Note));
1759                 memcpy (note, &tmp_note, sizeof (struct Note));
1760
1761                 /* Move this node to the undo buffer */
1762                 ADDHEAD ((struct List *)&ped->UndoList, (struct Node *)undo);
1763
1764                 ped->UndoCount++;
1765                 ped->Changes++;
1766                 ped->UndoMem += sizeof (struct UndoNode);
1767
1768                 return TRUE;
1769         }
1770
1771         return FALSE;
1772 }
1773
1774
1775
1776 static void FreeUndoBuffers (struct PattEditData *ped, BOOL freeall)
1777
1778 /* If <freeall> is TRUE, this routine will free all the undo buffers.
1779  * Otherwhise, it will check for undo overflow and free enough nodes
1780  * to keep the undo buffers inside the memory size and nodes count
1781  * limits.
1782  */
1783 {
1784         struct UndoNode *undo;
1785
1786         if (!freeall)
1787         {
1788                 while (ped->UndoCount >= ped->MaxUndoLevels || ped->UndoMem >= ped->MaxUndoMem)
1789                         if (undo = (struct UndoNode *) RemTail ((struct List *)&ped->UndoList))
1790                         {
1791                                 FreeMem (undo, sizeof (struct UndoNode));
1792                                 ped->UndoCount--;
1793                                 ped->UndoMem -= sizeof (struct UndoNode);
1794                         }
1795
1796                 return;
1797         }
1798
1799         /* Free everything */
1800
1801         while (undo = (struct UndoNode *) REMHEAD ((struct List *)&ped->UndoList))
1802                 FreeMem (undo, sizeof (struct UndoNode));
1803
1804         while (undo = (struct UndoNode *) REMHEAD ((struct List *)&ped->RedoList))
1805                 FreeMem (undo, sizeof (struct UndoNode));
1806
1807         ped->UndoCount = 0; ped->UndoMem = 0;
1808 }
1809
1810
1811
1812 static void NotifyCursor (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
1813 {
1814         static LONG tags[] =
1815         {
1816                 PEA_CursLine,           0,
1817                 PEA_CursTrack,  0,
1818                 PEA_CursColumn, 0,
1819                 GA_ID,                          0,
1820                 TAG_DONE
1821         };
1822
1823         /* Always send notification if the gadget has the GACT_IMMEDIATE
1824          * flag set.  If it isn't, the editor will report its cursor
1825          * position only on last cursor update.
1826          */
1827         if ((g->Activation & GACT_IMMEDIATE) || !(flags & OPUF_INTERIM))
1828         {
1829                 tags[1] = ped->Line;
1830                 tags[3] = ped->Track;
1831                 tags[5] = ped->Column;
1832                 tags[7] = g->GadgetID;
1833
1834                 DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
1835         }
1836 }
1837
1838
1839
1840 static void NotifyVSlider (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
1841 {
1842         static LONG tags[] =
1843         {
1844                 PEA_TopLine,                    0,
1845 //              PEA_DisplayLines,               0,
1846                 GA_ID,                                  0,
1847                 TAG_DONE
1848         };
1849
1850
1851         /* Optimized slider update; only broadcast updates if one of
1852          * these conditions is satisfied:
1853          *
1854          * - Final update (keyup or selectup),
1855          * - SliderCounter reached,
1856          * - Reached top/bottom of the pattern.
1857          *
1858          * In addition, notifications are not sent when the we receive OM_UPDATE messages
1859          * from the sliders
1860          */
1861         if ((!(flags & OPUF_INTERIM)) || (ped->SliderCounter == 0) || (ped->TopLine == 0)
1862                 || (ped->TopLine + ped->DisplayLines >= ped->Patt->Lines))
1863         {
1864                 ped->SliderCounter = 4;
1865
1866                 tags[1] = ped->TopLine;
1867 //              tags[3] = ped->DisplayLines;
1868                 tags[3] = g->GadgetID;
1869
1870                 DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
1871         }
1872
1873         ped->SliderCounter--;
1874 }
1875
1876
1877
1878 static void NotifyHSlider (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
1879 {
1880         static LONG tags[] =
1881         {
1882                 PEA_LeftTrack,          0,
1883 //              PEA_DisplayTracks,      0,
1884                 GA_ID,                          0,
1885                 TAG_DONE
1886         };
1887
1888
1889         /* Optimized slider update; only send updates if one of
1890          * these conditions is satisfied:
1891          *
1892          * - Final update (keyup or selectup),
1893          * - SliderCounter reached,
1894          * - Reached left/right of pattern.
1895          */
1896         if ((!(flags & OPUF_INTERIM)) || (ped->SliderCounter == 0) || (ped->LeftTrack == 0)
1897                 || (ped->LeftTrack + ped->DisplayTracks >= ped->Patt->Tracks))
1898         {
1899                 ped->SliderCounter = 3;
1900
1901                 tags[1] = ped->LeftTrack;
1902 //              tags[3] = ped->DisplayTracks;
1903                 tags[3] = g->GadgetID;
1904
1905                 DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
1906         }
1907
1908         ped->SliderCounter--;
1909 }
1910
1911
1912
1913 static void NotifySliders (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
1914 {
1915         static LONG tags[] =
1916         {
1917                 PEA_LeftTrack,          0,
1918                 PEA_DisplayTracks,      0,
1919                 PEA_TopLine,            0,
1920                 PEA_DisplayLines,       0,
1921                 GA_ID,                          0,
1922                 TAG_DONE
1923         };
1924
1925         tags[1] = ped->LeftTrack;
1926         tags[3] = ped->DisplayTracks;
1927         tags[5] = ped->TopLine;
1928         tags[7] = ped->DisplayLines;
1929         tags[9] = g->GadgetID;
1930
1931         DoMethod ((Object *)g, OM_NOTIFY, tags, gi, flags);
1932 }
1933
1934
1935
1936
1937
1938 static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect)
1939
1940 /* This function gets the actual IBox where a gadget exists
1941  * in a window.  The special cases it handles are all the REL#?
1942  * (relative positioning flags).
1943  *
1944  * The function takes a struct GadgetInfo pointer, a struct Gadget
1945  * pointer, and a struct IBox pointer.  It uses the window and
1946  * gadget to fill in the IBox.
1947  */
1948 {
1949         rect->Left = g->LeftEdge;
1950         if (g->Flags & GFLG_RELRIGHT) rect->Left += ginfo->gi_Domain.Width - 1;
1951
1952         rect->Top = g->TopEdge;
1953         if (g->Flags & GFLG_RELBOTTOM) rect->Top += ginfo->gi_Domain.Height - 1;
1954
1955         rect->Width = g->Width;
1956         if (g->Flags & GFLG_RELWIDTH) rect->Width += ginfo->gi_Domain.Width;
1957
1958         rect->Height = g->Height;
1959         if (g->Flags & GFLG_RELHEIGHT) rect->Height += ginfo->gi_Domain.Height;
1960 }
1961
1962
1963
1964 static BOOL CalcDisplaySize (struct PattEditData *ped, struct ExtGadget *g, struct GadgetInfo *ginfo)
1965
1966 /* Calculate maximum number of tracks and lines that will fit in the gadget
1967  * size.  Returns TRUE if something has changed.
1968  *
1969  * GBounds are the bounds of the whole gadget and
1970  * TBounds are the bounds of the portion where the cursor lives.
1971  *
1972  * +--Window--------------------+
1973  * |                            |
1974  * | +--GBounds----------------+|
1975  * | |   +--TBounds-----------+||
1976  * | |001|C#2 1 000|B#2 2 C20 |||
1977  * | |002|--- 0 000|A-2 2 C30 |||
1978  * | |003|--- 0 000|C-2 2 C10 |||
1979  * | |004|--- 0 000|C#2 3 000 |||
1980  * | |   +--------------------+||
1981  * | +-------------------------+|
1982  * +----------------------------+
1983  */
1984 {
1985         UWORD   old_displaytracks = ped->DisplayTracks,
1986                         old_displaylines = ped->DisplayLines,
1987                         numcol_width = ped->FontXSize * 4;
1988
1989         GetGadgetBox (ginfo, g, &ped->GBounds);
1990
1991         /* Setup DisplayTracks and DisplayLines */
1992
1993         if (ped->Patt)
1994         {
1995                 ped->DisplayTracks      = min ((ped->GBounds.Width - numcol_width) / ped->TrackWidth, ped->Patt->Tracks);
1996                 ped->DisplayLines       = min (ped->GBounds.Height / ped->FontYSize, ped->Patt->Lines);
1997
1998                 if (ped->TopLine + ped->DisplayLines > ped->Patt->Lines)
1999                         ped->TopLine = max (0, ped->Patt->Lines - ped->DisplayLines);
2000
2001                 if (ped->LeftTrack + ped->DisplayTracks > ped->Patt->Tracks)
2002                         ped->LeftTrack = max (0, ped->Patt->Tracks - ped->DisplayTracks);
2003         }
2004
2005
2006         /* Setup Text Bounds */
2007
2008         ped->TBounds.Top        = ped->GBounds.Top;
2009         ped->TBounds.Left       = ped->GBounds.Left + numcol_width;
2010         ped->TBounds.Width      = ped->DisplayTracks * ped->TrackWidth;
2011         ped->TBounds.Height     = ped->DisplayLines * ped->FontYSize;
2012
2013         return ((BOOL)((old_displaytracks != ped->DisplayTracks) || (old_displaylines != ped->DisplayLines)));
2014 }
2015
2016
2017
2018 static BOOL MoveCursor (struct PattEditData *ped, WORD x, WORD y)
2019
2020 /* Moves the cursor to a given xy position.  Checks whether the cursor has
2021  * really moved and returns FALSE if there is no need to update its imagery.
2022  */
2023 {
2024         WORD tmp;
2025         BOOL moved = FALSE, maxtrack = FALSE;
2026
2027
2028         /* X Position (Track) */
2029
2030         if (x < 0)
2031                 tmp = ped->LeftTrack - 1;
2032         else
2033                 tmp = (x / ped->TrackWidth) + ped->LeftTrack;
2034
2035         if (tmp < 0) tmp = 0;
2036
2037         if (tmp >= ped->Patt->Tracks)
2038         {
2039                 tmp = ped->Patt->Tracks - 1;
2040                 maxtrack = TRUE;
2041         }
2042
2043         if (ped->Track != tmp)
2044         {
2045                 moved = TRUE;
2046                 ped->Track = tmp;
2047         }
2048
2049
2050         /* X Position (Column) */
2051
2052         if (maxtrack)
2053                 tmp = ped->TrackChars - 1;
2054         else
2055                 tmp = (x / ped->FontXSize) % ped->TrackChars;
2056
2057
2058         if (tmp < 3)            tmp = COL_NOTE;
2059         else if (tmp == 3)      tmp = COL_INSTH;
2060         else if (tmp == 4)      tmp = COL_INSTL;
2061         else if (tmp < 7)       tmp = COL_EFF;
2062         else if (tmp == 7)      tmp = COL_VALH;
2063         else                            tmp = COL_VALL;
2064
2065
2066         if (ped->Column != tmp)
2067         {
2068                 moved = TRUE;
2069                 ped->Column = tmp;
2070         }
2071
2072
2073         /* Y Position */
2074
2075         tmp = (y / ped->FontYSize) + ped->TopLine;
2076
2077         if (tmp < 0) tmp = 0;
2078
2079         if (tmp >= ped->Patt->Lines)
2080                 tmp = ped->Patt->Lines - 1;
2081
2082         if (ped->Line != tmp)
2083         {
2084                 moved = TRUE;
2085                 ped->Line = tmp;
2086         }
2087
2088         return moved;
2089 }
2090
2091
2092
2093 static void EraseCursor (struct RastPort *rp, struct PattEditData *ped)
2094 {
2095         SetDrMd (rp,COMPLEMENT);
2096
2097         switch (ped->CursState)
2098         {
2099                 case IDS_DISABLED:
2100                         SetAfPt (rp, GhostPattern, 1);
2101                         RectFill (rp, ped->CursRect.MinX, ped->CursRect.MinY,
2102                                 ped->CursRect.MaxX, ped->CursRect.MaxY);
2103                         SetAfPt (rp, NULL, 0);  /* Reset Area Pattern   */
2104                         break;
2105
2106                 case IDS_BUSY:
2107                         SetAfPt (rp, MarkPattern, 1);
2108                         RectFill (rp, ped->CursRect.MinX, ped->CursRect.MinY,
2109                                 ped->CursRect.MaxX, ped->CursRect.MaxY);
2110                         SetAfPt (rp, NULL, 0);  /* Reset Area Pattern   */
2111                         break;
2112
2113                 case IDS_SELECTED:
2114                         RectFill (rp, ped->CursRect.MinX, ped->CursRect.MinY,
2115                                 ped->CursRect.MaxX, ped->CursRect.MaxY);
2116
2117                         if (ped->CursLinePos)
2118                         {
2119                                 /* Erase cursor line */
2120
2121                                 /* Question: Is RectFill faster than Move()+Draw()? Hmmm... */
2122                                 RectFill (rp, ped->GBounds.Left, ped->CursRect.MaxY,
2123                                         ped->TBounds.Left + ped->TBounds.Width - 1, ped->CursRect.MaxY);
2124                         //      Move (rp, ped->GBounds.Left, ped->CursRect.MaxY);
2125                         //      Draw (rp, ped->TBounds.Left + ped->TBounds.Width - 1, ped->CursRect.MaxY);
2126                                 ped->CursLinePos = 0;
2127                         }
2128                         break;
2129
2130                 default:
2131
2132                         /* When the cursor is not rendered, its state
2133                          * will be IDS_NORMAL, so we won't erase it at all.
2134                          */
2135                         break;
2136         }
2137
2138         ped->CursState = IDS_NORMAL;
2139 }
2140
2141
2142
2143 static UWORD DrawCursor (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g)
2144
2145 /* Draw the cursor image on the editor.  If the cursor goes outside
2146  * of the scrolling region, the view is scrolled to make
2147  * it visible.
2148  *
2149  * RESULT
2150  *  0                            - no scrolling occurred,
2151  *  SCROLLED_VERT                - vertical scroll occurred,
2152  *  SCROLLED_HORIZ               - horizontal scroll occurred,
2153  *  SCROLLED_VERT|SCROLLED_HORIZ - scrolled in both directions.
2154  */
2155
2156 {
2157         /* Cursor offsets for each track column */
2158         static UWORD ColumnOff[COL_COUNT] =  { 0, 3, 4, 6, 7, 8 };
2159
2160         struct Rectangle        NewCurs;
2161         UWORD                           NewState;
2162
2163
2164         /* Do not attemt to draw a cursor if the editor is so small that
2165          * it can't display anything,
2166          */
2167         if (!ped->DisplayTracks || !ped->DisplayLines)
2168                 return 0;
2169
2170         /* Check whether cursor is outside the display bounds and
2171          * scroll pattern to make it visible if required.
2172          */
2173         {
2174                 if (ped->Line < ped->TopLine)
2175                         return ScrollPattern (rp, ped, g, ped->LeftTrack, ped->Line);
2176                 else if (ped->Line >= ped->TopLine + ped->DisplayLines)
2177                         return ScrollPattern (rp, ped, g, ped->LeftTrack, ped->Line - ped->DisplayLines + 1);
2178                 if (ped->Track < ped->LeftTrack)
2179                         return ScrollPattern (rp, ped, g, ped->Track, ped->TopLine);
2180                 else if (ped->Track >= ped->LeftTrack + ped->DisplayTracks)
2181                         return ScrollPattern (rp, ped, g, ped->Track - ped->DisplayTracks + 1, ped->TopLine);
2182         }
2183
2184
2185         /* Calculate new cursor rectangle */
2186
2187         NewCurs.MinX = ped->TBounds.Left +
2188                 (((ped->Track - ped->LeftTrack) * ped->TrackChars) + ColumnOff[ped->Column]) * ped->FontXSize;
2189         NewCurs.MinY = ped->TBounds.Top + (ped->Line - ped->TopLine) * ped->FontYSize;
2190         NewCurs.MaxX = NewCurs.MinX + ped->FontXSize - 1;
2191         NewCurs.MaxY = NewCurs.MinY + ped->FontYSize - 1;
2192
2193
2194         /* Note field is three characters wide */
2195         if (ped->Column == COL_NOTE)
2196                 NewCurs.MaxX += ped->FontXSize * 2;
2197
2198
2199         /* Set AreaPattern to show current cursor state */
2200
2201         if (!(g->Flags & GFLG_SELECTED))
2202         {
2203                 NewState = IDS_DISABLED;
2204
2205                 /* Set this pattern to draw an inactive cursor */
2206                 SetAfPt (rp, GhostPattern, 1);
2207         }
2208         else if (ped->Flags & PEF_MARKING)
2209         {
2210                 NewState = IDS_BUSY;
2211
2212                 /* Set this pattern to draw marking cursor */
2213                 SetAfPt (rp, MarkPattern, 1);
2214         }
2215         else NewState = IDS_SELECTED;
2216
2217         SetDrMd (rp, COMPLEMENT);
2218         /* Draw cursor */
2219         RectFill (rp, NewCurs.MinX, NewCurs.MinY,
2220                 NewCurs.MaxX, NewCurs.MaxY);
2221         SetAfPt (rp, NULL, 0);  /* Reset AreaFill Pattern */
2222
2223         if ((ped->Flags & PEF_DOCURSORRULER) && (NewState == IDS_SELECTED))
2224         {
2225                 if (ped->CursLinePos != NewCurs.MaxY)
2226                 {
2227                         /* Draw horizontal line */
2228
2229                         /* Question: Is RectFill faster than Move()+Draw()? Hmmm... */
2230                         RectFill (rp, ped->GBounds.Left, NewCurs.MaxY,
2231                                 ped->TBounds.Left + ped->TBounds.Width - 1, NewCurs.MaxY);
2232                 //      Move (rp, ped->GBounds.Left, NewCurs.MaxY);
2233                 //      Draw (rp, ped->TBounds.Left + ped->TBounds.Width - 1, NewCurs.MaxY);
2234                 }
2235                 /* Cause EraseCursor() to leave the old line alone */
2236                 else ped->CursLinePos = 0;
2237         }
2238
2239
2240         /* Erase old cursor */
2241         EraseCursor (rp, ped);
2242
2243         if (ped->Flags & PEF_MARKING)
2244                 /* Update the range */
2245                 DrawRange (rp, ped);
2246
2247
2248         /* Store new position and state */
2249         ped->CursRect   = NewCurs;
2250         ped->CursState  = NewState;
2251
2252         if ((ped->Flags & PEF_DOCURSORRULER) && (NewState == IDS_SELECTED))
2253                 ped->CursLinePos = NewCurs.MaxY;
2254
2255         return FALSE;
2256 }
2257
2258
2259
2260 static void DrawRange (struct RastPort *rp, struct PattEditData *ped)
2261 {
2262         UWORD tmin, tmax, lmin, lmax;
2263         struct Rectangle newrange;
2264
2265         ped->RangeEndTrack      = ped->Track;
2266
2267         if (!(ped->Flags & PEF_MARKFULLTRACKS))
2268                 ped->RangeEndLine = ped->Line;
2269
2270         if (ped->RangeStartTrack < ped->RangeEndTrack)
2271         {
2272                 tmin = ped->RangeStartTrack;
2273                 tmax = ped->RangeEndTrack;
2274         }
2275         else
2276         {
2277                 tmin = ped->RangeEndTrack;
2278                 tmax = ped->RangeStartTrack;
2279         }
2280
2281         if (ped->RangeStartLine < ped->RangeEndLine)
2282         {
2283                 lmin = ped->RangeStartLine;
2284                 lmax = ped->RangeEndLine;
2285         }
2286         else
2287         {
2288                 lmin = ped->RangeEndLine;
2289                 lmax = ped->RangeStartLine;
2290         }
2291
2292         /* Limit to visible portion of range rectangle */
2293
2294         if (tmin < ped->LeftTrack)
2295                 tmin = ped->LeftTrack;
2296         if (tmin >= ped->LeftTrack + ped->DisplayTracks)
2297                 tmin = ped->LeftTrack + ped->DisplayTracks - 1;
2298
2299         if (tmax < ped->LeftTrack)
2300                 tmax = ped->LeftTrack;
2301         if (tmax >= ped->LeftTrack + ped->DisplayTracks)
2302                 tmax = ped->LeftTrack + ped->DisplayTracks - 1;
2303
2304         if (lmin < ped->TopLine)
2305                 lmin = ped->TopLine;
2306         if (lmin >= ped->TopLine + ped->DisplayLines)
2307                 lmin = ped->TopLine + ped->DisplayLines - 1;
2308
2309         if (lmax < ped->TopLine)
2310                 lmax = ped->TopLine;
2311         if (lmax >= ped->TopLine + ped->DisplayLines)
2312                 lmax = ped->TopLine + ped->DisplayLines - 1;
2313
2314
2315         newrange.MinX = ped->TBounds.Left + (tmin - ped->LeftTrack) * ped->TrackWidth;
2316         newrange.MinY = ped->TBounds.Top + (lmin - ped->TopLine) * ped->FontYSize;
2317         newrange.MaxX = ped->TBounds.Left + ((tmax - ped->LeftTrack + 1) * ped->TrackWidth) - 1;
2318         newrange.MaxY = ped->TBounds.Top + ((lmax - ped->TopLine + 1) * ped->FontYSize) - 1;
2319
2320 #ifdef OS30_ONLY
2321         SetWriteMask (rp, ped->TextPen | ped->BGPen);
2322 #else
2323         SafeSetWriteMask (rp, ped->TextPen | ped->BGPen);
2324 #endif  /* OS30_ONLY */
2325
2326         SetDrMd (rp, COMPLEMENT);
2327
2328
2329         if (ped->RangeRect.MaxX == 0)
2330         {
2331                 RectFill (rp, newrange.MinX,
2332                         newrange.MinY,
2333                         newrange.MaxX,
2334                         newrange.MaxY);
2335         }
2336         else
2337         {
2338                 /* Incremental range box update.  We only draw changes relative to last
2339                  * ranged box.  As the range box is drawn complementing bitplane 1,
2340                  * complementing it again will erase it, so we do not care if the box
2341                  * has grown or shrinked; we just complemnt delta boxes.
2342                  */
2343
2344                 /*       range box
2345                  *      +---+-----+
2346                  *      |###|     |
2347                  *      |###|     |
2348                  *      |###|   <-+-- unchanged
2349                  *      |###|     |
2350                  *      +---+-----+
2351                  *        ^---------- changed
2352                  */
2353                 if (newrange.MinX != ped->RangeRect.MinX)
2354                         RectFill (rp, min (ped->RangeRect.MinX, newrange.MinX),
2355                                 newrange.MinY,
2356                                 max (ped->RangeRect.MinX, newrange.MinX) - 1,
2357                                 newrange.MaxY);
2358
2359                 /*      +-----+---+
2360                  *      |     |###|
2361                  *      |     |###|
2362                  *      |     |###|
2363                  *      |     |###|
2364                  *      +-----+---+
2365                  */
2366                 if (newrange.MaxX != ped->RangeRect.MaxX)
2367                         RectFill (rp, min (ped->RangeRect.MaxX, newrange.MaxX) + 1,
2368                                 newrange.MinY,
2369                                 max (ped->RangeRect.MaxX, newrange.MaxX),
2370                                 newrange.MaxY);
2371
2372                 /*      +---------+
2373                  *      |#########|
2374                  *      +---------+
2375                  *      |         |
2376                  *      |         |
2377                  *      +---------+
2378                  */
2379                 if (newrange.MinY != ped->RangeRect.MinY)
2380                         RectFill (rp, ped->RangeRect.MinX,
2381                                 min (ped->RangeRect.MinY, newrange.MinY),
2382                                 ped->RangeRect.MaxX,
2383                                 max (ped->RangeRect.MinY, newrange.MinY) - 1);
2384
2385                 /*      +---------+
2386                  *      |         |
2387                  *      |         |
2388                  *      +---------+
2389                  *      |#########|
2390                  *      +---------+
2391                  */
2392                 if (newrange.MaxY != ped->RangeRect.MaxY)
2393                         RectFill (rp, ped->RangeRect.MinX,
2394                                 min (ped->RangeRect.MaxY, newrange.MaxY) + 1,
2395                                 ped->RangeRect.MaxX,
2396                                 max (ped->RangeRect.MaxY, newrange.MaxY));
2397         }
2398
2399         /* Copy new values */
2400         ped->RangeRect = newrange;
2401
2402 #ifdef OS30_ONLY
2403         SetWriteMask (rp, ~0);
2404 #else
2405         SafeSetWriteMask (rp, (UBYTE)~0);
2406 #endif  /* OS30_ONLY */
2407 }
2408
2409
2410
2411 static void ClearRange (struct RastPort *rp, struct PattEditData *ped)
2412 {
2413 #ifdef OS30_ONLY
2414         SetWriteMask (rp, ped->TextPen | ped->BGPen);
2415 #else
2416         SafeSetWriteMask (rp, ped->TextPen | ped->BGPen);
2417 #endif  /* OS30_ONLY */
2418         SetDrMd (rp, COMPLEMENT);
2419
2420         RectFill (rp, ped->RangeRect.MinX, ped->RangeRect.MinY,
2421                 ped->RangeRect.MaxX, ped->RangeRect.MaxY);
2422
2423         memset (&ped->RangeRect, 0, sizeof (ped->RangeRect));
2424
2425 #ifdef OS30_ONLY
2426         SetWriteMask (rp, ~0);
2427 #else
2428         SafeSetWriteMask (rp, (UBYTE)~0);
2429 #endif  /* OS30_ONLY */
2430 }
2431
2432
2433
2434 static void RedrawAll (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g)
2435 {
2436         UWORD   HOffset,
2437                         tmp;
2438         ULONG   i;
2439
2440
2441         /* Erase the whole gadget imagery */
2442
2443         SetAPen (rp, ped->BGPen);
2444         SetDrMd (rp, JAM2);
2445
2446         if (!ped->Patt)
2447         {
2448                 /* Clear everything */
2449
2450                 RectFill (rp, ped->GBounds.Left, ped->GBounds.Top,
2451                         ped->GBounds.Left + ped->GBounds.Width - 1, ped->GBounds.Top + ped->GBounds.Height - 1);
2452                 return;
2453         }
2454
2455         if (rp->BitMap->Depth > 1)
2456         {
2457                 /* Do not clear the bitplanes used by the text because they will
2458                  * be completely redrawn later.
2459                  */
2460 #ifdef OS30_ONLY
2461                 SetWriteMask (rp, ~ped->TextPen);
2462 #else
2463                 SafeSetWriteMask (rp, (UBYTE)~ped->TextPen);
2464 #endif  /* OS30_ONLY */
2465
2466
2467                 /* +------------+
2468                  * |*********   |
2469                  * |*Cleared*   |
2470                  * |*********   |
2471                  * |            |
2472                  * +------------+
2473                  */
2474                 RectFill (rp, ped->GBounds.Left, ped->GBounds.Top,
2475                         ped->TBounds.Left + ped->TBounds.Width - 1, ped->TBounds.Top + ped->TBounds.Height - 1);
2476
2477                 /* Restore the Mask */
2478 #ifdef OS30_ONLY
2479                 SetWriteMask (rp, ~0);
2480 #else
2481                 SafeSetWriteMask (rp, (UBYTE)~0);
2482 #endif  /* OS30_ONLY */
2483         }
2484
2485         /* Now clear the area at the right and bottom side of
2486          * the editing field.
2487          */
2488
2489                 /* +------------+
2490                  * |         ***|
2491                  * |         ***|
2492                  * |         ***|
2493                  * |            |
2494                  * +------------+
2495                  */
2496         if (ped->TBounds.Left + ped->TBounds.Width < ped->GBounds.Left + ped->GBounds.Width)
2497                 RectFill (rp, ped->TBounds.Left + ped->TBounds.Width, ped->GBounds.Top,
2498                         ped->GBounds.Left + ped->GBounds.Width - 1, ped->TBounds.Top + ped->TBounds.Height - 1);
2499
2500                 /* +------------+
2501                  * |            |
2502                  * |            |
2503                  * |            |
2504                  * |************|
2505                  * +------------+
2506                  */
2507         if (ped->TBounds.Top + ped->TBounds.Height < ped->GBounds.Top + ped->GBounds.Height)
2508                 RectFill (rp, ped->GBounds.Left, ped->TBounds.Top + ped->TBounds.Height,
2509                         ped->GBounds.Left + ped->GBounds.Width - 1, ped->GBounds.Top + ped->GBounds.Height - 1);
2510
2511
2512         /* Cursor shouldn't be deleted again since everything has been cleared */
2513         ped->CursState          = IDS_NORMAL;
2514         ped->CursLinePos        = 0;
2515
2516         if (!(ped->Patt)) return;
2517
2518         /* Draw track separator lines */
2519         SetAPen (rp, ped->LinesPen);
2520         DrawTrackSeparators (rp, ped, ped->TrackWidth);
2521
2522         /* Draw tiny vertical separator lines */
2523         if (ped->Flags & PEF_DOTINYLINES)
2524         {
2525                 SetAPen (rp, ped->TinyLinesPen);
2526                 SetDrPt (rp, 0xCCCC);
2527                 HOffset = ped->TBounds.Left - 1;
2528
2529                 for (i = 0; i < ped->DisplayTracks; i++, HOffset += ped->TrackWidth)
2530                 {
2531                         Move (rp, tmp = HOffset + ped->FontXSize * 3, ped->GBounds.Top);
2532                         Draw (rp, tmp, ped->GBounds.Top + ped->GBounds.Height - 1);
2533                         Move (rp, tmp = HOffset + ped->FontXSize * 5, ped->GBounds.Top);
2534                         Draw (rp, tmp, ped->GBounds.Top + ped->GBounds.Height - 1);
2535                         Move (rp, tmp = HOffset + ped->FontXSize * 7, ped->GBounds.Top);
2536                         Draw (rp, tmp, ped->GBounds.Top + ped->GBounds.Height - 1);
2537                 }
2538
2539                 SetDrPt (rp, 0xFFFF);
2540         }
2541
2542         RedrawPattern (rp, ped, g);
2543
2544         DrawTrackNumbers (rp, ped);
2545 }
2546
2547
2548
2549 static void DrawTrackSeparators (struct RastPort *rp, struct PattEditData *ped, UWORD width)
2550 {
2551         UWORD i, HOffset = ped->TBounds.Left - 1;
2552
2553         for (i = 0; i <= ped->DisplayTracks; i++, HOffset += width)
2554         {
2555                 /* Never draw outside gadget bounds */
2556                 if (HOffset >= ped->GBounds.Left + ped->GBounds.Width + 1) break;
2557                 RectFill (rp, HOffset - 1, ped->GBounds.Top, HOffset, ped->GBounds.Top + ped->GBounds.Height - 1);
2558         }
2559 }
2560
2561
2562
2563 static void DrawTrackNumbers (struct RastPort *rp, struct PattEditData *ped)
2564 {
2565         struct TextFont *oldfont  = rp->Font;
2566         UWORD   HOffset = ped->TBounds.Left + ped->FontXSize * 5;
2567         UWORD   i;
2568         UBYTE   ch;
2569
2570
2571 #ifndef OS30_ONLY
2572         if (GfxBase->LibNode.lib_Version < 39)
2573         {
2574                 rp->Mask = ped->LinesPen;
2575                 SetAPen (rp, ped->LinesPen);
2576                 SetBPen (rp, ped->BGPen);
2577                 SetDrMd (rp, JAM2);     /* JAM1 or JAM2... Which one is faster? */
2578         }
2579         else
2580         {
2581 #endif /* !OS30_ONLY */
2582                 SetWrMsk (rp, ped->LinesPen);
2583                 SetABPenDrMd (rp, ped->LinesPen, ped->BGPen, JAM2);
2584 #ifndef OS30_ONLY
2585         }
2586 #endif /* !OS30_ONLY */
2587
2588         SetFont (rp, ped->EditorFont);
2589
2590         for (i = 0; i < ped->DisplayTracks; i++, HOffset += ped->TrackWidth)
2591         {
2592                 Move (rp, HOffset, ped->GBounds.Top + ped->EditorFont->tf_Baseline);
2593                 ch = HexValuesNo0[((i+ped->LeftTrack)>>4) & 0xF];
2594                 Text (rp, &ch, 1);
2595                 Move (rp, HOffset, ped->GBounds.Top + ped->FontXSize + ped->EditorFont->tf_Baseline);
2596                 ch = HexValues[(i+ped->LeftTrack) & 0xF];
2597                 Text (rp, &ch, 1);
2598         }
2599
2600         SetFont (rp, oldfont);
2601 }
2602
2603
2604
2605 static void RedrawPattern (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g)
2606 {
2607         EraseCursor (rp, ped);
2608
2609         /* Redraw all lines */
2610         if (ped->DisplayLines)
2611                 DrawPatternLines (rp, ped, 0, ped->DisplayLines - 1);
2612
2613         if (ped->Flags & PEF_MARKING)
2614                 /* Clear the range rectangle bounds so the next call
2615                  * to DrawCursor() will redraw all the range box.
2616                  */
2617                 memset (&ped->RangeRect, 0, sizeof (ped->RangeRect));
2618
2619         DrawCursor (rp, ped, g);
2620 }
2621
2622
2623
2624 static void DrawPatternLines (struct RastPort *rp, struct PattEditData *ped, UWORD min, UWORD max)
2625
2626 /* Draws Pattern display lines.  Only lines from <min> to <max> are drawn.
2627  * To redraw all the display, pass min = 0 and max = ped->DisplayLines - 1.
2628  */
2629 {
2630         struct Pattern  *patt;
2631         struct TextFont *oldfont = rp->Font;
2632         UBYTE                   *l;
2633         UWORD                    i, j;
2634         UWORD                    VOffset = ped->TBounds.Top + ped->EditorFont->tf_Baseline
2635                                                 + min * ped->FontYSize;
2636
2637         /* Note well: I've put 32 and not MAXTRACKS here because 255 tracks would
2638          * have used too much stack. :-(
2639          */
2640         ALIGNED UBYTE   line[MAXTRACKCHARS * 32 + 4];
2641
2642
2643         if (!(patt = ped->Patt)) return;
2644
2645         SetFont (rp, ped->EditorFont);
2646
2647 #ifndef OS30_ONLY
2648         if (GfxBase->LibNode.lib_Version >= 39)
2649         {
2650 #endif /* !OS30_ONLY */
2651                 SetWriteMask (rp, ped->TextPen | ped->BGPen);
2652                 SetABPenDrMd (rp, ped->TextPen, ped->BGPen, JAM2);
2653 #ifndef OS30_ONLY
2654         }
2655         else
2656         {
2657                 rp->Mask = ped->TextPen | ped->BGPen;
2658                 SetAPen (rp, ped->TextPen);
2659                 SetBPen (rp, ped->BGPen);
2660                 SetDrMd (rp, JAM2);     /* JAM1 or JAM2... Which one is faster? */
2661         }
2662 #endif /* !OS30_ONLY */
2663
2664
2665         for (i = min; i <= max; i++)
2666         {
2667                 l = line;
2668
2669                 /* Write Line Numbers column */
2670
2671                 if (ped->Flags & PEF_HEXMODE)
2672                 {
2673                         *l++ = HexValues[((i+ped->TopLine)>>8) & 0xF];
2674                         *l++ = HexValues[((i+ped->TopLine)>>4) & 0xF];
2675                         *l++ = HexValues[((i+ped->TopLine) & 0xF)];
2676                 }
2677                 else
2678                 {
2679                         *l++ = HexValues[((i+ped->TopLine) / 100) % 10];
2680                         *l++ = HexValues[((i+ped->TopLine) / 10) % 10];
2681                         *l++ = HexValues[((i+ped->TopLine) % 10)];
2682                 }
2683
2684                 *l++ = ' ';
2685
2686                 for (j = 0; j < ped->DisplayTracks; j++, l += ped->TrackChars)
2687                         ped->Note2ASCIIFunc (l, &patt->Notes[j+ped->LeftTrack][i+ped->TopLine]);
2688
2689                 Move (rp, ped->GBounds.Left, VOffset);
2690                 Text (rp, line, ped->DisplayTracks * ped->TrackChars + 4);
2691                 VOffset += ped->FontYSize;
2692         }
2693
2694 #ifdef OS30_ONLY
2695         SetWriteMask (rp, ~0);
2696 #else
2697         SafeSetWriteMask (rp, (UBYTE)~0);
2698 #endif  /* OS30_ONLY */
2699         SetFont (rp, oldfont);
2700 }
2701
2702
2703
2704 static void DrawNote (struct RastPort *rp, struct PattEditData *ped)
2705
2706 /* Draws the note under the cursor. DrawNote() won't draw if the cursor
2707  * is outside of the editing area. Range marking mode is also taken into
2708  * account and the colors of the note will be reversed when the note must
2709  * be hilighted.
2710  */
2711 {
2712         struct Pattern          *patt;
2713         struct TextFont         *oldfont = rp->Font;
2714
2715         UBYTE buf[MAXTRACKCHARS];
2716
2717
2718         if (!(patt = ped->Patt)) return;
2719
2720         /* Is the cursor outside the editing area? */
2721
2722         if ((ped->Line < ped->TopLine) || (ped->Line >= ped->TopLine + ped->DisplayLines) ||
2723                 (ped->Track < ped->LeftTrack) || (ped->Track >= ped->LeftTrack + ped->DisplayTracks))
2724                         return;
2725
2726         SetFont (rp, ped->EditorFont);
2727
2728 #ifndef OS30_ONLY
2729         if (GfxBase->LibNode.lib_Version >= 39)
2730         {
2731 #endif /* OS30_ONLY */
2732                 SetWriteMask (rp, ped->TextPen | ped->BGPen);
2733                 if (ped->Flags & PEF_MARKING)
2734                         SetABPenDrMd (rp, ped->BGPen, ped->TextPen, JAM2);
2735                 else
2736                         SetABPenDrMd (rp, ped->TextPen, ped->BGPen, JAM2);
2737 #ifndef OS30_ONLY
2738         }
2739         else
2740         {
2741                 rp->Mask = ped->TextPen | ped->BGPen;
2742                 if (ped->Flags & PEF_MARKING)
2743                 {
2744                         SetAPen (rp, ped->BGPen);
2745                         SetBPen (rp, ped->TextPen);
2746                 }
2747                 else
2748                 {
2749                         SetAPen (rp, ped->TextPen);
2750                         SetBPen (rp, ped->BGPen);
2751                 }
2752                 SetDrMd (rp, JAM2);
2753         }
2754 #endif /* OS30_ONLY */
2755
2756
2757         ped->Note2ASCIIFunc (buf, &patt->Notes[ped->Track][ped->Line]);
2758
2759         Move (rp, ped->TBounds.Left + ((ped->Track - ped->LeftTrack) * ped->TrackWidth),
2760                 ped->TBounds.Top + ped->EditorFont->tf_Baseline + (ped->Line - ped->TopLine) * ped->FontYSize);
2761         Text (rp, buf, ped->TrackChars);
2762
2763 #ifdef OS30_ONLY
2764         SetWriteMask (rp, (UBYTE)~0);
2765 #else
2766         SafeSetWriteMask (rp, (UBYTE)~0);
2767 #endif  /* OS30_ONLY */
2768
2769         SetFont (rp, oldfont);
2770 }
2771
2772
2773
2774 #ifdef PORTABLE
2775
2776 void ASMCALL Note2ASCIIBlank0 (REG(a1, UBYTE *s), REG(a2, struct Note *note))
2777
2778 /* Fill buffer <s> with the ASCII representation of a
2779  * Note structure.  <s> will be able to hold at
2780  * least <ped->TrackChars> characters.
2781  */
2782 {
2783         *((ULONG *)s) = TextNotes[note->Note];
2784
2785         if (note->Inst)
2786         {
2787                 s[3] = HexValuesNo0[note->Inst>>4];
2788                 s[4] = HexValues[note->Inst & 0x0F];
2789         }
2790         else
2791         {
2792                 s[3] = ' ';
2793                 s[4] = '-';
2794         }
2795
2796         s[5] = ' ';
2797
2798         if (note->EffNum || note->EffVal)
2799         {
2800                 s[6] = HexValues[note->EffNum];
2801                 s[7] = HexValues[note->EffVal>>4];
2802                 s[8] = HexValues[note->EffVal & 0xF];
2803                 s[9] = ' ';
2804         }
2805         else
2806                 *((ULONG *)(s+6)) = ' .  ';
2807 }
2808
2809
2810
2811 void ASMCALL Note2ASCII (REG(a1, UBYTE *s), REG(a2, struct Note *note))
2812
2813 /* Fill buffer <s> with the ASCII representation of a
2814  * Note structure.  <s> will be able to hold at
2815  * least <ped->TrackChars> characters.
2816  */
2817 {
2818         *((ULONG *)s) = note->Note ? TextNotes[note->Note] : '--- ';
2819
2820         s[3] = HexValuesNo0[note->Inst>>4];
2821         s[4] = HexValues[note->Inst & 0x0F];
2822
2823         s[5] = ' ';
2824
2825         s[6] = HexValues[note->EffNum];
2826         s[7] = HexValues[note->EffVal>>4];
2827         s[8] = HexValues[note->EffVal & 0xF];
2828         s[9] = ' ';
2829 }
2830
2831 #endif /* PORTABLE */
2832
2833
2834
2835 static UWORD ScrollPattern (struct RastPort *rp, struct PattEditData *ped, struct ExtGadget *g, UWORD lefttrack, UWORD topline)
2836 /*
2837  * Scroll the display to the new position <lefttrack>, <topline>.
2838  *
2839  * RESULT
2840  *  0                              - no scrolling occurred,
2841  *  SCROLLED_VERT                  - vertical scroll occurred,
2842  *  SCROLLED_HORIZ                 - horizontal scroll occurred,
2843  *  SCROLLED_VERT | SCROLLED_HORIZ - scrolled in both directions.
2844  */
2845 {
2846         if (ped->LeftTrack != lefttrack)
2847         {
2848                 UWORD retval = SCROLLED_HORIZ | ped->TopLine != topline;
2849
2850                 ped->LeftTrack = lefttrack;
2851                 ped->TopLine = topline;
2852
2853                 EraseCursor (rp, ped);
2854                 DrawTrackNumbers (rp, ped);
2855                 RedrawPattern (rp, ped, g);
2856
2857                 return retval;
2858         }
2859         else
2860         {
2861                 UWORD scroll_lines = abs(ped->TopLine - topline);
2862
2863                 /* Redraw everything if more than an half of the screen
2864                  * has scrolled.  When in mark mode, the scroll limit is set
2865                  * to two thirds of the display, because redrawing the mark
2866                  * region takes up a lot of time.
2867                  */
2868                 //if (scroll_lines > ((ped->Flags & PEF_MARKING) ? ((ped->DisplayLines * 3) >> 1) : (ped->DisplayLines >> 1)))
2869                 if (scroll_lines > (ped->DisplayLines * 3) >> 1)
2870                 {
2871                         ped->LeftTrack = lefttrack;
2872                         ped->TopLine = topline;
2873
2874                         EraseCursor (rp, ped);
2875                         RedrawPattern (rp, ped, g);
2876                         return SCROLLED_VERT;
2877                 }
2878                 else
2879                 {
2880                         WORD scroll_dy = (topline - ped->TopLine) * ped->FontYSize;
2881
2882                         EraseCursor (rp, ped);
2883
2884                         /* Scroll range rectangle */
2885
2886                         ped->RangeRect.MinY -= scroll_dy;
2887                         ped->RangeRect.MaxY -= scroll_dy;
2888
2889                         if (ped->RangeRect.MinY < ped->TBounds.Top)
2890                                 ped->RangeRect.MinY = ped->TBounds.Top;
2891                         if (ped->RangeRect.MaxY >= ped->TBounds.Top + ped->TBounds.Height)
2892                                 ped->RangeRect.MaxY = ped->TBounds.Top + ped->TBounds.Height - 1;
2893
2894                         /* We use ClipBlit() to scroll the pattern because it doesn't clear
2895                          * the scrolled region like ScrollRaster() would do.  Unfortunately,
2896                          * ClipBlit() does not scroll along the damage regions, so we also
2897                          * call ScrollRaster() with the mask set to 0, which will scroll the
2898                          * layer damage regions without actually modifying the display.
2899                          */
2900
2901 #ifdef OS30_ONLY
2902                         SetWriteMask (rp, ped->TextPen | ped->BGPen);
2903 #else
2904                         SafeSetWriteMask (rp, ped->TextPen | ped->BGPen);
2905 #endif  /* OS30_ONLY */
2906
2907                         if (scroll_dy > 0)
2908                                 /* Scroll Down */
2909                                 ClipBlit (rp, ped->GBounds.Left, ped->TBounds.Top + scroll_dy,
2910                                         rp, ped->GBounds.Left, ped->TBounds.Top,
2911                                         ped->TBounds.Width + (ped->TBounds.Left - ped->GBounds.Left), ped->TBounds.Height - scroll_dy,
2912                                         0x0C0);
2913                         else
2914                                 /* Scroll Up */
2915                                 ClipBlit (rp, ped->GBounds.Left, ped->TBounds.Top,
2916                                         rp, ped->GBounds.Left, ped->TBounds.Top - scroll_dy,
2917                                         ped->TBounds.Width + (ped->TBounds.Left - ped->GBounds.Left), ped->TBounds.Height + scroll_dy,
2918                                         0x0C0);
2919
2920
2921                         /* This will scroll the layer damage regions without actually
2922                          * scrolling the display.
2923                          */
2924                         if (rp->Layer->Flags & LAYERSIMPLE)
2925                         {
2926 #ifdef OS30_ONLY
2927                                 SetWriteMask (rp, 0);
2928 #else
2929                                 SafeSetWriteMask (rp, 0);
2930 #endif  /* OS30_ONLY */
2931
2932                                 ScrollRaster (rp, 0, scroll_dy,
2933                                         ped->GBounds.Left, ped->TBounds.Top,
2934                                         ped->TBounds.Left + ped->TBounds.Width - 1,
2935                                         ped->TBounds.Top + ped->TBounds.Height - 1);
2936                         }
2937
2938                         ped->LeftTrack = lefttrack;
2939                         ped->TopLine = topline;
2940
2941                         if (scroll_dy > 0)
2942                                 /* Scroll down */
2943                                 DrawPatternLines (rp, ped, ped->DisplayLines - scroll_lines, ped->DisplayLines-1);
2944                         else
2945                                 /* Scroll up */
2946                                 DrawPatternLines (rp, ped, 0, scroll_lines - 1);
2947
2948
2949 #ifdef OS30_ONLY
2950                         SetWriteMask (rp, ~0);
2951 #else
2952                         SafeSetWriteMask (rp, (UBYTE)~0);
2953 #endif  /* OS30_ONLY */
2954
2955                         DrawCursor (rp, ped, g);
2956
2957                         return SCROLLED_VERT;
2958                 }
2959         }
2960
2961         return 0;
2962 }
2963
2964
2965
2966 #if 0
2967 INTCALL static void TimerIntServer (REG(a1) struct ExtGadget *g)
2968 {
2969         struct PattEditData *ped = INST_DATA (OCLASS(g), g); /*!*/
2970
2971         /* Remove TimerIO for further usage */
2972         GetMsg (&ped->TimerPort);
2973
2974         if ((ped->Flags & PEF_SCROLLING) && ped->TopLine)
2975         {
2976                 SetGadgetAttrs (g, ped->MyWindow, NULL,
2977                         PEA_Up, 1,
2978                         TAG_DONE);
2979
2980                 /* Send another request for next scroll */
2981                 ped->TimerIO.tr_node.io_Command = TR_ADDREQUEST;
2982                 ped->TimerIO.tr_time.tv_micro = 500000;
2983                 BeginIO ((struct IORequest *)&ped->TimerIO);
2984         }
2985 }
2986 #endif
2987
2988
2989
2990 /*
2991  * Class library support functions
2992  */
2993
2994 HOOKCALL struct ClassLibrary * _UserLibInit (REG(a6, struct ClassLibrary *mybase))
2995 {
2996         SysBase = *((struct ExecBase **)4);     /* Initialize SysBase */
2997
2998         IntuitionBase   = (struct IntuitionBase *) OpenLibrary ("intuition.library", WANTED_LIBVER);
2999         GfxBase                 = (struct GfxBase *) OpenLibrary ("graphics.library", WANTED_LIBVER);
3000         UtilityBase             = (struct UtilityBase *) OpenLibrary ("utility.library", WANTED_LIBVER);
3001         KeymapBase              = OpenLibrary ("keymap.library", 36);   /* Kick V37 has keymap V36! */
3002
3003         if (!(IntuitionBase && GfxBase && UtilityBase && KeymapBase))
3004         {
3005                 _UserLibCleanup (mybase);
3006                 return NULL;
3007         }
3008
3009         if (mybase->cl_Class = MakeClass (PATTEDITCLASS, GADGETCLASS, NULL, sizeof (struct PattEditData), 0))
3010         {
3011                 mybase->cl_Class->cl_Dispatcher.h_Entry = (ULONG (*)()) PattEditDispatcher;
3012                 AddClass (mybase->cl_Class);
3013         }
3014         else
3015         {
3016                 _UserLibCleanup (mybase);
3017                 return NULL;
3018         }
3019
3020         return mybase;
3021 }
3022
3023
3024
3025 HOOKCALL struct ClassLibrary * _UserLibCleanup (REG(a6, struct ClassLibrary *mybase))
3026 {
3027         if (mybase->cl_Class)
3028                 if (!FreeClass (mybase->cl_Class))
3029                         return NULL;
3030
3031         /* The tests against NULL are needed here because CloseLibrary()
3032          * in V34 crashes when passed a NULL pointer, and this
3033          * function might get called when someone tries to load this
3034          * library on an 1.3 system.
3035          */
3036         if (KeymapBase)         CloseLibrary ((struct Library *)KeymapBase);
3037         if (UtilityBase)        CloseLibrary ((struct Library *)UtilityBase);
3038         if (GfxBase)            CloseLibrary ((struct Library *)GfxBase);
3039         if (IntuitionBase)      CloseLibrary ((struct Library *)IntuitionBase);
3040
3041         return mybase;
3042 }
3043
3044
3045
3046 HOOKCALL Class * _GetEngine (REG(a6, struct ClassLibrary *mybase))
3047 {
3048         return (mybase->cl_Class);
3049 }