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