comparison mcabber/src/screen.c @ 930:a75f7a13df7b

UTF-8 terminal support (Reimar Döffinger) This is a patch from Reimar Döffinger, slightly modified, which makes mcabber work better on UTF-8 terminals.
author Mikael Berthe <mikael@lilotux.net>
date Sat, 08 Jul 2006 09:40:05 +0200
parents f4bf564893b5
children 1cd6d694ac3c
comparison
equal deleted inserted replaced
929:f4bf564893b5 930:a75f7a13df7b
39 #else 39 #else
40 # include <curses.h> 40 # include <curses.h>
41 #endif 41 #endif
42 42
43 #include "screen.h" 43 #include "screen.h"
44 #include "utf8.h"
44 #include "hbuf.h" 45 #include "hbuf.h"
45 #include "commands.h" 46 #include "commands.h"
46 #include "compl.h" 47 #include "compl.h"
47 #include "roster.h" 48 #include "roster.h"
48 #include "histolog.h" 49 #include "histolog.h"
881 // is added 882 // is added
882 buddylist_build(); 883 buddylist_build();
883 // Wrap existing status buffer lines 884 // Wrap existing status buffer lines
884 hbuf_rebuild(&statushbuf, maxX - Roster_Width - PREFIX_WIDTH); 885 hbuf_rebuild(&statushbuf, maxX - Roster_Width - PREFIX_WIDTH);
885 886
887 #ifndef UNICODE
886 if (utf8_mode) 888 if (utf8_mode)
887 scr_LogPrint(LPRINT_NORMAL, "WARNING: UTF-8 not yet supported!"); 889 scr_LogPrint(LPRINT_NORMAL,
890 "WARNING: Compiled without full UTF-8 support!");
891 #endif
888 } else { 892 } else {
889 // Update panels 893 // Update panels
890 replace_panel(rosterPanel, rosterWnd); 894 replace_panel(rosterPanel, rosterWnd);
891 replace_panel(chatPanel, chatWnd); 895 replace_panel(chatPanel, chatWnd);
892 replace_panel(logPanel, logWnd); 896 replace_panel(logPanel, logWnd);
1948 // Drag the character before point forward over the character at 1952 // Drag the character before point forward over the character at
1949 // point, moving point forward as well. If point is at the end of 1953 // point, moving point forward as well. If point is at the end of
1950 // the line, then this transposes the two characters before point. 1954 // the line, then this transposes the two characters before point.
1951 void readline_transpose_chars() 1955 void readline_transpose_chars()
1952 { 1956 {
1953 char swp; 1957 char *c1, *c2;
1958 unsigned a, b;
1954 1959
1955 if (ptr_inputline == inputLine) return; 1960 if (ptr_inputline == inputLine) return;
1956 1961
1957 if (!*ptr_inputline) { // We're at EOL 1962 if (!*ptr_inputline) { // We're at EOL
1958 // If line is only 1 char long, nothing to do... 1963 // If line is only 1 char long, nothing to do...
1959 if (ptr_inputline == inputLine+1) return; 1964 if (ptr_inputline == prev_char(ptr_inputline, inputLine)) return;
1960 // Transpose the two previous characters 1965 // Transpose the two previous characters
1961 swp = *(ptr_inputline-2); 1966 c2 = prev_char(ptr_inputline, inputLine);
1962 *(ptr_inputline-2) = *(ptr_inputline-1); 1967 c1 = prev_char(c2, inputLine);
1963 *(ptr_inputline-1) = swp; 1968 a = get_char(c1);
1969 b = get_char(c2);
1970 put_char(put_char(c1, b), a);
1964 } else { 1971 } else {
1965 // Swap the two characters before the cursor and move right. 1972 // Swap the two characters before the cursor and move right.
1966 swp = *(ptr_inputline-1); 1973 c2 = ptr_inputline;
1967 *(ptr_inputline-1) = *ptr_inputline; 1974 c1 = prev_char(c2, inputLine);
1968 *ptr_inputline++ = swp; 1975 a = get_char(c1);
1976 b = get_char(c2);
1977 put_char(put_char(c1, b), a);
1969 check_offset(1); 1978 check_offset(1);
1970 } 1979 }
1971 } 1980 }
1972 1981
1973 // readline_backward_kill_word() 1982 // readline_backward_kill_word()
1977 char *c, *old = ptr_inputline; 1986 char *c, *old = ptr_inputline;
1978 int spaceallowed = 1; 1987 int spaceallowed = 1;
1979 1988
1980 if (ptr_inputline == inputLine) return; 1989 if (ptr_inputline == inputLine) return;
1981 1990
1982 for (c = ptr_inputline-1 ; c > inputLine ; c--) { 1991 c = prev_char(ptr_inputline, inputLine);
1983 if (!isalnum(*c)) { 1992 for ( ; c > inputLine ; c = prev_char(c, inputLine)) {
1984 if (*c == ' ') 1993 if (!iswalnum(get_char(c))) {
1994 if (iswblank(get_char(c)))
1985 if (!spaceallowed) break; 1995 if (!spaceallowed) break;
1986 } else spaceallowed = 0; 1996 } else spaceallowed = 0;
1987 } 1997 }
1988 1998
1989 if (c != inputLine || *c != ' ') 1999 if (c != inputLine || iswblank(get_char(c)))
1990 if ((c < ptr_inputline-1) && (!isalnum(*c))) 2000 if ((c < prev_char(ptr_inputline, inputLine)) && (!iswalnum(get_char(c))))
1991 c++; 2001 c = next_char(c);
1992 2002
1993 // Modify the line 2003 // Modify the line
1994 ptr_inputline = c; 2004 ptr_inputline = c;
1995 for (;;) { 2005 for (;;) {
1996 *c = *old++; 2006 *c = *old++;
2006 char *old_ptr_inputLine = ptr_inputline; 2016 char *old_ptr_inputLine = ptr_inputline;
2007 int spaceallowed = 1; 2017 int spaceallowed = 1;
2008 2018
2009 if (ptr_inputline == inputLine) return; 2019 if (ptr_inputline == inputLine) return;
2010 2020
2011 for (ptr_inputline-- ; ptr_inputline > inputLine ; ptr_inputline--) { 2021 for (ptr_inputline = prev_char(ptr_inputline, inputLine) ;
2012 if (!isalnum(*ptr_inputline)) { 2022 ptr_inputline > inputLine ;
2013 if (*ptr_inputline == ' ') 2023 ptr_inputline = prev_char(ptr_inputline, inputLine)) {
2024 if (!iswalnum(get_char(ptr_inputline))) {
2025 if (iswblank(get_char(ptr_inputline)))
2014 if (!spaceallowed) break; 2026 if (!spaceallowed) break;
2015 } else spaceallowed = 0; 2027 } else spaceallowed = 0;
2016 } 2028 }
2017 2029
2018 if (ptr_inputline < old_ptr_inputLine-1 2030 if (ptr_inputline < prev_char(old_ptr_inputLine, inputLine)
2019 && *ptr_inputline == ' ' && *(ptr_inputline+1) != ' ') 2031 && iswblank(get_char(ptr_inputline))
2020 ptr_inputline++; 2032 && iswblank(get_char(next_char(ptr_inputline))))
2033 ptr_inputline = next_char(ptr_inputline);
2021 2034
2022 check_offset(-1); 2035 check_offset(-1);
2023 } 2036 }
2024 2037
2025 // readline_forward_word() 2038 // readline_forward_word()
2027 void readline_forward_word() 2040 void readline_forward_word()
2028 { 2041 {
2029 int spaceallowed = 1; 2042 int spaceallowed = 1;
2030 2043
2031 while (*ptr_inputline) { 2044 while (*ptr_inputline) {
2032 ptr_inputline++; 2045 ptr_inputline = next_char(ptr_inputline);
2033 if (!isalnum(*ptr_inputline)) { 2046 if (!iswalnum(get_char(ptr_inputline))) {
2034 if (*ptr_inputline == ' ') 2047 if (iswblank(get_char(ptr_inputline)))
2035 if (!spaceallowed) break; 2048 if (!spaceallowed) break;
2036 } else spaceallowed = 0; 2049 } else spaceallowed = 0;
2037 } 2050 }
2038 2051
2039 check_offset(1); 2052 check_offset(1);
2062 return -2; 2075 return -2;
2063 } 2076 }
2064 2077
2065 // This is a command 2078 // This is a command
2066 row = 0; 2079 row = 0;
2067 for (p = inputLine ; p < ptr_inputline ; p++) { 2080 for (p = inputLine ; p < ptr_inputline ; p = next_char(p)) {
2068 if (quote) { 2081 if (quote) {
2069 if (*p == '"' && *(p-1) != '\\') 2082 if (*p == '"' && *(p-1) != '\\')
2070 quote = FALSE; 2083 quote = FALSE;
2071 continue; 2084 continue;
2072 } 2085 }
2099 strcpy(ptr_inputline, text); 2112 strcpy(ptr_inputline, text);
2100 ptr_inputline += len; 2113 ptr_inputline += len;
2101 strcpy(ptr_inputline, tmpLine); 2114 strcpy(ptr_inputline, tmpLine);
2102 } 2115 }
2103 2116
2117 static void scr_cancel_current_completion(void);
2118
2104 // scr_handle_tab() 2119 // scr_handle_tab()
2105 // Function called when tab is pressed. 2120 // Function called when tab is pressed.
2106 // Initiate or continue a completion... 2121 // Initiate or continue a completion...
2107 static void scr_handle_tab(void) 2122 static void scr_handle_tab(void)
2108 { 2123 {
2119 // b) We can't have more than 2 parameters (we use 2 flags) 2134 // b) We can't have more than 2 parameters (we use 2 flags)
2120 if ((nrow == -2) || (nrow == 3 && !completion_started) || nrow > 3) 2135 if ((nrow == -2) || (nrow == 3 && !completion_started) || nrow > 3)
2121 return; 2136 return;
2122 2137
2123 if (nrow == 0) { // Command completion 2138 if (nrow == 0) { // Command completion
2124 row = &inputLine[1]; 2139 row = next_char(inputLine);
2125 compl_categ = COMPL_CMD; 2140 compl_categ = COMPL_CMD;
2126 } else if (nrow == -1) { // Nickname completion 2141 } else if (nrow == -1) { // Nickname completion
2127 compl_categ = COMPL_RESOURCE; 2142 compl_categ = COMPL_RESOURCE;
2128 } else { // Other completion, depending on the command 2143 } else { // Other completion, depending on the command
2129 int alias = FALSE; 2144 int alias = FALSE;
2157 if (cchar) 2172 if (cchar)
2158 scr_insert_text(cchar); 2173 scr_insert_text(cchar);
2159 completion_started = TRUE; 2174 completion_started = TRUE;
2160 } 2175 }
2161 } else { // Completion already initialized 2176 } else { // Completion already initialized
2162 char *c; 2177 scr_cancel_current_completion();
2163 guint back = cancel_completion();
2164 // Remove $back chars
2165 ptr_inputline -= back;
2166 c = ptr_inputline;
2167 for ( ; *c ; c++)
2168 *c = *(c+back);
2169 // Now complete again 2178 // Now complete again
2170 cchar = complete(); 2179 cchar = complete();
2171 if (cchar) 2180 if (cchar)
2172 scr_insert_text(cchar); 2181 scr_insert_text(cchar);
2173 } 2182 }
2174 } 2183 }
2175 2184
2176 static void scr_cancel_current_completion(void) 2185 static void scr_cancel_current_completion(void)
2177 { 2186 {
2178 char *c; 2187 char *c;
2188 char *src = ptr_inputline;
2179 guint back = cancel_completion(); 2189 guint back = cancel_completion();
2190 guint i;
2180 // Remove $back chars 2191 // Remove $back chars
2181 ptr_inputline -= back; 2192 for (i = 0; i < back; i++)
2193 ptr_inputline = prev_char(ptr_inputline, inputLine);
2182 c = ptr_inputline; 2194 c = ptr_inputline;
2183 for ( ; *c ; c++) 2195 for ( ; *src ; )
2184 *c = *(c+back); 2196 *c++ = *src++;
2197 *c = 0;
2185 } 2198 }
2186 2199
2187 static void scr_end_current_completion(void) 2200 static void scr_end_current_completion(void)
2188 { 2201 {
2189 done_completion(); 2202 done_completion();
2193 // check_offset(int direction) 2206 // check_offset(int direction)
2194 // Check inputline_offset value, and make sure the cursor is inside the 2207 // Check inputline_offset value, and make sure the cursor is inside the
2195 // screen. 2208 // screen.
2196 static inline void check_offset(int direction) 2209 static inline void check_offset(int direction)
2197 { 2210 {
2211 int i;
2212 char *c = &inputLine[inputline_offset];
2198 // Left side 2213 // Left side
2199 if (inputline_offset && direction <= 0) { 2214 if (inputline_offset && direction <= 0) {
2200 while (ptr_inputline <= (char*)&inputLine + inputline_offset) { 2215 while (ptr_inputline <= c) {
2201 if (inputline_offset) { 2216 for (i = 0; i < 5; i++)
2202 inputline_offset -= 5; 2217 c = prev_char(c, inputLine);
2203 if (inputline_offset < 0) 2218 if (c == inputLine)
2204 inputline_offset = 0;
2205 } else
2206 break; 2219 break;
2207 } 2220 }
2208 } 2221 }
2209 // Right side 2222 // Right side
2210 if (direction >= 0) { 2223 if (direction >= 0) {
2211 while (ptr_inputline >= inputline_offset + (char*)&inputLine + maxX) 2224 int delta = wcwidth(get_char(c));
2212 inputline_offset += 5; 2225 while (ptr_inputline > c) {
2213 } 2226 c = next_char(c);
2227 delta += wcwidth(get_char(c));
2228 }
2229 c = &inputLine[inputline_offset];
2230 while (delta >= maxX) {
2231 for (i = 0; i < 5; i++) {
2232 delta -= wcwidth(get_char(c));
2233 c = next_char(c);
2234 }
2235 }
2236 }
2237 inputline_offset = c - inputLine;
2214 } 2238 }
2215 2239
2216 static inline void refresh_inputline(void) 2240 static inline void refresh_inputline(void)
2217 { 2241 {
2218 mvwprintw(inputWnd, 0,0, "%s", inputLine + inputline_offset); 2242 mvwprintw(inputWnd, 0,0, "%s", inputLine + inputline_offset);
2219 wclrtoeol(inputWnd); 2243 wclrtoeol(inputWnd);
2220 if (*ptr_inputline) 2244 if (*ptr_inputline) {
2221 wmove(inputWnd, 0, ptr_inputline - (char*)&inputLine - inputline_offset); 2245 // hack to set cursor pos. Characters can have different width,
2246 // so I know of no better way.
2247 char c = *ptr_inputline;
2248 *ptr_inputline = 0;
2249 mvwprintw(inputWnd, 0,0, "%s", inputLine + inputline_offset);
2250 *ptr_inputline = c;
2251 }
2222 } 2252 }
2223 2253
2224 void scr_handle_CtrlC(void) 2254 void scr_handle_CtrlC(void)
2225 { 2255 {
2226 if (!Curses) return; 2256 if (!Curses) return;
2290 if (needmore) 2320 if (needmore)
2291 return 0; 2321 return 0;
2292 return -1; 2322 return -1;
2293 } 2323 }
2294 2324
2325 static inline int match_utf8_keyseq(int *iseq)
2326 {
2327 int *strp = iseq;
2328 unsigned c = *strp++;
2329 unsigned mask = 0x80;
2330 int len = -1;
2331 while (c & mask) {
2332 mask >>= 1;
2333 len++;
2334 }
2335 if (len <= 0 || len > 4)
2336 return -1;
2337 c &= mask - 1;
2338 while ((*strp & 0xc0) == 0x80) {
2339 if (len-- <= 0) // can't happen
2340 return -1;
2341 c = (c << 6) | (*strp++ & 0x3f);
2342 }
2343 if (len)
2344 return 0;
2345 return c;
2346 }
2347
2295 void scr_Getch(keycode *kcode) 2348 void scr_Getch(keycode *kcode)
2296 { 2349 {
2297 keyseq *mks = NULL; 2350 keyseq *mks = NULL;
2298 int ks[MAX_KEYSEQ_LENGTH+1]; 2351 int ks[MAX_KEYSEQ_LENGTH+1];
2299 int i; 2352 int i;
2300 2353
2301 memset(kcode, 0, sizeof(keycode)); 2354 memset(kcode, 0, sizeof(keycode));
2302 memset(ks, 0, sizeof(ks)); 2355 memset(ks, 0, sizeof(ks));
2303 2356
2304 kcode->value = wgetch(inputWnd); 2357 kcode->value = wgetch(inputWnd);
2358 if (utf8_mode) {
2359 ks[0] = kcode->value;
2360 for (i = 0; i < MAX_KEYSEQ_LENGTH - 1; i++) {
2361 int match = match_utf8_keyseq(ks);
2362 if (match == -1)
2363 break;
2364 if (match > 0) {
2365 kcode->value = match;
2366 kcode->utf8 = 1;
2367 return;
2368 }
2369 ks[i + 1] = wgetch(inputWnd);
2370 if (ks[i + 1] == ERR)
2371 break;
2372 }
2373 while (i > 0)
2374 ungetch(ks[i--]);
2375 memset(ks, 0, sizeof(ks));
2376 }
2305 if (kcode->value != 27) 2377 if (kcode->value != 27)
2306 return; 2378 return;
2307 2379
2308 // Check for escape key sequence 2380 // Check for escape key sequence
2309 for (i=0; i < MAX_KEYSEQ_LENGTH; i++) { 2381 for (i=0; i < MAX_KEYSEQ_LENGTH; i++) {
2335 ungetch(ks[i]); 2407 ungetch(ks[i]);
2336 } 2408 }
2337 return; 2409 return;
2338 } 2410 }
2339 2411
2340 static int bindcommand(keycode kcode) { 2412 static int bindcommand(keycode kcode)
2413 {
2341 gchar asciikey[16]; 2414 gchar asciikey[16];
2342 const gchar *boundcmd; 2415 const gchar *boundcmd;
2343 2416
2344 if (!kcode.mcode || kcode.mcode == MKEY_EQUIV) 2417 if (!kcode.mcode || kcode.mcode == MKEY_EQUIV)
2345 g_snprintf(asciikey, 15, "%d", kcode.value); 2418 g_snprintf(asciikey, 15, "%d", kcode.value);
2361 g_free(cmd); 2434 g_free(cmd);
2362 return 0; 2435 return 0;
2363 } 2436 }
2364 2437
2365 scr_LogPrint(LPRINT_NORMAL, "Unknown key=%s", asciikey); 2438 scr_LogPrint(LPRINT_NORMAL, "Unknown key=%s", asciikey);
2439 #ifndef UNICODE
2366 if (utf8_mode) 2440 if (utf8_mode)
2367 scr_LogPrint(LPRINT_NORMAL, "WARNING: UTF-8 not yet supported!"); 2441 scr_LogPrint(LPRINT_NORMAL,
2442 "WARNING: Compiled without full UTF-8 support!");
2443 #endif
2368 return -1; 2444 return -1;
2369 } 2445 }
2370 2446
2371 // process_key(key) 2447 // process_key(key)
2372 // Handle the pressed key, in the command line (bottom). 2448 // Handle the pressed key, in the command line (bottom).
2403 break; 2479 break;
2404 case 8: // Ctrl-h 2480 case 8: // Ctrl-h
2405 case 127: // Backspace too 2481 case 127: // Backspace too
2406 case KEY_BACKSPACE: 2482 case KEY_BACKSPACE:
2407 if (ptr_inputline != (char*)&inputLine) { 2483 if (ptr_inputline != (char*)&inputLine) {
2408 char *c = --ptr_inputline; 2484 char *src = ptr_inputline;
2409 for ( ; *c ; c++) 2485 char *c = prev_char(ptr_inputline, inputLine);
2410 *c = *(c+1); 2486 ptr_inputline = c;
2487 for ( ; *src ; )
2488 *c++ = *src++;
2489 *c = 0;
2411 check_offset(-1); 2490 check_offset(-1);
2412 } 2491 }
2413 break; 2492 break;
2414 case KEY_DC:// Del 2493 case KEY_DC:// Del
2415 if (*ptr_inputline) 2494 if (*ptr_inputline)
2416 strcpy(ptr_inputline, ptr_inputline+1); 2495 strcpy(ptr_inputline, next_char(ptr_inputline));
2417 break; 2496 break;
2418 case KEY_LEFT: 2497 case KEY_LEFT:
2419 if (ptr_inputline != (char*)&inputLine) { 2498 if (ptr_inputline != (char*)&inputLine) {
2420 ptr_inputline--; 2499 ptr_inputline = prev_char(ptr_inputline, inputLine);
2421 check_offset(-1); 2500 check_offset(-1);
2422 } 2501 }
2423 break; 2502 break;
2424 case KEY_RIGHT: 2503 case KEY_RIGHT:
2425 if (*ptr_inputline) 2504 if (*ptr_inputline)
2426 ptr_inputline++; 2505 ptr_inputline = next_char(ptr_inputline);
2427 check_offset(1); 2506 check_offset(1);
2428 break; 2507 break;
2429 case 7: // Ctrl-g 2508 case 7: // Ctrl-g
2430 scr_cancel_current_completion(); 2509 scr_cancel_current_completion();
2431 scr_end_current_completion(); 2510 scr_end_current_completion();
2529 readline_transpose_chars(); 2608 readline_transpose_chars();
2530 break; 2609 break;
2531 case 23: // Ctrl-w 2610 case 23: // Ctrl-w
2532 readline_backward_kill_word(); 2611 readline_backward_kill_word();
2533 break; 2612 break;
2613 case 515:
2534 case 516: // Ctrl-Left 2614 case 516: // Ctrl-Left
2535 readline_backward_word(); 2615 readline_backward_word();
2536 break; 2616 break;
2617 case 517:
2537 case 518: // Ctrl-Right 2618 case 518: // Ctrl-Right
2538 readline_forward_word(); 2619 readline_forward_word();
2539 break; 2620 break;
2540 case 12: // Ctrl-l 2621 case 12: // Ctrl-l
2541 scr_CheckAutoAway(TRUE); 2622 scr_CheckAutoAway(TRUE);
2557 top_panel(chatPanel); 2638 top_panel(chatPanel);
2558 top_panel(inputPanel); 2639 top_panel(inputPanel);
2559 update_panels(); 2640 update_panels();
2560 break; 2641 break;
2561 default: 2642 default:
2562 if (isprint(key)) { 2643 if (iswprint(key) && (!utf8_mode || kcode.utf8 || key < 128)) {
2563 char tmpLine[INPUTLINE_LENGTH+1]; 2644 char tmpLine[INPUTLINE_LENGTH+1];
2564 2645
2565 // Check the line isn't too long 2646 // Check the line isn't too long
2566 if (strlen(inputLine) >= INPUTLINE_LENGTH) 2647 if (strlen(inputLine) + 4 > INPUTLINE_LENGTH)
2567 return 0; 2648 return 0;
2568 2649
2569 // Insert char 2650 // Insert char
2570 strcpy(tmpLine, ptr_inputline); 2651 strcpy(tmpLine, ptr_inputline);
2571 *ptr_inputline++ = key; 2652 ptr_inputline = put_char(ptr_inputline, key);
2572 strcpy(ptr_inputline, tmpLine); 2653 strcpy(ptr_inputline, tmpLine);
2573 check_offset(1); 2654 check_offset(1);
2574 } else { 2655 } else {
2575 // Look for a key binding. 2656 // Look for a key binding.
2576 if (bindcommand(kcode) == 255) 2657 if (bindcommand(kcode) == 255)