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