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