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/
21 ***************************************************************************/
26 #include <clib/alib_protos.h>
27 #include <clib/macros.h>
28 #include <proto/exec.h>
29 #include <proto/graphics.h>
30 #include <proto/intuition.h>
31 #include <proto/muimaster.h>
32 #include <proto/keymap.h>
33 #include <proto/layers.h>
34 #include <proto/locale.h>
35 #include <proto/dos.h>
39 #include "SDI_stdarg.h"
41 #define BlockEnabled(data) (isFlagSet((data)->Flags, FLG_BlockEnabled) && (data)->BlockStart != (data)->BlockStop)
43 #if defined(__amigaos4__) || defined(__MORPHOS__)
44 static int VARARGS68K
MySPrintf(char *buf
, const char *fmt
, ...)
49 RawDoFmt(fmt
, VA_ARG(args
, void *), NULL
, (STRPTR
)buf
);
54 #elif defined(__AROS__)
55 #define MySPrintf __sprintf /* from amiga lib */
57 static int STDARGS
MySPrintf(char *buf
, const char *fmt
, ...)
59 static const UWORD PutCharProc
[2] = {0x16C0,0x4E75};
60 /* dirty hack to avoid assembler part :-)
66 RawDoFmt(fmt
, args
, (void (*)(void))PutCharProc
, (STRPTR
)buf
);
73 static void AddToUndo(struct InstData
*data
)
77 FreeContentString(data
->Undo
);
79 if((data
->Undo
= AllocContentString(strlen(data
->Contents
)+1)) != NULL
)
81 strlcpy(data
->Undo
, data
->Contents
, strlen(data
->Contents
)+1);
82 data
->UndoPos
= data
->BufferPos
;
83 clearFlag(data
->Flags
, FLG_RedoAvailable
);
89 static WORD
AlignOffset(Object
*obj
, struct InstData
*data
)
91 WORD width
= _mwidth(obj
);
96 if(data
->Alignment
!= MUIV_String_Format_Left
)
98 STRPTR text
= data
->Contents
+data
->DisplayPos
;
99 UWORD StrLength
= strlen(text
);
100 UWORD length
, textlength
, crsr_width
;
101 struct TextExtent tExtend
;
103 SetFont(&data
->rport
, _font(obj
));
104 length
= TextFit(&data
->rport
, text
, StrLength
, &tExtend
, NULL
, 1, width
, _font(obj
)->tf_YSize
);
105 textlength
= TextLength(&data
->rport
, text
, length
);
107 crsr_width
= isFlagSet(data
->Flags
, FLG_Active
) ? TextLength(&data
->rport
, (*(data
->Contents
+data
->BufferPos
) == '\0') ? (char *)"n" : (char *)(data
->Contents
+data
->BufferPos
), 1) : 0;
108 if(crsr_width
&& BlockEnabled(data
) == FALSE
&& data
->BufferPos
== data
->DisplayPos
+StrLength
)
110 textlength
+= crsr_width
;
113 switch(data
->Alignment
)
115 case MUIV_String_Format_Center
:
116 offset
= (width
- textlength
)/2;
118 case MUIV_String_Format_Right
:
119 offset
= (width
- textlength
);
128 static BOOL
Reject(UBYTE code
, STRPTR reject
)
136 while(*reject
!= '\0')
138 if(code
== *reject
++)
150 static BOOL
Accept(UBYTE code
, STRPTR accept
)
152 return(accept
? !Reject(code
, accept
) : TRUE
);
155 static UWORD
DecimalValue(UBYTE code
)
157 if(code
>= '0' && code
<= '9')
159 if(code
>= 'a' && code
<= 'f')
160 return(code
- 'a' + 10);
161 if(code
>= 'A' && code
<= 'F')
162 return(code
- 'A' + 10);
167 static BOOL
IsHex(UBYTE code
)
170 (code
>= '0' && code
<= '9') ||
171 (code
>= 'a' && code
<= 'f') ||
172 (code
>= 'A' && code
<= 'F') ? TRUE
: FALSE
);
175 static LONG
FindDigit(struct InstData
*data
)
177 WORD pos
= data
->BufferPos
;
181 if(IsDigit(data
->locale
, *(data
->Contents
+pos
)))
183 while(pos
> 0 && IsDigit(data
->locale
, *(data
->Contents
+pos
-1)))
188 while(*(data
->Contents
+pos
) != '\0' && !IsDigit(data
->locale
, *(data
->Contents
+pos
)))
192 if(*(data
->Contents
+pos
) == '\0')
194 pos
= data
->BufferPos
;
195 while(pos
&& !IsDigit(data
->locale
, *(data
->Contents
+pos
)))
198 while(pos
> 0 && IsDigit(data
->locale
, *(data
->Contents
+pos
-1)))
201 if(!pos
&& !IsDigit(data
->locale
, *data
->Contents
))
211 static UWORD
NextWord(STRPTR text
, UWORD x
, struct Locale
*locale
)
215 while(IsAlNum(locale
, (UBYTE
)text
[x
]))
218 while(text
[x
] != '\0' && !IsAlNum(locale
, (UBYTE
)text
[x
]))
225 static UWORD
PrevWord(STRPTR text
, UWORD x
, struct Locale
*locale
)
232 while(x
&& !IsAlNum(locale
, (UBYTE
)text
[x
]))
235 while(x
> 0 && IsAlNum(locale
, (UBYTE
)text
[x
-1]))
242 void DeleteBlock(struct InstData
*data
)
248 if(BlockEnabled(data
) == TRUE
)
250 UWORD Blk_Start
, Blk_Width
;
252 Blk_Start
= (data
->BlockStart
< data
->BlockStop
) ? data
->BlockStart
: data
->BlockStop
;
253 Blk_Width
= abs(data
->BlockStop
-data
->BlockStart
);
254 memmove(data
->Contents
+Blk_Start
, data
->Contents
+Blk_Start
+Blk_Width
, strlen(data
->Contents
+Blk_Start
+Blk_Width
)+1);
255 data
->BufferPos
= Blk_Start
;
261 static void CopyBlock(struct InstData
*data
)
265 if(isFlagClear(data
->Flags
, FLG_Secret
))
267 UWORD Blk_Start
, Blk_Width
;
268 //struct IFFHandle *iff;
270 if(BlockEnabled(data
) == TRUE
)
272 Blk_Start
= (data
->BlockStart
< data
->BlockStop
) ? data
->BlockStart
: data
->BlockStop
;
273 Blk_Width
= abs(data
->BlockStop
-data
->BlockStart
);
278 Blk_Width
= strlen(data
->Contents
);
281 StringToClipboard(&data
->Contents
[Blk_Start
], Blk_Width
);
287 static void CutBlock(struct InstData
*data
)
293 if(BlockEnabled(data
) == TRUE
)
297 clearFlag(data
->Flags
, FLG_BlockEnabled
);
301 *data
->Contents
= '\0';
308 static void Paste(struct InstData
*data
)
315 if(ClipboardToString(&str
, &length
) == TRUE
)
317 // clear the selection
320 if(data
->MaxLength
!= 0 && strlen(data
->Contents
) + length
> (ULONG
)data
->MaxLength
- 1)
323 length
= data
->MaxLength
- 1 - strlen(data
->Contents
);
326 if(ExpandContentString(&data
->Contents
, length
) == TRUE
)
328 memmove(data
->Contents
+ data
->BufferPos
+ length
, data
->Contents
+ data
->BufferPos
, strlen(data
->Contents
+ data
->BufferPos
)+1);
329 memcpy(data
->Contents
+ data
->BufferPos
, str
, length
);
330 data
->BufferPos
+= length
;
334 E(DBF_ALWAYS
, "content expansion by %ld bytes failed", length
);
343 static void UndoRedo(struct InstData
*data
)
350 // swap content and undo pointers
351 oldcontents
= data
->Contents
;
352 data
->Contents
= data
->Undo
;
353 data
->Undo
= oldcontents
;
355 if(isFlagSet(data
->Flags
, FLG_RedoAvailable
))
356 clearFlag(data
->Flags
, FLG_RedoAvailable
);
358 setFlag(data
->Flags
, FLG_RedoAvailable
);
360 clearFlag(data
->Flags
, FLG_BlockEnabled
);
362 // swap content and undo positions
363 oldpos
= data
->BufferPos
;
364 data
->BufferPos
= data
->UndoPos
;
365 data
->UndoPos
= oldpos
;
370 static void RevertToOriginal(struct InstData
*data
)
376 // swap content and original pointers
377 oldcontents
= data
->Contents
;
378 data
->Contents
= data
->Original
;
379 data
->Original
= oldcontents
;
381 setFlag(data
->Flags
, FLG_Original
);
382 clearFlag(data
->Flags
, FLG_BlockEnabled
);
383 data
->BufferPos
= strlen(data
->Contents
);
388 static BOOL
ToggleCaseChar(struct InstData
*data
)
390 UBYTE key
= *(data
->Contents
+data
->BufferPos
);
395 if(data
->BufferPos
< strlen(data
->Contents
))
397 *(data
->Contents
+data
->BufferPos
) = IsLower(data
->locale
, key
) ? ConvToUpper(data
->locale
, key
) : ConvToLower(data
->locale
, key
);
406 static BOOL
ToggleCaseWord(struct InstData
*data
)
408 UWORD Stop
= NextWord(data
->Contents
, data
->BufferPos
, data
->locale
);
413 while(data
->BufferPos
< Stop
)
415 UBYTE key
= *(data
->Contents
+data
->BufferPos
);
417 *(data
->Contents
+data
->BufferPos
) = IsLower(data
->locale
, key
) ? ConvToUpper(data
->locale
, key
) : ConvToLower(data
->locale
, key
);
426 static BOOL
IncreaseNearNumber(struct InstData
*data
)
433 if((pos
= FindDigit(data
)) >= 0)
438 if((cut
= StrToLong(data
->Contents
+pos
, (LONG
*)&res
)))
444 MySPrintf(format
, "%%0%ldlu", cut
);
445 MySPrintf(string
, format
, res
);
446 Overwrite(string
, pos
, cut
, data
);
455 static BOOL
DecreaseNearNumber(struct InstData
*data
)
462 if((pos
= FindDigit(data
)) >= 0)
467 if((cut
= StrToLong(data
->Contents
+pos
, (LONG
*)&res
)))
475 format
= &format2
[0];
476 MySPrintf(format
, "%%0%ldlu", cut
);
478 MySPrintf(string
, format
, res
);
479 Overwrite(string
, pos
, cut
, data
);
489 static BOOL
HexToDec(struct InstData
*data
)
492 UWORD pos
= data
->BufferPos
;
498 while(pos
&& IsHex(*(data
->Contents
+pos
-1)))
501 while(IsHex(*(data
->Contents
+pos
+cut
)))
503 res
= (result
<< 4) + DecimalValue(*(data
->Contents
+pos
+cut
));
511 MySPrintf(string
, "%lu", res
);
512 Overwrite(string
, pos
, cut
, data
);
520 static BOOL
DecToHex(struct InstData
*data
)
527 if((pos
= FindDigit(data
)) >= 0)
532 if((cut
= StrToLong(data
->Contents
+pos
, (LONG
*)&res
)))
534 const char *format
= "%lx";
537 MySPrintf(string
, format
, res
);
538 Overwrite(string
, pos
, cut
, data
);
547 void TriggerNotify(struct IClass
*cl
, Object
*obj
)
549 struct InstData
*data
= (struct InstData
*)INST_DATA(cl
, obj
);
550 struct TagItem tags
[] =
552 { MUIA_String_Contents
, (IPTR
)data
->Contents
},
558 // pass the attribute directly to our superclass as our own
559 // handling of OM_SET will suppress the notification in case
560 // the contents did not change
561 if(isFlagClear(data
->Flags
, FLG_NoNotify
))
563 DoSuperMethod(cl
, obj
, OM_SET
, tags
, NULL
);
564 clearFlag(data
->Flags
, FLG_NotifyQueued
);
567 setFlag(data
->Flags
, FLG_NotifyQueued
);
572 ULONG
ConvertKey(struct IntuiMessage
*imsg
)
574 struct InputEvent event
;
575 unsigned char code
= 0;
579 event
.ie_NextEvent
= NULL
;
580 event
.ie_Class
= IECLASS_RAWKEY
;
581 event
.ie_SubClass
= 0;
582 event
.ie_Code
= imsg
->Code
;
583 event
.ie_Qualifier
= imsg
->Qualifier
;
584 event
.ie_EventAddress
= (APTR
*) *((IPTR
*)imsg
->IAddress
);
586 MapRawKey(&event
, (STRPTR
)&code
, 1, NULL
);
592 IPTR
mDoAction(struct IClass
*cl
, Object
*obj
, struct MUIP_BetterString_DoAction
*msg
)
594 struct InstData
*data
= (struct InstData
*)INST_DATA(cl
, obj
);
600 D(DBF_INPUT
, "DoAction(%ld)");
604 case MUIV_BetterString_DoAction_Cut
:
612 case MUIV_BetterString_DoAction_Copy
:
615 clearFlag(data
->Flags
, FLG_BlockEnabled
);
620 case MUIV_BetterString_DoAction_Paste
:
623 clearFlag(data
->Flags
, FLG_BlockEnabled
);
629 case MUIV_BetterString_DoAction_Delete
:
632 clearFlag(data
->Flags
, FLG_BlockEnabled
);
638 case MUIV_BetterString_DoAction_SelectAll
:
640 data
->BlockStart
= 0;
641 data
->BlockStop
= strlen(data
->Contents
);
642 setFlag(data
->Flags
, FLG_BlockEnabled
);
647 case MUIV_BetterString_DoAction_SelectNone
:
649 clearFlag(data
->Flags
, FLG_BlockEnabled
);
654 case MUIV_BetterString_DoAction_Undo
:
655 case MUIV_BetterString_DoAction_Redo
:
658 (((msg
->action
== MUIV_BetterString_DoAction_Redo
) && isFlagSet(data
->Flags
, FLG_RedoAvailable
)) ||
659 ((msg
->action
== MUIV_BetterString_DoAction_Undo
) && isFlagClear(data
->Flags
, FLG_RedoAvailable
))))
662 clearFlag(data
->Flags
, FLG_BlockEnabled
);
669 case MUIV_BetterString_DoAction_Revert
:
671 RevertToOriginal(data
);
672 clearFlag(data
->Flags
, FLG_BlockEnabled
);
678 case MUIV_BetterString_DoAction_ToggleCase
:
680 edited
= result
= ToggleCaseChar(data
);
681 clearFlag(data
->Flags
, FLG_BlockEnabled
);
685 case MUIV_BetterString_DoAction_ToggleCaseWord
:
687 edited
= result
= ToggleCaseWord(data
);
688 clearFlag(data
->Flags
, FLG_BlockEnabled
);
692 case MUIV_BetterString_DoAction_IncreaseNum
:
694 edited
= result
= IncreaseNearNumber(data
);
695 clearFlag(data
->Flags
, FLG_BlockEnabled
);
699 case MUIV_BetterString_DoAction_DecreaseNum
:
701 edited
= result
= DecreaseNearNumber(data
);
702 clearFlag(data
->Flags
, FLG_BlockEnabled
);
706 case MUIV_BetterString_DoAction_HexToDec
:
708 edited
= result
= HexToDec(data
);
709 clearFlag(data
->Flags
, FLG_BlockEnabled
);
713 case MUIV_BetterString_DoAction_DecToHex
:
715 edited
= result
= DecToHex(data
);
716 clearFlag(data
->Flags
, FLG_BlockEnabled
);
720 case MUIV_BetterString_DoAction_NextFileComp
:
722 edited
= result
= FileNameComplete(obj
, FALSE
, data
);
723 clearFlag(data
->Flags
, FLG_BlockEnabled
);
727 case MUIV_BetterString_DoAction_PrevFileComp
:
729 edited
= result
= FileNameComplete(obj
, TRUE
, data
);
730 clearFlag(data
->Flags
, FLG_BlockEnabled
);
735 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
738 TriggerNotify(cl
, obj
);
744 IPTR
mHandleInput(struct IClass
*cl
, Object
*obj
, struct MUIP_HandleEvent
*msg
)
746 struct InstData
*data
= (struct InstData
*)INST_DATA(cl
, obj
);
748 BOOL movement
= FALSE
;
754 // handle the standard MUI keys first
755 if(msg
->muikey
!= MUIKEY_NONE
)
761 if(data
->KeyUpFocus
!= NULL
&& _win(obj
) != NULL
)
763 set(_win(obj
), MUIA_Window_ActiveObject
, data
->KeyUpFocus
);
764 result
= MUI_EventHandlerRC_Eat
;
771 if(data
->KeyDownFocus
!= NULL
&& _win(obj
) != NULL
)
773 set(_win(obj
), MUIA_Window_ActiveObject
, data
->KeyDownFocus
);
774 result
= MUI_EventHandlerRC_Eat
;
782 clearFlag(data
->Flags
, FLG_BlockEnabled
);
783 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
784 result
= MUI_EventHandlerRC_Eat
;
791 clearFlag(data
->Flags
, FLG_BlockEnabled
);
792 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
793 TriggerNotify(cl
, obj
);
794 result
= MUI_EventHandlerRC_Eat
;
801 clearFlag(data
->Flags
, FLG_BlockEnabled
);
802 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
803 TriggerNotify(cl
, obj
);
804 result
= MUI_EventHandlerRC_Eat
;
811 if(data
->Undo
&& ((msg
->muikey
== MUIKEY_REDO
&& isFlagSet(data
->Flags
, FLG_RedoAvailable
)) ||
812 (msg
->muikey
== MUIKEY_UNDO
&& isFlagClear(data
->Flags
, FLG_RedoAvailable
))))
815 clearFlag(data
->Flags
, FLG_BlockEnabled
);
816 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
817 TriggerNotify(cl
, obj
);
818 result
= MUI_EventHandlerRC_Eat
;
827 if(result
== 0 && msg
->imsg
!= NULL
)
829 ULONG StringLength
= strlen(data
->Contents
);
831 if(msg
->imsg
->Class
== IDCMP_RAWKEY
&&
832 // msg->imsg->Code >= IECODE_KEY_CODE_FIRST &&
833 msg
->imsg
->Code
<= IECODE_KEY_CODE_LAST
)
835 if(isFlagSet(data
->Flags
, FLG_Active
))
839 if(isFlagClear(data
->Flags
, FLG_BlockEnabled
))
840 data
->BlockStart
= data
->BufferPos
;
842 if(isFlagSet(data
->Flags
, FLG_NoInput
))
844 switch(msg
->imsg
->Code
)
847 case RAWKEY_BACKSPACE
:
856 switch(msg
->imsg
->Code
)
861 if(isFlagSet(data
->Flags
, FLG_NoShortcuts
) || isFlagClear(msg
->imsg
->Qualifier
, IEQUALIFIER_RCOMMAND
))
867 if(!(edited
= FileNameComplete(obj
, isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
|IEQUALIFIER_LSHIFT
), data
)))
875 case RAWKEY_CRSRRIGHT
:
877 if(data
->BufferPos
< StringLength
)
879 if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
|IEQUALIFIER_LSHIFT
))
881 data
->BufferPos
= StringLength
;
883 else if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RALT
|IEQUALIFIER_LALT
))
885 data
->BufferPos
= NextWord(data
->Contents
, data
->BufferPos
, data
->locale
);
887 else if(BlockEnabled(data
) && isFlagClear(msg
->imsg
->Qualifier
, IEQUALIFIER_CONTROL
))
889 data
->BufferPos
= MAX(data
->BlockStart
, data
->BlockStop
);
901 case RAWKEY_CRSRLEFT
:
905 if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
|IEQUALIFIER_LSHIFT
))
909 else if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RALT
|IEQUALIFIER_LALT
))
911 data
->BufferPos
= PrevWord(data
->Contents
, data
->BufferPos
, data
->locale
);
915 if(BlockEnabled(data
) && isFlagClear(msg
->imsg
->Qualifier
, IEQUALIFIER_CONTROL
))
916 data
->BufferPos
= MIN(data
->BlockStart
, data
->BlockStop
);
927 case RAWKEY_BACKSPACE
:
929 if(BlockEnabled(data
))
935 if(data
->BufferPos
!= 0)
937 if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
|IEQUALIFIER_LSHIFT
|IEQUALIFIER_CONTROL
))
940 memmove(data
->Contents
, data
->Contents
+data
->BufferPos
, strlen(data
->Contents
+data
->BufferPos
)+1);
943 else if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RALT
|IEQUALIFIER_LALT
))
945 UWORD NewPos
= PrevWord(data
->Contents
, data
->BufferPos
, data
->locale
);
948 memmove(data
->Contents
+NewPos
, data
->Contents
+data
->BufferPos
, strlen(data
->Contents
+data
->BufferPos
)+1);
949 data
->BufferPos
= NewPos
;
953 memmove(data
->Contents
+data
->BufferPos
-1, data
->Contents
+data
->BufferPos
, strlen(data
->Contents
+data
->BufferPos
)+1);
965 if(BlockEnabled(data
))
971 if(data
->BufferPos
< StringLength
)
973 if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
|IEQUALIFIER_LSHIFT
) || isFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_CONTROL
))
976 *(data
->Contents
+data
->BufferPos
) = '\0';
978 else if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RALT
|IEQUALIFIER_LALT
))
980 STRPTR start
= data
->Contents
+NextWord(data
->Contents
, data
->BufferPos
, data
->locale
);
983 memmove(data
->Contents
+data
->BufferPos
, start
, strlen(start
)+1);
987 memmove(data
->Contents
+data
->BufferPos
, data
->Contents
+data
->BufferPos
+1, strlen(data
->Contents
+data
->BufferPos
+1)+1);
1008 if(data
->BufferPos
< StringLength
)
1009 data
->BufferPos
= StringLength
;
1017 if(data
->Popup
&& msg
->muikey
== MUIKEY_POPUP
)
1019 DoMethod(data
->Popup
, MUIM_Popstring_Open
);
1023 UBYTE code
= ConvertKey(msg
->imsg
);
1025 if((((code
>= 32 && code
<= 126) || code
>= 160) && isFlagClear(msg
->imsg
->Qualifier
, IEQUALIFIER_RCOMMAND
)) ||
1026 (code
&& isFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_CONTROL
)))
1028 if(isFlagClear(data
->Flags
, FLG_NoInput
))
1032 if((data
->MaxLength
== 0 || (ULONG
)data
->MaxLength
-1 > strlen(data
->Contents
)) &&
1033 Accept(code
, data
->Accept
) && Reject(code
, data
->Reject
))
1035 if(ExpandContentString(&data
->Contents
, 1) == TRUE
)
1037 memmove(data
->Contents
+data
->BufferPos
+1, data
->Contents
+data
->BufferPos
, strlen(data
->Contents
+ data
->BufferPos
)+1);
1038 *(data
->Contents
+data
->BufferPos
) = code
;
1044 E(DBF_ALWAYS
, "content expansion by %ld bytes failed", 1);
1055 // if this betterstring object is a read-only object
1056 // we only accept a view characters or otherwise reject
1057 // the rawkey operation
1058 if(isFlagSet(data
->Flags
, FLG_NoInput
))
1067 RETURN(MUI_EventHandlerRC_Eat
);
1068 return MUI_EventHandlerRC_Eat
;
1073 // check if the user pressed return and if he has activated AdvanceOnCr or not
1076 if(isFlagClear(data
->Flags
, FLG_StayActive
))
1080 if(isFlagSet(data
->Flags
, FLG_AdvanceOnCr
))
1082 if(isAnyFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
|IEQUALIFIER_LSHIFT
))
1083 active
= MUIV_Window_ActiveObject_Prev
;
1085 active
= MUIV_Window_ActiveObject_Next
;
1088 active
= MUIV_Window_ActiveObject_None
;
1090 set(_win(obj
), MUIA_Window_ActiveObject
, active
);
1093 set(obj
, MUIA_String_Acknowledge
, data
->Contents
);
1095 RETURN(MUI_EventHandlerRC_Eat
);
1096 return MUI_EventHandlerRC_Eat
;
1099 // see if we should skip the default shorcuts or not
1100 if(isFlagClear(data
->Flags
, FLG_NoShortcuts
))
1102 // depending on the pressed key code
1103 // we perform different actions.
1108 edited
= ToggleCaseChar(data
);
1114 edited
= ToggleCaseWord(data
);
1134 if((edited
= IncreaseNearNumber(data
)) == FALSE
)
1141 if((edited
= DecreaseNearNumber(data
)) == FALSE
)
1148 if((edited
= HexToDec(data
)) == FALSE
)
1155 if((edited
= DecToHex(data
)) == FALSE
)
1162 RevertToOriginal(data
);
1170 if(data
->Undo
&& (((code
== 'Z') && isFlagSet(data
->Flags
, FLG_RedoAvailable
)) ||
1171 ((code
== 'z') && isFlagClear(data
->Flags
, FLG_RedoAvailable
))))
1182 clearFlag(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
);
1189 clearFlag(msg
->imsg
->Qualifier
, IEQUALIFIER_RSHIFT
);
1200 if(data
->FNCBuffer
&& !FNC
)
1202 struct FNCData
*fncbuffer
= data
->FNCBuffer
, *fncframe
;
1206 fncframe
= fncbuffer
;
1207 fncbuffer
= fncbuffer
->next
;
1208 SharedPoolFree(fncframe
);
1210 data
->FNCBuffer
= NULL
;
1213 if(movement
&& isFlagSet(msg
->imsg
->Qualifier
, IEQUALIFIER_CONTROL
))
1214 setFlag(data
->Flags
, FLG_BlockEnabled
);
1216 clearFlag(data
->Flags
, FLG_BlockEnabled
);
1218 if(isFlagSet(data
->Flags
, FLG_BlockEnabled
))
1219 data
->BlockStop
= data
->BufferPos
;
1221 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
1224 TriggerNotify(cl
, obj
);
1226 result
= MUI_EventHandlerRC_Eat
;
1230 if(data
->CtrlChar
&& ConvertKey(msg
->imsg
) == data
->CtrlChar
)
1232 set(_win(obj
), MUIA_Window_ActiveObject
, obj
);
1233 result
= MUI_EventHandlerRC_Eat
;
1239 D(DBF_INPUT
, "%08lx: keycode: %08lx (%ld) %08lx (%ld)", obj
, msg
->imsg
->Code
, isFlagSet(data
->Flags
, FLG_Active
), RAWKEY_TAB
, msg
->imsg
->Code
<= IECODE_KEY_CODE_LAST
);
1241 // check if the user pressed the TAB key for cycling to the next
1242 // mui object and if this object is part of the cyclechain
1243 if(msg
->imsg
->Class
== IDCMP_RAWKEY
&& isFlagSet(data
->Flags
, FLG_Active
) && isFlagSet(data
->Flags
, FLG_FreshActive
))
1245 // clear the FreshActive flag
1246 clearFlag(data
->Flags
, FLG_FreshActive
);
1247 // no need to do an DoAction(SelectAll), because this effectively has been
1248 // done during MUIM_GoActive already
1251 // we check if this is a mousemove input message and if
1252 // so we check whether the mouse is currently over our
1253 // texteditor object or not.
1254 if(msg
->imsg
->Class
== IDCMP_MOUSEMOVE
)
1256 BOOL isOverObject
= FALSE
;
1258 D(DBF_INPUT
, "IDCMP_MOUSEMOVE");
1259 if(_isinobject(obj
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
1261 #if defined(__MORPHOS__)
1263 isOverObject
= TRUE
;
1266 if(isOverObject
== FALSE
)
1268 struct Layer_Info
*li
= &(_screen(obj
)->LayerInfo
);
1269 struct Layer
*layer
;
1271 // get the layer that belongs to the current mouse coordinates
1273 layer
= WhichLayer(li
, _window(obj
)->LeftEdge
+ msg
->imsg
->MouseX
, _window(obj
)->TopEdge
+ msg
->imsg
->MouseY
);
1274 UnlockLayerInfo(li
);
1276 // if the mouse is currently over the object and over the object's
1277 // window we go and change the pointer to show the selection pointer
1278 if(layer
!= NULL
&& layer
->Window
== _window(obj
))
1279 isOverObject
= TRUE
;
1283 if(isOverObject
== TRUE
)
1284 ShowSelectPointer(obj
, data
);
1286 HideSelectPointer(obj
, data
);
1288 else if(msg
->imsg
->Class
== IDCMP_MOUSEBUTTONS
)
1290 D(DBF_INPUT
, "IDCMP_MOUSEBUTTONS");
1291 if(msg
->imsg
->Code
== (IECODE_LBUTTON
| IECODE_UP_PREFIX
))
1293 // forget the pressed mouse button
1294 clearFlag(data
->Flags
, FLG_MouseButtonDown
);
1296 if(isAnyFlagSet(data
->ehnode
.ehn_Events
, /*IDCMP_MOUSEMOVE|*/IDCMP_INTUITICKS
))
1298 DoMethod(_win(obj
), MUIM_Window_RemEventHandler
, &data
->ehnode
);
1299 // clearFlag(data->ehnode.ehn_Events, IDCMP_MOUSEMOVE);
1300 clearFlag(data
->ehnode
.ehn_Events
, IDCMP_INTUITICKS
);
1301 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
, &data
->ehnode
);
1304 // make sure to select the whole content in case the object was freshly activated
1305 // and the user just released the mousebutton (no matter if inside or outside)
1306 if(isFlagSet(data
->Flags
, FLG_FreshActive
) && BlockEnabled(data
) == FALSE
&&
1307 ((data
->SelectOnActive
== TRUE
&& isFlagClear(data
->Flags
, FLG_ForceSelectOff
)) || isFlagSet(data
->Flags
, FLG_ForceSelectOn
)))
1309 DoMethod(obj
, MUIM_BetterString_DoAction
, MUIV_BetterString_DoAction_SelectAll
);
1312 #ifdef ALLOW_OUTSIDE_MARKING
1313 if(isFlagSet(data
->Flags
, FLG_Active
) && isFlagSet(data
->Flags
, FLG_DragOutside
))
1316 get(_win(obj
), MUIA_Window_ActiveObject
, &active
);
1318 E(DBF_STARTUP
, "MUI error: %lx, %lx", active
, obj
);
1320 WORD x
= _mleft(obj
);
1321 WORD y
= _mtop(obj
);
1322 WORD width
= _mwidth(obj
);
1323 WORD height
= _font(obj
)->tf_YSize
;
1325 if(!(msg
->imsg
->MouseX
>= x
&& msg
->imsg
->MouseX
< x
+width
&& msg
->imsg
->MouseY
>= y
&& msg
->imsg
->MouseY
< y
+height
))
1327 D(DBF_STARTUP
, "Detected LMB+up outside (drag: %ld)", isFlagSet(data
->Flags
, FLG_DragOutside
));
1328 set(_win(obj
), MUIA_Window_ActiveObject
, MUIV_Window_ActiveObject_None
);
1333 // clear the FreshActive flag
1334 clearFlag(data
->Flags
, FLG_FreshActive
);
1336 else if(msg
->imsg
->Code
== IECODE_LBUTTON
)
1338 WORD x
= _mleft(obj
);
1339 WORD y
= _mtop(obj
);
1340 WORD width
= _mwidth(obj
);
1341 WORD height
= _font(obj
)->tf_YSize
;
1343 // remember the pressed mouse button
1344 setFlag(data
->Flags
, FLG_MouseButtonDown
);
1346 if(msg
->imsg
->MouseX
>= x
&& msg
->imsg
->MouseX
< x
+width
&& msg
->imsg
->MouseY
>= y
&& msg
->imsg
->MouseY
< y
+height
)
1348 WORD offset
= msg
->imsg
->MouseX
- x
;
1349 struct TextExtent tExtend
;
1351 offset
-= AlignOffset(obj
, data
);
1353 SetFont(&data
->rport
, _font(obj
));
1354 data
->BufferPos
= data
->DisplayPos
+ TextFit(&data
->rport
, data
->Contents
+data
->DisplayPos
, StringLength
-data
->DisplayPos
, &tExtend
, NULL
, 1, offset
+1, _font(obj
)->tf_YSize
);
1356 if(data
->BufferPos
== data
->BufferLastPos
&&
1357 DoubleClick(data
->StartSecs
, data
->StartMicros
, msg
->imsg
->Seconds
, msg
->imsg
->Micros
))
1359 // on a secret gadget we skip clickcount step 1 as
1360 // it might be misused to guess the words in the gadget.
1361 if(isFlagSet(data
->Flags
, FLG_Secret
) && data
->ClickCount
== 0)
1366 else if((data
->SelectOnActive
== TRUE
&& isFlagClear(data
->Flags
, FLG_ForceSelectOff
)) ||
1367 isFlagSet(data
->Flags
, FLG_ForceSelectOn
))
1369 // special handling for the "select on active" feature
1370 // this makes it possible to start a selection upon the first click within
1371 // the object *and* a full selection
1372 data
->ClickCount
= 3;
1376 data
->ClickCount
= 0;
1379 // lets save the current bufferpos to the lastpos variable
1380 data
->BufferLastPos
= data
->BufferPos
;
1382 data
->StartSecs
= msg
->imsg
->Seconds
;
1383 data
->StartMicros
= msg
->imsg
->Micros
;
1385 switch(data
->ClickCount
)
1389 if(isFlagClear(data
->Flags
, FLG_BlockEnabled
) || isFlagClear(msg
->imsg
->Qualifier
, IEQUALIFIER_CONTROL
))
1390 data
->BlockStart
= data
->BufferPos
;
1396 if(data
->Contents
[data
->BufferPos
] != '\0')
1398 UWORD start
= data
->BufferPos
;
1399 UWORD stop
= data
->BufferPos
;
1400 ULONG alpha
= IsAlNum(data
->locale
, (UBYTE
)*(data
->Contents
+data
->BufferPos
));
1402 while(start
> 0 && alpha
== (ULONG
)IsAlNum(data
->locale
, (UBYTE
)*(data
->Contents
+start
-1)))
1405 while(alpha
== (ULONG
)IsAlNum(data
->locale
, (UBYTE
)*(data
->Contents
+stop
)) && *(data
->Contents
+stop
) != '\0')
1408 data
->BlockStart
= start
;
1409 data
->BufferPos
= stop
;
1416 data
->BlockStart
= 0;
1417 data
->BufferPos
= strlen(data
->Contents
);
1423 data
->BlockStart
= data
->BufferPos
;
1424 data
->ClickCount
= 0;
1428 data
->BlockStop
= data
->BufferPos
;
1429 setFlag(data
->Flags
, FLG_BlockEnabled
);
1431 DoMethod(_win(obj
), MUIM_Window_RemEventHandler
, &data
->ehnode
);
1432 // setFlag(data->ehnode.ehn_Events, IDCMP_MOUSEMOVE);
1433 setFlag(data
->ehnode
.ehn_Events
, IDCMP_INTUITICKS
);
1434 DoMethod(_win(obj
), MUIM_Window_AddEventHandler
, &data
->ehnode
);
1436 if(isFlagSet(data
->Flags
, FLG_Active
))
1437 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
1440 // set the active flag now already
1441 // this will be checked in MUIM_GoActive to distinguish between
1442 // activation by mouse and by keyboard/application
1443 setFlag(data
->Flags
, FLG_Active
);
1444 set(_win(obj
), MUIA_Window_ActiveObject
, obj
);
1447 result
= MUI_EventHandlerRC_Eat
;
1451 data
->ClickCount
= 0;
1452 if(isFlagSet(data
->Flags
, FLG_Active
) && isFlagClear(data
->Flags
, FLG_StayActive
))
1454 #ifdef ALLOW_OUTSIDE_MARKING
1455 D(DBF_STARTUP
, "Clicked outside gadget");
1456 setFlag(data
->Flags
, FLG_DragOutside
);
1458 // DoMethod(_win(obj), MUIM_Window_RemEventHandler, &data->ehnode);
1459 // data->ehnode.ehn_Events |= IDCMP_MOUSEMOVE;
1460 // DoMethod(_win(obj), MUIM_Window_AddEventHandler, &data->ehnode);
1462 set(_win(obj
), MUIA_Window_ActiveObject
, MUIV_Window_ActiveObject_None
);
1470 #ifdef ALLOW_OUTSIDE_MARKING
1471 if(msg
->imsg
->Class
== IDCMP_MOUSEMOVE
)
1473 clearFlag(data
->Flags
, FLG_DragOutside
);
1474 D(DBF_STARTUP
, "Detected drag");
1477 if((/*msg->imsg->Class == IDCMP_MOUSEMOVE ||*/ msg
->imsg
->Class
== IDCMP_INTUITICKS
) && isFlagSet(data
->Flags
, FLG_Active
))
1479 WORD x
, width
, mousex
;
1480 struct TextExtent tExtend
;
1483 mousex
= msg
->imsg
->MouseX
- AlignOffset(obj
, data
);
1484 width
= _mwidth(obj
);
1486 SetFont(&data
->rport
, _font(obj
));
1488 switch(data
->ClickCount
)
1494 if(data
->DisplayPos
)
1496 data
->BufferPos
= data
->DisplayPos
;
1500 if(mousex
>= x
+width
)
1502 if(data
->DisplayPos
< StringLength
)
1505 data
->BufferPos
= data
->DisplayPos
+ TextFit(&data
->rport
, data
->Contents
+data
->DisplayPos
, StringLength
-data
->DisplayPos
, &tExtend
, NULL
, 1, _mwidth(obj
), _font(obj
)->tf_YSize
);
1509 data
->BufferPos
= StringLength
;
1514 WORD offset
= mousex
- x
;
1517 data->BufferPos = 0;
1519 */ data
->BufferPos
= data
->DisplayPos
+ TextFit(&data
->rport
, data
->Contents
+data
->DisplayPos
, StringLength
-data
->DisplayPos
, &tExtend
, NULL
, 1, offset
+1, _font(obj
)->tf_YSize
);
1522 data
->BlockStop
= data
->BufferPos
;
1523 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
1529 WORD offset
= mousex
- x
;
1532 if(mousex
< x
&& data
->DisplayPos
)
1535 newpos
= data
->DisplayPos
;
1539 // offset -= AlignOffset(obj, data);
1541 newpos
= data
->DisplayPos
+ TextFit(&data
->rport
, data
->Contents
+data
->DisplayPos
, StringLength
-data
->DisplayPos
, &tExtend
, NULL
, 1, offset
+1, _font(obj
)->tf_YSize
);
1544 if(newpos
>= data
->BlockStart
)
1546 while(IsAlNum(data
->locale
, (UBYTE
)*(data
->Contents
+newpos
)))
1551 while(newpos
> 0 && IsAlNum(data
->locale
, (UBYTE
)*(data
->Contents
+newpos
-1)))
1555 if(data
->BufferPos
!= newpos
)
1557 data
->BlockStop
= data
->BufferPos
= newpos
;
1558 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
1572 IPTR
mInsert(struct IClass
*cl
, Object
*obj
, struct MUIP_BetterString_Insert
*msg
)
1574 struct InstData
*data
= (struct InstData
*)INST_DATA(cl
, obj
);
1582 case MUIV_BetterString_Insert_StartOfString:
1587 case MUIV_BetterString_Insert_EndOfString
:
1588 pos
= strlen(data
->Contents
);
1591 case MUIV_BetterString_Insert_BufferPos
:
1592 pos
= data
->BufferPos
;
1600 Overwrite(msg
->text
, pos
, 0, data
);
1601 clearFlag(data
->Flags
, FLG_BlockEnabled
);
1602 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
1603 // trigger a notification as we just changed the contents
1604 TriggerNotify(cl
, obj
);