1 /***************************************************************************
3 TextEditor.mcc - Textediting MUI Custom Class
4 Copyright (C) 1997-2000 Allan Odgaard
5 Copyright (C) 2005-2014 TextEditor.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 TextEditor class Support Site: http://www.sf.net/projects/texteditor-mcc
21 ***************************************************************************/
23 #include <libraries/mui.h>
24 #include <intuition/classes.h>
26 #if !defined(__amigaos4__)
27 #include <clib/alib_protos.h>
29 #include <clib/macros.h>
31 #include <proto/muimaster.h>
32 #include <proto/intuition.h>
33 #include <proto/keymap.h>
34 #include <proto/locale.h>
35 #include <proto/utility.h>
36 #include <proto/layers.h>
43 #if !defined(__amigaos4__)
48 static ULONG
RAWToANSI(struct IntuiMessage
*imsg
)
50 struct InputEvent event
;
55 event
.ie_NextEvent
= NULL
;
56 event
.ie_Class
= IECLASS_RAWKEY
;
57 event
.ie_SubClass
= 0;
58 event
.ie_Code
= imsg
->Code
;
59 event
.ie_Qualifier
= imsg
->Qualifier
;
60 event
.ie_EventAddress
= (APTR
*) *((IPTR
*)imsg
->IAddress
);
62 MapRawKey(&event
, (STRPTR
)&code
, 1, NULL
);
64 SHOWVALUE(DBF_INPUT
, code
);
72 static BOOL
MatchQual(struct InstData
*data
, ULONG input
, ULONG match
, UWORD action
)
78 if(isFlagSet(match
, IEQUALIFIER_SHIFT
) && isAnyFlagSet(input
, IEQUALIFIER_LSHIFT
| IEQUALIFIER_RSHIFT
))
80 clearFlag(match
, IEQUALIFIER_SHIFT
);
81 clearFlag(input
, IEQUALIFIER_LSHIFT
);
82 clearFlag(input
, IEQUALIFIER_RSHIFT
);
84 if(isFlagSet(match
, IEQUALIFIER_ALT
) && isAnyFlagSet(input
, IEQUALIFIER_LALT
| IEQUALIFIER_RALT
))
86 clearFlag(match
, IEQUALIFIER_ALT
);
87 clearFlag(input
, IEQUALIFIER_LALT
);
88 clearFlag(input
, IEQUALIFIER_RALT
);
90 if(isFlagSet(match
, IEQUALIFIER_COMMAND
) && isAnyFlagSet(input
, IEQUALIFIER_LCOMMAND
| IEQUALIFIER_RCOMMAND
))
92 clearFlag(match
, IEQUALIFIER_COMMAND
);
93 clearFlag(input
, IEQUALIFIER_LCOMMAND
);
94 clearFlag(input
, IEQUALIFIER_RCOMMAND
);
100 if(result
== FALSE
&& ((input
& ~data
->blockqual
) == match
))
102 if(action
== MUIV_TextEditor_KeyAction_Up
||
103 action
== MUIV_TextEditor_KeyAction_Down
||
104 action
== MUIV_TextEditor_KeyAction_Left
||
105 action
== MUIV_TextEditor_KeyAction_Right
||
106 action
== MUIV_TextEditor_KeyAction_PageUp
||
107 action
== MUIV_TextEditor_KeyAction_PageDown
||
108 action
== MUIV_TextEditor_KeyAction_StartOfLine
||
109 action
== MUIV_TextEditor_KeyAction_EndOfLine
||
110 action
== MUIV_TextEditor_KeyAction_Top
||
111 action
== MUIV_TextEditor_KeyAction_Bottom
||
112 action
== MUIV_TextEditor_KeyAction_PrevWord
||
113 action
== MUIV_TextEditor_KeyAction_NextWord
||
114 action
== MUIV_TextEditor_KeyAction_PrevLine
||
115 action
== MUIV_TextEditor_KeyAction_NextLine
||
116 action
== MUIV_TextEditor_KeyAction_PrevSentence
||
117 action
== MUIV_TextEditor_KeyAction_NextSentence
)
128 /// Key_DelSomething()
129 enum { Del_BOL
= 0, Del_EOL
, Del_BOW
, Del_EOW
};
130 static void Key_DelSomething(struct InstData
*data
, ULONG what
)
134 if(data
->blockinfo
.enabled
== TRUE
)
136 data
->blockinfo
.enabled
= FALSE
;
137 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
140 data
->blockinfo
.startx
= data
->CPos_X
;
141 data
->blockinfo
.startline
= data
->actualline
;
149 setFlag(data
->flags
, FLG_FreezeCrsr
);
153 GoPreviousWord(data
);
156 setFlag(data
->flags
, FLG_FreezeCrsr
);
160 data
->blockinfo
.stopx
= data
->CPos_X
;
161 data
->blockinfo
.stopline
= data
->actualline
;
162 data
->blockinfo
.enabled
= TRUE
;
166 data
->blockinfo
.enabled
= FALSE
;
168 clearFlag(data
->flags
, FLG_FreezeCrsr
);
175 /*---------------------------------*
176 * Function to handle a cursormove *
177 *---------------------------------*/
178 static LONG
FindKey(struct InstData
*data
, UBYTE key
, ULONG qualifier
)
180 struct te_key
*t_keys
= data
->RawkeyBindings
;
182 UBYTE pressKey
= key
& ~IECODE_UP_PREFIX
;
183 UBYTE releaseKey
= key
| IECODE_UP_PREFIX
;
188 t_keys
= (struct te_key
*)default_keybindings
;
190 clearFlag(qualifier
, IEQUALIFIER_RELATIVEMOUSE
);
191 clearFlag(qualifier
, IEQUALIFIER_REPEAT
);
192 clearFlag(qualifier
, IEQUALIFIER_CAPSLOCK
);
194 for(i
=0; (WORD
)t_keys
[i
].code
!= -1; i
++)
196 struct te_key
*curKey
= &t_keys
[i
];
198 if(curKey
->code
== pressKey
&& MatchQual(data
, qualifier
, curKey
->qual
, curKey
->act
) == TRUE
)
200 if(key
== releaseKey
)
202 // We have been called for a known shortcut, but this was a release action.
203 // This will also be "eaten" to avoid double input
208 if(isFlagSet(data
->flags
, FLG_ReadOnly
))
210 LONG new_y
= data
->visual_y
-1;
214 case MUIV_TextEditor_KeyAction_Top
:
218 case MUIV_TextEditor_KeyAction_PageUp
:
219 new_y
-= data
->maxlines
;
222 case MUIV_TextEditor_KeyAction_Up
:
226 case MUIV_TextEditor_KeyAction_Bottom
:
227 new_y
= data
->totallines
-data
->maxlines
;
230 case MUIV_TextEditor_KeyAction_PageDown
:
231 new_y
+= data
->maxlines
;
234 case MUIV_TextEditor_KeyAction_Down
:
238 case MUIV_TextEditor_KeyAction_Copy
:
242 case MUIV_TextEditor_KeyAction_NextGadget
:
243 set(_win(data
->object
), MUIA_Window_ActiveObject
, MUIV_Window_ActiveObject_Next
);
246 case MUIV_TextEditor_KeyAction_SelectAll
:
248 MarkAllBlock(data
, &data
->blockinfo
);
249 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
253 case MUIV_TextEditor_KeyAction_SelectNone
:
255 data
->blockinfo
.enabled
= FALSE
;
256 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
262 if(new_y
!= data
->visual_y
-1)
264 if(new_y
> data
->totallines
-data
->maxlines
)
265 new_y
= data
->totallines
-data
->maxlines
;
268 set(data
->object
, MUIA_TextEditor_Prop_First
, new_y
*data
->fontheight
);
276 D(DBF_INPUT
, "curKey->act: %ld", curKey
->act
);
280 case MUIV_TextEditor_KeyAction_Top
:
285 case MUIV_TextEditor_KeyAction_PrevLine
:
286 GoPreviousLine(data
);
290 case MUIV_TextEditor_KeyAction_PageUp
:
291 GoPreviousPage(data
);
295 case MUIV_TextEditor_KeyAction_Up
:
300 case MUIV_TextEditor_KeyAction_Bottom
:
305 case MUIV_TextEditor_KeyAction_NextLine
:
310 case MUIV_TextEditor_KeyAction_PageDown
:
315 case MUIV_TextEditor_KeyAction_Down
:
320 case MUIV_TextEditor_KeyAction_NextWord
:
325 case MUIV_TextEditor_KeyAction_NextSentence
:
326 GoNextSentence(data
);
330 case MUIV_TextEditor_KeyAction_EndOfLine
:
335 case MUIV_TextEditor_KeyAction_Right
:
340 case MUIV_TextEditor_KeyAction_PrevWord
:
341 GoPreviousWord(data
);
345 case MUIV_TextEditor_KeyAction_PrevSentence
:
346 GoPreviousSentence(data
);
350 case MUIV_TextEditor_KeyAction_StartOfLine
:
355 case MUIV_TextEditor_KeyAction_Left
:
360 case MUIV_TextEditor_KeyAction_SuggestWord
:
364 case MUIV_TextEditor_KeyAction_Backspace
:
368 case MUIV_TextEditor_KeyAction_Delete
:
372 case MUIV_TextEditor_KeyAction_Return
:
376 case MUIV_TextEditor_KeyAction_Tab
:
380 case MUIV_TextEditor_KeyAction_Undo
:
384 case MUIV_TextEditor_KeyAction_Redo
:
388 case MUIV_TextEditor_KeyAction_Cut
:
392 case MUIV_TextEditor_KeyAction_Copy
:
396 case MUIV_TextEditor_KeyAction_Paste
:
400 case MUIV_TextEditor_KeyAction_DelEOL
:
401 Key_DelSomething(data
, Del_EOL
);
404 case MUIV_TextEditor_KeyAction_DelBOL
:
405 Key_DelSomething(data
, Del_BOL
);
408 case MUIV_TextEditor_KeyAction_DelEOW
:
409 Key_DelSomething(data
, Del_EOW
);
412 case MUIV_TextEditor_KeyAction_DelBOW
:
413 Key_DelSomething(data
, Del_BOW
);
416 case MUIV_TextEditor_KeyAction_DelLine
:
420 case MUIV_TextEditor_KeyAction_NextGadget
:
421 set(_win(data
->object
), MUIA_Window_ActiveObject
, MUIV_Window_ActiveObject_Next
);
424 case MUIV_TextEditor_KeyAction_GotoBookmark1
:
425 GotoBookmark(data
, 0);
429 case MUIV_TextEditor_KeyAction_GotoBookmark2
:
430 GotoBookmark(data
, 1);
434 case MUIV_TextEditor_KeyAction_GotoBookmark3
:
435 GotoBookmark(data
, 2);
439 case MUIV_TextEditor_KeyAction_SetBookmark1
:
440 SetBookmark(data
, 0);
443 case MUIV_TextEditor_KeyAction_SetBookmark2
:
444 SetBookmark(data
, 1);
447 case MUIV_TextEditor_KeyAction_SetBookmark3
:
448 SetBookmark(data
, 2);
451 case MUIV_TextEditor_KeyAction_SelectAll
:
453 MarkAllBlock(data
, &data
->blockinfo
);
454 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
458 case MUIV_TextEditor_KeyAction_SelectNone
:
460 data
->blockinfo
.enabled
= FALSE
;
461 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
479 void Key_Normal(struct InstData
*data
, char key
)
486 ScrollIntoDisplay(data
);
488 // check if the user wants to do a direct spell checking while
489 // writing some text.
490 if(key
!= '-' && IsAlpha(data
->mylocale
, key
) == FALSE
)
492 // only perform a spell check if it has been enabled
493 if(data
->TypeAndSpell
== TRUE
)
494 SpellCheckWord(data
);
496 // do a keyword lookup only if we have a keyword list
497 if(data
->Keywords
!= NULL
)
501 // add the pastechar to the undobuffer and paste the current
503 AddToUndoBuffer(data
, ET_PASTECHAR
, NULL
);
504 PasteChars(data
, data
->CPos_X
++, data
->actualline
, 1, &key
, NULL
);
506 // check if the user selected the texteditor to do an automatic hard word
507 // wrapping during writing text and if so we go and perform the hard word
508 // wrapping at the correct border.
509 if(data
->WrapMode
== MUIV_TextEditor_WrapMode_HardWrap
&&
510 data
->WrapBorder
> 0 &&
511 data
->CPos_X
> data
->WrapBorder
&&
514 LONG xpos
= data
->WrapBorder
+1;
515 D(DBF_INPUT
, "must wrap");
517 // now we make sure to wrap *exactly* at the WrapBorder the user
518 // specified instead of wrapping right where we are.
519 while(xpos
> 0 && data
->actualline
->line
.Contents
[xpos
] != ' ')
522 // if we reached the end we go and find a wrap position after
526 xpos
= data
->WrapBorder
;
527 while(xpos
< data
->CPos_X
&& data
->actualline
->line
.Contents
[xpos
] != ' ')
531 D(DBF_INPUT
, "xpos=%ld cposx=%ld", xpos
, data
->CPos_X
);
532 // now we do the line split operation at the xpos we found
533 if(xpos
!= 0 && xpos
< data
->CPos_X
)
535 LONG length
= data
->CPos_X
-xpos
;
538 AddToUndoBuffer(data
, ET_SPLITLINE
, NULL
);
539 SplitLine(data
, data
->CPos_X
, data
->actualline
, TRUE
, NULL
);
540 AddToUndoBuffer(data
, ET_DELETECHAR
, &data
->actualline
->line
.Contents
[data
->CPos_X
]);
541 data
->CPos_X
= length
-1;
542 RemoveChars(data
, 0, data
->actualline
, 1);
554 static BOOL
ConvertKey(struct InstData
*data
, struct IntuiMessage
*imsg
)
558 struct InputEvent event
;
562 event
.ie_NextEvent
= NULL
;
563 event
.ie_Class
= IECLASS_RAWKEY
;
564 event
.ie_SubClass
= 0;
565 event
.ie_Code
= imsg
->Code
;
566 event
.ie_Qualifier
= imsg
->Qualifier
;
567 event
.ie_EventAddress
= (APTR
*) *((IPTR
*)imsg
->IAddress
);
569 if(MapRawKey(&event
, (STRPTR
)&code
, 1, NULL
) > 0)
571 SHOWVALUE(DBF_INPUT
, code
);
573 #ifdef FILTER_NONPRINTABLE
574 if((code
>= 32 && code
<= 126) || code
>= 160)
581 // now we perform the key action
582 Key_Normal(data
, code
);
594 static BOOL
ReactOnRawKey(struct InstData
*data
, struct IntuiMessage
*imsg
)
596 struct line_node
*oldactualline
= data
->actualline
;
597 LONG oldCPos_X
= data
->CPos_X
;
603 dummy
= FindKey(data
, imsg
->Code
, imsg
->Qualifier
);
605 D(DBF_INPUT
, "FindKey: %ld (%lx)", dummy
, imsg
->Code
);
607 if(dummy
== 1 || dummy
== 0)
612 if((data
->CPos_X
!= oldCPos_X
|| oldactualline
!= data
->actualline
) || (!(imsg
->Qualifier
& data
->blockqual
) && data
->blockinfo
.enabled
== TRUE
))
614 SetCursor(data
, oldCPos_X
, oldactualline
, FALSE
);
616 if(isFlagClear(data
->flags
, FLG_ReadOnly
))
618 if(imsg
->Qualifier
& data
->blockqual
)
620 data
->blockinfo
.stopline
= data
->actualline
;
621 data
->blockinfo
.stopx
= data
->CPos_X
;
622 if(data
->blockinfo
.enabled
== FALSE
)
624 data
->blockinfo
.enabled
= TRUE
;
625 data
->blockinfo
.startline
= oldactualline
;
626 data
->blockinfo
.startx
= oldCPos_X
;
629 MarkText(data
, oldCPos_X
, oldactualline
, data
->CPos_X
, data
->actualline
);
633 clearFlag(data
->flags
, FLG_ARexxMark
);
634 if(data
->blockinfo
.enabled
== TRUE
)
636 data
->blockinfo
.enabled
= FALSE
;
637 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
641 ScrollIntoDisplay(data
);
642 SetCursor(data
, data
->CPos_X
, data
->actualline
, TRUE
);
645 ScrollIntoDisplay(data
);
650 // we execute the ConvertKey() action which in fact will
651 // perform the actual key press reaction
652 if(isFlagClear(data
->flags
, FLG_ReadOnly
) && isFlagClear(imsg
->Qualifier
, IEQUALIFIER_RCOMMAND
))
653 ConvertKey(data
, imsg
);
666 IPTR
mHandleInput(struct IClass
*cl
, Object
*obj
, struct MUIP_HandleEvent
*msg
)
668 struct InstData
*data
= INST_DATA(cl
, obj
);
674 // check if the gadget was activate recently (via TAB key?)
675 wasActivated
= isFlagSet(data
->flags
, FLG_Activated
);
677 // clear the activated flag immediately
678 clearFlag(data
->flags
, FLG_Activated
);
680 D(DBF_INPUT
, "imsg->Code: %lx msg->muikey: %d", msg
->imsg
!= NULL
? msg
->imsg
->Code
: 0, msg
->muikey
);
682 if(msg
->muikey
!= MUIKEY_NONE
)
688 if(data
->KeyUpFocus
!= NULL
&& _win(obj
) != NULL
&& data
->actualline
== GetFirstLine(&data
->linelist
))
690 set(_win(obj
), MUIA_Window_ActiveObject
, data
->KeyUpFocus
);
691 result
= MUI_EventHandlerRC_Eat
;
699 result
= MUI_EventHandlerRC_Eat
;
705 if(isFlagSet(data
->flags
, FLG_ReadOnly
))
708 result
= MUI_EventHandlerRC_Eat
;
715 if(isFlagSet(data
->flags
, FLG_ReadOnly
))
718 result
= MUI_EventHandlerRC_Eat
;
725 if(isFlagSet(data
->flags
, FLG_ReadOnly
))
728 result
= MUI_EventHandlerRC_Eat
;
735 if(isFlagSet(data
->flags
, FLG_ReadOnly
))
738 result
= MUI_EventHandlerRC_Eat
;
747 RETURN(MUI_EventHandlerRC_Eat
);
748 return MUI_EventHandlerRC_Eat
;
750 else if(isFlagClear(data
->flags
, FLG_Ghosted
) &&
751 data
->shown
== TRUE
&&
756 struct IntuiMessage
*imsg
= msg
->imsg
;
758 // here we check if the GADGET_NEXT muikey has been used (usually associated with the
759 // TAB key) but if it is not the TAB key we activate the next object in the cyclechain because
760 // we need to catch the TAB key to use it for storing either \t or a specific number of
761 // spaces. So TE.mcc has to be TAB key aware. (note: MUIA_Window_DisableKeys is used in
762 // Dispatcher.c to make this happen)
763 if(msg
->muikey
== MUIKEY_GADGET_NEXT
&& imsg
->Code
!= 0x42)
765 set(_win(obj
), MUIA_Window_ActiveObject
, MUIV_Window_ActiveObject_Next
);
767 RETURN(MUI_EventHandlerRC_Eat
);
768 return MUI_EventHandlerRC_Eat
;
771 // next we check if TE.mcc is the currently active object in the window and if not and if
772 // it is also not the default object assigned to the window it is embedded we check if the
773 // CtrlChar has been pressed which would signal that we need to activate the object accordingly
774 // if the developer used MUIA_ControlChar correctly.
775 activeobj
= (Object
*)xget(_win(obj
), MUIA_Window_ActiveObject
);
776 defaultobj
= (Object
*)xget(_win(obj
), MUIA_Window_DefaultObject
);
778 D(DBF_INPUT
, "actobj: %08lx, defobj: %08lx, obj: %08lx", activeobj
, defaultobj
, obj
);
779 if(data
->CtrlChar
&& activeobj
!= obj
&& defaultobj
!= obj
&& RAWToANSI(imsg
) == data
->CtrlChar
)
781 set(_win(obj
), MUIA_Window_ActiveObject
, obj
);
783 RETURN(MUI_EventHandlerRC_Eat
);
784 return(MUI_EventHandlerRC_Eat
);
787 // we check if this is a mousemove input message and if
788 // so we check whether the mouse is currently over our
789 // texteditor object or not.
790 if(imsg
->Class
== IDCMP_MOUSEMOVE
)
792 BOOL isOverObject
= FALSE
;
794 if(_isinobject(obj
, msg
->imsg
->MouseX
, msg
->imsg
->MouseY
))
796 #if defined(__MORPHOS__)
801 if (isOverObject
== FALSE
)
803 struct Layer_Info
*li
= &(_screen(obj
)->LayerInfo
);
806 // get the layer that belongs to the current mouse coordinates
808 layer
= WhichLayer(li
, _window(obj
)->LeftEdge
+ msg
->imsg
->MouseX
, _window(obj
)->TopEdge
+ msg
->imsg
->MouseY
);
811 // if the mouse is currently over the object and over the object's
812 // window we go and change the pointer to show the selection pointer
813 if(layer
!= NULL
&& layer
->Window
== _window(obj
))
818 if(isOverObject
== TRUE
)
819 ShowSelectPointer(data
, obj
);
821 HideSelectPointer(data
, obj
);
823 D(DBF_INPUT
, "IDCMP_MOUSEMOVE");
826 #if defined(__amigaos4__)
827 if(imsg
->Class
== IDCMP_MOUSEBUTTONS
||
829 (isFlagSet(data
->flags
, FLG_ReadOnly
) && defaultobj
== obj
&& activeobj
== NULL
) ||
830 (imsg
->Class
== IDCMP_EXTENDEDMOUSE
&& isFlagSet(imsg
->Code
, IMSGCODE_INTUIWHEELDATA
)))
832 if(imsg
->Class
== IDCMP_MOUSEBUTTONS
||
834 (isFlagSet(data
->flags
, FLG_ReadOnly
) && defaultobj
== obj
&& activeobj
== NULL
) ||
835 (imsg
->Class
== IDCMP_RAWKEY
&& (imsg
->Code
== NM_WHEEL_UP
|| imsg
->Code
== NM_WHEEL_DOWN
|| imsg
->Code
== NM_WHEEL_LEFT
|| imsg
->Code
== NM_WHEEL_RIGHT
)))
838 if(isFlagClear(data
->flags
, FLG_Draw
))
840 data
->UpdateInfo
= msg
;
841 MUI_Redraw(obj
, MADF_DRAWUPDATE
);
843 RETURN((IPTR
)data
->UpdateInfo
);
844 return((IPTR
)data
->UpdateInfo
);
851 D(DBF_INPUT
, "HandleInput rawkey code=%02x qual=%04x", imsg
->Code
, imsg
->Qualifier
);
853 if(data
->ypos
!= _mtop(obj
) ||
854 (wasActivated
&& (msg
->muikey
== MUIKEY_GADGET_NEXT
|| imsg
->Code
== 0x42))) // ignore TAB key if the gadget was activated recently
856 RETURN(MUI_EventHandlerRC_Eat
);
857 return(MUI_EventHandlerRC_Eat
);
860 #if !defined(__amigaos4__)
861 // we check wheter the mouse is currently within our object borders
862 // and if so we check wheter the newmouse wheel stuff is used or not
864 _isinwholeobject(obj
, imsg
->MouseX
, imsg
->MouseY
))
866 // MouseWheel events are only possible if the mouse is above the
868 if(imsg
->Code
== NM_WHEEL_UP
|| imsg
->Code
== NM_WHEEL_LEFT
||
869 imsg
->Code
== NM_WHEEL_DOWN
|| imsg
->Code
== NM_WHEEL_RIGHT
)
871 LONG visible
= xget(obj
, MUIA_TextEditor_Prop_Visible
);
875 // we scroll about 1/6 of the displayed text by default
876 LONG delta
= (visible
+ 3) / 6;
878 // make sure that we scroll at least 1 line
879 if(delta
< 1) delta
= 1;
881 if(imsg
->Code
== NM_WHEEL_UP
|| imsg
->Code
== NM_WHEEL_LEFT
)
883 DoMethod(data
->slider
, MUIM_Prop_Decrease
, delta
);
886 DoMethod(data
->slider
, MUIM_Prop_Increase
, delta
);
889 RETURN(MUI_EventHandlerRC_Eat
);
890 return MUI_EventHandlerRC_Eat
;
895 // if not we check wheter we have to react on that particular RAWKEY event
896 if(ReactOnRawKey(data
, imsg
) == FALSE
)
898 D(DBF_INPUT
, "not reacted");
904 D(DBF_INPUT
, "reacted");
905 RETURN(MUI_EventHandlerRC_Eat
);
906 return(MUI_EventHandlerRC_Eat
);
911 #if defined(__amigaos4__)
912 case IDCMP_EXTENDEDMOUSE
:
915 _isinwholeobject(obj
, imsg
->MouseX
, imsg
->MouseY
))
917 LONG visible
= xget(obj
, MUIA_TextEditor_Prop_Visible
);
921 struct IntuiWheelData
*iwd
= (struct IntuiWheelData
*)imsg
->IAddress
;
923 // we scroll about 1/6 of the displayed text by default
924 LONG delta
= (visible
+ 3) / 6;
926 // make sure that we scroll at least 1 line
930 D(DBF_INPUT
, "WheelX: %ld WheelY: %ld", iwd
->WheelX
, iwd
->WheelY
);
932 if(iwd
->WheelY
< 0 || iwd
->WheelX
< 0)
933 DoMethod(data
->slider
, MUIM_Prop_Decrease
, delta
* abs(MIN(iwd
->WheelX
, iwd
->WheelY
)));
934 else if(iwd
->WheelY
> 0 || iwd
->WheelX
> 0)
935 DoMethod(data
->slider
, MUIM_Prop_Increase
, delta
* abs(MAX(iwd
->WheelX
, iwd
->WheelY
)));
938 RETURN(MUI_EventHandlerRC_Eat
);
939 return MUI_EventHandlerRC_Eat
;
948 case IDCMP_MOUSEBUTTONS
:
950 if(data
->ypos
!= _mtop(obj
))
956 if(imsg
->Code
== (IECODE_LBUTTON
| IECODE_UP_PREFIX
) && data
->mousemove
== TRUE
)
958 data
->mousemove
= FALSE
;
961 if(isFlagSet(data
->flags
, FLG_ReadOnly
) && isFlagSet(data
->flags
, FLG_AutoClip
) && Enabled(data
))
966 // user has pressed the left mousebutton
967 if(imsg
->Code
== IECODE_LBUTTON
)
969 if(imsg
->MouseX
>= _left(obj
) &&
970 imsg
->MouseX
<= _right(obj
) &&
971 imsg
->MouseY
>= data
->ypos
&&
972 imsg
->MouseY
< data
->ypos
+(data
->maxlines
* data
->fontheight
))
974 if(isFlagClear(data
->flags
, FLG_Active
) &&
975 isFlagClear(data
->flags
, FLG_Activated
) &&
976 isFlagSet(data
->flags
, FLG_ActiveOnClick
) &&
978 isFlagSet(imsg
->Qualifier
, IEQUALIFIER_CONTROL
))
980 // in case the user hold the control key while pressing in an
981 // inactive object we go and just activate it and let the MUIM_GoActive
982 // function refresh the selected area.
983 set(_win(obj
), MUIA_Window_ActiveObject
, obj
);
987 LONG last_x
= data
->CPos_X
;
988 struct line_node
*lastline
= data
->actualline
;
991 data
->mousemove
= TRUE
;
993 setFlag(data
->flags
, FLG_Activated
);
994 SetCursor(data
, data
->CPos_X
, data
->actualline
, FALSE
);
995 PosFromCursor(data
, imsg
->MouseX
, imsg
->MouseY
);
997 if(isFlagSet(imsg
->Qualifier
, IEQUALIFIER_LSHIFT
) || isFlagSet(imsg
->Qualifier
, IEQUALIFIER_RSHIFT
))
998 data
->selectmode
= 0;
1000 if(!(data
->blockinfo
.enabled
== TRUE
&& (isFlagSet(imsg
->Qualifier
, IEQUALIFIER_LSHIFT
) || isFlagSet(imsg
->Qualifier
, IEQUALIFIER_RSHIFT
))))
1002 // if we already have an enabled block we have to disable it
1003 // and clear the marking area with MarkText()
1006 data
->blockinfo
.enabled
= FALSE
;
1007 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
1010 data
->blockinfo
.enabled
= TRUE
;
1011 data
->blockinfo
.startline
= data
->actualline
;
1012 data
->blockinfo
.startx
= data
->CPos_X
;
1014 if(last_x
== data
->CPos_X
&& lastline
== data
->actualline
&& DoubleClick(data
->StartSecs
, data
->StartMicros
, imsg
->Seconds
, imsg
->Micros
))
1016 BOOL doubleClickHandled
;
1018 if(data
->DoubleClickHook
!= NULL
)
1020 // we have a user defined hook, let this one decide
1021 doubleClickHandled
= CallHook(data
->DoubleClickHook
, obj
, data
->actualline
->line
.Contents
, data
->CPos_X
, imsg
->Qualifier
);
1025 // no hook function, treat this double click as unhandled yet
1026 doubleClickHandled
= FALSE
;
1029 if(doubleClickHandled
== FALSE
)
1031 if(CheckSep(data
, data
->actualline
->line
.Contents
[data
->CPos_X
]) == FALSE
)
1033 if(data
->selectmode
> 0)
1035 GoStartOfLine(data
);
1036 data
->blockinfo
.startx
= data
->CPos_X
;
1039 // set selectmode to 2 so that PrintLine() knows that the user has tripleclicked
1040 // on a line, it will afterwards automatically set to 3 anyway.
1041 data
->selectmode
= 2;
1043 // reset the time values
1044 data
->StartSecs
= 0;
1045 data
->StartMicros
= 0;
1049 LONG x
= data
->CPos_X
;
1051 while(x
> 0 && CheckSep(data
, data
->actualline
->line
.Contents
[x
-1]) == FALSE
)
1054 data
->blockinfo
.startx
= x
;
1055 data
->selectmode
= 1;
1056 data
->StartSecs
= imsg
->Seconds
;
1057 data
->StartMicros
= imsg
->Micros
;
1062 // if the user clicked somewhere where we didn't find any separator
1063 // we have to check wheter this is already a tripleclick or still a doubleclick
1064 // because we ensure that on a tripleclick ALWAYS the whole line is marked
1065 // regardless if the user clicked on a actual word that can be separated or not.
1066 if(data
->selectmode
== 1)
1068 GoStartOfLine(data
);
1069 data
->blockinfo
.startx
= data
->CPos_X
;
1072 // set selectmode to 2 so that PrintLine() knows that the user has tripleclicked
1073 // on a line, it will afterwards automatically set to 3 anyway.
1074 data
->selectmode
= 2;
1076 // reset the time values
1077 data
->StartSecs
= 0;
1078 data
->StartMicros
= 0;
1082 if(data
->selectmode
== 0)
1083 data
->selectmode
= 1;
1086 data
->blockinfo
.enabled
= FALSE
;
1087 data
->selectmode
= 0;
1090 data
->StartSecs
= imsg
->Seconds
;
1091 data
->StartMicros
= imsg
->Micros
;
1097 // If the double click was handled externally we immediately assume to
1098 // have received the corresponding mouse release event as well.
1099 // It might happen that the double click hook launches a program which
1100 // steals our focus and leaves us pending in a "mouse pressed" situation.
1101 // This will result in an immediate marking action as soon as we regain
1102 // the focus. For example this happens when a URL is double clicked in
1103 // YAM and if launching the browser takes some seconds.
1104 if(data
->mousemove
== TRUE
)
1106 data
->mousemove
= FALSE
;
1109 if(isFlagSet(data
->flags
, FLG_ReadOnly
) && isFlagSet(data
->flags
, FLG_AutoClip
) && Enabled(data
))
1116 data
->blockinfo
.enabled
= FALSE
;
1117 data
->selectmode
= 0;
1118 data
->StartSecs
= imsg
->Seconds
;
1119 data
->StartMicros
= imsg
->Micros
;
1124 if(data
->blockinfo
.stopline
!= data
->actualline
|| data
->blockinfo
.stopx
!= data
->CPos_X
)
1125 MarkText(data
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
, data
->CPos_X
, data
->actualline
);
1128 data
->blockinfo
.stopline
= data
->actualline
;
1129 data
->blockinfo
.stopx
= data
->CPos_X
;
1131 SetCursor(data
, data
->CPos_X
, data
->actualline
, TRUE
);
1133 if(isFlagSet(data
->flags
, FLG_ActiveOnClick
))
1134 set(_win(obj
), MUIA_Window_ActiveObject
, obj
);
1159 RETURN(MUI_EventHandlerRC_Eat
);
1160 return(MUI_EventHandlerRC_Eat
);
1163 D(DBF_INPUT
, "ignore reached");
1172 static void DoBlock(struct InstData
*data
, ULONG flags
)
1176 data
->blockinfo
.enabled
= FALSE
;
1177 CutBlock(data
, flags
|CUTF_UPDATE
);
1184 static void EraseBlock(struct InstData
*data
, ULONG flags
)
1190 DoBlock(data
, flags
|CUTF_CUT
);
1191 data
->HasChanged
= TRUE
;
1195 DoMethod(data
->object
, MUIM_TextEditor_HandleError
, Error_NoAreaMarked
);
1203 void Key_Clear(struct InstData
*data
)
1207 EraseBlock(data
, 0);
1214 void Key_DelLine(struct InstData
*data
)
1218 if(data
->blockinfo
.enabled
== TRUE
)
1220 data
->blockinfo
.enabled
= FALSE
;
1221 MarkText(data
, data
->blockinfo
.startx
, data
->blockinfo
.startline
, data
->blockinfo
.stopx
, data
->blockinfo
.stopline
);
1224 if(HasNextLine(data
->actualline
) == FALSE
&& data
->actualline
->line
.Contents
[0] == '\n')
1227 GoStartOfLine(data
);
1229 data
->blockinfo
.startx
= data
->CPos_X
;
1230 data
->blockinfo
.startline
= data
->actualline
;
1232 // setFlag(data->flags, FLG_FreezeCrsr);
1235 data
->blockinfo
.stopx
= data
->CPos_X
;
1236 data
->blockinfo
.stopline
= data
->actualline
;
1237 data
->blockinfo
.enabled
= TRUE
;
1241 data
->blockinfo
.enabled
= FALSE
;
1243 // clearFlag(data->flags, FLG_FreezeCrsr);
1250 void Key_Cut(struct InstData
*data
)
1254 ScrollIntoDisplay(data
);
1255 EraseBlock(data
, CUTF_CLIPBOARD
);
1262 void Key_Copy(struct InstData
*data
)
1268 ScrollIntoDisplay(data
);
1269 DoBlock(data
, CUTF_CLIPBOARD
);
1272 DoMethod(data
->object
, MUIM_TextEditor_HandleError
, Error_NoAreaMarked
);
1279 void Key_Paste(struct InstData
*data
)
1281 struct marking block
;
1288 ScrollIntoDisplay(data
);
1290 block
.startline
= data
->actualline
;
1291 block
.startx
= data
->CPos_X
;
1292 // SetCursor(data, data->CPos_X, data->actualline, FALSE);
1293 data
->update
= FALSE
;
1294 if(PasteClip(data
, data
->CPos_X
, data
->actualline
) == TRUE
)
1296 block
.stopline
= data
->actualline
;
1297 block
.stopx
= data
->CPos_X
;
1298 AddToUndoBuffer(data
, ET_PASTEBLOCK
, &block
);
1302 data
->update
= TRUE
;
1309 void Key_Tab(struct InstData
*data
)
1316 ScrollIntoDisplay(data
);
1318 if(data
->ConvertTabs
== FALSE
)
1320 struct marking block
=
1329 SpellCheckWord(data
);
1331 AddToUndoBuffer(data
, ET_PASTEBLOCK
, &block
);
1333 PasteChars(data
, data
->CPos_X
-1, data
->actualline
, 1, "\t", NULL
);
1337 struct marking block
=
1343 data
->CPos_X
+data
->TabSize
1346 SpellCheckWord(data
);
1348 AddToUndoBuffer(data
, ET_PASTEBLOCK
, &block
);
1349 data
->CPos_X
+= data
->TabSize
;
1350 PasteChars(data
, data
->CPos_X
-data
->TabSize
, data
->actualline
, data
->TabSize
, " ", NULL
);
1358 void Key_Return(struct InstData
*data
)
1365 ScrollIntoDisplay(data
);
1367 SpellCheckWord(data
);
1369 AddToUndoBuffer(data
, ET_SPLITLINE
, NULL
);
1370 SplitLine(data
, data
->CPos_X
, data
->actualline
, TRUE
, NULL
);
1372 // make sure the cursor is visible
1373 ScrollIntoDisplay(data
);
1380 void Key_Backspace(struct InstData
*data
)
1388 if(data
->CPos_X
> 0)
1390 struct marking block
;
1392 // move the cursor position one to the left
1395 // define a block which consists of a single character only
1396 block
.enabled
= TRUE
;
1397 block
.startline
= data
->actualline
;
1398 block
.startx
= data
->CPos_X
;
1399 block
.stopline
= data
->actualline
;
1400 block
.stopx
= data
->CPos_X
+1;
1402 // add this single character block to the Undo buffer
1403 AddToUndoBuffer(data
, ET_DELETEBLOCK
, &block
);
1405 // erase the character
1406 RemoveChars(data
, data
->CPos_X
, data
->actualline
, 1);
1408 else if(HasPrevLine(data
->actualline
) == TRUE
)
1410 // merge two lines to a single line by appending
1411 // the current line to the previous
1412 data
->actualline
= GetPrevLine(data
->actualline
);
1413 data
->CPos_X
= data
->actualline
->line
.Length
-1;
1414 AddToUndoBuffer(data
, ET_BACKSPACEMERGE
, NULL
);
1415 ScrollIntoDisplay(data
);
1416 MergeLines(data
, data
->actualline
);
1419 ScrollIntoDisplay(data
);
1427 void Key_Delete(struct InstData
*data
)
1435 ScrollIntoDisplay(data
);
1437 if(data
->CPos_X
+1 < data
->actualline
->line
.Length
)
1439 struct marking block
;
1441 // define a block which consists of a single character only
1442 block
.enabled
= TRUE
;
1443 block
.startline
= data
->actualline
;
1444 block
.startx
= data
->CPos_X
;
1445 block
.stopline
= data
->actualline
;
1446 block
.stopx
= data
->CPos_X
+1;
1448 // add this single character block to the Undo buffer
1449 AddToUndoBuffer(data
, ET_DELETEBLOCK_NOMOVE
, &block
);
1451 // erase the character
1452 RemoveChars(data
, data
->CPos_X
, data
->actualline
, 1);
1454 else if(HasNextLine(data
->actualline
) == TRUE
)
1456 AddToUndoBuffer(data
, ET_MERGELINES
, NULL
);
1457 MergeLines(data
, data
->actualline
);
1465 /// ScrollIntoDisplay()
1466 /*------------------------------------------------------*
1467 * Make sure that the cursor is inside the visible area *
1468 *------------------------------------------------------*/
1469 void ScrollIntoDisplay(struct InstData
*data
)
1471 struct pos_info pos
;
1476 if(data
->shown
== TRUE
)
1478 OffsetToLines(data
, data
->CPos_X
, data
->actualline
, &pos
);
1479 diff
= pos
.lines
+ LineToVisual(data
, data
->actualline
) - 1;
1480 if(diff
> data
->maxlines
)
1482 data
->visual_y
+= diff
-data
->maxlines
;
1483 D(DBF_INPUT
,"scrollup: %ld", diff
-data
->maxlines
);
1488 data
->visual_y
+= diff
-1;
1489 D(DBF_INPUT
,"scrolldown: %ld", -diff
+1);
1499 /*------------------------*
1500 * Update the marked area *
1501 *------------------------*/
1502 void MarkText(struct InstData
*data
, LONG x1
, struct line_node
*line1
, LONG x2
, struct line_node
*line2
)
1504 struct marking newblock
;
1505 struct marking fakeblock
;
1506 struct line_node
*startline
;
1507 struct line_node
*stopline
;
1508 struct pos_info pos1
;
1509 struct pos_info pos2
;
1517 fakeblock
.startx
= x1
;
1518 fakeblock
.startline
= line1
;
1519 fakeblock
.stopx
= x2
;
1520 fakeblock
.stopline
= line2
;
1522 NiceBlock(&fakeblock
, &newblock
);
1523 startx
= newblock
.startx
;
1524 stopx
= newblock
.stopx
;
1525 startline
= newblock
.startline
;
1526 stopline
= newblock
.stopline
;
1528 line_nr1
= LineToVisual(data
, startline
) - 1;
1529 line_nr2
= LineToVisual(data
, stopline
) - 1;
1531 OffsetToLines(data
, startx
, startline
, &pos1
);
1532 OffsetToLines(data
, stopx
, stopline
, &pos2
);
1534 data
->blockinfo
.stopx
= x2
;
1535 data
->blockinfo
.stopline
= line2
;
1537 line_nr1
+= pos1
.lines
-1;
1541 line_nr2
+= pos2
.lines
-1;
1542 if(line_nr2
>= data
->maxlines
)
1543 line_nr2
= data
->maxlines
-1;
1545 if(line_nr1
<= line_nr2
)
1546 DumpText(data
, data
->visual_y
+line_nr1
, line_nr1
, line_nr2
+1, FALSE
);