1 /*
2
3 Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. Neither the name of authors nor the names of its contributors
17 may be used to endorse or promote products derived from this software
18 without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31
32 */
33
34 /*
35 * プリエディットやステータスラインを描画する
36 */
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40 #if (!defined(DEBUG) && !defined(NDEBUG))
41 #define NDEBUG
42 #endif
43 #ifdef HAVE_STRING_H
44 #include <string.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #ifdef HAVE_STDLIB_H
50 #include <stdlib.h>
51 #endif
52 #ifdef HAVE_ASSERT_H
53 #include <assert.h>
54 #endif
55 #include <stdio.h>
56
57 #define WINNOSIZE 5
58 #define MODESIZE 50
59
60 #include "uim-fep.h"
61 #include "callbacks.h"
62 #include "escseq.h"
63 #include "draw.h"
64 #include "str.h"
65 #include "udsock.h"
66
67 /* コミットされてから出力されるまでTRUE */
68 int g_commit = FALSE;
69 /* s_preedit->width > 0と同じ */
70 int g_start_preedit = FALSE;
71
72 /* 現在のプリエディット */
73 static struct preedit_tag *s_preedit;
74 /* コミットされたときのプリエディットを保存する */
75 static struct preedit_tag s_save_preedit;
76 /* プリエディットの先頭の位置 */
77 static struct point_tag s_head;
78 /* 行数 -> 行末のカラム */
79 static int *s_line2width = NULL;
80 static int *s_prev_line2width = NULL;
81 /* プリエディットの行数 */
82 static int s_preedit_lines = 0;
83 static int s_prev_preedit_lines = 0;
84
85 /* 疑似端末マスタのファイル記述子 */
86 static int s_master;
87 /* モード状態文字列 */
88 static char s_modebuf[MODESIZE];
89 /* 候補一覧 */
90 static char s_candbuf[CANDSIZE];
91 /* GNU screenのWINDOW番号 */
92 static char s_win_no[WINNOSIZE];
93 /* モード状態を書き込むファイル */
94 static const char *s_path_getmode;
95 /* 端末サイズが変換したときTRUE */
96 static int s_winch = FALSE;
97
98 static void init_backtick(void);
99 static void start_preedit(void);
100 static void end_preedit(void);
101 static void draw_statusline(int force, int restore, int visible, int draw_background);
102 static void draw_preedit(struct preedit_tag *preedit, struct preedit_tag *prev_preedit);
103 static int is_eq_region(void);
104 static void draw_subpreedit(struct preedit_tag *p, int start, int end);
105 static void draw_pseg(struct preedit_segment_tag *pseg, int start_width);
106 static int compare_preedit(struct preedit_tag *p1, struct preedit_tag *p2);
107 static int compare_preedit_rev(struct preedit_tag *p1, struct preedit_tag *p2);
108 static int min(int a, int b);
109 static void erase_prev_preedit(void);
110 static void erase_preedit(void);
111 static void set_line2width(struct preedit_tag *preedit);
112 static void goto_char(int width);
113 static void goto_col(int width);
114 static struct point_tag width2point_char(int width);
115 static struct point_tag width2point_col(int width);
116 static int width2lineno_char(int width);
117 static int width2lineno_col(int width);
118
119 #if defined(DEBUG) && DEBUG > 2
120 static void print_preedit(struct preedit_tag *p);
121 #endif
122
init_draw(int master,const char * path_getmode)123 void init_draw(int master, const char *path_getmode)
124 {
125 s_master = master;
126 s_path_getmode = path_getmode;
127 s_preedit = create_preedit();
128 if (g_opt.status_type == BACKTICK) {
129 init_backtick();
130 }
131 }
132
init_backtick(void)133 static void init_backtick(void)
134 {
135 if (getenv("WINDOW")) {
136 snprintf(s_win_no, sizeof(s_win_no), "%s ", getenv("WINDOW"));
137 }
138 }
139
update_backtick(void)140 void update_backtick(void)
141 {
142 char sendbuf[CANDSIZE];
143 if (s_candbuf[0] == '\0') {
144 /* モード表示 */
145 snprintf(sendbuf, sizeof(sendbuf), "%s%s", s_win_no, s_modebuf);
146 } else {
147 /* 候補一覧表示 */
148 strlcpy(sendbuf, s_candbuf, sizeof(sendbuf));
149 }
150 sendline(sendbuf);
151 }
152
153 /*
154 * プリエディット,ステータスラインを描画する
155 * 描写する必要がない場合はFALSEを返す
156 */
draw(void)157 int draw(void)
158 {
159 char *commit_str;
160
161 struct preedit_tag *prev_preedit;
162
163 int i;
164
165 if (!end_callbacks()) {
166 return FALSE;
167 }
168
169 prev_preedit = s_preedit;
170 s_preedit = get_preedit();
171 commit_str = get_commit_str();
172
173 /* 端末サイズが変更されたときはs_headを変更し、前のpreeditがなかったことにする */
174 if (s_winch && g_start_preedit) {
175 if (g_opt.no_report_cursor) {
176 s_preedit->cursor = 0;
177 } else {
178 s_head = get_cursor_position();
179 }
180 end_preedit();
181 free_preedit(prev_preedit);
182 prev_preedit = create_preedit();
183 }
184 s_winch = FALSE;
185
186 debug2(("draw()\n"));
187 debug2(("commit_str = \"%s\"\n", commit_str));
188 debug2(("preedit->width = %d\n", s_preedit->width));
189
190 /* 保存しておいたプリエディットはもういらない */
191 for (i = 0; i < s_save_preedit.nr_psegs; i++) {
192 free(s_save_preedit.pseg[i].str);
193 }
194 s_save_preedit.width = s_save_preedit.cursor = s_save_preedit.nr_psegs = 0;
195
196 /* プリエディットが無ければカーソルを戻す */
197 draw_statusline(FALSE, !g_start_preedit || g_opt.no_report_cursor, FALSE, FALSE);
198
199 /* コミットされたか */
200 if (commit_str[0] != '\0') {
201 g_commit = TRUE;
202 /* プリエディットを消す必要があるか */
203 if (prev_preedit->width > 0) {
204 put_cursor_invisible();
205 if (g_opt.no_report_cursor) {
206 put_cursor_left(prev_preedit->cursor);
207 if (g_opt.on_the_spot) {
208 put_delete(prev_preedit->width);
209 } else {
210 put_erase(prev_preedit->width);
211 put_cursor_left(prev_preedit->width);
212 }
213 } else {
214 erase_preedit();
215 }
216 end_preedit();
217 }
218 write(s_master, commit_str, strlen(commit_str));
219 }
220 if (!g_commit) {
221 /* 描画または消去するプリエディットがあるか */
222 if (s_preedit->width != 0 || prev_preedit->width != 0) {
223 if (prev_preedit->width == 0) {
224 start_preedit();
225 }
226 draw_preedit(s_preedit, prev_preedit);
227 if (s_preedit->width == 0) {
228 end_preedit();
229 }
230 }
231 } else { /* if (g_commit) */
232 if (s_preedit->width > 0) {
233 /* コミットされたときにプリエディットがあったので,後から出力す
234 * るためにプリエディットを保存する */
235 s_save_preedit = *s_preedit;
236 }
237 s_preedit->width = s_preedit->cursor = s_preedit->nr_psegs = 0;
238 }
239
240 free_preedit(prev_preedit);
241 free(commit_str);
242 put_cursor_normal();
243
244 debug2(("\ndraw end\n"));
245 return TRUE;
246 }
247
248 /*
249 * プリエディットを開始するときに呼ぶ
250 */
start_preedit(void)251 static void start_preedit(void)
252 {
253 if (!g_start_preedit) {
254 debug2(("start_preedit()\n"));
255 g_start_preedit = TRUE;
256 if (g_opt.no_report_cursor) {
257 return;
258 }
259
260 s_head = get_cursor_position();
261 if (s_head.col == g_win->ws_col - 1) {
262 s_head.col = 0;
263 if (s_head.row == g_win->ws_row - 1) {
264 put_crlf();
265 } else {
266 s_head.row++;
267 }
268 }
269 debug2(("s_head.row = %d s_head.col = %d\n", s_head.row, s_head.col));
270 s_line2width = uim_malloc(sizeof(int));
271 s_line2width[0] = s_head.col;
272 s_preedit_lines = 1;
273 }
274 }
275
276 /*
277 * プリエディットを終了するときに呼ぶ
278 * カーソルはプリエディット開始位置に移動
279 */
end_preedit(void)280 static void end_preedit(void)
281 {
282 debug2(("end_preedit()\n"));
283 assert(g_start_preedit);
284 g_start_preedit = FALSE;
285 if (g_opt.no_report_cursor) {
286 return;
287 }
288
289 put_cursor_address_p(&s_head);
290 s_preedit_lines = 0;
291 if (s_line2width != NULL) {
292 free(s_line2width);
293 s_line2width = NULL;
294 }
295 if (s_prev_line2width != NULL) {
296 free(s_prev_line2width);
297 s_prev_line2width = NULL;
298 }
299 }
300
301 /*
302 * コミットとプリエディットが同時に発生し,
303 * その後,コミットを出力したときにTRUEを返す
304 */
is_commit_and_preedit(void)305 int is_commit_and_preedit(void)
306 {
307 return !g_commit && s_save_preedit.width > 0;
308 }
309
310 /*
311 * is_commit_and_preedit() == TRUEのときに呼ばれ,
312 * プリエディットを出力する
313 */
draw_commit_and_preedit(void)314 void draw_commit_and_preedit(void)
315 {
316 assert(is_commit_and_preedit());
317 assert(s_preedit->width == 0);
318 *s_preedit = s_save_preedit;
319 s_save_preedit.width = 0;
320 s_save_preedit.cursor = 0;
321 s_save_preedit.nr_psegs = 0;
322 start_preedit();
323 /* prev_preeditは空 */
324 draw_preedit(s_preedit, &s_save_preedit);
325 put_cursor_normal();
326 }
327
328 /*
329 * ステータスラインを描画する
330 * forceがTRUEのときは状態が変わってなくても描画する
331 * restoreがTRUEのときはカーソルを開始位置に戻す
332 * visibleがTRUEのときは終了後にカーソルを表示する
333 * draw_backgroundがTRUEのときは背景色で塗りつぶす
334 */
draw_statusline(int force,int restore,int visible,int draw_background)335 static void draw_statusline(int force, int restore, int visible, int draw_background)
336 {
337 static char *statusline_str = NULL;
338 static int statusline_str_width = 0;
339 static char *candidate_str = NULL;
340 static int candidate_col = UNDEFINED;
341 static char *mode_str = NULL;
342 static char *index_str = NULL;
343 static int index_col = UNDEFINED;
344
345 char *prev_statusline_str;
346 int prev_statusline_str_width;
347 char *prev_candidate_str;
348 int prev_candidate_col;
349 char *prev_mode_str;
350 char *prev_index_str;
351 int prev_index_col;
352
353 /* static変数の初期化 1回しか実行されない */
354 if (statusline_str == NULL) {
355 statusline_str = uim_strdup("");
356 }
357 if (candidate_str == NULL) {
358 candidate_str = uim_strdup("");
359 }
360 if (index_str == NULL) {
361 index_str = uim_strdup("");
362 }
363 if (mode_str == NULL) {
364 mode_str = uim_strdup("");
365 }
366
367 prev_statusline_str = statusline_str;
368 prev_statusline_str_width = statusline_str_width;
369 prev_candidate_str = candidate_str;
370 prev_candidate_col = candidate_col;
371 prev_mode_str = mode_str;
372 prev_index_str = index_str;
373 prev_index_col = index_col;
374
375 statusline_str = get_statusline_str();
376 candidate_str = get_candidate_str();
377 candidate_col = get_candidate_col();
378 mode_str = get_mode_str();
379 index_str = get_index_str();
380 index_col = get_index_col();
381
382 debug2(("draw_statusline()\n"));
383 debug2(("statusline_str = \"%s\"\n", statusline_str));
384 debug2(("candidate_str = \"%s\"\n", candidate_str));
385 debug2(("candidate_col = %d\n", candidate_col));
386 debug2(("prev_mode_str = %s\n", prev_mode_str));
387 debug2(("mode_str = %s\n", mode_str));
388 debug2(("index_str = \"%s\"\n", index_str));
389 debug2(("index_col = %d\n", index_col));
390
391 /* 候補一覧を消去 */
392 if (statusline_str[0] == '\0' && prev_statusline_str[0] != '\0') {
393 if (g_opt.status_type == LASTLINE) {
394 /* 候補一覧を消した後はモードを描画する必要がある */
395 force = TRUE;
396 } else if (g_opt.status_type == BACKTICK) {
397 s_candbuf[0] = '\0';
398 }
399 } else {
400 /* 新しい候補一覧か */
401 if (strcmp(statusline_str, prev_statusline_str) != 0 || (force && statusline_str[0] != '\0')) {
402 /* 新しい候補一覧なので前回の候補はない */
403 prev_candidate_col = UNDEFINED;
404 prev_index_col = UNDEFINED;
405 if (g_opt.status_type == LASTLINE) {
406 if (restore) {
407 put_save_cursor();
408 }
409 put_cursor_invisible();
410 put_goto_lastline(0);
411 /* 候補が選択されているか */
412 if (candidate_col != UNDEFINED) {
413 int byte_cand = (width2byte(statusline_str, candidate_col))[0];
414 int byte_index;
415 put_uim_str_len(statusline_str, UPreeditAttr_None, byte_cand);
416 put_uim_str(candidate_str, UPreeditAttr_Reverse);
417 if (index_col == UNDEFINED) {
418 put_uim_str(statusline_str + byte_cand + strlen(candidate_str), UPreeditAttr_None);
419 } else {
420 if (g_opt.ddskk) {
421 put_uim_str(statusline_str + byte_cand + strlen(candidate_str), UPreeditAttr_None);
422 } else {
423 byte_index = (width2byte(statusline_str, index_col))[0];
424 put_uim_str_len(statusline_str + byte_cand + strlen(candidate_str),
425 UPreeditAttr_None,
426 byte_index - byte_cand - strlen(candidate_str));
427 put_uim_str(index_str, UPreeditAttr_None);
428 put_uim_str(statusline_str + byte_index + strlen(index_str), UPreeditAttr_None);
429 }
430 }
431 } else {
432 put_uim_str(statusline_str, UPreeditAttr_None);
433 }
434 statusline_str_width = strwidth(statusline_str);
435 if (draw_background) {
436 put_clear_to_end_of_line(g_win->ws_col - statusline_str_width);
437 } else if (statusline_str_width < prev_statusline_str_width) {
438 put_clear_to_end_of_line(prev_statusline_str_width - statusline_str_width);
439 }
440 goto end_candidate;
441 } else if (g_opt.status_type == BACKTICK) {
442 strlcpy(s_candbuf, statusline_str, CANDSIZE);
443 }
444 }
445 if (prev_candidate_col != candidate_col) {
446 /* 前回の候補の反転を戻す */
447 if (prev_candidate_col != UNDEFINED) {
448 if (g_opt.status_type == LASTLINE) {
449 if (restore) {
450 put_save_cursor();
451 }
452 put_cursor_invisible();
453 put_goto_lastline(prev_candidate_col);
454 put_uim_str(prev_candidate_str, UPreeditAttr_None);
455 }
456 }
457 /* 選択された候補を反転する */
458 if (candidate_col != UNDEFINED) {
459 if (g_opt.status_type == LASTLINE) {
460 if (restore) {
461 put_save_cursor();
462 }
463 put_cursor_invisible();
464 put_goto_lastline(candidate_col);
465 put_uim_str(candidate_str, UPreeditAttr_Reverse);
466 } else if (g_opt.status_type == BACKTICK) {
467 int byte;
468 strlcpy(s_candbuf, statusline_str, CANDSIZE);
469 byte = (width2byte(statusline_str, candidate_col))[0] + strlen(candidate_str);
470 if (0 <= byte && byte <= CANDSIZE - 1) {
471 s_candbuf[byte] = ']';
472 }
473 byte -= (strlen(candidate_str) + 1);
474 if (0 <= byte && byte <= CANDSIZE - 1) {
475 s_candbuf[byte] = '[';
476 }
477 }
478 }
479 }
480 if (index_col != UNDEFINED && !g_opt.ddskk) {
481 if (g_opt.status_type == LASTLINE) {
482 int i = 0;
483 if (restore) {
484 put_save_cursor();
485 }
486 put_cursor_invisible();
487 /* index_strはascii */
488 if (prev_index_col != UNDEFINED) {
489 for (i = 0; i < (int)strlen(index_str); i++) {
490 if (index_str[i] != prev_index_str[i]) {
491 break;
492 }
493 }
494 }
495 if (i < (int)strlen(index_str)) {
496 put_goto_lastline(index_col + i);
497 put_uim_str(index_str + i, UPreeditAttr_None);
498 }
499 } else if (g_opt.status_type == BACKTICK) {
500 memcpy(s_candbuf + (width2byte(statusline_str, index_col))[0], index_str, strlen(index_str));
501 }
502 }
503 }
504 end_candidate:
505
506 if (force || strcmp(mode_str, prev_mode_str) != 0) {
507
508 /* 現在のモードをUIM_FEP_GETMODEに書き込む */
509 if (s_path_getmode[0] != '\0') {
510 FILE *fp = fopen(s_path_getmode, "wt");
511 if (fp) {
512 int mode = get_mode();
513 fprintf(fp, "%d\n", mode);
514 fclose(fp);
515 }
516 }
517
518 if (g_opt.status_type != NONE && statusline_str[0] == '\0') {
519 if (g_opt.status_type == LASTLINE) {
520 int mode_str_width = strwidth(mode_str);
521
522 statusline_str_width = mode_str_width;
523
524 if (restore) {
525 put_save_cursor();
526 }
527 put_cursor_invisible();
528
529 /* draw_background ならば force である */
530 /* 論理的には関係ないがそのような使われ方しかしていない */
531 assert(!draw_background || force);
532
533 if (force) {
534 put_goto_lastline(0);
535 put_uim_str(mode_str, UPreeditAttr_None);
536
537 if (draw_background) {
538 put_clear_to_end_of_line(g_win->ws_col - statusline_str_width);
539 } else if (statusline_str_width < prev_statusline_str_width) {
540 put_clear_to_end_of_line(prev_statusline_str_width - statusline_str_width);
541 }
542
543 } else {
544 /* !force なので prev_statusline_str_width はモード表示の長さである */
545 int eq_width = compare_str(mode_str, prev_mode_str);
546 int eq_byte = width2byte(mode_str, eq_width)[0];
547 int prev_mode_str_width = strwidth(prev_mode_str);
548
549 put_goto_lastline(eq_width);
550 if (mode_str_width == prev_mode_str_width) {
551 int eq_width_rev = compare_str_rev(mode_str, prev_mode_str);
552
553 if (eq_width_rev > 0) {
554 int draw_byte = width2byte(mode_str + eq_byte, mode_str_width - eq_width - eq_width_rev)[0];
555 put_uim_str_len(mode_str + eq_byte, UPreeditAttr_None, draw_byte);
556
557 } else {
558 put_uim_str(mode_str + eq_byte, UPreeditAttr_None);
559 }
560
561 } else {
562 put_uim_str(mode_str + eq_byte, UPreeditAttr_None);
563 if (statusline_str_width < prev_statusline_str_width) {
564 put_clear_to_end_of_line(prev_statusline_str_width - statusline_str_width);
565 }
566 }
567 }
568
569 } else if (g_opt.status_type == BACKTICK) {
570 strlcpy(s_modebuf, mode_str, sizeof(s_modebuf));
571 }
572 }
573 }
574 free(prev_candidate_str);
575 free(prev_statusline_str);
576 free(prev_index_str);
577 free(prev_mode_str);
578 if (restore) {
579 put_restore_cursor();
580 }
581 if (g_opt.status_type == BACKTICK) {
582 update_backtick();
583 }
584 if (visible) {
585 put_cursor_normal();
586 }
587 debug2(("draw_statusline end\n"));
588 }
589
590 /*
591 * ステータスラインのモード表示をmodeにする
592 * カーソル位置は変わらない
593 */
draw_statusline_restore(void)594 void draw_statusline_restore(void)
595 {
596 if (!end_callbacks()) {
597 if (g_opt.status_type == BACKTICK) {
598 update_backtick();
599 }
600 return;
601 }
602 draw_statusline(FALSE, TRUE, TRUE, FALSE);
603 }
604
605 /*
606 * 最下行を再描画する
607 * カーソルは最下行に移動する
608 */
draw_statusline_force_no_restore(void)609 void draw_statusline_force_no_restore(void)
610 {
611 end_callbacks();
612 draw_statusline(TRUE, FALSE, FALSE, TRUE);
613 }
614
615 /*
616 * 最下行を再描画する
617 * カーソル位置は変わらない
618 */
draw_statusline_force_restore(void)619 void draw_statusline_force_restore(void)
620 {
621 end_callbacks();
622 draw_statusline(TRUE, TRUE, TRUE, TRUE);
623 }
624
625 /*
626 * 最下行を消す
627 * カーソルは最下行に移動する
628 */
clear_lastline(void)629 void clear_lastline(void)
630 {
631 assert(g_opt.status_type == LASTLINE);
632 put_goto_lastline(0);
633 put_clear_to_end_of_line(g_win->ws_col);
634 }
635
636 /*
637 * backtickを消す
638 */
clear_backtick(void)639 void clear_backtick(void)
640 {
641 assert(g_opt.status_type == BACKTICK);
642 sendline("");
643 }
644
645 /*
646 * プリエディットを描画する
647 * 開始時のカーソル位置は任意
648 * 終了時のカーソル位置はpreedit->cursor
649 */
draw_preedit(struct preedit_tag * preedit,struct preedit_tag * prev_preedit)650 static void draw_preedit(struct preedit_tag *preedit, struct preedit_tag *prev_preedit)
651 {
652 int eq_width;
653
654 /* 端末サイズが変更されたときはprev_preeditは無視する */
655 eq_width = compare_preedit(preedit, prev_preedit);
656
657 #if DEBUG > 2
658 debug2(("\neq_width = %d\n", eq_width));
659 debug2(("prev "));
660 print_preedit(prev_preedit);
661 debug2(("\n"));
662 debug2(("preedit "));
663 print_preedit(preedit);
664 debug2(("\n"));
665 #endif
666
667 /* プリエディットの末尾以外を編集しているときかprev_preeditを変更するときはカーソルを消す */
668 if (preedit->cursor != preedit->width || eq_width != prev_preedit->width) {
669 put_cursor_invisible();
670 }
671
672 /* preedit == prev_preeditのときは、カーソルの移動だけ */
673 if (eq_width == preedit->width && eq_width == prev_preedit->width && eq_width > 0) {
674 if (g_opt.no_report_cursor) {
675 put_move_cur(prev_preedit->cursor, preedit->cursor);
676 } else {
677 goto_char(preedit->cursor);
678 }
679 return;
680 }
681
682 if (!g_opt.no_report_cursor) {
683 set_line2width(preedit);
684 }
685
686 /* 出力する位置に移動 */
687 if (g_opt.no_report_cursor) {
688 put_move_cur(prev_preedit->cursor, eq_width);
689 } else {
690 goto_col(eq_width);
691 }
692
693 /* 領域が変わっていないので変更部分だけ上書き */
694 if ((g_opt.no_report_cursor && preedit->width == prev_preedit->width) || (!g_opt.no_report_cursor && is_eq_region())) {
695 int eq_width_rev = compare_preedit_rev(preedit, prev_preedit);
696 debug2(("eq_width_rev = %d\n", eq_width_rev));
697 draw_subpreedit(preedit, eq_width, preedit->width - eq_width_rev);
698 if (g_opt.no_report_cursor) {
699 put_move_cur(preedit->width - eq_width_rev, preedit->cursor);
700 } else {
701 goto_char(preedit->cursor);
702 }
703 return;
704 }
705
706 if (g_opt.no_report_cursor && g_opt.on_the_spot && preedit->width > prev_preedit->width) {
707 put_insert(preedit->width - prev_preedit->width);
708 }
709 draw_subpreedit(preedit, eq_width, preedit->width);
710
711 if (g_opt.no_report_cursor) {
712 if (preedit->width > prev_preedit->width) {
713 put_cursor_left(preedit->width - preedit->cursor);
714 } else {
715 if (g_opt.on_the_spot) {
716 put_delete(prev_preedit->width - preedit->width);
717 put_cursor_left(preedit->width - preedit->cursor);
718 } else {
719 put_erase(prev_preedit->width - preedit->width);
720 put_cursor_left(prev_preedit->width - preedit->cursor);
721 }
722 }
723 } else {
724 erase_prev_preedit();
725 /* カーソルの位置に移動 */
726 goto_char(preedit->cursor);
727 }
728
729 }
730
is_eq_region(void)731 static int is_eq_region(void)
732 {
733 int lineno;
734 if (s_preedit_lines != s_prev_preedit_lines) {
735 return FALSE;
736 }
737 for (lineno = 0; lineno < s_preedit_lines; lineno++) {
738 if (s_line2width[lineno] != s_prev_line2width[lineno]) {
739 return FALSE;
740 }
741 }
742 return TRUE;
743 }
744
745 /*
746 * pの幅startの次の文字から幅endの文字まで出力する。
747 */
draw_subpreedit(struct preedit_tag * p,int start,int end)748 static void draw_subpreedit(struct preedit_tag *p, int start, int end)
749 {
750 int i = 0;
751 int w = 0;
752 int byte_offset = 0;
753 int width = end - start;
754 int save_i;
755
756 if (width <= 0 || p->width <= start) {
757 return;
758 }
759
760 /* startがどこかを調べる */
761 if (start != 0) {
762 for (i = 0; i < p->nr_psegs; i++) {
763 char *seg_str = p->pseg[i].str;
764 int seg_w = strwidth(seg_str);
765 if (w + seg_w == start) {
766 i++;
767 break;
768 } else if (w + seg_w > start) {
769 byte_offset = (width2byte(seg_str, start - w))[0];
770 break;
771 }
772 w += seg_w;
773 }
774 }
775
776 assert(i < p->nr_psegs);
777
778 w = 0;
779
780 save_i = i;
781 p->pseg[save_i].str += byte_offset;
782 for (; i < p->nr_psegs; i++) {
783 char *seg_str = p->pseg[i].str;
784 int seg_w = strwidth(seg_str);
785 if (w + seg_w <= width) {
786 if (g_opt.no_report_cursor) {
787 put_uim_str(seg_str, p->pseg[i].attr);
788 } else {
789 draw_pseg(&(p->pseg[i]), start + w);
790 }
791 w += seg_w;
792 if (w == width) {
793 break;
794 }
795 } else {
796 int *byte_width = width2byte(seg_str, width - w);
797 int byte = byte_width[0];
798 int save_char = seg_str[byte];
799 seg_str[byte] = '\0';
800 if (g_opt.no_report_cursor) {
801 put_uim_str(seg_str, p->pseg[i].attr);
802 } else {
803 draw_pseg(&(p->pseg[i]), start + w);
804 }
805 seg_str[byte] = save_char;
806 w += byte_width[1];
807 break;
808 }
809 }
810 p->pseg[save_i].str -= byte_offset;
811 }
812
813 /*
814 * psegを描画する
815 * 幅start_widthの次の文字がpsegの先頭文字
816 */
draw_pseg(struct preedit_segment_tag * pseg,int start_width)817 static void draw_pseg(struct preedit_segment_tag *pseg, int start_width)
818 {
819 struct point_tag start_point = width2point_col(start_width);
820 int margin = g_win->ws_col - start_point.col;
821 int lineno = start_point.row - s_head.row;
822 char *seg_str = pseg->str;
823 int seg_w = strwidth(seg_str);
824
825 assert(margin >= 0 && margin <= g_win->ws_col);
826
827 while (TRUE) {
828 int *byte_width;
829 int byte;
830 int width;
831
832 if (g_opt.on_the_spot && s_line2width[lineno] > s_prev_line2width[lineno]) {
833 int margin2 = margin - g_win->ws_col - s_prev_line2width[lineno];
834 if (seg_w >= margin2) {
835 byte_width = width2byte(seg_str, margin2);
836 byte = byte_width[0];
837 width = byte_width[1];
838 put_uim_str_len(seg_str, pseg->attr, byte);
839 if (width < margin2) {
840 put_cursor_address(s_head.row + lineno, s_prev_line2width[lineno]);
841 put_insert(s_line2width[lineno] - s_prev_line2width[lineno]);
842 put_cursor_address(s_head.row + lineno, s_prev_line2width[lineno] - (margin2 - width));
843 } else {
844 put_insert(s_line2width[lineno] - s_prev_line2width[lineno]);
845 }
846 seg_str += byte;
847 margin -= width;
848 seg_w -= width;
849 s_prev_line2width[lineno] = s_line2width[lineno];
850 }
851 }
852
853 /* 折り返す必要がないか */
854 if (seg_w < margin) {
855 put_uim_str(seg_str, pseg->attr);
856 break;
857 }
858
859 byte_width = width2byte(seg_str, margin);
860 byte = byte_width[0];
861 width = byte_width[1];
862
863 /* 行末まで出力 */
864 put_uim_str_len(seg_str, pseg->attr, byte);
865
866 /* 右端の文字を消す必要があるか */
867 if (s_line2width[lineno] < s_prev_line2width[lineno]) {
868 if (g_opt.on_the_spot) {
869 put_delete(s_prev_line2width[lineno] - s_line2width[lineno]);
870 } else {
871 put_erase(s_prev_line2width[lineno] - s_line2width[lineno]);
872 }
873 s_prev_line2width[lineno] = s_line2width[lineno];
874 }
875
876 /* プリエディットが端末の右下から出るときはスクロールする */
877 if (s_head.row + lineno == g_win->ws_row - 1 && s_head.row > 0) {
878 put_crlf();
879 s_head.row--;
880 } else {
881 put_cursor_address(s_head.row + lineno + 1, 0);
882 }
883
884 seg_str += byte;
885 seg_w -= width;
886 margin = g_win->ws_col;
887 lineno++;
888 }
889 }
890
891 /*
892 * p1とp2の先頭からの共通部分文字列(属性も等しい)の幅を返す
893 */
compare_preedit(struct preedit_tag * p1,struct preedit_tag * p2)894 static int compare_preedit(struct preedit_tag *p1, struct preedit_tag *p2)
895 {
896 int i;
897 int eq_width = 0;
898
899 for (i = 0; i < min(p1->nr_psegs, p2->nr_psegs); i++) {
900 struct preedit_segment_tag *pseg1 = &(p1->pseg[i]);
901 struct preedit_segment_tag *pseg2 = &(p2->pseg[i]);
902 if (pseg1->attr == pseg2->attr) {
903 if (strcmp(pseg1->str, pseg2->str) == 0) {
904 eq_width += strwidth(pseg1->str);
905 } else {
906 eq_width += compare_str(pseg1->str, pseg2->str);
907 break;
908 }
909 } else {
910 break;
911 }
912 }
913 return eq_width;
914 }
915
916 /*
917 * p1とp2の末尾からの共通部分文字列(属性も等しい)の幅を返す
918 */
compare_preedit_rev(struct preedit_tag * p1,struct preedit_tag * p2)919 static int compare_preedit_rev(struct preedit_tag *p1, struct preedit_tag *p2)
920 {
921 int i;
922 int eq_width_rev = 0;
923
924 for (i = 1; i <= min(p1->nr_psegs, p2->nr_psegs); i++) {
925 struct preedit_segment_tag *pseg1 = &(p1->pseg[p1->nr_psegs - i]);
926 struct preedit_segment_tag *pseg2 = &(p2->pseg[p2->nr_psegs - i]);
927 if (pseg1->attr == pseg2->attr) {
928 if (strcmp(pseg1->str, pseg2->str) == 0) {
929 eq_width_rev += strwidth(pseg1->str);
930 } else {
931 eq_width_rev += compare_str_rev(pseg1->str, pseg2->str);
932 break;
933 }
934 } else {
935 break;
936 }
937 }
938 return eq_width_rev;
939 }
940
min(int a,int b)941 static int min(int a, int b)
942 {
943 return a < b ? a : b;
944 }
945
erase_prev_preedit(void)946 static void erase_prev_preedit(void)
947 {
948 int lineno;
949 for (lineno = 0; lineno < s_preedit_lines; lineno++) {
950 assert(!g_opt.on_the_spot || s_line2width[lineno] <= s_prev_line2width[lineno]);
951 if (s_prev_line2width[lineno] > s_line2width[lineno]) {
952 put_cursor_address(s_head.row + lineno, s_line2width[lineno]);
953 if (g_opt.on_the_spot) {
954 put_delete(s_prev_line2width[lineno] - s_line2width[lineno]);
955 } else {
956 put_erase(s_prev_line2width[lineno] - s_line2width[lineno]);
957 }
958 }
959 }
960 for (; lineno < s_prev_preedit_lines; lineno++) {
961 if (s_prev_line2width[lineno] > 0) {
962 put_cursor_address(s_head.row + lineno, 0);
963 if (g_opt.on_the_spot) {
964 put_delete(s_prev_line2width[lineno]);
965 } else {
966 put_erase(s_prev_line2width[lineno]);
967 }
968 }
969 }
970 }
971
erase_preedit(void)972 static void erase_preedit(void)
973 {
974 s_prev_preedit_lines = s_preedit_lines;
975 s_preedit_lines = 1;
976 free(s_prev_line2width);
977 s_prev_line2width = s_line2width;
978 s_line2width = uim_malloc(sizeof(int));
979 s_line2width[0] = s_head.col;
980 erase_prev_preedit();
981 }
982
set_line2width(struct preedit_tag * preedit)983 static void set_line2width(struct preedit_tag *preedit)
984 {
985 int i;
986 int line_width = s_head.col;
987 int lineno = 0;
988
989 free(s_prev_line2width);
990 s_prev_line2width = s_line2width;
991 s_prev_preedit_lines = s_preedit_lines;
992 s_line2width = uim_malloc(sizeof(int));
993 s_preedit_lines = 1;
994
995 for (i = 0; i < preedit->nr_psegs; i++) {
996 char *seg_str = preedit->pseg[i].str;
997 int seg_w = strwidth(seg_str);
998 while (line_width + seg_w >= g_win->ws_col) {
999 if (lineno + 1 == s_preedit_lines) {
1000 s_line2width = uim_realloc(s_line2width, sizeof(int) * ++s_preedit_lines);
1001 }
1002 if (line_width + seg_w > g_win->ws_col) {
1003 int *byte_width = width2byte(seg_str, g_win->ws_col - line_width);
1004 s_line2width[lineno++] = line_width + byte_width[1];
1005 seg_str += byte_width[0];
1006 seg_w = strwidth(seg_str);
1007 debug2(("line = %d col = %d\n", lineno - 1, s_line2width[lineno - 1]));
1008 } else {
1009 s_line2width[lineno++] = g_win->ws_col;
1010 debug2(("line = %d col = %d\n", lineno - 1, s_line2width[lineno - 1]));
1011 seg_w = 0;
1012 }
1013 line_width = 0;
1014 }
1015 line_width += seg_w;
1016 }
1017 if (s_preedit_lines > s_prev_preedit_lines) {
1018 int i;
1019 s_prev_line2width = uim_realloc(s_prev_line2width, sizeof(int) * s_preedit_lines);
1020 for (i = s_prev_preedit_lines; i < s_preedit_lines; i++) {
1021 s_prev_line2width[i] = 0;
1022 }
1023 }
1024 s_line2width[lineno] = line_width;
1025 debug2(("line = %d col = %d\n", lineno, s_line2width[lineno]));
1026 }
1027
1028 /*
1029 * プリエディットの幅widthの次の文字に移動する
1030 * goto_char(0) => 先頭の文字に移動
1031 */
goto_char(int width)1032 static void goto_char(int width)
1033 {
1034 struct point_tag dest = width2point_char(width);
1035 assert(width >= 0);
1036 assert(s_preedit_lines > 0);
1037 put_cursor_address(dest.row, dest.col);
1038 }
1039
1040 /*
1041 * プリエディットの幅widthの次のcolumnに移動
1042 * goto_col(0) => 先頭に移動
1043 */
goto_col(int width)1044 static void goto_col(int width)
1045 {
1046 struct point_tag dest = width2point_col(width);
1047 assert(width >= 0);
1048 assert(s_preedit_lines > 0);
1049 put_cursor_address(dest.row, dest.col);
1050 }
1051
1052 /*
1053 * プリエディットの幅widthの次の文字が属す行番号を返す
1054 * 行番号は0から始まる
1055 */
width2lineno_char(int width)1056 static int width2lineno_char(int width)
1057 {
1058 int i;
1059 int w = -s_head.col;
1060 assert(s_preedit_lines > 0);
1061 for (i = 0; i < s_preedit_lines - 1; i++) {
1062 w += s_line2width[i];
1063 if (width < w) {
1064 return i;
1065 }
1066 }
1067 return s_preedit_lines - 1;
1068 }
1069
1070 /*
1071 * プリエディットの幅widthの次のcolumnが属す行番号を返す
1072 * 行番号は0から始まる
1073 */
width2lineno_col(int width)1074 static int width2lineno_col(int width)
1075 {
1076 int i;
1077 int w = -s_head.col;
1078 assert(s_preedit_lines > 0);
1079 for (i = 0; i < s_preedit_lines - 1; i++) {
1080 if (width < w + g_win->ws_col) {
1081 return i;
1082 }
1083 w += s_line2width[i];
1084 }
1085 return s_preedit_lines - 1;
1086 /* 端末サイズが変更されたときはここにくることもある */
1087 /* assert(s_winch); */
1088 /* return s_preedit_lines; */
1089 }
1090
1091 /*
1092 * プリエディットの幅widthの次の文字の位置を返す
1093 */
width2point_char(int width)1094 static struct point_tag width2point_char(int width)
1095 {
1096 struct point_tag point;
1097 int lineno = width2lineno_char(width);
1098 int col = s_head.col + width;
1099 int i;
1100 assert(s_preedit_lines > 0);
1101 for (i = 0; i < lineno; i++) {
1102 col -= s_line2width[i];
1103 }
1104 point.row = s_head.row + lineno;
1105 point.col = col;
1106 return point;
1107 }
1108
1109 /*
1110 * プリエディットの幅widthの次のcolumnの位置を返す
1111 */
width2point_col(int width)1112 static struct point_tag width2point_col(int width)
1113 {
1114 struct point_tag point;
1115 int lineno = width2lineno_col(width);
1116 int col = s_head.col + width;
1117 int i;
1118 assert(s_preedit_lines > 0);
1119 for (i = 0; i < lineno; i++) {
1120 col -= s_line2width[i];
1121 }
1122 point.row = s_head.row + lineno;
1123 point.col = col;
1124 return point;
1125 }
1126
1127 /*
1128 * 端末サイズが変更されたときに呼ぶ
1129 */
draw_winch(struct winsize * prev_win)1130 void draw_winch(struct winsize *prev_win)
1131 {
1132 s_winch = TRUE;
1133 if (g_opt.status_type == LASTLINE) {
1134 put_save_cursor();
1135 put_cursor_invisible();
1136 put_change_scroll_region(0, g_win->ws_row - 1);
1137 if (g_win->ws_row > prev_win->ws_row) {
1138 struct winsize save_win = *g_win;
1139 *g_win = *prev_win;
1140 clear_lastline();
1141 *g_win = save_win;
1142 }
1143 draw_statusline_force_no_restore();
1144 put_restore_cursor();
1145 put_cursor_normal();
1146 }
1147 }
1148
1149 #if defined(DEBUG) && DEBUG > 2
print_preedit(struct preedit_tag * p)1150 static void print_preedit(struct preedit_tag *p)
1151 {
1152 int i;
1153 debug2((" cursor = %d width = %d ", p->cursor, p->width));
1154 for (i = 0; i < p->nr_psegs; i++) {
1155 debug2((" under = %d rev = %d %s\t", p->pseg[i].attr & UPreeditAttr_UnderLine, p->pseg[i].attr & UPreeditAttr_Reverse, p->pseg[i].str));
1156 }
1157 }
1158 #endif
1159