revert between 56095 -> 55830 in arch
[AROS.git] / workbench / classes / zune / texteditor / mcc / HandleInput.c
blob079c8e53ec912133e06fbd7ffe73634a3cdec5c4
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
19 $Id$
21 ***************************************************************************/
23 #include <libraries/mui.h>
24 #include <intuition/classes.h>
26 #if !defined(__amigaos4__)
27 #include <clib/alib_protos.h>
28 #endif
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>
38 #include <stdlib.h>
40 #include "private.h"
41 #include "Debug.h"
43 #if !defined(__amigaos4__)
44 #include "newmouse.h"
45 #endif
47 /// RAWToANSI()
48 static ULONG RAWToANSI(struct IntuiMessage *imsg)
50 struct InputEvent event;
51 UBYTE code = 0;
53 ENTER();
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);
66 RETURN(code);
67 return(code);
70 ///
71 /// MatchQual()
72 static BOOL MatchQual(struct InstData *data, ULONG input, ULONG match, UWORD action)
74 BOOL result = FALSE;
76 ENTER();
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);
97 if(input == match)
98 result = TRUE;
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)
119 result = TRUE;
123 RETURN(result);
124 return result;
128 /// Key_DelSomething()
129 enum { Del_BOL = 0, Del_EOL, Del_BOW, Del_EOW };
130 static void Key_DelSomething(struct InstData *data, ULONG what)
132 ENTER();
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;
143 switch(what)
145 case Del_BOL:
146 GoStartOfLine(data);
147 break;
148 case Del_EOL:
149 setFlag(data->flags, FLG_FreezeCrsr);
150 GoEndOfLine(data);
151 break;
152 case Del_BOW:
153 GoPreviousWord(data);
154 break;
155 case Del_EOW:
156 setFlag(data->flags, FLG_FreezeCrsr);
157 GoNextWord(data);
158 break;
160 data->blockinfo.stopx = data->CPos_X;
161 data->blockinfo.stopline = data->actualline;
162 data->blockinfo.enabled = TRUE;
163 if(Enabled(data))
164 Key_Clear(data);
165 else
166 data->blockinfo.enabled = FALSE;
168 clearFlag(data->flags, FLG_FreezeCrsr);
170 LEAVE();
174 /// FindKey()
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;
181 int i;
182 UBYTE pressKey = key & ~IECODE_UP_PREFIX;
183 UBYTE releaseKey = key | IECODE_UP_PREFIX;
185 ENTER();
187 if(t_keys == NULL)
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
204 RETURN(5);
205 return 5;
208 if(isFlagSet(data->flags, FLG_ReadOnly))
210 LONG new_y = data->visual_y-1;
212 switch(curKey->act)
214 case MUIV_TextEditor_KeyAction_Top:
215 new_y = 0;
216 break;
218 case MUIV_TextEditor_KeyAction_PageUp:
219 new_y -= data->maxlines;
220 break;
222 case MUIV_TextEditor_KeyAction_Up:
223 new_y -= 1;
224 break;
226 case MUIV_TextEditor_KeyAction_Bottom:
227 new_y = data->totallines-data->maxlines;
228 break;
230 case MUIV_TextEditor_KeyAction_PageDown:
231 new_y += data->maxlines;
232 break;
234 case MUIV_TextEditor_KeyAction_Down:
235 new_y += 1;
236 break;
238 case MUIV_TextEditor_KeyAction_Copy:
239 Key_Copy(data);
240 break;
242 case MUIV_TextEditor_KeyAction_NextGadget:
243 set(_win(data->object), MUIA_Window_ActiveObject, MUIV_Window_ActiveObject_Next);
244 break;
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);
251 break;
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);
258 break;
262 if(new_y != data->visual_y-1)
264 if(new_y > data->totallines-data->maxlines)
265 new_y = data->totallines-data->maxlines;
266 if(new_y < 0)
267 new_y = 0;
268 set(data->object, MUIA_TextEditor_Prop_First, new_y*data->fontheight);
270 RETURN(4);
271 return(4);
274 else
276 D(DBF_INPUT, "curKey->act: %ld", curKey->act);
278 switch(curKey->act)
280 case MUIV_TextEditor_KeyAction_Top:
281 GoTop(data);
282 RETURN(TRUE);
283 return(TRUE);
285 case MUIV_TextEditor_KeyAction_PrevLine:
286 GoPreviousLine(data);
287 RETURN(TRUE);
288 return(TRUE);
290 case MUIV_TextEditor_KeyAction_PageUp:
291 GoPreviousPage(data);
292 RETURN(FALSE);
293 return(FALSE);
295 case MUIV_TextEditor_KeyAction_Up:
296 GoUp(data);
297 RETURN(FALSE);
298 return(FALSE);
300 case MUIV_TextEditor_KeyAction_Bottom:
301 GoBottom(data);
302 RETURN(TRUE);
303 return(TRUE);
305 case MUIV_TextEditor_KeyAction_NextLine:
306 GoNextLine(data);
307 RETURN(TRUE);
308 return(TRUE);
310 case MUIV_TextEditor_KeyAction_PageDown:
311 GoNextPage(data);
312 RETURN(FALSE);
313 return(FALSE);
315 case MUIV_TextEditor_KeyAction_Down:
316 GoDown(data);
317 RETURN(FALSE);
318 return(FALSE);
320 case MUIV_TextEditor_KeyAction_NextWord:
321 GoNextWord(data);
322 RETURN(TRUE);
323 return(TRUE);
325 case MUIV_TextEditor_KeyAction_NextSentence:
326 GoNextSentence(data);
327 RETURN(TRUE);
328 return(TRUE);
330 case MUIV_TextEditor_KeyAction_EndOfLine:
331 GoEndOfLine(data);
332 RETURN(TRUE);
333 return(TRUE);
335 case MUIV_TextEditor_KeyAction_Right:
336 GoRight(data);
337 RETURN(TRUE);
338 return(TRUE);
340 case MUIV_TextEditor_KeyAction_PrevWord:
341 GoPreviousWord(data);
342 RETURN(TRUE);
343 return(TRUE);
345 case MUIV_TextEditor_KeyAction_PrevSentence:
346 GoPreviousSentence(data);
347 RETURN(TRUE);
348 return(TRUE);
350 case MUIV_TextEditor_KeyAction_StartOfLine:
351 GoStartOfLine(data);
352 RETURN(TRUE);
353 return(TRUE);
355 case MUIV_TextEditor_KeyAction_Left:
356 GoLeft(data);
357 RETURN(TRUE);
358 return(TRUE);
360 case MUIV_TextEditor_KeyAction_SuggestWord:
361 SuggestWord(data);
362 break;
364 case MUIV_TextEditor_KeyAction_Backspace:
365 Key_Backspace(data);
366 break;
368 case MUIV_TextEditor_KeyAction_Delete:
369 Key_Delete(data);
370 break;
372 case MUIV_TextEditor_KeyAction_Return:
373 Key_Return(data);
374 break;
376 case MUIV_TextEditor_KeyAction_Tab:
377 Key_Tab(data);
378 break;
380 case MUIV_TextEditor_KeyAction_Undo:
381 Undo(data);
382 break;
384 case MUIV_TextEditor_KeyAction_Redo:
385 Redo(data);
386 break;
388 case MUIV_TextEditor_KeyAction_Cut:
389 Key_Cut(data);
390 break;
392 case MUIV_TextEditor_KeyAction_Copy:
393 Key_Copy(data);
394 break;
396 case MUIV_TextEditor_KeyAction_Paste:
397 Key_Paste(data);
398 break;
400 case MUIV_TextEditor_KeyAction_DelEOL:
401 Key_DelSomething(data, Del_EOL);
402 break;
404 case MUIV_TextEditor_KeyAction_DelBOL:
405 Key_DelSomething(data, Del_BOL);
406 break;
408 case MUIV_TextEditor_KeyAction_DelEOW:
409 Key_DelSomething(data, Del_EOW);
410 break;
412 case MUIV_TextEditor_KeyAction_DelBOW:
413 Key_DelSomething(data, Del_BOW);
414 break;
416 case MUIV_TextEditor_KeyAction_DelLine:
417 Key_DelLine(data);
418 break;
420 case MUIV_TextEditor_KeyAction_NextGadget:
421 set(_win(data->object), MUIA_Window_ActiveObject, MUIV_Window_ActiveObject_Next);
422 break;
424 case MUIV_TextEditor_KeyAction_GotoBookmark1:
425 GotoBookmark(data, 0);
426 RETURN(TRUE);
427 return(TRUE);
429 case MUIV_TextEditor_KeyAction_GotoBookmark2:
430 GotoBookmark(data, 1);
431 RETURN(TRUE);
432 return(TRUE);
434 case MUIV_TextEditor_KeyAction_GotoBookmark3:
435 GotoBookmark(data, 2);
436 RETURN(TRUE);
437 return(TRUE);
439 case MUIV_TextEditor_KeyAction_SetBookmark1:
440 SetBookmark(data, 0);
441 break;
443 case MUIV_TextEditor_KeyAction_SetBookmark2:
444 SetBookmark(data, 1);
445 break;
447 case MUIV_TextEditor_KeyAction_SetBookmark3:
448 SetBookmark(data, 2);
449 break;
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);
456 break;
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);
463 break;
466 RETURN(3);
467 return(3);
469 break;
473 RETURN(2);
474 return(2);
478 /// Key_Normal()
479 void Key_Normal(struct InstData *data, char key)
481 ENTER();
483 if(Enabled(data))
484 Key_Clear(data);
485 else
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)
498 KeywordCheck(data);
501 // add the pastechar to the undobuffer and paste the current
502 // key immediately.
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 &&
512 key != ' ')
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] != ' ')
520 xpos--;
522 // if we reached the end we go and find a wrap position after
523 // the wrap border
524 if(xpos == 0)
526 xpos = data->WrapBorder;
527 while(xpos < data->CPos_X && data->actualline->line.Contents[xpos] != ' ')
528 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;
537 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);
546 LEAVE();
550 /// ConvertKey()
551 /*----------------*
552 * Convert Rawkey *
553 *----------------*/
554 static BOOL ConvertKey(struct InstData *data, struct IntuiMessage *imsg)
556 BOOL result = FALSE;
557 UBYTE code = 0;
558 struct InputEvent event;
560 ENTER();
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)
575 #else
576 if(code >= 32)
577 #endif
579 data->pixel_x = 0;
581 // now we perform the key action
582 Key_Normal(data, code);
584 result = TRUE;
588 RETURN(result);
589 return(result);
593 /// ReactOnRawKey()
594 static BOOL ReactOnRawKey(struct InstData *data, struct IntuiMessage *imsg)
596 struct line_node *oldactualline = data->actualline;
597 LONG oldCPos_X = data->CPos_X;
598 BOOL result = TRUE;
599 LONG dummy;
601 ENTER();
603 dummy = FindKey(data, imsg->Code, imsg->Qualifier);
605 D(DBF_INPUT, "FindKey: %ld (%lx)", dummy, imsg->Code);
607 if(dummy == 1 || dummy == 0)
609 if(dummy == 1)
610 data->pixel_x = 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);
631 else
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);
644 else
645 ScrollIntoDisplay(data);
648 else if(dummy == 2)
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);
654 else
655 result = FALSE;
657 else if(dummy == 3)
658 data->pixel_x = 0;
660 RETURN(result);
661 return(result);
665 /// mHandleInput()
666 IPTR mHandleInput(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
668 struct InstData *data = INST_DATA(cl, obj);
669 IPTR result = 0;
670 BOOL wasActivated;
672 ENTER();
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)
684 switch(msg->muikey)
686 case MUIKEY_UP:
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;
694 break;
696 case MUIKEY_COPY:
698 Key_Copy(data);
699 result = MUI_EventHandlerRC_Eat;
701 break;
703 case MUIKEY_CUT:
705 if(isFlagSet(data->flags, FLG_ReadOnly))
707 Key_Cut(data);
708 result = MUI_EventHandlerRC_Eat;
711 break;
713 case MUIKEY_PASTE:
715 if(isFlagSet(data->flags, FLG_ReadOnly))
717 Key_Paste(data);
718 result = MUI_EventHandlerRC_Eat;
721 break;
723 case MUIKEY_UNDO:
725 if(isFlagSet(data->flags, FLG_ReadOnly))
727 Undo(data);
728 result = MUI_EventHandlerRC_Eat;
731 break;
733 case MUIKEY_REDO:
735 if(isFlagSet(data->flags, FLG_ReadOnly))
737 Redo(data);
738 result = MUI_EventHandlerRC_Eat;
741 break;
745 if(result != 0)
747 RETURN(MUI_EventHandlerRC_Eat);
748 return MUI_EventHandlerRC_Eat;
750 else if(isFlagClear(data->flags, FLG_Ghosted) &&
751 data->shown == TRUE &&
752 msg->imsg != NULL)
754 Object *activeobj;
755 Object *defaultobj;
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__)
797 if (IS_MORPHOS2)
798 isOverObject = TRUE;
799 #endif
801 if (isOverObject == FALSE)
803 struct Layer_Info *li = &(_screen(obj)->LayerInfo);
804 struct Layer *layer;
806 // get the layer that belongs to the current mouse coordinates
807 LockLayerInfo(li);
808 layer = WhichLayer(li, _window(obj)->LeftEdge + msg->imsg->MouseX, _window(obj)->TopEdge + msg->imsg->MouseY);
809 UnlockLayerInfo(li);
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))
814 isOverObject = TRUE;
818 if(isOverObject == TRUE)
819 ShowSelectPointer(data, obj);
820 else
821 HideSelectPointer(data, obj);
823 D(DBF_INPUT, "IDCMP_MOUSEMOVE");
825 else
826 #if defined(__amigaos4__)
827 if(imsg->Class == IDCMP_MOUSEBUTTONS ||
828 activeobj == obj ||
829 (isFlagSet(data->flags, FLG_ReadOnly) && defaultobj == obj && activeobj == NULL) ||
830 (imsg->Class == IDCMP_EXTENDEDMOUSE && isFlagSet(imsg->Code, IMSGCODE_INTUIWHEELDATA)))
831 #else
832 if(imsg->Class == IDCMP_MOUSEBUTTONS ||
833 activeobj == obj ||
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)))
836 #endif
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);
847 switch(imsg->Class)
849 case IDCMP_RAWKEY:
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
863 if(data->slider &&
864 _isinwholeobject(obj, imsg->MouseX, imsg->MouseY))
866 // MouseWheel events are only possible if the mouse is above the
867 // object
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);
873 if(visible > 0)
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);
885 else
886 DoMethod(data->slider, MUIM_Prop_Increase, delta);
889 RETURN(MUI_EventHandlerRC_Eat);
890 return MUI_EventHandlerRC_Eat;
893 #endif
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");
899 RETURN(0);
900 return(0);
902 else
904 D(DBF_INPUT, "reacted");
905 RETURN(MUI_EventHandlerRC_Eat);
906 return(MUI_EventHandlerRC_Eat);
909 break;
911 #if defined(__amigaos4__)
912 case IDCMP_EXTENDEDMOUSE:
914 if(data->slider &&
915 _isinwholeobject(obj, imsg->MouseX, imsg->MouseY))
917 LONG visible = xget(obj, MUIA_TextEditor_Prop_Visible);
919 if(visible > 0)
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
927 if(delta < 1)
928 delta = 1;
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;
942 RETURN(0);
943 return(0);
945 break;
946 #endif
948 case IDCMP_MOUSEBUTTONS:
950 if(data->ypos != _mtop(obj))
952 RETURN(0);
953 return(0);
956 if(imsg->Code == (IECODE_LBUTTON | IECODE_UP_PREFIX) && data->mousemove == TRUE)
958 data->mousemove = FALSE;
959 RejectInput(data);
961 if(isFlagSet(data->flags, FLG_ReadOnly) && isFlagSet(data->flags, FLG_AutoClip) && Enabled(data))
962 Key_Copy(data);
964 else
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) &&
977 Enabled(data) &&
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);
985 else
987 LONG last_x = data->CPos_X;
988 struct line_node *lastline = data->actualline;
990 RequestInput(data);
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()
1004 if(Enabled(data))
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);
1023 else
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;
1037 GoEndOfLine(data);
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;
1047 else
1049 LONG x = data->CPos_X;
1051 while(x > 0 && CheckSep(data, data->actualline->line.Contents[x-1]) == FALSE)
1052 x--;
1054 data->blockinfo.startx = x;
1055 data->selectmode = 1;
1056 data->StartSecs = imsg->Seconds;
1057 data->StartMicros = imsg->Micros;
1060 else
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;
1070 GoEndOfLine(data);
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;
1080 else
1082 if(data->selectmode == 0)
1083 data->selectmode = 1;
1084 else
1086 data->blockinfo.enabled = FALSE;
1087 data->selectmode = 0;
1090 data->StartSecs = imsg->Seconds;
1091 data->StartMicros = imsg->Micros;
1095 else
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;
1107 RejectInput(data);
1109 if(isFlagSet(data->flags, FLG_ReadOnly) && isFlagSet(data->flags, FLG_AutoClip) && Enabled(data))
1110 Key_Copy(data);
1114 else
1116 data->blockinfo.enabled = FALSE;
1117 data->selectmode = 0;
1118 data->StartSecs = imsg->Seconds;
1119 data->StartMicros = imsg->Micros;
1122 else
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);
1137 else
1139 RETURN(0);
1140 return(0);
1143 else
1145 RETURN(0);
1146 return(0);
1150 break;
1152 default:
1154 RETURN(0);
1155 return(0);
1159 RETURN(MUI_EventHandlerRC_Eat);
1160 return(MUI_EventHandlerRC_Eat);
1162 else
1163 D(DBF_INPUT, "ignore reached");
1166 RETURN(0);
1167 return(0);
1171 /// DoBlock()
1172 static void DoBlock(struct InstData *data, ULONG flags)
1174 ENTER();
1176 data->blockinfo.enabled = FALSE;
1177 CutBlock(data, flags|CUTF_UPDATE);
1179 LEAVE();
1183 /// EraseBlock()
1184 static void EraseBlock(struct InstData *data, ULONG flags)
1186 ENTER();
1188 if(Enabled(data))
1190 DoBlock(data, flags|CUTF_CUT);
1191 data->HasChanged = TRUE;
1193 else
1195 DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NoAreaMarked);
1198 LEAVE();
1202 /// Key_Clear()
1203 void Key_Clear(struct InstData *data)
1205 ENTER();
1207 EraseBlock(data, 0);
1209 LEAVE();
1213 /// Key_DelLine()
1214 void Key_DelLine(struct InstData *data)
1216 ENTER();
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')
1225 GoLeft(data);
1227 GoStartOfLine(data);
1229 data->blockinfo.startx = data->CPos_X;
1230 data->blockinfo.startline = data->actualline;
1232 // setFlag(data->flags, FLG_FreezeCrsr);
1233 GoEndOfLine(data);
1234 GoRight(data);
1235 data->blockinfo.stopx = data->CPos_X;
1236 data->blockinfo.stopline = data->actualline;
1237 data->blockinfo.enabled = TRUE;
1238 if(Enabled(data))
1239 Key_Clear(data);
1240 else
1241 data->blockinfo.enabled = FALSE;
1243 // clearFlag(data->flags, FLG_FreezeCrsr);
1245 LEAVE();
1249 /// Key_Cut()
1250 void Key_Cut(struct InstData *data)
1252 ENTER();
1254 ScrollIntoDisplay(data);
1255 EraseBlock(data, CUTF_CLIPBOARD);
1257 LEAVE();
1261 /// Key_Copy()
1262 void Key_Copy(struct InstData *data)
1264 ENTER();
1266 if(Enabled(data))
1268 ScrollIntoDisplay(data);
1269 DoBlock(data, CUTF_CLIPBOARD);
1271 else
1272 DoMethod(data->object, MUIM_TextEditor_HandleError, Error_NoAreaMarked);
1274 LEAVE();
1278 /// Key_Paste()
1279 void Key_Paste(struct InstData *data)
1281 struct marking block;
1283 ENTER();
1285 if(Enabled(data))
1286 Key_Clear(data);
1287 else
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);
1299 data->pixel_x = 0;
1301 else
1302 data->update = TRUE;
1304 LEAVE();
1308 /// Key_Tab()
1309 void Key_Tab(struct InstData *data)
1311 ENTER();
1313 if(Enabled(data))
1314 Key_Clear(data);
1315 else
1316 ScrollIntoDisplay(data);
1318 if(data->ConvertTabs == FALSE)
1320 struct marking block =
1322 TRUE,
1323 data->actualline,
1324 data->CPos_X,
1325 data->actualline,
1326 data->CPos_X+1
1329 SpellCheckWord(data);
1330 KeywordCheck(data);
1331 AddToUndoBuffer(data, ET_PASTEBLOCK, &block);
1332 data->CPos_X++;
1333 PasteChars(data, data->CPos_X-1, data->actualline, 1, "\t", NULL);
1335 else
1337 struct marking block =
1339 TRUE,
1340 data->actualline,
1341 data->CPos_X,
1342 data->actualline,
1343 data->CPos_X+data->TabSize
1346 SpellCheckWord(data);
1347 KeywordCheck(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);
1353 LEAVE();
1357 /// Key_Return()
1358 void Key_Return(struct InstData *data)
1360 ENTER();
1362 if(Enabled(data))
1363 Key_Clear(data);
1364 else
1365 ScrollIntoDisplay(data);
1367 SpellCheckWord(data);
1368 KeywordCheck(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);
1375 LEAVE();
1379 /// Key_Backspace()
1380 void Key_Backspace(struct InstData *data)
1382 ENTER();
1384 if(Enabled(data))
1385 Key_Clear(data);
1386 else
1388 if(data->CPos_X > 0)
1390 struct marking block;
1392 // move the cursor position one to the left
1393 data->CPos_X--;
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);
1422 LEAVE();
1426 /// Key_Delete()
1427 void Key_Delete(struct InstData *data)
1429 ENTER();
1431 if(Enabled(data))
1432 Key_Clear(data);
1433 else
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);
1461 LEAVE();
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;
1472 LONG diff;
1474 ENTER();
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);
1484 ScrollUpDown(data);
1486 else if(diff < 1)
1488 data->visual_y += diff-1;
1489 D(DBF_INPUT,"scrolldown: %ld", -diff+1);
1490 ScrollUpDown(data);
1494 LEAVE();
1498 /// MarkText()
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;
1510 LONG startx;
1511 LONG stopx;
1512 LONG line_nr1;
1513 LONG line_nr2;
1515 ENTER();
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;
1538 if(line_nr1 < 0)
1539 line_nr1 = 0;
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);
1548 LEAVE();