4 ** Copyright (C) 1995,96 Bernardo Innocenti
6 ** Sample editor class built on top of the "gadgetclass".
11 #include <exec/types.h>
12 #include <exec/memory.h>
13 #include <exec/nodes.h>
14 #include <exec/ports.h>
15 #include <utility/tagitem.h>
16 #include <utility/hooks.h>
17 #include <devices/inputevent.h>
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 #include <libraries/songclass.h>
28 #include <libraries/sampeditclass.h>
30 #include <clib/exec_protos.h>
31 #include <clib/intuition_protos.h>
32 #include <clib/graphics_protos.h>
33 #include <clib/utility_protos.h>
34 #include <clib/alib_protos.h>
36 #include <pragmas/exec_sysbase_pragmas.h>
37 #include <pragmas/intuition_pragmas.h>
38 #include <pragmas/graphics_pragmas.h>
39 #include <pragmas/utility_pragmas.h>
42 /* Some handy definitions missing in <devices/inputevent.h> */
43 #define IEQUALIFIER_SHIFT (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
44 #define IEQUALIFIER_ALT (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
45 #define IEQUALIFIER_COMMAND (IEQUALIFIER_LCOMMAND | IEQUALIFIER_RCOMMAND)
47 /* Scrolling control */
48 #define SCROLLED_VERT 1
49 #define SCROLLED_HORIZ 2
55 struct Instrument *Instr;
57 /* These are the actual Gadget position and size,
58 * regardless of any GREL_#? flags.
62 ULONG Flags; /* See definitions in <SampEditClass.h> */
64 UWORD XStart; /* X offset from the beginning of the sample */
65 UWORD XVisible; /* Number of visible samples */
68 UWORD CursX; /* Cursor position (which sample) */
69 UWORD CursScrPosX; /* Screen cursor position to erase it quickly. */
73 UWORD BackupCursX; /* Cursor drag undo */
76 ULONG XRatio; /* X Zoom ratio, 16:16 bit fixed point number */
78 /* Range marking info */
84 struct Rectangle RangeRect; /* Backup of range rect to erase it quickly. */
86 /* Rendering routine for the sample. */
87 void (*RenderFunc)(struct Note *note, UBYTE *s);
89 /* Pens used to draw various gadget elements */
96 /* For double click test */
97 ULONG DoubleClickSeconds;
98 ULONG DoubleClickMicros;
104 /* Function prototypes */
106 static ULONG __asm SampEditDispatcher (register __a0 Class *cl,
107 register __a2 struct ExtGadget *g,
108 register __a1 Msg msg);
109 static ULONG SAMP_HandleInputMethod (Class *cl, struct ExtGadget *g, struct gpInput *msg);
110 static void SAMP_RenderMethod (Class *cl, struct ExtGadget *g, struct gpRender *msg);
111 static ULONG SAMP_SetMethod (Class *cl, struct ExtGadget *g, struct opSet *msg);
112 static ULONG SAMP_GetMethod (Class *cl, struct ExtGadget *g, struct opGet *msg);
113 static ULONG SAMP_NewMethod (Class *cl, struct ExtGadget *g, struct opSet *msg);
115 static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect);
116 static BOOL CalcDisplaySize (struct SampEditData *sed, struct ExtGadget *g, struct GadgetInfo *gpi);
117 static void SaveUndo (struct SampEditData *sed);
118 static BOOL UndoChange (struct SampEditData *sed);
119 static BOOL RedoChange (struct SampEditData *sed);
120 static void FreeUndoBuffers (struct SampEditData *sed, BOOL freeall);
121 static BOOL MoveCursor (struct SampEditData *sed, WORD x /*, WORD y*/);
122 static void EraseCursor (struct RastPort *rp, struct SampEditData *sed);
123 static UWORD DrawCursor (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g);
124 static void DrawRange (struct RastPort *rp, struct SampEditData *sed);
125 static void ClearRange (struct RastPort *rp, struct SampEditData *sed);
126 static void RedrawAll (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g);
127 static void RedrawSample (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g);
128 static void DrawGrid (struct RastPort *rp, struct SampEditData *sed, UWORD min, UWORD max);
129 static UWORD ScrollDisplay (struct RastPort *rp, struct SampEditData *sed, struct ExtGadget *g,
130 UWORD lefttrack, UWORD topline);
131 static void NotifyCursor (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
132 static void NotifyHSlider (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags);
134 //static void __asm TimerIntServer (register __a1 struct Gadget *g);
136 struct Library * __asm _UserLibInit (register __a6 struct Library *mybase);
137 struct Library * __asm _UserLibCleanup (register __a6 struct Library *mybase);
138 struct IClass * __asm _GetClass (register __a6 struct Library *mybase);
145 #define SAMPVERS "_020"
150 const UBYTE LibName[] = "sampedit.gadget";
151 const UBYTE LibVer[] = { '$', 'V', 'E', 'R', ':', ' ' };
152 const UBYTE LibId[] = "sampedit.gadget" SAMPVERS " 1.1 (5.5.96) © 1995,96 by Bernardo Innocenti";
161 /* Get around a SAS/C bug which causes some annoying warnings
162 * with the library bases defined below.
165 #pragma msg 72 ignore push
168 struct ExecBase *SysBase = NULL;
169 struct IntuitionBase *IntuitionBase = NULL;
170 struct GfxBase *GfxBase = NULL;
171 struct Library *UtilityBase = NULL;
177 static struct IClass *SampEditClass = NULL;
180 static ULONG __asm SampEditDispatcher (register __a0 Class *cl,
181 register __a2 struct ExtGadget *g,
182 register __a1 Msg msg)
184 /* SampEdit Class Dispatcher entrypoint.
185 * Handle BOOPSI messages.
188 switch (msg->MethodID)
191 return SAMP_HandleInputMethod (cl, g, (struct gpInput *)msg);
195 SAMP_RenderMethod (cl, g, (struct gpRender *)msg);
200 struct SampEditData *sed = INST_DATA (cl, g);
202 if (CalcDisplaySize (sed, g, ((struct gpLayout *)msg)->gpl_GInfo))
203 NotifyHSlider (g, ((struct gpLayout *)msg)->gpl_GInfo, 0);
210 return SAMP_SetMethod (cl, g, (struct opSet *)msg);
213 return SAMP_GetMethod (cl, g, (struct opGet *)msg);
216 return (ULONG) SAMP_NewMethod (cl, g, (struct opSet *)msg);
219 /* NOTE: I'm falling through here! */
223 /* Unsupported method: let our superclass's
224 * dispatcher take a look at it.
226 return DoSuperMethodA (cl, (Object *)g, msg);
232 static ULONG SAMP_HandleInputMethod (Class *cl, struct ExtGadget *g, struct gpInput *msg)
234 struct SampEditData *sed = INST_DATA (cl, g);
235 struct InputEvent *ie = msg->gpi_IEvent;
236 WORD MouseX, MouseY; /* Mouse coordinates relative to editing area bounds */
239 ULONG result = GMR_MEACTIVE;
241 if (msg->MethodID == GM_GOACTIVE)
246 g->Flags |= GFLG_SELECTED;
251 /* Render active cursor */
252 if (rp = ObtainGIRPort (((struct gpInput *)msg)->gpi_GInfo))
254 DrawCursor (rp, sed, g);
259 /* Do not process InputEvent when the gadget has been
260 * activated by ActivateGadget().
262 if (!((struct gpInput *)msg)->gpi_IEvent)
265 /* Note: The input event that triggered the gadget
266 * activation (usually a mouse click) should be passed
267 * to the GM_HANDLEINPUT method, so we fall down to it.
272 MouseX = ((struct gpInput *)msg)->gpi_Mouse.X;
273 MouseY = ((struct gpInput *)msg)->gpi_Mouse.Y;
275 switch (ie->ie_Class)
279 /* Timer events are used to keep scrolling
280 * when the mouse is outside the bounds of the
281 * gadget. When a timer event is recevied and
282 * the mouse button is still pressed, we scroll the
285 if (sed->Flags & SEF_DRAGGING)
286 moved = MoveCursor (sed, MouseX);
290 case IECLASS_RAWMOUSE:
292 BOOL double_click = FALSE;
293 BOOL outside = (MouseX < 0) || (MouseX >= sed->GBounds.Width);
299 /* Abort cursor dragging operation and restore
300 * old cursor position.
302 if (sed->Flags & SEF_DRAGGING)
304 sed->CursX = sed->BackupCursX;
305 sed->XStart = sed->BackupXStart;
307 sed->Flags &= ~(SEF_DRAGGING | SEF_SCROLLING);
309 else result = GMR_REUSE;
314 /* Send final update to sliders */
315 scrolled = SCROLLED_HORIZ | SCROLLED_VERT;
316 sed->Flags &= ~(SEF_DRAGGING | SEF_SCROLLING);
321 /* Check if mouse click is still over the gadget */
325 /* Click outside editing area box:
326 * Check if click is really outside gadget box.
327 * If it is, deactivate the gadget and reuse the event.
328 * Notify application if it is interested in
329 * hearing IDCMP_GADGETUP codes.
332 if ((MouseX < 0) || (MouseX >= sed->GBounds.Width) ||
333 (MouseY < 0) || (MouseY >= sed->GBounds.Height))
334 result = GMR_REUSE | GMR_VERIFY;
339 /* Backup cursor position for undo feature */
340 sed->BackupCursX = sed->CursX;
341 sed->BackupXStart = sed->XStart;
343 /* Start cursor drag mode */
344 sed->Flags |= SEF_DRAGGING;
347 /* Check for double clicking */
349 if (DoubleClick (sed->DoubleClickSeconds, sed->DoubleClickMicros,
350 ie->ie_TimeStamp.tv_secs, ie->ie_TimeStamp.tv_micro))
353 sed->DoubleClickSeconds = ie->ie_TimeStamp.tv_secs;
354 sed->DoubleClickMicros = ie->ie_TimeStamp.tv_micro;
356 /* NOTE: I'm falling through here! */
360 if (sed->Flags & SEF_DRAGGING)
363 sed->Flags |= SEF_SCROLLING;
365 moved = MoveCursor (sed, MouseX);
367 else if (!moved && double_click)
369 if (sed->Flags & SEF_MARKING)
370 sed->Flags &= ~SEF_MARKING;
373 sed->Flags |= SEF_MARKING;
374 sed->RangeStartX = sed->CursX;
377 if (rp = ObtainGIRPort (msg->gpi_GInfo))
379 if (!(sed->Flags & SEF_MARKING))
380 ClearRange (rp, sed);
382 DrawCursor (rp, sed, g);
397 } /* End switch (ie->ie_Class) */
403 if (rp = ObtainGIRPort (msg->gpi_GInfo))
405 scrolled |= DrawCursor (rp, sed, g);
409 /* Broadcast notification to our target object. */
410 NotifyCursor (g, msg->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
414 NotifyHSlider (g, msg->gpi_GInfo, (ie->ie_Code & IECODE_UP_PREFIX) ? 0 : OPUF_INTERIM);
421 static void SAMP_RenderMethod (Class *cl, struct ExtGadget *g, struct gpRender *msg)
423 struct SampEditData *sed = INST_DATA (cl, g);
425 /* We do not support GREDRAW_UPDATE and GREDRAW_TOGGLE */
427 if (msg->gpr_Redraw == GREDRAW_REDRAW)
429 /* Recalculate the display size only on V37.
430 * As of V39, Intuition supports GM_LAYOUT, which
431 * allows a more optimized way to handle dynamic resizing.
433 if (IntuitionBase->LibNode.lib_Version < 39)
435 if (CalcDisplaySize (sed, g, ((struct gpRender *)msg)->gpr_GInfo))
436 NotifyHSlider (g, msg->gpr_GInfo, 0);
439 RedrawAll (msg->gpr_RPort, sed, g);
445 static ULONG SAMP_SetMethod (Class *cl, struct ExtGadget *g, struct opSet *msg)
447 struct SampEditData *sed = INST_DATA (cl, g);
449 *tstate = msg->ops_AttrList;
455 while (ti = NextTagItem(&tstate))
460 if (sed->CursXPos != ti->ti_Data)
462 sed->CursXPos = ti->ti_Data;
468 if (sed->XStart != ti->ti_Data)
470 sed->XStart = ti->ti_Data;
476 if (sed->XRatio != ti->ti_Data)
478 sed->XRatio = ti->ti_Data;
491 case SEA_ScrollRight:
492 if (sed->XStart + sed->XVisible < sed->Lenght)
501 struct Rectangle *region = (struct Rectangle *)ti->ti_Data;
503 if (!region) /* End mark mode */
505 sed->Flags &= ~SEF_MARKING;
507 else if (region == (struct Rectangle *)-1) /* Toggle mark mode */
509 sed->Flags ^= SEF_MARKING;
510 sed->RangeStartX = sed->XStart;
511 // sed->RangeStartY = sed->YStart;
513 else /* Start mark mode */
515 memcpy (&sed->RangeStartX, region, sizeof (struct Rectangle));
516 sed->CursX = region->MaxX;
517 sed->Flags |= PEF_MARKING;
520 if (rp = ObtainGIRPort (((struct opSet *)msg)->ops_GInfo))
522 if (!(sed->Flags & PEF_MARKING))
523 ClearRange (rp, sed);
525 DrawCursor (rp, sed, g);
534 sed->Instrument = (struct Instrument *) ti->ti_Data;
536 FreeUndoBuffers (sed, TRUE);
538 if (sed->Instrument == NULL)
539 sed->CursX = sed->VisibleX = 0;
542 /* Recalculate pattern dimensions */
543 if (CalcDisplaySize (sed, g, msg->ops_GInfo))
544 NotifyHSlider (g, msg->ops_GInfo, 0);
548 case SEA_MaxUndoLevels:
549 sed->MaxUndoLevels = ti->ti_Data;
550 FreeUndoBuffers (sed, FALSE);
554 sed->MaxUndoMem = ti->ti_Data;
556 /* Unlimited undo memory */
557 if (sed->MaxUndoMem == 0) sed->MaxUndoMem = ~0;
559 FreeUndoBuffers (sed, FALSE);
563 case SEA_BackgroundPen:
564 if (sed->BackgroundPen != ti->ti_Data)
566 sed->BackgroundPen = ti->ti_Data;
572 if (sed->LinePen != ti->ti_Data)
574 sed->LinePen = ti->ti_Data;
580 if (sed->FillPen != ti->ti_Data)
582 sed->FillPen = ti->ti_Data;
588 if (sed->GridPen != ti->ti_Data)
590 sed->GridPen = ti->ti_Data;
599 } /* End while (NextTagItem()) */
602 result = DoSuperMethodA (cl, (Object *)g, msg);
604 if (redraw_all || scroll || move_cursor || zoom)
608 if (rp = ObtainGIRPort (msg->ops_GInfo))
612 DoMethod ((Object *)g, GM_RENDER, ((struct opSet *)msg)->ops_GInfo, rp, GREDRAW_REDRAW);
616 scrolled = ScrollPattern (rp, sed, g, lefttrack, topline);
619 scrolled |= DrawCursor (rp, sed, g);
625 NotifyVSlider (g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
627 NotifyHSlider (g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
629 if (move_cursor || scrolled)
630 NotifyCursor (g, ((struct opSet *)msg)->ops_GInfo, msg->MethodID == OM_UPDATE ? (((struct opUpdate *)msg)->opu_Flags) : 0);
636 static LONG SAMP_GetMethod (Class *cl, struct ExtGadget *g, struct opGet *msg)
638 struct SampEditData *sed = INST_DATA (cl, g);
640 switch (msg->opg_AttrID)
643 *msg->opg_Storage = (ULONG) sed->CursXPos;
647 *msg->opg_Storage = (ULONG) sed->CursYPos;
651 *msg->opg_Storage = (ULONG) sed->XStart;
655 *msg->opg_Storage = (ULONG) sed->YStart;
659 *msg->opg_Storage = (ULONG) sed->XRatio;
663 *msg->opg_Storage = (ULONG) sed->YRatio;
668 struct Rectangle *region = (struct Rectangle *) *msg->opg_Storage);
670 region->MinX = min (sed->RangeStartX, sed->RangeEndX);
671 region->MaxX = max (sed->RangeStartX, sed->RangeEndX);
672 region->MinY = min (sed->RangeStartY, sed->RangeEndY);
673 region->MaxY = max (sed->RangeStartY, sed->RangeEndY);
678 *msg->opg_Storage = (ULONG) sed->Flags;
682 *msg->opg_Storage = (ULONG) sed->Instrument;
686 return DoSuperMethodA (cl, (Object *)g, msg);
694 static ULONG SAMP_NewMethod (Class *cl, struct ExtGadget *g, struct opSet *msg)
696 struct SampEditData *sed;
699 if (!(g = DoSuperMethodA (cl, (Object *)g, msg)))
702 sed = INST_DATA (cl, g); /* Get pointer to instance data */
704 memset (sed, 0, sizeof (struct SampEditData));
706 /* We are going to use ScrollRaster() in this gadget... */
708 if (g->Flags & GFLG_EXTENDED)
709 g->MoreFlags |= GMORE_SCROLLRASTER;
711 g->Flags |= GFLG_TABCYCLE | GFLG_RELSPECIAL;
713 /* Initialize our lists */
714 NEWLIST ((struct List *)&sed->UndoList);
715 NEWLIST ((struct List *)&sed->RedoList);
718 /* Initialize attributes */
720 sed->Instrument = (struct Instrument *) GetTagData (SEA_Instrument, NULL, msg->ops_AttrList);
721 sed->BackgroundPen = GetTagData (SEA_BackgroundPen, 0, msg->ops_AttrList);
722 sed->LinePen = GetTagData (SEA_LinePen, 2, msg->ops_AttrList);
723 sed->FillPen = GetTagData (SEA_FillPen, 1, msg->ops_AttrList);
724 sed->GridPen = GetTagData (SEA_GridPen, 3, msg->ops_AttrList);
725 sed->MaxUndoLevels = GetTagData (SEA_MaxUndoLevels, 8, ((struct opSet *)msg)->ops_AttrList);
726 sed->MaxUndoMem = GetTagData (SEA_MaxUndoMem, 32768, ((struct opSet *)msg)->ops_AttrList);
727 sed->Flags = GetTagData (SEA_Flags, 0, ((struct opSet *)msg)->ops_AttrList);
729 /* Unlimited undo memory */
730 if (sed->MaxUndoMem == 0) sed->MaxUndoMem = ~0;
737 static void NotifyCursor (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
739 /* Always sends notification if the gadget has the GACT_IMMEDIATE
740 * flag set. If it isn't, the editor will report its cursor
741 * position only the last time it updates the cursor position.
743 if ((g->Activation & GACT_IMMEDIATE) || !(flags & OPUF_INTERIM))
745 struct SampEditData *sed = INST_DATA (OCLASS(g), g);
747 NotifyAttrChanges ((Object *)g, gi, flags,
748 SEA_CursXPos, sed->CursXPos,
749 SEA_CursYPos, sed->CursYPos,
757 static void NotifyHSlider (struct ExtGadget *g, struct GadgetInfo *gi, ULONG flags)
759 struct SampEditData *sed = INST_DATA (OCLASS(g), g);
761 NotifyAttrChanges ((Object *)g, gi, flags,
762 SEA_XStart, sed->XStart;
769 static void GetGadgetBox (struct GadgetInfo *ginfo, struct ExtGadget *g, struct IBox *rect)
771 /* This function gets the actual IBox where a gadget exists
772 * in a window. The special cases it handles are all the REL#?
773 * (relative positioning flags).
775 * The function takes a struct GadgetInfo pointer, a struct Gadget
776 * pointer, and a struct IBox pointer. It uses the window and
777 * gadget to fill in the IBox.
780 rect->Left = g->LeftEdge;
781 if (g->Flags & GFLG_RELRIGHT) rect->Left += ginfo->gi_Domain.Width - 1;
783 rect->Top = g->TopEdge;
784 if (g->Flags & GFLG_RELBOTTOM) rect->Top += ginfo->gi_Domain.Height - 1;
786 rect->Width = g->Width;
787 if (g->Flags & GFLG_RELWIDTH) rect->Width += ginfo->gi_Domain.Width;
789 rect->Height = g->Height;
790 if (g->Flags & GFLG_RELHEIGHT) rect->Height += ginfo->gi_Domain.Height;
795 XDEF HOOKCALL struct Library *_UserLibInit (REG(a6,struct Library *mybase))
797 SysBase = *((struct ExecBase **)4);
799 IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 37);
800 GfxBase = (struct GfxBase *) OpenLibrary ("graphics.library", 37);
801 UtilityBase = OpenLibrary ("utility.library", 37);
803 if (!(IntuitionBase && GfxBase && UtilityBase && KeymapBase))
805 _UserLibCleanup (mybase);
809 if (SampEditClass = MakeClass (SAMPEDITCLASS, GADGETCLASS, NULL, sizeof (struct SampEditData), 0))
811 SampEditClass->cl_Dispatcher.h_Entry = (ULONG (*)()) SampEditDispatcher;
812 AddClass (SampEditClass);
816 _UserLibCleanup (mybase);
825 XDEF HOOKCALL struct Library *_UserLibCleanup (REG(a6,struct Library *mybase))
828 if (!FreeClass (SampEditClass))
831 /* Passing NULL to CloseLibrary() is safe */
832 CloseLibrary ((struct Library *)UtilityBase);
833 CloseLibrary ((struct Library *)GfxBase);
834 CloseLibrary ((struct Library *)IntuitionBase);
841 XDEF HOOKCALL struct IClass *_GetEngine (REG(a6,struct Library *mybase))
843 return (SampEditClass);