revert between 56095 -> 55830 in arch
[AROS.git] / workbench / libs / muimaster / classes / string.c
blob199b632f2423577cbd1e186d1aacc31f844a83be
1 /*
2 Copyright © 2003-2018, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 /* This is based on muimaster/class/text.c (first string version)
7 * and on rom/intuition/str*.c
8 */
10 #define MUIMASTER_YES_INLINE_STDARG
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <ctype.h>
17 #include <exec/types.h>
18 #include <clib/alib_protos.h>
20 #include <proto/exec.h>
21 #include <proto/dos.h>
22 #include <proto/graphics.h>
23 #include <proto/intuition.h>
24 #include <proto/utility.h>
25 #include <proto/muimaster.h>
26 #include <proto/locale.h>
28 #ifdef __AROS__
29 #include <devices/rawkeycodes.h>
30 #endif
32 #include "mui.h"
33 #include "muimaster_intern.h"
34 #include "support.h"
35 #include "prefs.h"
36 #include "penspec.h"
37 #include "imspec.h"
38 #include "clipboard.h"
39 #include "string.h"
41 //#define MYDEBUG 1
42 #include "debug.h"
44 extern struct Library *MUIMasterBase;
46 struct MUI_StringData
48 ULONG msd_Flags;
49 CONST_STRPTR msd_Accept; /* MUIA_String_Accept */
50 CONST_STRPTR msd_Reject; /* MUIA_String_Reject */
51 LONG msd_Align;
52 struct Hook *msd_EditHook;
53 Object *msd_AttachedList;
54 LONG msd_RedrawReason;
56 ULONG msd_useSecret;
57 /* Fields mostly ripped from rom/intuition/strgadgets.c */
58 STRPTR Buffer; /* char container */
59 STRPTR SecBuffer; /* Buffer for secret string */
60 STRPTR VisualBuffer; /* Pointer to either Buffer or SecBuffer */
61 ULONG BufferSize; /* memory allocated */
62 ULONG NumChars; /* string length */
63 ULONG BufferPos; /* cursor (insert/delete) position */
64 ULONG MarkPos; /* cursor text marking start pos */
65 LONG DispPos; /* leftmost visible char */
66 ULONG DispCount; /* number of visible chars */
68 ULONG OldClick_Sec;
69 ULONG OldClick_Micro;
70 ULONG NewClick_Sec;
71 ULONG NewClick_Micro;
72 WORD MultiClick;
74 WORD Columns;
76 struct MUI_EventHandlerNode ehn;
77 struct MUI_PenSpec_intern inactive_text;
78 struct MUI_PenSpec_intern active_text;
79 struct MUI_PenSpec_intern marked_text;
80 struct MUI_PenSpec_intern marked_bg;
81 struct MUI_PenSpec_intern cursor;
83 BOOL is_active;
86 #define MSDF_ADVANCEONCR (1<<0)
87 #define MSDF_LONELYEDITHOOK (1<<1)
88 #define MSDF_MARKING (1<<2)
89 #define MSDF_KEYMARKING (1<<3)
90 #define MSDF_NOINPUT (1<<4)
91 #define MSDF_STAYACTIVE (1<<5)
93 enum
95 NO_REASON = 0,
96 WENT_ACTIVE = 1,
97 WENT_INACTIVE,
98 DO_CURSOR_LEFT = 3,
99 DO_CURSOR_RIGHT,
100 DO_DELETE = 5,
101 DO_BACKSPACE,
102 DO_ADDCHAR = 7,
103 NEW_CONTENTS,
104 MOVE_CURSOR,
105 DO_UNKNOWN,
108 #define SECRET_CHAR '*'
111 /****** String.mui/MUIA_String_Accept ****************************************
113 * NAME
114 * MUIA_String_Accept -- (V4) [ISG], STRPTR
116 * FUNCTION
117 * A string containing the characters to be accepted by the string
118 * gadget. If NULL is specified, all characters are accepted. The
119 * character list is case sensitive.
121 * NOTES
122 * MUIA_String_Reject takes precedence over MUIA_String_Accept.
124 * The specified string is not cached within the object.
126 * SEE ALSO
127 * MUIA_String_Reject
129 ******************************************************************************
133 /****** String.mui/MUIA_String_Reject ****************************************
135 * NAME
136 * MUIA_String_Reject -- (V4) [ISG], STRPTR
138 * FUNCTION
139 * A string containing the characters to be rejected by the string
140 * gadget. If NULL is specified, no characters are rejected. The
141 * character list is case sensitive.
143 * NOTES
144 * MUIA_String_Reject takes precedence over MUIA_String_Accept.
146 * The specified string is not cached within the object.
148 * SEE ALSO
149 * MUIA_String_Accept
151 ******************************************************************************
155 /****** String.mui/MUIA_String_BufferPos *************************************
157 * NAME
158 * MUIA_String_BufferPos -- (V4) [ISG], STRPTR
160 * FUNCTION
161 * The cursor's position relative to the start of the string.
163 * NOTES
164 * The MUI AutoDocs claim this attribute is not initialisable.
166 ******************************************************************************
170 /**************************************************************************
171 Buffer_Alloc
172 Allocate memory for buffer
173 **************************************************************************/
174 static BOOL Buffer_Alloc(struct MUI_StringData *data)
176 UWORD i;
178 data->Buffer =
179 (STRPTR) AllocVec(data->BufferSize * sizeof(char), MEMF_ANY);
180 if (NULL == data->Buffer)
182 bug("[MUI:String] Buffer_Alloc: Failed to allocate %ld bytes for buffer1\n",
183 data->BufferSize * sizeof(char));
184 return FALSE;
186 if (data->msd_useSecret)
188 data->SecBuffer =
189 (STRPTR) AllocVec(data->BufferSize * sizeof(char), MEMF_ANY);
190 if (NULL == data->SecBuffer)
192 bug("[MUI:String] Buffer_Alloc: Failed to allocate %ld bytes for buffer2\n",
193 data->BufferSize * sizeof(char));
194 FreeVec(data->Buffer);
195 data->Buffer = NULL;
196 return FALSE;
199 /* Initialise maximum number of asterisks we'll need */
200 for (i = 0; i < data->BufferSize - 1; i++)
201 data->SecBuffer[i] = SECRET_CHAR;
203 data->VisualBuffer = data->SecBuffer;
205 else
206 data->VisualBuffer = data->Buffer;
208 return TRUE;
211 static inline VOID PrepareVisualBuffer(struct MUI_StringData *data)
213 if (data->msd_useSecret)
214 data->VisualBuffer[data->NumChars] = '\0';
217 static inline VOID CleanVisualBuffer(struct MUI_StringData *data)
219 if (data->msd_useSecret)
220 data->VisualBuffer[data->NumChars] = SECRET_CHAR;
223 /**************************************************************************
224 Buffer_SetNewContents
225 Initialize buffer with a string, replace former content if any
226 **************************************************************************/
227 static BOOL Buffer_SetNewContents(struct MUI_StringData *data,
228 CONST_STRPTR str)
230 if (NULL == data->Buffer)
231 return FALSE;
233 if (NULL == str)
235 data->Buffer[0] = 0;
236 data->NumChars = 0;
238 else
240 data->NumChars = strlen(str);
241 if (data->NumChars >= data->BufferSize)
242 data->NumChars = data->BufferSize - 1;
244 strncpy(data->Buffer, str, data->BufferSize);
245 data->Buffer[data->BufferSize - 1] = 0;
248 // avoid to BufferPos jumps to end of string if characters are inserted
249 // in string
250 if (data->BufferPos > data->NumChars)
251 data->BufferPos = data->NumChars;
252 data->DispPos = 0;
253 return TRUE;
256 /**************************************************************************
257 Buffer_AddChar
258 Add a char on cursor position
259 **************************************************************************/
260 static BOOL Buffer_AddChar(struct MUI_StringData *data, unsigned char code)
262 STRPTR dst;
264 if (data->Buffer == NULL)
265 return FALSE;
267 if (data->NumChars + 1 >= data->BufferSize)
268 return FALSE;
270 dst = &data->Buffer[data->BufferPos + 1];
272 memmove(dst, &data->Buffer[data->BufferPos],
273 data->NumChars - data->BufferPos);
275 dst[data->NumChars - data->BufferPos] = 0;
276 dst[-1] = code;
278 data->BufferPos++;
279 data->NumChars++;
280 return TRUE;
283 static WORD Buffer_GetWordStartIndex(struct MUI_StringData *data,
284 WORD startindex)
286 WORD index = startindex;
288 while (index > 0)
290 if (data->Buffer[index - 1] == ' ')
292 break;
294 index--;
297 return index;
300 static WORD Buffer_GetWordEndIndex(struct MUI_StringData *data,
301 WORD startindex)
303 WORD index = startindex;
305 while (index < data->NumChars)
307 if (data->Buffer[index] == ' ')
309 break;
311 index++;
314 return index;
317 static WORD Buffer_GetPrevWordIndex(struct MUI_StringData *data,
318 WORD startindex)
320 WORD index = startindex;
322 while (index > 0)
324 index--;
326 if ((index == 0) ||
327 ((data->Buffer[index - 1] == ' ') &&
328 (data->Buffer[index] != ' ')))
330 break;
335 return index;
338 static WORD Buffer_GetSuccWordIndex(struct MUI_StringData *data,
339 WORD startindex)
341 WORD index = startindex;
343 while (index < data->NumChars)
345 index++;
347 if ((index == data->NumChars) ||
348 ((data->Buffer[index - 1] == ' ') &&
349 (data->Buffer[index] != ' ')))
351 break;
355 return index;
358 static BOOL Buffer_GetMarkedRange(struct MUI_StringData *data, WORD *start,
359 WORD *stop)
361 WORD markstart = data->MarkPos;
362 WORD markstop = data->BufferPos;
364 markstart = MIN(markstart, data->NumChars);
365 markstart = MAX(markstart, 0);
367 markstop = MIN(markstop, data->NumChars);
368 markstop = MAX(markstop, 0);
370 if (markstart > markstop)
372 markstart ^= markstop;
373 markstop ^= markstart;
374 markstart ^= markstop;
377 switch (data->MultiClick)
379 case 0:
380 /* char select */
381 break;
383 case 1:
384 /* word select */
385 markstart = Buffer_GetWordStartIndex(data, markstart);
386 markstop = Buffer_GetWordEndIndex(data, markstop);
387 break;
389 default:
390 /* select all */
391 markstart = 0;
392 markstop = data->NumChars;
393 break;
397 if (markstart == markstop)
398 return FALSE;
400 if (start)
401 *start = markstart;
402 if (stop)
403 *stop = markstop;
405 //kprintf("Buffer_GetMarkedRange: returning %d .. %d\n",
406 // markstart, markstop);
408 return TRUE;
411 static BOOL Buffer_AnythingMarked(struct MUI_StringData *data)
413 if (!(data->msd_Flags & MSDF_MARKING))
414 return FALSE;
416 return Buffer_GetMarkedRange(data, NULL, NULL);
419 static BOOL Buffer_KillMarked(struct MUI_StringData *data)
421 WORD markstart = data->MarkPos;
422 WORD markstop = data->BufferPos;
423 WORD marklen;
425 //kprintf("\nBuffer_KillMarked 1 markpos %d bufferpos %d numchars %d\n",
426 // markstart, markstop, data->NumChars);
428 if (!(data->msd_Flags & MSDF_MARKING))
429 return FALSE;
431 data->msd_Flags &= ~MSDF_MARKING;
433 if (!Buffer_GetMarkedRange(data, &markstart, &markstop))
434 return FALSE;
436 //kprintf("Buffer_KillMarked 2 markstart %d markstop %d\n",
437 // markstart, markstop);
439 if (markstart > markstop)
441 markstart ^= markstop;
442 markstop ^= markstart;
443 markstart ^= markstop;
446 marklen = markstop - markstart;
448 //kprintf("Buffer_KillMarked: markstart %d markstop %d\n",
449 // markstart, markstop);
451 memmove(&data->Buffer[markstart],
452 &data->Buffer[markstart + marklen],
453 data->NumChars - markstart - marklen + 1);
454 data->NumChars -= marklen;
455 data->BufferPos = markstart;
457 return TRUE;
460 /**************************************************************************
461 OM_NEW
462 **************************************************************************/
463 IPTR String__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
465 struct MUI_StringData *data;
466 struct TagItem *tags, *tag;
467 CONST_STRPTR str = NULL;
468 char integerbuf[20];
470 obj = (Object *) DoSuperNewTags(cl, obj, NULL,
471 /* MUIA_FillArea, TRUE, */
472 TAG_MORE, (IPTR) msg->ops_AttrList);
473 if (!obj)
474 return FALSE;
476 data = INST_DATA(cl, obj);
477 data->msd_useSecret = FALSE;
478 data->msd_Align = MUIV_String_Format_Left;
479 data->BufferSize = 80;
480 data->Columns = -1;
482 Buffer_SetNewContents(data, ""); /* <-- isn't this pointless? */
484 /* parse initial taglist */
485 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
487 switch (tag->ti_Tag)
489 case MUIA_String_Accept:
490 data->msd_Accept = (CONST_STRPTR) tag->ti_Data;
491 break;
493 case MUIA_String_Reject:
494 data->msd_Reject = (CONST_STRPTR) tag->ti_Data;
495 break;
497 case MUIA_String_AdvanceOnCR:
498 _handle_bool_tag(data->msd_Flags, tag->ti_Data,
499 MSDF_ADVANCEONCR);
500 break;
502 case MUIA_String_BufferPos:
503 data->BufferPos = (ULONG) tag->ti_Data;
504 break;
506 case MUIA_String_AttachedList:
507 data->msd_AttachedList = (Object *) tag->ti_Data;
508 break;
510 case MUIA_String_Secret:
511 data->msd_useSecret = (BOOL) tag->ti_Data;
512 break;
514 case MUIA_String_Contents:
515 str = (CONST_STRPTR) tag->ti_Data;
516 break;
518 case MUIA_String_EditHook:
519 data->msd_EditHook = (struct Hook *)tag->ti_Data;
520 break;
522 case MUIA_String_Format:
523 data->msd_Align = (LONG) tag->ti_Data;
524 break;
526 case MUIA_String_Integer:
527 snprintf(integerbuf, 19, "%ld", tag->ti_Data);
528 str = integerbuf;
529 break;
531 case MUIA_String_LonelyEditHook:
532 _handle_bool_tag(data->msd_Flags, tag->ti_Data,
533 MSDF_LONELYEDITHOOK);
534 break;
536 case MUIA_String_MaxLen:
537 data->BufferSize = tag->ti_Data;
538 if (data->BufferSize < 1)
539 data->BufferSize = 1;
540 break;
542 case MUIA_String_Columns: /* BetterString */
543 data->Columns = (WORD) tag->ti_Data;
544 break;
546 case MUIA_String_NoInput: /* BetterString */
547 _handle_bool_tag(data->msd_Flags, tag->ti_Data, MSDF_NOINPUT);
548 break;
550 case MUIA_String_StayActive: /* BetterString */
551 _handle_bool_tag(data->msd_Flags, tag->ti_Data,
552 MSDF_STAYACTIVE);
553 break;
558 if (Buffer_Alloc(data))
560 Buffer_SetNewContents(data, str);
563 if (NULL == data->Buffer)
565 CoerceMethod(cl, obj, OM_DISPOSE);
566 return 0;
569 D(bug("[MUI:String] New(%p)\n", obj));
571 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS;
572 data->ehn.ehn_Priority = 0;
573 data->ehn.ehn_Flags = 0;
574 data->ehn.ehn_Object = obj;
575 data->ehn.ehn_Class = cl;
577 CurrentTime(&data->OldClick_Sec, &data->OldClick_Micro);
579 return (IPTR) obj;
582 /**************************************************************************
583 OM_DISPOSE
584 **************************************************************************/
585 IPTR String__OM_DISPOSE(struct IClass *cl, Object *obj, Msg msg)
587 struct MUI_StringData *data = INST_DATA(cl, obj);
589 FreeVec(data->Buffer);
590 FreeVec(data->SecBuffer);
592 D(bug("[MUI:String] Dispose %p\n", obj));
594 return DoSuperMethodA(cl, obj, msg);
597 /**************************************************************************
598 OM_SET
599 **************************************************************************/
600 IPTR String__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
602 struct MUI_StringData *data = INST_DATA(cl, obj);
603 struct TagItem *tags = msg->ops_AttrList;
604 struct TagItem *tag;
606 while ((tag = NextTagItem(&tags)) != NULL)
608 switch (tag->ti_Tag)
610 case MUIA_String_Contents:
611 Buffer_SetNewContents(data, (STRPTR) tag->ti_Data);
612 data->msd_RedrawReason = NEW_CONTENTS;
613 data->msd_Flags &= ~(MSDF_MARKING | MSDF_KEYMARKING);
614 MUI_Redraw(obj, MADF_DRAWOBJECT);
615 break;
617 case MUIA_String_Accept:
618 data->msd_Accept = (CONST_STRPTR) tag->ti_Data;
619 break;
621 case MUIA_String_Reject:
622 data->msd_Reject = (CONST_STRPTR) tag->ti_Data;
623 break;
625 case MUIA_String_AttachedList:
626 data->msd_AttachedList = (Object *) tag->ti_Data;
627 break;
629 case MUIA_String_Integer:
631 char buf[20];
633 snprintf(buf, 19, "%ld", tag->ti_Data);
634 set(obj, MUIA_String_Contents, buf);
636 break;
638 case MUIA_String_AdvanceOnCR:
639 _handle_bool_tag(data->msd_Flags, tag->ti_Data,
640 MSDF_ADVANCEONCR);
641 break;
643 case MUIA_String_BufferPos:
644 data->BufferPos = (ULONG) tag->ti_Data;
645 data->msd_Flags &= ~MSDF_MARKING;
646 data->msd_RedrawReason = MOVE_CURSOR;
647 MUI_Redraw(obj, MADF_DRAWUPDATE);
648 break;
650 case MUIA_String_DisplayPos:
651 data->BufferPos = (ULONG) tag->ti_Data;
652 data->DispPos = data->BufferPos; // move both pos
653 data->msd_Flags &= ~MSDF_MARKING;
654 data->msd_RedrawReason = MOVE_CURSOR;
655 MUI_Redraw(obj, MADF_DRAWUPDATE);
656 break;
658 case MUIA_String_NoInput: /* BetterString */
659 _handle_bool_tag(data->msd_Flags, tag->ti_Data, MSDF_NOINPUT);
660 break;
662 case MUIA_String_StayActive: /* BetterString */
663 _handle_bool_tag(data->msd_Flags, tag->ti_Data,
664 MSDF_STAYACTIVE);
665 break;
667 case MUIA_String_SelectSize: /* BetterString */
668 // TODO: Implement OM_SET(MUIA_String_SelectSize)!
669 break;
674 return DoSuperMethodA(cl, obj, (Msg) msg);
678 /**************************************************************************
679 OM_GET
680 **************************************************************************/
681 IPTR String__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
683 struct MUI_StringData *data = INST_DATA(cl, obj);
685 #define STORE *(msg->opg_Storage)
686 switch (msg->opg_AttrID)
688 case MUIA_String_Contents:
689 STORE = (IPTR) data->Buffer;
690 return TRUE;
692 case MUIA_String_Secret:
693 STORE = (IPTR) data->msd_useSecret;
694 return TRUE;
696 case MUIA_String_Accept:
697 STORE = (IPTR) data->msd_Accept;
698 return TRUE;
700 case MUIA_String_Reject:
701 STORE = (IPTR) data->msd_Reject;
702 return TRUE;
704 case MUIA_String_AttachedList:
705 STORE = (IPTR) data->msd_AttachedList;
706 return TRUE;
708 case MUIA_String_Format:
709 STORE = (IPTR) data->msd_Align;
710 return TRUE;
712 case MUIA_String_Integer:
714 STRPTR buf = NULL;
715 STORE = 0;
716 get(obj, MUIA_String_Contents, &buf);
717 if (NULL != buf)
719 LONG val = 0;
720 StrToLong(buf, &val);
721 STORE = val;
723 return TRUE;
726 case MUIA_String_MaxLen:
727 STORE = (IPTR) data->BufferSize;
728 return TRUE;
730 case MUIA_String_AdvanceOnCR:
731 STORE = (data->msd_Flags & MSDF_ADVANCEONCR) ? TRUE : FALSE;
732 return TRUE;
734 case MUIA_String_BufferPos:
735 STORE = data->BufferPos;
736 return TRUE;
738 case MUIA_String_DisplayPos:
739 STORE = data->DispPos;
740 return TRUE;
742 case MUIA_String_NoInput: /* BetterString */
743 STORE = (data->msd_Flags & MSDF_NOINPUT) ? TRUE : FALSE;
744 return TRUE;
746 case MUIA_String_StayActive: /* BetterString */
747 STORE = (data->msd_Flags & MSDF_STAYACTIVE) ? TRUE : FALSE;
748 return TRUE;
750 case MUIA_String_SelectSize: /* BetterString */
751 if (data->msd_Flags & MSDF_MARKING)
753 WORD markstart, markstop;
755 if (Buffer_GetMarkedRange(data, &markstart, &markstop))
757 LONG size = markstop - markstart;
759 if (data->MarkPos < data->BufferPos)
760 size = -size;
762 STORE = (IPTR) size;
764 return 1;
769 STORE = 0;
770 return TRUE;
774 return DoSuperMethodA(cl, obj, (Msg) msg);
775 #undef STORE
778 /**************************************************************************
779 MUIM_Setup
780 **************************************************************************/
781 IPTR String__MUIM_Setup(struct IClass *cl, Object *obj,
782 struct MUIP_Setup *msg)
784 struct MUI_StringData *data = INST_DATA(cl, obj);
786 if (0 == DoSuperMethodA(cl, obj, (Msg) msg))
787 return FALSE;
789 data->is_active = FALSE;
790 set(obj, MUIA_Background,
791 (IPTR) muiGlobalInfo(obj)->mgi_Prefs->string_bg_inactive);
793 zune_pen_spec_to_intern(
794 (const struct MUI_PenSpec *)muiGlobalInfo(obj)->mgi_Prefs->
795 string_text_inactive, &data->inactive_text);
796 zune_penspec_setup(&data->inactive_text, muiRenderInfo(obj));
798 zune_pen_spec_to_intern(
799 (const struct MUI_PenSpec *)muiGlobalInfo(obj)->mgi_Prefs->
800 string_text_active, &data->active_text);
801 zune_penspec_setup(&data->active_text, muiRenderInfo(obj));
803 zune_pen_spec_to_intern(
804 (const struct MUI_PenSpec *)muiGlobalInfo(obj)->mgi_Prefs->
805 string_text_marked, &data->marked_text);
806 zune_penspec_setup(&data->marked_text, muiRenderInfo(obj));
808 zune_pen_spec_to_intern(
809 (const struct MUI_PenSpec *)muiGlobalInfo(obj)->mgi_Prefs->
810 string_bg_marked, &data->marked_bg);
811 zune_penspec_setup(&data->marked_bg, muiRenderInfo(obj));
813 zune_pen_spec_to_intern(
814 (const struct MUI_PenSpec *)muiGlobalInfo(obj)->mgi_Prefs->
815 string_cursor, &data->cursor);
816 zune_penspec_setup(&data->cursor, muiRenderInfo(obj));
818 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) & data->ehn);
819 return TRUE;
822 /**************************************************************************
823 MUIM_Cleanup
824 **************************************************************************/
825 IPTR String__MUIM_Cleanup(struct IClass *cl, Object *obj,
826 struct MUIP_Cleanup *msg)
828 struct MUI_StringData *data = INST_DATA(cl, obj);
830 D(bug("[MUI:String] Cleanup %p\n", obj));
831 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) & data->ehn);
833 zune_penspec_cleanup(&data->inactive_text);
834 zune_penspec_cleanup(&data->active_text);
835 zune_penspec_cleanup(&data->marked_text);
836 zune_penspec_cleanup(&data->marked_bg);
837 zune_penspec_cleanup(&data->cursor);
839 return (DoSuperMethodA(cl, obj, (Msg) msg));
842 /**************************************************************************
843 MUIM_AskMinMax
844 **************************************************************************/
845 IPTR String__MUIM_AskMinMax(struct IClass *cl, Object *obj,
846 struct MUIP_AskMinMax *msg)
848 struct MUI_StringData *data = INST_DATA(cl, obj);
850 DoSuperMethodA(cl, obj, (Msg) msg);
852 if (data->Columns >= 0)
854 msg->MinMaxInfo->MinWidth += _font(obj)->tf_XSize * data->Columns;
855 msg->MinMaxInfo->DefWidth += _font(obj)->tf_XSize * data->Columns;
856 msg->MinMaxInfo->MaxWidth += _font(obj)->tf_XSize * data->Columns;
858 else
860 msg->MinMaxInfo->MinWidth += _font(obj)->tf_XSize * 4;
861 msg->MinMaxInfo->DefWidth += _font(obj)->tf_XSize * 12;
862 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
865 msg->MinMaxInfo->MinHeight += _font(obj)->tf_YSize;
866 msg->MinMaxInfo->DefHeight += _font(obj)->tf_YSize;
867 msg->MinMaxInfo->MaxHeight += _font(obj)->tf_YSize;
869 /* D(bug("String_AskMinMax(%p): Min=%ldx%ld Max=%ldx%ld Def=%ldx%ld\n", */
870 /* obj, msg->MinMaxInfo->MinWidth, msg->MinMaxInfo->MinHeight, */
871 /* msg->MinMaxInfo->MaxWidth, msg->MinMaxInfo->MaxHeight, */
872 /* msg->MinMaxInfo->DefWidth, msg->MinMaxInfo->DefHeight)); */
874 return TRUE;
878 static WORD MaxDispPos(struct IClass *cl, Object *obj)
880 struct MUI_StringData *data = INST_DATA(cl, obj);
881 WORD numfit, max_disppos, numchars;
882 struct TextExtent te;
883 BOOL cursor_at_end;
885 cursor_at_end = (data->BufferPos == data->NumChars);
887 /* D(bug("MaxDispPos(current length: %d, bufferpos=%d)\n", */
888 /* data->NumChars, data->BufferPos)); */
890 /* D(bug("cursor_at_end: %d\n", cursor_at_end)); */
892 if (cursor_at_end) /* Cursor at end of string ? */
894 /* D(bug("Making cursor last char\n")); */
895 numchars = data->NumChars + 1; /* Take cursor into account */
897 /* This has already been done by UpdateDisp() which called us
898 strinfo->Buffer[strinfo->NumChars] = 0x20;
902 else
904 numchars = data->NumChars;
907 /* Find the amount of characters that fit into the bbox, counting
908 ** from the last character in the buffer and forward,
910 numfit = TextFit(_rp(obj),
911 &(data->Buffer[numchars - 1]),
912 numchars, &te, NULL, -1, _mwidth(obj), _mheight(obj));
914 max_disppos = numchars - numfit;
916 /* if ((max_disppos > 0) && (!cursor_at_end))
917 max_disppos --;
920 /* D(bug("Numchars w/cursor: %d, Numfit: %d, maxdisppos=%d " */
921 /* "bbox->Width = %d te->te_Width = %d\n", */
922 /* numchars, numfit, max_disppos, _mwidth(obj), te.te_Width)); */
924 return max_disppos;
928 static void UpdateDisp(struct IClass *cl, Object *obj)
930 struct MUI_StringData *data = INST_DATA(cl, obj);
931 struct TextExtent te;
932 STRPTR dispstr;
934 /* If the cursor is at the trailing \0, insert a SPACE instead */
935 if (data->BufferPos == data->NumChars)
936 data->VisualBuffer[data->NumChars] = ' ';
938 /* In this function we check if the cursor has gone outside
939 ** of the visible area (because of application setting
940 ** strinfo->BufferPos or strinfo->DispPos to a different value, or
941 ** because of user input).
942 ** This is made a bit difficult by the rule (R), that there
943 ** should NOT be available space on the right, and characters
944 ** scrolled out at the left, at the same time.
945 ** We have 3 possible scenarios:
946 ** 1) Cursor to the left of DispPos:
947 ** Set DispPos to the lowest of BufferPos and the
948 ** maximum allowed disppos (according to (R) ).
949 ** 2) Cursor to the right of visible area:
950 ** Set dispose sou that the cursor is the last visible character.
951 ** This afheres to (R).
952 ** 3) Cursor inside visible area. Do a check on rule (R),
953 ** and if DispPos > max allowed, then adjust it down,
954 ** so that the last character in the buffer becomes last character
955 ** displayed. (The cursor will still be visible after adjustion)
958 /* 1) Cursor to the left of visible area */
959 if (data->BufferPos < data->DispPos)
961 WORD max_disppos;
963 max_disppos = MaxDispPos(cl, obj);
964 data->DispPos = MIN(data->BufferPos, max_disppos);
966 else /* Cursor equal to the right of disppos [ 2) or 3) ] */
968 UWORD strsize;
970 /* How many pixels are there from current 1st displayed to cursor? */
971 strsize = TextLength(_rp(obj),
972 data->VisualBuffer + data->DispPos,
973 data->BufferPos - data->DispPos + 1);
975 /* 2) More than fits into the gadget ? */
976 if (strsize > _mwidth(obj))
978 /* Compute new DispPos such that the cursor is at the right */
979 data->DispPos = data->BufferPos
980 - TextFit(_rp(obj),
981 &(data->VisualBuffer[data->BufferPos]),
982 data->NumChars, &te, NULL, -1,
983 _mwidth(obj), _mheight(obj)) + 1;
985 /* D(bug("cursor right of visible area, new disppos: %d\n", */
986 /* data->DispPos)); */
988 else /* 3). Cursor inside gadget */
990 WORD max_disppos;
992 max_disppos = MaxDispPos(cl, obj);
993 if (data->DispPos > max_disppos)
994 data->DispPos = max_disppos;
996 } /* if (cursor inside or to the right of visible area ) */
1000 /* Update the DispCount */
1001 /* It might be necessary with special handling for centre aligned gads */
1002 dispstr = &(data->VisualBuffer[data->DispPos]);
1003 /* D(bug("DispCount before = %d\n", data->DispCount)); */
1004 data->DispCount = TextFit(_rp(obj), dispstr,
1005 data->NumChars - data->DispPos,
1006 &te, NULL, 1, _mwidth(obj), _mheight(obj));
1007 /* D(bug("DispCount after = %d\n", data->DispCount)); */
1009 /* 0-terminate string (in case we put a SPACE at the end) */
1010 data->VisualBuffer[data->NumChars] = '\0';
1014 /* Gets left position of text in the string gadget */
1015 static UWORD GetTextLeft(struct IClass *cl, Object *obj)
1017 struct MUI_StringData *data = INST_DATA(cl, obj);
1018 UWORD text_left = 0;
1019 STRPTR dispstr = &(data->VisualBuffer[data->DispPos]);
1020 UWORD dispstrlen;
1021 BOOL cursor_at_end;
1023 cursor_at_end = (data->BufferPos == data->NumChars);
1024 dispstrlen = MIN(data->DispCount, data->NumChars - data->DispPos);
1026 switch (data->msd_Align)
1028 case MUIV_String_Format_Left:
1029 text_left = _mleft(obj);
1030 break;
1032 case MUIV_String_Format_Center:
1034 WORD textwidth = TextLength(_rp(obj), dispstr, dispstrlen);
1035 if (cursor_at_end)
1036 textwidth += TextLength(_rp(obj), " ", 1);
1037 text_left = _mleft(obj) + ((_mwidth(obj) - textwidth) / 2);
1038 /* D(bug("GetTextLeft: dispstr=%s, dispstrlen=%d, textw=%d, " */
1039 /* "textl=%d\n", */
1040 /* dispstr, dispstrlen, textwidth, text_left)); */
1042 break;
1044 case MUIV_String_Format_Right:
1046 WORD textwidth = TextLength(_rp(obj), dispstr, dispstrlen);
1048 if (cursor_at_end)
1049 textwidth += TextLength(_rp(obj), " ", 1);
1050 text_left = _mleft(obj) + (_mwidth(obj) - 1 - textwidth);
1052 break;
1054 return (text_left);
1057 /* Gets right offset of text in the string gadget */
1058 static UWORD GetTextRight(struct IClass *cl, Object *obj)
1060 struct MUI_StringData *data = INST_DATA(cl, obj);
1061 UWORD text_right = 0;
1062 STRPTR dispstr = &(data->VisualBuffer[data->DispPos]);
1063 UWORD dispstrlen;
1064 BOOL cursor_at_end;
1066 cursor_at_end = (data->BufferPos == data->NumChars);
1067 dispstrlen = MIN(data->DispCount, data->NumChars - data->DispPos);
1069 switch (data->msd_Align)
1071 case MUIV_String_Format_Left:
1072 text_right =
1073 _mleft(obj) + TextLength(_rp(obj), dispstr, dispstrlen);
1074 break;
1076 case MUIV_String_Format_Center:
1078 WORD textwidth = TextLength(_rp(obj), dispstr, dispstrlen);
1080 if (cursor_at_end)
1081 textwidth += TextLength(_rp(obj), " ", 1);
1082 text_right = _mright(obj) - ((_mwidth(obj) - textwidth) / 2);
1084 break;
1086 case MUIV_String_Format_Right:
1087 text_right = _mright(obj);
1088 break;
1090 return (text_right);
1094 /* Updates the stringdata in case user has set some fields */
1095 static VOID UpdateStringData(struct IClass *cl, Object *obj)
1097 struct MUI_StringData *data = INST_DATA(cl, obj);
1099 data->NumChars = strlen(data->Buffer);
1101 if (data->BufferPos > data->NumChars)
1103 data->BufferPos = data->NumChars;
1107 static VOID TextM(Object *obj, struct MUI_StringData *data,
1108 STRPTR text, WORD textlen, WORD markstart, WORD markend)
1110 struct RastPort *rp = _rp(obj);
1111 ULONG textpen;
1112 WORD len;
1114 if (data->is_active)
1115 textpen = data->active_text.p_pen;
1116 else
1117 textpen = data->inactive_text.p_pen;
1119 //kprintf("TextM: textlen %d markstart %d markend %d ... \n",
1120 // textlen, markstart, markend);
1122 /* <unmarked><marked><unmarked> */
1124 /* <unmarked> */
1126 len = MIN(markstart, textlen);
1127 len = MAX(len, 0);
1129 if (len)
1131 //kprintf("A: %d ", len);
1133 SetABPenDrMd(rp, textpen, _pens(obj)[MPEN_BACKGROUND], JAM1);
1134 Text(rp, text, len);
1136 text += len;
1137 textlen -= len;
1141 len = MIN(markend - len, textlen);
1142 len = MAX(len, 0);
1144 if (len)
1146 //kprintf("B: %d ", len);
1147 SetABPenDrMd(_rp(obj), data->marked_text.p_pen,
1148 data->marked_bg.p_pen, JAM2);
1149 Text(rp, text, len);
1151 text += len;
1152 textlen -= len;
1155 if (textlen)
1157 //kprintf("C: %d ", textlen);
1159 SetABPenDrMd(rp, textpen, _pens(obj)[MPEN_BACKGROUND], JAM1);
1160 Text(rp, text, textlen);
1162 //kprintf("\n");
1165 /**************************************************************************
1166 MUIM_Draw
1167 **************************************************************************/
1168 IPTR String__MUIM_Draw(struct IClass *cl, Object *obj,
1169 struct MUIP_Draw *msg)
1171 struct MUI_StringData *data = INST_DATA(cl, obj);
1172 UWORD text_left;
1173 UWORD text_top;
1174 STRPTR dispstr;
1175 UWORD dispstrlen;
1176 ULONG textpen;
1177 UWORD textleft_save;
1178 WORD markstart = 0, markstop = 0;
1180 /* D(bug("\nString_Draw(%p) %ldx%ldx%ldx%ld reason=%ld msgflgs=%ld " */
1181 /* "curs=%d " */
1182 /* "displ=%ld len=%ld buf='%s'\n",obj,_mleft(obj),_mtop(obj), */
1183 /* _mwidth(obj),_mheight(obj), data->msd_RedrawReason, msg->flags, */
1184 /* data->BufferPos, data->DispPos, data->NumChars, data->Buffer)); */
1186 DoSuperMethodA(cl, obj, (Msg) msg);
1188 if (!(msg->flags & MADF_DRAWUPDATE) && !(msg->flags & MADF_DRAWOBJECT))
1189 return 0;
1191 PrepareVisualBuffer(data);
1192 SetFont(_rp(obj), _font(obj));
1193 if (data->is_active)
1194 textpen = data->active_text.p_pen;
1195 else
1196 textpen = data->inactive_text.p_pen;
1198 /* Update the stringdata in case of user change */
1199 UpdateStringData(cl, obj);
1201 /* Update the DispPos and DispCount fields so that the gadget renders
1202 * properly */
1203 UpdateDisp(cl, obj);
1205 text_top = _mtop(obj)
1206 + ((_mheight(obj) - _rp(obj)->Font->tf_YSize) >> 1)
1207 + _rp(obj)->Font->tf_Baseline;
1209 dispstr = data->VisualBuffer + data->DispPos;
1210 dispstrlen = MIN(data->DispCount, data->NumChars - data->DispPos);
1211 textleft_save = text_left = GetTextLeft(cl, obj);
1213 // little flicker improvement, don't redraw first part of string
1214 // when adding a char
1215 if (msg->flags & MADF_DRAWUPDATE &&
1216 data->msd_RedrawReason == DO_ADDCHAR &&
1217 data->msd_Align == MUIV_String_Format_Left && data->DispPos == 0)
1219 text_left += TextLength(_rp(obj), dispstr, data->BufferPos - 1);
1220 dispstr += data->BufferPos - 1;
1221 dispstrlen -= data->BufferPos - 1;
1222 DoMethod(obj, MUIM_DrawBackground, text_left, _mtop(obj),
1223 _mwidth(obj) - text_left + _mleft(obj), _mheight(obj),
1224 text_left, _mtop(obj), 0);
1226 else if (msg->flags & MADF_DRAWUPDATE)
1228 DoMethod(obj, MUIM_DrawBackground, _mleft(obj), _mtop(obj),
1229 _mwidth(obj), _mheight(obj), _mleft(obj), _mtop(obj), 0);
1232 SetABPenDrMd(_rp(obj), textpen, _pens(obj)[MPEN_BACKGROUND], JAM1);
1233 Move(_rp(obj), text_left, text_top);
1235 if ((data->msd_Flags & MSDF_MARKING)
1236 && Buffer_GetMarkedRange(data, &markstart, &markstop))
1238 TextM(obj, data, dispstr, dispstrlen, markstart - data->DispPos,
1239 markstop - data->DispPos);
1242 else
1244 Text(_rp(obj), dispstr, dispstrlen);
1246 if (data->is_active) // active, draw cursor
1248 UWORD cursoroffset = data->BufferPos - data->DispPos;
1250 dispstr = data->VisualBuffer + data->DispPos;
1251 text_left = textleft_save;
1253 SetABPenDrMd(_rp(obj), data->active_text.p_pen,
1254 data->cursor.p_pen, JAM2);
1255 text_left += TextLength(_rp(obj), dispstr, cursoroffset);
1258 Move(_rp(obj), text_left, text_top);
1259 Text(_rp(obj),
1260 ((data->BufferPos < data->NumChars)
1261 ? dispstr + cursoroffset : (STRPTR) " "), 1);
1265 data->msd_RedrawReason = NO_REASON;
1266 CleanVisualBuffer(data);
1268 return TRUE;
1271 /**************************************************************************
1272 Returns whether object needs redrawing
1273 **************************************************************************/
1274 static int String_HandleVanillakey(struct IClass *cl, Object *obj,
1275 unsigned char code, UWORD qual, IPTR *retval)
1277 struct MUI_StringData *data =
1278 (struct MUI_StringData *)INST_DATA(cl, obj);
1279 BOOL doinput;
1281 D(bug("[MUI:String] HandleVanillakey: code=%d qual=%d\n", code, qual));
1283 if (0 == code)
1284 return 0;
1286 doinput = (data->msd_Flags & MSDF_NOINPUT) ? FALSE : TRUE;
1288 if (doinput && (code == '\b')) /* backspace */
1290 if (Buffer_KillMarked(data))
1292 return 1;
1295 if (data->BufferPos > 0)
1297 LONG shift;
1299 if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
1301 shift = data->BufferPos;
1302 data->msd_RedrawReason = NEW_CONTENTS;
1304 else
1306 shift = 1;
1307 data->msd_RedrawReason = DO_BACKSPACE;
1310 D(bug("[MUI:String] HandleVanillakey: 'Backspace' count=%d pos=%d"
1311 " shift=%d\n", data->NumChars, data->BufferPos, shift));
1312 memmove(&data->Buffer[data->BufferPos - shift],
1313 &data->Buffer[data->BufferPos],
1314 data->NumChars - data->BufferPos + 1);
1315 data->BufferPos -= shift;
1316 data->NumChars -= shift;
1317 return 1;
1319 return 0;
1322 if (doinput && (code == 21)) // ctrl-u == NAK (like shift-bs)
1324 if (Buffer_KillMarked(data))
1326 return 1;
1329 if (data->BufferPos > 0)
1331 D(bug("[MUI:String] HandleVanillakey: 'Ctrl-U' count=%d pos=%d\n",
1332 data->NumChars, data->BufferPos));
1333 memmove(&data->Buffer[0],
1334 &data->Buffer[data->BufferPos],
1335 data->NumChars - data->BufferPos + 1);
1336 data->NumChars -= data->BufferPos;
1337 data->BufferPos = 0;
1338 data->msd_RedrawReason = NEW_CONTENTS;
1339 return 1;
1341 return 0;
1344 if (doinput && (code == 127)) /* del */
1346 if (!Buffer_KillMarked(data))
1348 if ((qual & IEQUALIFIER_LSHIFT) || (qual & IEQUALIFIER_RSHIFT))
1350 data->Buffer[data->BufferPos] = 0;
1351 data->NumChars = data->BufferPos;
1352 data->msd_RedrawReason = NEW_CONTENTS;
1354 else
1356 if (data->BufferPos < data->NumChars)
1358 D(bug("[MUI:String] HandleVanillakey: 'Delete' count=%d"
1359 " pos=%d\n", data->NumChars, data->BufferPos));
1360 memmove(&data->Buffer[data->BufferPos],
1361 &data->Buffer[data->BufferPos + 1],
1362 data->NumChars - data->BufferPos);
1363 data->NumChars--;
1366 data->msd_RedrawReason = DO_DELETE;
1369 return 1;
1372 if (doinput && (code == 11)) // ctrl-k == VT == \v (like shift-del)
1374 if (!Buffer_KillMarked(data))
1376 data->Buffer[data->BufferPos] = 0;
1377 data->NumChars = data->BufferPos;
1378 data->msd_RedrawReason = NEW_CONTENTS;
1380 return 1;
1383 if (doinput && (code == 24)) /* ctrl x == ascii cancel */
1385 if (!Buffer_KillMarked(data))
1387 data->Buffer[0] = 0;
1388 data->BufferPos = 0;
1389 data->NumChars = 0;
1390 data->msd_RedrawReason = NEW_CONTENTS;
1392 return 1;
1395 if (code == 1) // ctrl-a, linestart
1397 data->BufferPos = 0;
1398 data->msd_Flags &= ~(MSDF_MARKING | MSDF_KEYMARKING);
1399 return 1;
1402 if (code == 26) // ctrl-z, lineend
1404 data->BufferPos = data->NumChars;
1405 data->msd_Flags &= ~(MSDF_MARKING | MSDF_KEYMARKING);
1406 return 1;
1409 if (code == '\t') // tab
1411 return 0;
1414 if (((ToLower(code) == 'c') || (ToLower(code) == 'x')) &&
1415 (qual & IEQUALIFIER_RCOMMAND))
1417 WORD markstart, markstop;
1419 if ((data->msd_Flags & MSDF_MARKING)
1420 && Buffer_GetMarkedRange(data, &markstart, &markstop))
1422 clipboard_write_text(&data->Buffer[markstart],
1423 markstop - markstart);
1425 if (doinput && (ToLower(code) == 'x'))
1427 Buffer_KillMarked(data);
1429 else
1431 data->BufferPos = markstop;
1432 data->msd_Flags &= ~MSDF_MARKING;
1434 return 1;
1436 return 0;
1439 if (doinput && (ToLower(code) == 'v') && (qual & IEQUALIFIER_RCOMMAND))
1441 STRPTR text;
1442 int retval;
1443 struct Locale *locale = OpenLocale(NULL);
1445 retval = Buffer_KillMarked(data);
1446 if ((text = clipboard_read_text()))
1448 STRPTR text2 = text;
1449 UBYTE c;
1451 while ((c = *text2++))
1453 if (!IsPrint(locale, c))
1454 break;
1455 if (!(Buffer_AddChar(data, c)))
1456 break;
1457 if (!retval)
1458 retval = 1;
1461 clipboard_free_text(text);
1464 CloseLocale(locale);
1466 return retval;
1469 if (data->msd_Accept != NULL)
1471 /* Check if character is accepted */
1472 if (NULL == strchr(data->msd_Accept, code))
1474 DisplayBeep(NULL);
1475 *retval = MUI_EventHandlerRC_Eat;
1476 return 0;
1480 if (data->msd_Reject != NULL)
1482 /* Check if character is rejected */
1483 if (NULL != strchr(data->msd_Reject, code))
1485 DisplayBeep(NULL);
1486 *retval = MUI_EventHandlerRC_Eat;
1487 return 0;
1491 if (doinput)
1493 struct Locale *locale = OpenLocale(NULL);
1495 if (!(code >= 0x09 && code <= 0x0D) && IsPrint(locale, code))
1497 Buffer_KillMarked(data);
1498 if (Buffer_AddChar(data, code))
1500 data->msd_RedrawReason = DO_ADDCHAR;
1501 return 2;
1505 CloseLocale(locale);
1508 data->msd_RedrawReason = DO_UNKNOWN;
1509 return 0;
1513 /**************************************************************************
1514 MUIM_HandleEvent
1515 **************************************************************************/
1516 IPTR String__MUIM_HandleEvent(struct IClass *cl, Object *obj,
1517 struct MUIP_HandleEvent *msg)
1519 struct MUI_StringData *data =
1520 (struct MUI_StringData *)INST_DATA(cl, obj);
1521 IPTR retval = 0;
1522 int update = 0;
1523 BOOL edited = FALSE;
1524 LONG muikey = msg->muikey;
1525 BOOL cursor_kills_marking = FALSE;
1527 if ((data->msd_Flags & MSDF_MARKING)
1528 && !(data->msd_Flags & MSDF_KEYMARKING))
1530 cursor_kills_marking = TRUE;
1533 /* Convert raw keys to MUI keys for marking */
1534 if (muikey == MUIKEY_NONE && msg->imsg != NULL)
1536 if (msg->imsg->Class == IDCMP_RAWKEY)
1538 static LONG muikeytable[3][2] = {
1539 {MUIKEY_LEFT, MUIKEY_RIGHT},
1540 {MUIKEY_WORDLEFT, MUIKEY_WORDRIGHT},
1541 {MUIKEY_LINESTART, MUIKEY_LINEEND}
1543 WORD dirindex = -1, amountindex = 0;
1545 switch (msg->imsg->Code)
1547 case 0x4F:
1548 dirindex = 0;
1549 break;
1551 case 0x4E:
1552 dirindex = 1;
1553 break;
1555 #ifdef __AROS__
1556 case RAWKEY_HOME:
1557 muikey = MUIKEY_LINESTART;
1558 break;
1560 case RAWKEY_END:
1561 muikey = MUIKEY_LINEEND;
1562 break;
1563 #endif
1567 if (dirindex != -1)
1569 if (msg->imsg->
1570 Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
1572 amountindex = 2;
1574 else if (msg->imsg->Qualifier & IEQUALIFIER_CONTROL)
1576 amountindex = 1;
1579 muikey = muikeytable[amountindex][dirindex];
1585 D(bug("[MUI:String] HandleEvent: muikey %d, imsg %p is_active=%d\n", muikey,
1586 msg->imsg, data->is_active));
1587 if (muikey != MUIKEY_NONE && data->is_active)
1589 retval = MUI_EventHandlerRC_Eat;
1591 switch (muikey)
1593 case MUIKEY_LEFT:
1594 if (cursor_kills_marking)
1596 update = 1;
1597 data->BufferPos = MIN(data->BufferPos, data->MarkPos);
1598 if (data->BufferPos > 0)
1599 data->BufferPos--;
1601 data->msd_Flags &= ~MSDF_MARKING;
1603 else if (data->BufferPos > 0)
1605 update = 1;
1607 data->BufferPos--;
1608 data->msd_RedrawReason = DO_CURSOR_LEFT;
1610 break;
1611 case MUIKEY_RIGHT:
1612 if (cursor_kills_marking)
1614 update = 1;
1615 data->BufferPos = MAX(data->BufferPos, data->MarkPos);
1616 data->msd_Flags &= ~MSDF_MARKING;
1618 else if (data->BufferPos < data->NumChars)
1620 update = 1;
1621 data->BufferPos++;
1622 data->msd_RedrawReason = DO_CURSOR_RIGHT;
1624 break;
1625 case MUIKEY_WORDLEFT:
1626 if (data->BufferPos > 0)
1628 data->BufferPos =
1629 Buffer_GetPrevWordIndex(data, data->BufferPos);
1630 update = 1;
1631 data->msd_RedrawReason = DO_CURSOR_LEFT;
1633 if (cursor_kills_marking)
1635 data->msd_Flags &= ~MSDF_MARKING;
1636 update = 1;
1638 break;
1639 case MUIKEY_WORDRIGHT:
1640 if (data->BufferPos < data->NumChars)
1642 data->BufferPos =
1643 Buffer_GetSuccWordIndex(data, data->BufferPos);
1644 update = 1;
1645 data->msd_RedrawReason = DO_CURSOR_RIGHT;
1647 if (cursor_kills_marking)
1649 data->msd_Flags &= ~MSDF_MARKING;
1650 update = 1;
1652 break;
1653 case MUIKEY_LINESTART:
1654 data->BufferPos = 0;
1655 update = 1;
1656 if (cursor_kills_marking)
1658 data->msd_Flags &= ~MSDF_MARKING;
1660 break;
1662 case MUIKEY_LINEEND:
1663 data->BufferPos = data->NumChars;
1664 update = 1;
1665 if (cursor_kills_marking)
1667 data->msd_Flags &= ~MSDF_MARKING;
1669 break;
1671 case MUIKEY_UP:
1672 if (data->msd_AttachedList)
1673 set(data->msd_AttachedList,
1674 MUIA_List_Active, MUIV_List_Active_Up);
1675 break;
1676 case MUIKEY_DOWN:
1677 if (data->msd_AttachedList)
1678 set(data->msd_AttachedList,
1679 MUIA_List_Active, MUIV_List_Active_Down);
1680 break;
1681 case MUIKEY_PAGEUP:
1682 if (data->msd_AttachedList)
1683 set(data->msd_AttachedList,
1684 MUIA_List_Active, MUIV_List_Active_PageUp);
1685 break;
1686 case MUIKEY_PAGEDOWN:
1687 if (data->msd_AttachedList)
1688 set(data->msd_AttachedList,
1689 MUIA_List_Active, MUIV_List_Active_PageDown);
1690 break;
1691 case MUIKEY_TOP:
1692 if (data->msd_AttachedList)
1693 set(data->msd_AttachedList,
1694 MUIA_List_Active, MUIV_List_Active_Top);
1695 break;
1696 case MUIKEY_BOTTOM:
1697 if (data->msd_AttachedList)
1698 set(data->msd_AttachedList,
1699 MUIA_List_Active, MUIV_List_Active_Bottom);
1700 break;
1701 case MUIKEY_PRESS:
1703 UBYTE *buf = NULL;
1704 LONG val = 0;
1706 get(obj, MUIA_String_Contents, &buf);
1708 if (data->msd_Flags & MSDF_STAYACTIVE)
1710 /* Do not change active object */
1712 else if (data->msd_Flags & MSDF_ADVANCEONCR)
1714 set(_win(obj), MUIA_Window_ActiveObject,
1715 MUIV_Window_ActiveObject_Next);
1717 else if (!(data->msd_Flags & MSDF_STAYACTIVE))
1719 set(_win(obj), MUIA_Window_ActiveObject,
1720 MUIV_Window_ActiveObject_None);
1723 /* Notify listeners of new string value */
1724 set(obj, MUIA_String_Acknowledge, buf);
1726 /* Notify listeners of new integer value (if any) */
1727 if (StrToLong(buf, &val) >= 0)
1728 superset(cl, obj, MUIA_String_Integer, val);
1730 break;
1732 case MUIKEY_WINDOW_CLOSE:
1733 data->is_active = FALSE;
1734 set(obj, MUIA_Background,
1735 (IPTR) muiGlobalInfo(obj)->mgi_Prefs->string_bg_inactive);
1736 DoMethod(obj, MUIM_GoInactive);
1737 retval = 0;
1738 break;
1740 default:
1741 retval = 0;
1742 } // switch(muikey)
1743 } // if (muikey != MUIKEY_NONE)
1745 if (msg->imsg)
1747 UWORD code = msg->imsg->Code;
1748 //UWORD qual = msg->imsg->Qualifier;
1749 WORD x = msg->imsg->MouseX;
1750 WORD y = msg->imsg->MouseY;
1752 //bug("String_HandleEvent: parsing imsg %p, class=%ld\n",
1753 // msg->imsg, msg->imsg->Class);
1755 switch (msg->imsg->Class)
1757 case IDCMP_MOUSEBUTTONS: /* set cursor and activate it */
1758 if (code == SELECTDOWN)
1760 //bug("String_HandleEvent: code == SELECTDOWN, x=%d y=%d\n",
1761 // x, y);
1763 if (_isinobject(obj, x, y))
1765 UWORD text_left, text_right;
1767 retval = MUI_EventHandlerRC_Eat;
1769 CurrentTime(&data->NewClick_Sec, &data->NewClick_Micro);
1770 if (DoubleClick(data->OldClick_Sec,
1771 data->OldClick_Micro, data->NewClick_Sec,
1772 data->NewClick_Micro))
1774 data->MultiClick++;
1776 else
1778 data->MultiClick = 0;
1780 data->OldClick_Sec = data->NewClick_Sec;
1781 data->OldClick_Micro = data->NewClick_Micro;
1783 //kprintf("multiclick %d\n", data->MultiClick);
1785 if (!data->is_active)
1787 //bug("String got button, lets activate\n");
1788 data->is_active = TRUE;
1789 data->msd_RedrawReason = WENT_ACTIVE;
1790 // redraw
1791 set(obj, MUIA_Background,
1792 (IPTR) muiGlobalInfo(obj)->mgi_Prefs->
1793 string_bg_active);
1795 //DoMethod(obj, MUIM_GoActive);
1796 set(_win(obj), MUIA_Window_ActiveObject, obj);
1797 // let other objects a chance to get desactivated
1798 //retval = 0;
1801 if (!(data->ehn.ehn_Events & IDCMP_MOUSEMOVE))
1803 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1804 (IPTR) & data->ehn);
1805 data->ehn.ehn_Events |= IDCMP_MOUSEMOVE;
1806 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1807 (IPTR) & data->ehn);
1810 PrepareVisualBuffer(data);
1811 text_left = GetTextLeft(cl, obj);
1812 text_right = GetTextRight(cl, obj);
1813 CleanVisualBuffer(data);
1815 /* Check if mouseclick is inside displayed text */
1816 if ((x >= text_left) && (x <= text_right))
1818 /* Find new cursor pos. */
1819 struct TextExtent te;
1820 ULONG newpos;
1821 STRPTR dispstr = data->Buffer + data->DispPos;
1823 newpos = data->DispPos
1824 + TextFit(_rp(obj), dispstr,
1825 data->NumChars - data->DispPos, &te, NULL, 1,
1826 x - text_left, _rp(obj)->Font->tf_YSize);
1828 if (data->BufferPos != newpos)
1830 data->BufferPos = newpos;
1831 update = 1;
1834 else if (x < text_left)
1836 /* Click on empty space at left. Set cursor to first
1837 * visible */
1838 if (data->BufferPos != data->DispPos)
1840 data->BufferPos = data->DispPos;
1841 update = 1;
1844 else
1846 /* Click on empty space at right. Set cursor to last
1847 * visible */
1848 if (data->BufferPos !=
1849 data->DispPos + data->DispCount)
1851 data->BufferPos =
1852 data->DispPos + data->DispCount;
1853 update = 1;
1855 } /* if (click is on text or not) */
1857 data->MarkPos = data->BufferPos;
1859 if (data->MultiClick == 0)
1861 if (data->msd_Flags & MSDF_MARKING)
1863 data->msd_Flags &= ~MSDF_MARKING;
1864 update = 1;
1867 else if (data->MultiClick
1868 && Buffer_GetMarkedRange(data, NULL, NULL))
1870 data->msd_Flags |= MSDF_MARKING;
1871 update = 1;
1875 } /* is in object */
1876 else if (data->is_active
1877 && !(data->msd_Flags & MSDF_STAYACTIVE))
1878 /* and click not on object */
1880 data->is_active = FALSE;
1881 set(obj, MUIA_Background,
1882 (IPTR) muiGlobalInfo(obj)->mgi_Prefs->
1883 string_bg_inactive);
1884 //DoMethod(obj, MUIM_GoInactive);
1885 // let other objects a chance to get activated
1886 //retval = 0;
1888 } /* if (code == SELECTDOWN) */
1889 else if (code == SELECTUP)
1891 if (data->ehn.ehn_Events & IDCMP_MOUSEMOVE)
1893 DoMethod(_win(obj), MUIM_Window_RemEventHandler,
1894 (IPTR) & data->ehn);
1895 data->ehn.ehn_Events &= ~IDCMP_MOUSEMOVE;
1896 DoMethod(_win(obj), MUIM_Window_AddEventHandler,
1897 (IPTR) & data->ehn);
1900 break;
1902 case IDCMP_MOUSEMOVE:
1903 if (data->is_active)
1905 UWORD text_left, text_right;
1907 if (!(data->msd_Flags & MSDF_MARKING))
1909 data->msd_Flags |= MSDF_MARKING;
1912 PrepareVisualBuffer(data);
1913 text_left = GetTextLeft(cl, obj);
1914 text_right = GetTextRight(cl, obj);
1915 CleanVisualBuffer(data);
1917 /* Check if mouseclick is inside displayed text */
1918 if ((x >= text_left) && (x <= text_right))
1920 /* Find new cursor pos. */
1921 struct TextExtent te;
1922 ULONG newpos;
1923 STRPTR dispstr = data->Buffer + data->DispPos;
1925 newpos = data->DispPos
1926 + TextFit(_rp(obj), dispstr,
1927 data->NumChars - data->DispPos, &te, NULL, 1,
1928 x - text_left, _rp(obj)->Font->tf_YSize);
1930 if (data->BufferPos != newpos)
1932 WORD old_markstart = 0, old_markstop = 0;
1933 WORD markstart = 0, markstop = 0;
1934 BOOL was_marked, is_marked;
1936 was_marked = Buffer_AnythingMarked(data) &&
1937 Buffer_GetMarkedRange(data, &old_markstart,
1938 &old_markstop);
1940 data->BufferPos = newpos;
1942 is_marked = Buffer_AnythingMarked(data) &&
1943 Buffer_GetMarkedRange(data, &markstart,
1944 &markstop);
1946 if ((was_marked != is_marked) ||
1947 (old_markstart != markstart) ||
1948 (old_markstop != markstop))
1950 update = 1;
1954 else if ((x < text_left) && (data->BufferPos > 0))
1956 data->BufferPos--;
1957 update = 1;
1958 data->msd_RedrawReason = DO_CURSOR_LEFT;
1960 else if ((x > text_right)
1961 && (data->BufferPos < data->NumChars))
1963 data->BufferPos++;
1964 update = 1;
1965 data->msd_RedrawReason = DO_CURSOR_RIGHT;
1967 //kprintf(" ---- bp: %d\n", data->BufferPos);
1969 break;
1971 case IDCMP_RAWKEY:
1973 unsigned char code;
1975 //bug("String_HandleEvent: idcmp_rawkey\n");
1977 if (!data->is_active)
1978 break;
1980 code = ConvertKey(msg->imsg);
1981 if (!code)
1983 switch (msg->imsg->Code)
1985 case 0x64: /* LALT */
1986 case 0x65: /* RALT */
1987 case 0x64 | IECODE_UP_PREFIX:
1988 case 0x65 | IECODE_UP_PREFIX:
1989 if (msg->imsg->
1990 Qualifier & (IEQUALIFIER_LALT |
1991 IEQUALIFIER_RALT))
1993 data->MarkPos = data->BufferPos;
1994 data->msd_Flags |=
1995 (MSDF_MARKING | MSDF_KEYMARKING);
1997 else
1999 data->msd_Flags &= ~MSDF_KEYMARKING;
2001 break;
2006 if (code)
2008 update =
2009 String_HandleVanillakey(cl, obj, code,
2010 msg->imsg->Qualifier, &retval);
2011 if (update)
2013 retval = MUI_EventHandlerRC_Eat;
2014 edited = TRUE;
2018 break;
2022 /* Trigger notification */
2023 if (edited)
2024 superset(cl, obj, MUIA_String_Contents, data->Buffer);
2026 if (update)
2028 MUI_Redraw(obj, MADF_DRAWUPDATE);
2030 //D(bug("String eh return %ld\n", retval));
2032 return retval;
2036 /**************************************************************************
2037 MUIM_Export : to export an object's "contents" to a dataspace object.
2038 **************************************************************************/
2039 IPTR String__MUIM_Export(struct IClass *cl, Object *obj,
2040 struct MUIP_Export *msg)
2042 struct MUI_StringData *data = INST_DATA(cl, obj);
2043 ULONG id;
2045 if ((id = muiNotifyData(obj)->mnd_ObjectID))
2047 if (data->Buffer != NULL)
2048 DoMethod(msg->dataspace, MUIM_Dataspace_Add,
2049 (IPTR) data->Buffer, data->NumChars + 1, (IPTR) id);
2050 else
2051 DoMethod(msg->dataspace, MUIM_Dataspace_Remove, (IPTR) id);
2054 return 0;
2058 /**************************************************************************
2059 MUIM_Import : to import an object's "contents" from a dataspace object.
2060 **************************************************************************/
2061 IPTR String__MUIM_Import(struct IClass *cl, Object *obj,
2062 struct MUIP_Import *msg)
2064 ULONG id;
2065 STRPTR s;
2067 if ((id = muiNotifyData(obj)->mnd_ObjectID))
2069 if ((s = (STRPTR) DoMethod(msg->dataspace, MUIM_Dataspace_Find,
2070 (IPTR) id)))
2072 set(obj, MUIA_String_Contents, s);
2076 return 0;
2079 /**************************************************************************
2080 MUIM_GoActive
2081 **************************************************************************/
2082 IPTR String__MUIM_GoActive(struct IClass *cl, Object *obj, Msg msg)
2084 struct MUI_StringData *data =
2085 (struct MUI_StringData *)INST_DATA(cl, obj);
2087 //D(bug("String_GoActive %p\n", obj));
2088 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) & data->ehn);
2089 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS | IDCMP_RAWKEY;
2090 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) & data->ehn);
2091 data->is_active = TRUE;
2092 data->msd_Flags &= ~MSDF_KEYMARKING;
2093 data->msd_RedrawReason = WENT_ACTIVE;
2094 // redraw
2095 set(obj, MUIA_Background,
2096 (IPTR) muiGlobalInfo(obj)->mgi_Prefs->string_bg_active);
2098 return 0;
2101 /**************************************************************************
2102 MUIM_GoInactive
2103 **************************************************************************/
2104 IPTR String__MUIM_GoInactive(struct IClass *cl, Object *obj, Msg msg)
2106 struct MUI_StringData *data =
2107 (struct MUI_StringData *)INST_DATA(cl, obj);
2109 //D(bug("String_GoInactive %p\n", obj));
2111 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR) & data->ehn);
2112 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS;
2113 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR) & data->ehn);
2114 data->is_active = FALSE;
2115 data->msd_RedrawReason = WENT_INACTIVE;
2116 data->MultiClick = 0;
2118 // redraw
2119 set(obj, MUIA_Background,
2120 (IPTR) muiGlobalInfo(obj)->mgi_Prefs->string_bg_inactive);
2122 return 0;
2125 /**************************************************************************
2126 MUIM_String_ClearSelected (BetterString)
2127 **************************************************************************/
2128 IPTR String__MUIM_ClearSelected(struct IClass *cl, Object *obj,
2129 struct MUIP_String_ClearSelected *msg)
2131 struct MUI_StringData *data =
2132 (struct MUI_StringData *)INST_DATA(cl, obj);
2134 //D(bug("String_ClearSelected %p\n", obj));
2136 if (Buffer_KillMarked(data))
2138 MUI_Redraw(obj, MADF_DRAWUPDATE);
2141 return 0;
2144 /**************************************************************************
2145 MUIM_String_Insert (BetterString)
2146 **************************************************************************/
2147 IPTR String__MUIM_Insert(struct IClass *cl, Object *obj,
2148 struct MUIP_String_Insert *msg)
2150 struct MUI_StringData *data =
2151 (struct MUI_StringData *)INST_DATA(cl, obj);
2152 LONG pos;
2153 ULONG old_bufferpos;
2154 ULONG num_inserted = 0;
2156 //D(bug("String_Insert %p\n", obj));
2158 switch ((ULONG) msg->pos)
2160 case MUIV_String_Insert_StartOfString:
2161 pos = 0;
2162 break;
2164 case MUIV_String_Insert_EndOfString:
2165 pos = data->NumChars;
2166 break;
2168 case MUIV_String_Insert_BufferPos:
2169 pos = data->BufferPos;
2170 break;
2172 default:
2173 pos = msg->pos;
2174 break;
2177 if ((pos < 0) || (pos > data->NumChars))
2178 return 0;
2180 old_bufferpos = data->BufferPos;
2181 data->BufferPos = pos;
2183 while (msg->text[num_inserted]
2184 && Buffer_AddChar(data, msg->text[num_inserted]))
2186 num_inserted++;
2189 if (num_inserted)
2191 if (old_bufferpos >= pos)
2193 data->BufferPos = old_bufferpos + num_inserted;
2195 else
2197 data->BufferPos = old_bufferpos;
2200 MUI_Redraw(obj, MADF_DRAWUPDATE);
2203 return 0;
2206 /**************************************************************************
2207 MUIM_String_FileNameStart (BetterString)
2208 **************************************************************************/
2209 IPTR String__MUIM_FileNameStart(struct IClass *cl, Object *obj,
2210 struct MUIP_String_FileNameStart *msg)
2212 struct MUI_StringData *data =
2213 (struct MUI_StringData *)INST_DATA(cl, obj);
2214 STRPTR buf;
2216 //D(bug("String_FileNameStart %p\n", obj));
2218 buf = data->Buffer;
2220 // TODO: Implement String_FileNameStart correctly!
2222 return (IPTR) buf;
2225 BOOPSI_DISPATCHER(IPTR, String_Dispatcher, cl, obj, msg)
2227 switch (msg->MethodID)
2229 case OM_NEW:
2230 return String__OM_NEW(cl, obj, (struct opSet *)msg);
2231 case OM_DISPOSE:
2232 return String__OM_DISPOSE(cl, obj, msg);
2233 case OM_SET:
2234 return String__OM_SET(cl, obj, (struct opSet *)msg);
2235 case OM_GET:
2236 return String__OM_GET(cl, obj, (struct opGet *)msg);
2238 case MUIM_Setup:
2239 return String__MUIM_Setup(cl, obj, (APTR) msg);
2240 case MUIM_Cleanup:
2241 return String__MUIM_Cleanup(cl, obj, (APTR) msg);
2242 case MUIM_AskMinMax:
2243 return String__MUIM_AskMinMax(cl, obj, (APTR) msg);
2244 case MUIM_Draw:
2245 return String__MUIM_Draw(cl, obj, (APTR) msg);
2246 case MUIM_Export:
2247 return String__MUIM_Export(cl, obj, (APTR) msg);
2248 case MUIM_Import:
2249 return String__MUIM_Import(cl, obj, (APTR) msg);
2250 case MUIM_GoActive:
2251 return String__MUIM_GoActive(cl, obj, (APTR) msg);
2252 case MUIM_GoInactive:
2253 return String__MUIM_GoInactive(cl, obj, (APTR) msg);
2254 case MUIM_HandleEvent:
2255 return String__MUIM_HandleEvent(cl, obj, (APTR) msg);
2257 /* BetterString */
2258 case MUIM_String_ClearSelected:
2259 return String__MUIM_ClearSelected(cl, obj, (APTR) msg);
2260 case MUIM_String_Insert:
2261 return String__MUIM_Insert(cl, obj, (APTR) msg);
2262 case MUIM_String_FileNameStart:
2263 return String__MUIM_FileNameStart(cl, obj, (APTR) msg);
2266 return DoSuperMethodA(cl, obj, msg);
2268 BOOPSI_DISPATCHER_END
2271 * Class descriptor.
2273 const struct __MUIBuiltinClass _MUI_String_desc =
2275 MUIC_String,
2276 MUIC_Area,
2277 sizeof(struct MUI_StringData),
2278 (void *)String_Dispatcher