moin Hans, moin list,

Alright, diff attached.  Should be pretty well human-readable, and
comments should be mostly self explanatory (famous last words).  A few
changes (or lack thereof) deserve special mention, since they don't
directly relate to UTF-8 handling:

Tk proc pdtk_canvas_editval in src/pdtk_canvas.tcl:
+ SVN HEAD code caused a race condition / loop of 'pdsend pd_editmode'
calls as bound to the menu entry for 'Edit Mode', which made editing
impossible.  My reversion to the formerly commented-out code makes
editing possible again, but display and toggle are still buggy.  Oddly,
I didn't encounter this bug during the whole debugging process (despite
occasional edits of a test patch), but it does happen here now with both
a "clean" SVN HEAD version of pd-devel/0.41-4 as well as my cleaned-up
working copy.  Maybe all my debug code (not in the diff) was slowing
things down enough to avoid the loop...

Tk variable font_fixed_metrics src/pd.tk:
+ the values here are inconsistent with those in sys_fontlist[] in
s_main.c.  My fonts looked much better when I changed the tk variable to
jive with sys_fontlist[].  Also, with the current version, the
pixel-to-character translation code in g_rtext.c doesn't work right (as
of the 3rd or 4th line of text).  I haven't included this change in the
diff (except for comments where relevant), since I'm not sure whether
the inconsistency was intensional (e.g. to test font metric computation
and communication)...

new C sources src/s_utf8.h, src/s_utf8.c
+ modified and trimmed versions of the Bezanson code.  I actually only
use 3 or 4 functions of these, so it could conceivably be trimmed down
even further; I just cut out the stuff I don't expect to need (escapes,
vprintf, ...).  Should remain separate source files though, since
they're needed in both g_rtext.c and g_editor.c.

marmosets,
        Bryan

On 2009-03-12 02:03:04, Hans-Christoph Steiner <h...@eds.org> appears to
have written:
> 
> On Mar 11, 2009, at 6:07 PM, Bryan Jurish wrote:
> 
>> moin folks,
>>
>> I believe I've finally got pd-devel 0.41-4 using UTF-8 across the board.
>> So far, I've tested message boxes & comments (g_rtext), as well as
>> symbol atoms, and all seems good.  I think we can still expect goofiness
>> if someone names an abstraction using a multibyte character when the
>> filesystem isn't UTF-8 encoded (raw 8-bit works for me here too), but I
>> really don't want to open that particular can of worms.
>>
>> So I guess I have 2 questions:
>>
>> (1) what should I call the generic UTF-8 source files? (see my other
>> post)
> 
> I don't quite follow...  do you mean there are separate .c files for the
> UTF-8 code?

Yes, there is 1 .c and 1 .h file for utf8 handling, in the diff as
s_utf8.[ch].  I was tempted to use "z_", but didn't want to be the first ;-)

> I guess I am not sure which post you mean.

The one with subject "Pd C source prefix conventions?".

>> (2) shall I commit these changes to pd-devel/0.41-4, or somewhere else,
>> or just post a diff (ca. 33k, ought to be easier to read now; I've tried
>> to follow the indentation conventions of the source files I modified)?
> 
> If there are major changes to the C code, we'll have to run that by
> Miller.  He's already more or less accepted the existing pd-devel Tcl
> code, so he might be willing to accept these C changes as well.  I think
> its really important work, especially since Pd-devel also has localized
> menus and text.

I'm not sure how "major" the changes are... they're pretty much
restricted to a few lines in g_editor.c, and a smattering of changes in
g_rtext.c.  Most heavily hit was rtext_senditup(), which now has to keep
track of both byte offsets (for C) and logical character offsets (for
Tk).  I renamed a few local variables there to help keep track of what
was being counted where.   I haven't added any fields to any built-in
structs though, nor changed the interpretation of any existing fields,
parameters, etc., so everything else ought to be pretty much compatible.

> If you have the diff, maybe post it here and let's start there.

here it is...

marmosets,
        Bryan

-- 
Bryan Jurish                           "There is *always* one more bug."
jur...@ling.uni-potsdam.de      -Lubarsky's Law of Cybernetic Entomology
Index: src/g_editor.c
===================================================================
--- src/g_editor.c      (revision 10858)
+++ src/g_editor.c      (working copy)
@@ -9,6 +9,7 @@
 #include "s_stuff.h"
 #include "g_canvas.h"
 #include <string.h>
+#include "s_utf8.h" /*-- moo --*/
 
 void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename,
     int selectem);
@@ -1468,8 +1469,9 @@
         gotkeysym = av[1].a_w.w_symbol;
     else if (av[1].a_type == A_FLOAT)
     {
-        char buf[3];
-        sprintf(buf, "%c", (int)(av[1].a_w.w_float));
+        /*-- moo: assume keynum is a Unicode codepoint; encode as UTF-8 --*/
+        char buf[UTF8_MAXBYTES1];
+        u8_wc_toutf8_nul(buf, (UCS4)(av[1].a_w.w_float));
         gotkeysym = gensym(buf);
     }
     else gotkeysym = gensym("?");
Index: src/makefile.in
===================================================================
--- src/makefile.in     (revision 10858)
+++ src/makefile.in     (working copy)
@@ -47,6 +47,7 @@
     m_conf.c m_glob.c m_sched.c \
     s_main.c s_inter.c s_file.c s_print.c \
     s_loader.c s_path.c s_entry.c s_audio.c s_midi.c \
+    s_utf8.c \
     d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \
     d_math.c d_fft.c d_array.c d_global.c \
     d_delay.c d_resample.c \
Index: src/pdtk_canvas.tcl
===================================================================
--- src/pdtk_canvas.tcl (revision 10858)
+++ src/pdtk_canvas.tcl (working copy)
@@ -134,8 +134,8 @@
 
 # check or uncheck the "edit" menu item
 proc pdtk_canvas_editval {mytoplevel value} {
-       $mytoplevel.menubar.edit invoke [_ "Edit Mode"]
-#      $mytoplevel.menubar.edit entryconfigure "Edit Mode" -indicatoron $value
+#      $mytoplevel.menubar.edit invoke [_ "Edit Mode"] ;##-- moo: pd_editmode 
loop!
+       $mytoplevel.menubar.edit entryconfigure "Edit Mode" -indicatoron $value
        # TODO make this work
 }
 
Index: src/pd.tk
===================================================================
--- src/pd.tk   (revision 10858)
+++ src/pd.tk   (working copy)
@@ -44,6 +44,7 @@
 set font_weight "normal"
 # sizes of chars for each of the Pd fixed font sizes:
 #  fontsize  width(pixels)  height(pixels)
+# - moo: these should probably be made consistent with sys_fontlist[] in 
s_main.c
 set font_fixed_metrics {
        8 5 10
        9 6 11
@@ -187,6 +188,7 @@
 # into the metrics given by $::font_fixed_metrics for any given font/weight
 proc fit_font_into_metrics {} {
 # TODO the fonts picked seem too small, probably on fixed width
+# + moo: possibly a bug in 'font_fixed_metrics' (see above)
     foreach {size width height} $::font_fixed_metrics {
         set myfont [get_font_for_size $size]
         font create $myfont -family $::font_family -weight $::font_weight \
Index: src/s_utf8.c
===================================================================
--- src/s_utf8.c        (revision 0)
+++ src/s_utf8.c        (revision 0)
@@ -0,0 +1,280 @@
+/*
+  Basic UTF-8 manipulation routines
+  by Jeff Bezanson
+  placed in the public domain Fall 2005
+
+  This code is designed to provide the utilities you need to manipulate
+  UTF-8 as an internal string encoding. These functions do not perform the
+  error checking normally needed when handling UTF-8 data, so if you happen
+  to be from the Unicode Consortium you will want to flay me alive.
+  I do this because error checking can be performed at the boundaries (I/O),
+  with these routines reserved for higher performance on data known to be
+  valid.
+
+  modified by Bryan Jurish (moo) March 2009
+  + removed some unneeded functions (escapes, printf etc), added others
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#ifdef WIN32
+#include <malloc.h>
+#else
+#include <alloca.h>
+#endif
+
+#include "s_utf8.h"
+
+static const u_int32_t offsetsFromUTF8[6] = {
+    0x00000000UL, 0x00003080UL, 0x000E2080UL,
+    0x03C82080UL, 0xFA082080UL, 0x82082080UL
+};
+
+static const char trailingBytesForUTF8[256] = {
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+    1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+    2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+
+/* returns length of next utf-8 sequence */
+int u8_seqlen(char *s)
+{
+    return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1;
+}
+
+/* conversions without error checking
+   only works for valid UTF-8, i.e. no 5- or 6-byte sequences
+   srcsz = source size in bytes, or -1 if 0-terminated
+   sz = dest size in # of wide characters
+
+   returns # characters converted
+   dest will always be L'\0'-terminated, even if there isn't enough room
+   for all the characters.
+   if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space.
+*/
+int u8_toucs(u_int32_t *dest, int sz, char *src, int srcsz)
+{
+    u_int32_t ch;
+    char *src_end = src + srcsz;
+    int nb;
+    int i=0;
+
+    while (i < sz-1) {
+        nb = trailingBytesForUTF8[(unsigned char)*src];
+        if (srcsz == -1) {
+            if (*src == 0)
+                goto done_toucs;
+        }
+        else {
+            if (src + nb >= src_end)
+                goto done_toucs;
+        }
+        ch = 0;
+        switch (nb) {
+            /* these fall through deliberately */
+#if UTF8_SUPPORT_FULL_UCS4
+        case 5: ch += (unsigned char)*src++; ch <<= 6;
+        case 4: ch += (unsigned char)*src++; ch <<= 6;
+#endif
+        case 3: ch += (unsigned char)*src++; ch <<= 6;
+        case 2: ch += (unsigned char)*src++; ch <<= 6;
+        case 1: ch += (unsigned char)*src++; ch <<= 6;
+        case 0: ch += (unsigned char)*src++;
+        }
+        ch -= offsetsFromUTF8[nb];
+        dest[i++] = ch;
+    }
+ done_toucs:
+    dest[i] = 0;
+    return i;
+}
+
+/* srcsz = number of source characters, or -1 if 0-terminated
+   sz = size of dest buffer in bytes
+
+   returns # characters converted
+   dest will only be '\0'-terminated if there is enough space. this is
+   for consistency; imagine there are 2 bytes of space left, but the next
+   character requires 3 bytes. in this case we could NUL-terminate, but in
+   general we can't when there's insufficient space. therefore this function
+   only NUL-terminates if all the characters fit, and there's space for
+   the NUL as well.
+   the destination string will never be bigger than the source string.
+*/
+int u8_toutf8(char *dest, int sz, u_int32_t *src, int srcsz)
+{
+    u_int32_t ch;
+    int i = 0;
+    char *dest_end = dest + sz;
+
+    while (srcsz<0 ? src[i]!=0 : i < srcsz) {
+        ch = src[i];
+        if (ch < 0x80) {
+            if (dest >= dest_end)
+                return i;
+            *dest++ = (char)ch;
+        }
+        else if (ch < 0x800) {
+            if (dest >= dest_end-1)
+                return i;
+            *dest++ = (ch>>6) | 0xC0;
+            *dest++ = (ch & 0x3F) | 0x80;
+        }
+        else if (ch < 0x10000) {
+            if (dest >= dest_end-2)
+                return i;
+            *dest++ = (ch>>12) | 0xE0;
+            *dest++ = ((ch>>6) & 0x3F) | 0x80;
+            *dest++ = (ch & 0x3F) | 0x80;
+        }
+        else if (ch < 0x110000) {
+            if (dest >= dest_end-3)
+                return i;
+            *dest++ = (ch>>18) | 0xF0;
+            *dest++ = ((ch>>12) & 0x3F) | 0x80;
+            *dest++ = ((ch>>6) & 0x3F) | 0x80;
+            *dest++ = (ch & 0x3F) | 0x80;
+        }
+        i++;
+    }
+    if (dest < dest_end)
+        *dest = '\0';
+    return i;
+}
+
+/* moo: get byte length of character number, or 0 if not supported */
+int u8_wc_nbytes(u_int32_t ch)
+{
+  if (ch < 0x80) return 1;
+  if (ch < 0x800) return 2;
+  if (ch < 0x10000) return 3;
+  if (ch < 0x200000) return 4;
+#if UTF8_SUPPORT_FULL_UCS4
+  /*-- moo: support full UCS-4 range? --*/
+  if (ch < 0x4000000) return 5;
+  if (ch < 0x7fffffffUL) return 6;
+#endif
+  return 0; /*-- bad input --*/
+}
+
+int u8_wc_toutf8(char *dest, u_int32_t ch)
+{
+    if (ch < 0x80) {
+        dest[0] = (char)ch;
+        return 1;
+    }
+    if (ch < 0x800) {
+        dest[0] = (ch>>6) | 0xC0;
+        dest[1] = (ch & 0x3F) | 0x80;
+        return 2;
+    }
+    if (ch < 0x10000) {
+        dest[0] = (ch>>12) | 0xE0;
+        dest[1] = ((ch>>6) & 0x3F) | 0x80;
+        dest[2] = (ch & 0x3F) | 0x80;
+        return 3;
+    }
+    if (ch < 0x110000) {
+        dest[0] = (ch>>18) | 0xF0;
+        dest[1] = ((ch>>12) & 0x3F) | 0x80;
+        dest[2] = ((ch>>6) & 0x3F) | 0x80;
+        dest[3] = (ch & 0x3F) | 0x80;
+        return 4;
+    }
+    return 0;
+}
+
+/*-- moo --*/
+int u8_wc_toutf8_nul(char *dest, u_int32_t ch)
+{
+  int sz = u8_wc_toutf8(dest,ch);
+  dest[sz] = '\0';
+  return sz;
+}
+
+/* charnum => byte offset */
+int u8_offset(char *str, int charnum)
+{
+    int offs=0;
+
+    while (charnum > 0 && str[offs]) {
+        (void)(isutf(str[++offs]) || isutf(str[++offs]) ||
+               isutf(str[++offs]) || ++offs);
+        charnum--;
+    }
+    return offs;
+}
+
+/* byte offset => charnum */
+int u8_charnum(char *s, int offset)
+{
+    int charnum = 0, offs=0;
+
+    while (offs < offset && s[offs]) {
+        (void)(isutf(s[++offs]) || isutf(s[++offs]) ||
+               isutf(s[++offs]) || ++offs);
+        charnum++;
+    }
+    return charnum;
+}
+
+/* reads the next utf-8 sequence out of a string, updating an index */
+u_int32_t u8_nextchar(char *s, int *i)
+{
+    u_int32_t ch = 0;
+    int sz = 0;
+
+    do {
+        ch <<= 6;
+        ch += (unsigned char)s[(*i)++];
+        sz++;
+    } while (s[*i] && !isutf(s[*i]));
+    ch -= offsetsFromUTF8[sz-1];
+
+    return ch;
+}
+
+/* number of characters */
+int u8_strlen(char *s)
+{
+    int count = 0;
+    int i = 0;
+
+    while (u8_nextchar(s, &i) != 0)
+        count++;
+
+    return count;
+}
+
+void u8_inc(char *s, int *i)
+{
+    (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) ||
+           isutf(s[++(*i)]) || ++(*i));
+}
+
+void u8_dec(char *s, int *i)
+{
+    (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) ||
+           isutf(s[--(*i)]) || --(*i));
+}
+
+/*-- moo --*/
+void u8_inc_ptr(char **sp)
+{
+  (void)(isutf(*(++(*sp))) || isutf(*(++(*sp))) ||
+        isutf(*(++(*sp))) || ++(*sp));
+}
+
+/*-- moo --*/
+void u8_dec_ptr(char **sp)
+{
+  (void)(isutf(*(--(*sp))) || isutf(*(--(*sp))) ||
+        isutf(*(--(*sp))) || --(*sp));
+}
Index: src/g_rtext.c
===================================================================
--- src/g_rtext.c       (revision 10858)
+++ src/g_rtext.c       (working copy)
@@ -13,6 +13,7 @@
 #include "m_pd.h"
 #include "s_stuff.h"
 #include "g_canvas.h"
+#include "s_utf8.h"
 
 
 #define LMARGIN 2
@@ -32,10 +33,10 @@
 
 struct _rtext
 {
-    char *x_buf;
-    int x_bufsize;
-    int x_selstart;
-    int x_selend;
+    char *x_buf;    /*-- raw byte string, assumed UTF-8 encoded (moo) --*/
+    int x_bufsize;  /*-- byte length --*/
+    int x_selstart; /*-- byte offset --*/
+    int x_selend;   /*-- byte offset --*/
     int x_active;
     int x_dragfrom;
     int x_height;
@@ -106,6 +107,15 @@
 
 /* LATER deal with tcl-significant characters */
 
+/* firstone(), lastone()
+ *  + returns byte offset of (first|last) occurrence of 'c' in 's[0..n-1]', or
+ *    -1 if none was found
+ *  + 's' is a raw byte string
+ *  + 'c' is a byte value
+ *  + 'n' is the length (in bytes) of the prefix of 's' to be searched.
+ *  + we could make these functions work on logical characters in utf8 strings,
+ *    but we don't really need to...
+ */
 static int firstone(char *s, int c, int n)
 {
     char *s2 = s + n;
@@ -142,6 +152,16 @@
     of the entire text in pixels.
     */
 
+   /*-- moo: 
+    * + some variables from the original version have been renamed
+    * + variables with a "_b" suffix are raw byte strings, lengths, or offsets
+    * + variables with a "_c" suffix are logical character lengths or offsets
+    *   (assuming valid UTF-8 encoded byte string in x->x_buf)
+    * + a fair amount of O(n) computations required to convert between raw byte
+    *   offsets (needed by the C side) and logical character offsets (needed by
+    *   the GUI)
+    */
+
     /* LATER get this and sys_vgui to work together properly,
         breaking up messages as needed.  As of now, there's
         a limit of 1950 characters, imposed by sys_vgui(). */
@@ -158,14 +178,16 @@
 {
     t_float dispx, dispy;
     char smallbuf[200], *tempbuf;
-    int outchars = 0, nlines = 0, ncolumns = 0,
+    int outchars_b = 0, nlines = 0, ncolumns = 0,
         pixwide, pixhigh, font, fontwidth, fontheight, findx, findy;
     int reportedindex = 0;
     t_canvas *canvas = glist_getcanvas(x->x_glist);
-    int widthspec = x->x_text->te_width;
-    int widthlimit = (widthspec ? widthspec : BOXWIDTH);
-    int inindex = 0;
-    int selstart = 0, selend = 0;
+    int widthspec_c = x->x_text->te_width;
+    int widthlimit_c = (widthspec_c ? widthspec_c : BOXWIDTH);
+    int inindex_b = 0;
+    int inindex_c = 0;
+    int selstart_b = 0, selend_b = 0;
+    int x_bufsize_c = u8_charnum(x->x_buf, x->x_bufsize);
         /* if we're a GOP (the new, "goprect" style) borrow the font size
         from the inside to preserve the spacing */
     if (pd_class(&x->x_text->te_pd) == canvas_class &&
@@ -180,65 +202,76 @@
     if (x->x_bufsize >= 100)
          tempbuf = (char *)t_getbytes(2 * x->x_bufsize + 1);
     else tempbuf = smallbuf;
-    while (x->x_bufsize - inindex > 0)
+    while (x_bufsize_c - inindex_c > 0)
     {
-        int inchars = x->x_bufsize - inindex;
-        int maxindex = (inchars > widthlimit ? widthlimit : inchars);
+        int inchars_b  = x->x_bufsize - inindex_b;
+        int inchars_c  = x_bufsize_c  - inindex_c;
+        int maxindex_c = (inchars_c > widthlimit_c ? widthlimit_c : inchars_c);
+        int maxindex_b = u8_offset(x->x_buf + inindex_b, maxindex_c);
         int eatchar = 1;
-        int foundit = firstone(x->x_buf + inindex, '\n', maxindex);
-        if (foundit < 0)
+        int foundit_b  = firstone(x->x_buf + inindex_b, '\n', maxindex_b);
+        int foundit_c;
+        if (foundit_b < 0)
         {
-            if (inchars > widthlimit)
+            if (inchars_c > widthlimit_c)
             {
-                foundit = lastone(x->x_buf + inindex, ' ', maxindex);
-                if (foundit < 0)
+                foundit_b = lastone(x->x_buf + inindex_b, ' ', maxindex_b);
+                if (foundit_b < 0)
                 {
-                    foundit = maxindex;
+                    foundit_b = maxindex_b;
+                    foundit_c = maxindex_c;
                     eatchar = 0;
                 }
+                else
+                    foundit_c = u8_charnum(x->x_buf + inindex_b, foundit_b);
             }
             else
             {
-                foundit = inchars;
+                foundit_b = inchars_b;
+                foundit_c = inchars_c;
                 eatchar = 0;
             }
         }
+        else
+            foundit_c = u8_charnum(x->x_buf + inindex_b, foundit_b);
+
         if (nlines == findy)
         {
             int actualx = (findx < 0 ? 0 :
-                (findx > foundit ? foundit : findx));
-            *indexp = inindex + actualx;
+                (findx > foundit_c ? foundit_c : findx));
+            *indexp = inindex_b + u8_offset(x->x_buf + inindex_b, actualx);
             reportedindex = 1;
         }
-        strncpy(tempbuf+outchars, x->x_buf + inindex, foundit);
-        if (x->x_selstart >= inindex &&
-            x->x_selstart <= inindex + foundit + eatchar)
-                selstart = x->x_selstart + outchars - inindex;
-        if (x->x_selend >= inindex &&
-            x->x_selend <= inindex + foundit + eatchar)
-                selend = x->x_selend + outchars - inindex;
-        outchars += foundit;
-        inindex += (foundit + eatchar);
-        if (inindex < x->x_bufsize)
-            tempbuf[outchars++] = '\n';
-        if (foundit > ncolumns)
-            ncolumns = foundit;
+        strncpy(tempbuf+outchars_b, x->x_buf + inindex_b, foundit_b);
+        if (x->x_selstart >= inindex_b &&
+            x->x_selstart <= inindex_b + foundit_b + eatchar)
+                selstart_b = x->x_selstart + outchars_b - inindex_b;
+        if (x->x_selend >= inindex_b &&
+            x->x_selend <= inindex_b + foundit_b + eatchar)
+                selend_b = x->x_selend + outchars_b - inindex_b;
+        outchars_b += foundit_b;
+        inindex_b += (foundit_b + eatchar);
+        inindex_c += (foundit_c + eatchar);
+        if (inindex_b < x->x_bufsize)
+            tempbuf[outchars_b++] = '\n';
+        if (foundit_c > ncolumns)
+            ncolumns = foundit_c;
         nlines++;
     }
     if (!reportedindex)
-        *indexp = outchars;
+        *indexp = outchars_b;
     dispx = text_xpix(x->x_text, x->x_glist);
     dispy = text_ypix(x->x_text, x->x_glist);
     if (nlines < 1) nlines = 1;
-    if (!widthspec)
+    if (!widthspec_c)
     {
         while (ncolumns < 3)
         {
-            tempbuf[outchars++] = ' ';
+            tempbuf[outchars_b++] = ' ';
             ncolumns++;
         }
     }
-    else ncolumns = widthspec;
+    else ncolumns = widthspec_c;
     pixwide = ncolumns * fontwidth + (LMARGIN + RMARGIN);
     pixhigh = nlines * fontheight + (TMARGIN + BMARGIN);
 
@@ -246,31 +279,32 @@
         sys_vgui("pdtk_text_new .x%lx.c %s %f %f {%.*s} %d %s\n",
             canvas, x->x_tag,
             dispx + LMARGIN, dispy + TMARGIN,
-            outchars, tempbuf, sys_hostfontsize(font),
+            outchars_b, tempbuf, sys_hostfontsize(font),
             (glist_isselected(x->x_glist,
                 &x->x_glist->gl_gobj)? "blue" : "black"));
     else if (action == SEND_UPDATE)
     {
         sys_vgui("pdtk_text_set .x%lx.c %s {%.*s}\n",
-            canvas, x->x_tag, outchars, tempbuf);
+            canvas, x->x_tag, outchars_b, tempbuf);
         if (pixwide != x->x_drawnwidth || pixhigh != x->x_drawnheight) 
             text_drawborder(x->x_text, x->x_glist, x->x_tag,
                 pixwide, pixhigh, 0);
         if (x->x_active)
         {
-            if (selend > selstart)
+            if (selend_b > selstart_b)
             {
                 sys_vgui(".x%lx.c select from %s %d\n", canvas, 
-                    x->x_tag, selstart);
+                    x->x_tag, u8_charnum(x->x_buf, selstart_b));
                 sys_vgui(".x%lx.c select to %s %d\n", canvas, 
-                    x->x_tag, selend + (sys_oldtclversion ? 0 : -1));
+                    x->x_tag, u8_charnum(x->x_buf, selend_b)
+                             + (sys_oldtclversion ? 0 : -1));
                 sys_vgui(".x%lx.c focus \"\"\n", canvas);        
             }
             else
             {
                 sys_vgui(".x%lx.c select clear\n", canvas);
                 sys_vgui(".x%lx.c icursor %s %d\n", canvas, x->x_tag,
-                    selstart);
+                    u8_charnum(x->x_buf, selstart_b));
                 sys_vgui(".x%lx.c focus %s\n", canvas, x->x_tag);        
             }
         }
@@ -430,12 +464,12 @@
                 ....
             } */
             if (x->x_selstart && (x->x_selstart == x->x_selend))
-                x->x_selstart--;
+                u8_dec(x->x_buf, &x->x_selstart);
         }
         else if (n == 127)      /* delete */
         {
             if (x->x_selend < x->x_bufsize && (x->x_selstart == x->x_selend))
-                x->x_selend++;
+                u8_inc(x->x_buf, &x->x_selend);
         }
         
         ndel = x->x_selend - x->x_selstart;
@@ -448,7 +482,13 @@
 /* at Guenter's suggestion, use 'n>31' to test wither a character might
 be printable in whatever 8-bit character set we find ourselves. */
 
-        if (n == '\n' || (n > 31 && n != 127))
+/*-- moo:
+  ... but test with "<" rather than "!=" in order to accomodate unicode
+  codepoints for n (which we get since Tk is sending the "%A" substitution
+  for bind <Key>), effectively reducing the coverage of this clause to 7
+  bits.  Case n>127 is covered by the next clause.
+*/
+        if (n == '\n' || (n > 31 && n < 127))
         {
             newsize = x->x_bufsize+1;
             x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize);
@@ -458,20 +498,39 @@
             x->x_bufsize = newsize;
             x->x_selstart = x->x_selstart + 1;
         }
+       /*--moo: check for unicode codepoints beyond 7-bit ASCII --*/
+       else if (n > 127)
+        {
+            int ch_nbytes = u8_wc_nbytes(n);
+            newsize = x->x_bufsize + ch_nbytes;
+            x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize);
+            for (i = x->x_bufsize; i > x->x_selstart; i--)
+                x->x_buf[i] = x->x_buf[i-1];
+            x->x_bufsize = newsize;
+            /*-- moo: assume canvas_key() has encoded keysym as UTF-8 */
+            strncpy(x->x_buf+x->x_selstart, keysym->s_name, ch_nbytes);
+            x->x_selstart = x->x_selstart + ch_nbytes;
+        }
         x->x_selend = x->x_selstart;
         x->x_glist->gl_editor->e_textdirty = 1;
     }
     else if (!strcmp(keysym->s_name, "Right"))
     {
         if (x->x_selend == x->x_selstart && x->x_selstart < x->x_bufsize)
-            x->x_selend = x->x_selstart = x->x_selstart + 1;
+        {
+            u8_inc(x->x_buf, &x->x_selstart);
+            x->x_selend = x->x_selstart;
+        }
         else
             x->x_selstart = x->x_selend;
     }
     else if (!strcmp(keysym->s_name, "Left"))
     {
         if (x->x_selend == x->x_selstart && x->x_selstart > 0)
-            x->x_selend = x->x_selstart = x->x_selstart - 1;
+        {
+            u8_dec(x->x_buf, &x->x_selstart);
+            x->x_selend = x->x_selstart;
+        }
         else
             x->x_selend = x->x_selstart;
     }
@@ -479,18 +538,18 @@
     else if (!strcmp(keysym->s_name, "Up"))
     {
         if (x->x_selstart)
-            x->x_selstart--;
+            u8_dec(x->x_buf, &x->x_selstart);
         while (x->x_selstart > 0 && x->x_buf[x->x_selstart] != '\n')
-            x->x_selstart--;
+            u8_dec(x->x_buf, &x->x_selstart);
         x->x_selend = x->x_selstart;
     }
     else if (!strcmp(keysym->s_name, "Down"))
     {
         while (x->x_selend < x->x_bufsize &&
             x->x_buf[x->x_selend] != '\n')
-            x->x_selend++;
+            u8_inc(x->x_buf, &x->x_selend);
         if (x->x_selend < x->x_bufsize)
-            x->x_selend++;
+            u8_inc(x->x_buf, &x->x_selend);
         x->x_selstart = x->x_selend;
     }
     rtext_senditup(x, SEND_UPDATE, &w, &h, &indx);
Index: src/s_utf8.h
===================================================================
--- src/s_utf8.h        (revision 0)
+++ src/s_utf8.h        (revision 0)
@@ -0,0 +1,88 @@
+#ifndef S_UTF8_H
+#define S_UTF8_H
+
+/*--moo--*/
+#ifndef u_int32_t
+# define u_int32_t unsigned int
+#endif
+
+#ifndef UCS4
+# define UCS4 u_int32_t
+#endif
+
+/* UTF8_SUPPORT_FULL_UCS4
+ *  define this to support the full potential range of UCS-4 codepoints
+ *  (in anticipation of a future UTF-8 standard)
+ */
+/*#define UTF8_SUPPORT_FULL_UCS4 1*/
+#undef UTF8_SUPPORT_FULL_UCS4
+
+/* UTF8_MAXBYTES
+ *   maximum number of bytes required to represent a single character in UTF-8
+ *
+ * UTF8_MAXBYTES1 = UTF8_MAXBYTES+1 
+ *  maximum bytes per character including NUL terminator
+ */
+#ifdef UTF8_SUPPORT_FULL_UCS4
+# ifndef UTF8_MAXBYTES
+#  define UTF8_MAXBYTES  6
+# endif
+# ifndef UTF8_MAXBYTES1
+#  define UTF8_MAXBYTES1 7
+# endif
+#else
+# ifndef UTF8_MAXBYTES
+#  define UTF8_MAXBYTES  4
+# endif
+# ifndef UTF8_MAXBYTES1
+#  define UTF8_MAXBYTES1 5
+# endif
+#endif
+/*--/moo--*/
+
+/* is c the start of a utf8 sequence? */
+#define isutf(c) (((c)&0xC0)!=0x80)
+
+/* convert UTF-8 data to wide character */
+int u8_toucs(u_int32_t *dest, int sz, char *src, int srcsz);
+
+/* the opposite conversion */
+int u8_toutf8(char *dest, int sz, u_int32_t *src, int srcsz);
+
+/* moo: get byte length of character number, or 0 if not supported */
+int u8_wc_nbytes(u_int32_t ch);
+
+/* moo: compute required storage for UTF-8 encoding of 's[0..n-1]' */
+int u8_wcs_nbytes(u_int32_t *ucs, int size);
+
+/* single character to UTF-8, no NUL termination */
+int u8_wc_toutf8(char *dest, u_int32_t ch);
+
+/* moo: single character to UTF-8, with NUL termination */
+int u8_wc_toutf8_nul(char *dest, u_int32_t ch);
+
+/* character number to byte offset */
+int u8_offset(char *str, int charnum);
+
+/* byte offset to character number */
+int u8_charnum(char *s, int offset);
+
+/* return next character, updating an index variable */
+u_int32_t u8_nextchar(char *s, int *i);
+
+/* move to next character */
+void u8_inc(char *s, int *i);
+
+/* move to previous character */
+void u8_dec(char *s, int *i);
+
+/* moo: move pointer to next character */
+void u8_inc_ptr(char **sp);
+
+/* moo: move pointer to previous character */
+void u8_dec_ptr(char **sp);
+
+/* returns length of next utf-8 sequence */
+int u8_seqlen(char *s);
+
+#endif /* S_UTF8_H */
_______________________________________________
Pd-dev mailing list
Pd-dev@iem.at
http://lists.puredata.info/listinfo/pd-dev

Reply via email to