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