home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d3xx / d326 / snap.lha / Snap / source / snapchars.c < prev    next >
C/C++ Source or Header  |  1990-03-05  |  22KB  |  771 lines

  1. /* Auto: make
  2. */
  3.  
  4. IMPORT struct SnapRsrc *SnapRsrc;
  5.  
  6. #define COPY 0xC0L
  7. #define INVCOPY 0x30L
  8. #define CopyChar(_x, _y, _m)                                  \
  9.     BltBitMap(&MyBM, (LONG)_x, (LONG)_y,                      \
  10.       &TempBM, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight,     \
  11.       _m, -1L, NULL);                                         \
  12.     WaitBlit()
  13.  
  14. WORD Unit;
  15. WORD Pattern[5] = {
  16.     0,
  17.     0x0c3f,     /* Frame ....oo....oooooo */
  18.     0x3333,     /* Char  ..oo..oo..oo..oo */
  19.     0x1f1f,     /* Word  ...ooooo...ooooo */
  20.     0xffff      /* Line  oooooooooooooooo */
  21. };
  22.  
  23. IMPORT LONG xl; /* leftmost x position */
  24. IMPORT LONG xr; /* rightmost x position */
  25. IMPORT LONG yt; /* topmost y position */
  26. IMPORT LONG yb; /* bottommost y position */
  27. LONG minx; /* left limit */
  28. LONG maxx; /* right limit */
  29. LONG maxy; /* bottom limit */
  30. LONG tl, tr; /* used by findword - left and right edge of word */
  31. WORD fw, fh; /* Font width and height used when drawing the frame */
  32. WORD GZZ;
  33. WORD SBM;
  34.  
  35. IMPORT LONG mx, my; /* Mouse position in character steps */
  36. #define closetop     0
  37. #define closebottom  1
  38. WORD closey;
  39. #define closeleft   0
  40. #define closeright  1
  41. WORD closex;
  42.  
  43.  
  44. struct Window *window;  /* The window we're snapping from */
  45.  
  46. /* Data for font being snapped */
  47. UWORD FontHeight;
  48. UWORD FontWidth;
  49. UWORD Underscore;
  50. UBYTE FontType;
  51. UBYTE LoChar;
  52. UBYTE HiChar;
  53. UWORD Modulo;
  54. UWORD *CharLoc;
  55. UWORD NoOfChars;
  56. UBYTE *SrcData;
  57. IMPORT UBYTE *CharData;
  58. UBYTE IFlags;
  59.  
  60. IMPORT struct RastPort TempRp, MyRP;
  61. IMPORT struct BitMap TempBM, MyBM;
  62.  
  63. IMPORT UWORD *TempRaster;   /* Used for character recognition */
  64.  
  65. IMPORT struct Screen *theScreen;
  66. IMPORT struct RastPort rp;
  67. struct Layer *LockedLayer;
  68.  
  69. IMPORT LONGBITS cancelsignal, donesignal, movesignal, clicksignal, timersignal;
  70. IMPORT WORD action;
  71.  
  72. WORD starting;
  73.  
  74. /* Init vars with font data.
  75. */
  76.  
  77. VOID SetSnapFont(font)
  78. struct TextFont *font;
  79. {
  80.     if (!font) {
  81.         FontWidth = -1;
  82.         return;
  83.     }
  84.     FontHeight = font->tf_YSize;
  85.     Underscore = font->tf_Baseline + 1;
  86.     FontType = font->tf_Flags;
  87.     FontWidth = (FontType & FPF_PROPORTIONAL ? -1 : font->tf_XSize);
  88.     if (FontWidth == -1) {
  89.         return;
  90.     }
  91.     LoChar = font->tf_LoChar;
  92.     HiChar = font->tf_HiChar;
  93.     Modulo = font->tf_Modulo;
  94.     CharLoc = (UWORD *)font->tf_CharLoc;
  95.     NoOfChars = HiChar - LoChar + 1;
  96.     Modulo = font->tf_Modulo;
  97.     SrcData = (UBYTE *)font->tf_CharData;
  98.     BltClear(CharData, 256L * 32, 0L);
  99.     WaitBlit();
  100.     CopyFont();
  101. }
  102.  
  103. /* Check if the character at x, y is a space
  104. */
  105.  
  106. WORD IsSpace(x, y)
  107. LONG x, y;
  108. {
  109.     REGISTER WORD i = FontHeight - 1;
  110.     REGISTER UWORD *data = &TempRaster[i];
  111.  
  112.       /* Copy character at x, y */
  113.     BltClear((char *)TempRaster, 32L, 0L);
  114.     ClipBlit(&rp, x, y,
  115.       &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY);
  116.     WaitBlit();
  117.  
  118.     if (*data) {         /* Try inverted copy */
  119.         ClipBlit(&rp, x, y,
  120.           &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, INVCOPY);
  121.         WaitBlit();
  122.     }
  123.     while (i--) {
  124.         if (*data--) {
  125.             return 0;
  126.         }
  127.     }
  128.     return 1;
  129. }
  130.  
  131. #define ShortFrame 4L      /* Square frame - columnar select */
  132. #define LongFrame  8L      /* Strange frame - char or word select */
  133. IMPORT LONG OFType;        /* Old frame type: ShortFrame/LongFrame */
  134. IMPORT UWORD Ptrn;
  135. IMPORT Point OldFrame[];
  136. IMPORT Point NewFrame[];
  137.  
  138.  
  139. /* update_frame calculates the new frame,
  140. ** erases the old frame and draws the new one.
  141. ** It's all pretty obvious if you take it slowly.
  142. */
  143. VOID update_frame()
  144. {
  145.     LONG ft;
  146.     switch (Unit) {
  147.         case UNIT_FRAME: {
  148.               /*********\
  149.               *         *
  150.               *         *
  151.               \*********/
  152.             NewFrame[0].x = xl - 1;  NewFrame[0].y = yt - 1;
  153.             NewFrame[1].x = xr + fw; NewFrame[1].y = yt - 1;
  154.             NewFrame[2].x = xr + fw; NewFrame[2].y = yb + fh;
  155.             NewFrame[3].x = xl - 1;  NewFrame[3].y = yb + fh;
  156.             NewFrame[4].x = xl - 1;  NewFrame[4].y = yt - 1;
  157.             ft = ShortFrame;
  158.             break;
  159.         }
  160.         case UNIT_CHAR:
  161.         case UNIT_WORD: {
  162.             if (yt == yb) {   /* On the same line - same as UNIT_FRAME */
  163.                 NewFrame[0].x = xl - 1;  NewFrame[0].y = yt - 1;
  164.                 NewFrame[1].x = xr + fw; NewFrame[1].y = yt - 1;
  165.                 NewFrame[2].x = xr + fw; NewFrame[2].y = yb + fh;
  166.                 NewFrame[3].x = xl - 1;  NewFrame[3].y = yb + fh;
  167.                 NewFrame[4].x = xl - 1;  NewFrame[4].y = yt - 1;
  168.                 ft = ShortFrame;
  169.             } else {
  170.                       /*****\
  171.                  ******     *
  172.                  *          *
  173.                  *      *****
  174.                  *******/
  175.                 NewFrame[0].x = xl - 1;    NewFrame[0].y = yt - 1;
  176.                 NewFrame[1].x = maxx + fw; NewFrame[1].y = yt - 1;
  177.                 NewFrame[2].x = maxx + fw; NewFrame[2].y = yb;
  178.                 NewFrame[3].x = xr + fw;   NewFrame[3].y = yb;
  179.                 NewFrame[4].x = xr + fw;   NewFrame[4].y = yb + fh;
  180.                 NewFrame[5].x = minx - 1;  NewFrame[5].y = yb + fh;
  181.                 NewFrame[6].x = minx - 1;  NewFrame[6].y = yt + fh;
  182.                 NewFrame[7].x = xl - 1;    NewFrame[7].y = yt + fh;
  183.                 NewFrame[8].x = xl - 1;    NewFrame[8].y = yt - 1;
  184.                 ft = LongFrame;
  185.             }
  186.             break;
  187.         }
  188.         case UNIT_LINE: {
  189.             NewFrame[0].x = minx - 1;  NewFrame[0].y = yt - 1;
  190.             NewFrame[1].x = maxx + fw; NewFrame[1].y = yt - 1;
  191.             NewFrame[2].x = maxx + fw; NewFrame[2].y = yb + fh;
  192.             NewFrame[3].x = minx - 1;  NewFrame[3].y = yb + fh;
  193.             NewFrame[4].x = minx - 1;  NewFrame[4].y = yt - 1;
  194.             ft = ShortFrame;
  195.             break;
  196.         }
  197.         default: {
  198.             break;
  199.         }
  200.     }
  201.     draw_frame(ft);
  202. }
  203.  
  204. VOID FindWord()
  205. {
  206.       /* Must remove frame to be able to search for spaces */
  207.     WaitTOF();
  208.     erase_frame();
  209.     tl = mx;
  210.       /* Find a space to the left... */
  211.     while (!IsSpace(tl, my)) {
  212.         tl -= fw;
  213.         if (tl < minx) {
  214.             break;
  215.         }
  216.     }
  217.     tl += fw;
  218.     tr = mx;
  219.       /* ...and to the right */
  220.     while (!IsSpace(tr, my)) {
  221.         tr += fw;
  222.         if (tr + fw > maxx) {
  223.             break;
  224.         }
  225.     }
  226.     tr -= fw;
  227.     if (tr < tl) {
  228.         tl = xl;
  229.         tr = xr;
  230.     }
  231. }
  232.  
  233. /* ChangeUnit cycles the unit of selection. The differents units
  234.    are: character, word and line.
  235. */
  236.  
  237. VOID ChangeUnit()
  238. {
  239.  
  240.     switch (Unit) {
  241.         case UNIT_FRAME: {
  242.             Unit = UNIT_CHAR;
  243.             break;
  244.         }
  245.         case UNIT_CHAR: {
  246.             Unit = UNIT_WORD;
  247.             FindWord();
  248.             xl = tl;
  249.             xr = tr;
  250.             break;
  251.         }
  252.         case UNIT_WORD: {
  253.             Unit = UNIT_LINE;
  254.             xl = minx;
  255.             xr = maxx;
  256.             break;
  257.         }
  258.         case UNIT_LINE: {
  259.             Unit = UNIT_FRAME;
  260.             xl = xr = mx;
  261.             break;
  262.         }
  263.     }
  264.     if (SnapRsrc->CrawlPtrn == 0) {
  265.         Ptrn = Pattern[Unit];
  266.     }
  267. }
  268.  
  269. /* ExtendSelection extends the current selection according to
  270.    the mouse position and the selected unit.
  271.    Note that ExtendSelection doesn't optimize moves that don't
  272.    make any difference. FIXME
  273. */
  274.  
  275. VOID ExtendSelection()
  276. {
  277.     /* Fix which row we're talking about */
  278.     if (closey == closetop) {       /* Find closest row */
  279.         yt = my;               /* change top row */
  280.     } else {
  281.         yb = my;               /* change bottom row */
  282.     }
  283.  
  284.     /* Take care of left and right character pos */
  285.     switch (Unit) {
  286.         case UNIT_FRAME: {
  287.             if (closex == closeleft) {
  288.                 xl = mx;
  289.             } else {
  290.                 xr = mx;
  291.             }
  292.             break;
  293.         }
  294.         case UNIT_CHAR: {
  295.             if (yt == yb) {            /* One line */
  296.                 if (closex == closeleft) {
  297.                     xl = mx;
  298.                 } else {
  299.                     xr = mx;
  300.                 }
  301.             } else {                    /* Multiple lines */
  302.                 if (yt == my) {
  303.                     xl = mx;            /* At top - set left */
  304.                 } else {
  305.                     xr = mx;            /* At bottom - set right */
  306.                 }
  307.             }
  308.             break;
  309.         }
  310.         case UNIT_WORD: {
  311.             FindWord();                 /* Find the word */
  312.             if (yt == yb) {             /* One line */
  313.                 if (closex == closeleft) {   /* Find closest char pos */
  314.                     xl = tl;
  315.                 } else {
  316.                     xr = tr;
  317.                 }
  318.             } else {                   /* Multiple lines */
  319.                 if (yt == my) {        /* Where am I */
  320.                     xl = tl;           /* At top - set left */
  321.                 } else {
  322.                     xr = tr;           /* At bottom - set right */
  323.                 }
  324.             }
  325.             break;
  326.         }
  327.         case UNIT_LINE: {              /* Always full width */
  328.             break;
  329.         }
  330.     }
  331.     if (yt - fh == yb) {
  332.         yb += fh;
  333.     }
  334.     if (yt == yb && xl - fw == xr) {
  335.         xr += fw;
  336.     }
  337.     if (xr > maxx) {         /* Check for window bounds */
  338.         xr = maxx;
  339.     }
  340.     if (xl < minx) {         /* Check for window bounds */
  341.         xl = minx;
  342.     }
  343.     if (yb > maxy) {         /* Check for window bounds */
  344.         yb = maxy;
  345.     }
  346. }
  347.  
  348. /* The actual character snapper. It actually works. :-) */
  349.  
  350. WORD SnapChars()
  351. {
  352.     LONG width;
  353.     LONG height;
  354.     UBYTE *SnapSpace;
  355.     ULONG SnapSize;
  356.     ULONG counter;
  357.     REGISTER LONG x, y;
  358.  
  359.       /* Check coordinates */
  360.     if (yt - fh == yb) {        /* No rows, shouldn't happen */
  361.         return 0;
  362.     }
  363.     if (yt == yb && xl - fw == xr) {     /* Nothing at all */
  364.         return 0;
  365.     }
  366.  
  367.       /* Calculate stuff */
  368.     width = maxx - (minx + 1) + fw + fw;  /* Add one for a LF */
  369.     height = yb - yt + fh;
  370.     SnapSize = ((width / fw) + 1) * (height / fh);
  371.     counter = 0;
  372.  
  373.       /* Initialize things */
  374.     InitRastPort(&MyRP);
  375.     InitBitMap(&MyBM, 1L, width, height);
  376.     MyRP.BitMap = &MyBM;
  377.     SnapSpace = AllocMem(SnapSize, MEMF_PUBLIC|MEMF_CLEAR);
  378.       /* Please insert more memory */
  379.     if (!SnapSpace) {
  380.         return 0;
  381.     }
  382.     MyBM.Planes[0] = AllocRaster(width, height);
  383.     if (!MyBM.Planes[0]) {
  384.         FreeMem(SnapSpace, SnapSize);
  385.         return 0;
  386.     }
  387.     IFlags = 0;
  388.       /* Make a local copy of the snapped chars */
  389.     ClipBlit(&rp, minx, yt, &MyRP, 0L, 0L, width, height, COPY);
  390.  
  391.       /* Ok, now we've got a copy of the character data */
  392.       /* Now it's ok to mess with the layers again */
  393.     UnlockLayer(LockedLayer);
  394.  
  395.       /* Clear our work area */
  396.     BltClear((char *)TempRaster, 32L, 0L);
  397.  
  398.       /* Calculate bounds */
  399.     xl -= minx;
  400.     xr -= minx;
  401.     maxx -= minx;
  402.     minx = 0;
  403.     yb -= yt;
  404.     yt = 0;
  405.  
  406.       /* Single line - needs to be handled separately */
  407.     if (yt == yb) { /* Ok, we've got one */
  408.  
  409.           /* Read from left to right */
  410.         for (x = xl; x <= xr; x += fw, counter++) {
  411.             CopyChar(x, yt, COPY);
  412.             if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
  413.                 SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
  414.             }
  415.         }
  416.         if (Unit == UNIT_LINE) {
  417.             while (counter && SnapSpace[counter-1] == ' ') {
  418.                 counter--;
  419.             }
  420.         }
  421.     } else { /* Multiple lines */
  422.  
  423.         if (Unit == UNIT_FRAME) {
  424.             minx = xl;
  425.             maxx = xr;
  426.         }
  427.  
  428.           /* Read first line */
  429.         for (x = xl; x <= maxx; x += fw, counter++) {
  430.             CopyChar(x, yt, COPY);
  431.             if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
  432.                 SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
  433.             }
  434.         }
  435.         if (Unit == UNIT_FRAME) {
  436.             SnapSpace[counter++] = 10;
  437.         } else {
  438.             SHORT endspace = (SnapSpace[counter-1] == ' ');
  439.               /* Remove trailing blanks */
  440.             while (counter && SnapSpace[counter-1] == ' ') {
  441.                 counter--;
  442.             }
  443.             if (endspace || !(SnapRsrc->flags & JOINLONG)) {
  444.                 SnapSpace[counter++] = 10;
  445.             }
  446.         }
  447.  
  448.           /* If more than two rows - read full middle rows */
  449.         if (yt + fh != yb) {
  450.             for (y = yt + fh; y < yb; y += fh) {
  451.                 for (x = minx; x <= maxx; x += fw, counter++) {
  452.                     CopyChar(x, y, COPY);
  453.                     if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
  454.                         SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
  455.                     }
  456.                 }
  457.                 if (Unit == UNIT_FRAME) {
  458.                     SnapSpace[counter++] = 10;
  459.                 } else {
  460.                     SHORT endspace = (SnapSpace[counter-1] == ' ');
  461.                       /* Remove trailing blanks */
  462.                     while (counter && SnapSpace[counter-1] == ' ') {
  463.                         counter--;
  464.                     }
  465.                     if (endspace || !(SnapRsrc->flags & JOINLONG)) {
  466.                         SnapSpace[counter++] = 10;
  467.                     }
  468.                 }
  469.             }
  470.         }
  471.  
  472.           /* Read last line */
  473.         for (x = minx; x <= xr; x += fw, counter++) {
  474.             CopyChar(x, yb, COPY);
  475.             if ((SnapSpace[counter] = interpret(TempRaster)) == 255) {
  476.                 SnapSpace[counter] = SnapRsrc->BadChar;  /* Unrecognized */
  477.             }
  478.         }
  479.         /* Remove trailing blanks */
  480.         while (counter && SnapSpace[counter-1] == ' ') {
  481.             counter--;
  482.         }
  483.     }
  484.     FreeRaster(MyBM.Planes[0], width, height);
  485.     SaveClip(SnapSpace, counter);
  486.     FreeMem(SnapSpace, SnapSize);
  487.     return 1;
  488. }
  489.  
  490.  
  491. /* HandleChars is the part of the Snap state machine that handles
  492.    snapping of characters. The selection is done in different
  493.    units: char, word, line.
  494. */
  495.  
  496. WORD HandleChars()
  497. {
  498.     LONG xoff, yoff;
  499.     LONG ox, oy;
  500.  
  501.       /* Find out which screen we're working on */
  502.     theScreen = WhichScreen();
  503.  
  504.       /* Oops, no screen? */
  505.     if (!theScreen) {
  506.         action = noaction;
  507.         return 0;
  508.     }
  509.  
  510.       /* Ok, what window? */
  511.     window = WhichWindow(theScreen);
  512.  
  513.       /* Oh dear, no window. */
  514.     if (!window) {
  515.         action = noaction;
  516.         return 0;
  517.     }
  518.  
  519.       /* No messing with the layers while I think */
  520.     LockedLayer = window->WLayer;
  521.     LockLayer(0L, LockedLayer);
  522.  
  523.       /* Don't want to wreck somebody's rastport */
  524.     CopyMem((char *)window->RPort, (char *)&rp, (LONG)sizeof(struct RastPort));
  525.  
  526.       /* Or his picture */
  527.     SetDrMd(&rp, COMPLEMENT);
  528.     rp.Mask = SnapRsrc->FrameMask;
  529.  
  530.       /* Find out what we're trying to read */
  531.     SetSnapFont(rp.Font);
  532.     if (FontWidth == -1) {
  533.         UnlockLayer(LockedLayer);
  534.         action = noaction;
  535.         return 0;
  536.     }
  537.  
  538.     if (window->Flags & GIMMEZEROZERO) {
  539.         GZZ = 1;
  540.     } else {
  541.         GZZ = 0;
  542.     }
  543.     if (window->Flags & SUPER_BITMAP) {
  544.         SBM = 1;
  545.     } else {
  546.         SBM = 0;
  547.     }
  548.  
  549.       /* Find a position */
  550.     xl = (GZZ ? window->GZZMouseX : window->MouseX)
  551.       + window->RPort->Layer->Scroll_X;
  552.     yt = (GZZ ? window->GZZMouseY : window->MouseY)
  553.       + window->RPort->Layer->Scroll_Y;
  554.  
  555.     if (xl < 0) {
  556.         xl = 0;
  557.     }
  558.     if (yt < 0) {
  559.         yt = 0;
  560.     }
  561.  
  562.       /* Check your position */
  563.     if (xl > (GZZ ? window->GZZWidth : window->Width) ||
  564.       yt > (GZZ ? window->GZZHeight : window->Height)) {
  565.         UnlockLayer(LockedLayer);
  566.         action = noaction;
  567.         return 0;
  568.     }
  569.     IFlags = 0;
  570.  
  571.       /* Find out the offset for the clicked character, if any.
  572.       ** This is the part that makes it special. Simple, isn't it. Hah!
  573.       */
  574.     {
  575.         REGISTER struct CacheWindow *cw = GetCachedWindow(theScreen, window);
  576.  
  577.         if (cw) {
  578.             xoff = - ((xl - cw->xoff) % cw->fw);
  579.             yoff = - ((yt - cw->yoff) % cw->fh);
  580.             BltClear((char *)TempRaster, 32L, 0L);
  581.             ClipBlit(&rp, xl + xoff, yt + yoff,
  582.               &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY);
  583.             WaitBlit();
  584.             if (interpret(TempRaster) != 255) {
  585.                 goto found;
  586.             }
  587.         }
  588.           /* No cache or cache didn't match */
  589.         xl -= 7;
  590.         yt -= 7;
  591.         BltClear((char *)TempRaster, 32L, 0L);
  592.         xoff = 0;
  593.         while (xoff < (16 - FontWidth)) {
  594.             ClipBlit(&rp, xl + xoff, yt,
  595.               &TempRp, 0L, 0L, (LONG)FontWidth, 16L, COPY);
  596.             WaitBlit();
  597.             yoff = 0;
  598.             while (yoff < (16 - FontHeight)) {
  599.                 if (interpret(&TempRaster[yoff]) != 255) {
  600.                     goto found;
  601.                 }
  602.                 ++yoff;
  603.             }
  604.             ++xoff;
  605.         }
  606.  
  607.           /* No character found. Back off */
  608.         UnlockLayer(LockedLayer);
  609.         action = noaction;
  610.         return 0;
  611.  
  612. found:
  613.           /* Ok, now we know where to look for chars.
  614.           ** xoff and yoff is character position within our 16x16 bitmap.
  615.           */
  616.         xl = xl + xoff;         /* Adjust x */
  617.         yt = yt + yoff;         /* Adjust y */
  618.  
  619.         fw = FontWidth;
  620.         fh = FontHeight;
  621.  
  622.         {
  623.             SHORT temp = fh;
  624.             while (temp <= fh + 1) {  /* Check for extra pixel row */
  625.                 BltClear((char *)TempRaster, 32L, 0L);
  626.                 ClipBlit(&rp, xl, yt + temp,
  627.                   &TempRp, 0L, 0L, (LONG)FontWidth, (LONG)FontHeight, COPY);
  628.                 WaitBlit();
  629.                 if (interpret(TempRaster) != 255) {
  630.                     fh = temp;
  631.                     break;
  632.                 }
  633.                 ++temp;
  634.             }
  635.         }
  636.  
  637.           /* Find out offsets within the window */
  638.         xoff = xl % fw;
  639.         yoff = yt % fh;
  640.  
  641.         if (cw) {
  642.             cw->xoff = xoff;
  643.             cw->yoff = yoff;
  644.             cw->fw = fw;
  645.             cw->fh = fh;
  646.         } else {
  647.             CacheWindow(window, xoff, yoff, fw, fh);
  648.         }
  649.     }
  650.  
  651.       /* Set bounds */
  652.     minx = xoff;
  653.     maxx = minx +
  654.       (((GZZ ?
  655.         window->GZZWidth :
  656.         window->Width - window->BorderRight
  657.           /* Hack for borderless conman windows */
  658.         + (window->Flags & BORDERLESS && window->Flags & WINDOWSIZING ? 14 : 0))
  659.       - minx - fw) / fw) * fw;
  660.     maxy = ((GZZ ? window->GZZHeight : window->Height) / fh) * fh;
  661.  
  662.       /* Check bounds */
  663.     if (xl > maxx) {
  664.         UnlockLayer(LockedLayer);
  665.         action = noaction;
  666.         return 0;
  667.     }
  668.       /* Set box dimensions */
  669.     xr = xl;
  670.     yb = yt;
  671.     ox = xr;
  672.     oy = yt;
  673.  
  674.       /* Select unit while starting */
  675.     starting = 1;
  676.  
  677.       /* Starting unit is character or frame */
  678.     Unit = SnapRsrc->StartUnit;
  679.     Ptrn = (SnapRsrc->CrawlPtrn ? SnapRsrc->CrawlPtrn : Pattern[Unit]);
  680.     OFType = 0L;
  681.     update_frame();
  682.  
  683.       /* Get the state machine running */
  684.     FOREVER {
  685.           /* Wait for something to happen */
  686.         REGISTER LONGBITS sig =
  687.           Wait(movesignal|cancelsignal|donesignal|clicksignal|timersignal);
  688.  
  689.         if ((sig & timersignal) && (SnapRsrc->CrawlPtrn != 0xffff)) {
  690.             crawl_frame(0L);
  691.         }
  692.  
  693.         mx = (LONG)(GZZ ? window->GZZMouseX : window->MouseX)
  694.           + window->RPort->Layer->Scroll_X;
  695.         if (mx < 0) {
  696.             mx = 0;
  697.         }
  698.           /* Calculate which edge is closest */
  699.         if ((mx - xl) < (xr - mx)) {
  700.             closex = closeleft;
  701.         } else {
  702.             closex = closeright;
  703.         }
  704.           /* Only interested in real char pos */
  705.         mx = mx - ((mx - xoff) % fw);
  706.  
  707.         my = (LONG)(GZZ ? window->GZZMouseY : window->MouseY)
  708.           + window->RPort->Layer->Scroll_Y;
  709.         if (my < 0) {
  710.             my = 0;
  711.         }
  712.           /* Calculate which row is closest */
  713.         if ((my - yt) < (yb - my)) {
  714.             closey = closetop;
  715.         } else {
  716.             closey = closebottom;
  717.         }
  718.         my = my - ((my - yoff) % fh);
  719.  
  720.           /* Hey, it moves! It's alive!! */
  721.         if ((sig & movesignal) && (action == snaptext)) {
  722.             if (mx != ox || my != oy) {  /* Something's happened */
  723.                 ExtendSelection();
  724.                 update_frame();
  725.                 starting = 0;
  726.                 ox = mx;
  727.                 oy = my;
  728.                 sig &= ~clicksignal;
  729.             }
  730.         }
  731.  
  732.           /* Ok, forget it... */
  733.         if (sig & cancelsignal) {
  734.             erase_frame();
  735.             UnlockLayer(LockedLayer);
  736.             return 0;
  737.         }
  738.  
  739.           /* Click */
  740.         if ((sig & clicksignal) && (action == snaptext)) {
  741.               /* Selecting unit */
  742.             if (starting) {
  743.                 if (mx == ox && my == oy) {
  744.                     ChangeUnit();
  745.                     if (Unit == UNIT_CHAR) {
  746.                         ChangeUnit();
  747.                     }
  748.                     update_frame();
  749.                 } else if (Unit == UNIT_FRAME) {
  750.                     ChangeUnit();
  751.                     update_frame();
  752.                 }
  753.             }
  754.             if (mx != ox || my != oy) { /* Click in a new place */
  755.                 ExtendSelection();
  756.                 update_frame();
  757.                 starting = 0;
  758.                 ox = mx;
  759.                 oy = my;
  760.             }
  761.         }
  762.  
  763.           /* Finished */
  764.         if (sig & donesignal) {
  765.             erase_frame();
  766.             return SnapChars();
  767.         }
  768.     }
  769. }
  770.  
  771.