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