Initial commit.
[amiga/xmodule.git] / PatternWin.c
1 /*
2 **      PatternWin.c
3 **
4 **      Copyright (C) 1994,95,96 Bernardo Innocenti
5 **
6 **      Parts of the code have been inspired by ScrollerWindow 0.3 demo
7 **      Copyright © 1994 Christoph Feck, TowerSystems.
8 **
9 **      Pattern editor handling functions.
10 **
11 **
12 **      Diagram of object interconnections:
13 **
14 **
15 **                 ScrollButtonClass objects
16 **      +----------+ +---------+ +----------+ +----------+
17 **      | UpButton | | DnButton| | SxButton | | DxButton |
18 **      +----------+ +---------+ +----------+ +----------+
19 **       | GA_ID =   | GA_ID =    | GA_ID =    | GA_ID =
20 **       | PATTA_Up  | PATTA_Down | PATTA_Left | PATTA_Right
21 **       |           |            |            |
22 **       |  +--------+            |            |
23 **       |  |  +------------------+            |
24 **       |  |  |  +----------------------------+
25 **       |  |  |  |      propgclass  object                       icclass object
26 **       |  |  |  |      +----------------+                     +-----------------+
27 **       |  |  |  |   +--|     HSlider    |<--------------------| EditorToHSlider |
28 **       |  |  |  |   |  +----------------+  PATTA_LeftTrack =  +-----------------+
29 **       |  |  |  |   | PGA_Top =            PGA_Top                    ^
30 **       |  |  |  |   | PATTA_TopLine        PATTA_DisplayTracks =      |
31 **       |  |  |  |   |                      PGA_Visible                |
32 **       |  |  |  |   |                   +-----------------------------+
33 **       V  V  V  V   V                   |
34 **      +---------------+            ************
35 **      |  PattEditGad  |----------->*   Model  *----------------> IDCMP_IDCMPUPDATE
36 **      +---------------+            ************ PATTA_CursLine   to PatternWin
37 **        ^                               |       PATTA_CursTrack
38 **        | propgclass object             |
39 **        | PGA_Top = PATTA_TopLine       V icclass object
40 **      +----------------+       +-----------------+
41 **      |    VSlider     |<------| EditorToVSlider | PATTA_TopLine      = PGA_Top
42 **      +----------------+       +-----------------+ PATTA_DisplayLines = PGA_Visible
43 */
44
45
46 #include <intuition/intuition.h>
47 #include <intuition/icclass.h>
48 #include <intuition/gadgetclass.h>
49 #include <intuition/imageclass.h>
50 #include <graphics/gfxbase.h>
51 #include <libraries/gadtools.h>
52 #include <libraries/iffparse.h>
53 #include <libraries/patteditclass.h>
54
55 #include <proto/exec.h>
56 #include <proto/intuition.h>
57 #include <proto/graphics.h>
58 #include <proto/layers.h>
59 #include <proto/utility.h>
60 #include <proto/dos.h>
61 #include <proto/iffparse.h>
62 #include <proto/diskfont.h>
63 #include <proto/xmodule.h>
64
65 #include "XModulePriv.h"
66 #include "Gui.h"
67 #include "CustomClasses.h"
68
69
70 /* Gadget IDs */
71 enum {
72         GD_PattEdit,
73         GD_UpButton,
74         GD_DnButton,
75         GD_VSlider,
76         GD_SxButton,
77         GD_DxButton,
78         GD_HSlider,
79
80         Pattern_CNT
81 };
82
83
84 /* Local function prototypes */
85
86 static struct Gadget    *CreatePattEdit (void);
87 static struct Gadget    *LayoutPatternWin (struct WinUserData *wud);
88
89 static void PatternPostOpen             (struct WinUserData *wud);
90 static void PatternWindowCleanup(struct WinUserData *wud);
91
92 static void RenderPatternWindow (struct WinUserData *wud);
93 static void HandlePatternIDCMP  (struct WinUserData *wud);
94 static void PatternLoad                 (STRPTR name, ULONG num, ULONG count);
95 static LONG PatternSave                 (STRPTR name, struct Pattern *patt);
96
97 static void PatternMiOpen               (void);
98 static void PatternMiSave               (void);
99 static void PatternMiSaveAs             (void);
100 static void PatternMiSize               (void);
101 static void PatternMiMark               (struct WinUserData *wud);
102 static void PatternMiCut                (struct WinUserData *wud);
103 static void PatternMiCopy               (struct WinUserData *wud);
104 static void PatternMiPaste              (struct WinUserData *wud);
105 static void PatternMiErase              (struct WinUserData *wud);
106 static void PatternMiUndo               (struct WinUserData *wud);
107 static void PatternMiRedo               (struct WinUserData *wud);
108 static void PatternMiSettings   (void);
109
110
111
112 /* Local data */
113
114 static struct Library   *PattEditBase           = NULL;
115 static struct TextFont  *EditorFont                     = NULL;
116 static Object                   *Model                          = NULL,
117                                                 *EditorToVSlider        = NULL,
118                                                 *EditorToHSlider        = NULL;
119
120 static UBYTE WindowTitle[128];
121 static UBYTE TitleInfo[16];
122
123
124
125 XDEF struct PattSwitches PattSwitches =
126 {
127         0,      1,                                      /* AdvanceTracks,       AdvanceLines    */
128         32,     8192,                           /* MaxUndoLevels,       MaxUndoMem              */
129         0,                                              /* Flags                                                        */
130         SCROLLERPLACE_RIGHT,    /* VScrollerPlace                                       */
131         SCROLLERPLACE_BOTTOM,   /* HScrollerPlace                                       */
132         0,      0,                                      /* ClipboardUnit,       Pad0                    */
133         10, 0,                                  /* TrackChars, Backdrop                         */
134         0,      1,      2,      2                       /* BGPen, TextPen, LinesPen, TinyLinesPen */
135 };
136
137
138 static LONG MapVSlider2Editor[] =
139 {
140         PGA_Top,                                PEA_TopLine,
141         TAG_DONE
142 };
143
144 static LONG MapHSlider2Editor[] =
145 {
146         PGA_Top,                                PEA_LeftTrack,
147         TAG_DONE
148 };
149
150 static LONG MapEditorToVSlider[] =
151 {
152         PEA_TopLine,                    PGA_Top,
153         PEA_DisplayLines,               PGA_Visible,
154         TAG_DONE
155 };
156
157 static LONG MapEditorToHSlider[] =
158 {
159         PEA_LeftTrack,          PGA_Top,
160         PEA_DisplayTracks,      PGA_Visible,
161         TAG_DONE
162 };
163
164 static LONG MapUp2Editor[] =
165 {
166         GA_ID,          PEA_Up,
167         TAG_DONE
168 };
169
170 static LONG MapDn2Editor[] =
171 {
172         GA_ID,          PEA_Down,
173         TAG_DONE
174 };
175
176 static LONG MapSx2Editor[] =
177 {
178         GA_ID,          PEA_Left,
179         TAG_DONE
180 };
181
182 static LONG MapDx2Editor[] =
183 {
184         GA_ID,          PEA_Right,
185         TAG_DONE
186 };
187
188
189
190 static struct NewMenu PatternNewMenu[] = {
191         NM_TITLE, (STRPTR)MSG_PATTERNS_MEN, NULL, 0, NULL, NULL,
192         NM_ITEM, (STRPTR)MSG_OPEN_MEN, (STRPTR)"O", 0, 0L, (APTR)PatternMiOpen,
193         NM_ITEM, (STRPTR)MSG_SAVE_MEN, (STRPTR)"S", 0, 0L, (APTR)PatternMiSave,
194         NM_ITEM, (STRPTR)MSG_SAVE_AS_MEN, (STRPTR)"A", 0, 0L, (APTR)PatternMiSaveAs,
195         NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
196         NM_ITEM, (STRPTR)MSG_SIZE_MEN, NULL, 0, 0L, (APTR)PatternMiSize,
197         NM_TITLE, (STRPTR)MSG_EDIT_MEN, NULL, 0, NULL, NULL,
198         NM_ITEM, (STRPTR)MSG_MARK_MEN, (STRPTR)"B", 0, 0L, (APTR)PatternMiMark,
199         NM_ITEM, (STRPTR)MSG_CUT_MEN, (STRPTR)"X", 0, 0L, (APTR)PatternMiCut,
200         NM_ITEM, (STRPTR)MSG_COPY_MEN, (STRPTR)"C", 0, 0L, (APTR)PatternMiCopy,
201         NM_ITEM, (STRPTR)MSG_PASTE_MEN, (STRPTR)"V", 0, 0L, (APTR)PatternMiPaste,
202         NM_ITEM, (STRPTR)MSG_ERASE_MEN, (STRPTR)"E", 0, 0L, (APTR)PatternMiErase,
203         NM_ITEM, (STRPTR)NM_BARLABEL, NULL, 0, 0L, NULL,
204         NM_ITEM, (STRPTR)MSG_UNDO_MEN, (STRPTR)"U", 0, 0L, (APTR)PatternMiUndo,
205         NM_ITEM, (STRPTR)MSG_REDO_MEN, (STRPTR)"R", 0, 0L, (APTR)PatternMiRedo,
206         NM_TITLE, (STRPTR)MSG_SETTINGS_MEN, NULL, 0, NULL, NULL,
207         NM_ITEM, (STRPTR)MSG_EDITOR_SETTINGS_MEN, (STRPTR)"P", 0, 0L, (APTR)PatternMiSettings,
208         NM_END, NULL, NULL, 0, 0L, NULL };
209
210
211
212 XDEF LONG PatternWinTags[] =
213 {
214         TAG_IGNORE,                     TRUE,   /* WA_SizeB(Left|Right) */
215 #ifdef OS30_ONLY
216         WA_BackFill,            LAYERS_NOBACKFILL,
217 #endif /* OS30_ONLY */
218         XMWIN_NewMenu,          (LONG)PatternNewMenu,
219         XMWIN_LayoutFunc,       (LONG)LayoutPatternWin,
220         XMWIN_GCount,           Pattern_CNT,
221         XMWIN_Title,            MSG_PATTERN_TITLE,
222         XMWIN_WindowFlags,      WFLG_CLOSEGADGET | WFLG_ACTIVATE|
223                 WFLG_SIZEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH,
224         XMWIN_IDCMPFlags,       IDCMP_MENUPICK|IDCMP_CLOSEWINDOW|IDCMP_ACTIVEWINDOW|IDCMP_INACTIVEWINDOW|IDCMP_NEWSIZE|IDCMP_IDCMPUPDATE|IDCMP_INTUITICKS,
225         XMWIN_IDCMPFunc,        (LONG)HandlePatternIDCMP,
226         XMWIN_PostOpenFunc,     (LONG)PatternPostOpen,
227         XMWIN_LayoutCleanup,(LONG)PatternWindowCleanup,
228         XMWIN_HelpNode,         (LONG)"Pattern",
229         TAG_DONE
230 };
231
232
233
234 static struct Gadget *CreatePattEdit (void)
235 {
236         /* We do not initialize PEA_Pattern right now because
237          * it is done later by UpdatePattern().
238          */
239         return ((struct Gadget *)NewObject (NULL, PATTEDITCLASS,
240                 GA_ID,                          GD_PattEdit,
241                 GA_Left,                        ((PattSwitches.VScrollerPlace == SCROLLERPLACE_LEFT) ? SizeWidth : OffX),
242                 GA_Top,                         OffY,
243                 GA_RelWidth,            - SizeWidth - ((PattSwitches.VScrollerPlace == SCROLLERPLACE_LEFT) ? Scr->WBorRight : OffX),
244                 GA_RelHeight,           - OffY - (((PattSwitches.HScrollerPlace == SCROLLERPLACE_BOTTOM) ||
245                         (PattSwitches.VScrollerPlace != SCROLLERPLACE_RIGHT)) ? SizeHeight : Scr->WBorBottom),
246                 PEA_TextFont,           EditorFont ? EditorFont : TopazFont,
247                 PEA_AdvanceCurs,        (PattSwitches.AdvanceTracks << 16) | PattSwitches.AdvanceLines,
248                 PEA_MaxUndoLevels,      PattSwitches.MaxUndoLevels,
249                 PEA_MaxUndoMem,         PattSwitches.MaxUndoMem,
250                 PEA_Flags,                      PattSwitches.Flags,
251                 PEA_TrackChars,         PattSwitches.TrackChars,
252                 PEA_BGPen,                      PattSwitches.BGPen,
253                 PEA_TextPen,            PattSwitches.TextPen,
254                 PEA_LinesPen,           PattSwitches.LinesPen,
255                 PEA_TinyLinesPen,       PattSwitches.TinyLinesPen,
256                 TAG_DONE));
257 }
258
259
260
261 static struct Gadget *LayoutPatternWin (struct WinUserData *wud)
262 {
263         struct Gadget *pattedit;
264
265         if (!PattEditBase)
266                 if (!(PattEditBase = MyOpenLibrary (PATTEDITNAME, PATTEDITVERS)))
267                         return NULL;
268
269         {
270                 ULONG numcols = 1 << Scr->RastPort.BitMap->Depth; /**/
271
272                 if (PattSwitches.TextPen >= numcols)
273                         PattSwitches.TextPen = 1;
274
275                 if (PattSwitches.LinesPen >= numcols)
276                         PattSwitches.LinesPen = 2;
277
278                 if (PattSwitches.TinyLinesPen >= numcols)
279                         PattSwitches.TinyLinesPen = 2;
280         }
281
282         if (!(EditorFont = OpenFont (&EditorAttr)))
283                 if (DiskfontBase)
284                         EditorFont = OpenDiskFont (&EditorAttr);
285
286         if (!EditorFont)
287         {
288                 CantOpenLib (EditorAttr.ta_Name, 0);
289                 EditorFont = OpenFont (&TopazAttr);
290         }
291
292
293         PatternWinTags[0] = (PattSwitches.VScrollerPlace == SCROLLERPLACE_RIGHT) ?
294                 WA_SizeBRight : WA_SizeBBottom;
295
296         if (PattSwitches.Backdrop)
297         {
298                 wud->Flags = WFLG_BORDERLESS | WFLG_BACKDROP | WFLG_SMART_REFRESH | WFLG_NOCAREREFRESH;
299                 wud->WindowSize.Left = wud->WindowSize.Top = 0;
300                 wud->WindowSize.Width = Scr->Width;
301                 wud->WindowSize.Height = Scr->Height;
302         }
303         else
304         {
305                 wud->WindowSize.Width = max (wud->WindowSize.Width,
306                         EditorFont->tf_XSize * (MINTRACKCHARS + 4));
307                 wud->WindowSize.Height = max (wud->WindowSize.Height,
308                         EditorFont->tf_YSize * 2);
309         }
310
311         if (!(pattedit = wud->Gadgets[GD_PattEdit] = CreatePattEdit()))
312                 return NULL;
313
314         if (PattSwitches.VScrollerPlace)
315         {
316                 if (!(wud->Gadgets[GD_UpButton] = CreateUpButton (GD_UpButton, pattedit, MapUp2Editor, PattSwitches.VScrollerPlace)))
317                         return NULL;
318                 if (!(wud->Gadgets[GD_DnButton] = CreateDnButton (GD_DnButton, pattedit, MapDn2Editor, PattSwitches.VScrollerPlace)))
319                         return NULL;
320                 if (!(wud->Gadgets[GD_VSlider] = CreateVSlider (GD_VSlider, pattedit, MapVSlider2Editor,
321                         wud->Gadgets[GD_UpButton]->Height + wud->Gadgets[GD_DnButton]->Height,
322                         PattSwitches.VScrollerPlace)))
323                         return NULL;
324         }
325
326         if (PattSwitches.HScrollerPlace)
327         {
328                 if (!(wud->Gadgets[GD_SxButton] = CreateSxButton (GD_SxButton, pattedit, MapSx2Editor)))
329                         return NULL;
330                 if (!(wud->Gadgets[GD_DxButton] = CreateDxButton (GD_DxButton, pattedit, MapDx2Editor)))
331                         return NULL;
332                 if (!(wud->Gadgets[GD_HSlider] = CreateHSlider (GD_HSlider, pattedit, MapHSlider2Editor,
333                 wud->Gadgets[GD_DxButton]->Width + wud->Gadgets[GD_SxButton]->Width)))
334                         return NULL;
335         }
336
337
338         /* Link gadgets together */
339
340         if (PattSwitches.VScrollerPlace)
341         {
342                 wud->Gadgets[GD_PattEdit]->NextGadget   = wud->Gadgets[GD_UpButton];
343                 wud->Gadgets[GD_UpButton]->NextGadget   = wud->Gadgets[GD_DnButton];
344                 wud->Gadgets[GD_DnButton]->NextGadget   = wud->Gadgets[GD_VSlider];
345                 if (PattSwitches.HScrollerPlace)
346                         wud->Gadgets[GD_VSlider]->NextGadget = wud->Gadgets[GD_SxButton];
347         }
348         if (PattSwitches.HScrollerPlace)
349         {
350                 if (!PattSwitches.VScrollerPlace)
351                         wud->Gadgets[GD_PattEdit]->NextGadget   = wud->Gadgets[GD_SxButton];
352
353                 wud->Gadgets[GD_SxButton]->NextGadget   = wud->Gadgets[GD_DxButton];
354                 wud->Gadgets[GD_DxButton]->NextGadget   = wud->Gadgets[GD_HSlider];
355         }
356
357
358         if (PattSwitches.VScrollerPlace || PattSwitches.HScrollerPlace)
359         {
360                 /* Create the Model */
361                 if (Model = NewObject (NULL, MODELCLASS,
362                         ICA_TARGET,             ICTARGET_IDCMP,
363                         TAG_DONE))
364                 {
365                         /* Connect Editor to Model */
366                         SetAttrs (wud->Gadgets[GD_PattEdit],
367                                 ICA_TARGET, Model,
368                                 TAG_DONE);
369
370                         if (PattSwitches.VScrollerPlace)
371                         {
372                                 if (EditorToVSlider = NewObject (NULL, ICCLASS,
373                                         ICA_TARGET,     wud->Gadgets[GD_VSlider],
374                                         ICA_MAP,        MapEditorToVSlider,
375                                         TAG_DONE))
376                                         /* Connect Model to VSlider */
377                                         if (!DoMethod (Model, OM_ADDMEMBER, EditorToVSlider))
378                                                 DisposeObject (EditorToVSlider); EditorToVSlider = NULL;
379                         }
380
381                         if (PattSwitches.HScrollerPlace)
382                         {
383                                 if (EditorToHSlider = NewObject (NULL, ICCLASS,
384                                         ICA_TARGET,     wud->Gadgets[GD_HSlider],
385                                         ICA_MAP,        MapEditorToHSlider,
386                                         TAG_DONE))
387                                                 /* Connect Model to HSlider */
388                                                 if (!DoMethod (Model, OM_ADDMEMBER, EditorToHSlider))
389                                                         DisposeObject (EditorToHSlider); EditorToHSlider = NULL;
390                         }
391                 }
392         }
393         else
394                 /* Connect Editor to application */
395                 SetAttrs (wud->Gadgets[GD_PattEdit],
396                         ICA_TARGET, ICTARGET_IDCMP,
397                         TAG_DONE);
398
399         return wud->Gadgets[GD_PattEdit];
400
401         /* NOTE: The model will also dispose its members... */
402 /*
403         DisposeObject (Model); Model = NULL;
404         EditorToVSlider = NULL; EditorToHSlider = NULL;
405         return NULL;
406 */
407 }
408
409
410
411
412 #ifndef OS30_ONLY
413
414 static ULONG __asm BFHookFunc (void)
415
416 /* No-op backfilling hook.  Since we are going to redraw the whole window
417  * anyway, we can disable backfilling.  This avoids ugly flashing while
418  * resizing or revealing the window.
419  *
420  * This function hasn't the __saveds attribute because it makes no
421  * references to external data.
422  */
423 {
424         return 1;       /* Do nothing */
425 }
426
427 static struct Hook BFHook =
428 {
429         NULL, NULL,
430         BFHookFunc,
431 };
432 #endif /* !OS30_ONLY */
433
434
435
436 static void PatternPostOpen (struct WinUserData *wud)
437 {
438         /* Limit window flashing by providing a no-op hook for window
439          * backfilling.
440          */
441 #ifndef OS30_ONLY
442         InstallLayerHook (wud->Win->WLayer, (LayersBase->lib_Version >= 39) ?
443                 ((struct Hook *)LAYERS_NOBACKFILL) : &BFHook);
444 #endif /* !OS30_ONLY */
445
446         UpdatePattern();
447
448         /* Allow window resizing.  Minimum size is chosen so that at least
449          * two lines are visible.
450          */
451         WindowLimits (wud->Win,
452                 wud->Win->BorderLeft + wud->Win->BorderRight + EditorFont->tf_XSize * (10 + 4),
453                 wud->Win->BorderTop + wud->Win->BorderBottom + EditorFont->tf_YSize * 2,
454                 -1, -1);
455 }
456
457
458
459 static void PatternWindowCleanup (struct WinUserData *wud)
460 {
461         /* NOTE: The model will also dispose its members. */
462         DisposeObject (Model);  Model = NULL;
463         EditorToVSlider = NULL;
464         EditorToHSlider = NULL;
465
466         if (wud->Gadgets)
467         {
468                 DisposeObject (wud->Gadgets[GD_VSlider]);
469                 wud->Gadgets[GD_VSlider] = NULL;
470                 DisposeObject (wud->Gadgets[GD_HSlider]);
471                 wud->Gadgets[GD_HSlider] = NULL;
472
473                 if (wud->Gadgets[GD_UpButton])
474                 {
475                         DisposeObject (wud->Gadgets[GD_UpButton]->GadgetRender);
476                         DisposeObject (wud->Gadgets[GD_UpButton]);
477                         wud->Gadgets[GD_UpButton] = NULL;
478                 }
479
480                 if (wud->Gadgets[GD_DnButton])
481                 {
482                         DisposeObject (wud->Gadgets[GD_DnButton]->GadgetRender);
483                         DisposeObject (wud->Gadgets[GD_DnButton]);
484                         wud->Gadgets[GD_DnButton] = NULL;
485                 }
486
487                 if (wud->Gadgets[GD_SxButton])
488                 {
489                         DisposeObject (wud->Gadgets[GD_SxButton]->GadgetRender);
490                         DisposeObject (wud->Gadgets[GD_SxButton]);
491                         wud->Gadgets[GD_SxButton] = NULL;
492                 }
493
494                 if (wud->Gadgets[GD_DxButton])
495                 {
496                         DisposeObject (wud->Gadgets[GD_DxButton]->GadgetRender);
497                         DisposeObject (wud->Gadgets[GD_DxButton]);
498                         wud->Gadgets[GD_DxButton] = NULL;
499                 }
500
501                 DisposeObject (wud->Gadgets[GD_PattEdit]);
502                 wud->Gadgets[GD_PattEdit] = NULL;
503         }
504
505         if (EditorFont)
506         {
507                 CloseFont (EditorFont);
508                 EditorFont = NULL;
509         }
510
511         if (PattEditBase)
512         {
513                 CloseLibrary (PattEditBase);
514                 PattEditBase = NULL;
515         }
516 }
517
518
519
520 static void HandlePatternIDCMP (struct WinUserData *wud)
521 {
522         switch (IntuiMsg.Class)
523         {
524                 case IDCMP_IDCMPUPDATE:
525                 {
526                         struct TagItem *ti, *tstate = IntuiMsg.IAddress;
527                         ULONG line = ((UWORD)~0), track = ((UWORD)~0);
528
529                         while (ti = NextTagItem (&tstate))
530                                 switch (ti->ti_Tag)
531                                 {
532                                         case PEA_CursLine:
533                                                 line = ti->ti_Data;
534                                                 break;
535
536                                         case PEA_CursTrack:
537                                                 track = ti->ti_Data;
538                                                 break;
539
540                                         case PEA_ChangeNote:
541                                                 /* TODO: Increment changes counter of song */
542                                                 break;
543
544                                         default:
545                                                 break;
546                                 }
547
548                         if (track != ((UWORD)~0))
549                         {
550                                 SPrintf (TitleInfo, "   %lu, %lu", track, line);
551                                 RenderPatternWindow (wud);
552                         }
553
554                         break;
555                 }
556
557                 case IDCMP_ACTIVEWINDOW:
558                 case IDCMP_INACTIVEWINDOW:
559                 case IDCMP_NEWSIZE:
560                         RenderPatternWindow (wud);
561                         break;
562
563                 case IDCMP_INTUITICKS:
564                         if (!(wud->Gadgets[GD_PattEdit]->Flags & GFLG_SELECTED))
565                                 ActivateGadget (wud->Gadgets[GD_PattEdit], wud->Win, NULL);
566                         break;
567
568                 default:
569                         break;
570         }
571 }
572
573
574
575 static void RenderPatternWindow (struct WinUserData *wud)
576
577 /* Prints <TitleInfo> on the right hand of the window title bar */
578 {
579         UWORD tlen, twidth;
580         struct RastPort *rp;
581         struct TextFont *oldfont;
582
583         tlen = strlen (TitleInfo);      /* Couldn't get it from SPrintf()!! */
584
585         rp = wud->Win->RPort;
586         oldfont = rp->Font;
587         SetFont (rp, DrawInfo->dri_Font);
588
589         twidth = TextLength (rp, TitleInfo, tlen);
590
591         if (wud->Win->Flags & WFLG_WINDOWACTIVE)
592         {
593 #ifndef OS30_ONLY
594                 if (GfxBase->LibNode.lib_Version >= 39)
595 #endif /* !OS30_ONLY */
596                         SetABPenDrMd (rp, DrawInfo->dri_Pens[FILLTEXTPEN],
597                                 DrawInfo->dri_Pens[FILLPEN], JAM2);
598 #ifndef OS30_ONLY
599                 else
600                 {
601                         SetAPen (rp, DrawInfo->dri_Pens[FILLTEXTPEN]);
602                         SetBPen (rp, DrawInfo->dri_Pens[FILLPEN]);
603                         SetDrMd (rp, JAM2);
604                 }
605 #endif /* !OS30_ONLY */
606         }
607         else
608         {
609 #ifndef OS30_ONLY
610                 if (GfxBase->LibNode.lib_Version >= 39)
611 #endif /* !OS30_ONLY */
612                         SetABPenDrMd (rp, DrawInfo->dri_Pens[TEXTPEN],
613                                 DrawInfo->dri_Pens[BACKGROUNDPEN], JAM2);
614 #ifndef OS30_ONLY
615                 else
616                 {
617                         SetAPen (rp, DrawInfo->dri_Pens[TEXTPEN]);
618                         SetBPen (rp, DrawInfo->dri_Pens[BACKGROUNDPEN]);
619                         SetDrMd (rp, JAM2);
620                 }
621 #endif /* !OS30_ONLY */
622         }
623
624         Move (rp, wud->Win->Width - twidth - 60, rp->TxBaseline + 1);
625         Text (rp, TitleInfo, tlen);
626
627         SetFont (rp, oldfont);
628 }
629
630
631
632 GLOBALCALL void UpdatePattern (void)
633 {
634         struct WinUserData *wud = WDescr[WID_PATTERN].Wud;
635
636         if (wud && wud->Win)
637         {
638                 struct SongInfo *si;
639                 struct Pattern *patt;
640                 ULONG currentinst;
641
642                 if (si = xmLockActiveSong (SM_SHARED))
643                 {
644                         patt = si->Patt[si->CurrentPatt];
645                         currentinst = si->CurrentInst;
646
647                         SPrintf (WindowTitle, "%s/%03ld: %s  (%ld/%ld)",
648                                 si->Title,
649                                 si->CurrentPatt,
650                                 patt->Name ? patt->Name : (UBYTE *)"", patt->Tracks, patt->Lines);
651
652                         ReleaseSemaphore (&si->Lock);
653                 }
654                 else
655                 {
656                         patt = NULL;
657                         currentinst = 0;
658                         strcpy (WindowTitle, "Pattern Editor");
659                 }
660
661                 SetWindowTitles (wud->Win, WindowTitle, NULL);
662
663                 SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
664                         PEA_Pattern,            patt,
665                         PEA_CurrentInst,        currentinst,
666                         TAG_DONE);
667
668                 SetGadgetAttrs (wud->Gadgets[GD_VSlider], wud->Win, NULL,
669                         PGA_Total,                      patt ? patt->Lines : 0,
670                         TAG_DONE);
671
672                 SetGadgetAttrs (wud->Gadgets[GD_HSlider], wud->Win, NULL,
673                         PGA_Total,                      patt ? patt->Tracks : 0,
674                         TAG_DONE);
675         }
676
677         UpdatePattSize();
678 }
679
680
681
682 GLOBALCALL void UpdateEditorInst (void)
683 {
684         struct WinUserData *wud = WDescr[WID_PATTERN].Wud;
685
686         if (wud && wud->Win)
687         {
688                 struct SongInfo *si;
689
690                 if (si = xmLockActiveSong (SM_SHARED))
691                 {
692                         SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
693                                 PEA_CurrentInst,        si->CurrentInst,
694                                 TAG_DONE);
695                         ReleaseSemaphore (&si->Lock);
696                 }
697         }
698 }
699
700
701
702
703 static void PatternLoad (STRPTR name, ULONG num, ULONG count)
704
705 /* Load a pattern from an IFF PATT file.  This function is called only
706  * by the file requester handler.
707  */
708 {
709         struct SongInfo *si;
710         struct IFFHandle *iff;
711         LONG err;
712
713         if (si = xmLockActiveSong (SM_SHARED))
714         {
715                 LockWindows();
716
717                 if (iff = AllocIFF())
718                 {
719                         InitIFFasDOS (iff);
720                         if (iff->iff_Stream = (ULONG) Open (name, MODE_OLDFILE))
721                         {
722                                 if (!(err = OpenIFF (iff, IFFF_READ)))
723                                 {
724                                         LoadPattern (si, si->CurrentPatt + num, iff);
725                                         CloseIFF (iff);
726                                 }
727
728                                 Close (iff->iff_Stream);
729                         }
730                         else err = IoErr();
731
732                         FreeIFF (iff);
733                 }
734                 else err = ERROR_NO_FREE_STORE;
735
736                 ReleaseSemaphore (&si->Lock);
737
738                 UpdatePatternList(); // This will also update the Pattern Editor.
739
740                 UnlockWindows();
741
742                 LastErr = err;
743         }
744 }
745
746
747
748 static LONG PatternSave (STRPTR name, struct Pattern *patt)
749
750 /* Store a pattern to an IFF PATT file. */
751 {
752         struct IFFHandle        *iff;
753         LONG err;
754
755
756         if (iff = AllocIFF())
757         {
758                 if (name)
759                 {
760                         InitIFFasDOS (iff);
761                         iff->iff_Stream = (ULONG) Open (name, MODE_NEWFILE);
762                 }
763                 else /* Clipboard */
764                 {
765                         InitIFFasClip (iff);
766                         iff->iff_Stream = (ULONG) OpenClipboard (PattSwitches.ClipboardUnit);
767                 }
768
769                 if (iff->iff_Stream)
770                 {
771
772                         if (!(err = OpenIFF (iff, IFFF_WRITE)))
773                         {
774                                 SavePattern (iff, patt);
775                                 CloseIFF (iff);
776                         }
777
778                         if (name)
779                                 Close (iff->iff_Stream);
780                         else
781                                 CloseClipboard ((struct ClipboardHandle *)iff->iff_Stream);
782                 }
783                 else err = IoErr();
784
785                 FreeIFF (iff);
786         }
787         else err = ERROR_NO_FREE_STORE;
788
789         return err;
790 }
791
792
793
794 /*****************/
795 /* Pattern Menus */
796 /*****************/
797
798 static void PatternMiOpen (void)
799 {
800         StartFileRequest (FREQ_LOADPATT, PatternLoad);
801 }
802
803
804
805 static void PatternMiSave (void)
806 {
807         struct SongInfo *si;
808         struct Pattern *patt;
809
810
811         if (si = xmLockActiveSong (SM_SHARED))
812         {
813                 LockWindows();
814                 patt = si->Patt[si->CurrentPatt];
815                 LastErr = PatternSave (patt->Name, patt);
816                 UnlockWindows();
817                 ReleaseSemaphore (&si->Lock);
818         }
819 }
820
821
822
823 static void PatternMiSaveAs (void)
824 {
825         struct SongInfo *si;
826         struct Pattern *patt;
827         UBYTE name[PATHNAME_MAX];
828
829         if (si = xmLockActiveSong (SM_SHARED))
830         {
831                 LockWindows();
832
833                 patt = si->Patt[si->CurrentPatt];
834
835                 strncpy (name, patt->Name, PATHNAME_MAX-1);
836                 name[PATHNAME_MAX-1] = '\0';
837
838                 if (FileRequest (FREQ_SAVEINST, name))
839                         LastErr = PatternSave (name, patt);
840
841                 UnlockWindows();
842                 ReleaseSemaphore (&si->Lock);
843         }
844 }
845
846
847
848 static void PatternMiSize (void)
849 {
850         NewWindow (WID_PATTSIZE);
851 }
852
853
854
855 static void PatternMiMark (struct WinUserData *wud)
856 {
857         SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
858                 PEA_MarkRegion, -1,
859                 TAG_DONE);
860 }
861
862
863
864 static void PatternMiCut (struct WinUserData *wud)
865 {
866         struct SongInfo *si;
867         struct Pattern *patt, *cpatt;
868         struct Rectangle markregion;
869         UWORD i;
870
871         if (si = xmLockActiveSong (SM_SHARED))
872         {
873                 patt = si->Patt[si->CurrentPatt];
874
875                 if (GetAttr (PEA_MarkRegion, wud->Gadgets[GD_PattEdit], (ULONG *)&markregion))
876                 {
877                         if ((markregion.MaxX == 0) && (markregion.MaxY == 0))
878                                 return; /* Not in mark mode */
879
880                         if (cpatt = xmAddPattern (si, -1,
881                                 PATTA_Tracks,   markregion.MaxX - markregion.MinX + 1,
882                                 PATTA_Lines,    0, /* Do not allocate anything */
883                                 PATTA_Name,             patt->Name,
884                                 TAG_DONE))
885                         {
886                                 cpatt->Lines = markregion.MaxY - markregion.MinY + 1;
887
888                                 for (i = 0; i < cpatt->Tracks; i++)
889                                         cpatt->Notes[i] = patt->Notes[i + markregion.MinX] + markregion.MinY;
890
891                                 /* Snap marked region to Clipboard */
892                                 PatternSave (NULL, cpatt);
893
894                                 /* And then clear it */
895                                 for (i = 0; i < cpatt->Tracks; i++)
896                                         memset (cpatt->Notes[i], 0, sizeof (struct Note) * cpatt->Lines);
897
898                                 /* Free dummy pattern */
899                                 cpatt->Lines = 0;
900                                 for (i = 0; i < cpatt->Tracks; i++)
901                                         cpatt->Notes[i] = NULL;
902                                 xmRemPattern (si, -1, (ULONG)cpatt);
903
904                                 SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
905                                         PEA_Pattern,            patt,           /* Refresh display      */
906                                         PEA_MarkRegion,         NULL,           /* Stop marking         */
907                                         TAG_DONE);
908                         }
909                 }
910
911                 ReleaseSemaphore (&si->Lock);
912         }
913 }
914
915
916
917 static void PatternMiCopy (struct WinUserData *wud)
918 {
919         struct SongInfo *si;
920         struct Pattern *patt, *cpatt;
921         struct Rectangle markregion;
922         UWORD i;
923
924         if (si = xmLockActiveSong (SM_SHARED))
925         {
926                 patt = si->Patt[si->CurrentPatt];
927
928                 if (GetAttr (PEA_MarkRegion, wud->Gadgets[GD_PattEdit], (ULONG *)&markregion))
929                 {
930                         if ((markregion.MaxX == 0) && (markregion.MaxY == 0))
931                                 return; /* Not in mark mode */
932
933                         if (cpatt = xmAddPattern (si, -1,
934                                 PATTA_Tracks,   markregion.MaxX - markregion.MinX + 1,
935                                 PATTA_Lines,    0, /* Do not allocate anything */
936                                 PATTA_Name,             patt->Name,
937                                 TAG_DONE))
938                         {
939                                 cpatt->Lines = markregion.MaxY - markregion.MinY + 1;
940
941                                 for (i = 0; i < cpatt->Tracks; i++)
942                                         cpatt->Notes[i] = patt->Notes[i + markregion.MinX] + markregion.MinY;
943
944                                 PatternSave (NULL, cpatt);
945
946                                 /* Free dummy pattern */
947                                 cpatt->Lines = 0;
948                                 for (i = 0; i < cpatt->Tracks; i++)
949                                         cpatt->Notes[i] = NULL;
950                                 xmRemPattern (si, -1, (ULONG)cpatt);
951
952                                 SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
953                                         PEA_MarkRegion, NULL,   /* Stop marking */
954                                         TAG_DONE);
955                         }
956                 }
957
958                 ReleaseSemaphore (&si->Lock);
959         }
960 }
961
962
963
964 static void PatternMiPaste (struct WinUserData *wud)
965 {
966         struct SongInfo *si;
967         struct IFFHandle *iff;
968         struct Pattern *patt, *cpatt;
969         ULONG track, line;
970         UWORD i;
971
972         if (si = xmLockActiveSong (SM_EXCLUSIVE))
973         {
974                 patt = si->Patt[si->CurrentPatt];
975
976
977                 /* Get pattern in Clip */
978
979                 if (iff = AllocIFF())
980                 {
981                         InitIFFasClip (iff);
982                         if (iff->iff_Stream = (ULONG) OpenClipboard (PattSwitches.ClipboardUnit))
983                         {
984                                 if (!OpenIFF (iff, IFFF_READ))
985                                 {
986                                         if (cpatt = LoadPattern (si, -1, iff))
987                                         {
988                                                 if (GetAttr (PEA_CursTrack, wud->Gadgets[GD_PattEdit], &track) &&
989                                                         GetAttr (PEA_CursLine, wud->Gadgets[GD_PattEdit], &line))
990                                                 {
991                                                         /* Paste it into the pattern */
992
993                                                         for (i = 0; i < min (cpatt->Tracks, patt->Tracks - track); i++)
994                                                                 CopyMem (cpatt->Notes[i], patt->Notes[i + track] + line,
995                                                                         sizeof (struct Note) * min (patt->Lines - line, cpatt->Lines));
996
997                                                         SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
998                                                                 PEA_Pattern,    patt,   /* Refresh display      */
999                                                                 PEA_CursTrack,  min (track + cpatt->Tracks, patt->Tracks) - 1,
1000                                                                 PEA_CursLine,   min (line + cpatt->Lines, patt->Lines)  - 1,
1001                                                                 TAG_DONE);
1002                                                 }
1003
1004                                                 xmRemPattern (si, -1, (ULONG)cpatt);
1005                                         }
1006                                         CloseIFF (iff);
1007                                 }
1008
1009                                 CloseClipboard ((struct ClipboardHandle *)iff->iff_Stream);
1010                         }
1011
1012                         FreeIFF (iff);
1013                 }
1014
1015                 ReleaseSemaphore (&si->Lock);
1016         }
1017 }
1018
1019
1020
1021 static void PatternMiErase (struct WinUserData *wud)
1022 {
1023         struct SongInfo *si;
1024         struct Pattern *patt;
1025         struct Rectangle markregion;
1026         UWORD i;
1027
1028         if (si = xmLockActiveSong (SM_EXCLUSIVE))
1029         {
1030                 patt = si->Patt[si->CurrentPatt];
1031
1032                 if (GetAttr (PEA_MarkRegion, wud->Gadgets[GD_PattEdit], (ULONG *)&markregion))
1033                 {
1034                         if ((markregion.MaxX == 0) && (markregion.MaxY == 0))
1035                                 return; /* Not in mark mode */
1036
1037                         /* Clear marked region */
1038                         for (i = markregion.MinX; i <= markregion.MaxX; i++)
1039                                 memset (patt->Notes[i] + markregion.MinY, 0, sizeof (struct Note) *
1040                                         (markregion.MaxY - markregion.MinY + 1));
1041
1042                         SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
1043                                 PEA_Pattern,    patt,   /* Refresh display      */
1044                                 PEA_MarkRegion, NULL,   /* Stop marking         */
1045                                 TAG_DONE);
1046                 }
1047
1048                 ReleaseSemaphore (&si->Lock);
1049         }
1050 }
1051
1052
1053
1054 static void PatternMiUndo (struct WinUserData *wud)
1055 {
1056         SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
1057                 PEA_UndoChange, 1,
1058                 TAG_DONE);
1059 }
1060
1061
1062
1063 static void PatternMiRedo (struct WinUserData *wud)
1064 {
1065         SetGadgetAttrs (wud->Gadgets[GD_PattEdit], wud->Win, NULL,
1066                 PEA_UndoChange, -1,
1067                 TAG_DONE);
1068 }
1069
1070
1071
1072 static void PatternMiSettings (void)
1073 {
1074         NewWindow (WID_PATTPREFS);
1075 }