diskimage: Compiler delint
[AROS.git] / rom / devs / console / charmapconclass.c
blob9a9dd149056baaf6c76a748aa90da462b84068a0
1 /*
2 Copyright © 1995-2012, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Code for CONU_CHARMAP console units.
6 Lang: english
7 */
8 #include <string.h>
10 #include <exec/ports.h>
11 #include <proto/graphics.h>
12 #include <proto/intuition.h>
13 #include <proto/alib.h>
14 #include <intuition/intuition.h>
16 #include <intuition/imageclass.h>
17 #include <intuition/gadgetclass.h>
18 #include <intuition/sghooks.h>
19 #include <libraries/gadtools.h>
21 #include <graphics/rastport.h>
22 #include <aros/asmcall.h>
24 #include <stdlib.h>
26 #define SDEBUG 0
27 //#define DEBUG 1
28 #define DEBUG 0
29 #include <aros/debug.h>
31 #include "console_gcc.h"
32 #include "consoleif.h"
34 #include "charmap.h"
36 #define PROP_FLAGS \
37 AUTOKNOB | FREEVERT | PROPNEWLOOK | PROPBORDERLESS
39 #define CODE_COPY 'C'
40 #define CODE_PASTE 'V'
42 struct MyEditHookMsg
44 struct Message msg;
45 struct SGWork *sgw;
46 WORD code;
49 static const STRPTR CONCLIP_PORTNAME = "ConClip.rendezvous";
50 struct Scroll
52 struct Gadget scroller; /* proportionnal gadget */
53 struct Gadget down; /* down gadget */
54 struct Gadget up; /* up gadget */
55 struct PropInfo pinfo; /* PropInfo for scroller */
56 struct Image simage; /* image for scroller */
57 struct Image * upimage; /* Boopsi image for up arrow */
58 struct Image * downimage; /* ditto for down arrow */
61 // FIXME: Abstract out the non-GUI aspects
62 struct charmapcondata
64 /* Start of the scrollback buffer */
65 struct charmap_line * top_of_scrollback;
66 /* The line currently displayed at the top of the screen */
67 struct charmap_line * top_of_window;
68 /* Saved position for the top of the screen at the end of
69 the buffer; where the buffer is reset to if there is
70 output while scrolling */
71 struct charmap_line * saved_top_of_window;
72 ULONG saved_scrollback_pos;
74 ULONG scrollback_size; /* Total size of the scrollback buffer */
75 ULONG scrollback_pos; /* Position of the top of the window */
77 ULONG scrollback_max; /* Maximum number of lines in scrollback
78 buffer on top of CHAR_YMAX(o) */
80 BOOL unrendered; /* Unrendered cursor while scrolled back? */
82 /* FIXME: Belongs in snipmap class */
83 /* Current selection */
84 LONG select_x_min;
85 LONG select_y_min;
86 struct charmap_line * select_line_min;
87 LONG select_x_max;
88 LONG select_y_max;
89 struct charmap_line * select_line_max;
90 BOOL active_selection; /* If true, mouse move will affect the selection */
91 BOOL ignore_drag;
94 UBYTE boopsigad; /* Type of right prop gadget of window */
95 struct Scroll * prop;
97 /* Active gadget */
98 APTR activeGad;
100 /* Libraries */
101 struct Library *ccd_GfxBase;
105 /* Template used to quickly fill constant fields */
106 CONST struct Scroll ScrollBar = {
107 { /* PropGadget */
108 NULL, 0,0, 0,0,
109 GFLG_RELRIGHT|GFLG_RELHEIGHT,
110 GACT_RIGHTBORDER|GACT_FOLLOWMOUSE|GACT_IMMEDIATE|GACT_RELVERIFY,
111 GTYP_PROPGADGET,
112 NULL, NULL, NULL, 0, NULL,
113 0, NULL },
114 { /* Up-gadget */
115 NULL, 0,0, 0,0,
116 GFLG_RELRIGHT|GFLG_RELBOTTOM|GFLG_GADGHIMAGE|GFLG_GADGIMAGE,
117 GACT_RIGHTBORDER|GACT_RELVERIFY|GACT_IMMEDIATE,
118 GTYP_BOOLGADGET,
119 NULL, NULL, NULL, 0, NULL,
120 1, NULL },
121 { /* Down-Gadget */
122 NULL, 0,0, 0,0,
123 GFLG_RELRIGHT|GFLG_RELBOTTOM|GFLG_GADGHIMAGE|GFLG_GADGIMAGE,
124 GACT_RIGHTBORDER|GACT_RELVERIFY|GACT_IMMEDIATE,
125 GTYP_BOOLGADGET,
126 NULL, NULL, NULL, 0, NULL,
127 2, NULL },
128 { /* PropInfo */
129 PROP_FLAGS,
130 MAXPOT, MAXPOT,
131 MAXBODY, MAXBODY }
132 /* Other values may be NULL */
136 #undef ConsoleDevice
137 #define ConsoleDevice ((struct ConsoleBase *)cl->cl_UserData)
139 #undef GfxBase
140 #define GfxBase (((struct charmapcondata *)INST_DATA(cl, o))->ccd_GfxBase)
142 static VOID charmapcon_refresh(Class *cl, Object * o, LONG off);
144 /*** Allocate and attach a prop gadget to the window ***/
145 static VOID charmapcon_add_prop(Class * cl, Object * o)
147 struct charmapcondata *data= INST_DATA(cl, o);
148 struct Scroll * pg;
149 struct Image *dummy;
150 struct Window *win = CU(o)->cu_Window;
152 UWORD height, size_width, size_height;
154 /* If the window is a backdrop'ed one, use a simplified BOOPSI propgadget **
155 ** because the next propgadget aspect depends on window activated state */
156 if (win->Flags & WFLG_BACKDROP)
158 /* Yes this is actually a (struct Gadget *)... */
159 if ((data->prop = pg = (struct Scroll *)NewObject(NULL, "propgclass",
160 GA_Top, 0,
161 GA_Left, win->Width - 10,
162 GA_Width, 10,
163 GA_Height, win->Height,
164 GA_RelVerify, TRUE,
165 GA_FollowMouse, TRUE,
166 GA_Immediate, TRUE,
167 PGA_VertPot, MAXPOT,
168 PGA_VertBody, MAXBODY,
169 PGA_Freedom, FREEVERT,
170 PGA_NewLook, TRUE,
171 TAG_END)))
173 /* And finally, add it to the window */
174 AddGList(win, (struct Gadget *)pg, 0, 1, NULL);
175 RefreshGList((struct Gadget *)pg, win, NULL, 1);
178 data->boopsigad = TRUE;
179 return;
181 data->boopsigad = FALSE;
183 /* Get memory */
184 if( ( data->prop = pg = (void *) AllocMem(sizeof(*pg), MEMF_PUBLIC) ) )
186 /* Copy default flags/modes/etc. */
187 CopyMem(&ScrollBar, pg, sizeof(*pg));
188 pg->pinfo.Flags = PROP_FLAGS;
189 struct DrawInfo *di;
191 di = (void *) GetScreenDrawInfo(win->WScreen);
193 /* We need to get size-gadget height, to adjust properly arrows */
194 if((dummy = (struct Image *) NewObject(NULL, "sysiclass",
195 SYSIA_Which, SIZEIMAGE,
196 SYSIA_DrawInfo, (IPTR)di,
197 TAG_END) ))
199 size_width = dummy->Width; /* width of up/down-gadgets */
200 size_height = dummy->Height; /* bottom offset */
202 /* We don't need the image anymore */
203 DisposeObject(dummy);
205 /* Get the boopsi image of the up and down arrow */
206 if((pg->upimage = (struct Image *) NewObject(NULL, "sysiclass",
207 SYSIA_Which, UPIMAGE,
208 SYSIA_DrawInfo, (IPTR)di,
209 TAG_END) ))
211 pg->up.GadgetRender = pg->up.SelectRender = (APTR)pg->upimage;
212 height = pg->upimage->Height;
214 if((pg->downimage = (struct Image *) NewObject(NULL, "sysiclass",
215 SYSIA_Which, DOWNIMAGE,
216 SYSIA_DrawInfo, (IPTR)di,
217 TAG_END) ))
219 struct Gadget *G = (void *)pg;
220 WORD hoffset = size_width / 4;
222 pg->down.GadgetRender = pg->down.SelectRender = (APTR)pg->downimage;
224 /* Release drawinfo */
225 FreeScreenDrawInfo (win->WScreen, di);
227 /* Now init all sizes/positions relative to window's borders */
228 G->Height = -(win->BorderTop + size_height + 2*height + 2);
229 G->TopEdge = win->BorderTop + 1;
230 G->Width = size_width - hoffset * 2 + 2;
231 G->LeftEdge = -(size_width - hoffset); G++;
232 pg->up.LeftEdge=
233 G->LeftEdge = -(size_width - 1);
234 G->Width = pg->up.Width = size_width;
235 G->Height = pg->up.Height = height;
236 G->TopEdge = -(size_height + height - 1);
237 pg->up.TopEdge = G->TopEdge - height;
239 /* Other fields */
240 pg->scroller.GadgetRender = (APTR)&pg->simage;
241 pg->scroller.SpecialInfo = (APTR)&pg->pinfo;
243 /* Link gadgets */
244 pg->scroller.NextGadget = &pg->up;
245 pg->up.NextGadget = &pg->down;
247 /* And finally, add them to the window */
248 AddGList(win, &pg->scroller, 0, 3, NULL);
249 RefreshGList(&pg->scroller, win, NULL, 3);
251 return;
253 DisposeObject(pg->upimage);
256 FreeMem(pg, sizeof(*pg));
257 FreeScreenDrawInfo(win->WScreen, di);
259 return;
262 static VOID charmapcon_adj_prop(Class *cl, Object *o)
264 struct charmapcondata *data = INST_DATA(cl, o);
265 struct Window *w = CU(o)->cu_Window;
266 ULONG VertBody, VertPot;
268 ULONG hidden = data->scrollback_size > CHAR_YMAX(o) ? data->scrollback_size - CHAR_YMAX(o) - 1 : 0;
270 if (hidden > 0) {
271 VertPot = (data->scrollback_pos) * MAXPOT / hidden;
272 VertBody = CHAR_YMAX(o) * MAXBODY / data->scrollback_size;
273 } else {
274 VertPot = 0;
275 VertBody = MAXBODY;
278 if (VertPot > MAXPOT) {
279 VertPot = MAXPOT;
280 D(bug("VERTPOT SET TOO HIGH. Adjusted\n"));
283 NewModifyProp((struct Gadget *)&(data->prop->scroller),w,NULL,((struct PropInfo *)data->prop->scroller.SpecialInfo)->Flags,MAXPOT,VertPot,MAXBODY,VertBody,1);
286 /*** Free resources allocated for scroller ***/
287 void charmapcon_free_prop(Class * cl, Object * o)
289 struct charmapcondata *data= INST_DATA(cl, o);
290 struct Window * win = CU(o)->cu_Window;
292 if( data->prop )
294 if (win)
296 if (data->boopsigad) RemoveGadget(win,(struct Gadget *)data->prop);
297 else RemoveGList(win,&data->prop->scroller, 3);
299 if( data->boopsigad ) DisposeObject( data->prop );
300 else
302 /* Free elements */
303 DisposeObject(data->prop->upimage);
304 DisposeObject(data->prop->downimage);
306 /* Free struct */
307 FreeMem(data->prop, sizeof(* data->prop));
314 /*********** CharMapCon::New() **********************/
316 static Object *charmapcon_new(Class *cl, Object *o, struct opSet *msg)
318 EnterFunc(bug("CharMapCon::New()\n"));
319 APTR newGfxBase = TaggedOpenLibrary(TAGGEDOPEN_GRAPHICS);
320 if (newGfxBase == NULL)
321 return NULL;
323 o = (Object *)DoSuperMethodA(cl, o, (Msg)msg);
324 if (o)
326 struct charmapcondata *data = INST_DATA(cl, o);
328 /* Clear for checking inside dispose() whether stuff was allocated.
329 Basically this is bug-prevention.
331 memset(data, 0, sizeof (struct charmapcondata));
333 data->scrollback_max = 500; /* FIXME: Don't hardcode it */
334 data->ccd_GfxBase = newGfxBase;
335 charmapcon_add_prop(cl,o);
337 ReturnPtr("CharMapCon::New", Object *, o);
340 CloseLibrary(newGfxBase);
341 ReturnPtr("CharMapCon::New", Object *, NULL);
345 /*********** CharMapCon::Dispose() **************************/
347 static VOID charmapcon_dispose(Class *cl, Object *o, Msg msg)
349 struct charmapcondata *data= INST_DATA(cl, o);
351 charmap_dispose_lines(data->top_of_scrollback);
352 charmapcon_free_prop(cl,o);
354 CloseLibrary(data->ccd_GfxBase);
356 DoSuperMethodA(cl, o, msg);
359 /********* CharMapCon::DoCommand() ****************************/
361 static struct charmap_line * charmapcon_find_line(Class * cl, Object * o, ULONG ycp)
363 struct charmapcondata *data = INST_DATA(cl, o);
365 // Find the line. This is inefficient but the number of lines on screen
366 // should never be very high.
367 // FIXME: Optimizing the case of appending to the end (e.g. know what line
368 // is on the last line of the buffer).
370 struct charmap_line * line = data->top_of_window;
371 if (!line) {
372 D(bug("Initializing charmap\n"));
373 data->top_of_window = data->top_of_scrollback = line = charmap_newline(0,0);
374 data->scrollback_size = 1;
377 D(bug("Finding line %ld\n",ycp));
378 while(ycp > 0) {
379 if (!line->next) {
380 charmap_newline(0,line);
381 data->scrollback_size += 1;
383 line = line->next;
384 ycp -= 1;
387 while (data->scrollback_size > data->scrollback_max + CHAR_YMAX(o) &&
388 data->top_of_window != data->top_of_scrollback) {
389 data->scrollback_size -= 1;
390 data->scrollback_pos -= 1;
392 /* FIXME: Needs testing... */
393 if (data->select_line_max == data->select_line_min &&
394 data->select_line_min == data->top_of_scrollback)
396 /* The entire selection has scrolled out, but we keep it in case
397 it is still active
399 data->select_line_min = data->select_line_min->next;
400 data->select_line_max = data->select_line_min;
401 data->select_x_min = 0;
402 data->select_x_max = 0;
403 data->select_y_min += 1;
404 data->select_y_max += 1;
406 else if (data->top_of_scrollback == data->select_line_min)
408 data->select_line_min = data->select_line_min->next;
409 data->select_y_min += 1;
410 data->select_x_min = 0;
412 else if (data->top_of_scrollback == data->select_line_max)
414 /* "Reversed" selection */
415 data->select_line_max = data->select_line_max->next;
416 data->select_y_max += 1;
417 data->select_x_max = 0;
420 data->top_of_scrollback = charmap_dispose_line(data->top_of_scrollback);
423 return line;
426 static VOID charmap_ascii(Class * cl, Object * o, ULONG xcp, ULONG ycp, char * str, ULONG len)
428 struct charmap_line *line = charmapcon_find_line(cl,o, ycp);
429 ULONG oldsize = line->size;
431 // Ensure the line has sufficient capacity.
432 if (line->size < xcp + len) charmap_resize(line, xcp + len);
434 // .. copy the required data
435 memset(line->fgpen + xcp, CU(o)->cu_FgPen, len);
436 memset(line->bgpen + xcp, CU(o)->cu_BgPen, len);
437 memset(line->flags + xcp, CU(o)->cu_TxFlags, len);
438 memcpy(line->text + xcp, str, len);
440 // If cursor output is moved further right on the screen than
441 // the last output, we need to fill the line
442 if (oldsize < xcp) {
443 memset(line->fgpen + oldsize, CU(o)->cu_FgPen, xcp - oldsize);
444 memset(line->bgpen + oldsize, CU(o)->cu_BgPen, xcp - oldsize);
445 memset(line->flags + oldsize, CU(o)->cu_TxFlags, xcp - oldsize);
446 memset(line->text + oldsize, ' ', xcp - oldsize);
450 static VOID charmap_scroll_up(Class * cl, Object * o, ULONG y)
452 struct charmapcondata *data = INST_DATA(cl, o);
454 if (!data->top_of_window) return;
456 while(y--) {
457 if (!data->top_of_window->next) {
458 charmap_newline(0, data->top_of_window);
459 data->scrollback_size += 1;
461 data->top_of_window = data->top_of_window->next;
462 data->scrollback_pos += 1;
463 data->select_y_max -= 1;
464 data->select_y_min -= 1;
467 if (data->scrollback_size - CHAR_YMAX(o) - 1 <= data->scrollback_pos &&
468 data->unrendered) {
469 Console_RenderCursor(o);
470 data->unrendered = 0;
473 while (data->scrollback_size > data->scrollback_max + CHAR_YMAX(o) &&
474 data->top_of_window != data->top_of_scrollback) {
475 data->scrollback_size -= 1;
476 data->scrollback_pos -= 1;
477 data->top_of_scrollback = charmap_dispose_line(data->top_of_scrollback);
481 static VOID charmap_scroll_down(Class * cl, Object * o, ULONG y)
483 // FIXME: Need to adjust cursor position or reset to bottom when editing.
484 struct charmapcondata *data = INST_DATA(cl, o);
485 if (!data->unrendered) {
486 Console_UnRenderCursor(o);
487 data->unrendered = 1;
488 data->saved_scrollback_pos = data->scrollback_pos;
489 data->saved_top_of_window = data->top_of_window;
491 // FIXME: Select position.
493 if (data->top_of_window) {
494 while(y-- && data->top_of_window->prev) {
495 data->top_of_window = data->top_of_window->prev;
496 data->scrollback_pos -= 1;
497 data->select_y_max += 1;
498 data->select_y_min += 1;
503 static VOID charmapcon_scroll_to(Class * cl, Object * o, ULONG y)
505 struct charmapcondata *data = INST_DATA(cl, o);
506 struct Window *w = CU(o)->cu_Window;
507 struct RastPort *rp = w->RPort;
508 LONG off = data->scrollback_pos - y;
509 LONG old_pos = data->scrollback_pos;
511 if (off == 0) return;
513 Console_UnRenderCursor(o);
515 if (off > 0) charmap_scroll_down(cl, o, off);
516 else charmap_scroll_up(cl,o,-off);
518 /* Correct offset to account for the fact we might reach the
519 * top or bottom of the buffer:
521 off = old_pos - data->scrollback_pos;
523 /* A whole screenful? If so we have no choice but a full refresh
524 * (though we could double buffer... Not sure that's worth the
525 * memory cost)
527 if (abs(off) > CHAR_YMAX(o))
529 charmapcon_refresh(cl,o,0);
530 Console_RenderCursor(o);
531 return;
534 /* Avoid a full refresh by scrolling the rastport */
535 SetBPen( rp, CU(o)->cu_BgPen); /* Use "standard" background to reduce flicker */
536 if (off > 0)
538 ScrollRaster(rp,
540 -YRSIZE*off,
541 GFX_XMIN(o),
542 GFX_YMIN(o),
543 GFX_XMAX(o),
544 GFX_YMAX(o));
546 else
548 ScrollRaster(rp,
550 -YRSIZE*off,
551 GFX_XMIN(o),
552 GFX_YMIN(o),
553 GFX_XMAX(o),
554 GFX_YMAX(o));
558 /* Partial refresh */
559 charmapcon_refresh(cl,o,off);
561 Console_RenderCursor(o);
565 static VOID charmap_delete_char(Class * cl, Object *o, ULONG x, ULONG y)
567 struct charmap_line * line = charmapcon_find_line(cl,o, y);
569 if (!line || x >= line->size) return;
571 // FIXME: Shrink the buffer, or keep track of capacity separately.
572 if (x + 1 >= line->size) {
573 line->text[x] = 0;
574 return;
577 memmove(line->fgpen + x, line->fgpen + x + 1, 1);
578 memmove(line->bgpen + x, line->bgpen + x + 1, 1);
579 memmove(line->flags + x, line->flags + x + 1, 1);
580 memmove(line->text + x, line->text + x + 1, 1);
583 static VOID charmap_insert_char(Class * cl, Object *o, ULONG x, ULONG y)
585 struct charmap_line * line = charmapcon_find_line(cl,o, y);
587 if (x >= line->size) return;
589 /* FIXME: This is wasteful, since it copies the buffers straight over,
590 * so we have to do memmove's further down. */
591 charmap_resize(line, line->size + 1);
593 memmove(line->fgpen + x + 1, line->fgpen + x, line->size - x - 1);
594 memmove(line->bgpen + x + 1, line->bgpen + x, line->size - x - 1);
595 memmove(line->flags + x + 1, line->flags + x, line->size - x - 1);
596 memmove(line->text + x + 1, line->text + x, line->size - x - 1);
598 line->fgpen[x] = CU(o)->cu_FgPen;
599 line->bgpen[x] = CU(o)->cu_BgPen;
600 line->flags[x] = CU(o)->cu_TxFlags;
601 line->text[x] = ' ';
604 static VOID charmap_formfeed(Class *cl, Object * o)
606 struct charmapcondata *data = INST_DATA(cl, o);
607 struct charmap_line * line = data->top_of_window;
609 while (line) {
610 charmap_resize(line,0);
611 line = line->next;
615 static VOID charmapcon_docommand(Class *cl, Object *o, struct P_Console_DoCommand *msg)
618 IPTR *params = msg->Params;
619 struct charmapcondata *data = INST_DATA(cl, o);
621 EnterFunc(bug("CharMapCon::DoCommand(o=%p, cmd=%d, params=%p) x=%d, y=%d, ymax=%d\n",
622 o, msg->Command, params,XCP,YCP, CHAR_YMAX(o)));
624 // This is a bit of a hack: Set position to bottom in order to prevent output while
625 // scrolled.
627 ULONG old_scrollback_size = data->scrollback_size;
628 ULONG old_scrollback_pos = data->scrollback_pos;
630 if (data->unrendered) {
631 data->unrendered = 0;
632 data->scrollback_pos = data->saved_scrollback_pos;
633 data->select_y_min += old_scrollback_pos - data->scrollback_pos;
634 data->select_y_max += old_scrollback_pos - data->scrollback_pos;
635 data->top_of_window = data->saved_top_of_window;
636 charmapcon_refresh(cl,o,0);
637 Console_RenderCursor(o);
640 switch (msg->Command)
642 case C_ASCII:
643 charmap_ascii(cl,o, XCP, YCP, (char *)&params[0],1);
644 DoSuperMethodA(cl, o, (Msg)msg);
645 break;
647 case C_ASCII_STRING:
648 charmap_ascii(cl,o, XCP, YCP, (char *)params[0], (int)params[1]);
649 DoSuperMethodA(cl, o, (Msg)msg);
650 break;
652 case C_FORMFEED:
653 charmap_formfeed(cl,o);
654 DoSuperMethodA(cl, o, (Msg)msg);
655 break;
657 case C_DELETE_CHAR: /* FIXME: can it have params!? */
658 charmap_delete_char(cl,o, XCP,YCP);
659 DoSuperMethodA(cl, o, (Msg)msg);
660 break;
662 case C_INSERT_CHAR:
663 charmap_insert_char(cl,o, XCP,YCP);
664 DoSuperMethodA(cl, o, (Msg)msg);
665 break;
667 case C_SCROLL_UP:
669 D(bug("C_SCROLL_UP area (%d, %d) to (%d, %d), %d\n",
670 GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o), YRSIZE * params[0]));
671 charmap_scroll_up(cl,o, params[0]);
672 DoSuperMethodA(cl, o, (Msg)msg);
673 break;
676 case C_SCROLL_DOWN:
678 D(bug("C_SCROLL_DOWN area (%d, %d) to (%d, %d), %d\n",
679 GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o), YRSIZE * params[0]));
680 charmap_scroll_down(cl,o, params[0]);
681 DoSuperMethodA(cl, o, (Msg)msg);
682 break;
685 default:
686 DoSuperMethodA(cl, o, (Msg)msg);
687 break;
690 if (old_scrollback_size != data->scrollback_size ||
691 old_scrollback_pos != data->scrollback_pos)
692 charmapcon_adj_prop(cl,o);
694 ReturnVoid("CharMapCon::DoCommand");
697 /**************************
698 ** CharMapCon::ClearCell() **
699 **************************/
700 static VOID charmapcon_clearcell(Class *cl, Object *o, struct P_Console_ClearCell *msg)
702 // FIXME, insert space.
703 DoSuperMethodA(cl, o, (Msg)msg);
706 static VOID charmapcon_refresh_lines(Class *cl, Object * o, LONG fromLine, LONG toLine)
708 struct Window *w = CU(o)->cu_Window;
709 struct RastPort *rp = w->RPort;
710 struct charmapcondata *data = INST_DATA(cl, o);
712 if (fromLine < CHAR_YMIN(o)) fromLine = CHAR_YMIN(o);
713 if (toLine > CHAR_YMAX(o)) toLine = CHAR_YMAX(o);
715 D(bug("fromLine: %ld, toLine: %ld, char_ymax: %ld\n",fromLine,toLine, CHAR_YMAX(o)));
717 if (toLine < fromLine) return;
719 Console_UnRenderCursor(o);
721 D(bug("Rendering charmap\n"));
723 struct charmap_line * line = charmapcon_find_line(cl,o, fromLine);
724 ULONG y = GFX_YMIN(o)+fromLine*YRSIZE+rp->Font->tf_Baseline;
725 ULONG yc = fromLine;
726 UBYTE flags = 255;
728 LONG selectstart_x,selectstart_y,selectend_x,selectend_y;
729 if (data->select_y_min == data->select_y_max)
731 selectstart_y = selectend_y = data->select_y_min;
732 selectstart_x = MIN(data->select_x_min,data->select_x_max);
733 selectend_x = MAX(data->select_x_min,data->select_x_max);
735 else if (data->select_y_min < data->select_y_max)
737 selectstart_y = data->select_y_min;
738 selectstart_x = data->select_x_min;
739 selectend_y = data->select_y_max;
740 selectend_x = data->select_x_max;
742 else
744 selectstart_y = data->select_y_max;
745 selectstart_x = data->select_x_max;
746 selectend_y = data->select_y_min;
747 selectend_x = data->select_x_min;
750 while(line && yc <= toLine)
752 const char * str = line->text;
753 ULONG start = 0;
754 ULONG remaining_space = CHAR_XMAX(o)+1;
755 Move(rp, GFX_XMIN(o), y);
756 while (line->size > start && remaining_space > 0 && str[start])
758 /* Identify a batch of characters with the same fgpen/bgpen
759 to avoid having to move/set pens and do Text() on single
760 characters */
762 UBYTE fgpen = line->fgpen[start];
763 UBYTE bgpen = line->bgpen[start];
765 /* Is any part of this line part of a selection?
766 * If so, we bake in a state transition on "stop".
767 * This code is messy - there must be a nicer way.
769 ULONG stop = 9999999;
770 BOOL in_selection = 0;
772 if (yc > selectstart_y && yc < selectend_y)
774 in_selection = 1;
776 else if (yc == selectstart_y)
778 if (yc == selectend_y)
780 if (start >= selectstart_x &&
781 start < selectend_x)
783 in_selection = 1;
784 stop = selectend_x;
786 else if (start < selectstart_x)
788 stop = selectstart_x;
791 else
793 if (start >= selectstart_x) in_selection = 1;
794 else stop = selectstart_x;
797 else if (yc == selectend_y)
799 /* In this case, the selection *ends* on selectend_x */
800 if (start < selectend_x) {
801 in_selection = 1;
802 stop = selectend_x;
806 if (stop == start) stop += 1;
808 if (in_selection) {
809 fgpen = line->bgpen[start];
810 bgpen = line->fgpen[start];
813 ULONG len = 0;
814 while (line->size > start + len && str[start + len] &&
815 len < remaining_space &&
816 line->fgpen[start] == line->fgpen[start + len] &&
817 line->bgpen[start] == line->bgpen[start + len] &&
818 line->flags[start] == line->flags[start + len] &&
819 start + len < stop) len += 1;
821 setabpen(GfxBase, rp, line->flags[start], fgpen, bgpen);
822 if ((line->flags[start] & CON_TXTFLAGS_MASK) != (flags & CON_TXTFLAGS_MASK)) {
823 SetSoftStyle(rp, line->flags[start], CON_TXTFLAGS_MASK);
825 flags = line->flags[start];
827 Text(rp,&str[start],len);
829 start += len;
830 remaining_space -= len;
833 /* Clear to EOL, without overwriting scroll bar (ClearEOL does) */
834 SetAPen( rp, CU(o)->cu_BgPen);
835 RectFill (rp,
836 GFX_X(o,start), GFX_Y(o,yc),
837 GFX_XMAX(o), GFX_Y(o,yc+1)-1);
838 y += YRSIZE;
839 yc ++;
841 /* We want to make sure we have lines covering the window once
842 there's something to scroll back, as that simplies resize handling
843 etc.
845 if (!line->next && yc <= toLine) {
846 line->next = charmap_newline(0,line);
847 data->scrollback_size += 1;
849 line = line->next;
852 if (yc < toLine)
854 SetAPen( rp, CU(o)->cu_BgPen);
855 RectFill (rp,
856 GFX_XMIN(o), GFX_Y(o,yc),
857 GFX_XMAX(o), GFX_Y(o,toLine));
860 Console_RenderCursor(o);
865 * Refresh the full console unless "off" is provided.
866 * If off is set to a positive value, it indicates the
867 * number of rows from the top we start rendering.
868 * If off is set to a negative value, it indicates the
869 * number of rows from the top we stop rendering.
870 * This is used for partial refreshes when the screen is
871 * scrolled up/down.
873 static VOID charmapcon_refresh(Class *cl, Object * o, LONG off)
875 LONG fromLine = 0;
876 LONG toLine = CHAR_YMAX(o) - CHAR_YMIN(o);
878 if (off > 0) toLine = off-1;
879 if (off < 0) fromLine = CHAR_YMAX(o)+off+1;
881 charmapcon_refresh_lines(cl,o,fromLine,toLine);
885 static ULONG charmapcon_calc_selection_size(struct charmap_line * first,
886 struct charmap_line * last,
887 ULONG minx, ULONG maxx)
889 struct charmap_line * cur = first;
890 ULONG size = 0;
891 while (cur) {
892 if (cur->text) {
893 if (cur == first) {
894 if (cur == last) size += abs(maxx - minx);
895 else size += cur->size - minx;
896 } else if (cur == last) {
897 size += maxx;
898 } else {
899 size += cur->size;
902 if (cur != last || (cur == last && maxx == cur->size)) size += 1; /* line feed */
904 if (cur == last) break;
905 cur = cur->next;
907 return size;
910 char * charmapcon_get_selection(ULONG size,
911 struct charmap_line * first,
912 struct charmap_line * last,
913 ULONG minx,
914 ULONG maxx) {
915 char * buf = AllocMem(size, MEMF_ANY);
916 char * bufpos = buf;
917 struct charmap_line * cur = first;
919 if (!bufpos) return 0;
921 while(cur) {
922 if (cur->text) { /* empty lines may not have text ptrs */
923 if (cur == first) {
924 if (cur == last) {
925 CopyMem(cur->text + MIN(minx,maxx),bufpos, abs(maxx - minx));
926 bufpos += abs(maxx-minx);
927 } else {
928 CopyMem(cur->text + minx, bufpos, cur->size - minx);
929 bufpos += cur->size - minx;
931 } else if (cur == last) {
932 CopyMem(cur->text,bufpos, maxx);
933 bufpos += maxx;
934 } else {
935 CopyMem(cur->text,bufpos, cur->size);
936 bufpos += cur->size;
939 if (cur != last || (cur == last && maxx == cur->size)) /* line feed */
941 *bufpos = '\r';
942 bufpos += 1;
944 if (cur == last) break;
945 cur = cur->next;
947 return buf;
950 struct ConClipData
952 ULONG flags; /* always zero? */
953 ULONG size; /* does not include NUL termination */
954 APTR buffer; /* NUL-terminated string! */
957 /* FIXME: Belongs in snipmapcon - here temporary until refactored out
958 selection code */
959 static VOID charmapcon_copy(Class *cl, Object *o, Msg copymsg)
961 struct charmapcondata *data= INST_DATA(cl, o);
962 struct MsgPort replyport, *port;
963 struct SGWork sgw;
964 struct MyEditHookMsg msg;
965 char * buf;
967 struct charmap_line * first, *last;
968 ULONG minx,maxx,size;
970 /* Create a string from the contents of the scrollback buffer */
971 if (data->select_y_min < data->select_y_max) {
972 first = data->select_line_min;
973 last = data->select_line_max;
974 minx = data->select_x_min;
975 maxx = data->select_x_max;
976 } else {
977 first = data->select_line_max;
978 last = data->select_line_min;
979 minx = data->select_x_max;
980 maxx = data->select_x_min;
983 size = charmapcon_calc_selection_size(first,last,minx,maxx);
984 buf = charmapcon_get_selection(size,first,last,minx,maxx);
985 D(bug("%d bytes copied\n"));
987 /* If Conclip is running, we prefer using that */
988 if (IsListEmpty(&ConsoleDevice->sniphooks) && (port = FindPort(CONCLIP_PORTNAME))) {
989 /* AROS conclip format */
990 D(bug("AROS conclip\n"));
991 replyport.mp_Node.ln_Type = NT_MSGPORT;
992 replyport.mp_Node.ln_Name = NULL;
993 replyport.mp_Node.ln_Pri = 0;
994 replyport.mp_Flags = PA_SIGNAL;
995 replyport.mp_SigBit = SIGB_SINGLE;
996 replyport.mp_SigTask = FindTask(NULL);
997 NewList(&replyport.mp_MsgList);
999 msg.msg.mn_Node.ln_Type = NT_MESSAGE;
1000 msg.msg.mn_ReplyPort = &replyport;
1001 msg.msg.mn_Length = sizeof(msg);
1003 msg.code = CODE_COPY;
1004 msg.sgw = &sgw;
1006 sgw.Gadget = 0;
1007 sgw.WorkBuffer = buf;
1008 sgw.PrevBuffer = 0;
1009 sgw.IEvent = 0;
1010 sgw.Code = CODE_COPY;
1011 sgw.Actions = 0;
1012 sgw.LongInt = 0;
1013 sgw.GadgetInfo = 0;
1014 sgw.EditOp = EO_BIGCHANGE;
1015 sgw.BufferPos = 0;
1016 sgw.NumChars = size;
1018 SetSignal(0, SIGF_SINGLE);
1019 PutMsg(port, &msg.msg);
1020 WaitPort(&replyport);
1022 FreeMem(buf,size);
1023 return;
1026 ObtainSemaphore(&ConsoleDevice->copyBufferLock);
1027 FreeMem((APTR)ConsoleDevice->copyBuffer,ConsoleDevice->copyBufferSize);
1029 ConsoleDevice->copyBuffer = buf;
1030 if (ConsoleDevice->copyBuffer)
1031 ConsoleDevice->copyBufferSize = size;
1032 else
1033 ConsoleDevice->copyBufferSize = 0;
1035 if (!IsListEmpty(&ConsoleDevice->sniphooks)) {
1036 /* OS2-3.x compatible conclip format */
1037 struct Hook *conhook;
1038 struct ConClipData ccd;
1040 D(bug("AOS conclip\n"));
1041 if (ConsoleDevice->copyBufferSize) {
1042 ccd.flags = 0;
1043 ccd.size = ConsoleDevice->copyBufferSize;
1044 /* must be NUL-terminated */
1045 ccd.buffer = AllocVec(ccd.size + 1, MEMF_CLEAR);
1046 if (ccd.buffer) {
1047 CopyMem(ConsoleDevice->copyBuffer, ccd.buffer, ccd.size);
1048 ForeachNode(&ConsoleDevice->sniphooks, conhook) {
1049 D(bug("Calling AOS conclip hook %p\n", conhook));
1050 CALLHOOKPKT(conhook, NULL, &ccd);
1052 FreeVec(ccd.buffer);
1056 ReleaseSemaphore(&ConsoleDevice->copyBufferLock);
1060 /*******************************
1061 ** CharMapCon::NewWindowSize() **
1062 *******************************/
1063 static VOID charmapcon_newwindowsize(Class *cl, Object *o, struct P_Console_NewWindowSize *msg)
1065 struct charmapcondata *data = INST_DATA(cl, o);
1067 WORD old_ycp = YCP;
1069 DoSuperMethodA(cl, o, (Msg)msg);
1070 D(bug("CharMapCon::NewWindowSize(o=%p) x=%d, y=%d, ymax=%d\n",
1071 o, XCP,YCP, CHAR_YMAX(o)));
1073 // Is console empty? Unlikely, but anyway.
1074 if (!data->top_of_window) return;
1076 // Scroll up if new window size has forced the cursor up
1077 if (old_ycp > CHAR_YMAX(o)) {
1078 charmap_scroll_up(cl,o, old_ycp - CHAR_YMAX(o));
1081 charmapcon_refresh(cl,o,0);
1084 static VOID charmapcon_handlemouse(Class *cl, Object *o, struct P_Console_HandleGadgets *msg)
1086 struct charmapcondata *data = INST_DATA(cl, o);
1087 struct Window *w = CU(o)->cu_Window;
1088 struct InputEvent * e = msg->Event;
1090 /* We have the following states:
1091 * - No active selection and no mouse button => ignore
1092 * - No active selection and left mouse button => Start selection
1093 * - Active selection and no mouse button => End selection
1094 * - Active selection and left mouse button => Update selection
1097 LONG x,y;
1100 * Take window-relative mouse position.
1101 * The original code here failed when the screen was moved away from (0, 0) position.
1102 * x and y were unaware of shifted display (ie_X and ie_Y are raw physical coordinates,
1103 * and w->LeftEdge and w->TopEdge are screen-relative), and this caused lockups here.
1104 * TODO: Verify this intuition's behavior with AmigaOS3.1 using screentest program.
1105 * sonic
1107 x = e->ie_X - w->LeftEdge;
1108 y = e->ie_Y - w->TopEdge;
1110 x = w->MouseX;
1111 y = w->MouseY;
1113 /* Active selection */
1115 if (!(e->ie_Qualifier & IEQUALIFIER_LEFTBUTTON))
1117 /* End selection */
1118 data->active_selection = 0;
1119 data->ignore_drag = 0;
1120 return;
1123 if (!data->active_selection)
1125 if (e->ie_Qualifier & IEQUALIFIER_LEFTBUTTON && !data->ignore_drag)
1127 /* Inside the console area? */
1128 if (x >= GFX_XMIN(o) && x <= GFX_XMAX(o) &&
1129 y >= GFX_YMIN(o) && y <= GFX_YMAX(o))
1131 D(bug("activated selection with x: %ld, y: %ld, xmin: %ld, ymin: %Ld, xmax: %ld, ymax: %ld\n",
1132 x,y,GFX_XMIN(o), GFX_YMIN(o), GFX_XMAX(o), GFX_YMAX(o)));
1134 /* We need to clear these lines
1136 LONG old_min_y = MIN(data->select_y_min, data->select_y_max);
1137 LONG old_max_y = MAX(data->select_y_min, data->select_y_max);
1139 /* Yes, so start selection */
1140 data->active_selection = 1;
1141 data->ignore_drag = 0;
1142 data->select_x_min = (x - GFX_XMIN(o)) / XRSIZE;
1143 data->select_y_min = (y - GFX_YMIN(o)) / YRSIZE;
1144 data->select_line_min = charmapcon_find_line(cl,o,data->select_y_min);
1146 data->select_x_max = data->select_x_min;
1147 data->select_y_max = data->select_y_min;
1149 /* Clear */
1151 /* FIXME: Determine exactly which lines are affected, as
1152 follows:
1154 - If max_y has increased, render from old_max_y to new
1155 - If min_y has decreased, refresh from new min y to old
1156 - If both have changed, refresh in two batches.
1157 - If there's been a reversal of the relative positions of
1158 y_min/y_max, refresh the entire range.
1160 charmapcon_refresh_lines(cl,o,old_min_y, old_max_y);
1162 } else data->ignore_drag = 1;
1164 return;
1167 /* Update selection. */
1168 if (x >= GFX_XMIN(o) && x <= GFX_XMAX(o) &&
1169 y >= GFX_YMIN(o) && y <= GFX_YMAX(o))
1172 LONG xmax, ymax;
1173 xmax = data->select_x_max;
1174 ymax = data->select_y_max;
1175 data->select_x_max = (x - GFX_XMIN(o)) / XRSIZE;
1176 data->select_y_max = (y - GFX_YMIN(o)) / YRSIZE;
1177 data->select_line_max = charmapcon_find_line(cl,o,data->select_y_max);
1178 /* FIXME: More intelligent refresh */
1179 if (xmax != data->select_x_max ||
1180 ymax != data->select_y_max)
1182 charmapcon_refresh_lines(cl,o, MIN(ymax,MIN(data->select_y_min, data->select_y_max)), MAX(ymax,MAX(data->select_y_min,data->select_y_max)));
1185 else
1187 /* FIXME: Outside the console area, we need to scroll the window
1188 and just update the selection based on that */
1193 static VOID charmapcon_handlegadgets(Class *cl, Object *o, struct P_Console_HandleGadgets *msg)
1195 struct InputEvent * e = msg->Event;
1197 if (e->ie_Class == IECLASS_RAWMOUSE) {
1198 charmapcon_handlemouse(cl,o,msg);
1199 return;
1202 struct charmapcondata *data = INST_DATA(cl, o);
1204 if (e->ie_Class == IECLASS_GADGETUP)
1206 data->activeGad = 0;
1207 return;
1210 if (e->ie_Class == IECLASS_GADGETDOWN)
1212 /* We pass 0 from consoletask if the mouse wheel is being used */
1213 if ((IPTR)e->ie_EventAddress == 1) data->activeGad = (APTR)&(data->prop->up);
1214 else if ((IPTR)e->ie_EventAddress == 2) data->activeGad = (APTR)&(data->prop->down);
1215 else data->activeGad = e->ie_EventAddress;
1218 if (data->activeGad == (APTR)&(data->prop->scroller))
1220 ULONG hidden = data->scrollback_size > CHAR_YMAX(o) ? data->scrollback_size - CHAR_YMAX(o) -1 : 0;
1221 ULONG pos = (((struct PropInfo *)((struct Gadget*)&(data->prop->scroller))->SpecialInfo)->VertPot * hidden + (MAXPOT / 2)) / MAXPOT;
1223 if (pos != data->scrollback_pos) charmapcon_scroll_to(cl,o, pos);
1225 else if (data->activeGad == (APTR)&(data->prop->down))
1227 if (data->scrollback_pos + CHAR_YMAX(o) < data->scrollback_size - 1)
1229 charmap_scroll_up(cl,o,1);
1230 charmapcon_refresh(cl,o,0);
1231 charmapcon_adj_prop(cl,o);
1234 else if (data->activeGad == (APTR)&(data->prop->up))
1236 if (data->top_of_window != data->top_of_scrollback)
1238 charmap_scroll_down(cl,o,1);
1239 charmapcon_refresh(cl,o,0);
1240 charmapcon_adj_prop(cl,o);
1247 AROS_UFH3S(IPTR, dispatch_charmapconclass,
1248 AROS_UFHA(Class *, cl, A0),
1249 AROS_UFHA(Object *, o, A2),
1250 AROS_UFHA(Msg, msg, A1)
1253 AROS_USERFUNC_INIT
1255 IPTR retval = 0UL;
1257 switch (msg->MethodID)
1259 case OM_NEW:
1260 retval = (IPTR)charmapcon_new(cl, o, (struct opSet *)msg);
1261 break;
1263 case OM_DISPOSE:
1264 charmapcon_dispose(cl, o, msg);
1265 break;
1267 case M_Console_DoCommand:
1268 charmapcon_docommand(cl, o, (struct P_Console_DoCommand *)msg);
1269 break;
1271 case M_Console_ClearCell:
1272 // FIXME: scroll down to end here if it's not there already.
1273 charmapcon_clearcell(cl, o, (struct P_Console_ClearCell *)msg);
1274 break;
1276 case M_Console_NewWindowSize:
1277 charmapcon_newwindowsize(cl, o, (struct P_Console_NewWindowSize *)msg);
1278 break;
1280 case M_Console_HandleGadgets:
1281 //D(bug("CharMapCon::HandleGadgets\n"));
1282 charmapcon_handlegadgets(cl, o, (struct P_Console_HandleGadgets *)msg);
1283 break;
1285 /* FIXME: Belongs in snimapcon - here temporary until refactored out
1286 selection code */
1287 case M_Console_Copy:
1288 charmapcon_copy(cl,o,msg);
1289 break;
1291 default:
1292 retval = DoSuperMethodA(cl, o, msg);
1293 break;
1296 return retval;
1298 AROS_USERFUNC_EXIT
1301 #undef ConsoleDevice
1303 Class *makeCharMapConClass(struct ConsoleBase *ConsoleDevice)
1306 Class *cl;
1308 cl = MakeClass(NULL, NULL ,STDCONCLASSPTR , sizeof(struct charmapcondata), 0UL);
1309 if (cl)
1311 cl->cl_Dispatcher.h_Entry = (APTR)dispatch_charmapconclass;
1312 cl->cl_Dispatcher.h_SubEntry = NULL;
1314 cl->cl_UserData = (IPTR)ConsoleDevice;
1316 return (cl);
1318 return (NULL);