Less sloppy handling of IoErr().
[AROS.git] / rom / intuition / strgadgets.c
blob00de8437b0e2a03edd9abe0e4818644d5a02e95f
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 Copyright © 2001-2003, The MorphOS Development Team. All Rights Reserved.
4 $Id$
5 */
7 #include <proto/exec.h>
8 #include <proto/intuition.h>
9 #include <proto/graphics.h>
10 #include <proto/utility.h>
11 #include <proto/keymap.h>
12 #include <intuition/screens.h>
13 #include <intuition/cghooks.h>
14 #include <graphics/gfxmacros.h>
15 #include <devices/inputevent.h>
16 #include <aros/asmcall.h>
17 #include <stdlib.h> /* atol() */
18 #include <stdio.h> /* snprintf() */
19 #include <string.h>
21 #include "intuition_intern.h"
22 #include "strgadgets.h"
24 #ifdef __MORPHOS__
25 size_t stccpy(char *s1, const char *s2, size_t n);
26 #endif
28 #undef DEBUG
29 #define SDEBUG 0
30 #define DEBUG 0
31 #include <aros/debug.h>
33 /*****************************************************************************************/
35 #define CURSORPEN 0
36 #define STRBACKPEN 1
37 #define STRTEXTPEN 2
39 #define NUMPENS 3
40 #define STRALIGNMASK (GACT_STRINGLEFT|GACT_STRINGCENTER|GACT_STRINGRIGHT)
42 #define KEYBUFSIZE 10
43 #define SHIFT (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)
45 #define HELPRAW 95 /* Raw */
46 #define CTRL_A 1
47 #define CTRL_B 2
48 #define CTRL_C 3
49 #define CTRL_D 4
50 #define CTRL_E 5
51 #define CTRL_F 6
52 #define CTRL_G 7
53 #define CTRL_H 8
54 #define CTRL_I 9
55 #define CTRL_J 10
56 #define CTRL_K 11
57 #define CTRL_L 12
58 #define CTRL_M 13
59 #define CTRL_N 14
60 #define CTRL_O 15
61 #define CTRL_P 16
62 #define CTRL_Q 17
63 #define CTRL_R 18
64 #define CTRL_S 19
65 #define CTRL_T 20
66 #define CTRL_U 21
67 #define CTRL_V 22
68 #define CTRL_W 23
69 #define CTRL_X 24
70 #define CTRL_Y 25
71 #define CTRL_Z 26
73 #define BACKSPACE 8 /* Vanilla */
74 #define TAB 9 /* Vanilla */
75 #define TABRAW 66 /* Raw */
76 #define RETURN 13 /* Vanilla */
77 #define DELETE 127 /* Vanilla */
78 #define HOMERAW 112 /* Raw */
79 #define ENDRAW 113 /* Raw */
81 /*****************************************************************************************/
83 VOID UpdateStringInfo(struct Gadget *);
85 /*****************************************************************************************/
87 #define CharXSize(char, rp) ((rp->Font->tf_Flags & FPF_PROPORTIONAL) ? \
88 rp->Font->tf_XSize : TextLength(rp, &(char), 1))
91 #undef MIN
92 #define MIN(a, b) ((a < b) ? a : b)
94 /*****************************************************************************************/
96 CONST char cursorkey[] = "_";
97 #define CURSORKEYLEN 1
99 /*****************************************************************************************/
101 STATIC WORD MaxDispPos(struct StringInfo *strinfo, struct BBox *bbox,
102 struct RastPort *rp, struct IntuitionBase *IntuitionBase)
105 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
106 WORD numfit, max_disppos, numchars;
107 struct TextExtent te;
108 BOOL cursor_at_end;
110 cursor_at_end = (strinfo->BufferPos == strinfo->NumChars);
112 EnterFunc(bug("MaxDispPos(current length: %d, bufferpos=%d)\n", strinfo->NumChars, strinfo->BufferPos));
114 D(bug("cursor_at_end: %d\n", cursor_at_end));
116 if (cursor_at_end) /* Cursor at end of string ? */
118 D(bug("Making cursor last char\n"));
119 numchars = strinfo->NumChars + 1; /* Take cursor into account */
121 /* This has allready been done by UpdateDisp() which called us
122 strinfo->Buffer[strinfo->NumChars] = 0x20;
126 else
129 numchars = strinfo->NumChars;
133 /* Find the amount of characters that fit into the bbox, counting
134 ** from the last character in the buffer and forward,
136 numfit = TextFit(rp,
137 &(strinfo->Buffer[numchars - 1]),
138 numchars, &te, NULL,
139 -1, bbox->Width, rp->Font->tf_YSize);
142 max_disppos = numchars - numfit;
144 /* if ((max_disppos > 0) && (!cursor_at_end))
145 max_disppos --;
148 D(bug("Numchars w/cursor: %d, Numfit: %d, maxdisppos=%d bbox->Width = %d te->te_Width = %d\n",
149 numchars, numfit, max_disppos, bbox->Width, te.te_Width));
151 ReturnInt("MaxDispPos", WORD, max_disppos);
154 /*****************************************************************************************/
156 void UpdateDisp(struct Gadget *gad,
157 struct BBox *bbox,
158 struct RastPort *rp,
159 struct IntuitionBase *IntuitionBase)
163 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
164 struct TextExtent te;
165 struct StringInfo *strinfo = (struct StringInfo *)gad->SpecialInfo;
166 STRPTR dispstr;
168 EnterFunc(bug("UpdateDisp(gad=%p, bbox=%p, rp=%p)\n",
169 gad, bbox, rp));
171 /* If the cursor is at the trailing \0, insert a SPACE instead */
172 if (strinfo->BufferPos == strinfo->NumChars)
173 strinfo->Buffer[strinfo->NumChars] = 0x20;
175 /* In this function we check if the cursor has gone outside
176 ** of the visible area (because of application setting
177 ** strinfo->BufferPos or strinfo->DispPos to a different value, or
178 ** because of user input).
179 ** This is made a bit difficult by the rule (R), that there
180 ** should NOT be available space on the right, and characters
181 ** scrolled out at the left, at the same time.
182 ** We have 3 possible scenarios:
183 ** 1) Cursor to the left of DispPos:
184 ** Set DispPos to the lowest of BufferPos and the
185 ** maximum allowed disppos (according to (R) ).
186 ** 2) Cursor to the right of visible area:
187 ** Set dispose sou that the cursor is the last visible character.
188 ** This afheres to (R).
189 ** 3) Cursor inside visible area. Do a check on rule (R),
190 ** and if DispPos > max allowed, then adjust it down,
191 ** so that the last character in the buffer becomes last character
192 ** displayed. (The cursor will still be visible after adjustion)
196 /* 1) Cursor to the left of visible area */
197 if (strinfo->BufferPos < strinfo->DispPos)
199 WORD max_disppos;
201 max_disppos = MaxDispPos(strinfo, bbox, rp, IntuitionBase);
202 strinfo->DispPos = MIN(strinfo->BufferPos, max_disppos);
204 else /* Cursor equal to the right of disppos [ 2) or 3) ] */
206 UWORD strsize;
208 /* How many pixels are there from current 1st displayed to the cursor ? */
209 strsize = TextLength(rp,
210 strinfo->Buffer + strinfo->DispPos,
211 strinfo->BufferPos - strinfo->DispPos + 1);
213 /* 2) More than fits into the gadget ? */
214 if (strsize > bbox->Width)
216 /* Compute new DispPos such that the cursor is at the right */
217 strinfo->DispPos = strinfo->BufferPos
218 - TextFit(rp,
219 &(strinfo->Buffer[strinfo->BufferPos]),
220 strinfo->NumChars, &te, NULL, -1,
221 bbox->Width, rp->Font->tf_YSize)
222 + 1;
224 D(bug("cursor right of visible area, new disppos: %d\n", strinfo->DispPos));
226 else /* 3). Cursor inside gadget */
228 WORD max_disppos;
230 max_disppos = MaxDispPos(strinfo, bbox, rp, IntuitionBase);
231 if (strinfo->DispPos > max_disppos)
232 strinfo->DispPos = max_disppos;
234 } /* if (cursor inside or to the right of visible area )*/
238 /* Update the DispCount */
239 /* It might be necessary with special handling for centre aligned gads */
240 dispstr = &(strinfo->Buffer[strinfo->DispPos]);
241 strinfo->DispCount = TextFit(rp, dispstr,
242 strinfo->NumChars - strinfo->DispPos,
243 &te, NULL, 1,
244 bbox->Width,
245 rp->Font->tf_YSize);
247 /* 0-terminate string */
248 strinfo->Buffer[strinfo->NumChars] = 0x00;
249 ReturnVoid("UpdateDisp");
252 /*****************************************************************************************/
254 STATIC UWORD GetTextLeft(struct Gadget *gad,
255 struct BBox *bbox,
256 struct RastPort *rp,
257 struct IntuitionBase *IntuitionBase)
259 /* Gets left position of text in the string gadget */
261 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
262 struct StringInfo *strinfo = (struct StringInfo *)gad->SpecialInfo;
263 UWORD text_left = 0;
264 STRPTR dispstr = &(strinfo->Buffer[strinfo->DispPos]);
265 UWORD dispstrlen;
266 BOOL cursor_at_end;
268 cursor_at_end = (strinfo->BufferPos == strinfo->NumChars);
270 dispstrlen = strinfo->NumChars - strinfo->DispPos;
272 /* Calcluate start offset of gadget text */
273 switch (gad->Activation & STRALIGNMASK)
275 case GACT_STRINGLEFT:
276 /* FIXME: is this default: correct? */
277 default:
278 text_left = bbox->Left;
279 break;
281 case GACT_STRINGCENTER:
283 WORD textwidth = TextLength(rp, dispstr, dispstrlen);
285 if (cursor_at_end) textwidth += TextLength(rp, cursorkey, CURSORKEYLEN);
286 text_left = bbox->Left + ((bbox->Width - textwidth) / 2);
288 break;
290 case GACT_STRINGRIGHT:
292 WORD textwidth = TextLength(rp, dispstr, dispstrlen);
294 if (cursor_at_end) textwidth += TextLength(rp, cursorkey, CURSORKEYLEN);
295 text_left = bbox->Left + (bbox->Width - 1 - textwidth);
297 break;
299 return (text_left);
302 /*****************************************************************************************/
304 STATIC UWORD GetTextRight(struct Gadget *gad,
305 struct BBox *bbox,
306 struct RastPort *rp,
307 struct IntuitionBase *IntuitionBase)
309 /* Gets right offset of text in the string gadget */
311 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
312 struct StringInfo *strinfo = (struct StringInfo *)gad->SpecialInfo;
313 UWORD text_right = 0;
314 STRPTR dispstr = &(strinfo->Buffer[strinfo->DispPos]);
315 UWORD dispstrlen;
316 BOOL cursor_at_end;
318 cursor_at_end = (strinfo->BufferPos == strinfo->NumChars);
320 dispstrlen = strinfo->NumChars - strinfo->DispPos;
322 /* Calcluate start offset of gadget text */
323 switch (gad->Activation & STRALIGNMASK)
325 case GACT_STRINGLEFT:
326 /* FIXME: is this default: correct? */
327 default:
328 text_right = bbox->Left + TextLength(rp, dispstr, dispstrlen);
329 break;
331 case GACT_STRINGCENTER:
333 WORD textwidth = TextLength(rp, dispstr, dispstrlen);
335 if (cursor_at_end) textwidth += TextLength(rp, cursorkey, CURSORKEYLEN);
336 text_right = bbox->Left + bbox->Width - 1 - ((bbox->Width - textwidth) / 2);
338 break;
340 case GACT_STRINGRIGHT:
341 text_right = bbox->Left + bbox->Width - 1;
342 break;
344 return (text_right);
347 /*****************************************************************************************/
349 STATIC VOID GetPensAndFont(struct Gadget *gad,
350 UWORD *pens,
351 struct Window *win,
352 struct RastPort *rp,
353 struct IntuitionBase *IntuitionBase)
356 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
357 struct DrawInfo *dri = GetScreenDrawInfo(win->WScreen);
359 SetFont(rp, dri->dri_Font);
361 if (gad->Flags & GFLG_STRINGEXTEND)
363 struct StringExtend *strext;
365 strext = ((struct StringInfo *)gad->SpecialInfo)->Extension;
367 if (strext->Font)
369 SetFont(rp, strext->Font);
372 if ((gad->Flags & GFLG_SELECTED) == GFLG_SELECTED)
374 pens[STRTEXTPEN] = strext->ActivePens[0];
375 pens[STRBACKPEN] = strext->ActivePens[1];
377 else
379 pens[STRTEXTPEN] = strext->Pens[0];
380 pens[STRBACKPEN] = strext->Pens[1];
384 else
386 /* We don't care to lock the screen because the window we're
387 ** drawn on does this. If the window is moved to another
388 ** public screen we will be called again to rerender (and get new
389 ** valid pens). If the window is closed, and the screen is closed
390 ** we won't be here anymore.
393 /* jDc: set the font to Topaz,8! */
394 if (GetPrivIBase(IntuitionBase)->TopazFont)
396 SetFont(rp,GetPrivIBase(IntuitionBase)->TopazFont);
397 } else {
398 SetFont(rp,GfxBase->DefaultFont);
401 #if 0
402 if ((gad->Flags & GFLG_SELECTED) == GFLG_SELECTED)
404 pens[STRTEXTPEN] = dri->dri_Pens[TEXTPEN];
405 pens[STRBACKPEN] = dri->dri_Pens[BACKGROUNDPEN];
408 else
410 #endif
411 pens[STRTEXTPEN] = dri->dri_Pens[TEXTPEN];
412 pens[STRBACKPEN] = dri->dri_Pens[BACKGROUNDPEN];
413 #if 0
415 #endif
418 pens[CURSORPEN] = dri->dri_Pens[FILLPEN];
420 FreeScreenDrawInfo(win->WScreen, dri);
422 return;
425 /*****************************************************************************************/
427 ULONG HandleStrInput( struct Gadget *gad,
428 struct GadgetInfo *ginfo,
429 struct InputEvent *ievent,
430 UWORD *imsgcode,
431 struct IntuitionBase *IntuitionBase)
433 struct Library *UtilityBase = GetPrivIBase(IntuitionBase)->UtilityBase;
434 struct Library *KeymapBase = GetPrivIBase(IntuitionBase)->KeymapBase;
435 struct SGWork sgw;
436 struct StringInfo *strinfo = (struct StringInfo *)gad->SpecialInfo;
437 struct StringExtend *strext = NULL;
438 ULONG command = 0;
440 EnterFunc(bug("HandleStrInput(gad=%p, ginfo=%p, ievent=%p)\n",
441 gad, ginfo, ievent));
443 if ((ievent->ie_Class == IECLASS_TIMER)) return 0;
445 D(bug("Gadget text: %s\n", strinfo->Buffer));
447 if (!ginfo)
448 ReturnInt("HandleStrInput", ULONG, 0UL);
450 UpdateStringInfo(gad);
452 /* Initialize SGWork */
453 sgw.Gadget = gad;
454 sgw.StringInfo = strinfo;
455 sgw.WorkBuffer = strinfo->Buffer; /* default */
456 sgw.PrevBuffer = strinfo->Buffer;
457 sgw.Modes = 0;
458 sgw.IEvent = ievent;
459 sgw.Code = 0;
460 sgw.BufferPos = strinfo->BufferPos;
461 sgw.NumChars = strinfo->NumChars;
462 sgw.Actions = 0;
463 sgw.LongInt = strinfo->LongInt;
464 sgw.GadgetInfo = ginfo;
465 sgw.EditOp = EO_NOOP;
467 if (gad->Flags & GFLG_STRINGEXTEND)
469 D(bug("HandleStrInput: Extended gadget\n"));
470 strext = strinfo->Extension;
471 if (strext->WorkBuffer)
473 sgw.WorkBuffer = strext->WorkBuffer;
474 /* The edit hook gets *copy* of the current buffer contents */
475 strcpy(sgw.WorkBuffer, strinfo->Buffer);
477 sgw.Modes = strext->InitialModes;
480 switch (ievent->ie_Class)
482 case IECLASS_RAWMOUSE:
483 if (ievent->ie_Code == SELECTDOWN)
485 command = SGH_CLICK;
486 sgw.Actions = SGA_USE | SGA_REDISPLAY;
487 D(bug("HandleStrInput: RAWMOUSE event\n"));
489 break;
491 case IECLASS_RAWKEY:
493 UBYTE buf;
495 D(bug("HandleStrInput: RAWKEY event\n"));
496 if (ievent->ie_Code & IECODE_UP_PREFIX)
498 D(bug("HandleStrInput: filter UP event\n"));
500 else
502 command = SGH_KEY;
503 sgw.Actions = SGA_USE;
504 if (1 == MapRawKey(sgw.IEvent, &buf, 1, strinfo->AltKeyMap))
506 D(bug("HandleStrInput: sgw.Code 0x%lx\n",buf));
507 sgw.Code = (UWORD)buf;
510 break;
515 if (!command)
516 ReturnInt("HandleStrInput", ULONG , 0UL);
518 /* Call the global editing hook */
520 D(bug("calling global hook, Buffer=%s, WorkBuffer=%s\n",
521 strinfo->Buffer, sgw.WorkBuffer));
522 CallHookPkt(GetPrivIBase(IntuitionBase)->GlobalEditHook, &sgw, &command);
524 /* If there is a local edit hook, run it */
525 if (strext)
527 if (strext->EditHook)
529 D(bug("calling local edit hook\n"));
530 CallHookPkt(strext->EditHook, &sgw, &command);
534 /* Copy possibly changed stuff into stringgad */
535 if (sgw.Actions & SGA_USE)
537 if (strext)
539 if (strext->WorkBuffer)
540 strcpy(strinfo->Buffer, strext->WorkBuffer);
543 strinfo->BufferPos = sgw.BufferPos;
544 strinfo->NumChars = sgw.NumChars;
545 strinfo->LongInt = sgw.LongInt;
547 #if 0
548 if (gad->Activation & GACT_LONGINT)
550 kprintf("strinfo->LongInt = %d\n",strinfo->LongInt);
553 else
555 kprintf("strinfo->Buffer = \"%s\"\n",strinfo->Buffer);
558 #endif
561 if (sgw.Actions & SGA_BEEP)
563 D(bug("SGA_BEEP not yet implemented. (lack of DisplayBeep())\n"));
566 if (sgw.Actions & (SGA_END | SGA_NEXTACTIVE | SGA_PREVACTIVE))
568 gad->Flags &= ~GFLG_SELECTED;
569 *imsgcode = sgw.Code;
570 D(bug("HandleStrInput: SGA_END\n"));
573 if (sgw.Actions & SGA_REDISPLAY)
575 D(bug("HandleStrInput: SGA_REDISPLAY\n"));
576 /* Hack for making strgclass work */
577 if ((gad->GadgetType & GTYP_GTYPEMASK) == GTYP_CUSTOMGADGET)
579 struct RastPort *rp;
580 D(bug("HandleStrInput: Rerendering boopsi gadget\n"));
582 if ((rp = ObtainGIRPort(ginfo)) != NULL)
584 struct gpRender method;
586 method.MethodID = GM_RENDER;
587 method.gpr_GInfo = ginfo;
588 method.gpr_RPort = rp;
589 method.gpr_Redraw = GREDRAW_UPDATE;
591 Custom_DoMethodA(IntuitionBase, gad, (Msg)&method);
593 ReleaseGIRPort(rp);
595 } /* if gadget is a strgclass object */
596 else
598 D(bug("HandleStrInput: Rerendering intuition gadget\n"));
599 RefreshStrGadget(gad, ginfo->gi_Window, ginfo->gi_Requester, IntuitionBase);
603 ReturnInt("HandleStrInput", ULONG, sgw.Actions);
606 /*****************************************************************************************/
608 STATIC ULONG DoSGHClick(struct SGWork *sgw, struct IntuitionBase *IntuitionBase)
611 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
612 struct Gadget *gad;
613 struct StringInfo *strinfo;
614 struct BBox bbox;
616 struct TextFont *oldfont;
617 struct RastPort *rp;
618 struct Window *window;
619 struct Requester *req;
621 UWORD text_left, text_right;
622 WORD mousex;
624 window = sgw->GadgetInfo->gi_Window;
625 req = sgw->GadgetInfo->gi_Requester;
627 GetGadgetDomain(sgw->Gadget, window->WScreen, window, NULL, (struct IBox *)&bbox);
628 mousex = window->MouseX - bbox.Left;
630 EnterFunc(bug("DoSGHClick(sgw=%p)\n", sgw));
632 D(bug("Gadget text: %s\n", sgw->WorkBuffer));
634 rp = sgw->GadgetInfo->gi_RastPort;
635 oldfont = rp->Font;
636 gad = sgw->Gadget;
637 strinfo = (struct StringInfo *)gad->SpecialInfo;
639 CalcBBox(window, req, gad, &bbox);
642 struct DrawInfo *dri;
644 dri = GetScreenDrawInfo(window->WScreen);
645 SetFont(rp, dri->dri_Font);
646 FreeScreenDrawInfo(window->WScreen, dri);
649 if (gad->Flags & GFLG_STRINGEXTEND)
651 struct StringExtend *strext = strinfo->Extension;
653 if (strext->Font)
655 SetFont(rp, strext->Font);
659 /* If we are made active, save contents to undobuffer (if any exists) */
660 if (!(gad->Flags & GFLG_SELECTED))
663 if (strinfo->UndoBuffer)
665 D(bug("sghclick: saving into undo buffer\n"));
666 strcpy(strinfo->UndoBuffer, strinfo->Buffer);
668 gad->Flags |= GFLG_SELECTED;
671 /* Get left & righ offsets of strgad text */
672 text_left = GetTextLeft (gad, &bbox, rp, IntuitionBase);
673 text_right = GetTextRight(gad, &bbox, rp, IntuitionBase);
675 D(bug("sghclick: text_left=%d, text_right=%d\n", text_left, text_right));
676 D(bug("sghclick: disppos=%d, dispcount=%d, cursor=%d\n",
677 strinfo->DispPos, strinfo->DispCount, sgw->BufferPos));
678 D(bug("Gadget text: %s\n", sgw->WorkBuffer));
680 /* Check if mouseclick is inside displayed text */
681 if ((mousex >= text_left) && (mousex <= text_right))
683 /* Find new cursor pos. Uses TextFit() to handle proportional fonts. */
685 struct TextExtent te;
686 STRPTR dispstr = strinfo->Buffer + strinfo->DispPos;
688 sgw->BufferPos = strinfo->DispPos
689 + TextFit(rp, dispstr, sgw->NumChars - strinfo->DispPos,
690 &te, NULL, 1,
691 mousex - text_left, rp->Font->tf_YSize);
692 D(bug("sghclick: click inside text.\n"));
694 else /* Click not inside text */
696 if (mousex < text_left)
698 /* Click on empty space at left. Set cursor to first visible */
699 sgw->BufferPos = strinfo->DispPos;
700 D(bug("sghclick: click left of text.\n"));
702 else
704 /* Click on empty space at right. Set cursor to last visible */
705 sgw->BufferPos = strinfo->DispPos + strinfo->DispCount;
706 D(bug("sghclick: click right of text.\n"));
709 } /* if (click is on text or not) */
711 D(bug("sghclick: new cursor position: %d\n", sgw->BufferPos));
713 sgw->Actions = (SGA_USE|SGA_REDISPLAY);
714 sgw->EditOp = EO_MOVECURSOR;
716 SetFont(rp, oldfont);
718 D(bug("Gadget text: %s\n", sgw->WorkBuffer));
720 ReturnInt ("DoSGHClick", ULONG, 1);
723 /*****************************************************************************************/
725 VOID MoveCharsLeft(STRPTR str, UWORD first, UWORD last, UWORD steps)
727 register UWORD i;
729 for (i = first; i <= last; i ++)
731 str[i - steps] = str[i];
733 str[last] = 0;
736 /*****************************************************************************************/
738 STATIC ULONG DoSGHKey(struct SGWork *sgw, struct IntuitionBase *IntuitionBase)
741 struct Library *UtilityBase = GetPrivIBase(IntuitionBase)->UtilityBase;
742 struct Gadget *gad;
743 struct StringInfo *strinfo;
744 UBYTE letter;
745 ULONG qual;
747 EnterFunc(bug("DoSGHKey(sgw=%p)\n", sgw));
749 gad = sgw->Gadget;
750 strinfo = sgw->StringInfo;
752 qual = sgw->IEvent->ie_Qualifier;
754 D(bug("sghkey: RawKey 0x%lx, sgw_Code 0x%lx\n",sgw->IEvent->ie_Code,sgw->Code));
756 sgw->EditOp = EO_NOOP;
758 D(bug("sghkey: qual 0x%lx ic_Flags 0x%lx Modes 0x%lx\n",
759 qual,
760 GetPrivIBase(IntuitionBase)->IControlPrefs.ic_Flags,
761 sgw->Modes));
763 if (sgw->Code == 0)
765 /* RAW Keys */
767 letter = sgw->IEvent->ie_Code;
769 if ((letter == CURSORLEFT) && (!(qual & SHIFT)))
771 D(bug("sghkey: CURSOR_LEFT\n"));
772 if (sgw->BufferPos > 0)
774 sgw->EditOp = EO_MOVECURSOR;
775 sgw->BufferPos --;
778 else if ((letter == CURSORRIGHT) && (!(qual & SHIFT)))
781 D(bug("sghkey: CURSOR_RIGHT\n"));
782 if (sgw->BufferPos < sgw->NumChars)
784 sgw->EditOp = EO_MOVECURSOR;
785 sgw->BufferPos ++;
789 else if ( ((letter == CURSORLEFT) && (qual & SHIFT)) ||
790 (letter == HOMERAW) )
792 if (sgw->BufferPos > 0)
794 sgw->BufferPos = 0;
795 sgw->EditOp = EO_MOVECURSOR;
798 else if ( ((letter == CURSORRIGHT) && (qual & SHIFT)) ||
799 (letter == ENDRAW) )
801 if (sgw->BufferPos < sgw->NumChars)
803 sgw->BufferPos = sgw->NumChars;
804 sgw->EditOp = EO_MOVECURSOR;
807 else if ((letter == TABRAW) && (qual & SHIFT))
809 D(bug("sghkey: SHIFT TAB\n"));
810 sgw->EditOp = EO_SPECIAL; /* FIXME: ??? is this correct ??? */
811 sgw->Code = 9;
812 sgw->Actions = (SGA_USE|SGA_PREVACTIVE);
814 else if (letter == HELPRAW)
818 else if (qual & IEQUALIFIER_RCOMMAND)
820 /* ANSI key but pressed together with right Amiga key */
821 #if 0
822 sgw->EditOp = EO_SPECIAL; /* FIXME: ??? is this correct ??? */
823 sgw->Actions = (SGA_USE|SGA_REUSE|SGA_END);
824 #endif
825 letter = ToUpper(sgw->Code);
827 /* stegerg: URGENTCHECMKE. Why disabled for AROS? */
828 #ifndef __MORPHOS__
829 /* FIXME: we need to handle RAMIGA-Q,RAMIGA-X here */
830 #else
831 if(letter == 'Q')
833 sgw->EditOp = EO_RESET;
835 if (strinfo->UndoBuffer)
837 D(bug("sghclick: saving into undo buffer\n"));
838 stccpy(strinfo->UndoBuffer, sgw->WorkBuffer,strinfo->MaxChars);
839 sgw->BufferPos = strinfo->UndoPos;
840 sgw->NumChars = strlen(sgw->WorkBuffer);
843 else
844 if(letter == 'X')
846 /* RCmd-X clears the input buffer. In fixed field mode
847 * jump cursor to the start of the buffer
849 D(bug("sghkey: CTRL-X\n"));
851 if (sgw->Modes & SGM_FIXEDFIELD)
853 D(bug("sghkey: SGM_FIXEDFIELD\n"));
854 sgw->BufferPos = 0;
855 sgw->EditOp = EO_MOVECURSOR;
857 else
859 sgw->BufferPos = 0;
860 sgw->WorkBuffer[sgw->BufferPos] = '\0';
861 sgw->NumChars = sgw->BufferPos;
862 sgw->EditOp = EO_CLEAR;
865 #endif
867 else
869 /* ANSI key */
871 letter = sgw->Code;
873 if ((qual & IEQUALIFIER_CONTROL) &&
874 (GetPrivIBase(IntuitionBase)->IControlPrefs.ic_Flags & ICF_STRGAD_FILTER) &&
875 !(sgw->Modes & SGM_NOFILTER))
877 /* ANSI key but pressed together with the control key */
879 D(bug("sghkey: CTRL pressed..filtering on\n"));
881 if (letter == CTRL_A)
883 /* CTRL-A jump cursor to start of buffer */
884 D(bug("sghkey: CTRL-A\n"));
885 sgw->BufferPos = 0;
886 sgw->EditOp = EO_MOVECURSOR;
888 else
889 if (letter == CTRL_B)
891 /* CTRL-B move the cursor back one char */
892 D(bug("sghkey: CTRL-B\n"));
893 if (sgw->BufferPos > 0)
895 sgw->BufferPos--;
896 sgw->EditOp = EO_MOVECURSOR;
899 else
900 if (letter == CTRL_D)
902 /* CTRL-D delete current char */
903 D(bug("sghkey: CTRL-D\n"));
904 if (sgw->BufferPos != sgw->NumChars)
906 MoveCharsLeft(sgw->WorkBuffer, sgw->BufferPos + 1, sgw->NumChars - 1, 1);
907 sgw->NumChars --;
908 sgw->EditOp = EO_DELFORWARD;
911 else
912 if (letter == CTRL_E)
914 /* CTRL-E move the cursor to the end of the line */
915 D(bug("sghkey: CTRL-E\n"));
916 if (sgw->BufferPos != sgw->NumChars)
918 sgw->BufferPos = sgw->NumChars;
919 sgw->EditOp = EO_MOVECURSOR;
922 else
923 if (letter == CTRL_F)
925 /* CTRL-F move the cursor forward one char */
926 D(bug("sghkey: CTRL-F\n"));
927 if (sgw->BufferPos != sgw->NumChars)
929 sgw->BufferPos++;
930 sgw->EditOp = EO_MOVECURSOR;
933 else
934 if (letter == CTRL_H)
936 /* CTRL-H Delete the character to the left of the cursor
937 * In fixed field mode, move cursor to previous character
939 D(bug("sghkey: CTRL-A\n"));
940 if (sgw->Modes & SGM_FIXEDFIELD)
942 D(bug("sghkey: SGM_FIXEDFIELD\n"));
943 if (sgw->BufferPos > 0)
945 sgw->BufferPos--;
946 sgw->EditOp = EO_MOVECURSOR;
949 else
951 if (sgw->BufferPos > 0)
953 int len;
954 len = sgw->NumChars - sgw->BufferPos;
955 memcpy(&sgw->WorkBuffer[sgw->BufferPos-1],&sgw->WorkBuffer[sgw->BufferPos],len);
957 sgw->BufferPos--;
958 sgw->WorkBuffer[sgw->BufferPos + len] = 0;
959 sgw->NumChars--;
960 sgw->EditOp = EO_DELBACKWARD;
964 else
965 if (letter == CTRL_K)
967 /* CTRL-K Delete from the character under the cursor to the end
968 * of the string
970 D(bug("sghkey: CTRL-K\n"));
971 sgw->WorkBuffer[sgw->BufferPos] = 0;
972 sgw->NumChars = sgw->BufferPos;
973 sgw->EditOp = EO_DELFORWARD;
975 else
976 if (letter == CTRL_M)
978 /* CTRL-M Equivalent to Return or Enter
980 D(bug("sghkey: CTRL-M\n"));
981 sgw->EditOp = EO_ENTER;
982 sgw->Code = 0;
983 sgw->Actions = (SGA_USE|SGA_END);
985 else
986 if (letter == CTRL_W)
988 /* CTRL-W Delete the previous word. In fixed field mode, jump
989 * cursor to the start of the previous word
991 D(bug("sghkey: CTRL-W\n"));
992 if (sgw->Modes & SGM_FIXEDFIELD)
994 D(bug("sghkey: SGM_FIXEDFIELD\n"));
996 else
998 if (sgw->BufferPos > 0)
1000 int len;
1001 int pos;
1003 * search space
1005 for (pos=sgw->BufferPos-1;pos >= 0;pos--)
1007 if (sgw->WorkBuffer[pos] == ' ')
1009 break;
1012 len = sgw->NumChars - sgw->BufferPos;
1013 memcpy(&sgw->WorkBuffer[pos],&sgw->WorkBuffer[sgw->BufferPos],len);
1015 sgw->NumChars -= sgw->BufferPos - pos;
1016 sgw->BufferPos = pos;
1017 sgw->WorkBuffer[sgw->BufferPos + len] = 0;
1018 sgw->EditOp = EO_DELBACKWARD;
1022 else
1023 if (letter == CTRL_U)
1025 /* CTRL-U Delete from the character to the left of the cursor
1026 * to the start of the buffer. In fixed field mode, jump cursor to the start
1027 * of the buffer
1029 D(bug("sghkey: CTRL-U\n"));
1030 if (sgw->Modes & SGM_FIXEDFIELD)
1032 D(bug("sghkey: SGM_FIXEDFIELD\n"));
1033 sgw->BufferPos = 0;
1034 sgw->EditOp = EO_MOVECURSOR;
1036 else
1038 if (sgw->BufferPos > 0)
1040 int len;
1041 len = sgw->NumChars - sgw->BufferPos;
1042 memcpy(&sgw->WorkBuffer[0],&sgw->WorkBuffer[sgw->BufferPos],len);
1044 sgw->BufferPos = 0;
1045 sgw->WorkBuffer[len] = 0;
1046 sgw->NumChars = len;
1047 sgw->EditOp = EO_DELBACKWARD;
1051 else
1052 if (letter == CTRL_X)
1054 /* CTRL-X clears the input buffer (like right amiga-x). In fixed field mode
1055 * jump cursor to the start of the buffer
1057 D(bug("sghkey: CTRL-X\n"));
1058 if (sgw->Modes & SGM_FIXEDFIELD)
1060 D(bug("sghkey: SGM_FIXEDFIELD\n"));
1061 sgw->BufferPos = 0;
1062 sgw->EditOp = EO_MOVECURSOR;
1064 else
1066 sgw->BufferPos = 0;
1067 sgw->WorkBuffer[sgw->BufferPos] = 0;
1068 sgw->NumChars = sgw->BufferPos;
1069 sgw->EditOp = EO_CLEAR;
1072 else
1073 if (letter == CTRL_Z)
1075 /* CTRL-Z Jump cursor to the end of the buffer
1077 D(bug("sghkey: CTRL-Z\n"));
1078 sgw->BufferPos = sgw->NumChars;
1079 sgw->EditOp = EO_MOVECURSOR;
1082 else
1083 if (letter == BACKSPACE)
1085 if (sgw->BufferPos != 0)
1087 UWORD first = sgw->BufferPos;
1088 UWORD last = sgw->NumChars - 1;
1089 UWORD steps;
1090 if (qual & SHIFT)
1092 steps = sgw->BufferPos;
1094 sgw->BufferPos = 0;
1095 sgw->NumChars -= steps;
1097 else
1099 sgw->NumChars --;
1100 sgw->BufferPos --;
1101 steps = 1;
1103 MoveCharsLeft(sgw->WorkBuffer, first, last, steps);
1104 sgw->EditOp = EO_DELBACKWARD;
1107 else if (letter == DELETE)
1109 /* Check whether cursor is at the trailing 0 */
1110 if (sgw->BufferPos != sgw->NumChars)
1112 if (qual & SHIFT)
1114 sgw->WorkBuffer[sgw->BufferPos] = 0;
1115 sgw->NumChars = sgw->BufferPos;
1117 else
1119 MoveCharsLeft(sgw->WorkBuffer, sgw->BufferPos + 1, sgw->NumChars - 1, 1);
1120 sgw->NumChars --;
1122 sgw->EditOp = EO_DELFORWARD;
1125 else if (letter == RETURN)
1127 D(bug("sghkey: ENTER\n"));
1128 sgw->EditOp = EO_ENTER;
1129 sgw->Code = 0;
1130 sgw->Actions = (SGA_USE|SGA_END);
1132 else if (letter == TAB)
1134 D(bug("sghkey: TAB\n"));
1135 sgw->EditOp = EO_SPECIAL; /* FIXME: ??? is this correct ??? */
1136 sgw->Actions = (SGA_USE|SGA_NEXTACTIVE);
1138 else
1141 /* Validity check of letter */
1142 if (gad->Activation & GACT_LONGINT)
1144 if (letter == '-')
1146 if ((sgw->BufferPos != 0) || ((sgw->NumChars > 0) && (sgw->WorkBuffer[0] == '-')))
1147 sgw->EditOp = EO_BADFORMAT;
1150 else if ((letter < 0x30) || (letter > 0x39))
1152 sgw->EditOp = EO_BADFORMAT;
1154 else if (sgw->WorkBuffer[sgw->BufferPos] == '-')
1156 sgw->EditOp = EO_BADFORMAT;
1159 else /* Integer gadget ? */
1161 /* Is key a printable character ? */
1162 /* FIXME: Locale should be used here... */
1163 if ((letter & 0x60) == 0)
1165 sgw->EditOp = EO_BADFORMAT;
1167 } /* if (integer or string gadget) */
1169 if (sgw->EditOp != EO_BADFORMAT)
1172 if ((sgw->Modes & SGM_REPLACE) && (sgw->WorkBuffer[sgw->BufferPos]))
1174 D(bug("sghkey: replacing char at pos %d\n", sgw->BufferPos));
1176 sgw->WorkBuffer[sgw->BufferPos] = letter;
1177 sgw->EditOp = EO_REPLACECHAR;
1179 if (sgw->BufferPos < strinfo->MaxChars - 1)
1180 sgw->BufferPos ++;
1183 else
1185 /* Insert mode. Check if there is space for one more character
1186 ** NOTE: MaxChars inludes traing \0, so therefore the '- 1'
1188 if (sgw->NumChars < (strinfo->MaxChars - 1))
1190 register UWORD i;
1192 D(bug("sghkey: inserting char at pos %d\n", sgw->BufferPos));
1193 /* Move characters to the right of insertion point one step to the right */
1194 for (i = sgw->NumChars; i > sgw->BufferPos; i --)
1196 sgw->WorkBuffer[i] = sgw->WorkBuffer[i - 1];
1199 /* Insert letter */
1200 sgw->WorkBuffer[i] = letter;
1201 sgw->EditOp = EO_INSERTCHAR;
1202 sgw->NumChars ++;
1203 sgw->BufferPos ++;
1206 else
1208 sgw->EditOp = EO_NOOP;
1209 } /* if (enough space for ione mor letter) */
1210 } /* if (Replace or Insert mode) */
1211 } /* If (user pressed valid letter) */
1212 } /* Vanilla key but not backspace, delete, ... */
1213 } /* if (key or scancode) */
1215 /* Null-terminate the new string */
1216 sgw->WorkBuffer[sgw->NumChars] = 0;
1218 if (sgw->EditOp != EO_NOOP)
1219 sgw->Actions |= (SGA_USE|SGA_REDISPLAY);
1221 /* Integer gadget ? */
1222 if ((sgw->Actions & SGA_USE) && (gad->Activation & GACT_LONGINT))
1224 sgw->LongInt = atol(sgw->WorkBuffer);
1225 D(bug("Updated string number to %d\n", sgw->LongInt));
1228 ReturnInt ("DoSGHKey", ULONG, 1);
1231 /*****************************************************************************************/
1233 AROS_UFH3(ULONG, GlobalEditFunc,
1234 AROS_UFHA(struct Hook *, hook, A0),
1235 AROS_UFHA(struct SGWork *, sgw, A2),
1236 AROS_UFHA(ULONG *, command, A1)
1239 AROS_USERFUNC_INIT
1241 ULONG retcode = 0;
1243 switch (*command)
1245 case SGH_CLICK:
1246 retcode = DoSGHClick(sgw, (struct IntuitionBase *)hook->h_Data);
1247 break;
1249 case SGH_KEY:
1250 retcode = DoSGHKey (sgw, (struct IntuitionBase *)hook->h_Data);
1251 break;
1254 return retcode;
1256 AROS_USERFUNC_EXIT
1259 /*****************************************************************************************/
1261 VOID RefreshStrGadget(struct Gadget *gad,
1262 struct Window *win,
1263 struct Requester *req,
1264 struct IntuitionBase *IntuitionBase)
1266 struct GadgetInfo gi;
1267 struct RastPort *rp;
1269 EnterFunc(bug("RefreshStrGadget(gad=%p, win=%s)\n", gad, win->Title));
1271 SetupGInfo(&gi, win, req, gad, IntuitionBase);
1273 if ((rp = ObtainGIRPort(&gi)))
1275 if (gad->GadgetRender)
1277 if (gad->Flags & GFLG_GADGIMAGE)
1279 DrawImage(rp,
1280 (struct Image *)gad->GadgetRender,
1281 gad->LeftEdge,
1282 gad->TopEdge);
1284 else
1286 DrawBorder(rp,
1287 (struct Border *)gad->GadgetRender,
1288 gad->LeftEdge,
1289 gad->TopEdge);
1292 ReleaseGIRPort(rp);
1294 UpdateStrGadget(gad, win, req, IntuitionBase);
1296 ReturnVoid("RefreshStrGadget");
1299 /*****************************************************************************************/
1301 VOID UpdateStringInfo(struct Gadget *gad)
1303 /* Updates the stringinfo in case user has set some fields */
1305 struct StringInfo *strinfo = (struct StringInfo *)gad->SpecialInfo;
1307 EnterFunc(bug("UpdateStringInfo(gad=%p)\n", gad));
1309 #if 0
1310 if (gad->Activation & GACT_LONGINT)
1312 if ((strinfo->NumChars != 0) || (strinfo->LongInt != 0))
1314 /* NOTE: The max number of chars written INCLUDES trailing \0 */
1315 snprintf(strinfo->Buffer, strinfo->MaxChars, "%d", strinfo->LongInt);
1318 #endif
1320 strinfo->NumChars = strlen(strinfo->Buffer);
1322 if (strinfo->BufferPos > strinfo->NumChars)
1324 strinfo->BufferPos = strinfo->NumChars;
1327 D(bug("%s gadget contains buffer %s of length %d\n",
1328 (gad->Activation & GACT_LONGINT) ? "Integer" : "String",
1329 strinfo->Buffer, strinfo->NumChars));
1331 ReturnVoid("UpdateStringInfo");
1334 /*****************************************************************************************/
1336 VOID UpdateStrGadget(struct Gadget *gad,
1337 struct Window *win,
1338 struct Requester *req,
1339 struct IntuitionBase *IntuitionBase)
1341 struct GfxBase *GfxBase = GetPrivIBase(IntuitionBase)->GfxBase;
1342 struct GadgetInfo gi;
1343 struct BBox bbox;
1344 struct StringInfo *strinfo = (struct StringInfo *)gad->SpecialInfo;
1345 UWORD text_left;
1346 UWORD text_top;
1347 struct RastPort *rp;
1348 STRPTR dispstr;
1349 UWORD pens[NUMPENS];
1351 EnterFunc(bug("UpdateStrGadget(current text=%s)\n", strinfo->Buffer));
1353 SetupGInfo(&gi, win, req, gad, IntuitionBase);
1355 rp = ObtainGIRPort(&gi);
1356 if (!rp) return;
1358 GetPensAndFont(gad, pens, win, rp, IntuitionBase);
1360 CalcBBox(win, req, gad, &bbox);
1362 /* Update the stringinfo struct in case of user change */
1363 UpdateStringInfo(gad);
1365 /* Update the DispPos and DispCount fields so that the gadget renders properly */
1366 UpdateDisp(gad, &bbox, rp, IntuitionBase);
1369 dispstr = strinfo->Buffer + strinfo->DispPos;
1371 /* Clear the background */
1372 SetAPen(rp, pens[STRBACKPEN]);
1373 SetDrMd(rp, JAM1);
1375 RectFill(rp,
1376 bbox.Left,
1377 bbox.Top,
1378 bbox.Left + bbox.Width - 1,
1379 bbox.Top + bbox.Height - 1);
1381 text_left = GetTextLeft(gad, &bbox, rp, IntuitionBase);
1384 /* Write the text into the gadget */
1385 SetABPenDrMd(rp, pens[STRTEXTPEN], pens[STRBACKPEN], JAM2);
1387 text_top = bbox.Top
1388 + ((bbox.Height - rp->Font->tf_YSize) >> 1)
1389 + rp->Font->tf_Baseline;
1391 Move(rp, text_left, text_top);
1393 D(bug("usg: Writing text %s of length %d at (%d, %d)\n",
1394 dispstr, strinfo->DispCount, text_left, text_top));
1396 Text(rp, dispstr, strinfo->DispCount);
1398 if (gad->Flags & GFLG_SELECTED)
1400 UWORD cursoroffset = strinfo->BufferPos - strinfo->DispPos;
1401 /* Render cursor */
1402 // SetAfPt(rp,NULL,0);
1403 D(bug("usg: Number of characters: %d\n", strinfo->NumChars));
1406 text_left += TextLength(rp, dispstr, cursoroffset);
1408 #if 1
1409 if (strinfo->BufferPos < strinfo->NumChars)
1411 SetABPenDrMd(rp, pens[STRTEXTPEN], pens[CURSORPEN], JAM2);
1412 Move(rp, text_left, text_top);
1413 Text(rp,
1414 dispstr + cursoroffset,
1415 1 );
1417 else
1419 struct TextExtent te;
1421 SetABPenDrMd(rp, pens[STRTEXTPEN], pens[CURSORPEN], JAM2|INVERSVID);
1422 TextExtent(rp, cursorkey, CURSORKEYLEN,&te);
1424 RectFill(rp,
1425 text_left+te.te_Extent.MinX,
1426 text_top+te.te_Extent.MinY,
1427 text_left+te.te_Extent.MaxX,
1428 text_top+te.te_Extent.MaxY);
1430 #else
1431 Move(rp, text_left, text_top);
1432 Text(rp,
1433 ((strinfo->BufferPos < strinfo->NumChars)
1434 ? dispstr + cursoroffset
1435 : cursorkey),
1436 1 );
1437 #endif
1439 } /* if (gadget selected => render cursor) */
1441 if (gad->Flags & GFLG_DISABLED )
1443 struct DrawInfo *dri = GetScreenDrawInfo(win->WScreen);
1445 RenderDisabledPattern(rp, dri, bbox.Left,
1446 bbox.Top,
1447 bbox.Left + bbox.Width - 1,
1448 bbox.Top + bbox.Height - 1,
1449 IntuitionBase );
1451 if (dri) FreeScreenDrawInfo(win->WScreen, dri);
1454 ReleaseGIRPort(rp);
1456 ReturnVoid("UpdateStrGadget");