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