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