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