#include "snap.h" #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; IMPORT WORD StartUnit; LONG xl; /* leftmost x position */ LONG xr; /* rightmost x position */ LONG yt; /* topmost y position */ LONG yb; /* bottommost y position */ LONG minx; /* left limit */ LONG maxx; /* right limit */ LONG tl, tr; /* used by findword - left and right edge of word */ 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; UBYTE FontType; UWORD FontWidth; UBYTE LoChar; UBYTE HiChar; UWORD Modulo; UWORD *CharLoc; UWORD NoOfChars; UBYTE *SrcData; IMPORT UBYTE *CharData; IMPORT struct RastPort TempRp, MyRP; IMPORT struct BitMap TempBM, MyBM; IMPORT WORD FrameMask; IMPORT UWORD *TempRaster; /* Used for character recognition */ IMPORT struct Screen *theScreen; IMPORT struct Layer_Info *LockedLayerInfo; IMPORT struct RastPort rp; IMPORT UWORD CrawlPtrn; 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; 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, 224L*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(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+FontWidth; NewFrame[1].y = yt-1; NewFrame[2].x = xr+FontWidth; NewFrame[2].y = yb+FontHeight; NewFrame[3].x = xl-1; NewFrame[3].y = yb+FontHeight; 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+FontWidth; NewFrame[1].y = yt-1; NewFrame[2].x = xr+FontWidth; NewFrame[2].y = yb+FontHeight; NewFrame[3].x = xl-1; NewFrame[3].y = yb+FontHeight; 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+FontWidth; NewFrame[1].y = yt-1; NewFrame[2].x = maxx+FontWidth; NewFrame[2].y = yb; NewFrame[3].x = xr+FontWidth; NewFrame[3].y = yb; NewFrame[4].x = xr+FontWidth; NewFrame[4].y = yb+FontHeight; NewFrame[5].x = minx-1; NewFrame[5].y = yb+FontHeight; NewFrame[6].x = minx-1; NewFrame[6].y = yt+FontHeight; NewFrame[7].x = xl-1; NewFrame[7].y = yt+FontHeight; 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+FontWidth; NewFrame[1].y = yt-1; NewFrame[2].x = maxx+FontWidth; NewFrame[2].y = yb+FontHeight; NewFrame[3].x = minx-1; NewFrame[3].y = yb+FontHeight; 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 -= FontWidth; if (tl < minx) { break; } } tl += FontWidth; tr = mx; /* ...and to the right */ while (!IsSpace(tr, my)) { tr += FontWidth; if (tr+FontWidth > maxx) { break; } } tr -= FontWidth; 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; } } } /* 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(mx, my); /* 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-FontHeight == yb) { yb += FontHeight; } if (yt == yb && xl-FontWidth == xr) { xr += FontWidth; } if (xr > maxx) { /* Check for window bounds */ xr = maxx; } if (xl < minx) { /* Check for window bounds */ xl = minx; } } /* The actual character snapper. It actually works. */ WORD SnapChars() { REGISTER struct RastPort MyRastPort; LONG width; LONG height; UBYTE *SnapSpace; ULONG SnapSize; ULONG counter; REGISTER LONG x, y; /* Check coordinates */ if (yt-FontHeight == yb) { /* No rows, shouldn't happen */ return 0; } if (yt == yb && xl-FontWidth == xr) { /* Nothing at all */ return 0; } /* Calculate stuff */ width = maxx - (minx+1) + FontWidth+FontWidth; /* Add one for a LF */ height = yb - yt + FontHeight; SnapSize = ((width/FontWidth)+1) * (height/FontHeight); 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; } /* 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 */ UnlockLayers(LockedLayerInfo); /* Clear our work area */ BltClear(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+=FontWidth, counter++) { CopyChar(x, yt, COPY); if ((SnapSpace[counter] = interpret(TempRaster)) == 255) { SnapSpace[counter] = 63; /* Unrecognized changed to ? */ } } } else { /* Multiple lines */ if (Unit == UNIT_FRAME) { minx = xl; maxx = xr; } /* Read first line */ for (x=xl; x<=maxx; x+=FontWidth, counter++) { CopyChar(x, yt, COPY); if ((SnapSpace[counter] = interpret(TempRaster)) == 255) { SnapSpace[counter] = 63; /* Unrecognized changed to ? */ } } if (Unit == UNIT_FRAME) { SnapSpace[counter++] = 10; } else { /* Remove trailing blanks */ while (counter && SnapSpace[counter-1] == ' ') { counter--; } SnapSpace[counter++] = 10; } /* If more than two rows - read full middle rows */ if (yt+FontHeight != yb) { for (y=yt+FontHeight; yWScreen->LayerInfo; LockLayers(LockedLayerInfo); /* Don't want to wreck somebody's rastport */ CopyMem(window->RPort, &rp, (long)sizeof(struct RastPort)); /* Or his picture */ SetDrMd(&rp, COMPLEMENT); rp.Mask = FrameMask; Ptrn = CrawlPtrn; SetDrPt(&rp, Ptrn); /* Find out what we're trying to read */ SetSnapFont(rp.Font); if (FontWidth == -1) { UnlockLayers(LockedLayerInfo); action = noaction; return 0; } /* Find a position */ xl = window->MouseX - 7; yt = window->MouseY - 7; if (xl<0) { xl = 0; } if (yt<0) { yt = 0; } /* Check your position */ if (xl>window->Width || yt>window->Height) { UnlockLayers(LockedLayerInfo); action = noaction; return 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 SHORT found = 0; BltClear(TempRaster, 32L, 0L); xoff = 0; while ((xoff<(16-FontWidth)) && !found) { ClipBlit(&rp, xl+xoff, yt, &TempRp, 0L, 0L, (LONG)FontWidth, 16L, COPY); WaitBlit(); yoff = 0; while ((yoff<(16-FontHeight)) && !found) { if (interpret(&TempRaster[yoff]) != 255) { found = 1; } ++yoff; } ++xoff; } /* Did we find a character? */ if (!found) { /* No, back off */ UnlockLayers(LockedLayerInfo); action = noaction; return 0; } } /* Ok, now we know where to look for chars. ** xoff and yoff is character position within our 16x16 bitmap. */ xl = xl + xoff-1; /* Adjust x */ yt = yt + yoff-1; /* Adjust y */ /* Find out offsets within the window */ xoff = xl % FontWidth; yoff = yt % FontHeight; /* Set bounds */ minx = xoff; maxx = minx+((window->Width-minx-FontWidth -(window->Flags & BORDERLESS ? 0 : 2) -(window->Flags & WINDOWSIZING ? 12 : 0))/FontWidth)*FontWidth; /* Check bounds */ if (xl>maxx) { UnlockLayers(LockedLayerInfo); 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 = StartUnit; 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) && (CrawlPtrn != 0xffff)) { crawl_frame(0L); } mx = (LONG)window->MouseX; 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) % FontWidth); my = (LONG)window->MouseY; if (my<0) { my = 0; } /* Calculate which row is closest */ if ((my-yt) < (yb-my)) { closey = closetop; } else { closey = closebottom; } my = my - ((my-yoff) % FontHeight); /* 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(); UnlockLayers(LockedLayerInfo); 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(); } } }