home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / w32cbrd.c < prev    next >
C/C++ Source or Header  |  1998-07-27  |  15KB  |  588 lines

  1. /*
  2.  * w32cbrd:  collection of common clipboard manipulation routines shared by
  3.  *           the Win32 console- and GUI-based vile editor.
  4.  *
  5.  * Caveats
  6.  * =======
  7.  * -- This code has not been tested with NT 3.51 .
  8.  *
  9.  * -- On a stock Win95 host, the first copy to the clipboard from the
  10.  *    console version of vile causes the busy thread cursor to be displayed
  11.  *    (i.e., cursor changes to a pointer/hourglass icon).  This cursor stays
  12.  *    active for 5-10 seconds (all apps are active) and then goes away.
  13.  *    Subsequent copies do not show this cursor.  On an NT 4.0 host, this
  14.  *    phenomenon does not occur.
  15.  *
  16.  * $Header: /usr/build/vile/vile/RCS/w32cbrd.c,v 1.11 1998/07/27 10:18:14 tom Exp $
  17.  */
  18.  
  19. #include <windows.h>
  20. #include <stdlib.h>
  21. #include <search.h>
  22.  
  23. #include "estruct.h"
  24. #include "edef.h"
  25.  
  26. #define  CLIPBOARD_BUSY      "Clipboard currently busy"
  27. #define  CLIPBOARD_COPYING   "[Copying...]"
  28. #define  CLIPBOARD_COPY_FAIL "Clipboad copy failed"
  29. #define  CLIPBOARD_COPY_MEM  "Insufficient memory for copy operation"
  30. #define  _SPC_               ' '
  31. #define  _TAB_               '\t'
  32. #define  _TILDE_             '~'
  33.  
  34. typedef struct rgn_cpyarg_struct
  35. {
  36.     unsigned      nbyte, nline;
  37.     unsigned char *dst;
  38. } RGN_CPYARG;
  39.  
  40. static char ctrl_lookup[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_";
  41.  
  42. /* ------------------------------------------------------------------ */
  43.  
  44. static void
  45. report_cbrdstats(unsigned nbyte, unsigned nline, char *direction)
  46. {
  47.     char buf[128];
  48.  
  49.     if (! global_b_val(MDTERSE))
  50.     {
  51.         lsprintf(buf,
  52.                   "[Copied %d line%s, %d bytes %s clipboard]",
  53.                   nline,
  54.                   PLURAL(nline),
  55.                   nbyte,
  56.                   direction);
  57.         mlwrite(buf);
  58.     }
  59.     else
  60.         mlforce("[%d lines]", nline);
  61. }
  62.  
  63.  
  64.  
  65. /* The memory block handle _must_ be unlocked before calling this fn. */
  66. static int
  67. setclipboard(HGLOBAL hClipMem, unsigned nbyte, unsigned nline)
  68. {
  69.     int rc, i;
  70.  
  71.     for (rc = i = 0; i < 8 && (! rc); i++)
  72.     {
  73.         /* Try to open clipboard */
  74.  
  75.         if (! OpenClipboard(NULL))
  76.             Sleep(500);
  77.         else
  78.             rc = 1;
  79.     }
  80.     if (! rc)
  81.     {
  82.         mlforce(CLIPBOARD_BUSY);
  83.         GlobalFree(hClipMem);
  84.         return (FALSE);
  85.     }
  86.     EmptyClipboard();
  87.     rc = (SetClipboardData(CF_TEXT, hClipMem) != NULL);
  88.     CloseClipboard();
  89.     if (! rc)
  90.     {
  91.         mlforce(CLIPBOARD_COPY_FAIL);
  92.         GlobalFree(hClipMem);
  93.     }
  94.     else
  95.     {
  96.         /* success */
  97.  
  98.         report_cbrdstats(nbyte - 1,  /* subtract terminating NUL */
  99.                          nline,
  100.                          "to");
  101.     }
  102.     return (rc);
  103. }
  104.  
  105.  
  106.  
  107. /* Count lines and nonbuffer data added during "copy to clipboard" operation. */
  108. static void
  109. cbrd_count_meta_data(int           len,
  110.                      unsigned      *nbyte,
  111.                      unsigned      *nline,
  112.                      unsigned char *src)
  113. {
  114.     register unsigned c;
  115.  
  116.     while (len--)
  117.     {
  118.         if ((c = *src++) == '\n')
  119.         {
  120.             (*nline)++;
  121.             (*nbyte)++;              /* API requires CR/LF terminator */
  122.         }
  123.         else if (c < _SPC_ && c != _TAB_) /* assumes ASCII char set   */
  124.             (*nbyte)++;              /* account for '^' meta char     */
  125.         else if (c > _TILDE_)        /* assumes ASCII char set        */
  126.             (*nbyte) += 3;           /* account for '\xdd' meta chars */
  127.     }
  128. }
  129.  
  130.  
  131.  
  132. /*
  133.  * This function is called to process each logical line of data in a
  134.  * user-selected region.  It counts the number of bytes of data in the line.
  135.  */
  136. static int
  137. count_rgn_data(void *argp, int l, int r)
  138. {
  139.     RGN_CPYARG *cpyp;
  140.     int        len;
  141.     LINE       *lp;
  142.  
  143.     lp = DOT.l;
  144.  
  145.     /* Rationalize offsets */
  146.     if (llength(lp) < l)
  147.         return (TRUE);
  148.     if (r > llength(lp))
  149.         r = llength(lp);
  150.     cpyp = argp;
  151.     if (r == llength(lp) || regionshape == RECTANGLE)
  152.     {
  153.         /* process implied newline */
  154.  
  155.         cpyp->nline++;
  156.         cpyp->nbyte += 2;   /* CBRD API maps NL -> CR/LF */
  157.     }
  158.     len          = r - l;
  159.     cpyp->nbyte += len;
  160.     cbrd_count_meta_data(len, &cpyp->nbyte, &cpyp->nline, lp->l_text + l);
  161.     return (TRUE);
  162. }
  163.  
  164.  
  165.  
  166. static void
  167. cbrd_copy_and_xlate(int len, unsigned char **cbrd_ptr, unsigned char *src)
  168. {
  169.     register unsigned c;
  170.     unsigned char     *dst = *cbrd_ptr;
  171.  
  172.     while (len--)
  173.     {
  174.         if ((c = *src++) == '\n')
  175.         {
  176.             *dst++ = '\r';
  177.             *dst++ = '\n';
  178.         }
  179.         else if ((c >= _SPC_ && c <= _TILDE_) || (c == _TAB_))
  180.             *dst++ = c;
  181.         else if (c < _SPC_)
  182.         {
  183.             *dst++ = '^';
  184.             *dst++ = ctrl_lookup[c];
  185.         }
  186.         else
  187.         {
  188.             /* c > _TILDE_ */
  189.  
  190.             *dst++ = '\\';
  191.             *dst++ = 'x';
  192.             *dst++ = hexdigits[(c & 0xf0) >> 4];
  193.             *dst++ = hexdigits[c & 0xf];
  194.         }
  195.     }
  196.     *cbrd_ptr = dst;
  197. }
  198.  
  199.  
  200.  
  201. /*
  202.  * This function is called to process each logical line of data in a
  203.  * user-selected region.  It copies region data to a buffer allocated on
  204.  * the heap.
  205.  */
  206. static int
  207. copy_rgn_data(void *argp, int l, int r)
  208. {
  209.     RGN_CPYARG *cpyp;
  210.     int        len;
  211.     LINE       *lp;
  212.  
  213.     lp = DOT.l;
  214.  
  215.     /* Rationalize offsets */
  216.     if (llength(lp) < l)
  217.         return (TRUE);
  218.     if (r > llength(lp))
  219.         r = llength(lp);
  220.     cpyp = argp;
  221.     len  = r - l;
  222.     cbrd_copy_and_xlate(len, &cpyp->dst, lp->l_text + l);
  223.     if (r == llength(lp) || regionshape == RECTANGLE)
  224.     {
  225.         /* process implied newline */
  226.  
  227.         *cpyp->dst++ = '\r';
  228.         *cpyp->dst++ = '\n';
  229.     }
  230.     return (TRUE);
  231. }
  232.  
  233.  
  234.  
  235. /*
  236.  * Copy contents of [un]named register to Windows clipboard.  The control
  237.  * flow is shamelessly copied from kwrite().
  238.  */
  239. static int
  240. cbrd_reg_copy(void)
  241. {
  242.     HGLOBAL                 hClipMem;
  243.     register int            i;
  244.     KILL                    *kp;      /* pointer into [un]named register */
  245.     DWORD                   nbyte;
  246.     unsigned                nline;
  247.     unsigned char           *dst;
  248.  
  249.     /* make sure there is something to put */
  250.     if (kbs[ukb].kbufh == NULL)
  251.     {
  252.         mlforce("Nothing to copy");
  253.         return (FALSE);     /* not an error, just nothing */
  254.     }
  255.  
  256.     /* tell us we're writing */
  257.     mlwrite(CLIPBOARD_COPYING);
  258.     nline = 0;
  259.     nbyte = 0;
  260.  
  261.     /*
  262.      * Make 2 passes over the data.  1st pass counts the data and
  263.      * adjusts for the fact that:
  264.      *
  265.      * 1) each '\n' must be warped to "\r\n" to satisfy clipboard APIs.
  266.      * 2) unprintable data (modulo tabs) must be warped to a printable
  267.      *    equivalent.
  268.      */
  269.     for (kp = kbs[ukb].kbufh; kp; kp = kp->d_next)
  270.     {
  271.         i      = KbSize(ukb, kp);
  272.         nbyte += i;
  273.         cbrd_count_meta_data(i, &nbyte, &nline, kp->d_chunk);
  274.     }
  275.     nbyte++;   /* Add room for terminating null */
  276.  
  277.     /* 2nd pass -- alloc storage for data and copy to clipboard. */
  278.     if ((hClipMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, nbyte)) == NULL)
  279.     {
  280.         mlforce(CLIPBOARD_COPY_MEM);
  281.         return (FALSE);
  282.     }
  283.     dst = GlobalLock(hClipMem);
  284.     for (kp = kbs[ukb].kbufh; kp; kp = kp->d_next)
  285.         cbrd_copy_and_xlate((int) KbSize(ukb, kp), &dst, kp->d_chunk);
  286.     *dst = '\0';
  287.     GlobalUnlock(hClipMem);
  288.     return (setclipboard(hClipMem, nbyte, nline));
  289. }
  290.  
  291.  
  292.  
  293. /*
  294.  * Copy contents of unnamed register to Windows clipboard.
  295.  *
  296.  * Bound to Alt+Insert.
  297.  */
  298. int
  299. cbrdcpy_unnamed(int unused1, int unused2)
  300. {
  301.     int rc;
  302.  
  303.     kregcirculate(FALSE);
  304.     rc  = cbrd_reg_copy();
  305.     ukb = 0;
  306.     return (rc);
  307. }
  308.  
  309.  
  310. /*
  311.  * Copy the currently-selected region (i.e., the range of lines from DOT to
  312.  * MK, inclusive) to the windows clipboard.  Lots of code has been borrowed
  313.  * and/or adapted from operyank() and writereg().
  314.  */
  315. static int
  316. cbrdcpy_region(void)
  317. {
  318.     RGN_CPYARG              cpyarg;
  319.     DORGNLINES              dorgn;
  320.     HGLOBAL                 hClipMem;
  321.     MARK                    odot;
  322.     int                     rc;
  323.  
  324.     mlwrite("[Copying...]");
  325.     odot         = DOT;          /* do_lines_in_region() moves DOT. */
  326.     cpyarg.nbyte = cpyarg.nline = 0;
  327.     dorgn        = get_do_lines_rgn();
  328.  
  329.     /*
  330.      * Make 2 passes over the data.  1st pass counts the data and
  331.      * adjusts for the fact that:
  332.      *
  333.      * 1) each '\n' must be warped to "\r\n" to satisfy clipboard APIs.
  334.      * 2) unprintable data (modulo tabs) must be warped to a printable
  335.      *    equivalent.
  336.      */
  337.     rc  = dorgn(count_rgn_data, &cpyarg, TRUE);
  338.     DOT = odot;
  339.     if (!rc)
  340.         return (FALSE);
  341.     cpyarg.nbyte++;        /* Terminating nul */
  342.  
  343.     /* 2nd pass -- alloc storage for data and copy to clipboard. */
  344.     if ((hClipMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,
  345.                                 cpyarg.nbyte)) == NULL)
  346.     {
  347.         mlforce(CLIPBOARD_COPY_MEM);
  348.         return (FALSE);
  349.     }
  350.     cpyarg.dst = GlobalLock(hClipMem);
  351.  
  352.     /*
  353.      * Pass #2 -> The actual copy (don't need to restore DOT, that
  354.      * is handled by opercbrdcpy().
  355.      */
  356.     rc = dorgn(copy_rgn_data, &cpyarg, TRUE);
  357.     GlobalUnlock(hClipMem);
  358.     if (! rc)
  359.     {
  360.         GlobalFree(hClipMem);
  361.         return (FALSE);
  362.     }
  363.     *cpyarg.dst = '\0';
  364.     return (setclipboard(hClipMem, cpyarg.nbyte, cpyarg.nline));
  365. }
  366.  
  367.  
  368.  
  369. /*
  370.  * Copy contents of specified region or register to Windows clipboard.
  371.  * This command is an operaor and mimics the functionality of ^W, but
  372.  * mimics operyank()'s implemenation.
  373.  *
  374.  * Bound to Ctrl+Insert.
  375.  */
  376. int
  377. opercbrdcpy(int f, int n)
  378. {
  379.     if (ukb != 0)
  380.         return (cbrd_reg_copy());
  381.     else
  382.     {
  383.         MARK odot;
  384.         int  rc;
  385.  
  386.         odot  = DOT;
  387.         opcmd = OPDEL;
  388.         rc    = vile_op(f, n, cbrdcpy_region, "Clipboard copy");
  389.         DOT   = odot;   /* cursor does not move */
  390.         return (rc);
  391.     }
  392. }
  393.  
  394. /* ------------------- Paste Functionality ----------------------- */
  395.  
  396. static int  map_and_insert(unsigned, unsigned *);
  397.  
  398. typedef struct { unsigned val; char *str; } MAP;
  399.  
  400. /* --------------------------------------------------------------- */
  401.  
  402. /*
  403.  * Paste contents of windows clipboard (if TEXT) to current buffer.
  404.  * Bound to Shift+Insert.
  405.  */
  406. int
  407. cbrdpaste(int f, int n)
  408. {
  409.     register unsigned      c;
  410.     register unsigned char *data;
  411.     HANDLE                 hClipMem;
  412.     int                    i, rc, suppressnl;
  413.     unsigned               nbyte, nline;
  414.  
  415.     for (rc = i = 0; i < 8 && (! rc); i++)
  416.     {
  417.         /* Try to open clipboard */
  418.  
  419.         if (! OpenClipboard(NULL))
  420.             Sleep(500);
  421.         else
  422.             rc = 1;
  423.     }
  424.     if (! rc)
  425.     {
  426.         mlforce(CLIPBOARD_BUSY);
  427.         return (FALSE);
  428.     }
  429.     if ((hClipMem = GetClipboardData(CF_TEXT)) == NULL)
  430.     {
  431.         CloseClipboard();
  432.         mlforce("[Clipboard empty or not TEXT data]");
  433.         return (FALSE);
  434.     }
  435.     if ((data = GlobalLock(hClipMem)) == NULL)
  436.     {
  437.         CloseClipboard();
  438.         mlforce("[Can't lock clipboard memory]");
  439.         return (FALSE);
  440.     }
  441.     mlwrite(CLIPBOARD_COPYING);
  442.     nbyte = nline = 0;
  443.     rc    = TRUE;
  444.  
  445.     /*
  446.      * Before stuffing data in the current buffer, save info regarding dot
  447.      * and mark.  The dot/mark manipulation code is more or less cribbed
  448.      * from doput() and PutChar().  Note also that clipboard data is always
  449.      * copied into the current region as if it were an "exact" shape, which
  450.      * should be the most intuitive result for Windows users who work with
  451.      * the clipboard (I hope).
  452.      */
  453.     suppressnl = is_header_line(DOT, curbp);
  454.     if (! is_at_end_of_line(DOT))
  455.         forwchar(TRUE,1);
  456.     (void) setmark();
  457.     while(*data && rc)
  458.     {
  459.         if ((c = *data) == '\n')
  460.         {
  461.             nbyte++;
  462.             nline++;
  463.             rc = lnewline();
  464.         }
  465.         else if (c == '\r' && *(data + 1) == '\n')
  466.         {
  467.  
  468.             /* Clipboard end of line delimiter is crlf.  Ignore cr. */
  469.  
  470.             ;
  471.         }
  472.         else if (c > _TILDE_)
  473.             rc = map_and_insert(c, &nbyte);
  474.         else
  475.         {
  476.             nbyte++;
  477.             rc = linsert(1, (int) c);
  478.         }
  479.         data++;
  480.     }
  481.     if (rc)
  482.     {
  483.         if (nbyte > 0 && (data[-1] == '\n') && suppressnl)
  484.         {
  485.             /*
  486.              * Last byte inserted was a newline and DOT was originally
  487.              * pointing at the beginning of the buffer(??).  In this
  488.              * situation, linsert() has added an additional newline to the
  489.              * buffer.  Remove it.
  490.              */
  491.  
  492.             (void) ldelete(1, FALSE);
  493.         }
  494.     }
  495.     GlobalUnlock(hClipMem);
  496.     CloseClipboard();
  497.     if (! rc)
  498.     {
  499.         mlforce("Memory allocation failed");
  500.  
  501.         /* Give user a chance to read message--more will surely follow. */
  502.         Sleep(3000);
  503.     }
  504.     else
  505.     {
  506.         /*
  507.          * Success.  Fiddle with dot and mark again (another chunk of doput()
  508.          * code).  "Tha' boy shore makes keen use of cut and paste."
  509.          */
  510.  
  511.         swapmark();                           /* I understand this. */
  512.         if (is_header_line(DOT, curbp))
  513.             DOT.l = lback(DOT.l);             /* This is a mystery. */
  514.         report_cbrdstats(nbyte, nline, "from");
  515.     }
  516.     return (rc);
  517. }
  518.  
  519.  
  520.  
  521. static int
  522. map_compare(const void *elem1, const void *elem2)
  523. {
  524.     return (((MAP *) elem1)->val - ((MAP *) elem2)->val);
  525. }
  526.  
  527.  
  528.  
  529. /*
  530.  * Map selected characters from the ANSI character set to their ASCII
  531.  * equivalents and insert same in the current buffer.
  532.  */
  533. static int
  534. map_and_insert(unsigned c,       /* ANSI char to insert   */
  535.                unsigned *nbyte   /* total #chars inserted */
  536.                )
  537. {
  538.     int  rc;
  539.     MAP  key, *rslt_p;
  540.     char *str;
  541.  
  542.     /* Keep this table sorted by "val" . */
  543.     static MAP map[] =
  544.     {
  545.         { 0x85, "..."  },
  546.         { 0x8B, "<"    },
  547.         { 0x91, "'"    },
  548.         { 0x92, "'"    },
  549.         { 0x93, "\""   },
  550.         { 0x94, "\""   },
  551.         { 0x96, "-"    },
  552.         { 0x97, "--"   },
  553.         { 0x99, "(TM)" },
  554.         { 0x9B, ">"    },
  555.         { 0xA6, "|"    },
  556.         { 0xA9, "(C)"  },
  557.         { 0xAB, "<<"   },
  558.         { 0xAD, "-"    },
  559.         { 0xAE, "(R)"  },
  560.         { 0xB1, "+/-"  },
  561.         { 0xBB, ">>"   },
  562.         { 0xBC, "1/4"  },
  563.         { 0xBD, "1/2"  },
  564.         { 0xBE, "3/4"  },
  565.     };
  566.  
  567.     key.val = c;
  568.     rslt_p  = bsearch(&key,
  569.                       map,
  570.                       sizeof(map) / sizeof(map[0]),
  571.                       sizeof(map[0]),
  572.                       map_compare);
  573.     if (! rslt_p)
  574.     {
  575.         (*nbyte)++;
  576.         rc = linsert(1, c);
  577.     }
  578.     else
  579.     {
  580.         for (rc = TRUE, str = rslt_p->str; *str && rc; str++)
  581.         {
  582.             (*nbyte)++;
  583.             rc = linsert(1, *str);
  584.         }
  585.     }
  586.     return (rc);
  587. }
  588.