(main): Copyright years updated.
[m17n/m17n-lib.git] / example / medit.c
1 /* medit.c -- simple multilingual editor.               -*- coding: euc-jp; -*-
2    Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21    02111-1307, USA.  */
22
23 /***en
24     @enpage m17n-edit edit multilingual text
25
26     @section m17n-edit-synopsis SYNOPSIS
27
28     m17n-edit [ XT-OPTION ...] [ OPTION ... ] FILE
29
30     @section m17n-edit-description DESCRIPTION
31
32     Display FILE on a window and allow users to edit it.
33
34     XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).
35
36     The following OPTIONs are available.
37
38     <ul>
39
40     <li> --version
41
42     Print version number.
43
44     <li> -h, --help
45
46     Print this message.
47
48     </ul>
49
50     This program is to demonstrate how to use the m17n GUI API.
51     Although m17n-edit directly uses the GUI API, the API is mainly
52     for toolkit libraries or to implement XOM (X Output Method), not
53     for direct use from application programs.
54 */
55 /***ja
56     @japage m17n-edit Â¿¸À¸ì¥Æ¥­¥¹¥È¤ÎÊÔ½¸
57
58     @section m17n-edit-synopsis SYNOPSIS
59
60     m17n-edit [ XT-OPTION ...] [ OPTION ... ] FILE
61
62     @section m17n-edit-description DESCRIPTION
63
64     FILE ¤ò¥¦¥£¥ó¥É¥¦¤Ëɽ¼¨¤·¡¢¥æ¡¼¥¶¤¬ÊÔ½¸¤Ç¤­¤ë¤è¤¦¤Ë¤¹¤ë¡£
65
66     XT-OPTIONs ¤Ï Xt ¤Îɸ½à¤Î°ú¿ô¤Ç¤¢¤ë¡£ (e.g. -fn, -fg). 
67
68     °Ê²¼¤Î¥ª¥×¥·¥ç¥ó¤¬ÍøÍѤǤ­¤ë¡£ 
69
70     <ul>
71
72     <li> --version
73
74     ¥Ð¡¼¥¸¥ç¥óÈÖ¹æ¤òɽ¼¨¤¹¤ë¡£ 
75
76     <li> -h, --help
77
78     ¤³¤Î¥á¥Ã¥»¡¼¥¸¤òɽ¼¨¤¹¤ë¡£ 
79
80     </ul>
81
82     ¤³¤Î¥×¥í¥°¥é¥à¤Ï m17n GUI API ¤Î»È¤¤Êý¤ò¼¨¤¹¤â¤Î¤Ç¤¢¤ë¡£m17n-edit
83     ¤ÏľÀÜ GUI API ¤ò»È¤Ã¤Æ¤¤¤ë¤¬¡¢¤³¤Î API ¤Ï¼ç¤Ë¥Ä¡¼¥ë¥­¥Ã¥È¥é¥¤¥Ö¥é
84     ¥ê¤äXOM (X Output Method) ¤Î¼ÂÁõÍѤǤ¢¤ê¡¢¥¢¥×¥ê¥±¡¼¥·¥ç¥ó¥×¥í¥°¥é
85     ¥à¤«¤é¤ÎľÀܤÎÍøÍѤò°Õ¿Þ¤·¤Æ¤¤¤Ê¤¤¡£
86 */
87
88 #ifndef FOR_DOXYGEN
89
90 #include <stdio.h>
91 #include <stdlib.h>
92 #include <sys/types.h>
93 #include <sys/stat.h>
94 #include <fcntl.h>
95 #include <unistd.h>
96 #include <libgen.h>
97 #include <locale.h>
98 #include <dlfcn.h>
99
100 #ifdef HAVE_X11_XAW_COMMAND_H
101
102 #include <X11/keysym.h>
103 #include <X11/Xatom.h>
104 #include <X11/Intrinsic.h>
105 #include <X11/StringDefs.h>
106 #include <X11/Shell.h>
107
108 #include <m17n-gui.h>
109 #include <m17n-misc.h>
110 #include <m17n-X.h>
111
112 #include <X11/Xaw/Command.h>
113 #include <X11/Xaw/Box.h>
114 #include <X11/Xaw/Form.h>
115 #include <X11/Xaw/Dialog.h>
116 #include <X11/Xaw/Scrollbar.h>
117 #include <X11/Xaw/Toggle.h>
118 #include <X11/Xaw/SimpleMenu.h>
119 #include <X11/Xaw/SmeBSB.h>
120 #include <X11/Xaw/SmeLine.h>
121 #include <X11/Xaw/MenuButton.h>
122
123 /* Global variables.  */
124
125 char *filename;
126 int serialized;
127
128 /* For the X Window System.  */
129 Display *display;
130 int screen;
131 /* GCs for normal drawing, filling by background color, normal drawing
132    on bitmap (i.e. pixmap of depth 1), filling bitmap by background
133    color. */
134 GC gc, gc_inv, mono_gc, mono_gc_inv;
135 Window win;
136 Atom XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING; /* X Selection types.  */
137 XtAppContext context;
138 int default_font_size;
139
140 /* Widget hierarchy
141
142 Shell - Form -+- Head -- File, Cursor, Bidi, LineBreak, InputMethod, CurIM;
143               +- Face -- Size, Family, Style, Color, Misc, Pop, CurFace
144               +- Lang -- A-B, C-D, ..., U-Z, Pop, CurLang
145               +- Body -- Sbar, Text
146               +- Tail -- Message
147 */
148
149 Widget ShellWidget, HeadWidget, TailWidget, MessageWidget;
150 Widget CursorMenus[5], BidiMenus[3], LineBreakMenus[3], *InputMethodMenus;
151 Widget SbarWidget, TextWidget;
152 Widget FileShellWidget, FileDialogWidget;
153 Widget FaceWidget, CurFaceWidget, LangWidget, CurLangWidget;
154 Widget CurIMLang, CurIMStatus;
155
156 int win_width, win_height;      /* Size of TextWidget.  */
157 Arg arg[10];
158
159 Pixmap input_status_pixmap;
160 int input_status_width, input_status_height;
161
162 /* Bitmap for "check" glyph.  */
163 #define check_width 9
164 #define check_height 8
165 static unsigned char check_bits[] = {
166    0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
167    0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00 };
168 Pixmap CheckPixmap;
169
170 /* For the m17n library.  */
171 MFrame *frame;
172 MText *mt;
173 int nchars;                     /* == mtext_len (mt) */
174 int mt_modified;
175 MDrawControl control, input_status_control;
176 MTextProperty *selection;
177
178 MSymbol Mword;
179
180 MFace *face_default;
181 MFace *face_xxx_large;
182 MFace *face_box;
183 MFace *face_courier, *face_helvetica, *face_times;
184 MFace *face_dv_ttyogesh, *face_freesans, *face_freeserif, *face_freemono;
185 MFace *face_default_fontset, *face_no_ctl_fontset;
186 MFace *face_input_status;
187
188 MSymbol Mcoding_compound_text;
189
190 int logical_move = 1;           /* If 0, move cursor visually.  */
191
192 typedef struct {
193   int available;
194   MSymbol language, name;
195   MInputMethod *im;
196 } InputMethodInfo;
197
198 InputMethodInfo *input_method_table;
199
200 int num_input_methods;
201 int current_input_method = -1;  /* i.e. none */
202 int unicode_input_method = -1;
203 int auto_input_method = 0;
204 int saved_input_method = -3;
205 MInputContext *current_input_context;
206
207 struct FaceRec
208 {
209   char *name;
210   MFace **face;
211 } face_table[] =
212   { {"Menu Size", NULL},
213     {"xx-small", &mface_xx_small},
214     {"x-small", &mface_x_small},
215     {"small", &mface_small},
216     {"normalsize", &mface_normalsize},
217     {"large", &mface_large},
218     {"x-large", &mface_x_large},
219     {"xx-large", &mface_xx_large},
220     {"xxx-large", &face_xxx_large},
221
222     {"Menu Family", NULL},
223     {"courier", &face_courier},
224     {"helvetica", &face_helvetica},
225     {"times", &face_times},
226     {"dv-ttyogesh", &face_dv_ttyogesh},
227     {"freesans", &face_freesans},
228     {"freeserif", &face_freeserif},
229     {"freemono", &face_freemono},
230
231     {"Menu Style", NULL},
232     {"medium", &mface_medium},
233     {"bold", &mface_bold},
234     {"italic", &mface_italic},
235
236     {"Menu Color", NULL},
237     {"black", &mface_black},
238     {"white", &mface_white},
239     {"red", &mface_red},
240     {"green", &mface_green},
241     {"blue", &mface_blue},
242     {"cyan", &mface_cyan},
243     {"yello", &mface_yellow},
244     {"magenta", &mface_magenta},
245
246     {"Menu Misc", NULL},
247     {"normal", &mface_normal_video},
248     {"reverse", &mface_reverse_video},
249     {"underline", &mface_underline},
250     {"box", &face_box},
251     {"No CTL", &face_no_ctl_fontset} };
252
253
254 int num_faces = sizeof (face_table) / sizeof (struct FaceRec);
255
256 /* Information about a physical line metric.  */
257 struct LineInfo
258 {
259   int from;                     /* BOL position of the line.  */
260   int to;                       /* BOL position of the next line.  */
261   int y0, y1;                   /* Top and bottom Y position of the line.  */
262   int ascent;                   /* Height of the top Y position.  */
263 };
264
265 struct LineInfo top;            /* Topmost line.  */
266 struct LineInfo cur;            /* Line containing cursor.  */
267 struct LineInfo sel_start;     /* Line containing selection start.  */
268 struct LineInfo sel_end;        /* Line containing selection end.  */
269
270 MDrawGlyphInfo cursor;      /* Information about the cursor glyph.  */
271
272 /* X position to keep on vertical (up and down) cursor motion. */
273 int target_x_position;
274
275 /* Interface macros for m17n-lib drawing routines. */
276
277 /* Draw a text in the range $FROM to $TO of the M-text #MT at the
278    coordinate ($X, $Y)  */
279 #define DRAW_TEXT(x, y, from, to)                               \
280     mdraw_text_with_control                                     \
281       (frame, (MDrawWindow) win,                                \
282        control.orientation_reversed ? x + win_width : x, y,     \
283        mt, from, to, &control)
284
285 /* Store the extents of a text in the range $FROM to $TO in the
286    structure $RECT (type MDrawMetric).  */
287 #define TEXT_EXTENTS(from, to, rect)    \
288   mdraw_text_extents (frame, mt, from, (to), &control, NULL, NULL, &(rect))
289
290 /* Store the glyph information of a character at the position $POS in
291    the struct $INFO (type MDrawGlyphInfo) assuming that the text from
292    $FROM is written at the coordinate (0, 0).  */
293 #define GLYPH_INFO(from, pos, info)     \
294   mdraw_glyph_info (frame, mt, from, (pos), &control, &(info))
295
296 /* Set $X and $Y to the coordinate of character at position $POS
297    assuming that the text from $FROM is written at the coordinate (0,
298    0).  */
299 #define COORDINATES_POSITION(from, pos, x, y)   \
300   mdraw_coordinates_position (frame, mt, (from), (pos), (x), (y), &control)
301
302 /* Interface macros for X library.  */
303 #define COPY_AREA(y0, y1, to)   \
304   XCopyArea (display, win, win, gc, 0, (y0), win_width, (y1) - (y0), 0, (to))
305
306 #define CLEAR_AREA(x, y, w, h)  \
307   XClearArea (display, win, (x), (y), (w), (h), False)
308
309 #define SELECTEDP() \
310   mtext_property_mtext (selection)
311
312 /* Format MSG by FMT and print the result to the stderr, and exit.  */
313 #define FATAL_ERROR(fmt, arg)   \
314   do {                          \
315     fprintf (stderr, fmt, arg); \
316     exit (1);                   \
317   } while (0)
318
319
320 /* If POS is greater than zero, move POS back to the beginning of line
321    (BOL) position.  If FORWARD is nonzero, move POS forward instead.
322    Return the new position.  */
323 int
324 bol (int pos, int forward)
325 {
326   int limit = forward ? nchars : 0;
327
328   pos = mtext_character (mt, pos, limit, '\n');
329   return (pos < 0 ? limit : pos + 1);
330 }
331
332 /* Update the structure #TOP (struct LineInfo) to make $POS the first
333    character position of the screen.  */
334 void
335 update_top (int pos)
336 {
337   int from = bol (pos, 0);
338   MDrawGlyphInfo info;
339
340   GLYPH_INFO (from, pos, info);
341   top.from = info.line_from;
342   top.to = info.line_to;
343   top.y0 = 0;
344   top.y1 = info.metrics.height;
345   top.ascent = - info.metrics.y;
346 }
347
348
349 /* Update the scroll bar so that the text of the range $FROM to $TO
350    are shown on the window.  */
351 void
352 update_scroll_bar (int from, int to)
353 {
354   float top = (float) from / nchars;
355   float shown = (float) (to - from) / nchars;
356   XtArgVal *l_top = (XtArgVal *) &top;
357   XtArgVal *l_shown = (XtArgVal *) &shown;
358
359   XtSetArg (arg[0], XtNtopOfThumb, *l_top);
360   XtSetArg (arg[1], XtNshown, *l_shown);
361   XtSetValues (SbarWidget, arg, 2);
362 }
363
364
365 /* Redraw the window area between $Y0 and $Y1 (both Y-codinates).  If
366    $CLEAR is nonzero, clear the area before drawing.  If $SCROLL_BAR
367    is nonzero, update the scoll bar.  */
368 void
369 redraw (int y0, int y1, int clear, int scroll_bar)
370 {
371   int from, to;
372   int y;
373   MDrawGlyphInfo info;
374   int sel_y0 = SELECTEDP () ? sel_start.y0 : 0;
375   struct LineInfo *line;
376   
377   if (clear || control.anti_alias)
378     CLEAR_AREA (0, y0, win_width, y1 - y0);
379
380   /* Find a line closest to y0.  It is a cursor line if the cursor is
381      Y0, otherwise the top line.  */
382   if (y0 >= cur.y0)
383     line = &cur;
384   else
385     line = &top;
386   /* If there exists a selected region, check it too.  */
387   if (sel_y0 > line->y0 && y0 >= sel_y0)
388     line = &sel_start;
389
390   from = line->from;
391   y = line->y0;
392   info.metrics.height = line->y1 - y;
393   info.metrics.y = - line->ascent;
394   info.line_to = line->to;
395   while (y + info.metrics.height <= y0)
396     {
397       y += info.metrics.height;
398       from = info.line_to;
399       if (from >= nchars)
400         break;
401       GLYPH_INFO (from, from, info);
402     }
403   if (y + info.metrics.height <= y0)
404     return;
405
406   y0 = y - info.metrics.y;
407   to = from;
408   while (to < nchars && y < y1)
409     {
410       GLYPH_INFO (to, to, info);
411       y += info.metrics.height;
412       to = info.line_to;
413     }
414   if (to == nchars)
415     to++;
416   if (from < to)
417     DRAW_TEXT (0, y0, from, to);
418   if (scroll_bar)
419     {
420       while (to < nchars)
421         {
422           GLYPH_INFO (to, to, info);
423           if (y + info.metrics.height >= win_height)
424             break;
425           to = info.line_to;
426           y += info.metrics.height;
427         }
428       update_scroll_bar (top.from, to);
429     }
430 }
431      
432
433 /* Set the current input method spot to the correct position.  */
434 void
435 set_input_method_spot ()
436 {
437   int x = cursor.x + (control.orientation_reversed ? win_width : 0);
438   int pos = cursor.from > 0 ? cursor.from - 1 : 0;
439   MFace *faces[256];
440   int n = 0;
441   int size = 0, ratio = 0, i;
442
443   if (nchars > 0)
444     n = mtext_get_prop_values (mt, pos, Mface, (void **) faces, 256);
445   if (n > 0)
446     for (i = n - 1; i >= 0; i--)
447       {
448         if (! size)
449           size = (int) mface_get_prop (faces[i], Msize);
450         if (! ratio)
451           ratio = (int) mface_get_prop (faces[i], Mratio);
452       }
453   if (! size)
454     size = default_font_size;
455   if (ratio)
456     size = size * ratio / 100;
457   minput_set_spot (current_input_context, x, cur.y0 + cur.ascent,
458                    cur.ascent, cur.y1 - (cur.y0 + cur.ascent), size,
459                    mt, cursor.from);
460 }
461
462
463 /* Redraw the cursor.  If $CLEAR is nonzero, clear the cursor area
464    before drawing.  */
465 void
466 redraw_cursor (int clear)
467 {
468   if (control.cursor_bidi)
469     {
470       /* We must update the whole line of the cursor.  */
471       int beg = bol (cur.from, 0);
472       int end = bol (cur.to - 1, 1);
473       MDrawMetric rect;
474       int y0 = cur.y0, y1 = cur.y1;
475
476       if (beg < cur.from)
477         {
478           TEXT_EXTENTS (beg, cur.from, rect);
479           y0 -= rect.height;
480         }
481       if (end > cur.to)
482         {
483           TEXT_EXTENTS (cur.to, end, rect);
484           y1 += rect.height; 
485         }
486       redraw (y0, y1, clear, 0);
487     }
488   else
489     {
490       if (clear)
491         {
492           int x = cursor.x;
493
494           if (control.orientation_reversed)
495             x += win_width - cursor.logical_width;
496           CLEAR_AREA (x, cur.y0, cursor.logical_width, cursor.metrics.height);
497         }
498       DRAW_TEXT (cursor.x, cur.y0 + cur.ascent, cursor.from, cursor.to);
499     }
500 }
501
502
503 /* Update the information about the location of cursor to the position
504    $POS.  If $FULL is nonzero, update the information fully only from
505    the information about the top line.  Otherwise, trust the current
506    information in the structure $CUR.  */
507 void
508 update_cursor (int pos, int full)
509 {
510   MDrawMetric rect;
511
512   control.cursor_pos = pos;
513   if (full)
514     {
515       /* CUR is inaccurate.  We can trust only TOP.  */
516       GLYPH_INFO (top.from, pos, cursor);
517       cur.y0 = top.ascent + cursor.y + cursor.metrics.y;
518     }
519   else if (pos < cur.from)
520     {
521       int from = bol (pos, 0);
522
523       TEXT_EXTENTS (from, cur.from, rect);
524       GLYPH_INFO (from, pos, cursor);
525       cur.y0 -= (rect.height + rect.y) - (cursor.y + cursor.metrics.y);
526     }
527   else if (pos < cur.to)
528     {
529       GLYPH_INFO (cur.from, pos, cursor);
530     }
531   else
532     {
533       GLYPH_INFO (cur.from, pos, cursor);
534       cur.y0 += cur.ascent + cursor.y + cursor.metrics.y;
535     }
536
537   cur.from = cursor.line_from;
538   cur.to = cursor.line_to;
539   cur.y1 = cur.y0 + cursor.metrics.height;
540   cur.ascent = - cursor.metrics.y;
541 }
542
543
544 /* Update the information about the selected region.  */
545 void
546 update_selection ()
547 {
548   int from, to;
549   MDrawMetric rect;
550   MDrawGlyphInfo info;
551
552   if (! SELECTEDP ())
553     return;
554   from = mtext_property_start (selection);
555   to = mtext_property_end (selection);
556
557   if (from < top.from)
558     {
559       int pos = bol (from, 0);
560
561       TEXT_EXTENTS (pos, top.from, rect);
562       sel_start.y0 = top.y0 - rect.height;
563       sel_start.ascent = - rect.y;
564       GLYPH_INFO (pos, from, info);
565       if (pos < info.line_from)
566         sel_start.y0 += - rect.y + info.y + info.metrics.y;
567     }
568   else
569     {
570       GLYPH_INFO (top.from, from, info);
571       sel_start.y0 = top.ascent + info.y + info.metrics.y;
572     }
573   sel_start.ascent = -info.metrics.y;
574   sel_start.y1 = sel_start.y0 + info.metrics.height;
575   sel_start.from = info.line_from;
576   sel_start.to = info.line_to;
577
578   if (to <= sel_start.to)
579     {
580       sel_end = sel_start;
581     }
582   else
583     {
584       GLYPH_INFO (sel_start.from, to, info);
585       sel_end.y0 = sel_start.y0 + sel_start.ascent + info.y + info.metrics.y;
586       sel_end.y1 = sel_end.y0 + info.metrics.height;
587       sel_end.ascent = - info.metrics.y;
588       sel_end.from = info.line_from;
589       sel_end.to = info.line_to;
590     }
591 }
592
593
594 /* Select the text in the region from $FROM to $TO.  */
595 void
596 select_region (int from, int to)
597 {
598   int pos;
599
600   if (from > to)
601     pos = from, from = to, to = pos;
602   mtext_push_property (mt, from, to, selection);
603   update_selection ();
604 }
605
606
607 /* Setup the window to display the character of $POS at the top left
608    of the window.  */
609 void
610 reseat (int pos)
611 {
612   MDrawMetric rect;
613   /* Top and bottom Y positions to redraw.  */
614   int y0, y1;
615
616   if (pos + 1000 < top.from)
617     y0 = 0, y1 = win_height;
618   else if (pos < top.from)
619     {
620       y0 = 0;
621       TEXT_EXTENTS (pos, top.from, rect);
622       if (rect.height >= win_height * 0.9)
623         y1 = win_height;
624       else
625         {
626           y1 = rect.height;
627           COPY_AREA (0, win_height - y1, y1);
628         }
629     }
630   else if (pos < top.to)
631     {
632       /* No need of redrawing.  */
633       y0 = y1 = 0;
634     }
635   else if (pos < top.from + 1000)
636     {
637       TEXT_EXTENTS (top.from, pos, rect);
638       if (rect.height >= win_height * 0.9)
639         y0 = 0;
640       else
641         {
642           y0 = win_height - rect.height;
643           COPY_AREA (rect.height, win_height, 0);
644         }
645       y1 = win_height;
646     }
647   else
648     y0 = 0, y1 = win_height;
649
650   if (y0 < y1)
651     {
652       update_top (pos);
653       if (cur.to <= pos)
654         update_cursor (pos, 1);
655       else
656         update_cursor (cursor.from, 1);
657       update_selection ();
658       redraw (y0, y1, 1, 1);
659     }
660 }
661
662 static void MenuHelpProc (Widget, XEvent *, String *, Cardinal *);
663
664
665 /* Select an input method accoding to $IDX.  If $IDX is negative, turn
666    off the current input method, otherwide turn on the input method
667    input_method_table[$IDX].  */
668 void
669 select_input_method (idx)
670 {
671   int previous_input_method = current_input_method;
672
673   if (idx == current_input_method)
674     return;
675   if (current_input_method >= 0)
676     {
677       minput_destroy_ic (current_input_context);
678       current_input_context = NULL;
679       current_input_method = -1;
680     }
681
682   if (idx >= 0
683       && input_method_table[idx].available >= 0)
684     {
685       InputMethodInfo *im = input_method_table + idx;
686
687       if (im->available == 0)
688         {
689           if (im->language)
690             im->im = minput_open_im (im->language, im->name, NULL);
691           else
692             {
693               MInputXIMArgIM arg_xim;
694
695               arg_xim.display = display;
696               arg_xim.db = NULL;  
697               arg_xim.res_name = arg_xim.res_class = NULL;
698               arg_xim.locale = NULL;
699               arg_xim.modifier_list = NULL;
700               im->im = minput_open_im (Mnil, im->name, &arg_xim);
701             }
702           im->available = im->im ? 1 : -1;
703         }
704       if (im->im)
705         {
706           if (im->language == Mnil)
707             {
708               MInputXIMArgIC arg_xic;
709               Window win = XtWindow (TextWidget);
710
711               arg_xic.input_style = 0;
712               arg_xic.client_win = arg_xic.focus_win = win;
713               arg_xic.preedit_attrs =  arg_xic.status_attrs = NULL;
714               current_input_context = minput_create_ic (im->im, &arg_xic);
715             }
716           else
717             {
718               MInputGUIArgIC arg_ic;
719
720               arg_ic.frame = frame;
721               arg_ic.client = (MDrawWindow) XtWindow (ShellWidget);
722               arg_ic.focus = (MDrawWindow) XtWindow (TextWidget);
723               current_input_context = minput_create_ic (im->im, &arg_ic);
724             }
725
726           if (current_input_context)
727             {
728               current_input_method = idx;
729               set_input_method_spot ();
730             }
731           else
732             {
733               minput_close_im (im->im);
734               im->im = NULL;
735               im->available = -1;
736               current_input_method = -1;
737             }
738         }
739     }
740   if (! auto_input_method)
741     {
742       XtSetArg (arg[0], XtNleftBitmap, None);
743       if (previous_input_method >= 0)
744         XtSetValues (InputMethodMenus[previous_input_method + 2], arg, 1);
745       else
746         XtSetValues (InputMethodMenus[0], arg, 1);
747       XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
748       if (current_input_method >= 0)
749         XtSetValues (InputMethodMenus[current_input_method + 2], arg, 1);
750       else
751         XtSetValues (InputMethodMenus[0], arg, 1);
752     }
753
754   if (current_input_method >= 0)
755     {
756       char *label;
757
758       XtSetArg (arg[0], XtNlabel, &label);
759       XtGetValues (InputMethodMenus[current_input_method + 2], arg, 1);
760       XtSetArg (arg[0], XtNlabel, label);
761     }
762   else
763     {
764       XtSetArg (arg[0], XtNlabel, "");
765     }
766   XtSetValues (CurIMLang, arg, 1);
767 }
768
769 static void MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num);
770
771
772 /* Display cursor according to the current information of #CUR.
773    $CLIENT_DATA is ignore.  Most callback functions add this function
774    as a background processing procedure the current application (by
775    XtAppAddWorkProc) via the function hide_cursor.  */
776 Boolean
777 show_cursor (XtPointer client_data)
778 {
779   MFaceHLineProp *hline;
780   MFaceBoxProp *box;
781
782   if (cur.y0 < 0)
783     {
784       reseat (cur.from);
785       update_cursor (cursor.from, 1);
786     }
787   while (cur.y1 > win_height)
788     {
789       reseat (top.to);
790       update_cursor (cursor.from, 1);
791     }
792
793   control.cursor_pos = cursor.from;
794   if (! SELECTEDP ())
795     {
796       control.with_cursor = 1;
797       redraw_cursor (0);
798     }
799   if (current_input_context)
800     set_input_method_spot ();
801
802   if (nchars > 0)
803     {
804       int pos = (SELECTEDP () ? mtext_property_start (selection)
805                  : cursor.from > 0 ? cursor.from - 1
806                  : cursor.from);
807       MFace *face = mface ();
808       MTextProperty *props[256];
809       int n = mtext_get_properties (mt, pos, Mface, props, 256);
810       int i;
811       char buf[256], *p = buf;
812       MSymbol sym;
813
814       buf[0] = '\0';
815       if (cursor.font)
816         {
817           int size = (int) mfont_get_prop (cursor.font, Msize);
818           MSymbol family = mfont_get_prop (cursor.font, Mfamily);
819           MSymbol weight = mfont_get_prop (cursor.font, Mweight);
820           MSymbol style = mfont_get_prop (cursor.font, Mstyle);
821           MSymbol registry = mfont_get_prop (cursor.font, Mregistry);
822
823           sprintf (p, "%dpt", size / 10), p += strlen (p);
824           if (family)
825             strcat (p, ","), strcat (p, msymbol_name (family)), p += strlen (p);
826           if (weight)
827             strcat (p, ","), strcat (p, msymbol_name (weight)), p += strlen (p);
828           if (style)
829             strcat (p, ","), strcat (p, msymbol_name (style)), p += strlen (p);
830           if (registry)
831             strcat (p, ","), strcat (p, msymbol_name (registry)), p += strlen (p);
832           p += strlen (p);
833         }
834
835       mface_merge (face, face_default);
836       for (i = 0; i < n; i++)
837         if (props[i] != selection)
838           mface_merge (face, (MFace *) mtext_property_value (props[i]));
839       sym = (MSymbol) mface_get_prop (face, Mforeground);
840       if (sym != Mnil)
841         strcat (p, ","), strcat (p, msymbol_name (sym)), p += strlen (p);
842       if ((MSymbol) mface_get_prop (face, Mvideomode) == Mreverse)
843         strcat (p, ",rev"), p += strlen (p);
844       hline = mface_get_prop (face, Mhline);
845       if (hline && hline->width > 0)
846         strcat (p, ",ul"), p += strlen (p);
847       box = mface_get_prop (face, Mbox);
848       if (box && box->width > 0)
849         strcat (p, ",box"), p += strlen (p);
850       m17n_object_unref (face);
851
852       XtSetArg (arg[0], XtNborderWidth, 1);
853       XtSetArg (arg[1], XtNlabel, buf);
854       XtSetValues (CurFaceWidget, arg, 2);
855     }
856
857   if (control.cursor_pos < nchars)
858     {
859       MSymbol sym = mtext_get_prop (mt, control.cursor_pos, Mlanguage);
860
861       if (sym == Mnil)
862         {
863           XtSetArg (arg[0], XtNborderWidth, 0);
864           XtSetArg (arg[1], XtNlabel, "");
865         }
866       else
867         {
868           XtSetArg (arg[0], XtNborderWidth, 1);
869           sym = mlanguage_name (sym);
870           XtSetArg (arg[1], XtNlabel, msymbol_name (sym));
871           XtSetValues (CurLangWidget, arg, 2);
872         }
873       XtSetValues (CurLangWidget, arg, 2);
874
875       if (auto_input_method)
876         {
877           if (sym == Mnil)
878             select_input_method (-1);
879           else
880             {
881               int i;
882
883               for (i = 0; i < num_input_methods; i++)
884                 if (input_method_table[i].language == sym
885                     && input_method_table[i].available >= 0)
886                   break;
887               if (i < num_input_methods)
888                 select_input_method (i);
889               else
890                 select_input_method (-1);
891             }
892         }
893     }
894
895   MenuHelpProc (MessageWidget, NULL, NULL, NULL);
896
897   return True;
898 }
899
900
901 /* Hide the cursor.  */
902 void
903 hide_cursor ()
904 {
905   control.with_cursor = 0;
906   redraw_cursor (1);
907   XtAppAddWorkProc (context, show_cursor, NULL);
908 }
909
910
911 /* Update the window area between the Y-positions $Y0 and $OLD_Y1 to
912    $Y1 and $NEW_Y1 assuming that the text in the other area is not
913    changed.  */
914 void
915 update_region (int y0, int old_y1, int new_y1)
916 {
917   if (y0 < 0)
918     y0 = 0;
919   if (new_y1 < old_y1)
920     {
921       if (old_y1 < win_height)
922         {
923           COPY_AREA (old_y1, win_height, new_y1);
924           redraw (win_height - (old_y1 - new_y1), win_height, 1, 0);
925         }
926       else
927         redraw (new_y1, win_height, 1, 0);
928     }
929   else if (new_y1 > old_y1)
930     {
931       if (new_y1 < win_height)
932         COPY_AREA (old_y1, win_height, new_y1);
933     }
934   if (new_y1 > win_height)
935     new_y1 = win_height;
936   redraw (y0, new_y1, 1, 1);
937 }
938
939
940 /* Delete the next $N characters.  If $N is negative delete the
941    precious (- $N) characters.  */
942 void
943 delete_char (int n)
944 {
945   MDrawMetric rect;
946   MDrawGlyphInfo info;
947   int y0, old_y1, new_y1;
948   int from, to;
949   int line_from = cursor.line_from;
950
951   if (n > 0)
952     from = cursor.from, to = from + n;
953   else
954     {
955       from = cursor.from + n;
956       to = cursor.from;
957       if (cursor.from == cur.from)
958         {
959           /* We are at the beginning of line.  */
960           int pos = cursor.prev_from;
961           
962           if (cursor.from == top.from)
963             {
964               /* We are at the beginning of screen.  We must scroll
965                  down.  */
966               GLYPH_INFO (bol (top.from - 1, 0), top.from - 1, info);
967               reseat (info.line_from);
968             }
969           update_cursor (pos, 1);
970         }
971     }
972
973   TEXT_EXTENTS (cur.from, bol (to + 1, 1), rect);
974   old_y1 = cur.y0 + rect.height;
975
976   /* Now delete a character.  */
977   mtext_del (mt, from, to);
978   nchars -= to - from;
979   if (from >= top.from && from < top.to)
980     update_top (top.from);
981   update_cursor (from, 1);
982
983   y0 = cur.y0;
984   if (line_from != cursor.line_from)
985     y0 -= 1;
986
987   TEXT_EXTENTS (cur.from, bol (to, 1), rect);
988   new_y1 = cur.y0 + rect.height;
989
990   update_region (cur.y0, old_y1, new_y1);
991 }
992
993
994 /* Insert M-text $NEWTEXT at the current cursor position.  */
995 void
996 insert_chars (MText *newtext)
997 {
998   int n = mtext_len (newtext);
999   MDrawMetric rect;
1000   int y0, old_y1, new_y1;
1001   int line_from;
1002
1003   if (SELECTEDP ())
1004     {
1005       int n = (mtext_property_end (selection)
1006                - mtext_property_start (selection));
1007       mtext_detach_property (selection);
1008       delete_char (n);
1009     }
1010
1011   y0 = cur.y0;
1012   if (cursor.line_from > 0
1013       && mtext_ref_char (mt, cursor.line_from - 1) != '\n')
1014     y0 -= control.min_line_descent;
1015
1016   TEXT_EXTENTS (cur.from, bol (cur.to - 1, 1), rect);
1017   old_y1 = y0 + rect.height;
1018
1019   line_from = cursor.line_from;
1020
1021   /* Now insert chars.  */
1022   mtext_ins (mt, cursor.from, newtext);
1023   nchars += n;
1024   if (cur.from == top.from)
1025     update_top (top.from);
1026   update_cursor (cursor.from + n, 1);
1027
1028   TEXT_EXTENTS (cur.from, bol (cur.to - 1, 1), rect);
1029   new_y1 = cur.y0 + rect.height;
1030
1031   update_region (y0, old_y1, new_y1);
1032   update_selection ();
1033 }
1034
1035
1036 int
1037 word_constituent_p (int c)
1038 {
1039   MSymbol category = (MSymbol) mchar_get_prop (c, Mcategory);
1040   char *name = category != Mnil ? msymbol_name (category) : NULL;
1041
1042   return (name && (name[0] == 'L' || name[0] == 'M'));
1043 }
1044
1045
1046 void
1047 forward_word ()
1048 {
1049   int pos = cursor.from;
1050
1051   while (pos < nchars && ! word_constituent_p (mtext_ref_char (mt, pos)))
1052     pos++;
1053   if (pos < nchars)
1054     {
1055       MTextProperty *prop = mtext_get_property (mt, pos, Mword);
1056
1057       if (prop)
1058         pos = mtext_property_end (prop);
1059       else
1060         while (pos < nchars && word_constituent_p (mtext_ref_char (mt, pos)))
1061           pos++;
1062     }
1063   update_cursor (pos, 0);
1064 }
1065
1066 void
1067 backward_word ()
1068 {
1069   int pos = cursor.from;
1070
1071   while (pos > 0 && ! word_constituent_p (mtext_ref_char (mt, pos - 1)))
1072     pos--;
1073   if (pos > 0)
1074     {
1075       MTextProperty *prop = mtext_get_property (mt, pos - 1, Mword);
1076
1077       if (prop)
1078         pos = mtext_property_start (prop);
1079       else
1080         while (pos > 0 && word_constituent_p (mtext_ref_char (mt, pos - 1)))
1081           pos--;
1082     }
1083   update_cursor (pos, 0);
1084 }
1085
1086
1087 /* Convert the currently selected text to UTF8-STRING or
1088    COMPOUND-TEXT.  It is called when someone requests the current
1089    value of the selection.  */
1090 Boolean
1091 covert_selection (Widget w, Atom *selection_atom,
1092                   Atom *target, Atom *return_type,
1093                   XtPointer *value, unsigned long *length, int *format)
1094 {
1095   unsigned char *buf = (unsigned char *) XtMalloc (4096);
1096   MText *this_mt = mtext ();
1097   int from = mtext_property_start (selection);
1098   int to = mtext_property_end (selection);
1099   MSymbol coding;
1100   int len;
1101
1102   mtext_copy (this_mt, 0, mt, from, to);
1103   if (*target == XA_TEXT)
1104     {
1105 #ifdef X_HAVE_UTF8_STRING
1106       coding = Mcoding_utf_8;
1107       *return_type = XA_UTF8_STRING;
1108 #else
1109       coding = Mcoding_compound_text;
1110       *return_type = XA_COMPOUND_TEXT;
1111 #endif
1112     }
1113   else if (*target == XA_UTF8_STRING)
1114     {
1115       coding = Mcoding_utf_8;
1116       *return_type = XA_UTF8_STRING;
1117     }
1118   else if (*target == XA_STRING)
1119     {
1120       int i;
1121
1122       len = to - from;
1123       for (i = 0; i < len; i++)
1124         if (mtext_ref_char (this_mt, i) >= 0x100)
1125           /* Can't encode in XA_STRING */
1126           return False;
1127       coding = Mcoding_iso_8859_1;
1128       *return_type = XA_STRING;
1129     }
1130   else if (*target == XA_COMPOUND_TEXT)
1131     {
1132       coding = Mcoding_compound_text;
1133       *return_type = XA_COMPOUND_TEXT;
1134     }
1135   else
1136     return False;
1137
1138   len = mconv_encode_buffer (coding, this_mt, buf, 4096);
1139   m17n_object_unref (this_mt);
1140   if (len < 0)
1141     return False;
1142   *length = len;
1143   *value = (XtPointer) buf;
1144   *format = 8;
1145   return True;
1146 }
1147
1148
1149 /* Unselect the text.  It is called when we loose the selection.  */
1150 void
1151 lose_selection (Widget w, Atom *selection_atom)
1152 {
1153   if (SELECTEDP ())
1154     {
1155       mtext_detach_property (selection);
1156       redraw (sel_start.y0, sel_end.y1, 1, 0);
1157     }
1158 }
1159
1160 void
1161 get_selection (Widget w, XtPointer cliend_data, Atom *selection,  Atom *type,
1162                XtPointer value, unsigned long *length, int *format)
1163 {
1164   MText *this_mt;
1165   MSymbol coding;
1166
1167   if (*type == XT_CONVERT_FAIL || ! value)
1168     goto err;
1169   if (*type == XA_STRING)
1170     coding = Mnil;
1171   else if (*type == XA_COMPOUND_TEXT)
1172     coding = msymbol ("compound-text");
1173 #ifdef X_HAVE_UTF8_STRING
1174   else if (*type == XA_UTF8_STRING)
1175     coding = msymbol ("utf-8");
1176 #endif
1177   else
1178     goto err;
1179
1180   this_mt = mconv_decode_buffer (coding, (unsigned char *) value, *length);
1181   if (! this_mt && *type != XA_UTF8_STRING)
1182     {
1183       XtGetSelectionValue (w, XA_PRIMARY, XA_UTF8_STRING, get_selection, NULL,
1184                            CurrentTime);
1185       goto err;
1186     }
1187   if (this_mt)
1188     {
1189       hide_cursor ();
1190       insert_chars (this_mt);
1191       m17n_object_unref (this_mt);
1192     }
1193
1194  err:
1195   if (value)
1196     XtFree (value);
1197 }
1198
1199 static void
1200 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
1201 {
1202   XExposeEvent *expose = (XExposeEvent *) event;
1203
1204   if (top.from < 0)
1205     {
1206       Dimension width_max, width;
1207
1208       XtSetArg (arg[0], XtNwidth, &width);
1209       XtGetValues (XtParent (w), arg, 1);
1210       width_max = width;
1211       XtGetValues (HeadWidget, arg, 1);
1212       if (width_max < width)
1213         width_max = width;
1214       XtGetValues (FaceWidget, arg, 1);
1215       if (width_max < width)
1216         width_max = width;
1217       XtGetValues (LangWidget, arg, 1);
1218       if (width_max < width)
1219         width_max = width;
1220       XtSetArg (arg[0], XtNwidth, width_max);
1221       XtSetValues (HeadWidget, arg, 1);
1222       XtSetValues (FaceWidget, arg, 1);
1223       XtSetValues (LangWidget, arg, 1);
1224       XtSetValues (XtParent (w), arg, 1);
1225       XtSetValues (TailWidget, arg, 1);
1226
1227       update_top (0);
1228       update_cursor (0, 1);
1229       redraw (0, win_height, 0, 1);
1230       if (current_input_method >= 0)
1231         {
1232           int idx = current_input_method;
1233
1234           current_input_method = -1;
1235           select_input_method (idx);
1236         }
1237       show_cursor (NULL);
1238     }
1239   else
1240     {
1241       redraw (expose->y, expose->y + expose->height, 0, 0);
1242       if (current_input_context
1243           && expose->y < cur.y0 && expose->y + expose->height < cur.y1)
1244         set_input_method_spot ();
1245     }
1246 }
1247
1248 static void
1249 ConfigureProc (Widget w, XEvent *event, String *str, Cardinal *num)
1250 {
1251   XConfigureEvent *configure = (XConfigureEvent *) event;
1252   
1253   hide_cursor ();
1254   control.max_line_width = win_width = configure->width;
1255   win_height = configure->height;
1256   mdraw_clear_cache (mt);
1257   update_top (0);
1258   update_cursor (0, 1);
1259   redraw (0, win_height, 1, 1);
1260   if (current_input_context)
1261     set_input_method_spot ();
1262 }
1263
1264 static void
1265 ButtonProc (Widget w, XEvent *event, String *str, Cardinal *num)
1266 {
1267   int pos;
1268   int x = event->xbutton.x;
1269   int y = event->xbutton.y - top.ascent;
1270
1271   if (control.orientation_reversed)
1272     x -= win_width;
1273   pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
1274   if (SELECTEDP ())
1275     {
1276       XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1277       mtext_detach_property (selection);
1278       redraw (sel_start.y0, sel_end.y1, 1, 0);
1279     }
1280   hide_cursor ();
1281   if (current_input_context
1282       && minput_filter (current_input_context, Minput_focus_move, NULL) == 0)
1283     {
1284       MText *produced = mtext ();
1285
1286       minput_lookup (current_input_context, Mnil, NULL, produced);
1287       if (mtext_len (produced) > 0)
1288         {
1289           insert_chars (produced);
1290           if (pos >= cursor.from)
1291             pos += mtext_len (produced);
1292         }
1293       m17n_object_unref (produced);
1294     }
1295   update_cursor (pos, 0);
1296 }
1297
1298
1299 static void
1300 ButtonReleaseProc (Widget w, XEvent *event, String *str, Cardinal *num)
1301 {
1302   if (! SELECTEDP ())
1303     return;
1304
1305   XtOwnSelection (w, XA_PRIMARY, CurrentTime,
1306                   covert_selection, lose_selection, NULL);
1307   update_cursor (mtext_property_start (selection), 0);
1308 }
1309
1310 static
1311 void
1312 Button2Proc (Widget w, XEvent *event, String *str, Cardinal *num)
1313 {
1314   if (! SELECTEDP ())
1315     {
1316       /* We don't have a local selection.  */
1317       XtGetSelectionValue (w, XA_PRIMARY, XA_TEXT, get_selection, NULL,
1318                            CurrentTime);
1319     }
1320   else
1321     {
1322       int from = mtext_property_start (selection);
1323       int to = mtext_property_end (selection);
1324       MText *this_mt;
1325       int pos;
1326       int x = event->xbutton.x;
1327       int y = event->xbutton.y - top.ascent;
1328
1329       if (control.orientation_reversed)
1330         x -= win_width;
1331       pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
1332       
1333       XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1334       mtext_detach_property (selection);
1335       hide_cursor ();
1336       this_mt = mtext_copy (mtext (), 0, mt, from, to);
1337       update_cursor (pos, 0);
1338       insert_chars (this_mt);
1339       m17n_object_unref (this_mt);
1340     }
1341 }
1342
1343 static void
1344 ButtonMoveProc (Widget w, XEvent *event, String *str, Cardinal *num)
1345 {
1346   int pos;
1347   int x = event->xbutton.x;
1348   int y = event->xbutton.y;
1349
1350   if (control.orientation_reversed)
1351     x -= win_width;
1352   if (y < cur.y0)
1353     pos = top.from, y -= top.ascent;
1354   else
1355     pos = cur.from, y -= cur.y0 + cur.ascent;
1356   pos = COORDINATES_POSITION (pos, nchars + 1, x, y);
1357
1358   if (pos == cursor.from)
1359     return;
1360
1361   hide_cursor ();
1362   if (SELECTEDP ())
1363     {
1364       /* Selection range changed.  */
1365       int from = mtext_property_start (selection);
1366       int to = mtext_property_end (selection);
1367       int start_y0 = sel_start.y0, start_y1 = sel_start.y1;
1368       int end_y0 = sel_end.y0, end_y1 = sel_end.y1;
1369
1370       if (cursor.from == from)
1371         {
1372           /* Starting position changed.  */
1373           if (pos <= from)
1374             {
1375               /* Enlarged.  We can simply overdraw.  */
1376               select_region (pos, to);
1377               redraw (sel_start.y0, start_y1, 0, 0);
1378             }
1379           else if (pos < to)
1380             {
1381               /* Shrunken.  Previous selection face must be cleared.  */
1382               select_region (pos, to);
1383               redraw (start_y0, sel_start.y1, 1, 0);
1384             }
1385           else if (pos == to)
1386             {
1387               /* Shrunken to zero.  */
1388               XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1389               mtext_detach_property (selection);
1390               redraw (start_y0, end_y1, 1, 0);
1391             }
1392           else
1393             {
1394               /* Full update is necessary.  */
1395               select_region (to, pos);
1396               redraw (start_y0, sel_end.y1, 1, 0);
1397             }
1398         }
1399       else
1400         {
1401           /* Ending position changed.  */
1402           if (pos < from)
1403             {
1404               /* Full update is necessary.  */
1405               select_region (pos, from);
1406               redraw (sel_start.y0, end_y1, 1, 0);
1407             }
1408           else if (pos == from)
1409             {
1410               /* Shrunken to zero.  */
1411               XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1412               mtext_detach_property (selection);
1413               redraw (start_y0, end_y1, 1, 0);
1414             }
1415           else if (pos < to)
1416             {
1417               /* Shrunken.  Previous selection face must be cleared.  */
1418               select_region (from, pos);
1419               redraw (sel_end.y0, end_y1, 1, 0);
1420             }
1421           else
1422             {
1423               /* Enlarged.  We can simply overdraw.  */
1424               select_region (from, pos);
1425               redraw (end_y0, sel_end.y1, 0, 0);
1426             }
1427         }
1428     }
1429   else
1430     {
1431       /* Newly selected.  */
1432       select_region (pos, cursor.from);
1433       redraw (sel_start.y0, sel_end.y1, 0, 0);
1434     }
1435   update_cursor (pos, 1);
1436 }
1437
1438 void
1439 FocusInProc (Widget w, XEvent *event, String *str, Cardinal *num)
1440 {
1441   if (current_input_context
1442       && minput_filter (current_input_context, Minput_focus_in, NULL) == 0)
1443     {
1444       MText *produced = mtext ();
1445
1446       minput_lookup (current_input_context, Mnil, NULL, produced);
1447       if (mtext_len (produced) > 0)
1448         {
1449           hide_cursor ();
1450           insert_chars (produced);
1451         }
1452       m17n_object_unref (produced);
1453     }
1454 }
1455
1456 void
1457 FocusOutProc (Widget w, XEvent *event, String *str, Cardinal *num)
1458 {
1459   if (current_input_context
1460       && minput_filter (current_input_context, Minput_focus_out, NULL) == 0)
1461     {
1462       MText *produced = mtext ();
1463
1464       minput_lookup (current_input_context, Mnil, NULL, produced);
1465       if (mtext_len (produced) > 0)
1466         {
1467           hide_cursor ();
1468           insert_chars (produced);
1469         }
1470       m17n_object_unref (produced);
1471     }
1472 }
1473
1474 void
1475 ScrollProc (Widget w, XtPointer client_data, XtPointer position)
1476 {
1477   int from;
1478   MDrawGlyphInfo info;
1479   int height;
1480   int cursor_pos = cursor.from;
1481
1482   if (((int) position) < 0)
1483     {
1484       /* Scroll down.  */
1485       int pos;
1486
1487       from = top.from;
1488       height = top.y1 - top.y0;
1489       while (from > 0)
1490         {
1491           pos = bol (from - 1, 0);
1492           GLYPH_INFO (pos, from - 1, info);
1493           if (height + info.metrics.height > win_height)
1494             break;
1495           height += info.metrics.height;
1496           from = info.line_from;
1497         }
1498       if (cursor_pos >= top.to)
1499         {
1500           cursor_pos = top.from;
1501           pos = top.to;
1502           while (cursor_pos < nchars)
1503             {
1504               GLYPH_INFO (pos, pos, info);
1505               if (height + info.metrics.height > win_height)
1506                 break;
1507               height += info.metrics.height;
1508               cursor_pos = pos;
1509               pos = info.line_to;
1510             }
1511         }
1512     }
1513   else if (cur.to < nchars)
1514     {
1515       /* Scroll up, but leave at least one line.  */
1516       from = cur.to;
1517       height = cur.y1;
1518       while (from < nchars)
1519         {
1520           GLYPH_INFO (from, from, info);
1521           if (height + info.metrics.height > win_height
1522               || info.line_to >= nchars)
1523             break;
1524           height += info.metrics.height;
1525           from = info.line_to;
1526         }
1527       if (from == nchars)
1528         from = info.line_from;
1529       if (cursor_pos < from)
1530         cursor_pos = from;
1531     }
1532   else
1533     /* Scroll up to make the cursor line top.  */
1534     from = cur.from;
1535   hide_cursor ();
1536   reseat (from);
1537   update_cursor (cursor_pos, 1);
1538 }
1539
1540 void
1541 JumpProc (Widget w, XtPointer client_data, XtPointer persent_ptr)
1542 {
1543   float persent = *(float *) persent_ptr;
1544   int pos1, pos2 = nchars * persent;
1545   MDrawGlyphInfo info;
1546
1547   hide_cursor ();
1548   pos1 = bol (pos2, 0);
1549   GLYPH_INFO (pos1, pos2, info);
1550   pos1 = info.line_from;
1551   reseat (pos1);
1552   update_cursor (pos1, 1);
1553 }
1554
1555 static void InputMethodProc (Widget, XtPointer, XtPointer);
1556
1557 static void
1558 KeyProc (Widget w, XEvent *event, String *str, Cardinal *num)
1559 {
1560   XKeyEvent *key_event = (XKeyEvent *) event;
1561   char buf[512];
1562   KeySym keysym = NoSymbol;
1563   int ret;
1564   /* If set to 1, do not update target_x_position.  */
1565   int keep_target_x_position = 0;
1566   MText *produced;
1567   int y0, old_y1, new_y1;
1568
1569   hide_cursor ();
1570
1571   mt_modified = 0;
1572   y0 = cur.y0;
1573   old_y1 = cur.y1;
1574   if (current_input_context
1575       && minput_filter (current_input_context, Mnil, event))
1576     {
1577       if (mt_modified)
1578         {
1579           new_y1 = cur.y1;
1580           update_region (y0, old_y1, new_y1);
1581         }
1582       return;
1583     }
1584   if (event->type == KeyRelease)
1585     return;
1586
1587   produced = mtext ();
1588   ret = minput_lookup (current_input_context, Mnil, event, produced);
1589   if (mtext_len (produced) > 0)
1590     insert_chars (produced);
1591   if (ret)
1592     ret = XLookupString (key_event, buf, sizeof (buf), &keysym, NULL);
1593   m17n_object_unref (produced);
1594
1595   if (saved_input_method > -3)
1596     {
1597       InputMethodProc (w, (XtPointer) saved_input_method, NULL);
1598       saved_input_method = -3;
1599     }
1600
1601   switch (keysym)
1602     {
1603     case XK_Delete:
1604       {
1605         int n = 0;
1606
1607         if (SELECTEDP ())
1608           {
1609             n = (mtext_property_end (selection)
1610                  - mtext_property_start (selection));
1611             mtext_detach_property (selection);
1612           }
1613         else if (cursor.from < nchars)
1614           {
1615             /* Delete the following grapheme cluster.  */
1616             n = cursor.to - cursor.from;
1617           }
1618         if (n != 0)
1619           delete_char (n);
1620       }
1621       break;
1622
1623     case XK_BackSpace:
1624       {
1625         int n = 0;
1626
1627         if (SELECTEDP ())
1628           {
1629             /* Delete selected region.  */
1630             n = (mtext_property_end (selection)
1631                  - mtext_property_start (selection));
1632             mtext_detach_property (selection);
1633           }
1634         else if (cursor.from > 0)
1635           {
1636             /* Delete the preceding character.  */
1637             n = -1;
1638           }
1639         if (n != 0)
1640           delete_char (n);
1641       }
1642       break;
1643
1644     case XK_Left:
1645       if (SELECTEDP ())
1646         {
1647           mtext_detach_property (selection);
1648           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1649         }
1650       if (logical_move)
1651         {
1652           if (cursor.prev_from >= 0)
1653             update_cursor (cursor.prev_from, 0);
1654         }
1655       else
1656         {
1657           if (cursor.left_from >= 0)
1658             update_cursor (cursor.left_from, 0);
1659         }
1660       break;
1661
1662     case XK_Right:
1663       if (SELECTEDP ())
1664         {
1665           mtext_detach_property (selection);
1666           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1667         }
1668       if (logical_move)
1669         {
1670           if (cursor.next_to >= 0)
1671             update_cursor (cursor.to, 0);
1672         }
1673       else
1674         {
1675           if (cursor.right_from >= 0)
1676             update_cursor (cursor.right_from, 0);
1677         }
1678       break;
1679
1680     case XK_Down:
1681       if (SELECTEDP ())
1682         {
1683           mtext_detach_property (selection);
1684           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1685         }
1686       if (cur.to <= nchars)
1687         {
1688           MDrawGlyphInfo info;
1689           int pos;
1690
1691           GLYPH_INFO (cur.from, cur.to, info);
1692           pos = COORDINATES_POSITION (cur.from, nchars + 1,
1693                                       target_x_position, info.y);
1694           keep_target_x_position = 1;
1695           update_cursor (pos, 0);
1696         }
1697       break;
1698
1699     case XK_Up:
1700       if (SELECTEDP ())
1701         {
1702           mtext_detach_property (selection);
1703           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1704         }
1705       if (cur.from > 0)
1706         {
1707           MDrawMetric rect;
1708           int y;
1709           int pos = bol (cur.from - 1, 0);
1710
1711           TEXT_EXTENTS (pos, cur.from - 1, rect);
1712           y = rect.height + rect.y - 1;
1713           pos = COORDINATES_POSITION (pos, nchars,
1714                                       target_x_position, y);
1715           keep_target_x_position = 1;
1716           update_cursor (pos, 0);
1717         }
1718       break;
1719
1720     case XK_Page_Down:
1721       if (SELECTEDP ())
1722         {
1723           mtext_detach_property (selection);
1724           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1725         }
1726       if (top.from < nchars)
1727         ScrollProc (w, NULL, (XtPointer) 1);
1728       break;
1729
1730     case XK_Page_Up:
1731       if (SELECTEDP ())
1732         {
1733           mtext_detach_property (selection);
1734           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1735         }
1736       if (top.from > 0)
1737         ScrollProc (w, NULL, (XtPointer) -1);
1738       break;
1739
1740     case XK_b:
1741       if (key_event->state >= Mod1Mask)
1742         {
1743           lose_selection (NULL, NULL);
1744           backward_word ();
1745           break;
1746         }
1747
1748     case XK_f:
1749       if (key_event->state >= Mod1Mask)
1750         {
1751           lose_selection (NULL, NULL);
1752           forward_word ();
1753           break;
1754         }
1755
1756     default:
1757       if (ret > 0)
1758         {
1759           if (buf[0] == 17) /* C-q */
1760             {
1761               XtAppSetExitFlag (context);
1762               return;
1763             }
1764           else if (buf[0] == 12) /* C-l */
1765             {
1766               redraw (0, win_height, 1, 1);
1767               return;
1768             }
1769           else if (buf[0] == '='
1770                    && (event->xkey.state & ControlMask)
1771                    && unicode_input_method >= 0)
1772             {
1773               saved_input_method = current_input_method;
1774               InputMethodProc (w, (XtPointer) unicode_input_method, NULL);
1775               minput_filter (current_input_context, msymbol ("C-u"), NULL);
1776             }
1777           else
1778             {
1779               MText *temp = mtext ();
1780
1781               mtext_cat_char (temp, buf[0] == '\r' ? '\n'
1782                               : ((unsigned char *) buf)[0]);
1783               if (current_input_context)
1784                 mtext_put_prop (temp, 0, 1, Mlanguage,
1785                                 current_input_context->im->language);
1786               insert_chars (temp);
1787               m17n_object_unref (temp);
1788             }
1789         }
1790     }
1791
1792   if (! keep_target_x_position)
1793     target_x_position = cursor.x;
1794 }
1795
1796 void
1797 SaveProc (Widget w, XtPointer client_data, XtPointer call_data)
1798 {
1799   char *name = (char *) client_data;
1800   FILE *fp;
1801   int from = -1, to = 0;
1802   
1803   if (name)
1804     {
1805       free (filename);
1806       filename = strdup (name);
1807     }
1808
1809   fp = fopen (filename, "w");
1810   if (! fp)
1811     {
1812       fprintf (stderr, "Open for write fail: %s", filename);
1813       return;
1814     }
1815
1816   if (SELECTEDP ())
1817     {
1818       from = mtext_property_start (selection);
1819       to = mtext_property_end (selection);
1820       mtext_detach_property (selection);
1821     }
1822
1823   mconv_encode_stream (Mcoding_utf_8_full, mt, fp);
1824   fclose (fp);
1825   if (from >= 0)
1826     select_region (from, to);
1827 }
1828
1829 void
1830 SerializeProc (Widget w, XtPointer client_data, XtPointer call_data)
1831 {
1832   MText *new;
1833
1834   hide_cursor ();
1835   if (SELECTEDP ())
1836     mtext_detach_property (selection);
1837   serialized = (int) client_data;
1838   if (! serialized)
1839     new = mtext_deserialize (mt);
1840   else
1841     {
1842       MPlist *plist = mplist ();
1843
1844       mplist_push (plist, Mt, Mface);
1845       mplist_push (plist, Mt, Mlanguage);
1846       new = mtext_serialize (mt, 0, mtext_len (mt), plist);
1847       m17n_object_unref (plist);
1848     }
1849   if (new)
1850     {
1851       m17n_object_unref (mt);
1852       mt = new;
1853       serialized = ! serialized;
1854       nchars = mtext_len (mt);
1855       update_top (0);
1856     }
1857   update_cursor (0, 1);
1858   redraw (0, win_height, 1, 1);
1859 }
1860
1861 void
1862 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
1863 {
1864   XtAppSetExitFlag (context);
1865 }
1866
1867 MText *
1868 read_file ()
1869 {
1870   FILE *fp = fopen (filename, "r");
1871
1872   if (! fp)
1873     FATAL_ERROR ("Can't read \"%s\"!\n", filename);
1874   mt = mconv_decode_stream (Mcoding_utf_8_full, fp);
1875   fclose (fp);
1876   if (! mt)
1877     FATAL_ERROR ("Can't decode \"%s\" by UTF-8!\n", filename);
1878   return mt;
1879 }
1880
1881 void
1882 BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
1883 {
1884   int data = (int) client_data;
1885   int i;
1886
1887   if (data == 0)
1888     {
1889       control.enable_bidi = 0;
1890       control.orientation_reversed = 0;
1891     }
1892   else
1893     {
1894       control.enable_bidi = 1;
1895       control.orientation_reversed = data == 2;
1896     }
1897   for (i = 0; i < 3; i++)
1898     {
1899       if (i == data)
1900         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1901       else
1902         XtSetArg (arg[0], XtNleftBitmap, None);
1903       XtSetValues (BidiMenus[i], arg, 1);
1904     }
1905
1906   update_cursor (cursor.from, 1);
1907   redraw (0, win_height, 1, 0);
1908 }
1909
1910 void
1911 LineBreakProc (Widget w, XtPointer client_data, XtPointer call_data)
1912 {
1913   int data = (int) client_data;
1914   int i;
1915
1916   if (data == 0)
1917     control.max_line_width = 0;
1918   else
1919     {
1920       control.max_line_width = win_width;
1921       control.line_break = (data == 1 ? NULL : mdraw_default_line_break);
1922     }
1923   for (i = 0; i < 3; i++)
1924     {
1925       if (i == data)
1926         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1927       else
1928         XtSetArg (arg[0], XtNleftBitmap, None);
1929       XtSetValues (LineBreakMenus[i], arg, 1);
1930     }
1931
1932   update_cursor (cursor.from, 1);
1933   redraw (0, win_height, 1, 0);
1934 }
1935
1936 void
1937 FilterProc (Widget w, XtPointer client_data, XtPointer call_data)
1938 {
1939   char *filter_module = (char *) client_data;
1940   void *handle;
1941   void (*func) (MText *, int, int);
1942
1943   if (! SELECTEDP ())
1944     return;
1945   handle = dlopen (filter_module, RTLD_NOW);
1946   if (! handle)
1947     return;
1948   *(void **) (&func) = dlsym (handle, "filter");
1949   if (func)
1950     (*func) (mt, mtext_property_start (selection),
1951              mtext_property_end (selection));
1952   dlclose (handle);
1953 }
1954
1955 void
1956 CursorProc (Widget w, XtPointer client_data, XtPointer call_data)
1957 {
1958   int data = (int) client_data;
1959   int i, from, to;
1960
1961   switch (data)
1962     {
1963     case 0:
1964       logical_move = 1;
1965       from = 0, to = 2;
1966       break;
1967     case 1:
1968       logical_move = 0;
1969       from = 0, to = 2;
1970       break;
1971     case 2:
1972       control.cursor_bidi = 0, control.cursor_width = -1;
1973       from = 2, to = 5;
1974       break;
1975     case 3:
1976       control.cursor_bidi = 0, control.cursor_width = 2;
1977       from = 2, to = 5;
1978       break;
1979     default:
1980       control.cursor_bidi = 1;
1981       from = 2, to = 5;
1982       break;
1983     }
1984
1985   for (i = from; i < to; i++)
1986     {
1987       if (i == data)
1988         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1989       else
1990         XtSetArg (arg[0], XtNleftBitmap, None);
1991       XtSetValues (CursorMenus[i], arg, 1);
1992     }
1993
1994   update_cursor (cursor.from, 0);
1995   redraw (0, win_height, 1, 0);
1996 }
1997
1998 static void
1999 InputMethodProc (Widget w, XtPointer client_data, XtPointer call_data)
2000 {
2001   int idx = (int) client_data;
2002
2003   if (idx == -2 ? (! auto_input_method && current_input_method < 0)
2004       : idx == -1 ? auto_input_method
2005       : idx == current_input_method)
2006     return;
2007
2008   if (auto_input_method)
2009     {
2010       select_input_method (-1);
2011       XtSetArg (arg[0], XtNleftBitmap, None);
2012       XtSetValues (InputMethodMenus[1], arg, 1);
2013       auto_input_method = 0;
2014     }
2015
2016   if (idx == -1)
2017     {
2018       select_input_method (-1);
2019       XtSetArg (arg[0], XtNleftBitmap, None);
2020       XtSetValues (InputMethodMenus[0], arg, 1);
2021       XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
2022       XtSetValues (InputMethodMenus[1], arg, 1);
2023       auto_input_method = 1;
2024       hide_cursor ();
2025     }
2026   else
2027     {
2028       select_input_method (idx);
2029     }
2030 }
2031
2032 MPlist *default_face_list;
2033
2034 void
2035 FaceProc (Widget w, XtPointer client_data, XtPointer call_data)
2036 {
2037   int idx = (int) client_data;
2038   int from, to;
2039   int old_y1;
2040
2041   hide_cursor ();
2042   if (! SELECTEDP ())
2043     {
2044       MPlist *plist;
2045
2046       if (idx >= 0)
2047         {
2048           MFace *face = mframe_get_prop (frame, Mface);
2049
2050           for (plist = default_face_list; mplist_key (plist) != Mnil;
2051                plist = mplist_next (plist)) 
2052             mface_merge (face, mplist_value (plist));
2053           mplist_add (plist, Mt, *face_table[idx].face);
2054           mface_merge (face, *face_table[idx].face);
2055         }
2056       else if (mplist_key (mplist_next (default_face_list)) != Mnil)
2057         {
2058           MFace *face = mframe_get_prop (frame, Mface);
2059
2060           for (plist = default_face_list;
2061                mplist_key (mplist_next (plist)) != Mnil;
2062                plist = mplist_next (plist)) 
2063             mface_merge (face, mplist_value (plist));
2064           mplist_pop (plist);
2065         }
2066       update_top (0);
2067       update_cursor (0, 1);
2068       redraw (0, win_height, 1, 1);
2069       show_cursor (NULL);
2070       return;
2071     }
2072
2073   XtAppAddWorkProc (context, show_cursor, NULL);
2074   from = mtext_property_start (selection);
2075   to = mtext_property_end (selection);
2076   old_y1 = sel_end.y1;
2077
2078   mtext_detach_property (selection);
2079   if (idx >= 0)
2080     {
2081       MTextProperty *prop = mtext_property (Mface, *face_table[idx].face,
2082                                             MTEXTPROP_REAR_STICKY);
2083       mtext_push_property (mt, from, to, prop);
2084       m17n_object_unref (prop);
2085     }
2086   else
2087     mtext_pop_prop (mt, from, to, Mface);
2088   if (from < top.to)
2089     update_top (top.from);
2090   update_cursor (cursor.from, 1);
2091   select_region (from, to);
2092   update_region (sel_start.y0, old_y1, sel_end.y1);
2093   if (cur.y1 > win_height)
2094     {
2095       while (cur.y1 > win_height)
2096         {
2097           reseat (top.to);
2098           update_cursor (cursor.from, 1);
2099         }
2100     }
2101 }
2102
2103 void
2104 LangProc (Widget w, XtPointer client_data, XtPointer call_data)
2105 {
2106   MSymbol sym = (MSymbol) client_data;
2107   int from, to;
2108   int old_y1;
2109
2110   if (! SELECTEDP ())
2111     return;
2112
2113   XtAppAddWorkProc (context, show_cursor, NULL);
2114   from = mtext_property_start (selection);
2115   to = mtext_property_end (selection);
2116   old_y1 = sel_end.y1;
2117
2118   mtext_detach_property (selection);
2119   if (sym != Mnil)
2120     mtext_put_prop (mt, from, to, Mlanguage, sym);
2121   else
2122     mtext_pop_prop (mt, from, to, Mlanguage);
2123
2124   if (from < top.to)
2125     update_top (top.from);
2126   update_cursor (cursor.from, 1);
2127   select_region (from, to);
2128   update_region (sel_start.y0, old_y1, sel_end.y1);
2129   if (cur.y1 > win_height)
2130     {
2131       while (cur.y1 > win_height)
2132         {
2133           reseat (top.to);
2134           update_cursor (cursor.from, 1);
2135         }
2136     }
2137 }
2138
2139 void
2140 DumpImageProc (Widget w, XtPointer client_data, XtPointer call_data)
2141 {
2142   int narrowed = (int) client_data;
2143   FILE *mdump;
2144   int from, to;
2145   MConverter *converter;
2146
2147   if (narrowed)
2148     {
2149       if (! SELECTEDP ())
2150         return;
2151       from = mtext_property_start (selection);
2152       to = mtext_property_end (selection);
2153     }
2154   else
2155     {
2156       from = 0;
2157       to = nchars;
2158     }
2159
2160   if (! narrowed)
2161     mdump = popen ("mdump -q -p a4", "w");
2162   else
2163     mdump = popen ("mdump -q", "w");
2164   if (! mdump)
2165     return;
2166   converter = mconv_stream_converter (Mcoding_utf_8_full, mdump);
2167   mconv_encode_range (converter, mt, from, to);
2168   mconv_free_converter (converter);
2169   fclose (mdump);
2170 }
2171
2172 void
2173 input_status (MInputContext *ic, MSymbol command)
2174 {
2175   XFillRectangle (display, input_status_pixmap, gc_inv,
2176                   0, 0, input_status_width, input_status_height);
2177   if (command == Minput_status_draw)
2178     {
2179       MDrawMetric rect;
2180
2181       mtext_put_prop (ic->status, 0, mtext_len (ic->status),
2182                       Mface, face_input_status);
2183       if (ic->im->language != Mnil)
2184         mtext_put_prop (ic->status, 0, mtext_len (ic->status),
2185                         Mlanguage, ic->im->language);
2186       mdraw_text_extents (frame, ic->status, 0, mtext_len (ic->status),
2187                           &input_status_control, NULL, NULL, &rect);
2188       mdraw_text_with_control (frame, (MDrawWindow) input_status_pixmap,
2189                                input_status_width - rect.width - 2, - rect.y,
2190                                ic->status, 0, mtext_len (ic->status),
2191                                &input_status_control);
2192     }
2193   XtSetArg (arg[0], XtNbitmap, input_status_pixmap);
2194   XtSetValues (CurIMStatus, arg, 1);
2195 }
2196
2197 void
2198 surrounding_text_handler (MInputContext *ic, MSymbol command)
2199 {
2200   if (command == Minput_get_surrounding_text)
2201     {
2202       int len = (int) mplist_value (ic->plist);
2203       int pos;
2204       MText *surround;
2205
2206       if (len < 0)
2207         {
2208           pos = cursor.from + len;
2209           if (pos < 0)
2210             pos = 0;
2211           surround = mtext_duplicate (mt, pos, cursor.from);
2212         }
2213       else if (len > 0)
2214         {
2215           pos = cursor.from + len;
2216           if (pos > nchars)
2217             pos = nchars;
2218           surround = mtext_duplicate (mt, cursor.from, pos);
2219         }
2220       else
2221         surround = mtext ();
2222       mplist_set (ic->plist, Mtext, surround);
2223       m17n_object_unref (surround);
2224     }
2225   else if (command == Minput_delete_surrounding_text)
2226     {
2227       int len = (int) mplist_value (ic->plist);      
2228
2229       if (len < 0)
2230         {
2231           if (cursor.from + len < 0)
2232             len = - cursor.from;
2233           mtext_del (mt, cursor.from + len, cursor.from);
2234           nchars += len;
2235           update_cursor (cursor.from + len, 1);
2236         }
2237       else if (len > 0)
2238         {
2239           if (cursor.from + len > nchars)
2240             len = nchars - cursor.from;
2241           mtext_del (mt, cursor.from, cursor.from + len);
2242           nchars -= len;
2243           update_cursor (cursor.from, 1);
2244         }
2245       if (len)
2246         mt_modified = 1;
2247     }
2248 }
2249
2250 int
2251 compare_input_method (const void *elt1, const void *elt2)
2252 {
2253   const InputMethodInfo *im1 = elt1;
2254   const InputMethodInfo *im2 = elt2;
2255   MSymbol lang1, lang2;
2256
2257   if (im1->language == Mnil)
2258     return 1;
2259   if (im1->language == im2->language)
2260     return strcmp (msymbol_name (im1->name), msymbol_name (im2->name));
2261   if (im1->language == Mt)
2262     return 1;
2263   if (im2->language == Mt)
2264     return -1;
2265   lang1 = mlanguage_name (im1->language);
2266   lang2 = mlanguage_name (im2->language);
2267   return strcmp (msymbol_name (lang1), msymbol_name (lang2));
2268 }
2269
2270 void
2271 setup_input_methods (int with_xim, char *initial_input_method)
2272 {
2273   MPlist *plist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
2274   MPlist *pl;
2275   int i;
2276   MSymbol Municode = msymbol ("unicode");
2277
2278   num_input_methods = plist ? mplist_length (plist) : 0;
2279   if (with_xim)
2280     num_input_methods++;
2281   input_method_table = calloc (num_input_methods, sizeof (InputMethodInfo));
2282
2283   i = 0;
2284   if (plist)
2285     {
2286       for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl), i++)
2287         {
2288           MDatabase *mdb = mplist_value (pl);
2289           MSymbol *tag = mdatabase_tag (mdb);
2290
2291           if (tag[2] == Mnil)
2292             i--, num_input_methods--;
2293           else
2294             {
2295               input_method_table[i].language = tag[1];
2296               input_method_table[i].name = tag[2];
2297             }
2298         }
2299       m17n_object_unref (plist);
2300     }
2301   if (with_xim)
2302     {
2303       input_method_table[i].language = Mnil;
2304       input_method_table[i].name = msymbol ("xim");
2305       i++;
2306     }
2307
2308   qsort (input_method_table, num_input_methods, sizeof input_method_table[0],
2309          compare_input_method);
2310   for (i = 0; i < num_input_methods; i++)
2311     if (input_method_table[i].language == Mt
2312         && input_method_table[i].name == Municode)
2313       {
2314         unicode_input_method = i;
2315         break;
2316       }
2317   mplist_put_func (minput_driver->callback_list, Minput_status_start,
2318                    M17N_FUNC (input_status));
2319   mplist_put_func (minput_driver->callback_list, Minput_status_draw,
2320                    M17N_FUNC (input_status));
2321   mplist_put_func (minput_driver->callback_list, Minput_status_done,
2322                    M17N_FUNC (input_status));
2323   mplist_put_func (minput_driver->callback_list, Minput_get_surrounding_text,
2324                    M17N_FUNC (surrounding_text_handler));
2325   mplist_put_func (minput_driver->callback_list, Minput_delete_surrounding_text,
2326                    M17N_FUNC (surrounding_text_handler));
2327
2328   current_input_context = NULL;
2329   current_input_method = -1;
2330
2331   if (initial_input_method)
2332     {
2333       char *lang_name, *method_name;
2334       char *p = strchr (initial_input_method, '-');
2335
2336       if (p && p[1])
2337         lang_name = initial_input_method, *p = '\0', method_name = p + 1;
2338       else
2339         lang_name = "t", method_name = initial_input_method;
2340
2341       for (i = 0; i < num_input_methods; i++)
2342         if ((strcmp (method_name, msymbol_name (input_method_table[i].name))
2343              == 0)
2344             && (strcmp (lang_name, msymbol_name (input_method_table[i].language)) == 0))
2345           {
2346             current_input_method = i;
2347             break;
2348           }
2349     }
2350 }
2351
2352
2353 static void
2354 MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num)
2355 {
2356   char *msg;
2357
2358   if (num && *num > 0)
2359     {
2360       int bytes = 0, i;
2361
2362       for (i = 0; i < *num; i++)
2363         bytes += strlen (str[i]) + 1;
2364       msg = alloca (bytes);
2365       strcpy (msg, str[0]);
2366       for (i = 1; i < *num; i++)
2367         strcat (msg, " "), strcat (msg, str[i]);
2368     }
2369   else if (cursor.from < nchars)
2370     {
2371       int c = mtext_ref_char (mt, cursor.from);
2372       char *name = mchar_get_prop (c, Mname);
2373
2374       if (! name)
2375         name = "";
2376       msg = alloca (10 + strlen (name));
2377       sprintf (msg, "U+%04X %s", c, name);
2378     }
2379   else
2380     {
2381       msg = "";
2382     }
2383   XtSetArg (arg[0], XtNlabel, msg);
2384   XtSetValues (MessageWidget, arg, 1);
2385 }
2386
2387 typedef struct
2388 {
2389   int type;
2390   char *name1, *name2;
2391   XtCallbackProc proc;
2392   XtPointer client_data;
2393   int status;
2394   Widget w;
2395 } MenuRec;
2396
2397 void PopupProc (Widget w, XtPointer client_data, XtPointer call_data);
2398
2399 void SaveProc (Widget w, XtPointer client_data, XtPointer call_data);
2400
2401 MenuRec FileMenu[] =
2402   { { 0, "Open", NULL, PopupProc, FileMenu + 0, -1 },
2403     { 0, "Save", NULL, SaveProc, NULL, -1 },
2404     { 0, "Save as", NULL, PopupProc, FileMenu + 2, -1 },
2405     { 1 },
2406     { 0, "Serialize", NULL, SerializeProc, (void *) 1, -1 },
2407     { 0, "Deserialize", NULL, SerializeProc, (void *) 0, -1 },
2408     { 1 },
2409     { 0, "Dump Image Buffer", NULL, DumpImageProc, (void *) 0, -1 },
2410     { 0, "Dump Image Region", NULL, DumpImageProc, (void *) 1, -1 },
2411     { 1 },
2412     { 0, "Quit", NULL, QuitProc, NULL, -1 } };
2413
2414 void
2415 PopupProc (Widget w, XtPointer client_data, XtPointer call_data)
2416 {
2417   MenuRec *rec = (MenuRec *) client_data;
2418   Position x, y;
2419
2420   XtSetArg (arg[0], XtNvalue, "");
2421   XtSetArg (arg[1], XtNlabel, rec->name1);
2422   XtSetValues (FileDialogWidget, arg, 2);
2423   XtTranslateCoords (w, (Position) 0, (Position) 0, &x, &y);
2424   XtSetArg (arg[0], XtNx, x + 20);
2425   XtSetArg (arg[1], XtNy, y + 10);
2426   XtSetValues (FileShellWidget, arg, 2);
2427   XtPopup (FileShellWidget, XtGrabExclusive);
2428 }
2429
2430 void
2431 FileDialogProc (Widget w, XtPointer client_data, XtPointer call_data)
2432 {
2433   FILE *fp;
2434   char *label;
2435
2436   XtPopdown (FileShellWidget);
2437   if ((int) client_data == 1)
2438     return;
2439   XtSetArg (arg[0], XtNlabel, &label);
2440   XtGetValues (FileDialogWidget, arg, 1);
2441   if (strcmp (label, FileMenu[0].name1) == 0)
2442     {
2443       /* Open a file */
2444       free (filename);
2445       filename = strdup ((char *) XawDialogGetValueString (FileDialogWidget));
2446       fp = fopen (filename, "r");
2447       hide_cursor ();
2448       m17n_object_unref (mt);
2449       if (fp)
2450         {
2451           mt = mconv_decode_stream (Mcoding_utf_8_full, fp);
2452           fclose (fp);
2453           if (! mt)
2454             mt = mtext ();
2455         }
2456       else
2457         mt = mtext ();
2458       serialized = 0;
2459       nchars = mtext_len (mt);
2460       update_top (0);
2461       update_cursor (0, 1);
2462       redraw (0, win_height, 1, 1);
2463     }
2464   else if (strcmp (label, FileMenu[2].name1) == 0)
2465     SaveProc (w, (XtPointer) XawDialogGetValueString (FileDialogWidget), NULL);
2466   else
2467     fprintf (stderr, "Invalid calling sequence: FileDialogProc\n");
2468 }
2469
2470 #define SetMenu(MENU, TYPE, NAME1, NAME2, PROC, DATA, STATUS)            \
2471   ((MENU).type = (TYPE), (MENU).name1 = (NAME1), (MENU).name2 = (NAME2), \
2472    (MENU).proc = (PROC), (MENU).client_data = (XtPointer) (DATA),        \
2473    (MENU).status = (STATUS))
2474
2475
2476 Widget
2477 create_menu_button (Widget top, Widget parent, Widget left, char *button_name,
2478                     char *menu_name, MenuRec *menus, int num_menus, char *help)
2479 {
2480   Widget button, menu;
2481   char *fmt = "<EnterWindow>: highlight() MenuHelp(%s)\n\
2482                <LeaveWindow>: reset() MenuHelp()\n\
2483                <BtnDown>: reset() PopupMenu()\n\
2484                <BtnUp>: highlight()"; 
2485   int i;
2486   MenuRec *m;
2487   char *trans;
2488   int max_width = 0;
2489
2490   menu = XtCreatePopupShell (menu_name, simpleMenuWidgetClass, top, NULL, 0);
2491   for (i = 0; i < num_menus; i++)
2492     {
2493       m = menus + i;
2494       if (m->type == 0)
2495         {
2496           if (m->proc)
2497             {
2498               int n = 0;
2499
2500               if (m->status >= 0)
2501                 {
2502                   XtSetArg (arg[n], XtNleftMargin, 20), n++;
2503                   if (m->status > 0)
2504                     XtSetArg (arg[n], XtNleftBitmap, CheckPixmap), n++;
2505                 }
2506               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2507                                             menu, arg, n);
2508               XtAddCallback (m->w, XtNcallback, m->proc, m->client_data);
2509             }
2510           else
2511             {
2512               XtSetArg (arg[0], XtNsensitive, False);
2513               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2514                                             menu, arg, 2);
2515             }
2516         }
2517       else
2518         {
2519           XtCreateManagedWidget (m->name1, smeLineObjectClass, menu, NULL, 0);
2520         }
2521       if (m->name2)
2522         max_width = 1;
2523     }
2524   trans = alloca (strlen (fmt) + strlen (help));
2525   sprintf (trans, fmt, help);
2526   XtSetArg (arg[0], XtNmenuName, menu_name);
2527   XtSetArg (arg[1], XtNtranslations, XtParseTranslationTable ((String) trans));
2528   XtSetArg (arg[2], XtNinternalWidth, 2);
2529   XtSetArg (arg[3], XtNhighlightThickness, 1);
2530   XtSetArg (arg[4], XtNleft, XawChainLeft);
2531   XtSetArg (arg[5], XtNright, XawChainLeft);
2532   XtSetArg (arg[6], XtNinternational, True);
2533   i = 7;
2534   if (left)
2535     XtSetArg (arg[i], XtNfromHoriz, left), i++;
2536   button = XtCreateManagedWidget (button_name, menuButtonWidgetClass, parent,
2537                                   arg, i);
2538
2539   if (max_width)
2540     {
2541       int height, ascent, *width = alloca (sizeof (int) * num_menus);
2542       int *len = alloca (sizeof (int) * num_menus);
2543
2544       XFontSet font_set;
2545       XFontSetExtents *fontset_extents;
2546
2547       XtSetArg (arg[0], XtNfontSet, &font_set);
2548       XtGetValues (button, arg, 1);
2549
2550       fontset_extents = XExtentsOfFontSet (font_set);
2551       height = fontset_extents->max_logical_extent.height;
2552       ascent = - fontset_extents->max_logical_extent.y;
2553
2554       for (i = 0; i < num_menus; i++)
2555         if (menus[i].name2)
2556           {
2557             len[i] = strlen (menus[i].name2);
2558             width[i] = XmbTextEscapement (font_set, menus[i].name2, len[i]);
2559             if (max_width < width[i])
2560               max_width = width[i];
2561           }
2562       for (i = 0; i < num_menus; i++)
2563         if (menus[i].name2)
2564           {
2565             Pixmap pixmap = XCreatePixmap (display,
2566                                            RootWindow (display, screen),
2567                                            max_width, height, 1);
2568             XFillRectangle (display, pixmap, mono_gc_inv,
2569                             0, 0, max_width, height);
2570             XmbDrawString (display, pixmap, font_set, mono_gc,
2571                            max_width - width[i], ascent,
2572                            menus[i].name2, len[i]);
2573             XtSetArg (arg[0], XtNrightBitmap, pixmap);
2574             XtSetArg (arg[1], XtNrightMargin, max_width + 20);
2575             XtSetValues (menus[i].w, arg, 2);
2576           }
2577     }
2578
2579   return button;
2580 }
2581
2582
2583 XtActionsRec actions[] = {
2584   {"Expose", ExposeProc},
2585   {"Configure", ConfigureProc},
2586   {"Key", KeyProc},
2587   {"ButtonPress", ButtonProc},
2588   {"ButtonRelease", ButtonReleaseProc},
2589   {"ButtonMotion", ButtonMoveProc},
2590   {"Button2Press", Button2Proc},
2591   {"MenuHelp", MenuHelpProc},
2592   {"FocusIn", FocusInProc},
2593   {"FocusOut", FocusOutProc}
2594 };
2595
2596
2597 /* Print the usage of this program (the name is PROG), and exit with
2598    EXIT_CODE.  */
2599
2600 void
2601 help_exit (char *prog, int exit_code)
2602 {
2603   char *p = prog;
2604
2605   while (*p)
2606     if (*p++ == '/')
2607       prog = p;
2608
2609   printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] FILE\n", prog);
2610   printf ("Display FILE on a window and allow users to edit it.\n");
2611   printf ("XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
2612   printf ("The following OPTIONs are available.\n");
2613   printf ("  %-13s\n\t\t%s", "--fontset FONTSET",
2614           "Use the specified fontset\n");
2615   printf ("  %-13s %s", "-s SIZE", "Font size in 1/10 point (default 120).\n");
2616   printf ("  %-13s\n\t\t%s", "--im INPUT-METHOD",
2617           "Input method activated initially.\n");
2618   printf ("  %-13s %s", "--version", "print version number\n");
2619   printf ("  %-13s %s", "-h, --help", "print this message\n");
2620           
2621   exit (exit_code);
2622 }
2623
2624 int
2625 main (int argc, char **argv)
2626 {
2627   Widget form, BodyWidget, w;
2628   char *fontset_name = NULL;
2629   char *font_name = NULL;
2630   int fontsize = 0;
2631   char *initial_input_method = NULL;
2632   int col = 80, row = 32;
2633   /* Translation table for TextWidget.  */
2634   String trans = "<Expose>: Expose()\n\
2635                   <Configure>: Configure()\n\
2636                   <Key>: Key()\n\
2637                   <KeyUp>: Key()\n\
2638                   <Btn1Down>: ButtonPress()\n\
2639                   <Btn1Up>: ButtonRelease()\n\
2640                   <Btn1Motion>: ButtonMotion()\n\
2641                   <Btn2Down>: Button2Press()";
2642   /* Translation table for the top form widget.  */
2643   String trans2 = "<Key>: Key()\n\
2644                    <KeyUp>: Key()\n\
2645                    <FocusIn>: FocusIn()\n\
2646                    <FocusOut>: FocusOut()";
2647   String pop_face_trans
2648     = "<EnterWindow>: MenuHelp(Pop face property) highlight()\n\
2649        <LeaveWindow>: MenuHelp() reset()\n\
2650        <Btn1Down>: set()\n\
2651        <Btn1Up>: notify() unset()"; 
2652   String pop_lang_trans
2653     = "<EnterWindow>: MenuHelp(Pop language property) highlight()\n\
2654        <LeaveWindow>: MenuHelp() reset()\n\
2655        <Btn1Down>: set()\n\
2656        <Btn1Up>: notify() unset()"; 
2657   int font_width, font_ascent, font_descent;
2658   int with_xim = 0;
2659   int i, j;
2660   char *filter = NULL;
2661   MFont *font = NULL;
2662
2663   setlocale (LC_ALL, "");
2664   /* Create the top shell.  */
2665   XtSetLanguageProc (NULL, NULL, NULL);
2666   ShellWidget = XtOpenApplication (&context, "M17NEdit", NULL, 0, &argc, argv,
2667                                    NULL, sessionShellWidgetClass, NULL, 0);
2668   display = XtDisplay (ShellWidget);
2669   screen = XScreenNumberOfScreen (XtScreen (ShellWidget));
2670
2671   /* Parse the remaining command line arguments.  */
2672   for (i = 1; i < argc; i++)
2673     {
2674       if (! strcmp (argv[i], "--help")
2675           || ! strcmp (argv[i], "-h"))
2676         help_exit (argv[0], 0);
2677       else if (! strcmp (argv[i], "--version"))
2678         {
2679           printf ("m17n-edit (m17n library) %s\n", M17NLIB_VERSION_NAME);
2680           printf ("Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 AIST, JAPAN\n");
2681           exit (0);
2682         }
2683       else if (! strcmp (argv[i], "--geometry"))
2684         {
2685           i++;
2686           if (sscanf (argv[i], "%dx%d", &col, &row) != 2)
2687             help_exit (argv[0], 1);
2688         }
2689       else if (! strcmp (argv[i], "-s"))
2690         {
2691           i++;
2692           fontsize = atoi (argv[i]);
2693           if (fontsize < 0)
2694             fontsize = 120;
2695         }
2696       else if (! strcmp (argv[i], "--fontset"))
2697         {
2698           i++;
2699           fontset_name = strdup (argv[i]);
2700         }
2701       else if (! strcmp (argv[i], "--font"))
2702         {
2703           i++;
2704           font_name = strdup (argv[i]);
2705         }
2706       else if (! strcmp (argv[i], "--im"))
2707         {
2708           i++;
2709           initial_input_method = strdup (argv[i]);
2710         }
2711       else if (! strcmp (argv[i], "--with-xim"))
2712         {
2713           with_xim = 1;
2714         }
2715       else if (! strcmp (argv[i], "--filter"))
2716         {
2717           i++;
2718           filter = argv[i];
2719         }
2720       else if (argv[i][0] != '-')
2721         {
2722           filename = strdup (argv[i]);
2723         }
2724       else
2725         {
2726           fprintf (stderr, "Unknown option: %s\n", argv[i]);
2727           help_exit (argv[0], 1);
2728         }
2729     }
2730   if (! filename)
2731     filename = strdup ("/dev/null");
2732
2733   mdatabase_dir = ".";
2734   /* Initialize the m17n library.  */
2735   M17N_INIT ();
2736   if (merror_code != MERROR_NONE)
2737     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library!");
2738   minput_driver = &minput_gui_driver;
2739
2740   mt = read_file (filename);
2741   serialized = 0;
2742
2743   nchars = mtext_len (mt);
2744
2745   Mword = msymbol ("word");
2746
2747   {
2748     MFace *face = mface ();
2749
2750     mface_put_prop (face, Mforeground, msymbol ("blue"));
2751     mface_put_prop (face, Mbackground, msymbol ("yellow"));
2752     mface_put_prop (face, Mvideomode, Mreverse);
2753     selection = mtext_property (Mface, face, MTEXTPROP_NO_MERGE);
2754     m17n_object_unref (face);
2755   }
2756
2757   /* This tells ExposeProc to initialize everything.  */
2758   top.from = -1;
2759   
2760   XA_TEXT = XInternAtom (display, "TEXT", False);
2761   XA_COMPOUND_TEXT = XInternAtom (display, "COMPOUND_TEXT", False);
2762   XA_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
2763   Mcoding_compound_text = mconv_resolve_coding (msymbol ("compound-text"));
2764   if (Mcoding_compound_text == Mnil)
2765     FATAL_ERROR ("%s\n", "Don't know about COMPOUND-TEXT encoding!");
2766
2767   {
2768     MPlist *plist = mplist ();
2769     MFont *font;
2770
2771     mplist_put (plist, msymbol ("widget"), ShellWidget);
2772     if (fontset_name || font_name || fontsize > 0)
2773       {
2774         MFace *face;
2775
2776         if (font_name)
2777           {
2778             font = mfont_parse_name (font_name, Mnil);
2779             if (font)
2780               face = mface_from_font (font);
2781             else
2782               face = mface ();
2783           }
2784         else
2785           face = mface ();
2786         if (fontset_name)
2787           {
2788             MFontset *fontset = mfontset (fontset_name);
2789
2790             mface_put_prop (face, Mfontset, fontset);
2791             m17n_object_unref (fontset);
2792           }
2793         if (fontsize > 0)
2794           mface_put_prop (face, Msize, (void *) fontsize);
2795         mplist_add (plist, Mface, face);
2796         m17n_object_unref (face);
2797       }
2798     frame = mframe (plist);
2799     if (! frame)
2800       FATAL_ERROR ("%s\n", "Fail to create a frame!");
2801     m17n_object_unref (plist);
2802     face_default = mface_copy ((MFace *) mframe_get_prop (frame, Mface));
2803     default_face_list = mplist ();
2804     mplist_add (default_face_list, Mt, face_default);
2805     face_default_fontset = mface ();
2806     mface_put_prop (face_default_fontset, Mfontset,
2807                     mface_get_prop (face_default, Mfontset));
2808
2809     font = (MFont *) mframe_get_prop (frame, Mfont);
2810     default_font_size = (int) mfont_get_prop (font, Msize);
2811   }
2812
2813   font_width = (int) mframe_get_prop (frame, Mfont_width);
2814   font_ascent = (int) mframe_get_prop (frame, Mfont_ascent);
2815   font_descent = (int) mframe_get_prop (frame, Mfont_descent);
2816   win_width = font_width * col;
2817   win_height = (font_ascent + font_descent) * row;
2818
2819   {
2820     MFaceBoxProp prop;
2821
2822     prop.width = 4;
2823     prop.color_top = prop.color_left = msymbol ("magenta");
2824     prop.color_bottom = prop.color_right = msymbol ("red");
2825     prop.inner_hmargin = prop.inner_vmargin = 1;
2826     prop.outer_hmargin = prop.outer_vmargin = 2;
2827
2828     face_box = mface ();
2829     mface_put_prop (face_box, Mbox, &prop);
2830   }
2831
2832   face_courier = mface ();
2833   mface_put_prop (face_courier, Mfamily, msymbol ("courier"));
2834   face_helvetica = mface ();
2835   mface_put_prop (face_helvetica, Mfamily, msymbol ("helvetica"));
2836   face_times = mface ();
2837   mface_put_prop (face_times, Mfamily, msymbol ("times"));
2838   face_dv_ttyogesh = mface ();
2839   mface_put_prop (face_dv_ttyogesh, Mfamily, msymbol ("dv-ttyogesh"));
2840   face_freesans = mface ();
2841   mface_put_prop (face_freesans, Mfamily, msymbol ("freesans"));
2842   face_freeserif = mface ();
2843   mface_put_prop (face_freeserif, Mfamily, msymbol ("freeserif"));
2844   face_freemono = mface ();
2845   mface_put_prop (face_freemono, Mfamily, msymbol ("freemono"));
2846
2847   face_xxx_large = mface ();
2848   mface_put_prop (face_xxx_large, Mratio, (void *) 300);
2849   {
2850     MFont *latin_font = mframe_get_prop (frame, Mfont);
2851     MFont *dev_font = mfont ();
2852     MFont *thai_font = mfont ();
2853     MFont *tib_font = mfont ();
2854     MFontset *fontset, *fontset_no_ctl;
2855     MSymbol unicode_bmp = msymbol ("unicode-bmp");
2856     MSymbol no_ctl = msymbol ("no-ctl");
2857
2858     mfont_put_prop (dev_font, Mfamily, msymbol ("raghindi"));
2859     mfont_put_prop (dev_font, Mregistry, unicode_bmp);
2860     mfont_put_prop (thai_font, Mfamily, msymbol ("norasi"));
2861     mfont_put_prop (thai_font, Mregistry, unicode_bmp);
2862     mfont_put_prop (tib_font, Mfamily, msymbol ("mtib"));
2863     mfont_put_prop (tib_font, Mregistry, unicode_bmp);
2864
2865     fontset = mfontset (fontset_name);
2866     fontset_no_ctl = mfontset_copy (fontset, "no-ctl");
2867     m17n_object_unref (fontset);
2868     mfontset_modify_entry (fontset_no_ctl, msymbol ("latin"), Mnil, Mnil,
2869                            latin_font, Mnil, 0);
2870     mfontset_modify_entry (fontset_no_ctl, msymbol ("devanagari"), Mnil, Mnil,
2871                            dev_font, no_ctl, 0);
2872     mfontset_modify_entry (fontset_no_ctl, msymbol ("thai"), Mnil, Mnil,
2873                            thai_font, no_ctl, 0);
2874     mfontset_modify_entry (fontset_no_ctl, msymbol ("tibetan"), Mnil, Mnil,
2875                            tib_font, no_ctl, 0);
2876     face_no_ctl_fontset = mface ();
2877     mface_put_prop (face_no_ctl_fontset, Mfontset, fontset_no_ctl);
2878     m17n_object_unref (fontset_no_ctl);
2879
2880     free (dev_font);
2881     free (thai_font);
2882     free (tib_font);
2883   }
2884
2885   setup_input_methods (with_xim, initial_input_method);
2886
2887   gc = DefaultGC (display, screen);
2888
2889   XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans2));
2890   XtSetArg (arg[1], XtNdefaultDistance, 2);
2891   form = XtCreateManagedWidget ("form", formWidgetClass, ShellWidget, arg, 2);
2892
2893   XtSetArg (arg[0], XtNborderWidth, 0);
2894   XtSetArg (arg[1], XtNdefaultDistance, 2);
2895   XtSetArg (arg[2], XtNtop, XawChainTop);
2896   XtSetArg (arg[3], XtNbottom, XawChainTop);
2897   XtSetArg (arg[4], XtNleft, XawChainLeft);
2898   XtSetArg (arg[5], XtNright, XawChainRight);
2899   XtSetArg (arg[6], XtNresizable, True);
2900   HeadWidget = XtCreateManagedWidget ("head", formWidgetClass, form, arg, 7);
2901   XtSetArg (arg[7], XtNfromVert, HeadWidget);
2902   FaceWidget = XtCreateManagedWidget ("face", formWidgetClass, form, arg, 8);
2903   XtSetArg (arg[7], XtNfromVert, FaceWidget);
2904   LangWidget = XtCreateManagedWidget ("lang", formWidgetClass, form, arg, 8);
2905   XtSetArg (arg[3], XtNbottom, XawChainBottom);
2906   XtSetArg (arg[7], XtNfromVert, LangWidget);
2907   BodyWidget = XtCreateManagedWidget ("body", formWidgetClass, form, arg, 8);
2908   XtSetArg (arg[2], XtNtop, XawChainBottom);
2909   XtSetArg (arg[7], XtNfromVert, BodyWidget);
2910   TailWidget = XtCreateManagedWidget ("tail", formWidgetClass, form, arg, 8);
2911
2912   FileShellWidget = XtCreatePopupShell ("FileShell", transientShellWidgetClass,
2913                                         HeadWidget, NULL, 0);
2914   XtSetArg (arg[0], XtNvalue, "");
2915   FileDialogWidget = XtCreateManagedWidget ("File", dialogWidgetClass,
2916                                             FileShellWidget, arg, 1);
2917   XawDialogAddButton (FileDialogWidget, "OK",
2918                       FileDialogProc, (XtPointer) 0);
2919   XawDialogAddButton (FileDialogWidget, "CANCEL",
2920                       FileDialogProc, (XtPointer) 1);
2921
2922   CheckPixmap = XCreateBitmapFromData (display, RootWindow (display, screen),
2923                                        (char *) check_bits,
2924                                        check_width, check_height);
2925   {
2926     unsigned long valuemask = GCForeground;
2927     XGCValues values;
2928
2929     values.foreground = 1;
2930     mono_gc = XCreateGC (display, CheckPixmap, valuemask, &values);
2931     values.foreground = 0;
2932     mono_gc_inv = XCreateGC (display, CheckPixmap, valuemask, &values);
2933   }
2934
2935   {
2936     MenuRec *menus;
2937     int num_menus = 10;
2938
2939     if (num_menus < num_input_methods + 2)
2940       num_menus = num_input_methods + 2;
2941     if (num_menus < num_faces + 1)
2942       num_menus = num_faces + 1;
2943     menus = alloca (sizeof (MenuRec) * num_menus);
2944
2945     w = create_menu_button (ShellWidget, HeadWidget, NULL, "File", "File Menu",
2946                             FileMenu, sizeof FileMenu / sizeof (MenuRec),
2947                             "File I/O, Serialization, Image, Quit");
2948
2949     SetMenu (menus[0], 0, "Logical Move", NULL, CursorProc, 0, 1);
2950     SetMenu (menus[1], 0, "Visual Move", NULL, CursorProc, 1, 0);
2951     SetMenu (menus[2], 1, "", NULL, NULL, NULL, 0);
2952     SetMenu (menus[3], 0, "Box type", NULL, CursorProc, 2, 0);
2953     SetMenu (menus[4], 0, "Bar type", NULL, CursorProc, 3, 1);
2954     SetMenu (menus[5], 0, "Bidi type", NULL, CursorProc, 4, 0);
2955     w = create_menu_button (ShellWidget, HeadWidget, w,
2956                             "Cursor", "Cursor Menu",
2957                             menus, 6, "Cursor Movement Mode, Cursor Shape");
2958     CursorMenus[0] = menus[0].w;
2959     CursorMenus[1] = menus[1].w;
2960     CursorMenus[2] = menus[3].w;
2961     CursorMenus[3] = menus[4].w;
2962     CursorMenus[4] = menus[5].w;
2963
2964     SetMenu (menus[0], 0, "disable", NULL, BidiProc, 0, 0);
2965     SetMenu (menus[1], 0, "Left  (|--> |)", NULL, BidiProc, 1, 1);
2966     SetMenu (menus[2], 0, "Right (| <--|)", NULL, BidiProc, 2, 0);
2967     w = create_menu_button (ShellWidget, HeadWidget, w, "Bidi", "Bidi Menu",
2968                             menus, 3, "BIDI Processing Mode");
2969     for (i = 0; i < 3; i++)
2970       BidiMenus[i] = menus[i].w;
2971
2972     SetMenu (menus[0], 0, "truncate", NULL, LineBreakProc, 0, 0);
2973     SetMenu (menus[1], 0, "break at edge", NULL, LineBreakProc, 1, 1);
2974     SetMenu (menus[2], 0, "break at word boundary", NULL, LineBreakProc, 2, 0);
2975     w = create_menu_button (ShellWidget, HeadWidget, w, "LineBreak",
2976                             "LineBreak Menu",
2977                             menus, 3, "How to break lines");
2978     for (i = 0; i < 3; i++)
2979       LineBreakMenus[i] = menus[i].w;
2980
2981     SetMenu (menus[0], 0, "none", NULL, InputMethodProc, -2, 1);
2982     SetMenu (menus[1], 0, "auto", NULL, InputMethodProc, -1, 0);
2983     for (i = 0; i < num_input_methods; i++)
2984       {
2985         InputMethodInfo *im = input_method_table + i;
2986         char *name1, *name2;
2987
2988         if (im->language != Mnil && im->language != Mt)
2989           {
2990             MSymbol sym = mlanguage_name (im->language);
2991             if (sym == Mnil)
2992               name1 = msymbol_name (im->language);
2993             else
2994               name1 = msymbol_name (sym);
2995             name2 = msymbol_name (im->name);
2996           }
2997         else
2998           name1 = msymbol_name (im->name), name2 = NULL;
2999
3000         SetMenu (menus[i + 2], 0, name1, name2, InputMethodProc, i, 0);
3001       }
3002     w = create_menu_button (ShellWidget, HeadWidget, w, "InputMethod",
3003                             "Input Method Menu", menus, i + 2,
3004                             "Select input method");
3005
3006     {
3007       unsigned long valuemask = GCForeground;
3008       XGCValues values;
3009
3010       XtSetArg (arg[0], XtNbackground, &values.foreground);
3011       XtGetValues (w, arg, 1);
3012       gc_inv = XCreateGC (display, RootWindow (display, screen),
3013                           valuemask, &values);
3014     }
3015
3016     InputMethodMenus = malloc (sizeof (Widget) * (num_input_methods + 2));
3017     for (i = 0; i < num_input_methods + 2; i++)
3018       InputMethodMenus[i] = menus[i].w;
3019
3020     if (filter)
3021       {
3022         SetMenu (menus[0], 0, filter, NULL, FilterProc, filter, 0);
3023         w = create_menu_button (ShellWidget, HeadWidget, w, "Filter",
3024                                 "Filter Menu", menus, 1,
3025                                 "Select filter to run");
3026       }
3027
3028     input_status_width = font_width * 8;
3029     input_status_height = (font_ascent + font_descent) * 2.4;
3030     input_status_pixmap = XCreatePixmap (display, RootWindow (display, screen),
3031                                          input_status_width,
3032                                          input_status_height,
3033                                          DefaultDepth (display, screen));
3034     {
3035       MFaceBoxProp prop;
3036
3037       prop.width = 1;
3038       prop.color_top = prop.color_bottom
3039         = prop.color_left = prop.color_right = Mnil;
3040       prop.inner_hmargin = prop.inner_vmargin = 1;
3041       prop.outer_hmargin = prop.outer_vmargin = 0;
3042       face_input_status = mface_copy (face_default);
3043       mface_put_prop (face_input_status, Mbox, &prop);
3044     }
3045
3046     XFillRectangle (display, input_status_pixmap, gc_inv,
3047                     0, 0, input_status_width, input_status_height);
3048     XtSetArg (arg[0], XtNfromHoriz, w);
3049     XtSetArg (arg[1], XtNleft, XawRubber);
3050     XtSetArg (arg[2], XtNright, XawChainRight);
3051     XtSetArg (arg[3], XtNborderWidth, 0);
3052     XtSetArg (arg[4], XtNlabel, "          ");
3053     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
3054     CurIMLang = XtCreateManagedWidget ("CurIMLang", labelWidgetClass,
3055                                        HeadWidget, arg, 6);
3056     XtSetArg (arg[0], XtNfromHoriz, CurIMLang);
3057     XtSetArg (arg[1], XtNleft, XawChainRight);
3058     XtSetArg (arg[4], XtNbitmap, input_status_pixmap);
3059     CurIMStatus = XtCreateManagedWidget ("CurIMStatus", labelWidgetClass,
3060                                          HeadWidget, arg, 5);
3061
3062     XtSetArg (arg[0], XtNborderWidth, 0);
3063     XtSetArg (arg[1], XtNleft, XawChainLeft);
3064     XtSetArg (arg[2], XtNright, XawChainLeft);
3065     w = XtCreateManagedWidget ("Face", labelWidgetClass, FaceWidget, arg, 3);
3066     for (i = 0; i < num_faces;)
3067       {
3068         char *label_menu = face_table[i++].name; /* "Menu Xxxx" */
3069         char *label = label_menu + 5;            /* "Xxxx" */
3070
3071         for (j = i; j < num_faces && face_table[j].face; j++)
3072           SetMenu (menus[j - i], 0, face_table[j].name, NULL,
3073                    FaceProc, j, -1);
3074         w = create_menu_button (ShellWidget, FaceWidget, w,
3075                                 label, label_menu,
3076                                 menus, j - i, "Push face property");
3077         i = j;
3078       }
3079
3080     XtSetArg (arg[0], XtNfromHoriz, w);
3081     XtSetArg (arg[1], XtNleft, XawChainLeft);
3082     XtSetArg (arg[2], XtNright, XawChainLeft);
3083     XtSetArg (arg[3], XtNhorizDistance, 10);
3084     XtSetArg (arg[4], XtNlabel, "Pop");
3085     XtSetArg (arg[5], XtNtranslations,
3086               XtParseTranslationTable (pop_face_trans));
3087     w = XtCreateManagedWidget ("Pop Face", commandWidgetClass,
3088                                FaceWidget, arg, 6);
3089     XtAddCallback (w, XtNcallback, FaceProc, (void *) -1);
3090
3091     XtSetArg (arg[0], XtNfromHoriz, w);
3092     XtSetArg (arg[1], XtNleft, XawChainLeft);
3093     XtSetArg (arg[2], XtNright, XawChainRight);
3094     XtSetArg (arg[3], XtNlabel, "");
3095     XtSetArg (arg[4], XtNborderWidth, 0);
3096     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
3097     CurFaceWidget = XtCreateManagedWidget ("Current Face", labelWidgetClass,
3098                                            FaceWidget, arg, 6);
3099
3100     XtSetArg (arg[0], XtNborderWidth, 0);
3101     XtSetArg (arg[1], XtNleft, XawChainLeft);
3102     XtSetArg (arg[2], XtNright, XawChainLeft);
3103     w = XtCreateManagedWidget ("Lang", labelWidgetClass, LangWidget, arg, 3);
3104     {
3105       MPlist *plist[11], *pl;
3106       char langname[3];
3107
3108       for (i = 0; i < 11; i++) plist[i] = NULL;
3109       langname[2] = '\0';
3110       for (langname[0] = 'a'; langname[0] <= 'z'; langname[0]++)
3111         for (langname[1] = 'a'; langname[1] <= 'z'; langname[1]++)
3112           {
3113             MSymbol sym = msymbol_exist (langname);
3114             MSymbol fullname;
3115
3116             if (sym != Mnil
3117                 && ((fullname = mlanguage_name (sym)) != Mnil))
3118               {
3119                 char *name = msymbol_name (fullname);
3120                 char c = name[0];
3121
3122                 if (c >= 'a' && c <= 'z')
3123                   {
3124                     int idx = (c < 'u') ? (c - 'a') / 2 : 10;
3125
3126                     pl = plist[idx];
3127                     if (! pl)
3128                       pl = plist[idx] = mplist ();
3129                     for (; mplist_next (pl); pl = mplist_next (pl))
3130                       if (strcmp (name, (char *) mplist_value (pl)) < 0)
3131                         break;
3132                     mplist_push (pl, sym, fullname);
3133                   }
3134               }
3135           }
3136
3137       for (i = 0; i < 11; i++)
3138         if (plist[i])
3139           {
3140             char *name = alloca (9);
3141
3142             sprintf (name, "Menu %c-%c", 'A' + i * 2, 'A' + i * 2 + 1);
3143             if (i == 10)
3144               name[7] = 'Z';
3145             for (j = 0, pl = plist[i]; mplist_next (pl);
3146                  j++, pl = mplist_next (pl))
3147               SetMenu (menus[j], 0, msymbol_name ((MSymbol) mplist_value (pl)),
3148                        msymbol_name (mplist_key (pl)),
3149                        LangProc, mplist_key (pl), -1);
3150             w = create_menu_button (ShellWidget, LangWidget, w, name + 5, name,
3151                                     menus, j, "Push language property");
3152           }
3153       for (i = 0; i < 11; i++)
3154         if (plist[i])
3155           m17n_object_unref (plist[i]);
3156     }
3157     XtSetArg (arg[0], XtNfromHoriz, w);
3158     XtSetArg (arg[1], XtNleft, XawChainLeft);
3159     XtSetArg (arg[2], XtNright, XawChainLeft);
3160     XtSetArg (arg[3], XtNhorizDistance, 10);
3161     XtSetArg (arg[4], XtNlabel, "Pop");
3162     XtSetArg (arg[5], XtNtranslations,
3163               XtParseTranslationTable (pop_lang_trans));
3164     w = XtCreateManagedWidget ("Pop Lang", commandWidgetClass,
3165                                LangWidget, arg, 6);
3166     XtAddCallback (w, XtNcallback, LangProc, Mnil);
3167
3168     XtSetArg (arg[0], XtNfromHoriz, w);
3169     XtSetArg (arg[1], XtNleft, XawChainLeft);
3170     XtSetArg (arg[2], XtNright, XawChainRight);
3171     XtSetArg (arg[3], XtNlabel, "");
3172     XtSetArg (arg[4], XtNborderWidth, 0);
3173     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
3174     CurLangWidget = XtCreateManagedWidget ("Current Lang", labelWidgetClass,
3175                                            LangWidget, arg, 6);
3176   }
3177
3178   XtSetArg (arg[0], XtNheight, win_height);
3179   XtSetArg (arg[1], XtNwidth, 10);
3180   XtSetArg (arg[2], XtNleft, XawChainLeft);
3181   XtSetArg (arg[3], XtNright, XawChainLeft);
3182   SbarWidget = XtCreateManagedWidget ("sbar", scrollbarWidgetClass, BodyWidget,
3183                                       arg, 4);
3184   XtAddCallback (SbarWidget, XtNscrollProc, ScrollProc, NULL);
3185   XtAddCallback (SbarWidget, XtNjumpProc, JumpProc, NULL);
3186
3187   XtSetArg (arg[0], XtNheight, win_height);
3188   XtSetArg (arg[1], XtNwidth, win_width);
3189   XtSetArg (arg[2], XtNtranslations, XtParseTranslationTable (trans));
3190   XtSetArg (arg[3], XtNfromHoriz, SbarWidget);
3191   XtSetArg (arg[4], XtNleft, XawChainLeft);
3192   XtSetArg (arg[5], XtNright, XawChainRight);
3193   TextWidget = XtCreateManagedWidget ("text", simpleWidgetClass, BodyWidget,
3194                                       arg, 5);
3195
3196   XtSetArg (arg[0], XtNborderWidth, 0);
3197   XtSetArg (arg[1], XtNleft, XawChainLeft);
3198   XtSetArg (arg[2], XtNright, XawChainRight);
3199   XtSetArg (arg[3], XtNresizable, True);
3200   XtSetArg (arg[4], XtNjustify, XtJustifyLeft);
3201   MessageWidget = XtCreateManagedWidget ("message", labelWidgetClass,
3202                                          TailWidget, arg, 5);
3203
3204   memset (&control, 0, sizeof control);
3205   control.two_dimensional = 1;
3206   control.enable_bidi = 1;
3207   control.anti_alias = 1;
3208   control.min_line_ascent = font_ascent;
3209   control.min_line_descent = font_descent;
3210   control.max_line_width = win_width;
3211   control.with_cursor = 1;
3212   control.cursor_width = 2;
3213   control.partial_update = 1;
3214   control.ignore_formatting_char = 1;
3215
3216   memset (&input_status_control, 0, sizeof input_status_control);
3217   input_status_control.enable_bidi = 1;
3218
3219   XtAppAddActions (context, actions, XtNumber (actions));
3220   XtRealizeWidget (ShellWidget);
3221
3222   win = XtWindow (TextWidget);
3223
3224   XtAppMainLoop (context);
3225
3226   if (current_input_context)
3227     minput_destroy_ic (current_input_context);
3228   for (i = 0; i < num_input_methods; i++)
3229     if (input_method_table[i].im)
3230       minput_close_im (input_method_table[i].im);
3231   m17n_object_unref (frame);
3232   m17n_object_unref (mt);
3233   m17n_object_unref (face_xxx_large);
3234   m17n_object_unref (face_box);
3235   m17n_object_unref (face_courier);
3236   m17n_object_unref (face_helvetica);
3237   m17n_object_unref (face_times);
3238   m17n_object_unref (face_dv_ttyogesh);
3239   m17n_object_unref (face_freesans);
3240   m17n_object_unref (face_freeserif);
3241   m17n_object_unref (face_freemono);
3242   m17n_object_unref (face_default_fontset);
3243   m17n_object_unref (face_no_ctl_fontset);
3244   m17n_object_unref (face_input_status);
3245   m17n_object_unref (face_default);
3246   m17n_object_unref (default_face_list);
3247   m17n_object_unref (selection);
3248   if (font)
3249     free (font);
3250
3251   XFreeGC (display, mono_gc);
3252   XFreeGC (display, mono_gc_inv);
3253   XFreeGC (display, gc_inv);
3254   XtUninstallTranslations (form);
3255   XtUninstallTranslations (TextWidget);
3256   XtDestroyWidget (ShellWidget);
3257   XtDestroyApplicationContext (context);
3258
3259   M17N_FINI ();
3260
3261   free (font_name);
3262   free (fontset_name);
3263   free (filename);
3264   free (input_method_table);
3265   free (InputMethodMenus);
3266
3267   exit (0);
3268 }
3269
3270 #else  /* not HAVE_X11_XAW_COMMAND_H */
3271
3272 int
3273 main (int argc, char **argv)
3274 {
3275   fprintf (stderr,
3276            "Building of this program failed (lack of some header files)\n");
3277   exit (1);
3278 }
3279
3280 #endif /* not HAVE_X11_XAW_COMMAND_H */
3281
3282 #endif /* not FOR_DOXYGEN */