when dropping into user mode make sure endian is set
[AROS.git] / workbench / classes / zune / betterstring / mcc / Dispatcher.c
blob2c798ed822a2cfbccbe8fa744d13884a78ed05c9
1 /***************************************************************************
3 BetterString.mcc - A better String gadget MUI Custom Class
4 Copyright (C) 1997-2000 Allan Odgaard
5 Copyright (C) 2005-2013 by BetterString.mcc Open Source Team
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 BetterString class Support Site: http://www.sf.net/projects/bstring-mcc/
19 $Id$
21 ***************************************************************************/
23 #include <string.h>
25 #include <clib/alib_protos.h>
26 #include <proto/exec.h>
27 #include <proto/intuition.h>
28 #include <proto/muimaster.h>
29 #include <proto/graphics.h>
30 #include <proto/utility.h>
31 #include <proto/locale.h>
33 #include "private.h"
35 static IPTR mNew(struct IClass *cl, Object *obj, struct opSet *msg)
37 ENTER();
39 if((obj = (Object *)DoSuperMethodA(cl, obj, (Msg)msg)) != NULL)
41 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
43 if((data->Contents = AllocContentString(40)) != NULL)
45 struct TagItem *tag;
47 set(obj, MUIA_FillArea, FALSE);
49 // muimaster V20 is MUI 3.9
50 data->mui39 = LIB_VERSION_IS_AT_LEAST(MUIMasterBase, 20, 0);
51 // everything beyond muimaster 20.5500 is considered to be MUI4
52 data->mui4x = LIB_VERSION_IS_AT_LEAST(MUIMasterBase, 20, 5500);
54 data->locale = OpenLocale(NULL);
56 if((tag = FindTagItem(MUIA_Background, msg->ops_AttrList)) != NULL)
58 setFlag(data->Flags, FLG_OwnBackground);
59 data->OwnBackground = (STRPTR)tag->ti_Data;
62 mSet(cl, obj, (struct opSet *)msg);
64 data->BufferPos = 0;
66 else
68 CoerceMethod(cl, obj, OM_DISPOSE);
69 obj = NULL;
73 RETURN(obj);
74 return((IPTR)obj);
77 static IPTR mDispose(struct IClass *cl, Object *obj, Msg msg)
79 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
81 ENTER();
83 if(isFlagSet(data->Flags, FLG_WindowSleepNotifyAdded))
85 E(DBF_INPUT, "MUIA_Window_Sleep notify still active at OM_DISPOSE!!");
88 FreeContentString(data->Contents);
89 data->Contents = NULL;
91 FreeContentString(data->Original);
92 data->Original = NULL;
94 FreeContentString(data->Undo);
95 data->Undo = NULL;
97 if(data->FNCBuffer != NULL)
99 SharedPoolFree(data->FNCBuffer);
100 data->FNCBuffer = NULL;
103 if(data->locale != NULL)
105 CloseLocale(data->locale);
106 data->locale = NULL;
109 LEAVE();
110 return DoSuperMethodA(cl, obj, msg);
113 static IPTR mExport(struct IClass *cl, Object *obj, struct MUIP_Export *msg)
115 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
116 ULONG id;
118 ENTER();
120 if((id = (muiNotifyData(obj)->mnd_ObjectID)) != 0)
121 DoMethod(msg->dataspace, MUIM_Dataspace_Add, data->Contents, strlen(data->Contents)+1, id);
123 LEAVE();
124 return 0;
127 static IPTR mImport(UNUSED struct IClass *cl, Object *obj, struct MUIP_Import *msg)
129 ULONG id;
131 ENTER();
133 if((id = (muiNotifyData(obj)->mnd_ObjectID)) != 0)
135 STRPTR contents = (STRPTR)DoMethod(msg->dataspace, MUIM_Dataspace_Find, id);
137 // if(contents)
138 set(obj, MUIA_String_Contents, contents);
141 LEAVE();
142 return 0;
145 void AddWindowSleepNotify(struct IClass *cl, Object *obj)
147 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
149 ENTER();
151 // we must check for a successful MUIM_Setup, because this function might be called during
152 // OM_NEW and _win(obj) is not yet valid at that time
153 if(isFlagClear(data->Flags, FLG_WindowSleepNotifyAdded) && isFlagSet(data->Flags, FLG_Setup) && _win(obj) != NULL)
155 if(data->SelectOnActive == TRUE || isFlagSet(data->Flags, FLG_ForceSelectOn))
157 // !!! CAUTION !!!
158 // Ugly workaround for an ancient bug in MUI
159 // MUIbase <= 2.11 adds some notifies for certain attributes which in turn
160 // modify MUIA_Window_Sleep and hence trigger our own notify for this
161 // attribute.
162 // Removing our own notify will not remove it immediately but mark it as
163 // "killed" only by MUI. The removal happens when the notifies are checked
164 // for triggers. The problem arises if executing one notify triggers yet
165 // another notification handling on the same object. In this case the nested
166 // call will do the same removal as the first call is about to do next. This
167 // will cause a double Remove() and double free of memory later in the first
168 // call as here the pointer to the next notify to be handled has already been
169 // obtained and will be used without further checks in the next iteration.
170 // All this only happens if the removed notify directly follows the notify
171 // which causes the removal. Thus we add a dummy notify to produce a "hole"
172 // in the notify list and to let the nested notification check do its
173 // removal work without causing a bad impact on the first check. This "hole"
174 // just consists of another notify which never gets triggered. And even if
175 // it would get triggered it will not cause a nested notify check. Thus the
176 // first check will see this "hole" first before finally skipping the just
177 // removed notify.
178 // NOTE: this is neither a bug in MUIbase nor in BetterString but an ancient
179 // bug in MUI itself as it does not take into account that a set() may cause
180 // nested notifications which in turn may be removed inbetween!
181 BOOL safeNotifies;
183 #if defined(__amigaos3__) || defined(__amigaos4__)
184 if(LIB_VERSION_IS_AT_LEAST(MUIMasterBase, 20, 5824))
186 // MUI4 for AmigaOS is safe for V20.5824+
187 safeNotifies = TRUE;
189 else if(LIB_VERSION_IS_AT_LEAST(MUIMasterBase, 20, 2346) && LIBREV(MUIMasterBase) < 5000)
191 // MUI3.9 for AmigaOS is safe for V20.2346+
192 safeNotifies = TRUE;
194 else
196 // MUI 3.8 and older version of MUI 3.9 or MUI4 are definitely unsafe
197 safeNotifies = FALSE;
199 #else
200 // MorphOS and AROS must be considered unsafe unless someone from the
201 // MorphOS/AROS team confirms that removing notifies in nested OM_SET
202 // calls is safe.
203 safeNotifies = FALSE;
204 #endif
206 // add the dummy notify only once
207 if(safeNotifies == FALSE && isFlagClear(data->Flags, FLG_DummyNotifyAdded))
209 // add a notify for an attribute which will *NEVER* be modified, thus the
210 // trigger action will never be executed as well
211 DoMethod(_win(obj), MUIM_Notify, MUIA_BetterString_Nop, MUIV_EveryTime, obj, 5, MUIM_Set, MUIA_NoNotify, TRUE, MUIA_BetterString_Nop, MUIV_TriggerValue);
212 setFlag(data->Flags, FLG_DummyNotifyAdded);
213 D(DBF_INPUT, "added dummy notify");
216 // If the "select on active" feature is active we must be notified in case our
217 // window is put to sleep to be able to deactivate the feature, because waking
218 // the window up again will let ourself go active again and we will select the
219 // complete content, even if it was not selected before. See YAM ticket #360
220 // for details.
221 // We must use a private attribute here, because the public attribute will remove
222 // the notify again as soon as it is triggered.
223 DoMethod(_win(obj), MUIM_Notify, MUIA_Window_Sleep, MUIV_EveryTime, obj, 3, MUIM_Set, MUIA_BetterString_InternalSelectOnActive, MUIV_NotTriggerValue);
224 setFlag(data->Flags, FLG_WindowSleepNotifyAdded);
225 D(DBF_INPUT, "added MUIA_Window_Sleep notify");
229 LEAVE();
232 void RemWindowSleepNotify(struct IClass *cl, Object *obj)
234 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
236 ENTER();
238 // we must check for a successful MUIM_Setup, because this function might be called during
239 // OM_NEW and _win(obj) is not yet valid at that time
240 if(isFlagSet(data->Flags, FLG_WindowSleepNotifyAdded) && isFlagSet(data->Flags, FLG_Setup) && _win(obj) != NULL)
242 // remove the notify again
243 D(DBF_INPUT, "remove MUIA_Window_Sleep notify");
244 if(DoMethod(_win(obj), MUIM_KillNotifyObj, MUIA_Window_Sleep, obj) == 0)
245 E(DBF_INPUT, "removing MUIA_Window_Sleep notify failed?");
246 clearFlag(data->Flags, FLG_WindowSleepNotifyAdded);
249 LEAVE();
252 static IPTR mSetup(struct IClass *cl, Object *obj, struct MUI_RenderInfo *rinfo)
254 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
255 IPTR rc = FALSE;
257 ENTER();
259 InitConfig(obj, data);
261 if(DoSuperMethodA(cl, obj, (Msg)rinfo))
263 // tell MUI we know how to indicate the active state
264 _flags(obj) |= (1<<7);
266 // remember that we went through MUIM_Setup
267 setFlag(data->Flags, FLG_Setup);
269 data->ehnode.ehn_Priority = 0;
270 data->ehnode.ehn_Flags = MUI_EHF_GUIMODE;
271 data->ehnode.ehn_Object = obj;
272 data->ehnode.ehn_Class = cl;
273 data->ehnode.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
275 // setup the selection pointer
276 if(data->SelectPointer == TRUE)
278 data->ehnode.ehn_Events |= IDCMP_MOUSEMOVE;
279 SetupSelectPointer(data);
282 DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);
284 AddWindowSleepNotify(cl, obj);
286 rc = TRUE;
288 else
290 FreeConfig(muiRenderInfo(obj), data);
293 RETURN(rc);
294 return rc;
297 static IPTR mCleanup(struct IClass *cl, Object *obj, Msg msg)
299 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
301 ENTER();
303 // cleanup the selection pointer
304 CleanupSelectPointer(data);
306 RemWindowSleepNotify(cl, obj);
308 DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
310 FreeConfig(muiRenderInfo(obj), data);
312 // forget that we went through MUIM_Setup
313 clearFlag(data->Flags, FLG_Setup);
315 LEAVE();
316 return DoSuperMethodA(cl, obj, msg);
319 static IPTR mAskMinMax(struct IClass *cl, Object *obj, struct MUIP_AskMinMax *msg)
321 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
322 LONG Height;
324 ENTER();
326 DoSuperMethodA(cl, obj, (Msg)msg);
328 Height = _font(obj)->tf_YSize;
329 msg->MinMaxInfo->MinHeight += Height;
330 msg->MinMaxInfo->DefHeight += Height;
331 msg->MinMaxInfo->MaxHeight += Height;
333 if(data->Width)
335 ULONG width;
337 SetFont(&data->rport, _font(obj));
338 width = data->Width * TextLength(&data->rport, "n", 1);
340 msg->MinMaxInfo->MinWidth += width;
341 msg->MinMaxInfo->DefWidth += width;
342 msg->MinMaxInfo->MaxWidth += width;
344 else
346 msg->MinMaxInfo->MinWidth += 10;
347 msg->MinMaxInfo->DefWidth += 100;
348 msg->MinMaxInfo->MaxWidth += MBQ_MUI_MAXMAX;
351 LEAVE();
352 return 0;
355 static IPTR mShow(struct IClass *cl, Object *obj, Msg msg)
357 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
358 struct BitMap *friendBMp = _rp(obj)->BitMap;
359 WORD width, height, depth;
361 ENTER();
363 DoSuperMethodA(cl, obj, msg);
365 width = _mwidth(obj);
366 height = _font(obj)->tf_YSize;
367 depth = ((struct Library *)GfxBase)->lib_Version >= 39 ? GetBitMapAttr(friendBMp, BMA_DEPTH) : friendBMp->Depth;
369 InitRastPort(&data->rport);
370 data->rport.BitMap = MUIG_AllocBitMap(width+40, height, depth, 0, friendBMp);
371 SetFont(&data->rport, _font(obj));
372 SetDrMd(&data->rport, JAM1);
374 setFlag(data->Flags, FLG_Shown);
376 RETURN(TRUE);
377 return TRUE;
380 static IPTR mHide(struct IClass *cl, Object *obj, Msg msg)
382 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
384 ENTER();
386 clearFlag(data->Flags, FLG_Shown);
388 // hide the selection pointer
389 HideSelectPointer(obj, data);
391 MUIG_FreeBitMap(data->rport.BitMap);
393 LEAVE();
394 return DoSuperMethodA(cl, obj, msg);
397 static IPTR mDraw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
399 ENTER();
401 DoSuperMethodA(cl, obj, (Msg)msg);
403 if(isFlagSet(msg->flags, MADF_DRAWUPDATE) || isFlagSet(msg->flags, MADF_DRAWOBJECT))
405 PrintString(cl, obj);
408 LEAVE();
409 return 0;
412 static IPTR mHandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
414 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
415 IPTR result;
417 ENTER();
419 if(isFlagSet(data->Flags, FLG_Ghosted) || isFlagClear(data->Flags, FLG_Shown))
421 result = 0;
423 else
425 ULONG display_pos = data->DisplayPos;
427 result = mHandleInput(cl, obj, msg);
428 if(display_pos != data->DisplayPos)
429 set(obj, MUIA_String_DisplayPos, data->DisplayPos);
431 if(!result && data->ForwardObject != NULL)
433 ULONG attr = 0;
435 switch(msg->muikey)
437 case MUIKEY_TOP:
438 attr = MUIV_List_Active_Top;
439 break;
441 case MUIKEY_BOTTOM:
442 attr = MUIV_List_Active_Bottom;
443 break;
445 case MUIKEY_UP:
446 attr = MUIV_List_Active_Up;
447 break;
449 case MUIKEY_DOWN:
450 attr = MUIV_List_Active_Down;
451 break;
453 case MUIKEY_PAGEUP:
454 attr = MUIV_List_Active_PageUp;
455 break;
457 case MUIKEY_PAGEDOWN:
458 attr = MUIV_List_Active_PageDown;
459 break;
462 if(attr != 0)
464 set(data->ForwardObject, MUIA_List_Active, attr);
465 result = MUI_EventHandlerRC_Eat;
470 RETURN(result);
471 return result;
474 static IPTR mGoActive(struct IClass *cl, Object *obj, UNUSED Msg msg)
476 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
478 ENTER();
480 D(DBF_INPUT, "GoActive: %08lx %08lx", obj, data->Flags);
482 FreeContentString(data->Original);
483 if((data->Original = AllocContentString(strlen(data->Contents)+1)) != NULL)
484 strlcpy(data->Original, data->Contents, strlen(data->Contents+1));
486 // select everything if this is necessary or requested
487 if((data->SelectOnActive == TRUE && isFlagClear(data->Flags, FLG_ForceSelectOff)) ||
488 isFlagSet(data->Flags, FLG_ForceSelectOn))
490 // If the active flag is still clear we have been activated by keyboard or by
491 // the application. Otherwise this method is called due to activation by mouse
492 // and we must skip the "select on active" stuff as this has been done already.
493 if(isFlagClear(data->Flags, FLG_Active))
495 DoMethod(obj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_SelectAll);
499 // now declare ourself as active
500 setFlag(data->Flags, FLG_Active);
501 setFlag(data->Flags, FLG_FreshActive);
503 if(isFlagClear(data->Flags, FLG_OwnBackground) && data->mui4x == FALSE)
504 set(obj, MUIA_Background, data->ActiveBackground);
505 else if(data->mui4x == TRUE)
506 set(obj, MUIA_Background, MUII_StringActiveBack);
507 else
508 MUI_Redraw(obj, MADF_DRAWUPDATE);
510 RETURN(TRUE);
511 return TRUE;
514 static IPTR mGoInactive(struct IClass *cl, Object *obj, UNUSED Msg msg)
516 struct InstData *data = (struct InstData *)INST_DATA(cl, obj);
518 ENTER();
520 D(DBF_INPUT, "GoInActive: %08lx", obj);
522 // clean an eventually marked block and the
523 // active state flag of the gadget
524 clearFlag(data->Flags, FLG_BlockEnabled);
525 clearFlag(data->Flags, FLG_Active);
526 clearFlag(data->Flags, FLG_FreshActive);
528 if(isFlagSet(data->Flags, FLG_OwnBackground))
530 set(obj, MUIA_Background, data->OwnBackground);
531 // MUI 3.8 needs an explicit refresh
532 if(data->mui39 == FALSE && data->mui4x == FALSE)
533 MUI_Redraw(obj, MADF_DRAWUPDATE);
535 else if(data->mui4x == TRUE)
536 set(obj, MUIA_Background, MUII_StringBack);
537 else
538 set(obj, MUIA_Background, data->InactiveBackground);
540 RETURN(TRUE);
541 return TRUE;
544 DISPATCHER(_Dispatcher)
546 IPTR result;
548 ENTER();
550 switch(msg->MethodID)
552 case OM_NEW:
553 result = mNew(cl, obj, (struct opSet *)msg);
554 break;
556 case MUIM_Setup:
557 result = mSetup(cl, obj, (struct MUI_RenderInfo *)msg);
558 break;
560 case MUIM_Show:
561 result = mShow(cl, obj, msg);
562 break;
564 case MUIM_AskMinMax:
565 result = mAskMinMax(cl, obj, (struct MUIP_AskMinMax *)msg);
566 break;
568 case MUIM_Draw:
569 result = mDraw(cl, obj, (struct MUIP_Draw *)msg);
570 break;
572 case OM_GET:
573 result = mGet(cl, obj, (struct opGet *)msg);
574 break;
576 case OM_SET:
577 mSet(cl, obj, (struct opSet *)msg);
578 result = DoSuperMethodA(cl, obj, msg);
579 break;
581 case MUIM_HandleEvent:
582 result = mHandleEvent(cl, obj, (struct MUIP_HandleEvent *)msg);
583 break;
585 case MUIM_GoActive:
586 result = mGoActive(cl, obj, msg);
587 break;
589 case MUIM_GoInactive:
590 result = mGoInactive(cl, obj, msg);
591 break;
593 case MUIM_Hide:
594 result = mHide(cl, obj, msg);
595 break;
597 case MUIM_Cleanup:
598 result = mCleanup(cl, obj, msg);
599 break;
601 case OM_DISPOSE:
602 result = mDispose(cl, obj, msg);
603 break;
605 case MUIM_Export:
606 result = mExport(cl, obj, (struct MUIP_Export *)msg);
607 break;
609 case MUIM_Import:
610 result = mImport(cl, obj, (struct MUIP_Import *)msg);
611 break;
613 case MUIM_BetterString_ClearSelected:
615 // forward the clear request to our new DoAction method
616 // which in fact will do the very same, but a bit more clever
617 DoMethod(obj, MUIM_BetterString_DoAction, MUIV_BetterString_DoAction_Delete);
618 result = TRUE;
620 break;
622 case MUIM_BetterString_Insert:
623 result = mInsert(cl, obj, (struct MUIP_BetterString_Insert *)msg);
624 break;
626 case MUIM_BetterString_DoAction:
627 result = mDoAction(cl, obj, (struct MUIP_BetterString_DoAction *)msg);
628 break;
630 case MUIM_BetterString_FileNameStart:
631 result = mFileNameStart((struct MUIP_BetterString_FileNameStart *)msg);
632 break;
634 default:
635 result = DoSuperMethodA(cl, obj, msg);
636 break;
639 RETURN(result);
640 return(result);