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