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