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