/* Auto: make */ IMPORT struct SnapRsrc *SnapRsrc; #define COPY 0xC0L #define INVCOPY 0x30L #define CopyChar(_x, _y, _m) \ BltBitMap(&MyBM, (LONG)_x, (LONG)_y, \ &TempBM, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, \ _m, -1L, NULL); \ WaitBlit() WORD Unit; WORD Pattern[5] = { 0, 0x0c3f, /* Frame ....oo....oooooo */ 0x3333, /* Char ..oo..oo..oo..oo */ 0x1f1f, /* Word ...ooooo...ooooo */ 0xffff /* Line oooooooooooooooo */ }; IMPORT LONG xl; /* leftmost x position */ IMPORT LONG xr; /* rightmost x position */ IMPORT LONG yt; /* topmost y position */ IMPORT LONG yb; /* bottommost y position */ LONG minx; /* left limit */ LONG maxx; /* right limit */ LONG maxy; /* bottom limit */ LONG tl, tr; /* used by findword - left and right edge of word */ WORD fw, fh; /* Font width and height used when drawing the frame */ WORD GZZ; WORD SBM; IMPORT LONG mx, my; /* Mouse position in character steps */ #define closetop 0 #define closebottom 1 WORD closey; #define closeleft 0 #define closeright 1 WORD closex; struct Window *window; /* The window we're snapping from */ /* Data for font being snapped */ UWORD FontHeight; UWORD FontWidth; UWORD Underscore; UBYTE FontType; UBYTE LoChar; UBYTE HiChar; UWORD Modulo; UWORD *CharLoc; UWORD NoOfChars; UBYTE *SrcData; IMPORT UBYTE *CharData; UBYTE IFlags; IMPORT struct RastPort TempRp, MyRP; IMPORT struct BitMap TempBM, MyBM; IMPORT UWORD *TempRaster; /* Used for character recognition */ IMPORT struct Screen *theScreen; IMPORT struct RastPort rp; struct Layer *LockedLayer; IMPORT LONGBITS cancelsignal, donesignal, movesignal, clicksignal, timersignal; IMPORT WORD action; WORD starting; /* Init vars with font data. */ VOID SetSnapFont(font) struct TextFont *font; { if (!font) { FontWidth = -1; return; } FontHeight = font->tf_YSize; Underscore = font->tf_Baseline + 1; FontType = font->tf_Flags; FontWidth = (FontType & FPF_PROPORTIONAL ? -1 : font->tf_XSize); if (FontWidth == -1) { return; } LoChar = font->tf_LoChar; HiChar = font->tf_HiChar; Modulo = font->tf_Modulo; CharLoc = (UWORD *)font->tf_CharLoc; NoOfChars = HiChar - LoChar + 1; Modulo = font->tf_Modulo; SrcData = (UBYTE *)font->tf_CharData; BltClear(CharData, 256L * 32, 0L); WaitBlit(); CopyFont(); } /* Check if the character at x, y is a space */ WORD IsSpace(x, y) LONG x, y; { REGISTER WORD i = FontHeight - 1; REGISTER UWORD *data = &TempRaster[i]; /* Copy character at x, y */ BltClear((char *)TempRaster, 32L, 0L); ClipBlit(&rp, x, y, &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY); WaitBlit(); if (*data) { /* Try inverted copy */ ClipBlit(&rp, x, y, &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, INVCOPY); WaitBlit(); } while (i--) { if (*data--) { return 0; } } return 1; } #define ShortFrame 4L /* Square frame - columnar select */ #define LongFrame 8L /* Strange frame - char or word select */ IMPORT LONG OFType; /* Old frame type: ShortFrame/LongFrame */ IMPORT UWORD Ptrn; IMPORT Point OldFrame[]; IMPORT Point NewFrame[]; /* update_frame calculates the new frame, ** erases the old frame and draws the new one. ** It's all pretty obvious if you take it slowly. */ VOID update_frame() { LONG ft; switch (Unit) { case UNIT_FRAME: { /*********\ * * * * \*********/ NewFrame[0].x = xl - 1; NewFrame[0].y = yt - 1; NewFrame[1].x = xr + fw; NewFrame[1].y = yt - 1; NewFrame[2].x = xr + fw; NewFrame[2].y = yb + fh; NewFrame[3].x = xl - 1; NewFrame[3].y = yb + fh; NewFrame[4].x = xl - 1; NewFrame[4].y = yt - 1; ft = ShortFrame; break; } case UNIT_CHAR: case UNIT_WORD: { if (yt == yb) { /* On the same line - same as UNIT_FRAME */ NewFrame[0].x = xl - 1; NewFrame[0].y = yt - 1; NewFrame[1].x = xr + fw; NewFrame[1].y = yt - 1; NewFrame[2].x = xr + fw; NewFrame[2].y = yb + fh; NewFrame[3].x = xl - 1; NewFrame[3].y = yb + fh; NewFrame[4].x = xl - 1; NewFrame[4].y = yt - 1; ft = ShortFrame; } else { /*****\ ****** * * * * ***** *******/ NewFrame[0].x = xl - 1; NewFrame[0].y = yt - 1; NewFrame[1].x = maxx + fw; NewFrame[1].y = yt - 1; NewFrame[2].x = maxx + fw; NewFrame[2].y = yb; NewFrame[3].x = xr + fw; NewFrame[3].y = yb; NewFrame[4].x = xr + fw; NewFrame[4].y = yb + fh; NewFrame[5].x = minx - 1; NewFrame[5].y = yb + fh; NewFrame[6].x = minx - 1; NewFrame[6].y = yt + fh; NewFrame[7].x = xl - 1; NewFrame[7].y = yt + fh; NewFrame[8].x = xl - 1; NewFrame[8].y = yt - 1; ft = LongFrame; } break; } case UNIT_LINE: { NewFrame[0].x = minx - 1; NewFrame[0].y = yt - 1; NewFrame[1].x = maxx + fw; NewFrame[1].y = yt - 1; NewFrame[2].x = maxx + fw; NewFrame[2].y = yb + fh; NewFrame[3].x = minx - 1; NewFrame[3].y = yb + fh; NewFrame[4].x = minx - 1; NewFrame[4].y = yt - 1; ft = ShortFrame; break; } default: { break; } } draw_frame(ft); } VOID FindWord() { /* Must remove frame to be able to search for spaces */ WaitTOF(); erase_frame(); tl = mx; /* Find a space to the left... */ while (!IsSpace(tl, my)) { tl -= fw; if (tl < minx) { break; } } tl += fw; tr = mx; /* ...and to the right */ while (!IsSpace(tr, my)) { tr += fw; if (tr + fw > maxx) { break; } } tr -= fw; if (tr < tl) { tl = xl; tr = xr; } } /* ChangeUnit cycles the unit of selection. The differents units are: character, word and line. */ VOID ChangeUnit() { switch (Unit) { case UNIT_FRAME: { Unit = UNIT_CHAR; break; } case UNIT_CHAR: { Unit = UNIT_WORD; FindWord(); xl = tl; xr = tr; break; } case UNIT_WORD: { Unit = UNIT_LINE; xl = minx; xr = maxx; break; } case UNIT_LINE: { Unit = UNIT_FRAME; xl = xr = mx; break; } } if (SnapRsrc->CrawlPtrn == 0) { Ptrn = Pattern[Unit]; } } /* ExtendSelection extends the current selection according to the mouse position and the selected unit. Note that ExtendSelection doesn't optimize moves that don't make any difference. FIXME */ VOID ExtendSelection() { /* Fix which row we're talking about */ if (closey == closetop) { /* Find closest row */ yt = my; /* change top row */ } else { yb = my; /* change bottom row */ } /* Take care of left and right character pos */ switch (Unit) { case UNIT_FRAME: { if (closex == closeleft) { xl = mx; } else { xr = mx; } break; } case UNIT_CHAR: { if (yt == yb) { /* One line */ if (closex == closeleft) { xl = mx; } else { xr = mx; } } else { /* Multiple lines */ if (yt == my) { xl = mx; /* At top - set left */ } else { xr = mx; /* At bottom - set right */ } } break; } case UNIT_WORD: { FindWord(); /* Find the word */ if (yt == yb) { /* One line */ if (closex == closeleft) { /* Find closest char pos */ xl = tl; } else { xr = tr; } } else { /* Multiple lines */ if (yt == my) { /* Where am I */ xl = tl; /* At top - set left */ } else { xr = tr; /* At bottom - set right */ } } break; } case UNIT_LINE: { /* Always full width */ break; } } if (yt - fh == yb) { yb += fh; } if (yt == yb && xl - fw == xr) { xr += fw; } if (xr > maxx) { /* Check for window bounds */ xr = maxx; } if (xl < minx) { /* Check for window bounds */ xl = minx; } if (yb > maxy) { /* Check for window bounds */ yb = maxy; } } /* The actual character snapper. It actually works. :-) */ WORD SnapChars() { LONG width; LONG height; UBYTE *SnapSpace; ULONG SnapSize; ULONG counter; REGISTER LONG x, y; /* Check coordinates */ if (yt - fh == yb) { /* No rows, shouldn't happen */ return 0; } if (yt == yb && xl - fw == xr) { /* Nothing at all */ return 0; } /* Calculate stuff */ width = maxx - (minx + 1) + fw + fw; /* Add one for a LF */ height = yb - yt + fh; SnapSize = ((width / fw) + 1) * (height / fh); counter = 0; /* Initialize things */ InitRastPort(&MyRP); InitBitMap(&MyBM, 1L, width, height); MyRP.BitMap = &MyBM; SnapSpace = AllocMem(SnapSize, MEMF_PUBLIC|MEMF_CLEAR); /* Please insert more memory */ if (!SnapSpace) { return 0; } MyBM.Planes[0] = AllocRaster(width, height); if (!MyBM.Planes[0]) { FreeMem(SnapSpace, SnapSize); return 0; } IFlags = 0; /* Make a local copy of the snapped chars */ ClipBlit(&rp, minx, yt, &MyRP, 0L, 0L, width, height, COPY); /* Ok, now we've got a copy of the character data */ /* Now it's ok to mess with the layers again */ UnlockLayer(LockedLayer); /* Clear our work area */ BltClear((char *)TempRaster, 32L, 0L); /* Calculate bounds */ xl -= minx; xr -= minx; maxx -= minx; minx = 0; yb -= yt; yt = 0; /* Single line - needs to be handled separately */ if (yt == yb) { /* Ok, we've got one */ /* Read from left to right */ for (x = xl; x <= xr; x += fw, counter++) { CopyChar(x, yt, COPY); if ((SnapSpace[counter] = interpret(TempRaster)) == 255) { SnapSpace[counter] = SnapRsrc->BadChar; /* Unrecognized */ } } if (Unit == UNIT_LINE) { while (counter && SnapSpace[counter-1] == ' ') { counter--; } } } else { /* Multiple lines */ if (Unit == UNIT_FRAME) { minx = xl; maxx = xr; } /* Read first line */ for (x = xl; x <= maxx; x += fw, counter++) { CopyChar(x, yt, COPY); if ((SnapSpace[counter] = interpret(TempRaster)) == 255) { SnapSpace[counter] = SnapRsrc->BadChar; /* Unrecognized */ } } if (Unit == UNIT_FRAME) { SnapSpace[counter++] = 10; } else { SHORT endspace = (SnapSpace[counter-1] == ' '); /* Remove trailing blanks */ while (counter && SnapSpace[counter-1] == ' ') { counter--; } if (endspace || !(SnapRsrc->flags & JOINLONG)) { SnapSpace[counter++] = 10; } } /* If more than two rows - read full middle rows */ if (yt + fh != yb) { for (y = yt + fh; y < yb; y += fh) { for (x = minx; x <= maxx; x += fw, counter++) { CopyChar(x, y, COPY); if ((SnapSpace[counter] = interpret(TempRaster)) == 255) { SnapSpace[counter] = SnapRsrc->BadChar; /* Unrecognized */ } } if (Unit == UNIT_FRAME) { SnapSpace[counter++] = 10; } else { SHORT endspace = (SnapSpace[counter-1] == ' '); /* Remove trailing blanks */ while (counter && SnapSpace[counter-1] == ' ') { counter--; } if (endspace || !(SnapRsrc->flags & JOINLONG)) { SnapSpace[counter++] = 10; } } } } /* Read last line */ for (x = minx; x <= xr; x += fw, counter++) { CopyChar(x, yb, COPY); if ((SnapSpace[counter] = interpret(TempRaster)) == 255) { SnapSpace[counter] = SnapRsrc->BadChar; /* Unrecognized */ } } /* Remove trailing blanks */ while (counter && SnapSpace[counter-1] == ' ') { counter--; } } FreeRaster(MyBM.Planes[0], width, height); SaveClip(SnapSpace, counter); FreeMem(SnapSpace, SnapSize); return 1; } /* HandleChars is the part of the Snap state machine that handles snapping of characters. The selection is done in different units: char, word, line. */ WORD HandleChars() { LONG xoff, yoff; LONG ox, oy; /* Find out which screen we're working on */ theScreen = WhichScreen(); /* Oops, no screen? */ if (!theScreen) { action = noaction; return 0; } /* Ok, what window? */ window = WhichWindow(theScreen); /* Oh dear, no window. */ if (!window) { action = noaction; return 0; } /* No messing with the layers while I think */ LockedLayer = window->WLayer; LockLayer(0L, LockedLayer); /* Don't want to wreck somebody's rastport */ CopyMem((char *)window->RPort, (char *)&rp, (LONG)sizeof(struct RastPort)); /* Or his picture */ SetDrMd(&rp, COMPLEMENT); rp.Mask = SnapRsrc->FrameMask; /* Find out what we're trying to read */ SetSnapFont(rp.Font); if (FontWidth == -1) { UnlockLayer(LockedLayer); action = noaction; return 0; } if (window->Flags & GIMMEZEROZERO) { GZZ = 1; } else { GZZ = 0; } if (window->Flags & SUPER_BITMAP) { SBM = 1; } else { SBM = 0; } /* Find a position */ xl = (GZZ ? window->GZZMouseX : window->MouseX) + window->RPort->Layer->Scroll_X; yt = (GZZ ? window->GZZMouseY : window->MouseY) + window->RPort->Layer->Scroll_Y; if (xl < 0) { xl = 0; } if (yt < 0) { yt = 0; } /* Check your position */ if (xl > (GZZ ? window->GZZWidth : window->Width) || yt > (GZZ ? window->GZZHeight : window->Height)) { UnlockLayer(LockedLayer); action = noaction; return 0; } IFlags = 0; /* Find out the offset for the clicked character, if any. ** This is the part that makes it special. Simple, isn't it. Hah! */ { REGISTER struct CacheWindow *cw = GetCachedWindow(theScreen, window); if (cw) { xoff = - ((xl - cw->xoff) % cw->fw); yoff = - ((yt - cw->yoff) % cw->fh); BltClear((char *)TempRaster, 32L, 0L); ClipBlit(&rp, xl + xoff, yt + yoff, &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY); WaitBlit(); if (interpret(TempRaster) != 255) { goto found; } } /* No cache or cache didn't match */ xl -= 7; yt -= 7; BltClear((char *)TempRaster, 32L, 0L); xoff = 0; while (xoff < (16 - FontWidth)) { ClipBlit(&rp, xl + xoff, yt, &TempRp, 0L, 0L, (LONG)FontWidth, 16L, COPY); WaitBlit(); yoff = 0; while (yoff < (16 - FontHeight)) { if (interpret(&TempRaster[yoff]) != 255) { goto found; } ++yoff; } ++xoff; } /* No character found. Back off */ UnlockLayer(LockedLayer); action = noaction; return 0; found: /* Ok, now we know where to look for chars. ** xoff and yoff is character position within our 16x16 bitmap. */ xl = xl + xoff; /* Adjust x */ yt = yt + yoff; /* Adjust y */ fw = FontWidth; fh = FontHeight; { SHORT temp = fh; while (temp <= fh + 1) { /* Check for extra pixel row */ BltClear((char *)TempRaster, 32L, 0L); ClipBlit(&rp, xl, yt + temp, &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY); WaitBlit(); if (interpret(TempRaster) != 255) { fh = temp; break; } ++temp; } } /* Find out offsets within the window */ xoff = xl % fw; yoff = yt % fh; if (cw) { cw->xoff = xoff; cw->yoff = yoff; cw->fw = fw; cw->fh = fh; } else { CacheWindow(window, xoff, yoff, fw, fh); } } /* Set bounds */ minx = xoff; maxx = minx + (((GZZ ? window->GZZWidth : window->Width - window->BorderRight /* Hack for borderless conman windows */ + (window->Flags & BORDERLESS && window->Flags & WINDOWSIZING ? 14 : 0)) - minx - fw) / fw) * fw; maxy = ((GZZ ? window->GZZHeight : window->Height) / fh) * fh; /* Check bounds */ if (xl > maxx) { UnlockLayer(LockedLayer); action = noaction; return 0; } /* Set box dimensions */ xr = xl; yb = yt; ox = xr; oy = yt; /* Select unit while starting */ starting = 1; /* Starting unit is character or frame */ Unit = SnapRsrc->StartUnit; Ptrn = (SnapRsrc->CrawlPtrn ? SnapRsrc->CrawlPtrn : Pattern[Unit]); OFType = 0L; update_frame(); /* Get the state machine running */ FOREVER { /* Wait for something to happen */ REGISTER LONGBITS sig = Wait(movesignal|cancelsignal|donesignal|clicksignal|timersignal); if ((sig & timersignal) && (SnapRsrc->CrawlPtrn != 0xffff)) { crawl_frame(0L); } mx = (LONG)(GZZ ? window->GZZMouseX : window->MouseX) + window->RPort->Layer->Scroll_X; if (mx < 0) { mx = 0; } /* Calculate which edge is closest */ if ((mx - xl) < (xr - mx)) { closex = closeleft; } else { closex = closeright; } /* Only interested in real char pos */ mx = mx - ((mx - xoff) % fw); my = (LONG)(GZZ ? window->GZZMouseY : window->MouseY) + window->RPort->Layer->Scroll_Y; if (my < 0) { my = 0; } /* Calculate which row is closest */ if ((my - yt) < (yb - my)) { closey = closetop; } else { closey = closebottom; } my = my - ((my - yoff) % fh); /* Hey, it moves! It's alive!! */ if ((sig & movesignal) && (action == snaptext)) { if (mx != ox || my != oy) { /* Something's happened */ ExtendSelection(); update_frame(); starting = 0; ox = mx; oy = my; sig &= ~clicksignal; } } /* Ok, forget it... */ if (sig & cancelsignal) { erase_frame(); UnlockLayer(LockedLayer); return 0; } /* Click */ if ((sig & clicksignal) && (action == snaptext)) { /* Selecting unit */ if (starting) { if (mx == ox && my == oy) { ChangeUnit(); if (Unit == UNIT_CHAR) { ChangeUnit(); } update_frame(); } else if (Unit == UNIT_FRAME) { ChangeUnit(); update_frame(); } } if (mx != ox || my != oy) { /* Click in a new place */ ExtendSelection(); update_frame(); starting = 0; ox = mx; oy = my; } } /* Finished */ if (sig & donesignal) { erase_frame(); return SnapChars(); } } }