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