Tabs to spaces; more consistent formatting.
[AROS.git] / rom / devs / console / charmapconclass.c
blob93b44967b2bf635355b40c7c5326dc57376fab3e
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Code for CONU_CHARMAP console units.
6 Lang: english
7 */
9 #include <string.h>
11 #include <exec/ports.h>
12 #include <proto/graphics.h>
13 #include <proto/intuition.h>
14 #include <proto/alib.h>
15 #include <intuition/intuition.h>
17 #include <intuition/imageclass.h>
18 #include <intuition/gadgetclass.h>
19 #include <intuition/sghooks.h>
20 #include <libraries/gadtools.h>
22 #include <graphics/rastport.h>
23 #include <aros/asmcall.h>
25 #include <stdlib.h>
27 #define SDEBUG 0
28 //#define DEBUG 1
29 #define DEBUG 0
30 #include <aros/debug.h>
32 #include "console_gcc.h"
33 #include "consoleif.h"
35 #include "charmap.h"
37 #define PROP_FLAGS \
38 AUTOKNOB | FREEVERT | PROPNEWLOOK | PROPBORDERLESS
40 #define CODE_COPY 'C'
41 #define CODE_PASTE 'V'
43 struct MyEditHookMsg
45 struct Message msg;
46 struct SGWork *sgw;
47 WORD code;
50 static const STRPTR CONCLIP_PORTNAME = "ConClip.rendezvous";
51 struct Scroll
53 struct Gadget scroller; /* proportionnal gadget */
54 struct Gadget down; /* down gadget */
55 struct Gadget up; /* up gadget */
56 struct PropInfo pinfo; /* PropInfo for scroller */
57 struct Image simage; /* image for scroller */
58 struct Image *upimage; /* Boopsi image for up arrow */
59 struct Image *downimage; /* ditto for down arrow */
62 // FIXME: Abstract out the non-GUI aspects
63 struct charmapcondata
65 /* Start of the scrollback buffer */
66 struct charmap_line *top_of_scrollback;
67 /* The line currently displayed at the top of the screen */
68 struct charmap_line *top_of_window;
69 /* Saved position for the top of the screen at the end of
70 the buffer; where the buffer is reset to if there is
71 output while scrolling */
72 struct charmap_line *saved_top_of_window;
73 ULONG saved_scrollback_pos;
75 ULONG scrollback_size; /* Total size of the scrollback buffer */
76 ULONG scrollback_pos; /* Position of the top of the window */
78 ULONG scrollback_max; /* Maximum number of lines in scrollback
79 buffer on top of CHAR_YMAX(o) */
81 BOOL unrendered; /* Unrendered cursor while scrolled back? */
83 /* FIXME: Belongs in snipmap class */
84 /* Current selection */
85 LONG select_x_min;
86 LONG select_y_min;
87 struct charmap_line *select_line_min;
88 LONG select_x_max;
89 LONG select_y_max;
90 struct charmap_line *select_line_max;
91 BOOL active_selection; /* If true, mouse move will affect the selection */
92 BOOL ignore_drag;
95 UBYTE boopsigad; /* Type of right prop gadget of window */
96 struct Scroll *prop;
98 /* Active gadget */
99 APTR activeGad;
101 /* Libraries */
102 struct Library *ccd_GfxBase;
106 /* Template used to quickly fill constant fields */
107 CONST struct Scroll ScrollBar = {
109 /* PropGadget */
110 NULL, 0, 0, 0, 0,
111 GFLG_RELRIGHT | GFLG_RELHEIGHT,
112 GACT_RIGHTBORDER | GACT_FOLLOWMOUSE | GACT_IMMEDIATE |
113 GACT_RELVERIFY,
114 GTYP_PROPGADGET,
115 NULL, NULL, NULL, 0, NULL,
116 0, NULL
119 /* Up-gadget */
120 NULL, 0, 0, 0, 0,
121 GFLG_RELRIGHT | GFLG_RELBOTTOM | GFLG_GADGHIMAGE |
122 GFLG_GADGIMAGE,
123 GACT_RIGHTBORDER | GACT_RELVERIFY | GACT_IMMEDIATE,
124 GTYP_BOOLGADGET,
125 NULL, NULL, NULL, 0, NULL,
126 1, NULL
129 /* Down-Gadget */
130 NULL, 0, 0, 0, 0,
131 GFLG_RELRIGHT | GFLG_RELBOTTOM | GFLG_GADGHIMAGE |
132 GFLG_GADGIMAGE,
133 GACT_RIGHTBORDER | GACT_RELVERIFY | GACT_IMMEDIATE,
134 GTYP_BOOLGADGET,
135 NULL, NULL, NULL, 0, NULL,
136 2, NULL
139 /* PropInfo */
140 PROP_FLAGS,
141 MAXPOT, MAXPOT,
142 MAXBODY, MAXBODY
144 /* Other values may be NULL */
148 #undef ConsoleDevice
149 #define ConsoleDevice ((struct ConsoleBase *)cl->cl_UserData)
151 #undef GfxBase
152 #define GfxBase (((struct charmapcondata *)INST_DATA(cl, o))->ccd_GfxBase)
154 static VOID charmapcon_refresh(Class *cl, Object *o, LONG off);
156 /*** Allocate and attach a prop gadget to the window ***/
157 static VOID charmapcon_add_prop(Class *cl, Object *o)
159 struct charmapcondata *data = INST_DATA(cl, o);
160 struct Scroll *pg;
161 struct Image *dummy;
162 struct Window *win = CU(o)->cu_Window;
164 UWORD height, size_width, size_height;
166 /* If the window is a backdrop'ed one, use a simplified BOOPSI propgadget
167 * because the next propgadget aspect depends on window activated state */
168 if (win->Flags & WFLG_BACKDROP)
170 /* Yes this is actually a (struct Gadget *)... */
171 if ((data->prop = pg =
172 (struct Scroll *)NewObject(NULL, "propgclass", GA_Top, 0,
173 GA_Left, win->Width - 10, GA_Width, 10, GA_Height,
174 win->Height, GA_RelVerify, TRUE, GA_FollowMouse, TRUE,
175 GA_Immediate, TRUE, PGA_VertPot, MAXPOT, PGA_VertBody,
176 MAXBODY, PGA_Freedom, FREEVERT, PGA_NewLook, TRUE,
177 TAG_END)))
179 /* And finally, add it to the window */
180 AddGList(win, (struct Gadget *)pg, 0, 1, NULL);
181 RefreshGList((struct Gadget *)pg, win, NULL, 1);
184 data->boopsigad = TRUE;
185 return;
187 data->boopsigad = FALSE;
189 /* Get memory */
190 if ((data->prop = pg = (void *)AllocMem(sizeof(*pg), MEMF_PUBLIC)))
192 /* Copy default flags/modes/etc. */
193 CopyMem(&ScrollBar, pg, sizeof(*pg));
194 pg->pinfo.Flags = PROP_FLAGS;
195 struct DrawInfo *di;
197 di = (void *)GetScreenDrawInfo(win->WScreen);
199 /* We need to get size-gadget height, to adjust properly arrows */
200 if ((dummy = (struct Image *)NewObject(NULL, "sysiclass",
201 SYSIA_Which, SIZEIMAGE,
202 SYSIA_DrawInfo, (IPTR) di, TAG_END)))
204 size_width = dummy->Width; /* width of up/down-gadgets */
205 size_height = dummy->Height; /* bottom offset */
207 /* We don't need the image anymore */
208 DisposeObject(dummy);
210 /* Get the boopsi image of the up and down arrow */
211 if ((pg->upimage = (struct Image *)NewObject(NULL, "sysiclass",
212 SYSIA_Which, UPIMAGE,
213 SYSIA_DrawInfo, (IPTR) di, TAG_END)))
215 pg->up.GadgetRender = pg->up.SelectRender =
216 (APTR) pg->upimage;
217 height = pg->upimage->Height;
219 if ((pg->downimage =
220 (struct Image *)NewObject(NULL, "sysiclass",
221 SYSIA_Which, DOWNIMAGE, SYSIA_DrawInfo,
222 (IPTR) di, TAG_END)))
224 struct Gadget *G = (void *)pg;
225 WORD hoffset = size_width / 4;
227 pg->down.GadgetRender = pg->down.SelectRender =
228 (APTR) pg->downimage;
230 /* Release drawinfo */
231 FreeScreenDrawInfo(win->WScreen, di);
233 /* Now init all sizes/positions relative to window's
234 * borders */
235 G->Height =
236 -(win->BorderTop + size_height + 2 * height + 2);
237 G->TopEdge = win->BorderTop + 1;
238 G->Width = size_width - hoffset * 2 + 2;
239 G->LeftEdge = -(size_width - hoffset);
240 G++;
241 pg->up.LeftEdge = G->LeftEdge = -(size_width - 1);
242 G->Width = pg->up.Width = size_width;
243 G->Height = pg->up.Height = height;
244 G->TopEdge = -(size_height + height - 1);
245 pg->up.TopEdge = G->TopEdge - height;
247 /* Other fields */
248 pg->scroller.GadgetRender = (APTR) &pg->simage;
249 pg->scroller.SpecialInfo = (APTR) &pg->pinfo;
251 /* Link gadgets */
252 pg->scroller.NextGadget = &pg->up;
253 pg->up.NextGadget = &pg->down;
255 /* And finally, add them to the window */
256 AddGList(win, &pg->scroller, 0, 3, NULL);
257 RefreshGList(&pg->scroller, win, NULL, 3);
259 return;
261 DisposeObject(pg->upimage);
264 FreeMem(pg, sizeof(*pg));
265 FreeScreenDrawInfo(win->WScreen, di);
267 return;
270 static VOID charmapcon_adj_prop(Class *cl, Object *o)
272 struct charmapcondata *data = INST_DATA(cl, o);
273 struct Window *w = CU(o)->cu_Window;
274 ULONG VertBody, VertPot;
276 ULONG hidden =
277 data->scrollback_size >
278 CHAR_YMAX(o) ? data->scrollback_size - CHAR_YMAX(o) - 1 : 0;
280 if (hidden > 0)
282 VertPot = (data->scrollback_pos) * MAXPOT / hidden;
283 VertBody = CHAR_YMAX(o) * MAXBODY / data->scrollback_size;
285 else
287 VertPot = 0;
288 VertBody = MAXBODY;
291 if (VertPot > MAXPOT)
293 VertPot = MAXPOT;
294 D(bug("VERTPOT SET TOO HIGH. Adjusted\n"));
297 NewModifyProp((struct Gadget *)&(data->prop->scroller), w, NULL,
298 ((struct PropInfo *)data->prop->scroller.SpecialInfo)->Flags,
299 MAXPOT, VertPot, MAXBODY, VertBody, 1);
302 /*** Free resources allocated for scroller ***/
303 void charmapcon_free_prop(Class *cl, Object *o)
305 struct charmapcondata *data = INST_DATA(cl, o);
306 struct Window *win = CU(o)->cu_Window;
308 if (data->prop)
310 if (win)
312 if (data->boopsigad)
313 RemoveGadget(win, (struct Gadget *)data->prop);
314 else
315 RemoveGList(win, &data->prop->scroller, 3);
317 if (data->boopsigad)
318 DisposeObject(data->prop);
319 else
321 /* Free elements */
322 DisposeObject(data->prop->upimage);
323 DisposeObject(data->prop->downimage);
325 /* Free struct */
326 FreeMem(data->prop, sizeof(*data->prop));
333 /*********** CharMapCon::New() **********************/
335 static Object *charmapcon_new(Class *cl, Object *o, struct opSet *msg)
337 EnterFunc(bug("CharMapCon::New()\n"));
338 APTR newGfxBase = TaggedOpenLibrary(TAGGEDOPEN_GRAPHICS);
339 if (newGfxBase == NULL)
340 return NULL;
342 o = (Object *) DoSuperMethodA(cl, o, (Msg) msg);
343 if (o)
345 struct charmapcondata *data = INST_DATA(cl, o);
347 /* Clear for checking inside dispose() whether stuff was allocated.
348 Basically this is bug-prevention.
350 memset(data, 0, sizeof(struct charmapcondata));
352 data->scrollback_max = 500; /* FIXME: Don't hardcode it */
353 data->ccd_GfxBase = newGfxBase;
354 charmapcon_add_prop(cl, o);
356 ReturnPtr("CharMapCon::New", Object *, o);
359 CloseLibrary(newGfxBase);
360 ReturnPtr("CharMapCon::New", Object *, NULL);
364 /*********** CharMapCon::Dispose() **************************/
366 static VOID charmapcon_dispose(Class *cl, Object *o, Msg msg)
368 struct charmapcondata *data = INST_DATA(cl, o);
370 charmap_dispose_lines(data->top_of_scrollback);
371 charmapcon_free_prop(cl, o);
373 CloseLibrary(data->ccd_GfxBase);
375 DoSuperMethodA(cl, o, msg);
378 /********* CharMapCon::DoCommand() ****************************/
380 static struct charmap_line *charmapcon_find_line(Class *cl, Object *o,
381 ULONG ycp)
383 struct charmapcondata *data = INST_DATA(cl, o);
385 // Find the line. This is inefficient but the number of lines on screen
386 // should never be very high.
387 // FIXME: Optimizing the case of appending to the end (e.g. know what line
388 // is on the last line of the buffer).
390 struct charmap_line *line = data->top_of_window;
391 if (!line)
393 D(bug("Initializing charmap\n"));
394 data->top_of_window = data->top_of_scrollback = line =
395 charmap_newline(0, 0);
396 data->scrollback_size = 1;
399 D(bug("Finding line %ld\n", ycp));
400 while (ycp > 0)
402 if (!line->next)
404 charmap_newline(0, line);
405 data->scrollback_size += 1;
407 line = line->next;
408 ycp -= 1;
411 while (data->scrollback_size > data->scrollback_max + CHAR_YMAX(o) &&
412 data->top_of_window != data->top_of_scrollback)
414 data->scrollback_size -= 1;
415 data->scrollback_pos -= 1;
417 /* FIXME: Needs testing... */
418 if (data->select_line_max == data->select_line_min &&
419 data->select_line_min == data->top_of_scrollback)
421 /* The entire selection has scrolled out, but we keep it in case
422 it is still active
424 data->select_line_min = data->select_line_min->next;
425 data->select_line_max = data->select_line_min;
426 data->select_x_min = 0;
427 data->select_x_max = 0;
428 data->select_y_min += 1;
429 data->select_y_max += 1;
431 else if (data->top_of_scrollback == data->select_line_min)
433 data->select_line_min = data->select_line_min->next;
434 data->select_y_min += 1;
435 data->select_x_min = 0;
437 else if (data->top_of_scrollback == data->select_line_max)
439 /* "Reversed" selection */
440 data->select_line_max = data->select_line_max->next;
441 data->select_y_max += 1;
442 data->select_x_max = 0;
445 data->top_of_scrollback =
446 charmap_dispose_line(data->top_of_scrollback);
449 return line;
452 static VOID charmap_ascii(Class *cl, Object *o, ULONG xcp, ULONG ycp,
453 char *str, ULONG len)
455 struct charmap_line *line = charmapcon_find_line(cl, o, ycp);
456 ULONG oldsize = line->size;
458 // Ensure the line has sufficient capacity.
459 if (line->size < xcp + len)
460 charmap_resize(line, xcp + len);
462 // .. copy the required data
463 memset(line->fgpen + xcp, CU(o)->cu_FgPen, len);
464 memset(line->bgpen + xcp, CU(o)->cu_BgPen, len);
465 memset(line->flags + xcp, CU(o)->cu_TxFlags, len);
466 memcpy(line->text + xcp, str, len);
468 // If cursor output is moved further right on the screen than
469 // the last output, we need to fill the line
470 if (oldsize < xcp)
472 memset(line->fgpen + oldsize, CU(o)->cu_FgPen, xcp - oldsize);
473 memset(line->bgpen + oldsize, CU(o)->cu_BgPen, xcp - oldsize);
474 memset(line->flags + oldsize, CU(o)->cu_TxFlags, xcp - oldsize);
475 memset(line->text + oldsize, ' ', xcp - oldsize);
479 static VOID charmap_scroll_up(Class *cl, Object *o, ULONG y)
481 struct charmapcondata *data = INST_DATA(cl, o);
483 if (!data->top_of_window)
484 return;
486 while (y--)
488 if (!data->top_of_window->next)
490 charmap_newline(0, data->top_of_window);
491 data->scrollback_size += 1;
493 data->top_of_window = data->top_of_window->next;
494 data->scrollback_pos += 1;
495 data->select_y_max -= 1;
496 data->select_y_min -= 1;
499 if (data->scrollback_size - CHAR_YMAX(o) - 1 <= data->scrollback_pos &&
500 data->unrendered)
502 Console_RenderCursor(o);
503 data->unrendered = 0;
506 while (data->scrollback_size > data->scrollback_max + CHAR_YMAX(o) &&
507 data->top_of_window != data->top_of_scrollback)
509 data->scrollback_size -= 1;
510 data->scrollback_pos -= 1;
511 data->top_of_scrollback =
512 charmap_dispose_line(data->top_of_scrollback);
516 static VOID charmap_scroll_down(Class *cl, Object *o, ULONG y)
518 // FIXME: Need to adjust cursor position or reset to bottom when editing.
519 struct charmapcondata *data = INST_DATA(cl, o);
520 if (!data->unrendered)
522 Console_UnRenderCursor(o);
523 data->unrendered = 1;
524 data->saved_scrollback_pos = data->scrollback_pos;
525 data->saved_top_of_window = data->top_of_window;
527 // FIXME: Select position.
529 if (data->top_of_window)
531 while (y-- && data->top_of_window->prev)
533 data->top_of_window = data->top_of_window->prev;
534 data->scrollback_pos -= 1;
535 data->select_y_max += 1;
536 data->select_y_min += 1;
541 static VOID charmapcon_scroll_to(Class *cl, Object *o, ULONG y)
543 struct charmapcondata *data = INST_DATA(cl, o);
544 struct Window *w = CU(o)->cu_Window;
545 struct RastPort *rp = w->RPort;
546 LONG off = data->scrollback_pos - y;
547 LONG old_pos = data->scrollback_pos;
549 if (off == 0)
550 return;
552 Console_UnRenderCursor(o);
554 if (off > 0)
555 charmap_scroll_down(cl, o, off);
556 else
557 charmap_scroll_up(cl, o, -off);
559 /* Correct offset to account for the fact we might reach the
560 * top or bottom of the buffer:
562 off = old_pos - data->scrollback_pos;
564 /* A whole screenful? If so we have no choice but a full refresh
565 * (though we could double buffer... Not sure that's worth the
566 * memory cost)
568 if (abs(off) > CHAR_YMAX(o))
570 charmapcon_refresh(cl, o, 0);
571 Console_RenderCursor(o);
572 return;
575 /* Avoid a full refresh by scrolling the rastport.
576 * Use "standard" background to reduce flicker
578 SetBPen(rp, CU(o)->cu_BgPen);
579 if (off > 0)
581 ScrollRaster(rp,
583 -YRSIZE * off,
584 GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o));
586 else
588 ScrollRaster(rp,
590 -YRSIZE * off,
591 GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o));
595 /* Partial refresh */
596 charmapcon_refresh(cl, o, off);
598 Console_RenderCursor(o);
602 static VOID charmap_delete_char(Class *cl, Object *o, ULONG x, ULONG y)
604 struct charmap_line *line = charmapcon_find_line(cl, o, y);
606 if (!line || x >= line->size)
607 return;
609 // FIXME: Shrink the buffer, or keep track of capacity separately.
610 if (x + 1 >= line->size)
612 line->text[x] = 0;
613 return;
616 memmove(line->fgpen + x, line->fgpen + x + 1, 1);
617 memmove(line->bgpen + x, line->bgpen + x + 1, 1);
618 memmove(line->flags + x, line->flags + x + 1, 1);
619 memmove(line->text + x, line->text + x + 1, 1);
622 static VOID charmap_insert_char(Class *cl, Object *o, ULONG x, ULONG y)
624 struct charmap_line *line = charmapcon_find_line(cl, o, y);
626 if (x >= line->size)
627 return;
629 /* FIXME: This is wasteful, since it copies the buffers straight over,
630 * so we have to do memmove's further down. */
631 charmap_resize(line, line->size + 1);
633 memmove(line->fgpen + x + 1, line->fgpen + x, line->size - x - 1);
634 memmove(line->bgpen + x + 1, line->bgpen + x, line->size - x - 1);
635 memmove(line->flags + x + 1, line->flags + x, line->size - x - 1);
636 memmove(line->text + x + 1, line->text + x, line->size - x - 1);
638 line->fgpen[x] = CU(o)->cu_FgPen;
639 line->bgpen[x] = CU(o)->cu_BgPen;
640 line->flags[x] = CU(o)->cu_TxFlags;
641 line->text[x] = ' ';
644 static VOID charmap_formfeed(Class *cl, Object *o)
646 struct charmapcondata *data = INST_DATA(cl, o);
647 struct charmap_line *line = data->top_of_window;
649 while (line)
651 charmap_resize(line, 0);
652 line = line->next;
656 static VOID charmapcon_docommand(Class *cl, Object *o,
657 struct P_Console_DoCommand *msg)
659 IPTR *params = msg->Params;
660 struct charmapcondata *data = INST_DATA(cl, o);
662 EnterFunc(bug
663 ("CharMapCon::DoCommand(o=%p, cmd=%d, params=%p) x=%d, y=%d, ymax=%d\n",
664 o, msg->Command, params, XCP, YCP, CHAR_YMAX(o)));
666 // This is a bit of a hack: Set position to bottom in order to prevent
667 // output while scrolled.
669 ULONG old_scrollback_size = data->scrollback_size;
670 ULONG old_scrollback_pos = data->scrollback_pos;
672 if (data->unrendered)
674 data->unrendered = 0;
675 data->scrollback_pos = data->saved_scrollback_pos;
676 data->select_y_min += old_scrollback_pos - data->scrollback_pos;
677 data->select_y_max += old_scrollback_pos - data->scrollback_pos;
678 data->top_of_window = data->saved_top_of_window;
679 charmapcon_refresh(cl, o, 0);
680 Console_RenderCursor(o);
683 switch (msg->Command)
685 case C_ASCII:
686 charmap_ascii(cl, o, XCP, YCP, (char *)&params[0], 1);
687 DoSuperMethodA(cl, o, (Msg) msg);
688 break;
690 case C_ASCII_STRING:
691 charmap_ascii(cl, o, XCP, YCP, (char *)params[0], (int)params[1]);
692 DoSuperMethodA(cl, o, (Msg) msg);
693 break;
695 case C_FORMFEED:
696 charmap_formfeed(cl, o);
697 DoSuperMethodA(cl, o, (Msg) msg);
698 break;
700 case C_DELETE_CHAR: /* FIXME: can it have params!? */
701 charmap_delete_char(cl, o, XCP, YCP);
702 DoSuperMethodA(cl, o, (Msg) msg);
703 break;
705 case C_INSERT_CHAR:
706 charmap_insert_char(cl, o, XCP, YCP);
707 DoSuperMethodA(cl, o, (Msg) msg);
708 break;
710 case C_SCROLL_UP:
712 D(bug("C_SCROLL_UP area (%d, %d) to (%d, %d), %d\n",
713 GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o),
714 YRSIZE * params[0]));
715 charmap_scroll_up(cl, o, params[0]);
716 DoSuperMethodA(cl, o, (Msg) msg);
717 break;
720 case C_SCROLL_DOWN:
722 D(bug("C_SCROLL_DOWN area (%d, %d) to (%d, %d), %d\n",
723 GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o),
724 YRSIZE * params[0]));
725 charmap_scroll_down(cl, o, params[0]);
726 DoSuperMethodA(cl, o, (Msg) msg);
727 break;
730 default:
731 DoSuperMethodA(cl, o, (Msg) msg);
732 break;
735 if (old_scrollback_size != data->scrollback_size ||
736 old_scrollback_pos != data->scrollback_pos)
737 charmapcon_adj_prop(cl, o);
739 ReturnVoid("CharMapCon::DoCommand");
742 /**************************
743 ** CharMapCon::ClearCell() **
744 **************************/
745 static VOID charmapcon_clearcell(Class *cl, Object *o,
746 struct P_Console_ClearCell *msg)
748 // FIXME, insert space.
749 DoSuperMethodA(cl, o, (Msg) msg);
752 static VOID charmapcon_refresh_lines(Class *cl, Object *o, LONG fromLine,
753 LONG toLine)
755 struct Window *w = CU(o)->cu_Window;
756 struct RastPort *rp = w->RPort;
757 struct charmapcondata *data = INST_DATA(cl, o);
759 if (fromLine < CHAR_YMIN(o))
760 fromLine = CHAR_YMIN(o);
761 if (toLine > CHAR_YMAX(o))
762 toLine = CHAR_YMAX(o);
764 D(bug("fromLine: %ld, toLine: %ld, char_ymax: %ld\n", fromLine, toLine,
765 CHAR_YMAX(o)));
767 if (toLine < fromLine)
768 return;
770 Console_UnRenderCursor(o);
772 D(bug("Rendering charmap\n"));
774 struct charmap_line *line = charmapcon_find_line(cl, o, fromLine);
775 ULONG y = GFX_YMIN(o) + fromLine * YRSIZE + rp->Font->tf_Baseline;
776 ULONG yc = fromLine;
777 UBYTE flags = 255;
779 LONG selectstart_x, selectstart_y, selectend_x, selectend_y;
780 if (data->select_y_min == data->select_y_max)
782 selectstart_y = selectend_y = data->select_y_min;
783 selectstart_x = MIN(data->select_x_min, data->select_x_max);
784 selectend_x = MAX(data->select_x_min, data->select_x_max);
786 else if (data->select_y_min < data->select_y_max)
788 selectstart_y = data->select_y_min;
789 selectstart_x = data->select_x_min;
790 selectend_y = data->select_y_max;
791 selectend_x = data->select_x_max;
793 else
795 selectstart_y = data->select_y_max;
796 selectstart_x = data->select_x_max;
797 selectend_y = data->select_y_min;
798 selectend_x = data->select_x_min;
801 while (line && yc <= toLine)
803 const char *str = line->text;
804 ULONG start = 0;
805 ULONG remaining_space = CHAR_XMAX(o) + 1;
806 Move(rp, GFX_XMIN(o), y);
807 while (line->size > start && remaining_space > 0 && str[start])
809 /* Identify a batch of characters with the same fgpen/bgpen
810 to avoid having to move/set pens and do Text() on single
811 characters */
813 UBYTE fgpen = line->fgpen[start];
814 UBYTE bgpen = line->bgpen[start];
816 /* Is any part of this line part of a selection?
817 * If so, we bake in a state transition on "stop".
818 * This code is messy - there must be a nicer way.
820 ULONG stop = 9999999;
821 BOOL in_selection = 0;
823 if (yc > selectstart_y && yc < selectend_y)
825 in_selection = 1;
827 else if (yc == selectstart_y)
829 if (yc == selectend_y)
831 if (start >= selectstart_x && start < selectend_x)
833 in_selection = 1;
834 stop = selectend_x;
836 else if (start < selectstart_x)
838 stop = selectstart_x;
841 else
843 if (start >= selectstart_x)
844 in_selection = 1;
845 else
846 stop = selectstart_x;
849 else if (yc == selectend_y)
851 /* In this case, the selection *ends* on selectend_x */
852 if (start < selectend_x)
854 in_selection = 1;
855 stop = selectend_x;
859 if (stop == start)
860 stop += 1;
862 if (in_selection)
864 fgpen = line->bgpen[start];
865 bgpen = line->fgpen[start];
868 ULONG len = 0;
869 while (line->size > start + len && str[start + len] &&
870 len < remaining_space &&
871 line->fgpen[start] == line->fgpen[start + len] &&
872 line->bgpen[start] == line->bgpen[start + len] &&
873 line->flags[start] == line->flags[start + len] &&
874 start + len < stop)
875 len += 1;
877 setabpen(GfxBase, rp, line->flags[start], fgpen, bgpen);
878 if ((line->flags[start] & CON_TXTFLAGS_MASK) !=
879 (flags & CON_TXTFLAGS_MASK))
881 SetSoftStyle(rp, line->flags[start], CON_TXTFLAGS_MASK);
883 flags = line->flags[start];
885 Text(rp, &str[start], len);
887 start += len;
888 remaining_space -= len;
891 /* Clear to EOL, without overwriting scroll bar (ClearEOL does) */
892 SetAPen(rp, CU(o)->cu_BgPen);
893 RectFill(rp,
894 GFX_X(o, start), GFX_Y(o, yc),
895 GFX_XMAX(o), GFX_Y(o, yc + 1) - 1);
896 y += YRSIZE;
897 yc++;
899 /* We want to make sure we have lines covering the window once
900 there's something to scroll back, as that simplies resize handling
901 etc.
903 if (!line->next && yc <= toLine)
905 line->next = charmap_newline(0, line);
906 data->scrollback_size += 1;
908 line = line->next;
911 if (yc < toLine)
913 SetAPen(rp, CU(o)->cu_BgPen);
914 RectFill(rp,
915 GFX_XMIN(o), GFX_Y(o, yc), GFX_XMAX(o), GFX_Y(o, toLine));
918 Console_RenderCursor(o);
923 * Refresh the full console unless "off" is provided.
924 * If off is set to a positive value, it indicates the
925 * number of rows from the top we start rendering.
926 * If off is set to a negative value, it indicates the
927 * number of rows from the top we stop rendering.
928 * This is used for partial refreshes when the screen is
929 * scrolled up/down.
931 static VOID charmapcon_refresh(Class *cl, Object *o, LONG off)
933 LONG fromLine = 0;
934 LONG toLine = CHAR_YMAX(o) - CHAR_YMIN(o);
936 if (off > 0)
937 toLine = off - 1;
938 if (off < 0)
939 fromLine = CHAR_YMAX(o) + off + 1;
941 charmapcon_refresh_lines(cl, o, fromLine, toLine);
945 static ULONG charmapcon_calc_selection_size(struct charmap_line *first,
946 struct charmap_line *last, ULONG minx, ULONG maxx)
948 struct charmap_line *cur = first;
949 ULONG size = 0;
950 while (cur)
952 if (cur->text)
954 if (cur == first)
956 if (cur == last)
957 size += abs(maxx - minx);
958 else
959 size += cur->size - minx;
961 else if (cur == last)
963 size += maxx;
965 else
967 size += cur->size;
970 if (cur != last || (cur == last && maxx == cur->size))
971 size += 1; /* line feed */
973 if (cur == last)
974 break;
975 cur = cur->next;
977 return size;
980 char *charmapcon_get_selection(ULONG size,
981 struct charmap_line *first,
982 struct charmap_line *last, ULONG minx, ULONG maxx)
984 char *buf = AllocMem(size, MEMF_ANY);
985 char *bufpos = buf;
986 struct charmap_line *cur = first;
988 if (!bufpos)
989 return 0;
991 while (cur)
993 if (cur->text)
995 /* empty lines may not have text ptrs */
996 if (cur == first)
998 if (cur == last)
1000 CopyMem(cur->text + MIN(minx, maxx), bufpos,
1001 abs(maxx - minx));
1002 bufpos += abs(maxx - minx);
1004 else
1006 CopyMem(cur->text + minx, bufpos, cur->size - minx);
1007 bufpos += cur->size - minx;
1010 else if (cur == last)
1012 CopyMem(cur->text, bufpos, maxx);
1013 bufpos += maxx;
1015 else
1017 CopyMem(cur->text, bufpos, cur->size);
1018 bufpos += cur->size;
1021 if (cur != last || (cur == last && maxx == cur->size)) /* line feed */
1023 *bufpos = '\r';
1024 bufpos += 1;
1026 if (cur == last)
1027 break;
1028 cur = cur->next;
1030 return buf;
1033 struct ConClipData
1035 ULONG flags; /* always zero? */
1036 ULONG size; /* does not include NUL termination */
1037 APTR buffer; /* NUL-terminated string! */
1040 /* FIXME: Belongs in snipmapcon - here temporarily until refactored out
1041 selection code */
1042 static VOID charmapcon_copy(Class *cl, Object *o, Msg copymsg)
1044 struct charmapcondata *data = INST_DATA(cl, o);
1045 struct MsgPort replyport, *port;
1046 struct SGWork sgw;
1047 struct MyEditHookMsg msg;
1048 char *buf;
1050 struct charmap_line *first, *last;
1051 ULONG minx, maxx, size;
1053 /* Create a string from the contents of the scrollback buffer */
1054 if (data->select_y_min < data->select_y_max)
1056 first = data->select_line_min;
1057 last = data->select_line_max;
1058 minx = data->select_x_min;
1059 maxx = data->select_x_max;
1061 else
1063 first = data->select_line_max;
1064 last = data->select_line_min;
1065 minx = data->select_x_max;
1066 maxx = data->select_x_min;
1069 size = charmapcon_calc_selection_size(first, last, minx, maxx);
1070 buf = charmapcon_get_selection(size, first, last, minx, maxx);
1071 D(bug("%d bytes copied\n"));
1073 /* If Conclip is running, we prefer using that */
1074 if (IsListEmpty(&ConsoleDevice->sniphooks)
1075 && (port = FindPort(CONCLIP_PORTNAME)))
1077 /* AROS conclip format */
1078 D(bug("AROS conclip\n"));
1079 replyport.mp_Node.ln_Type = NT_MSGPORT;
1080 replyport.mp_Node.ln_Name = NULL;
1081 replyport.mp_Node.ln_Pri = 0;
1082 replyport.mp_Flags = PA_SIGNAL;
1083 replyport.mp_SigBit = SIGB_SINGLE;
1084 replyport.mp_SigTask = FindTask(NULL);
1085 NewList(&replyport.mp_MsgList);
1087 msg.msg.mn_Node.ln_Type = NT_MESSAGE;
1088 msg.msg.mn_ReplyPort = &replyport;
1089 msg.msg.mn_Length = sizeof(msg);
1091 msg.code = CODE_COPY;
1092 msg.sgw = &sgw;
1094 sgw.Gadget = 0;
1095 sgw.WorkBuffer = buf;
1096 sgw.PrevBuffer = 0;
1097 sgw.IEvent = 0;
1098 sgw.Code = CODE_COPY;
1099 sgw.Actions = 0;
1100 sgw.LongInt = 0;
1101 sgw.GadgetInfo = 0;
1102 sgw.EditOp = EO_BIGCHANGE;
1103 sgw.BufferPos = 0;
1104 sgw.NumChars = size;
1106 SetSignal(0, SIGF_SINGLE);
1107 PutMsg(port, &msg.msg);
1108 WaitPort(&replyport);
1110 FreeMem(buf, size);
1111 return;
1114 ObtainSemaphore(&ConsoleDevice->copyBufferLock);
1115 FreeMem((APTR) ConsoleDevice->copyBuffer,
1116 ConsoleDevice->copyBufferSize);
1118 ConsoleDevice->copyBuffer = buf;
1119 if (ConsoleDevice->copyBuffer)
1120 ConsoleDevice->copyBufferSize = size;
1121 else
1122 ConsoleDevice->copyBufferSize = 0;
1124 if (!IsListEmpty(&ConsoleDevice->sniphooks))
1126 /* OS2-3.x compatible conclip format */
1127 struct Hook *conhook;
1128 struct ConClipData ccd;
1130 D(bug("AOS conclip\n"));
1131 if (ConsoleDevice->copyBufferSize)
1133 ccd.flags = 0;
1134 ccd.size = ConsoleDevice->copyBufferSize;
1135 /* must be NUL-terminated */
1136 ccd.buffer = AllocVec(ccd.size + 1, MEMF_CLEAR);
1137 if (ccd.buffer)
1139 CopyMem(ConsoleDevice->copyBuffer, ccd.buffer, ccd.size);
1140 ForeachNode(&ConsoleDevice->sniphooks, conhook)
1142 D(bug("Calling AOS conclip hook %p\n", conhook));
1143 CALLHOOKPKT(conhook, NULL, &ccd);
1145 FreeVec(ccd.buffer);
1149 ReleaseSemaphore(&ConsoleDevice->copyBufferLock);
1153 /**********************************
1154 ** CharMapCon::NewWindowSize() **
1155 **********************************/
1156 static VOID charmapcon_newwindowsize(Class *cl, Object *o,
1157 struct P_Console_NewWindowSize *msg)
1159 struct charmapcondata *data = INST_DATA(cl, o);
1161 WORD old_ycp = YCP;
1163 DoSuperMethodA(cl, o, (Msg) msg);
1164 D(bug("CharMapCon::NewWindowSize(o=%p) x=%d, y=%d, ymax=%d\n",
1165 o, XCP, YCP, CHAR_YMAX(o)));
1167 // Is console empty? Unlikely, but anyway.
1168 if (!data->top_of_window)
1169 return;
1171 // Scroll up if new window size has forced the cursor up
1172 if (old_ycp > CHAR_YMAX(o))
1174 charmap_scroll_up(cl, o, old_ycp - CHAR_YMAX(o));
1177 charmapcon_refresh(cl, o, 0);
1180 static VOID charmapcon_handlemouse(Class *cl, Object *o,
1181 struct P_Console_HandleGadgets *msg)
1183 struct charmapcondata *data = INST_DATA(cl, o);
1184 struct Window *w = CU(o)->cu_Window;
1185 struct InputEvent *e = msg->Event;
1187 /* We have the following states:
1188 * - No active selection and no mouse button => ignore
1189 * - No active selection and left mouse button => Start selection
1190 * - Active selection and no mouse button => End selection
1191 * - Active selection and left mouse button => Update selection
1194 LONG x, y;
1197 * Take window-relative mouse position.
1198 * The original code here failed when the screen was moved away from (0, 0)
1199 * position. x and y were unaware of shifted display (ie_X and ie_Y are
1200 * raw physical coordinates, and w->LeftEdge and w->TopEdge are screen-
1201 * relative), and this caused lockups here.
1202 * TODO: Verify this intuition's behavior with AmigaOS3.1 using screentest
1203 * program. (sonic)
1205 x = e->ie_X - w->LeftEdge;
1206 y = e->ie_Y - w->TopEdge;
1208 x = w->MouseX;
1209 y = w->MouseY;
1211 /* Active selection */
1213 if (!(e->ie_Qualifier & IEQUALIFIER_LEFTBUTTON))
1215 /* End selection */
1216 data->active_selection = 0;
1217 data->ignore_drag = 0;
1218 return;
1221 if (!data->active_selection)
1223 if (e->ie_Qualifier & IEQUALIFIER_LEFTBUTTON && !data->ignore_drag)
1225 /* Inside the console area? */
1226 if (x >= GFX_XMIN(o) && x <= GFX_XMAX(o) &&
1227 y >= GFX_YMIN(o) && y <= GFX_YMAX(o))
1229 D(bug("activated selection with x: %ld, y: %ld,"
1230 " xmin: %ld, ymin: %Ld, xmax: %ld, ymax: %ld\n",
1231 x, y, GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o),
1232 GFX_YMAX(o)));
1234 /* We need to clear these lines
1236 LONG old_min_y =
1237 MIN(data->select_y_min, data->select_y_max);
1238 LONG old_max_y =
1239 MAX(data->select_y_min, data->select_y_max);
1241 /* Yes, so start selection */
1242 data->active_selection = 1;
1243 data->ignore_drag = 0;
1244 data->select_x_min = (x - GFX_XMIN(o)) / XRSIZE;
1245 data->select_y_min = (y - GFX_YMIN(o)) / YRSIZE;
1246 data->select_line_min =
1247 charmapcon_find_line(cl, o, data->select_y_min);
1249 data->select_x_max = data->select_x_min;
1250 data->select_y_max = data->select_y_min;
1252 /* Clear */
1254 /* FIXME: Determine exactly which lines are affected, as
1255 follows:
1257 - If max_y has increased, render from old_max_y to new
1258 - If min_y has decreased, refresh from new min y to old
1259 - If both have changed, refresh in two batches.
1260 - If there's been a reversal of the relative positions of
1261 y_min/y_max, refresh the entire range.
1263 charmapcon_refresh_lines(cl, o, old_min_y, old_max_y);
1266 else
1267 data->ignore_drag = 1;
1269 return;
1272 /* Update selection. */
1273 if (x >= GFX_XMIN(o) && x <= GFX_XMAX(o) &&
1274 y >= GFX_YMIN(o) && y <= GFX_YMAX(o))
1277 LONG xmax, ymax;
1278 xmax = data->select_x_max;
1279 ymax = data->select_y_max;
1280 data->select_x_max = (x - GFX_XMIN(o)) / XRSIZE;
1281 data->select_y_max = (y - GFX_YMIN(o)) / YRSIZE;
1282 data->select_line_max =
1283 charmapcon_find_line(cl, o, data->select_y_max);
1284 /* FIXME: More intelligent refresh */
1285 if (xmax != data->select_x_max || ymax != data->select_y_max)
1287 charmapcon_refresh_lines(cl, o, MIN(ymax,
1288 MIN(data->select_y_min, data->select_y_max)), MAX(ymax,
1289 MAX(data->select_y_min, data->select_y_max)));
1292 else
1294 /* FIXME: Outside the console area, we need to scroll the window
1295 and just update the selection based on that */
1300 static VOID charmapcon_handlegadgets(Class *cl, Object *o,
1301 struct P_Console_HandleGadgets *msg)
1303 struct InputEvent *e = msg->Event;
1305 if (e->ie_Class == IECLASS_RAWMOUSE)
1307 charmapcon_handlemouse(cl, o, msg);
1308 return;
1311 struct charmapcondata *data = INST_DATA(cl, o);
1313 if (e->ie_Class == IECLASS_GADGETUP)
1315 data->activeGad = 0;
1316 return;
1319 if (e->ie_Class == IECLASS_GADGETDOWN)
1321 /* We pass 0 from consoletask if the mouse wheel is being used */
1322 if ((IPTR) e->ie_EventAddress == 1)
1323 data->activeGad = (APTR) &(data->prop->up);
1324 else if ((IPTR) e->ie_EventAddress == 2)
1325 data->activeGad = (APTR) &(data->prop->down);
1326 else
1327 data->activeGad = e->ie_EventAddress;
1330 if (data->activeGad == (APTR) &(data->prop->scroller))
1332 ULONG hidden =
1333 data->scrollback_size >
1334 CHAR_YMAX(o) ? data->scrollback_size - CHAR_YMAX(o) - 1 : 0;
1335 ULONG pos =
1336 (((struct PropInfo *)((struct Gadget *)&(data->prop->
1337 scroller))->SpecialInfo)->VertPot * hidden +
1338 (MAXPOT / 2)) / MAXPOT;
1340 if (pos != data->scrollback_pos)
1341 charmapcon_scroll_to(cl, o, pos);
1343 else if (data->activeGad == (APTR) &(data->prop->down))
1345 if (data->scrollback_pos + CHAR_YMAX(o) < data->scrollback_size - 1)
1347 charmap_scroll_up(cl, o, 1);
1348 charmapcon_refresh(cl, o, 0);
1349 charmapcon_adj_prop(cl, o);
1352 else if (data->activeGad == (APTR) &(data->prop->up))
1354 if (data->top_of_window != data->top_of_scrollback)
1356 charmap_scroll_down(cl, o, 1);
1357 charmapcon_refresh(cl, o, 0);
1358 charmapcon_adj_prop(cl, o);
1365 AROS_UFH3S(IPTR, dispatch_charmapconclass,
1366 AROS_UFHA(Class *, cl, A0),
1367 AROS_UFHA(Object *, o, A2), AROS_UFHA(Msg, msg, A1))
1369 AROS_USERFUNC_INIT
1371 IPTR retval = 0UL;
1373 switch (msg->MethodID)
1375 case OM_NEW:
1376 retval = (IPTR) charmapcon_new(cl, o, (struct opSet *)msg);
1377 break;
1379 case OM_DISPOSE:
1380 charmapcon_dispose(cl, o, msg);
1381 break;
1383 case M_Console_DoCommand:
1384 charmapcon_docommand(cl, o, (struct P_Console_DoCommand *)msg);
1385 break;
1387 case M_Console_ClearCell:
1388 // FIXME: scroll down to end here if it's not there already.
1389 charmapcon_clearcell(cl, o, (struct P_Console_ClearCell *)msg);
1390 break;
1392 case M_Console_NewWindowSize:
1393 charmapcon_newwindowsize(cl, o,
1394 (struct P_Console_NewWindowSize *)msg);
1395 break;
1397 case M_Console_HandleGadgets:
1398 //D(bug("CharMapCon::HandleGadgets\n"));
1399 charmapcon_handlegadgets(cl, o,
1400 (struct P_Console_HandleGadgets *)msg);
1401 break;
1403 /* FIXME: Belongs in snimapcon - here temporarily until refactored out
1404 selection code */
1405 case M_Console_Copy:
1406 charmapcon_copy(cl, o, msg);
1407 break;
1409 default:
1410 retval = DoSuperMethodA(cl, o, msg);
1411 break;
1414 return retval;
1416 AROS_USERFUNC_EXIT
1419 #undef ConsoleDevice
1421 Class *makeCharMapConClass(struct ConsoleBase *ConsoleDevice)
1424 Class *cl;
1426 cl = MakeClass(NULL, NULL, STDCONCLASSPTR,
1427 sizeof(struct charmapcondata), 0UL);
1428 if (cl)
1430 cl->cl_Dispatcher.h_Entry = (APTR) dispatch_charmapconclass;
1431 cl->cl_Dispatcher.h_SubEntry = NULL;
1433 cl->cl_UserData = (IPTR) ConsoleDevice;
1435 return (cl);
1437 return (NULL);