From d3661c9a5e1d6356ea98b3805ca47e2ef1c56b12 Mon Sep 17 00:00:00 2001 From: tagoh Date: Mon, 22 Jan 2001 20:17:48 +0000 Subject: [PATCH] * Added i18n code. --- Makefile.in | 16 +- button.c | 7 +- checkbox.c | 4 +- checkboxtree.c | 16 +- dialogboxes.c | 15 +- eawidth.c | 459 +++++++++++++++++++++++++++++++++++++++++++++++++ eawidth.h | 37 ++++ entry.c | 7 +- form.c | 81 +++++++++ grid.c | 10 +- label.c | 6 +- listbox.c | 24 ++- newt.c | 4 + newt_pr.h | 1 + scale.c | 1 + scrollbar.c | 1 + test-j.c | 146 ++++++++++++++++ testgrid-j.c | 106 ++++++++++++ testtree-j.c | 132 ++++++++++++++ textbox.c | 140 ++++++++------- windows.c | 8 + 21 files changed, 1124 insertions(+), 97 deletions(-) create mode 100644 eawidth.c create mode 100644 eawidth.h create mode 100644 test-j.c create mode 100644 testgrid-j.c create mode 100644 testtree-j.c diff --git a/Makefile.in b/Makefile.in index f61c31c..4cdef5a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -12,8 +12,9 @@ VERSION = @VERSION@ CVSTAG = r$(subst .,-,$(VERSION)) SONAME = @SONAME@ -PROGS = test whiptail whiptcl.so testgrid testtree +PROGS = test test-j whiptail whiptcl.so testgrid testgrid-j testtree testtree-j TESTOBJS = test.o +TESTJOBJS = test-j.o NDIALOGOBJS = whiptail.o dialogboxes.o WHIPTCLOBJS = whiptcl.o dialogboxes.o LIBNEWT = libnewt.a @@ -21,7 +22,7 @@ LIBNEWTSH = libnewt.so.$(VERSION) LIBNEWTSONAME = libnewt.so.$(SONAME) LIBOBJS = newt.o button.o form.o checkbox.o entry.o label.o listbox.o \ scrollbar.o textbox.o scale.o grid.o windows.o buttonbar.o \ - checkboxtree.o + checkboxtree.o eawidth.o SHCFLAGS = -fPIC @@ -35,7 +36,7 @@ pythonbindir = $(prefix)/lib/python1.5/lib-dynload #-------------------------------------- -SOURCES = $(subst .o,.c,$(TESTOBJS) $(NDIALOGOBJS) $(LIBOBJS)) +SOURCES = $(subst .o,.c,$(TESTOBJS) $(TESTJOBJS) $(NDIALOGOBJS) $(LIBOBJS)) SHAREDDIR = shared SHAREDOBJS = $(patsubst %,$(SHAREDDIR)/%, $(LIBOBJS)) @@ -51,12 +52,21 @@ all: $(TARGET) _snackmodule.so test: $(TESTOBJS) $(LIBNEWT) gcc -g -o test $(TESTOBJS) $(LIBNEWT) $(LIBS) -static +test-j: $(TESTJOBJS) $(LIBNEWT) + gcc -g -o test-j $(TESTJOBJS) $(LIBNEWT) $(LIBS) -static + testgrid: testgrid.o $(LIBNEWT) gcc -g -o testgrid testgrid.o $(LIBNEWT) $(LIBS) +testgrid-j: testgrid-j.o $(LIBNEWT) + gcc -g -o testgrid-j testgrid-j.o $(LIBNEWT) $(LIBS) + testtree: testtree.o $(LIBNEWT) gcc -g -o testtree testtree.o $(LIBNEWT) $(LIBS) +testtree-j: testtree-j.o $(LIBNEWT) + gcc -g -o testtree-j testtree-j.o $(LIBNEWT) $(LIBS) + _snackmodule.so: snackmodule.o $(LIBNEWTSH) gcc --shared $(SHCFLAGS) -o _snackmodule.so snackmodule.o -L . $(LIBNEWTSH) diff --git a/button.c b/button.c index 1ff360d..079de9b 100644 --- a/button.c +++ b/button.c @@ -4,6 +4,7 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" struct button { char * text; @@ -30,6 +31,7 @@ static struct componentOps buttonOps = { static newtComponent createButton(int left, int row, const char * text, int compact) { newtComponent co; struct button * bu; + int width = get_east_asia_str_width (NULL, text, 0); co = malloc(sizeof(*co)); bu = malloc(sizeof(struct button)); @@ -41,16 +43,17 @@ static newtComponent createButton(int left, int row, const char * text, int comp if (bu->compact) { co->height = 1; - co->width = strlen(text) + 3; + co->width = width + 3; } else { co->height = 4; - co->width = strlen(text) + 5; + co->width = width + 5; } co->top = row; co->left = left; co->takesFocus = 1; co->isMapped = 0; + co->isLabel = 0; newtGotorc(co->top, co->left); diff --git a/checkbox.c b/checkbox.c index eee514c..7278788 100644 --- a/checkbox.c +++ b/checkbox.c @@ -4,6 +4,7 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" enum type { CHECK, RADIO }; @@ -117,10 +118,11 @@ newtComponent newtCheckbox(int left, int top, const char * text, char defValue, co->callback = NULL; co->height = 1; - co->width = strlen(text) + 4; + co->width = get_east_asia_str_width (NULL, text, 0) + 4; co->top = top; co->left = left; co->takesFocus = 1; + co->isLabel = 0; return co; } diff --git a/checkboxtree.c b/checkboxtree.c index d9cdaca..32ee62e 100644 --- a/checkboxtree.c +++ b/checkboxtree.c @@ -4,6 +4,7 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" struct items { char * text; @@ -158,7 +159,7 @@ int newtCheckboxTreeAddArray(newtComponent co, int flags, int * indexes) { struct items * curList, * newNode, * item; struct items ** listPtr = NULL; - int i, index, numIndexes; + int i, index, numIndexes, width; struct CheckboxTree * ct = co->data; numIndexes = 0; @@ -232,9 +233,10 @@ int newtCheckboxTreeAddArray(newtComponent co, item->depth = numIndexes - 1; i = 4 + (3 * item->depth); + width = get_east_asia_str_width (NULL, text, 0); - if ((strlen(text) + i + ct->pad) > co->width) { - co->width = strlen(text) + i + ct->pad; + if ((width + i + ct->pad) > co->width) { + co->width = width + i + ct->pad; } return 0; @@ -314,6 +316,7 @@ newtComponent newtCheckboxTreeMulti(int left, int top, int height, char *seq, in co->height = height; co->width = 0; co->isMapped = 0; + co->isLabel = 0; ct->itemlist = NULL; ct->firstItem = NULL; ct->currItem = NULL; @@ -657,7 +660,7 @@ void newtCheckboxTreeSetEntry(newtComponent co, const void * data, const char * { struct CheckboxTree * ct; struct items * item; - int i; + int i, width; if (!co) return; ct = co->data; @@ -668,9 +671,10 @@ void newtCheckboxTreeSetEntry(newtComponent co, const void * data, const char * item->text = strdup(text); i = 4 + (3 * item->depth); + width = get_east_asia_str_width (NULL, text, 0); - if ((strlen(text) + i + ct->pad) > co->width) { - co->width = strlen(text) + i + ct->pad; + if ((width + i + ct->pad) > co->width) { + co->width = width + i + ct->pad; } ctDraw(co); diff --git a/dialogboxes.c b/dialogboxes.c index 4144009..5847f26 100644 --- a/dialogboxes.c +++ b/dialogboxes.c @@ -9,6 +9,7 @@ #include "dialogboxes.h" #include "newt.h" #include "popt.h" +#include "eawidth.h" /* globals -- ick */ static int buttonHeight = 1; @@ -170,6 +171,7 @@ int listBox(const char * text, int height, int width, poptContext optCon, int maxTagWidth = 0; int maxTextWidth = 0; int scrollFlag; + int w; struct { const char * text; const char * tag; @@ -193,10 +195,10 @@ int listBox(const char * text, int height, int width, poptContext optCon, } else itemInfo[numItems].text = ""; - if (strlen(itemInfo[numItems].text) > (unsigned int)maxTextWidth) - maxTextWidth = strlen(itemInfo[numItems].text); - if (strlen(itemInfo[numItems].tag) > (unsigned int)maxTagWidth) - maxTagWidth = strlen(itemInfo[numItems].tag); + if ((w = get_east_asia_str_width (NULL, itemInfo[numItems].text, 0)) > (unsigned int)maxTextWidth) + maxTextWidth = w; + if ((w = get_east_asia_str_width (NULL, itemInfo[numItems].tag, 0)) > (unsigned int)maxTagWidth) + maxTagWidth = w; numItems++; } @@ -254,6 +256,7 @@ int checkList(const char * text, int height, int width, poptContext optCon, char buf[80], format[20]; int maxWidth = 0; int top; + int w; struct { const char * text; const char * tag; @@ -287,8 +290,8 @@ int checkList(const char * text, int height, int width, poptContext optCon, else cbStates[numBoxes] = ' '; - if (strlen(cbInfo[numBoxes].tag) > (unsigned int)maxWidth) - maxWidth = strlen(cbInfo[numBoxes].tag); + if ((w = get_east_asia_str_width (NULL, cbInfo[numBoxes].tag, 0)) > (unsigned int)maxWidth) + maxWidth = w; numBoxes++; } diff --git a/eawidth.c b/eawidth.c new file mode 100644 index 0000000..d9a0540 --- /dev/null +++ b/eawidth.c @@ -0,0 +1,459 @@ +/* #define TEST_GET_EAST_ASIA_STR_WIDTH 1 */ + +#include +#include +#include +#include +#include + +#include "eawidth.h" + +/* + * If the amount of columns the cursor advances on a TAB character depends + * on the current position, set this to a negative number (i.e. -8 for tab + * stops every eight columns. If static, set to a positive number. Zero if + * tabs are ignored. + */ +static const int tab_width = -8; + +typedef struct { + unsigned short start, end; + east_asia_type type; +} eaw_db_type; + +static const eaw_db_type eaw_db[] = { + { 0x0020,0x007E,narrow }, + { 0x00A1,0x00A1,ambiguous }, /*INVERTED EXCLAMATION MARK*/ + { 0x00A2,0x00A3,narrow }, + { 0x00A4,0x00A4,ambiguous }, /*CURRENCY SIGN*/ + { 0x00A5,0x00A6,narrow }, + { 0x00A7,0x00A8,ambiguous }, + { 0x00AA,0x00AA,ambiguous }, /*FEMININE ORDINAL INDICATOR*/ + { 0x00AC,0x00AC,narrow }, /*NOT SIGN*/ + { 0x00AD,0x00AD,ambiguous }, /*SOFT HYPHEN*/ + { 0x00AF,0x00AF,narrow }, /*MACRON*/ + { 0x00B0,0x00B4,ambiguous }, + { 0x00B6,0x00BA,ambiguous }, + { 0x00BC,0x00BF,ambiguous }, + { 0x00C6,0x00C6,ambiguous }, /*LATIN CAPITAL LETTER AE*/ + { 0x00D0,0x00D0,ambiguous }, /*LATIN CAPITAL LETTER ETH*/ + { 0x00D7,0x00D8,ambiguous }, + { 0x00DE,0x00E1,ambiguous }, + { 0x00E6,0x00E6,ambiguous }, /*LATIN SMALL LETTER AE*/ + { 0x00E8,0x00EA,ambiguous }, + { 0x00EC,0x00ED,ambiguous }, + { 0x00F0,0x00F0,ambiguous }, /*LATIN SMALL LETTER ETH*/ + { 0x00F2,0x00F3,ambiguous }, + { 0x00F7,0x00FA,ambiguous }, + { 0x00FC,0x00FC,ambiguous }, /*LATIN SMALL LETTER U WITH DIAERESIS*/ + { 0x00FE,0x00FE,ambiguous }, /*LATIN SMALL LETTER THORN*/ + { 0x0101,0x0101,ambiguous }, /*LATIN SMALL LETTER A WITH MACRON*/ + { 0x0111,0x0111,ambiguous }, /*LATIN SMALL LETTER D WITH STROKE*/ + { 0x0113,0x0113,ambiguous }, /*LATIN SMALL LETTER E WITH MACRON*/ + { 0x011B,0x011B,ambiguous }, /*LATIN SMALL LETTER E WITH CARON*/ + { 0x0126,0x0127,ambiguous }, + { 0x012B,0x012B,ambiguous }, /*LATIN SMALL LETTER I WITH MACRON*/ + { 0x0131,0x0133,ambiguous }, + { 0x0138,0x0138,ambiguous }, /*LATIN SMALL LETTER KRA*/ + { 0x013F,0x0142,ambiguous }, + { 0x0144,0x0144,ambiguous }, /*LATIN SMALL LETTER N WITH ACUTE*/ + { 0x0148,0x014A,ambiguous }, + { 0x014D,0x014D,ambiguous }, /*LATIN SMALL LETTER O WITH MACRON*/ + { 0x0152,0x0153,ambiguous }, + { 0x0166,0x0167,ambiguous }, + { 0x016B,0x016B,ambiguous }, /*LATIN SMALL LETTER U WITH MACRON*/ + { 0x01CE,0x01CE,ambiguous }, /*LATIN SMALL LETTER A WITH CARON*/ + { 0x01D0,0x01D0,ambiguous }, /*LATIN SMALL LETTER I WITH CARON*/ + { 0x01D2,0x01D2,ambiguous }, /*LATIN SMALL LETTER O WITH CARON*/ + { 0x01D4,0x01D4,ambiguous }, /*LATIN SMALL LETTER U WITH CARON*/ + { 0x01D6,0x01D6,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+MACRON*/ + { 0x01D8,0x01D8,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+ACUTE*/ + { 0x01DA,0x01DA,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+CARON*/ + { 0x01DC,0x01DC,ambiguous }, /*LATIN SMALL LETTER U W/DIAERESIS+GRAVE*/ + { 0x0251,0x0251,ambiguous }, /*LATIN SMALL LETTER ALPHA*/ + { 0x0261,0x0261,ambiguous }, /*LATIN SMALL LETTER SCRIPT G*/ + { 0x02C7,0x02C7,ambiguous }, /*CARON*/ + { 0x02C9,0x02CB,ambiguous }, + { 0x02CD,0x02CD,ambiguous }, /*MODIFIER LETTER LOW MACRON*/ + { 0x02D0,0x02D0,ambiguous }, /*MODIFIER LETTER TRIANGULAR COLON*/ + { 0x02D8,0x02DB,ambiguous }, + { 0x02DD,0x02DD,ambiguous }, /*DOUBLE ACUTE ACCENT*/ + { 0x0300,0x0362,ambiguous }, + { 0x0391,0x03A9,ambiguous }, + { 0x03B1,0x03C1,ambiguous }, + { 0x03C3,0x03C9,ambiguous }, + { 0x0401,0x0401,ambiguous }, /*CYRILLIC CAPITAL LETTER IO*/ + { 0x0410,0x044F,ambiguous }, + { 0x0451,0x0451,ambiguous }, /*CYRILLIC SMALL LETTER IO*/ + { 0x1100,0x115F,wide }, + { 0x2010,0x2010,ambiguous }, /*HYPHEN*/ + { 0x2013,0x2016,ambiguous }, + { 0x2018,0x2019,ambiguous }, + { 0x201C,0x201D,ambiguous }, + { 0x2020,0x2021,ambiguous }, + { 0x2025,0x2027,ambiguous }, + { 0x2030,0x2030,ambiguous }, /*PER MILLE SIGN*/ + { 0x2032,0x2033,ambiguous }, + { 0x2035,0x2035,ambiguous }, /*REVERSED PRIME*/ + { 0x203B,0x203B,ambiguous }, /*REFERENCE MARK*/ + { 0x2074,0x2074,ambiguous }, /*SUPERSCRIPT FOUR*/ + { 0x207F,0x207F,ambiguous }, /*SUPERSCRIPT LATIN SMALL LETTER N*/ + { 0x2081,0x2084,ambiguous }, + { 0x20A9,0x20A9,half_width }, /*WON SIGN*/ + { 0x20AC,0x20AC,ambiguous }, /*EURO SIGN*/ + { 0x2103,0x2103,ambiguous }, /*DEGREE CELSIUS*/ + { 0x2105,0x2105,ambiguous }, /*CARE OF*/ + { 0x2109,0x2109,ambiguous }, /*DEGREE FAHRENHEIT*/ + { 0x2113,0x2113,ambiguous }, /*SCRIPT SMALL L*/ + { 0x2121,0x2122,ambiguous }, + { 0x2126,0x2126,ambiguous }, /*OHM SIGN*/ + { 0x212B,0x212B,ambiguous }, /*ANGSTROM SIGN*/ + { 0x2154,0x2155,ambiguous }, + { 0x215B,0x215B,ambiguous }, /*VULGAR FRACTION ONE EIGHTH*/ + { 0x215E,0x215E,ambiguous }, /*VULGAR FRACTION SEVEN EIGHTHS*/ + { 0x2160,0x216B,ambiguous }, + { 0x2170,0x2179,ambiguous }, + { 0x2190,0x2199,ambiguous }, + { 0x21D2,0x21D2,ambiguous }, /*RIGHTWARDS DOUBLE ARROW*/ + { 0x21D4,0x21D4,ambiguous }, /*LEFT RIGHT DOUBLE ARROW*/ + { 0x2200,0x2200,ambiguous }, /*FOR ALL*/ + { 0x2202,0x2203,ambiguous }, + { 0x2207,0x2208,ambiguous }, + { 0x220B,0x220B,ambiguous }, /*CONTAINS AS MEMBER*/ + { 0x220F,0x220F,ambiguous }, /*N-ARY PRODUCT*/ + { 0x2211,0x2211,ambiguous }, /*N-ARY SUMMATION*/ + { 0x2215,0x2215,ambiguous }, /*DIVISION SLASH*/ + { 0x221A,0x221A,ambiguous }, /*SQUARE ROOT*/ + { 0x221D,0x2220,ambiguous }, + { 0x2223,0x2223,ambiguous }, /*DIVIDES*/ + { 0x2225,0x2225,ambiguous }, /*PARALLEL TO*/ + { 0x2227,0x222C,ambiguous }, + { 0x222E,0x222E,ambiguous }, /*CONTOUR INTEGRAL*/ + { 0x2234,0x2237,ambiguous }, + { 0x223C,0x223D,ambiguous }, + { 0x2248,0x2248,ambiguous }, /*ALMOST EQUAL TO*/ + { 0x224C,0x224C,ambiguous }, /*ALL EQUAL TO*/ + { 0x2252,0x2252,ambiguous }, /*APPROXIMATELY EQUAL TO OR THE IMAGE OF*/ + { 0x2260,0x2261,ambiguous }, + { 0x2264,0x2267,ambiguous }, + { 0x226A,0x226B,ambiguous }, + { 0x226E,0x226F,ambiguous }, + { 0x2282,0x2283,ambiguous }, + { 0x2286,0x2287,ambiguous }, + { 0x2295,0x2295,ambiguous }, /*CIRCLED PLUS*/ + { 0x2299,0x2299,ambiguous }, /*CIRCLED DOT OPERATOR*/ + { 0x22A5,0x22A5,ambiguous }, /*UP TACK*/ + { 0x22BF,0x22BF,ambiguous }, /*RIGHT TRIANGLE*/ + { 0x2312,0x2312,ambiguous }, /*ARC*/ + { 0x2460,0x24BF,ambiguous }, + { 0x24D0,0x24E9,ambiguous }, + { 0x2500,0x254B,ambiguous }, + { 0x2550,0x2574,ambiguous }, + { 0x2580,0x258F,ambiguous }, + { 0x2592,0x25A1,ambiguous }, + { 0x25A3,0x25A9,ambiguous }, + { 0x25B2,0x25B3,ambiguous }, + { 0x25B6,0x25B7,ambiguous }, + { 0x25BC,0x25BD,ambiguous }, + { 0x25C0,0x25C1,ambiguous }, + { 0x25C6,0x25C8,ambiguous }, + { 0x25CB,0x25CB,ambiguous }, /*WHITE CIRCLE*/ + { 0x25CE,0x25D1,ambiguous }, + { 0x25E2,0x25E5,ambiguous }, + { 0x25EF,0x25EF,ambiguous }, /*LARGE CIRCLE*/ + { 0x2605,0x2606,ambiguous }, + { 0x2609,0x2609,ambiguous }, /*SUN*/ + { 0x260E,0x260F,ambiguous }, + { 0x261C,0x261C,ambiguous }, /*WHITE LEFT POINTING INDEX*/ + { 0x261E,0x261E,ambiguous }, /*WHITE RIGHT POINTING INDEX*/ + { 0x2640,0x2640,ambiguous }, /*FEMALE SIGN*/ + { 0x2642,0x2642,ambiguous }, /*MALE SIGN*/ + { 0x2660,0x2661,ambiguous }, + { 0x2663,0x2665,ambiguous }, + { 0x2667,0x266A,ambiguous }, + { 0x266C,0x266D,ambiguous }, + { 0x266F,0x266F,ambiguous }, /*MUSIC SHARP SIGN*/ + { 0x2E80,0x3009,wide }, + { 0x300A,0x300B,ambiguous }, + { 0x300C,0x3019,wide }, + { 0x301A,0x301B,ambiguous }, + { 0x301C,0x303E,wide }, + { 0x3041,0xD7A3,wide }, + { 0xE000,0xF8FF,ambiguous }, + { 0xF900,0xFA2D,wide }, + { 0xFE30,0xFE6B,wide }, + { 0xFF01,0xFF5E,full_width }, + { 0xFF61,0xFFDC,half_width }, + { 0xFFE0,0xFFE6,full_width }, + { 0xFFE8,0xFFEE,half_width }, +}; + +static int +eaw_db_cmp (const void *ck, const void *ce) { + const eaw_db_type *key = ck, *element = ce; + + assert(key != NULL); + assert(element != NULL); + if (key->start < element->start) return -1; + else if (key->end > element->end) return 1; + return 0; +} + +static int +is_cjk_locale (const char *locale_name) { + static const char c[] = "zh"; /* Chinese */ + static const char j[] = "ja"; /* Japanese */ + static const char k[] = "ko"; /* Korean */ + + if (NULL == locale_name) return 0; + if (strncmp(locale_name, c, sizeof(c)) == 0) return 1; + if (strncmp(locale_name, j, sizeof(j)) == 0) return 1; + if (strncmp(locale_name, k, sizeof(k)) == 0) return 1; + return 0; +} + +east_asia_type +get_east_asia_type (wchar_t unicode) { + assert(0xFFFF != unicode && 0xFFFE != unicode); + + if (unicode > 0xFFFF) { + + /* + * Plane 2 is intended for CJK ideographs + */ + if (unicode >= 0x20000 && unicode <= 0x2FFFD) return wide; + return ambiguous; + } + else { + eaw_db_type *pos, key; + size_t n; + + n = sizeof(eaw_db) / sizeof(eaw_db_type); + key.start = key.end = (unsigned short) unicode; + pos = bsearch(&key, eaw_db, n, sizeof(eaw_db_type), eaw_db_cmp); + if (NULL != pos) return pos->type; + } + return neutral; +} + +int +east_asia_mblen (const char *locale_name, const char *s, size_t n, int x) +{ + wchar_t *wcs, *p; + int width = 0; + + if (NULL == s) s = ""; + + /* + * Getting the locale name via setlocale() is expensive, so we prefer + * to have it passed to us. + */ + if (NULL == locale_name) { + locale_name = setlocale(LC_CTYPE, NULL); + if (NULL == locale_name) return INT_MAX; + } + + wcs = (wchar_t *) calloc(n, sizeof(wchar_t)); + if (NULL == wcs) return INT_MAX; + +#if defined __GLIBC__ && !__GLIBC_PREREQ(2,2) +#warning wide character support is broken. Glibc 2.2 or better needed. +#endif + + if ((size_t) -1 == mbstowcs(wcs, s, n)) return INT_MAX; + + switch (get_east_asia_type(*wcs)) { + case neutral: + + /* + * Put characters that print nothing here. + * + * XXX: Yes, I know there are a lot more than this in ISO-10646, but + * this function is intended to calculate the width of strings for + * fixed width terminals displaying legacy CJK character sets. + * State-of-the-art Unicode handling terminals probably won't need + * this function anyway. + */ + if (0x0000 == *wcs) break; /* NULL */ + if (0x0007 == *wcs) break; /* BELL */ + + /* FIXME: there will probably be ASCII chars after the escape + * code, which will be counted as part of the width even though they + * aren't displayed. + */ + if (0x001B == *wcs) break; /* ESC */ + if (0xFEFF == *wcs) break; /* ZWNBSP aka BOM (magic, signature) */ + + /* + * Special characters go here + */ + if (0x0008 == *wcs) { /* BACKSPACE */ + width = -1; + break; + } + if (0x0009 == *wcs) { /* TAB */ + if (tab_width < 0) width = x % abs(tab_width); + else width = tab_width; + break; + } + + /*FALLTHRU*/ + case narrow: + case half_width: + width = 1; + break; + case wide: + case full_width: + width = 2; + break; + case ambiguous: + width = is_cjk_locale(locale_name) ? 2 : 1; + break; + default: + width = INT_MAX; + } + free(wcs); + return width; +} + +int +get_east_asia_str_n_width (const char *locale_name, const char *s, size_t n, int x) +{ + int total_width = 0; + wchar_t *wcs, *p; + + if (NULL == s) s = ""; + + /* + * Getting the locale name via setlocale() is expensive, so we prefer + * to have it passed to us. + */ + if (NULL == locale_name) { + locale_name = setlocale(LC_CTYPE, NULL); + if (NULL == locale_name) return INT_MAX; + } + + wcs = (wchar_t *) calloc(n, sizeof(wchar_t)); + if (NULL == wcs) return INT_MAX; + +#if defined __GLIBC__ && !__GLIBC_PREREQ(2,2) +#warning wide character support is broken. Glibc 2.2 or better needed. +#endif + + if ((size_t) -1 == mbstowcs(wcs, s, n)) return INT_MAX; + + for (p = wcs; L'\0' != *p; p++) { + int width = 0; + + switch (get_east_asia_type(*p)) { + case neutral: + + /* + * Put characters that print nothing here. + * + * XXX: Yes, I know there are a lot more than this in ISO-10646, but + * this function is intended to calculate the width of strings for + * fixed width terminals displaying legacy CJK character sets. + * State-of-the-art Unicode handling terminals probably won't need + * this function anyway. + */ + if (0x0000 == *p) break; /* NULL */ + if (0x0007 == *p) break; /* BELL */ + + /* FIXME: there will probably be ASCII chars after the escape + * code, which will be counted as part of the width even though they + * aren't displayed. + */ + if (0x001B == *p) break; /* ESC */ + if (0xFEFF == *p) break; /* ZWNBSP aka BOM (magic, signature) */ + + /* + * Special characters go here + */ + if (0x0008 == *p) { /* BACKSPACE */ + width = -1; + break; + } + if (0x0009 == *p) { /* TAB */ + if (tab_width < 0) width = x % abs(tab_width); + else width = tab_width; + break; + } + + /*FALLTHRU*/ + case narrow: + case half_width: + width = 1; + break; + case wide: + case full_width: + width = 2; + break; + case ambiguous: + width = is_cjk_locale(locale_name) ? 2 : 1; + break; + default: abort(); /* Doh! */ + } + x += width; + total_width += width; + } + free(wcs); + return total_width; +} + +int +get_east_asia_str_width (const char *locale_name, const char *s, int x) { + size_t n; + + n = strlen(s) + 1; + return get_east_asia_str_n_width (locale_name, s, n, x); +} + +#if TEST_GET_EAST_ASIA_STR_WIDTH + +#include + +int +main (int argc, char *argv[]) { + int i; + char *lc; + const char *fmt = "word #%d ('%s') length is %zu, width is %u\n"; + + lc = setlocale(LC_CTYPE, ""); + if (NULL == lc) { + fputs("couldn't set the default locale for LC_CTYPE\n", stderr); + exit(EXIT_FAILURE); + } + if (printf("character type locale is '%s'\n", lc) < 0) { + perror(NULL); + exit(EXIT_FAILURE); + } + for (i = 1; argc < 2 || i < argc; i++) { + char *s; + size_t length; + unsigned width; + + if (argc < 2) { + if (scanf("%as", &s) < 1 && ferror(stdin)) { + perror(NULL); + exit(EXIT_FAILURE); + } + else if (feof(stdin)) break; + } + else s = strdup(argv[(size_t) i]); + if (NULL == s) { + perror(NULL); + exit(EXIT_FAILURE); + } + length = strlen(s); + width = get_east_asia_str_width(lc, s, 0); + if (printf(fmt, i, s, length, width) < 0) { + perror(NULL); + exit(EXIT_FAILURE); + } + free(s); + } + return 0; +} + +#endif diff --git a/eawidth.h b/eawidth.h new file mode 100644 index 0000000..668e35a --- /dev/null +++ b/eawidth.h @@ -0,0 +1,37 @@ +#include + +typedef enum { + ambiguous, neutral, narrow, half_width, wide, full_width +} east_asia_type; + +/** + * get_east_asia_type returns the character classification of a given + * Unicode character according to Unicode Technical + * Report 11. + * + * @param unicode the Unicode/ISO-10646 character to get the type of + * @return the classification of the character, according to + * UAX#11 data. + */ +east_asia_type get_east_asia_type(wchar_t unicode); + +/** + * get_east_asia_width measures the amount of space a multibyte string + * takes an a traditional fixed width terminal such + * as xterm in character cells. + * + * @param locale_name used to determine the if character width should + * be interpreted as CJK or Asian in the case of + * Greek and Cyrillic characters. If NULL, it will + * use setlocale() to determine the LC_CTYPE setting. + * @param s the multibyte string to measure the width of. + * @param x the current cursor position, used to handle the + * calculation of tab widths. 0 is the first column. + * @return the number of fixed width character cells the string + * takes up. If the string is an illegal multibyte + * string, or a memory error or other occurs, INT_MAX + * is returned and errno is set. + */ +int get_east_asia_str_width(const char *locale_name, const char *s, int x); +int get_east_asia_str_n_width (const char *locale_name, const char *s, size_t n, int x); +int east_asia_mblen (const char *locale_name, const char *s, size_t n, int x); diff --git a/entry.c b/entry.c index 154edba..b597879 100644 --- a/entry.c +++ b/entry.c @@ -5,6 +5,7 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" struct entry { int flags; @@ -58,6 +59,7 @@ newtComponent newtEntry(int left, int top, const char * initialValue, int width, char ** resultPtr, int flags) { newtComponent co; struct entry * en; + int w; co = malloc(sizeof(*co)); en = malloc(sizeof(struct entry)); @@ -69,6 +71,7 @@ newtComponent newtEntry(int left, int top, const char * initialValue, int width, co->width = width; co->isMapped = 0; co->callback = NULL; + co->isLabel = 0; co->ops = &entryOps; @@ -84,8 +87,8 @@ newtComponent newtEntry(int left, int top, const char * initialValue, int width, else co->takesFocus = 0; - if (initialValue && strlen(initialValue) > (unsigned int)width) { - en->bufAlloced = strlen(initialValue) + 1; + if (initialValue && (w = get_east_asia_str_width (NULL, initialValue, 0)) > (unsigned int)width) { + en->bufAlloced = w + 1; } en->buf = malloc(en->bufAlloced); en->resultPtr = resultPtr; diff --git a/form.c b/form.c index 825fd46..3140213 100644 --- a/form.c +++ b/form.c @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef USE_GPM #include @@ -23,6 +24,12 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" + +struct label { + char *text; + int length; +}; #ifdef USE_GPM /*....................................... The connection data structure */ @@ -442,6 +449,7 @@ newtComponent newtForm(newtComponent vertBar, void * help, int flags) { co->top = -1; co->left = -1; co->isMapped = 0; + co->isLabel = 0; co->takesFocus = 0; /* we may have 0 components */ co->ops = &formOps; @@ -528,6 +536,8 @@ void newtFormSetWidth(newtComponent co, int width) { void newtFormAddComponent(newtComponent co, newtComponent newco) { struct form * form = co->data; + int i, j, k, l, xmap, ymap, x, y; + char *map, *s = NULL; co->takesFocus = 1; @@ -546,6 +556,76 @@ void newtFormAddComponent(newtComponent co, newtComponent newco) { form->currComp = form->numComps; form->numComps++; + + /* check multibyte char broken */ + /* create text map */ + xmap = 0; + ymap = 0; + for ( i = 0; i < form->numComps; i++ ) { + if ((form->elements[i].co->top + form->elements[i].co->height) > ymap ) + ymap = form->elements[i].co->top + form->elements[i].co->height; + if ((form->elements[i].co->left + form->elements[i].co->width) > xmap ) + xmap = form->elements[i].co->left + form->elements[i].co->width; + } + map = (char *)calloc ((xmap+1) * (ymap+1), sizeof (char)); + +#define MAP(x,y) *(map + ((x)*ymap + (y))) + + /* create non-label components map */ + for ( i = 0; i < form->numComps; i++ ) { + if ( form->elements[i].co->isLabel == 0 && form->elements[i].co->left >= 0 && form->elements[i].co->top >= 0 ) { + for ( x = 0; x < form->elements[i].co->width; x++ ) { + for ( y = 0; y < form->elements[i].co->height; y++ ) + MAP (form->elements[i].co->left+x, form->elements[i].co->top+y) = 1; + } + } + } + + /* check label overlap */ + for ( i = 0; i < form->numComps; i++ ) { + if ( form->elements[i].co->isLabel != 0 ) { + y = form->elements[i].co->top; + l = form->elements[i].co->left; + if ( y >= 0 && l >= 0 ) { + for ( j = 0; j < form->elements[i].co->width; j++ ) { + if ( MAP (j+l, y) != 0 ) { + if ( MAP (j+l, y) != 1 ) { + /* if label is here, move label */ + while ( MAP (form->elements[i].co->left, y) != 0 ) + form->elements[i].co->left++; + } else { + /* if label is overlapping, cut label */ + if ((j+l) == form->elements[i].co->left ) { + free (((struct label *)form->elements[i].co->data)->text); + ((struct label *)form->elements[i].co->data)->text = malloc (1); + ((struct label *)form->elements[i].co->data)->text[0] = 0; + ((struct label *)form->elements[i].co->data)->length = 0; + form->elements[i].co->width = 0; + } else { + x = 0; + while (x < j) { + s = &((struct label *)form->elements[i].co->data)->text[x]; + k = east_asia_mblen (NULL, s, strlen (s), 0); + if ( k <= 0 ) break; + else { + if ((x+k) < j ) x += k; + else break; + } + } + s = ((struct label *)form->elements[i].co->data)->text; + ((struct label *)form->elements[i].co->data)->text = realloc(s, x+1); + ((struct label *)form->elements[i].co->data)->text[x] = 0; + ((struct label *)form->elements[i].co->data)->length = strlen (s); + form->elements[i].co->width = get_east_asia_str_width (NULL, s, 0); + } + } + } else + MAP (j+l, y) = 2; + } + } + } + } + free (map); } void newtFormAddComponents(newtComponent co, ...) { @@ -802,6 +882,7 @@ void newtFormDestroy(newtComponent co) { free(form->elements); free(form); free(co); + newtResizeScreen(1); } newtComponent newtRunForm(newtComponent co) { diff --git a/grid.c b/grid.c index 4330113..391dd07 100644 --- a/grid.c +++ b/grid.c @@ -4,6 +4,7 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" struct gridField { enum newtGridElement type; @@ -241,12 +242,13 @@ void newtGridGetSize(newtGrid grid, int * width, int * height) { } void newtGridWrappedWindow(newtGrid grid, char * title) { - int width, height, offset = 0; + int width, height, offset = 0, w; newtGridGetSize(grid, &width, &height); - if (width < strlen(title) + 2) { - offset = ((strlen(title) + 2) - width) / 2; - width = strlen(title) + 2; + w = get_east_asia_str_width (NULL, title, 0); + if (width < (w + 2)) { + offset = ((w + 2) - width) / 2; + width = w + 2; } newtCenteredWindow(width + 2, height + 2, title); newtGridPlace(grid, 1 + offset, 1); diff --git a/label.c b/label.c index f1a9ceb..1c4ecfd 100644 --- a/label.c +++ b/label.c @@ -4,6 +4,7 @@ #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" struct label { char * text; @@ -32,10 +33,11 @@ newtComponent newtLabel(int left, int top, const char * text) { co->ops = &labelOps; co->height = 1; - co->width = strlen(text); + co->width = get_east_asia_str_width (NULL, text, 0); co->top = top; co->left = left; co->takesFocus = 0; + co->isLabel = 1; la->length = strlen(text); la->text = strdup(text); @@ -47,7 +49,7 @@ void newtLabelSetText(newtComponent co, const char * text) { int newLength; struct label * la = co->data; - newLength = strlen(text); + newLength = get_east_asia_str_width (NULL, text, 0); if (newLength <= la->length) { memset(la->text, ' ', la->length); memcpy(la->text, text, newLength); diff --git a/listbox.c b/listbox.c index ef276ae..4f951b6 100644 --- a/listbox.c +++ b/listbox.c @@ -11,7 +11,7 @@ #include "newt.h" #include "newt_pr.h" - +#include "eawidth.h" /* Linked list of items in the listbox */ struct items { @@ -133,6 +133,7 @@ newtComponent newtListbox(int left, int top, int height, int flags) { co->ops = &listboxOps; co->takesFocus = 1; co->callback = NULL; + co->isLabel = 0; updateWidth(co, li, 5); @@ -285,7 +286,7 @@ void ** newtListboxGetSelection(newtComponent co, int *numitems) void newtListboxSetEntry(newtComponent co, int num, const char * text) { struct listbox * li = co->data; - int i; + int i, w; struct items *item; for(i = 0, item = li->boxItems; item != NULL && i < num; @@ -297,8 +298,9 @@ void newtListboxSetEntry(newtComponent co, int num, const char * text) { free(item->text); item->text = strdup(text); } - if (li->userHasSetWidth == 0 && strlen(text) > li->curWidth) { - updateWidth(co, li, strlen(text)); + w = get_east_asia_str_width (NULL, text, 0); + if (li->userHasSetWidth == 0 && w > li->curWidth) { + updateWidth(co, li, w); } if (num >= li->startShowItem && num <= li->startShowItem + co->height) @@ -320,6 +322,7 @@ int newtListboxAppendEntry(newtComponent co, const char * text, const void * data) { struct listbox * li = co->data; struct items *item; + int w; if(li->boxItems) { for (item = li->boxItems; item->next != NULL; item = item->next); @@ -329,8 +332,9 @@ int newtListboxAppendEntry(newtComponent co, const char * text, item = li->boxItems = malloc(sizeof(struct items)); } - if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth)) - updateWidth(co, li, strlen(text)); + w = get_east_asia_str_width (NULL, text, 0); + if (!li->userHasSetWidth && text && (w > li->curWidth)) + updateWidth(co, li, w); item->text = strdup(text); item->data = data; item->next = NULL; item->isSelected = 0; @@ -346,6 +350,7 @@ int newtListboxInsertEntry(newtComponent co, const char * text, const void * data, void * key) { struct listbox * li = co->data; struct items *item, *t; + int w; if (li->boxItems) { if (key) { @@ -369,8 +374,9 @@ int newtListboxInsertEntry(newtComponent co, const char * text, item->next = NULL; } - if (!li->userHasSetWidth && text && (strlen(text) > li->curWidth)) - updateWidth(co, li, strlen(text)); + w = get_east_asia_str_width (NULL, text, 0); + if (!li->userHasSetWidth && text && (w > li->curWidth)) + updateWidth(co, li, w); item->text = strdup(text?text:"(null)"); item->data = data; item->isSelected = 0; @@ -417,7 +423,7 @@ int newtListboxDeleteEntry(newtComponent co, void * key) { if (!li->userHasSetWidth) { widest = 0; for (item = li->boxItems; item != NULL; item = item->next) - if ((t = strlen(item->text)) > widest) widest = t; + if ((t = get_east_asia_str_width (NULL, item->text, 0)) > widest) widest = t; } if (li->currItem >= num) diff --git a/newt.c b/newt.c index f919c33..0c4d6ff 100644 --- a/newt.c +++ b/newt.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "newt.h" #include "newt_pr.h" @@ -190,6 +191,9 @@ void newtResizeScreen(int redraw) { int newtInit(void) { char * MonoValue, * MonoEnv = "NEWT_MONO"; + /* initialize locale */ + setlocale (LC_ALL, ""); + /* use the version variable just to be sure it gets included */ strlen(version); diff --git a/newt_pr.h b/newt_pr.h index 76f5e2f..77f1458 100644 --- a/newt_pr.h +++ b/newt_pr.h @@ -33,6 +33,7 @@ struct newtComponent_struct { int top, left; int takesFocus; int isMapped; + int isLabel; struct componentOps * ops; diff --git a/scale.c b/scale.c index 11c72cc..826a0f8 100644 --- a/scale.c +++ b/scale.c @@ -35,6 +35,7 @@ newtComponent newtScale(int left, int top, int width, long long fullValue) { co->top = top; co->left = left; co->takesFocus = 0; + co->isLabel = 0; sc->fullValue = fullValue; sc->charsSet = 0; diff --git a/scrollbar.c b/scrollbar.c index cb4bc27..7e3b8cb 100644 --- a/scrollbar.c +++ b/scrollbar.c @@ -64,6 +64,7 @@ newtComponent newtVerticalScrollbar(int left, int top, int height, co->height = height; co->width = 1; co->takesFocus = 0; + co->isLabel = 0; return co; } diff --git a/test-j.c b/test-j.c new file mode 100644 index 0000000..0810182 --- /dev/null +++ b/test-j.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +#include "newt.h" + +struct callbackInfo { + newtComponent en; + char * state; +}; + +void disableCallback(newtComponent co, void * data) { + struct callbackInfo * cbi = data; + + if (*cbi->state == ' ') { + newtEntrySetFlags(cbi->en, NEWT_FLAG_DISABLED, NEWT_FLAGS_RESET); + } else { + newtEntrySetFlags(cbi->en, NEWT_FLAG_DISABLED, NEWT_FLAGS_SET); + } + + newtRefresh(); +} + +void suspend(void * d) { + newtSuspend(); + raise(SIGTSTP); + newtResume(); +} + +int main(void) { + newtComponent b1, b2, r1, r2, r3, e2, e3, l1, l2, l3, scale; + newtComponent lb, t, rsf, answer; + newtComponent cs[10]; + newtComponent f, chklist, e1; + struct callbackInfo cbis[3]; + char results[10]; + char * enr2, * enr3, * scaleVal; + void ** selectedList; + int i, numsel; + char buf[20]; + + newtInit(); + newtCls(); + + newtSetSuspendCallback(suspend, NULL); + + newtDrawRootText(0, 0, "Newt ¥Æ¥¹¥È¥×¥í¥°¥é¥à"); + newtPushHelpLine(NULL); + newtDrawRootText(-50, 0, "¥ë¡¼¥È¥Æ¥­¥¹¥È"); + + newtOpenWindow(2, 2, 30, 10, "£±ÈÖÌܤΥ¦¥£¥ó¥É¥¦"); + newtOpenWindow(10, 5, 65, 16, "¥¦¥£¥ó¥É¥¦£²"); + + f = newtForm(NULL, NULL, 0); + chklist = newtForm(NULL, NULL, 0); + + b1 = newtButton(3, 1, "½ªÎ»"); + b2 = newtButton(18, 1, "¹¹¿·"); + r1 = newtRadiobutton(20, 10, "ÁªÂò»è£±", 0, NULL); + r2 = newtRadiobutton(20, 11, "ÁªÂò»è£²", 1, r1); + r3 = newtRadiobutton(20, 12, "ÁªÂò»è£³", 0, r2); + rsf = newtForm(NULL, NULL, 0); + newtFormAddComponents(rsf, r1, r2, r3, NULL); + newtFormSetBackground(rsf, NEWT_COLORSET_CHECKBOX); + + for (i = 0; i < 10; i++) { + sprintf(buf, "¥Á¥§¥Ã¥¯ %d", i); + cs[i] = newtCheckbox(3, 10 + i, buf, ' ', NULL, &results[i]); + newtFormAddComponent(chklist, cs[i]); + } + + l1 = newtLabel(3, 6, "¥¹¥±¡¼¥ë:"); + l2 = newtLabel(3, 7, "¥¹¥¯¥í¡¼¥ë:"); + l3 = newtLabel(3, 8, "¥Ò¥É¥¥¥ó:"); + e1 = newtEntry(12, 6, "", 20, &scaleVal, 0); + e2 = newtEntry(12, 7, "ɸ½à", 20, &enr2, NEWT_FLAG_SCROLL); + e3 = newtEntry(12, 8, NULL, 20, &enr3, NEWT_FLAG_HIDDEN); + + cbis[0].state = &results[0]; + cbis[0].en = e1; + newtComponentAddCallback(cs[0], disableCallback, &cbis[0]); + + scale = newtScale(3, 14, 32, 100); + + newtFormSetHeight(chklist, 3); + + newtFormAddComponents(f, b1, b2, l1, l2, l3, e1, e2, e3, chklist, NULL); + newtFormAddComponents(f, rsf, scale, NULL); + + lb = newtListbox(45, 1, 6, NEWT_FLAG_MULTIPLE | NEWT_FLAG_BORDER | + NEWT_FLAG_SCROLL); + newtListboxAppendEntry(lb, "£±ÈÖÌÜ", (void *) 1); + newtListboxAppendEntry(lb, "£²ÈÖÌÜ", (void *) 2); + newtListboxAppendEntry(lb, "£³ÈÖÌÜ", (void *) 3); + newtListboxAppendEntry(lb, "£´ÈÖÌÜ", (void *) 4); + newtListboxAppendEntry(lb, "£¶ÈÖÌÜ", (void *) 6); + newtListboxAppendEntry(lb, "£·ÈÖÌÜ", (void *) 7); + newtListboxAppendEntry(lb, "£¸ÈÖÌÜ", (void *) 8); + newtListboxAppendEntry(lb, "£¹ÈÖÌÜ", (void *) 9); + newtListboxAppendEntry(lb, "£±£°ÈÖÌÜ", (void *) 10); + + newtListboxInsertEntry(lb, "£µÈÖÌÜ", (void *) 5, (void *) 4); + newtListboxInsertEntry(lb, "£±£±ÈÖÌÜ", (void *) 11, (void *) 10); + newtListboxDeleteEntry(lb, (void *) 11); + + t = newtTextbox(45, 10, 17, 5, NEWT_FLAG_WRAP); + newtTextboxSetText(t, "¤³¤ì¤Ï¥Æ¥­¥¹¥È¤Î¥µ¥ó¥×¥ë¤Ç¤¹¡£\nÀµ¾ï¤Ëɽ¼¨¤µ¤ì¤Æ¤¤¤Þ¤¹¤«¡©\n¤³¤ì¤ÏñÆÈ¹Ô¤Ç¤¹¡£\n¤³¤ì¤Ïɽ¼¨¤µ¤ì¤Æ¤Ï¤¤¤±¤Þ¤»¤ó"); + + newtFormAddComponents(f, lb, t, NULL); + newtRefresh(); + + do { + answer = newtRunForm(f); + + if (answer == b2) { + newtScaleSet(scale, atoi(scaleVal)); + newtRefresh(); + answer = NULL; + } + } while (!answer); + + scaleVal = strdup(scaleVal); + enr2 = strdup(enr2); + enr3 = strdup(enr3); + + selectedList = newtListboxGetSelection(lb, &numsel); + + newtFormDestroy(f); + + newtPopWindow(); + newtPopWindow(); + newtFinished(); + + printf("got string 1: %s\n", scaleVal); + printf("got string 2: %s\n", enr2); + printf("got string 3: %s\n", enr3); + + if(selectedList) { + printf("\nSelected listbox items:\n"); + for(i = 0; i < numsel; i++) + puts(selectedList[i]); + } + + return 0; +} diff --git a/testgrid-j.c b/testgrid-j.c new file mode 100644 index 0000000..2fda940 --- /dev/null +++ b/testgrid-j.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include + +#include "newt.h" + +int main(void) { + newtComponent b1, b2, b3, b4; + newtComponent answer, f, t; + newtGrid grid, subgrid; + char * flowedText; + int textWidth, textHeight, rc; + char * menuContents[] = { "°ì", "Æó", "»°", "»Í", "¸Þ", NULL }; + char * entries[10]; + struct newtWinEntry autoEntries[] = { + { "¥¨¥ó¥È¥ê", entries + 0, 0 }, + { "Ê̤Υ¨¥ó¥È¥ê", entries + 1, 0 }, + { "»°ÈÖÌܤΥ¨¥ó¥È¥ê", entries + 2, 0 }, + { "»ÍÈÖÌܤΥ¨¥ó¥È¥ê", entries + 3, 0 }, + { NULL, NULL, 0 } }; + + memset(entries, 0, sizeof(entries)); + + newtInit(); + newtCls(); + + b1 = newtCheckbox(-1, -1, "¥Æ¥¹¥È¤Î¤¿¤á¤Î¤«¤Ê¤êŤ¤¥Á¥§¥Ã¥¯¥Ü¥Ã¥¯¥¹", ' ', NULL, NULL); + b2 = newtButton(-1, -1, "Ê̤Υܥ¿¥ó"); + b3 = newtButton(-1, -1, "¤·¤«¤·¡¢¤·¤«¤·"); + b4 = newtButton(-1, -1, "¤·¤«¤·²¿¤À¤í¤¦¡©"); + + f = newtForm(NULL, NULL, 0); + + grid = newtCreateGrid(2, 2); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, b1, 0, 0, 0, 0, + NEWT_ANCHOR_RIGHT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, b2, 0, 0, 0, 0, 0, 0); + newtGridSetField(grid, 1, 0, NEWT_GRID_COMPONENT, b3, 0, 0, 0, 0, 0, 0); + newtGridSetField(grid, 1, 1, NEWT_GRID_COMPONENT, b4, 0, 0, 0, 0, 0, 0); + + + newtFormAddComponents(f, b1, b2, b3, b4, NULL); + + newtGridWrappedWindow(grid, "°ìÈÖÌܤΥ¦¥£¥ó¥É¥¦"); + newtGridFree(grid, 1); + + answer = newtRunForm(f); + + newtFormDestroy(f); + newtPopWindow(); + + flowedText = newtReflowText("¤³¤ì¤Ï¤«¤Ê¤ê¥Æ¥­¥¹¥È¤é¤·¤¤¤â¤Î¤Ç¤¹¡£40¥«¥é¥à" + "¤ÎŤµ¤Ç¡¢¥é¥Ã¥Ô¥ó¥°¤¬¹Ô¤ï¤ì¤Þ¤¹¡£" + "ÁÇÁᤤ¡¢Ãã¿§¤Î¸Ñ¤¬¤Î¤í¤Þ¤Ê¸¤¤òÈô¤Ó" + "±Û¤¨¤¿¤Î¤òÃΤäƤ뤫¤¤?\n\n" + "¾¤Ë¤ªÃΤ餻¤¹¤ë¤³¤È¤È¤·¤Æ¡¢Å¬Åö¤Ë²þ¹Ô¤ò¤¹¤ë" + "¤³¤È¤¬½ÅÍפǤ¹¡£", + 40, 5, 5, &textWidth, &textHeight); + t = newtTextbox(-1, -1, textWidth, textHeight, NEWT_FLAG_WRAP); + newtTextboxSetText(t, flowedText); + free(flowedText); + + + b1 = newtButton(-1, -1, "λ²ò"); + b2 = newtButton(-1, -1, "¥­¥ã¥ó¥»¥ë"); + + grid = newtCreateGrid(1, 2); + subgrid = newtCreateGrid(2, 1); + + newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT, b1, 0, 0, 0, 0, 0, 0); + newtGridSetField(subgrid, 1, 0, NEWT_GRID_COMPONENT, b2, 0, 0, 0, 0, 0, 0); + + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, t, 0, 0, 0, 1, 0, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, subgrid, 0, 0, 0, 0, 0, + NEWT_GRID_FLAG_GROWX); + newtGridWrappedWindow(grid, "Ê̤ÎÎã"); + newtGridDestroy(grid, 1); + + f = newtForm(NULL, NULL, 0); + newtFormAddComponents(f, b1, t, b2, NULL); + answer = newtRunForm(f); + + newtPopWindow(); + newtFormDestroy(f); + + newtWinMessage("¥·¥ó¥×¥ë", "λ²ò", "¤³¤ì¤Ï¥·¥ó¥×¥ë¤Ê¥á¥Ã¥»¡¼¥¸¥¦¥£¥ó¥É¥¦¤Ç¤¹"); + newtWinChoice("¥·¥ó¥×¥ë", "λ²ò", "¥­¥ã¥ó¥»¥ë", "¤³¤ì¤Ï¥·¥ó¥×¥ë¤ÊÁªÂò¥¦¥£¥ó¥É¥¦¤Ç¤¹"); + + textWidth = 0; + rc = newtWinMenu("¥Æ¥¹¥È¥á¥Ë¥å¡¼", "¤³¤ì¤Ï newtWinMenu() ¥³¡¼¥ë¤Î¥µ¥ó¥×¥ë" + "¤Ç¤¹¡£ ¥¹¥¯¥í¡¼¥ë¥Ð¡¼¤ÏɬÍפ˱þ¤¸¤Æ¤Ä¤¤¤¿¤ê¡¢ " + "¤Ä¤«¤Ê¤«¤Ã¤¿¤ê¤·¤Þ¤¹¡£", 50, 5, 5, 3, + menuContents, &textWidth, "λ²ò", "¥­¥ã¥ó¥»¥ë", NULL); + + rc = newtWinEntries("¥Æ¥­¥¹¥È newtWinEntries()", "¤³¤ì¤Ï newtWinEntries()" + "¥³¡¼¥ë¤Î¥µ¥ó¥×¥ë¤Ç¤¹¡£¤¿¤¤¤Ø¤ó´Êñ¤Ë¤¿¤¯¤µ¤ó¤ÎÆþÎϤò" + "°·¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£", 50, 5, 5, 20, autoEntries, "λ²ò", + "¥­¥ã¥ó¥»¥ë", NULL); + + newtFinished(); + + printf("rc = 0x%x item = %d\n", rc, textWidth); + + return 0; +} diff --git a/testtree-j.c b/testtree-j.c new file mode 100644 index 0000000..d871c2a --- /dev/null +++ b/testtree-j.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +#include "newt.h" + +int main(void) { + newtGrid grid; + newtComponent checktree; + newtComponent button; + newtComponent form; + newtComponent answer; + void ** result, **ptr; + int numselected, i, j; + int * list; + + newtInit(); + newtCls(); + + checktree = newtCheckboxTreeMulti(-1, -1, 10, " ab", NEWT_FLAG_SCROLL); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼", (void *) 2, 0, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "ËÜÅö¤ËËÜÅö¤ËŤ¤¥â¥Î", + (void *) 3, 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£µ", (void *) 5, + NEWT_FLAG_SELECTED, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£¶", (void *) 6, 0, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£·", (void *) 7, + NEWT_FLAG_SELECTED, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£¸", (void *) 8, 0, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£¹", (void *) 9, 0, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£±£°", (void *) 10, + NEWT_FLAG_SELECTED, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£±£±", (void *) 11, 0, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£±£²", (void *) 12, + NEWT_FLAG_SELECTED, + NEWT_ARG_APPEND, NEWT_ARG_LAST); + + newtCheckboxTreeAddItem(checktree, "¥«¥é¡¼", (void *) 1, 0, + 0, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "ÀÖ¿§", (void *) 100, 0, + 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "Çò¿§", (void *) 101, 0, + 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "ÀÄ¿§", (void *) 102, 0, + 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + + newtCheckboxTreeAddItem(checktree, "¥Ê¥ó¥Ð¡¼£´", (void *) 4, 0, + 3, NEWT_ARG_LAST); + + newtCheckboxTreeAddItem(checktree, "°ì·å¤Î¿ô»ú", (void *) 200, 0, + 1, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "°ì", (void *) 201, 0, + 1, 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "Æó", (void *) 202, 0, + 1, 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "»°", (void *) 203, 0, + 1, 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "»Í", (void *) 204, 0, + 1, 0, NEWT_ARG_APPEND, NEWT_ARG_LAST); + + newtCheckboxTreeAddItem(checktree, "Æó·å¤Î¿ô»ú", (void *) 300, 0, + 1, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "½½", (void *) 210, 0, + 1, 1, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "½½°ì", (void *) 211, 0, + 1, 1, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "½½Æó", (void *) 212, 0, + 1, 1, NEWT_ARG_APPEND, NEWT_ARG_LAST); + newtCheckboxTreeAddItem(checktree, "½½»°", (void *) 213, 0, + 1, 1, NEWT_ARG_APPEND, NEWT_ARG_LAST); + + button = newtButton(-1, -1, "½ªÎ»"); + + grid = newtCreateGrid(1, 2); + newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, checktree, 0, 0, 0, 1, + NEWT_ANCHOR_RIGHT, 0); + newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, button, 0, 0, 0, 0, + 0, 0); + + newtGridWrappedWindow(grid, "¥Á¥§¥Ã¥¯¥Ü¥Ã¥¯¥¹¥Ä¥ê¡¼¥Æ¥¹¥È"); + newtGridFree(grid, 1); + + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, checktree, button, NULL); + + answer = newtRunForm(form); + + newtFinished(); + + result = newtCheckboxTreeGetSelection(checktree, &numselected); + ptr = result; + if (!result || !numselected) + printf("none selected\n"); + else + printf("Current selection (all) (%d):\n", numselected); + for (i = 0; i < numselected; i++) { + j = (int) *ptr++; + printf("%d\n", j); + } + result = newtCheckboxTreeGetMultiSelection(checktree, &numselected, 'b'); + ptr = result; + if (!result || !numselected) + printf("none selected\n"); + else + printf("Current selection (b) (%d):\n",numselected); + for (i = 0; i < numselected; i++) { + j = (int) *ptr++; + printf("%d\n", j); + } + + if (result) + free(result); + + list = newtCheckboxTreeFindItem(checktree, (void *) 213); + printf("path:"); + for (i = 0; list && list[i] != NEWT_ARG_LAST; i++) + printf(" %d", list[i]); + printf("\n"); + + newtFormDestroy(form); + + return 0; +} diff --git a/textbox.c b/textbox.c index 2578738..0614fb6 100644 --- a/textbox.c +++ b/textbox.c @@ -1,10 +1,13 @@ #include #include +#include #include #include +#include #include "newt.h" #include "newt_pr.h" +#include "eawidth.h" struct textbox { char ** lines; @@ -94,6 +97,7 @@ newtComponent newtTextbox(int left, int top, int width, int height, int flags) { co->left = left; co->takesFocus = 0; co->width = width; + co->isLabel = 0; tb->doWrap = flags & NEWT_FLAG_WRAP; tb->numLines = 0; @@ -157,76 +161,88 @@ static void doReflow(const char * text, char ** resultPtr, int width, int howbad = 0; int height = 0; int kanji = 0; + int w, w2; + w = get_east_asia_str_width (NULL, text, 0); if (resultPtr) { - /* XXX I think this will work */ - result = malloc(strlen(text) + (strlen(text) / width) + 2); - *result = '\0'; + /* XXX I think this will work */ + result = malloc(w * 2 + 2); + *result = '\0'; } while (*text) { kanji = 0; - end = strchr(text, '\n'); - if (!end) - end = text + strlen(text); - - while (*text && text <= end) { - if (end - text < width) { - if (result) { - strncat(result, text, end - text); - strcat(result, "\n"); - height++; + end = strchr(text, '\n'); + if (!end) + end = text + strlen(text); + + while (*text && text <= end) { + char *ptr; + + ptr = (char *)malloc (sizeof (char)*((end-text)+1)); + strncpy (ptr, text, end - text); + *(ptr + (end-text)) = 0; + w2 = get_east_asia_str_width (NULL, ptr, 0); + free (ptr); + if (w2 < width) { + if (result) { + strncat(result, text, end - text); + strcat(result, "\n"); + } + height++; + + if (w2 < (width / 2)) + howbad += ((width / 2) - w2) / 2; + text = end; + if (*text) text++; + } else { + int is_space = 0, into_kanji = 0; + char *spcptr = NULL; + + chptr = text; + i = 0; + while ( i < (width - 1)) { + kanji = east_asia_mblen (NULL, chptr, end - chptr, 0); + if ( kanji == INT_MAX || kanji <= 0 ) kanji = 1; + else { + if ( kanji == 1 ) { + if ( is_space == 0 ) { + if (isspace(*chptr)) { + is_space = 1; + } + else if ( into_kanji == 1 ) { + if (!spcptr) spcptr = chptr; + is_space = 1; + } + } else { + if (!spcptr) spcptr = chptr; + else if (isspace(*chptr)) { + spcptr = NULL; + is_space = 1; + } + } + } else { + into_kanji = 1; + is_space = 0; + spcptr = NULL; + } + if ((i+kanji) < (width - 1)) i += kanji; + else break; + } + chptr = chptr + kanji; + } + if ( is_space != 0 && spcptr-text > 1 ) chptr = spcptr; + howbad += width - (chptr - text) + 1; + if ( result ) { + strncat (result, text, chptr - text); + strcat (result, "\n"); + } + height++; + text = chptr; + while (east_asia_mblen (NULL, text, end - text, 0) == 1 && isspace(*text)) text++; + } } - - if (end - text < (width / 2)) - howbad += ((width / 2) - (end - text)) / 2; - text = end; - if (*text) text++; - } else { - chptr = text; - kanji = 0; - for ( i = 0; i < width - 1; i++ ) { - if ( !iseuckanji(*chptr)) { - kanji = 0; - } else if ( kanji == 1 ) { - kanji = 2; - } else { - kanji = 1; - } - chptr++; - } - if (kanji == 0) { - while (chptr > text && !isspace(*chptr)) chptr--; - while (chptr > text && isspace(*chptr)) chptr--; - chptr++; - } - - if (chptr-text == 1 && !isspace(*chptr)) - chptr = text + width - 1; - - if (chptr > text) - howbad += width - (chptr - text) + 1; - if (result) { - if (kanji == 1) { - strncat(result, text, chptr - text + 1 ); - chptr++; - kanji = 0; - } else { - strncat(result, text, chptr - text ); - } - strcat(result, "\n"); - height++; - } - - if (isspace(*chptr)) - text = chptr + 1; - else - text = chptr; - while (isspace(*text)) text++; - } } - } - if (badness) *badness = howbad; if (resultPtr) *resultPtr = result; if (heightPtr) *heightPtr = height; diff --git a/windows.c b/windows.c index 4629fe6..86a8af0 100644 --- a/windows.c +++ b/windows.c @@ -71,6 +71,7 @@ static void * newtvwindow(char * title, char * button1, char * button2, newtFormDestroy(f); newtPopWindow(); + newtResizeScreen(1); if (answer == f) return NULL; @@ -89,6 +90,7 @@ int newtWinChoice(char * title, char * button1, char * button2, va_start(args, message); rc = newtvwindow(title, button1, button2, NULL, message, args); + newtResizeScreen(1); va_end(args); if (rc == button1) @@ -110,6 +112,7 @@ void newtWinMessage(char * title, char * buttonText, char * text, ...) { void newtWinMessagev(char * title, char * buttonText, char * text, va_list argv) { newtvwindow(title, buttonText, NULL, NULL, text, argv); + newtResizeScreen(1); } int newtWinTernary(char * title, char * button1, char * button2, @@ -119,6 +122,7 @@ int newtWinTernary(char * title, char * button1, char * button2, va_start(args, message); rc = newtvwindow(title, button1, button2, button3, message, args); + newtResizeScreen(1); va_end(args); if (rc == button1) @@ -167,6 +171,7 @@ int newtWinMenu(char * title, char * text, int suggestedWidth, int flexDown, buttonName = va_arg(args, char *); } + newtResizeScreen(1); va_end(button1); buttonBar = newtCreateGrid(numButtons, 1); @@ -196,6 +201,8 @@ int newtWinMenu(char * title, char * text, int suggestedWidth, int flexDown, newtFormDestroy(form); newtPopWindow(); + newtResizeScreen(1); + return rc; } @@ -271,5 +278,6 @@ int newtWinEntries(char * title, char * text, int suggestedWidth, int flexDown, newtFormDestroy(form); newtPopWindow(); + newtResizeScreen(1); return rc; } -- 2.47.2