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