(CursorProc): Call update_cursor.
[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.0.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
1007   mtext_copy (this_mt, 0, mt, from, to);
1008   if (*target == XA_TEXT)
1009     {
1010 #ifdef X_HAVE_UTF8_STRING
1011       coding = Mcoding_utf_8;
1012       *return_type = XA_UTF8_STRING;
1013 #else
1014       coding = Mcoding_compound_text;
1015       *return_type = XA_COMPOUND_TEXT;
1016 #endif
1017     }
1018   else if (*target == XA_STRING)
1019     {
1020       int len = to - from;
1021       int i;
1022
1023       for (i = 0; i < len; i++)
1024         if (mtext_ref_char (this_mt, i) >= 0x100)
1025           /* Can't encode in XA_STRING */
1026           return False;
1027       coding = Mcoding_iso_8859_1;
1028       *return_type = XA_STRING;
1029     }
1030   else if (*target == XA_COMPOUND_TEXT)
1031     {
1032       coding = Mcoding_compound_text;
1033       *return_type = XA_COMPOUND_TEXT;
1034     }
1035   *length = mconv_encode_buffer (coding, this_mt, buf, 4096);
1036   m17n_object_unref (this_mt);
1037   if (*length == 0)
1038     return False;
1039   *value = (XtPointer) buf;
1040   *format = 8;
1041   return True;
1042 }
1043
1044
1045 /* Unselect the text.  It is called when we loose the selection.  */
1046 void
1047 lose_selection (Widget w, Atom *selection_atom)
1048 {
1049   if (SELECTEDP ())
1050     {
1051       mtext_detach_property (selection);
1052       redraw (sel_start.y0, sel_end.y1, 1, 0);
1053     }
1054 }
1055
1056 void
1057 get_selection (Widget w, XtPointer cliend_data, Atom *selection,  Atom *type,
1058                XtPointer value, unsigned long *length, int *format)
1059 {
1060   MText *this_mt;
1061   MSymbol coding;
1062
1063   if (*type == XT_CONVERT_FAIL || ! value)
1064     goto err;
1065   if (*type == XA_STRING)
1066     coding = Mnil;
1067   else if (*type == XA_COMPOUND_TEXT)
1068     coding = msymbol ("compound-text");
1069 #ifdef X_HAVE_UTF8_STRING
1070   else if (*type == XA_UTF8_STRING)
1071     coding = msymbol ("utf-8");
1072 #endif
1073   else
1074     goto err;
1075
1076   this_mt = mconv_decode_buffer (coding, (unsigned char *) value, *length);
1077   if (! this_mt && *type != XA_UTF8_STRING)
1078     {
1079       XtGetSelectionValue (w, XA_PRIMARY, XA_UTF8_STRING, get_selection, NULL,
1080                            CurrentTime);
1081       goto err;
1082     }
1083   if (this_mt)
1084     {
1085       hide_cursor ();
1086       insert_chars (this_mt);
1087       m17n_object_unref (this_mt);
1088     }
1089
1090  err:
1091   if (value)
1092     XtFree (value);
1093 }
1094
1095 static void
1096 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
1097 {
1098   XExposeEvent *expose = (XExposeEvent *) event;
1099
1100   if (top.from < 0)
1101     {
1102       Dimension width_max, width;
1103
1104       XtSetArg (arg[0], XtNwidth, &width);
1105       XtGetValues (XtParent (w), arg, 1);
1106       width_max = width;
1107       XtGetValues (HeadWidget, arg, 1);
1108       if (width_max < width)
1109         width_max = width;
1110       XtGetValues (FaceWidget, arg, 1);
1111       if (width_max < width)
1112         width_max = width;
1113       XtGetValues (LangWidget, arg, 1);
1114       if (width_max < width)
1115         width_max = width;
1116       XtSetArg (arg[0], XtNwidth, width_max);
1117       XtSetValues (HeadWidget, arg, 1);
1118       XtSetValues (FaceWidget, arg, 1);
1119       XtSetValues (LangWidget, arg, 1);
1120       XtSetValues (XtParent (w), arg, 1);
1121       XtSetValues (TailWidget, arg, 1);
1122
1123       update_top (0);
1124       update_cursor (0, 1);
1125       redraw (0, win_height, 0, 1);
1126       show_cursor (NULL);
1127     }
1128   else
1129     {
1130       redraw (expose->y, expose->y + expose->height, 0, 0);
1131       if (current_input_context
1132           && expose->y < cur.y0 && expose->y + expose->height < cur.y1)
1133         set_input_method_spot ();
1134     }
1135 }
1136
1137 static void
1138 ConfigureProc (Widget w, XEvent *event, String *str, Cardinal *num)
1139 {
1140   XConfigureEvent *configure = (XConfigureEvent *) event;
1141   
1142   hide_cursor ();
1143   control.max_line_width = win_width = configure->width;
1144   win_height = configure->height;
1145   mdraw_clear_cache (mt);
1146   update_top (0);
1147   update_cursor (0, 1);
1148   redraw (0, win_height, 1, 1);
1149   if (current_input_context)
1150     set_input_method_spot ();
1151 }
1152
1153 static void
1154 ButtonProc (Widget w, XEvent *event, String *str, Cardinal *num)
1155 {
1156   int pos;
1157   int x = event->xbutton.x;
1158   int y = event->xbutton.y - top.ascent;
1159
1160   if (control.orientation_reversed)
1161     x -= win_width;
1162   pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
1163   if (SELECTEDP ())
1164     {
1165       XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1166       mtext_detach_property (selection);
1167       redraw (sel_start.y0, sel_end.y1, 1, 0);
1168     }
1169   hide_cursor ();
1170   update_cursor (pos, 0);
1171 }
1172
1173
1174 static void
1175 ButtonReleaseProc (Widget w, XEvent *event, String *str, Cardinal *num)
1176 {
1177   if (! SELECTEDP ())
1178     return;
1179
1180   XtOwnSelection (w, XA_PRIMARY, CurrentTime,
1181                   covert_selection, lose_selection, NULL);
1182   update_cursor (mtext_property_start (selection), 0);
1183 }
1184
1185 static
1186 void
1187 Button2Proc (Widget w, XEvent *event, String *str, Cardinal *num)
1188 {
1189   if (! SELECTEDP ())
1190     {
1191       /* We don't have a local selection.  */
1192       XtGetSelectionValue (w, XA_PRIMARY, XA_TEXT, get_selection, NULL,
1193                            CurrentTime);
1194     }
1195   else
1196     {
1197       int from = mtext_property_start (selection);
1198       int to = mtext_property_end (selection);
1199       MText *this_mt;
1200       int pos;
1201       int x = event->xbutton.x;
1202       int y = event->xbutton.y - top.ascent;
1203
1204       if (control.orientation_reversed)
1205         x -= win_width;
1206       pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
1207       
1208       XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1209       mtext_detach_property (selection);
1210       hide_cursor ();
1211       this_mt = mtext_copy (mtext (), 0, mt, from, to);
1212       update_cursor (pos, 0);
1213       insert_chars (this_mt);
1214       m17n_object_unref (this_mt);
1215     }
1216 }
1217
1218 static void
1219 ButtonMoveProc (Widget w, XEvent *event, String *str, Cardinal *num)
1220 {
1221   int pos;
1222   int x = event->xbutton.x;
1223   int y = event->xbutton.y;
1224
1225   if (control.orientation_reversed)
1226     x -= win_width;
1227   if (y < cur.y0)
1228     pos = top.from, y -= top.ascent;
1229   else
1230     pos = cur.from, y -= cur.y0 + cur.ascent;
1231   pos = COORDINATES_POSITION (pos, nchars + 1, x, y);
1232
1233   if (pos == cursor.from)
1234     return;
1235
1236   hide_cursor ();
1237   if (SELECTEDP ())
1238     {
1239       /* Selection range changed.  */
1240       int from = mtext_property_start (selection);
1241       int to = mtext_property_end (selection);
1242       int start_y0 = sel_start.y0, start_y1 = sel_start.y1;
1243       int end_y0 = sel_end.y0, end_y1 = sel_end.y1;
1244
1245       if (cursor.from == from)
1246         {
1247           /* Starting position changed.  */
1248           if (pos <= from)
1249             {
1250               /* Enlarged.  We can simply overdraw.  */
1251               select_region (pos, to);
1252               redraw (sel_start.y0, start_y1, 0, 0);
1253             }
1254           else if (pos < to)
1255             {
1256               /* Shrunken.  Previous selection face must be cleared.  */
1257               select_region (pos, to);
1258               redraw (start_y0, sel_start.y1, 1, 0);
1259             }
1260           else if (pos == to)
1261             {
1262               /* Shrunken to zero.  */
1263               XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1264               mtext_detach_property (selection);
1265               redraw (start_y0, end_y1, 1, 0);
1266             }
1267           else
1268             {
1269               /* Full update is necessary.  */
1270               select_region (to, pos);
1271               redraw (start_y0, sel_end.y1, 1, 0);
1272             }
1273         }
1274       else
1275         {
1276           /* Ending position changed.  */
1277           if (pos < from)
1278             {
1279               /* Full update is necessary.  */
1280               select_region (pos, from);
1281               redraw (sel_start.y0, end_y1, 1, 0);
1282             }
1283           else if (pos == from)
1284             {
1285               /* Shrunken to zero.  */
1286               XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1287               mtext_detach_property (selection);
1288               redraw (start_y0, end_y1, 1, 0);
1289             }
1290           else if (pos < to)
1291             {
1292               /* Shrunken.  Previous selection face must be cleared.  */
1293               select_region (from, pos);
1294               redraw (sel_end.y0, end_y1, 1, 0);
1295             }
1296           else
1297             {
1298               /* Enlarged.  We can simply overdraw.  */
1299               select_region (from, pos);
1300               redraw (end_y0, sel_end.y1, 0, 0);
1301             }
1302         }
1303     }
1304   else
1305     {
1306       /* Newly selected.  */
1307       select_region (pos, cursor.from);
1308       redraw (sel_start.y0, sel_end.y1, 0, 0);
1309     }
1310   update_cursor (pos, 1);
1311 }
1312
1313 void
1314 ScrollProc (Widget w, XtPointer client_data, XtPointer position)
1315 {
1316   int from;
1317   MDrawGlyphInfo info;
1318   int height;
1319   int cursor_pos = cursor.from;
1320
1321   if (((int) position) < 0)
1322     {
1323       /* Scroll down.  */
1324       int pos;
1325
1326       from = top.from;
1327       height = top.y1 - top.y0;
1328       while (from > 0)
1329         {
1330           pos = bol (from - 1, 0);
1331           GLYPH_INFO (pos, from - 1, info);
1332           if (height + info.this.height > win_height)
1333             break;
1334           height += info.this.height;
1335           from = info.line_from;
1336         }
1337       if (cursor_pos >= top.to)
1338         {
1339           cursor_pos = top.from;
1340           pos = top.to;
1341           while (cursor_pos < nchars)
1342             {
1343               GLYPH_INFO (pos, pos, info);
1344               if (height + info.this.height > win_height)
1345                 break;
1346               height += info.this.height;
1347               cursor_pos = pos;
1348               pos = info.line_to;
1349             }
1350         }
1351     }
1352   else if (cur.to < nchars)
1353     {
1354       /* Scroll up, but leave at least one line.  */
1355       from = cur.to;
1356       height = cur.y1;
1357       while (from < nchars)
1358         {
1359           GLYPH_INFO (from, from, info);
1360           if (height + info.this.height > win_height
1361               || info.line_to >= nchars)
1362             break;
1363           height += info.this.height;
1364           from = info.line_to;
1365         }
1366       if (from == nchars)
1367         from = info.line_from;
1368       if (cursor_pos < from)
1369         cursor_pos = from;
1370     }
1371   else
1372     /* Scroll up to make the cursor line top.  */
1373     from = cur.from;
1374   hide_cursor ();
1375   reseat (from);
1376   update_cursor (cursor_pos, 1);
1377 }
1378
1379 void
1380 JumpProc (Widget w, XtPointer client_data, XtPointer persent_ptr)
1381 {
1382   float persent = *(float *) persent_ptr;
1383   int pos1, pos2 = nchars * persent;
1384   MDrawGlyphInfo info;
1385
1386   hide_cursor ();
1387   pos1 = bol (pos2, 0);
1388   GLYPH_INFO (pos1, pos2, info);
1389   pos1 = info.line_from;
1390   reseat (pos1);
1391   update_cursor (pos1, 1);
1392 }
1393
1394
1395 static void
1396 KeyProc (Widget w, XEvent *event, String *str, Cardinal *num)
1397 {
1398   XKeyEvent *key_event = (XKeyEvent *) event;
1399   char buf[512];
1400   KeySym keysym = NoSymbol;
1401   int ret;
1402   /* If set to 1, do not update target_x_position.  */
1403   int keep_target_x_position = 0;
1404   MText *produced;
1405
1406   if (current_input_context
1407       && minput_filter (current_input_context, Mnil, event))
1408     return;
1409   if (event->type == KeyRelease)
1410     return;
1411
1412   hide_cursor ();
1413
1414   produced = mtext ();
1415   ret = minput_lookup (current_input_context, Mnil, event, produced);
1416   if (mtext_len (produced) > 0)
1417     insert_chars (produced);
1418   if (ret)
1419     ret = XLookupString (key_event, buf, sizeof (buf), &keysym, NULL);
1420   m17n_object_unref (produced);
1421
1422   switch (keysym)
1423     {
1424     case XK_Delete:
1425       {
1426         int n = 0;
1427
1428         if (SELECTEDP ())
1429           {
1430             n = (mtext_property_end (selection)
1431                  - mtext_property_start (selection));
1432             mtext_detach_property (selection);
1433           }
1434         else if (cursor.from < nchars)
1435           {
1436             /* Delete the following grapheme cluster.  */
1437             n = cursor.to - cursor.from;
1438           }
1439         if (n != 0)
1440           delete_char (n);
1441       }
1442       break;
1443
1444     case XK_BackSpace:
1445       {
1446         int n = 0;
1447
1448         if (SELECTEDP ())
1449           {
1450             /* Delete selected region.  */
1451             n = (mtext_property_end (selection)
1452                  - mtext_property_start (selection));
1453             mtext_detach_property (selection);
1454           }
1455         else if (cursor.from > 0)
1456           {
1457             /* Delete the preceding character.  */
1458             n = -1;
1459           }
1460         if (n != 0)
1461           delete_char (n);
1462       }
1463       break;
1464
1465     case XK_Left:
1466       if (SELECTEDP ())
1467         {
1468           mtext_detach_property (selection);
1469           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1470         }
1471       if (logical_move)
1472         {
1473           if (cursor.prev_from >= 0)
1474             update_cursor (cursor.prev_from, 0);
1475         }
1476       else
1477         {
1478           if (cursor.left_from >= 0)
1479             update_cursor (cursor.left_from, 0);
1480         }
1481       break;
1482
1483     case XK_Right:
1484       if (SELECTEDP ())
1485         {
1486           mtext_detach_property (selection);
1487           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1488         }
1489       if (logical_move)
1490         {
1491           if (cursor.next_to >= 0)
1492             update_cursor (cursor.to, 0);
1493         }
1494       else
1495         {
1496           if (cursor.right_from >= 0)
1497             update_cursor (cursor.right_from, 0);
1498         }
1499       break;
1500
1501     case XK_Down:
1502       if (SELECTEDP ())
1503         {
1504           mtext_detach_property (selection);
1505           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1506         }
1507       if (cur.to <= nchars)
1508         {
1509           MDrawGlyphInfo info;
1510           int pos;
1511
1512           GLYPH_INFO (cur.from, cur.to, info);
1513           pos = COORDINATES_POSITION (cur.from, nchars + 1,
1514                                       target_x_position, info.y);
1515           keep_target_x_position = 1;
1516           update_cursor (pos, 0);
1517         }
1518       break;
1519
1520     case XK_Up:
1521       if (SELECTEDP ())
1522         {
1523           mtext_detach_property (selection);
1524           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1525         }
1526       if (cur.from > 0)
1527         {
1528           MDrawMetric rect;
1529           int y;
1530           int pos = bol (cur.from - 1, 0);
1531
1532           TEXT_EXTENTS (pos, cur.from - 1, rect);
1533           y = rect.height + rect.y - 1;
1534           pos = COORDINATES_POSITION (pos, nchars,
1535                                       target_x_position, y);
1536           keep_target_x_position = 1;
1537           update_cursor (pos, 0);
1538         }
1539       break;
1540
1541     case XK_Page_Down:
1542       if (SELECTEDP ())
1543         {
1544           mtext_detach_property (selection);
1545           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1546         }
1547       if (top.from < nchars)
1548         ScrollProc (w, NULL, (XtPointer) 1);
1549       break;
1550
1551     case XK_Page_Up:
1552       if (SELECTEDP ())
1553         {
1554           mtext_detach_property (selection);
1555           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1556         }
1557       if (top.from > 0)
1558         ScrollProc (w, NULL, (XtPointer) -1);
1559       break;
1560
1561     default:
1562       if (ret > 0)
1563         {
1564           if (buf[0] == 17) /* C-q */
1565             {
1566               XtAppSetExitFlag (context);
1567               return;
1568             }
1569           else if (buf[0] == 12) /* C-l */
1570             {
1571               redraw (0, win_height, 1, 1);
1572               return;
1573             }
1574           else
1575             {
1576               MText *temp = mtext ();
1577
1578               mtext_cat_char (temp, buf[0] == '\r' ? '\n' : buf[0]);
1579               if (current_input_context)
1580                 mtext_put_prop (temp, 0, 1, Mlanguage,
1581                                 current_input_context->im->language);
1582               insert_chars (temp);
1583               m17n_object_unref (temp);
1584             }
1585         }
1586     }
1587
1588   if (! keep_target_x_position)
1589     target_x_position = cursor.x;
1590 }
1591
1592 void
1593 SaveProc (Widget w, XtPointer client_data, XtPointer call_data)
1594 {
1595   char *name = (char *) client_data;
1596   FILE *fp;
1597   int from = -1, to = 0;
1598   
1599   if (name)
1600     {
1601       free (filename);
1602       filename = strdup (name);
1603     }
1604
1605   fp = fopen (filename, "w");
1606   if (! fp)
1607     {
1608       fprintf (stderr, "Open for write fail: %s", filename);
1609       return;
1610     }
1611
1612   if (SELECTEDP ())
1613     {
1614       from = mtext_property_start (selection);
1615       to = mtext_property_end (selection);
1616       mtext_detach_property (selection);
1617     }
1618
1619   mconv_encode_stream (Mcoding_utf_8, mt, fp);
1620   fclose (fp);
1621   if (from >= 0)
1622     select_region (from, to);
1623 }
1624
1625 void
1626 SerializeProc (Widget w, XtPointer client_data, XtPointer call_data)
1627 {
1628   MText *new;
1629
1630   hide_cursor ();
1631   if (SELECTEDP ())
1632     mtext_detach_property (selection);
1633   serialized = (int) client_data;
1634   if (! serialized)
1635     new = mtext_deserialize (mt);
1636   else
1637     {
1638       MPlist *plist = mplist ();
1639
1640       mplist_push (plist, Mt, Mface);
1641       mplist_push (plist, Mt, Mlanguage);
1642       new = mtext_serialize (mt, 0, mtext_len (mt), plist);
1643       m17n_object_unref (plist);
1644     }
1645   if (new)
1646     {
1647       m17n_object_unref (mt);
1648       mt = new;
1649       serialized = ! serialized;
1650       nchars = mtext_len (mt);
1651       update_top (0);
1652     }
1653   update_cursor (0, 1);
1654   redraw (0, win_height, 1, 1);
1655 }
1656
1657 void
1658 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
1659 {
1660   XtAppSetExitFlag (context);
1661 }
1662
1663 MText *
1664 read_file ()
1665 {
1666   FILE *fp = fopen (filename, "r");
1667
1668   if (! fp)
1669     FATAL_ERROR ("Can't read \"%s\"!\n", filename);
1670   mt = mconv_decode_stream (Mcoding_utf_8, fp);
1671   fclose (fp);
1672   if (! mt)
1673     FATAL_ERROR ("Can't decode \"%s\" by UTF-8!\n", filename);
1674   return mt;
1675 }
1676
1677 void
1678 BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
1679 {
1680   int data = (int) client_data;
1681   int i;
1682
1683   if (data == 0)
1684     {
1685       control.enable_bidi = 0;
1686       control.orientation_reversed = 0;
1687     }
1688   else
1689     {
1690       control.enable_bidi = 1;
1691       control.orientation_reversed = data == 2;
1692     }
1693   for (i = 0; i < 3; i++)
1694     {
1695       if (i == data)
1696         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1697       else
1698         XtSetArg (arg[0], XtNleftBitmap, None);
1699       XtSetValues (BidiMenus[i], arg, 1);
1700     }
1701
1702   update_cursor (cursor.from, 1);
1703   redraw (0, win_height, 1, 0);
1704 }
1705
1706 extern int line_break (MText *mt, int pos, int from, int to, int line, int y);
1707
1708 void
1709 LineBreakProc (Widget w, XtPointer client_data, XtPointer call_data)
1710 {
1711   int data = (int) client_data;
1712   int i;
1713
1714   if (data == 0)
1715     control.max_line_width = 0;
1716   else
1717     {
1718       control.max_line_width = win_width;
1719       control.line_break = (data == 1 ? NULL : line_break);
1720     }
1721   for (i = 0; i < 3; i++)
1722     {
1723       if (i == data)
1724         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1725       else
1726         XtSetArg (arg[0], XtNleftBitmap, None);
1727       XtSetValues (LineBreakMenus[i], arg, 1);
1728     }
1729
1730   update_cursor (cursor.from, 1);
1731   redraw (0, win_height, 1, 0);
1732 }
1733
1734 void
1735 CursorProc (Widget w, XtPointer client_data, XtPointer call_data)
1736 {
1737   int data = (int) client_data;
1738   int i, from, to;
1739
1740   switch (data)
1741     {
1742     case 0:
1743       logical_move = 1;
1744       from = 0, to = 2;
1745       break;
1746     case 1:
1747       logical_move = 0;
1748       from = 0, to = 2;
1749       break;
1750     case 2:
1751       control.cursor_bidi = 0, control.cursor_width = -1;
1752       from = 2, to = 5;
1753       break;
1754     case 3:
1755       control.cursor_bidi = 0, control.cursor_width = 2;
1756       from = 2, to = 5;
1757       break;
1758     default:
1759       control.cursor_bidi = 1;
1760       from = 2, to = 5;
1761       break;
1762     }
1763
1764   for (i = from; i < to; i++)
1765     {
1766       if (i == data)
1767         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1768       else
1769         XtSetArg (arg[0], XtNleftBitmap, None);
1770       XtSetValues (CursorMenus[i], arg, 1);
1771     }
1772
1773   update_cursor (cursor.from, 0);
1774   redraw (0, win_height, 1, 0);
1775 }
1776
1777 static void
1778 InputMethodProc (Widget w, XtPointer client_data, XtPointer call_data)
1779 {
1780   int idx = (int) client_data;
1781
1782   if (idx == -2 ? current_input_method < 0
1783       : idx == -1 ? auto_input_method
1784       : idx == current_input_method)
1785     return;
1786
1787   XtSetArg (arg[0], XtNleftBitmap, None);
1788   if (auto_input_method)
1789     {
1790       XtSetValues (InputMethodMenus[1], arg, 1);
1791       auto_input_method = 0;
1792     }
1793   else if (current_input_method < 0)
1794     XtSetValues (InputMethodMenus[0], arg, 1);
1795   else
1796     XtSetValues (InputMethodMenus[current_input_method + 2], arg, 1);
1797
1798   if (idx == -1)
1799     {
1800       auto_input_method = 1;
1801       hide_cursor ();
1802     }
1803   else if (input_method_table[idx].available >= 0)
1804     {
1805       if (! input_method_table[idx].im)
1806         {
1807           input_method_table[idx].im = 
1808             minput_open_im (input_method_table[idx].language,
1809                             input_method_table[idx].name, NULL);
1810           if (! input_method_table[idx].im)
1811             input_method_table[idx].available = -1;
1812         }
1813       if (input_method_table[idx].im)
1814         select_input_method (idx);
1815     }
1816   XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1817   XtSetValues (InputMethodMenus[idx + 2], arg, 1);
1818 }
1819
1820 MPlist *default_face_list;
1821
1822 void
1823 FaceProc (Widget w, XtPointer client_data, XtPointer call_data)
1824 {
1825   int idx = (int) client_data;
1826   int from, to;
1827   int old_y1;
1828
1829   if (! SELECTEDP ())
1830     {
1831       MPlist *plist;
1832
1833       if (idx >= 0)
1834         {
1835           MFace *face = mframe_get_prop (frame, Mface);
1836
1837           for (plist = default_face_list; mplist_key (plist) != Mnil;
1838                plist = mplist_next (plist)) 
1839             mface_merge (face, mplist_value (plist));
1840           mplist_add (plist, Mt, *face_table[idx].face);
1841           mface_merge (face, *face_table[idx].face);
1842         }
1843       else if (mplist_key (mplist_next (default_face_list)) != Mnil)
1844         {
1845           MFace *face = mframe_get_prop (frame, Mface);
1846
1847           for (plist = default_face_list;
1848                mplist_key (mplist_next (plist)) != Mnil;
1849                plist = mplist_next (plist)) 
1850             mface_merge (face, mplist_value (plist));
1851           mplist_pop (plist);
1852         }
1853       update_top (0);
1854       update_cursor (0, 1);
1855       redraw (0, win_height, 1, 1);
1856       show_cursor (NULL);
1857       return;
1858     }
1859
1860   XtAppAddWorkProc (context, show_cursor, NULL);
1861   from = mtext_property_start (selection);
1862   to = mtext_property_end (selection);
1863   old_y1 = sel_end.y1;
1864
1865   mtext_detach_property (selection);
1866   if (idx >= 0)
1867     {
1868       MTextProperty *prop = mtext_property (Mface, *face_table[idx].face,
1869                                             MTEXTPROP_REAR_STICKY);
1870       mtext_push_property (mt, from, to, prop);
1871       m17n_object_unref (prop);
1872     }
1873   else
1874     mtext_pop_prop (mt, from, to, Mface);
1875   if (from < top.to)
1876     update_top (top.from);
1877   update_cursor (cursor.from, 1);
1878   select_region (from, to);
1879   update_region (sel_start.y0, old_y1, sel_end.y1);
1880   if (cur.y1 > win_height)
1881     {
1882       while (cur.y1 > win_height)
1883         {
1884           reseat (top.to);
1885           update_cursor (cursor.from, 1);
1886         }
1887     }
1888 }
1889
1890 void
1891 LangProc (Widget w, XtPointer client_data, XtPointer call_data)
1892 {
1893   MSymbol sym = (MSymbol) client_data;
1894   int from, to;
1895   int old_y1;
1896
1897   if (! SELECTEDP ())
1898     return;
1899
1900   XtAppAddWorkProc (context, show_cursor, NULL);
1901   from = mtext_property_start (selection);
1902   to = mtext_property_end (selection);
1903   old_y1 = sel_end.y1;
1904
1905   mtext_detach_property (selection);
1906   if (sym != Mnil)
1907     mtext_put_prop (mt, from, to, Mlanguage, sym);
1908   else
1909     mtext_pop_prop (mt, from, to, Mlanguage);
1910
1911   if (from < top.to)
1912     update_top (top.from);
1913   update_cursor (cursor.from, 1);
1914   select_region (from, to);
1915   update_region (sel_start.y0, old_y1, sel_end.y1);
1916   if (cur.y1 > win_height)
1917     {
1918       while (cur.y1 > win_height)
1919         {
1920           reseat (top.to);
1921           update_cursor (cursor.from, 1);
1922         }
1923     }
1924 }
1925
1926 void
1927 DumpImageProc (Widget w, XtPointer client_data, XtPointer call_data)
1928 {
1929   int narrowed = (int) client_data;
1930   FILE *mdump;
1931   int from, to;
1932   MConverter *converter;
1933
1934   if (narrowed)
1935     {
1936       if (! SELECTEDP ())
1937         return;
1938       from = mtext_property_start (selection);
1939       to = mtext_property_end (selection);
1940     }
1941   else
1942     {
1943       from = 0;
1944       to = nchars;
1945     }
1946
1947   if (! narrowed)
1948     mdump = popen ("mdump -q -p a4", "w");
1949   else
1950     mdump = popen ("mdump -q", "w");
1951   if (! mdump)
1952     return;
1953   converter = mconv_stream_converter (Mcoding_utf_8, mdump);
1954   mconv_encode_range (converter, mt, from, to);
1955   mconv_free_converter (converter);
1956   fclose (mdump);
1957 }
1958
1959 void
1960 input_status (MInputContext *ic, MSymbol command)
1961 {
1962   XFillRectangle (display, input_status_pixmap, gc_inv,
1963                   0, 0, input_status_width, input_status_height);
1964   if (command == Minput_status_draw)
1965     {
1966       MDrawMetric rect;
1967
1968       mtext_put_prop (ic->status, 0, mtext_len (ic->status),
1969                       Mface, face_input_status);
1970       if (ic->im->language != Mnil)
1971         mtext_put_prop (ic->status, 0, mtext_len (ic->status),
1972                         Mlanguage, ic->im->language);
1973       mdraw_text_extents (frame, ic->status, 0, mtext_len (ic->status),
1974                           &input_status_control, NULL, NULL, &rect);
1975       mdraw_text_with_control (frame, (MDrawWindow) input_status_pixmap,
1976                                input_status_width - rect.width - 2, - rect.y,
1977                                ic->status, 0, mtext_len (ic->status),
1978                                &input_status_control);
1979     }
1980   XtSetArg (arg[0], XtNbitmap, input_status_pixmap);
1981   XtSetValues (CurIMStatus, arg, 1);
1982 }
1983
1984 int
1985 compare_input_method (const void *elt1, const void *elt2)
1986 {
1987   const InputMethodInfo *im1 = elt1;
1988   const InputMethodInfo *im2 = elt2;
1989   MSymbol lang1, lang2;
1990
1991   if (im1->language == Mnil)
1992     return 1;
1993   if (im1->language == im2->language)
1994     return strcmp (msymbol_name (im1->name), msymbol_name (im2->name));
1995   if (im1->language == Mt)
1996     return 1;
1997   if (im2->language == Mt)
1998     return -1;
1999   lang1 = msymbol_get (im1->language, Mlanguage);
2000   lang2 = msymbol_get (im2->language, Mlanguage);
2001   return strcmp (msymbol_name (lang1), msymbol_name (lang2));
2002 }
2003
2004 void
2005 setup_input_methods (int with_xim)
2006 {
2007   MInputMethod *im = NULL;
2008   MPlist *plist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
2009   MPlist *pl;
2010   int i = 0;
2011
2012   num_input_methods = mplist_length (plist);
2013
2014   if (with_xim)
2015     {
2016       MInputXIMArgIM arg_xim;
2017
2018       arg_xim.display = display;
2019       arg_xim.db = NULL;  
2020       arg_xim.res_name = arg_xim.res_class = NULL;
2021       arg_xim.locale = NULL;
2022       arg_xim.modifier_list = NULL;
2023       im = minput_open_im (Mnil, msymbol ("xim"), &arg_xim);
2024       if (im)
2025         num_input_methods++;
2026     }
2027   input_method_table = calloc (num_input_methods, sizeof (InputMethodInfo));
2028   if (im)
2029     {
2030       input_method_table[i].available = 1;
2031       input_method_table[i].language = Mnil;
2032       input_method_table[i].name = im->name;
2033       input_method_table[i].im = im;
2034       i++;
2035     }
2036
2037   for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl))
2038     {
2039       MDatabase *mdb = mplist_value (pl);
2040       MSymbol *tag = mdatabase_tag (mdb);
2041
2042       if (tag[1] != Mnil)
2043         {
2044           input_method_table[i].language = tag[1];
2045           input_method_table[i].name = tag[2];
2046           i++;
2047         }
2048     }
2049
2050   m17n_object_unref (plist);
2051   num_input_methods = i;
2052   qsort (input_method_table, num_input_methods, sizeof input_method_table[0],
2053          compare_input_method);
2054   current_input_context = NULL;
2055
2056   mplist_put (minput_driver->callback_list, Minput_status_start,
2057               (void *) input_status);
2058   mplist_put (minput_driver->callback_list, Minput_status_draw,
2059               (void *) input_status);
2060   mplist_put (minput_driver->callback_list, Minput_status_done,
2061               (void *) input_status);
2062 }
2063
2064
2065 static void
2066 MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num)
2067 {
2068   char *msg;
2069
2070   if (num && *num > 0)
2071     {
2072       int bytes = 0, i;
2073
2074       for (i = 0; i < *num; i++)
2075         bytes += strlen (str[i]) + 1;
2076       msg = alloca (bytes);
2077       strcpy (msg, str[0]);
2078       for (i = 1; i < *num; i++)
2079         strcat (msg, " "), strcat (msg, str[i]);
2080     }
2081   else if (cursor.from < nchars)
2082     {
2083       int c = mtext_ref_char (mt, cursor.from);
2084       char *name = mchar_get_prop (c, Mname);
2085
2086       if (! name)
2087         name = "";
2088       msg = alloca (10 + strlen (name));
2089       sprintf (msg, "U+%04X %s", c, name);
2090     }
2091   else
2092     {
2093       msg = "";
2094     }
2095   XtSetArg (arg[0], XtNlabel, msg);
2096   XtSetValues (MessageWidget, arg, 1);
2097 }
2098
2099 typedef struct
2100 {
2101   int type;
2102   char *name1, *name2;
2103   XtCallbackProc proc;
2104   XtPointer client_data;
2105   int status;
2106   Widget w;
2107 } MenuRec;
2108
2109 void PopupProc (Widget w, XtPointer client_data, XtPointer call_data);
2110
2111 void SaveProc (Widget w, XtPointer client_data, XtPointer call_data);
2112
2113 MenuRec FileMenu[] =
2114   { { 0, "Open", NULL, PopupProc, FileMenu + 0, -1 },
2115     { 0, "Save", NULL, SaveProc, NULL, -1 },
2116     { 0, "Save as", NULL, PopupProc, FileMenu + 2, -1 },
2117     { 1 },
2118     { 0, "Serialize", NULL, SerializeProc, (void *) 1, -1 },
2119     { 0, "Deserialize", NULL, SerializeProc, (void *) 0, -1 },
2120     { 1 },
2121     { 0, "Dump Image Buffer", NULL, DumpImageProc, (void *) 0, -1 },
2122     { 0, "Dump Image Region", NULL, DumpImageProc, (void *) 1, -1 },
2123     { 1 },
2124     { 0, "Quit", NULL, QuitProc, NULL, -1 } };
2125
2126 void
2127 PopupProc (Widget w, XtPointer client_data, XtPointer call_data)
2128 {
2129   MenuRec *rec = (MenuRec *) client_data;
2130   Position x, y;
2131
2132   XtSetArg (arg[0], XtNvalue, "");
2133   XtSetArg (arg[1], XtNlabel, rec->name1);
2134   XtSetValues (FileDialogWidget, arg, 2);
2135   XtTranslateCoords (w, (Position) 0, (Position) 0, &x, &y);
2136   XtSetArg (arg[0], XtNx, x + 20);
2137   XtSetArg (arg[1], XtNy, y + 10);
2138   XtSetValues (FileShellWidget, arg, 2);
2139   XtPopup (FileShellWidget, XtGrabExclusive);
2140 }
2141
2142 void
2143 FileDialogProc (Widget w, XtPointer client_data, XtPointer call_data)
2144 {
2145   FILE *fp;
2146   char *label;
2147
2148   XtPopdown (FileShellWidget);
2149   if ((int) client_data == 1)
2150     return;
2151   XtSetArg (arg[0], XtNlabel, &label);
2152   XtGetValues (FileDialogWidget, arg, 1);
2153   if (strcmp (label, FileMenu[0].name1) == 0)
2154     {
2155       /* Open a file */
2156       free (filename);
2157       filename = strdup ((char *) XawDialogGetValueString (FileDialogWidget));
2158       fp = fopen (filename, "r");
2159       hide_cursor ();
2160       m17n_object_unref (mt);
2161       if (fp)
2162         {
2163           mt = mconv_decode_stream (Mcoding_utf_8, fp);
2164           fclose (fp);
2165           if (! mt)
2166             mt = mtext ();
2167         }
2168       else
2169         mt = mtext ();
2170       serialized = 0;
2171       nchars = mtext_len (mt);
2172       update_top (0);
2173       update_cursor (0, 1);
2174       redraw (0, win_height, 1, 1);
2175     }
2176   else if (strcmp (label, FileMenu[2].name1) == 0)
2177     SaveProc (w, (XtPointer) XawDialogGetValueString (FileDialogWidget), NULL);
2178   else
2179     fprintf (stderr, "Invalid calling sequence: FileDialogProc\n");
2180 }
2181
2182 #define SetMenu(MENU, TYPE, NAME1, NAME2, PROC, DATA, STATUS)            \
2183   ((MENU).type = (TYPE), (MENU).name1 = (NAME1), (MENU).name2 = (NAME2), \
2184    (MENU).proc = (PROC), (MENU).client_data = (XtPointer) (DATA),        \
2185    (MENU).status = (STATUS))
2186
2187
2188 Widget
2189 create_menu_button (Widget top, Widget parent, Widget left, char *button_name,
2190                     char *menu_name, MenuRec *menus, int num_menus, char *help)
2191 {
2192   Widget button, menu;
2193   char *fmt = "<EnterWindow>: highlight() MenuHelp(%s)\n\
2194                <LeaveWindow>: reset() MenuHelp()\n\
2195                <BtnDown>: reset() PopupMenu()\n\
2196                <BtnUp>: highlight()"; 
2197   int i;
2198   MenuRec *m;
2199   char *trans;
2200   int max_width = 0;
2201
2202   menu = XtCreatePopupShell (menu_name, simpleMenuWidgetClass, top, NULL, 0);
2203   for (i = 0; i < num_menus; i++)
2204     {
2205       m = menus + i;
2206       if (m->type == 0)
2207         {
2208           if (m->proc)
2209             {
2210               int n = 0;
2211
2212               if (m->status >= 0)
2213                 {
2214                   XtSetArg (arg[n], XtNleftMargin, 20), n++;
2215                   if (m->status > 0)
2216                     XtSetArg (arg[n], XtNleftBitmap, CheckPixmap), n++;
2217                 }
2218               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2219                                             menu, arg, n);
2220               XtAddCallback (m->w, XtNcallback, m->proc, m->client_data);
2221             }
2222           else
2223             {
2224               XtSetArg (arg[0], XtNsensitive, False);
2225               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2226                                             menu, arg, 2);
2227             }
2228         }
2229       else
2230         {
2231           XtCreateManagedWidget (m->name1, smeLineObjectClass, menu, NULL, 0);
2232         }
2233       if (m->name2)
2234         max_width = 1;
2235     }
2236   trans = alloca (strlen (fmt) + strlen (help));
2237   sprintf (trans, fmt, help);
2238   XtSetArg (arg[0], XtNmenuName, menu_name);
2239   XtSetArg (arg[1], XtNtranslations, XtParseTranslationTable ((String) trans));
2240   XtSetArg (arg[2], XtNinternalWidth, 2);
2241   XtSetArg (arg[3], XtNhighlightThickness, 1);
2242   XtSetArg (arg[4], XtNleft, XawChainLeft);
2243   XtSetArg (arg[5], XtNright, XawChainLeft);
2244   i = 6;
2245   if (left)
2246     XtSetArg (arg[i], XtNfromHoriz, left), i++;
2247   button = XtCreateManagedWidget (button_name, menuButtonWidgetClass, parent,
2248                                   arg, i);
2249
2250   if (max_width)
2251     {
2252       int height, ascent, *width = alloca (sizeof (int) * num_menus);
2253       int *len = alloca (sizeof (int) * num_menus);
2254
2255       XFontSet font_set;
2256       XFontSetExtents *fontset_extents;
2257
2258       XtSetArg (arg[0], XtNfontSet, &font_set);
2259       XtGetValues (button, arg, 1);
2260
2261       fontset_extents = XExtentsOfFontSet (font_set);
2262       height = fontset_extents->max_logical_extent.height;
2263       ascent = - fontset_extents->max_logical_extent.y;
2264
2265       for (i = 0; i < num_menus; i++)
2266         if (menus[i].name2)
2267           {
2268             len[i] = strlen (menus[i].name2);
2269             width[i] = XmbTextEscapement (font_set, menus[i].name2, len[i]);
2270             if (max_width < width[i])
2271               max_width = width[i];
2272           }
2273       for (i = 0; i < num_menus; i++)
2274         if (menus[i].name2)
2275           {
2276             Pixmap pixmap = XCreatePixmap (display,
2277                                            RootWindow (display, screen),
2278                                            max_width, height, 1);
2279             XFillRectangle (display, pixmap, mono_gc_inv,
2280                             0, 0, max_width, height);
2281             XmbDrawString (display, pixmap, font_set, mono_gc,
2282                            max_width - width[i], ascent,
2283                            menus[i].name2, len[i]);
2284             XtSetArg (arg[0], XtNrightBitmap, pixmap);
2285             XtSetArg (arg[1], XtNrightMargin, max_width + 20);
2286             XtSetValues (menus[i].w, arg, 2);
2287           }
2288     }
2289
2290   return button;
2291 }
2292
2293
2294 XtActionsRec actions[] = {
2295   {"Expose", ExposeProc},
2296   {"Configure", ConfigureProc},
2297   {"Key", KeyProc},
2298   {"ButtonPress", ButtonProc},
2299   {"ButtonRelease", ButtonReleaseProc},
2300   {"ButtonMotion", ButtonMoveProc},
2301   {"Button2Press", Button2Proc},
2302   {"MenuHelp", MenuHelpProc}
2303 };
2304
2305
2306 /* Print the usage of this program (the name is PROG), and exit with
2307    EXIT_CODE.  */
2308
2309 void
2310 help_exit (char *prog, int exit_code)
2311 {
2312   char *p = prog;
2313
2314   while (*p)
2315     if (*p++ == '/')
2316       prog = p;
2317
2318   printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] FILE\n", prog);
2319   printf ("Display FILE on a window and allow users to edit it.\n");
2320   printf ("XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
2321   printf ("The following OPTIONs are available.\n");
2322   printf ("  %-13s %s", "--version", "print version number\n");
2323   printf ("  %-13s %s", "-h, --help", "print this message\n");
2324   exit (exit_code);
2325 }
2326
2327 int
2328 main (int argc, char **argv)
2329 {
2330   Widget form, BodyWidget, w;
2331   char *fontset_name = NULL;
2332   int col = 80, row = 32;
2333   /* Translation table for TextWidget.  */
2334   String trans = "<Expose>: Expose()\n\
2335                   <Configure>: Configure()\n\
2336                   <Key>: Key()\n\
2337                   <KeyUp>: Key()\n\
2338                   <Btn1Down>: ButtonPress()\n\
2339                   <Btn1Up>: ButtonRelease()\n\
2340                   <Btn1Motion>: ButtonMotion()\n\
2341                   <Btn2Down>: Button2Press()";
2342   /* Translation table for the top form widget.  */
2343   String trans2 = "<Key>: Key()\n\
2344                    <KeyUp>: Key()";
2345   String pop_face_trans
2346     = "<EnterWindow>: MenuHelp(Pop face property) highlight()\n\
2347        <LeaveWindow>: MenuHelp() reset()\n\
2348        <Btn1Down>: set()\n\
2349        <Btn1Up>: notify() unset()"; 
2350   String pop_lang_trans
2351     = "<EnterWindow>: MenuHelp(Pop language property) highlight()\n\
2352        <LeaveWindow>: MenuHelp() reset()\n\
2353        <Btn1Down>: set()\n\
2354        <Btn1Up>: notify() unset()"; 
2355   int font_width, font_ascent, font_descent;
2356   int with_xim = 0;
2357   int i, j;
2358
2359   setlocale (LC_ALL, "");
2360   /* Create the top shell.  */
2361   XtSetLanguageProc (NULL, NULL, NULL);
2362   ShellWidget = XtOpenApplication (&context, "MEdit", NULL, 0, &argc, argv,
2363                                    NULL, sessionShellWidgetClass, NULL, 0);
2364   display = XtDisplay (ShellWidget);
2365   screen = XScreenNumberOfScreen (XtScreen (ShellWidget));
2366
2367   /* Parse the remaining command line arguments.  */
2368   for (i = 1; i < argc; i++)
2369     {
2370       if (! strcmp (argv[i], "--help")
2371           || ! strcmp (argv[i], "-h"))
2372         help_exit (argv[0], 0);
2373       else if (! strcmp (argv[i], "--version"))
2374         {
2375           printf ("medit (m17n library) %s\n", VERSION);
2376           printf ("Copyright (C) 2003 AIST, JAPAN\n");
2377           exit (0);
2378         }
2379       else if (! strcmp (argv[i], "--geometry"))
2380         {
2381           i++;
2382           if (sscanf (argv[i], "%dx%d", &col, &row) != 2)
2383             help_exit (argv[0], 1);
2384         }
2385       else if (! strcmp (argv[i], "--fontset"))
2386         {
2387           i++;
2388           fontset_name = strdup (argv[i]);
2389         }
2390       else if (! strcmp (argv[i], "--with-xim"))
2391         {
2392           with_xim = 1;
2393         }
2394       else if (argv[i][0] != '-')
2395         {
2396           filename = strdup (argv[i]);
2397         }
2398       else
2399         {
2400           fprintf (stderr, "Unknown option: %s", argv[i]);
2401           help_exit (argv[0], 1);
2402         }
2403     }
2404   if (! filename)
2405     help_exit (argv[0], 1);
2406
2407   mdatabase_dir = ".";
2408   /* Initialize the m17n library.  */
2409   M17N_INIT ();
2410   if (merror_code != MERROR_NONE)
2411     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library!");
2412
2413   mt = read_file (filename);
2414   serialized = 0;
2415
2416   nchars = mtext_len (mt);
2417
2418   {
2419     MFace *face = mface ();
2420
2421     mface_put_prop (face, Mforeground, msymbol ("blue"));
2422     mface_put_prop (face, Mbackground, msymbol ("yellow"));
2423     mface_put_prop (face, Mvideomode, Mreverse);
2424     selection = mtext_property (Mface, face, MTEXTPROP_NO_MERGE);
2425     m17n_object_unref (face);
2426   }
2427
2428   /* This tells ExposeProc to initialize everything.  */
2429   top.from = -1;
2430   
2431   XA_TEXT = XInternAtom (display, "TEXT", False);
2432   XA_COMPOUND_TEXT = XInternAtom (display, "COMPOUND_TEXT", False);
2433   XA_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
2434   Mcoding_compound_text = mconv_resolve_coding (msymbol ("compound-text"));
2435   if (Mcoding_compound_text == Mnil)
2436     FATAL_ERROR ("%s\n", "Don't know about COMPOUND-TEXT encoding!");
2437
2438   {
2439     MPlist *plist = mplist ();
2440     MFace *face;
2441     MFont *font;
2442
2443     mplist_put (plist, msymbol ("widget"), ShellWidget);
2444     if (fontset_name)
2445       {
2446         MFontset *fontset = mfontset (fontset_name);
2447         
2448         face = mface ();
2449         mface_put_prop (face, Mfontset, fontset);
2450         m17n_object_unref (fontset);
2451         mplist_add (plist, Mface, face);
2452         m17n_object_unref (face);
2453       }
2454     frame = mframe (plist);
2455     if (! frame)
2456       FATAL_ERROR ("%s\n", "Fail to create a frame!");
2457     m17n_object_unref (plist);
2458     face_default = mface_copy ((MFace *) mframe_get_prop (frame, Mface));
2459     default_face_list = mplist ();
2460     mplist_add (default_face_list, Mt, face_default);
2461     face_default_fontset = mface ();
2462     mface_put_prop (face_default_fontset, Mfontset,
2463                     mface_get_prop (face_default, Mfontset));
2464
2465     font = (MFont *) mframe_get_prop (frame, Mfont);
2466     default_font_size = (int) mfont_get_prop (font, Msize);
2467   }
2468
2469   font_width = (int) mframe_get_prop (frame, Mfont_width);
2470   font_ascent = (int) mframe_get_prop (frame, Mfont_ascent);
2471   font_descent = (int) mframe_get_prop (frame, Mfont_descent);
2472   win_width = font_width * col;
2473   win_height = (font_ascent + font_descent) * row;
2474
2475   {
2476     MFaceBoxProp prop;
2477
2478     prop.width = 4;
2479     prop.color_top = prop.color_left = msymbol ("magenta");
2480     prop.color_bottom = prop.color_right = msymbol ("red");
2481     prop.inner_hmargin = prop.inner_vmargin = 1;
2482     prop.outer_hmargin = prop.outer_vmargin = 2;
2483
2484     face_box = mface ();
2485     mface_put_prop (face_box, Mbox, &prop);
2486   }
2487
2488   face_courier = mface ();
2489   mface_put_prop (face_courier, Mfamily, msymbol ("courier"));
2490   face_helvetica = mface ();
2491   mface_put_prop (face_helvetica, Mfamily, msymbol ("helvetica"));
2492   face_times = mface ();
2493   mface_put_prop (face_times, Mfamily, msymbol ("times"));
2494   face_dv_ttyogesh = mface ();
2495   mface_put_prop (face_dv_ttyogesh, Mfamily, msymbol ("dv-ttyogesh"));
2496   face_freesans = mface ();
2497   mface_put_prop (face_freesans, Mfamily, msymbol ("freesans"));
2498   face_freeserif = mface ();
2499   mface_put_prop (face_freeserif, Mfamily, msymbol ("freeserif"));
2500   face_freemono = mface ();
2501   mface_put_prop (face_freemono, Mfamily, msymbol ("freemono"));
2502
2503   face_xxx_large = mface ();
2504   mface_put_prop (face_xxx_large, Mratio, (void *) 300);
2505   {
2506     MFont *latin_font = mframe_get_prop (frame, Mfont);
2507     MFont *dev_font = mfont ();
2508     MFont *thai_font = mfont ();
2509     MFont *tib_font = mfont ();
2510     MFontset *fontset;
2511     MSymbol unicode_bmp = msymbol ("unicode-bmp");
2512     MSymbol no_ctl = msymbol ("no-ctl");
2513
2514     mfont_put_prop (dev_font, Mfamily, msymbol ("raghindi"));
2515     mfont_put_prop (dev_font, Mregistry, unicode_bmp);
2516     mfont_put_prop (thai_font, Mfamily, msymbol ("norasi"));
2517     mfont_put_prop (thai_font, Mregistry, unicode_bmp);
2518     mfont_put_prop (tib_font, Mfamily, msymbol ("mtib"));
2519     mfont_put_prop (tib_font, Mregistry, unicode_bmp);
2520
2521     fontset = mfontset_copy (mfontset (fontset_name), "no-ctl");
2522     mfontset_modify_entry (fontset, msymbol ("latin"), Mnil, Mnil,
2523                            latin_font, Mnil, 0);
2524     mfontset_modify_entry (fontset, msymbol ("devanagari"), Mnil, Mnil,
2525                            dev_font, no_ctl, 0);
2526     mfontset_modify_entry (fontset, msymbol ("thai"), Mnil, Mnil,
2527                            thai_font, no_ctl, 0);
2528     mfontset_modify_entry (fontset, msymbol ("tibetan"), Mnil, Mnil,
2529                            tib_font, no_ctl, 0);
2530     face_no_ctl_fontset = mface ();
2531     mface_put_prop (face_no_ctl_fontset, Mfontset, fontset);
2532     m17n_object_unref (fontset);
2533
2534     free (dev_font);
2535     free (thai_font);
2536     free (tib_font);
2537   }
2538
2539   setup_input_methods (with_xim);
2540
2541   gc = DefaultGC (display, screen);
2542
2543   XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans2));
2544   XtSetArg (arg[1], XtNdefaultDistance, 2);
2545   form = XtCreateManagedWidget ("form", formWidgetClass, ShellWidget, arg, 2);
2546
2547   XtSetArg (arg[0], XtNborderWidth, 0);
2548   XtSetArg (arg[1], XtNdefaultDistance, 2);
2549   XtSetArg (arg[2], XtNtop, XawChainTop);
2550   XtSetArg (arg[3], XtNbottom, XawChainTop);
2551   XtSetArg (arg[4], XtNleft, XawChainLeft);
2552   XtSetArg (arg[5], XtNright, XawChainRight);
2553   XtSetArg (arg[6], XtNresizable, True);
2554   HeadWidget = XtCreateManagedWidget ("head", formWidgetClass, form, arg, 7);
2555   XtSetArg (arg[7], XtNfromVert, HeadWidget);
2556   FaceWidget = XtCreateManagedWidget ("face", formWidgetClass, form, arg, 8);
2557   XtSetArg (arg[7], XtNfromVert, FaceWidget);
2558   LangWidget = XtCreateManagedWidget ("lang", formWidgetClass, form, arg, 8);
2559   XtSetArg (arg[3], XtNbottom, XawChainBottom);
2560   XtSetArg (arg[7], XtNfromVert, LangWidget);
2561   BodyWidget = XtCreateManagedWidget ("body", formWidgetClass, form, arg, 8);
2562   XtSetArg (arg[2], XtNtop, XawChainBottom);
2563   XtSetArg (arg[7], XtNfromVert, BodyWidget);
2564   TailWidget = XtCreateManagedWidget ("tail", formWidgetClass, form, arg, 8);
2565
2566   FileShellWidget = XtCreatePopupShell ("FileShell", transientShellWidgetClass,
2567                                         HeadWidget, NULL, 0);
2568   XtSetArg (arg[0], XtNvalue, "");
2569   FileDialogWidget = XtCreateManagedWidget ("File", dialogWidgetClass,
2570                                             FileShellWidget, arg, 1);
2571   XawDialogAddButton (FileDialogWidget, "OK",
2572                       FileDialogProc, (XtPointer) 0);
2573   XawDialogAddButton (FileDialogWidget, "CANCEL",
2574                       FileDialogProc, (XtPointer) 1);
2575
2576   CheckPixmap = XCreateBitmapFromData (display, RootWindow (display, screen),
2577                                        (char *) check_bits,
2578                                        check_width, check_height);
2579   {
2580     unsigned long valuemask = GCForeground;
2581     XGCValues values;
2582
2583     values.foreground = 1;
2584     mono_gc = XCreateGC (display, CheckPixmap, valuemask, &values);
2585     values.foreground = 0;
2586     mono_gc_inv = XCreateGC (display, CheckPixmap, valuemask, &values);
2587   }
2588
2589   {
2590     MenuRec *menus;
2591     int num_menus = 10;
2592
2593     if (num_menus < num_input_methods + 2)
2594       num_menus = num_input_methods + 2;
2595     if (num_menus < num_faces + 1)
2596       num_menus = num_faces + 1;
2597     menus = alloca (sizeof (MenuRec) * num_menus);
2598
2599     w = create_menu_button (ShellWidget, HeadWidget, NULL, "File", "File Menu",
2600                             FileMenu, sizeof FileMenu / sizeof (MenuRec),
2601                             "File I/O, Serialization, Image, Quit");
2602
2603     SetMenu (menus[0], 0, "Logical Move", NULL, CursorProc, 0, 1);
2604     SetMenu (menus[1], 0, "Visual Move", NULL, CursorProc, 1, 0);
2605     SetMenu (menus[2], 1, "", NULL, NULL, NULL, 0);
2606     SetMenu (menus[3], 0, "Box type", NULL, CursorProc, 2, 0);
2607     SetMenu (menus[4], 0, "Bar type", NULL, CursorProc, 3, 1);
2608     SetMenu (menus[5], 0, "Bidi type", NULL, CursorProc, 4, 0);
2609     w = create_menu_button (ShellWidget, HeadWidget, w,
2610                             "Cursor", "Cursor Menu",
2611                             menus, 6, "Cursor Movement Mode, Cursor Shape");
2612     CursorMenus[0] = menus[0].w;
2613     CursorMenus[1] = menus[1].w;
2614     CursorMenus[2] = menus[3].w;
2615     CursorMenus[3] = menus[4].w;
2616     CursorMenus[4] = menus[5].w;
2617
2618     SetMenu (menus[0], 0, "disable", NULL, BidiProc, 0, 0);
2619     SetMenu (menus[1], 0, "Left  (|--> |)", NULL, BidiProc, 1, 1);
2620     SetMenu (menus[2], 0, "Right (| <--|)", NULL, BidiProc, 2, 0);
2621     w = create_menu_button (ShellWidget, HeadWidget, w, "Bidi", "Bidi Menu",
2622                             menus, 3, "BIDI Processing Mode");
2623     for (i = 0; i < 3; i++)
2624       BidiMenus[i] = menus[i].w;
2625
2626     SetMenu (menus[0], 0, "truncate", NULL, LineBreakProc, 0, 0);
2627     SetMenu (menus[1], 0, "break at edge", NULL, LineBreakProc, 1, 1);
2628     SetMenu (menus[2], 0, "break at word boundary", NULL, LineBreakProc, 2, 0);
2629     w = create_menu_button (ShellWidget, HeadWidget, w, "LineBreak",
2630                             "LineBreak Menu",
2631                             menus, 3, "How to break lines");
2632     for (i = 0; i < 3; i++)
2633       LineBreakMenus[i] = menus[i].w;
2634
2635     SetMenu (menus[0], 0, "none", NULL, InputMethodProc, -2, 1);
2636     SetMenu (menus[1], 0, "auto", NULL, InputMethodProc, -1, 0);
2637     for (i = 0; i < num_input_methods; i++)
2638       {
2639         InputMethodInfo *im = input_method_table + i;
2640         char *name1, *name2;
2641
2642         if (im->language != Mnil && im->language != Mt)
2643           {
2644             MSymbol sym = msymbol_get (im->language, Mlanguage);
2645             if (sym == Mnil)
2646               name1 = msymbol_name (im->language);
2647             else
2648               name1 = msymbol_name (sym);
2649             name2 = msymbol_name (im->name);
2650           }
2651         else
2652           name1 = msymbol_name (im->name), name2 = NULL;
2653
2654         SetMenu (menus[i + 2], 0, name1, name2, InputMethodProc, i, 0);
2655       }
2656     w = create_menu_button (ShellWidget, HeadWidget, w, "InputMethod",
2657                             "Input Method Menu", menus, i + 2,
2658                             "Select input method");
2659
2660     {
2661       unsigned long valuemask = GCForeground;
2662       XGCValues values;
2663
2664       XtSetArg (arg[0], XtNbackground, &values.foreground);
2665       XtGetValues (w, arg, 1);
2666       gc_inv = XCreateGC (display, RootWindow (display, screen),
2667                           valuemask, &values);
2668     }
2669
2670     InputMethodMenus = malloc (sizeof (Widget) * (num_input_methods + 2));
2671     for (i = 0; i < num_input_methods + 2; i++)
2672       InputMethodMenus[i] = menus[i].w;
2673
2674     input_status_width = font_width * 8;
2675     input_status_height = (font_ascent + font_descent) * 2.4;
2676     input_status_pixmap = XCreatePixmap (display, RootWindow (display, screen),
2677                                          input_status_width,
2678                                          input_status_height,
2679                                          DefaultDepth (display, screen));
2680     {
2681       MFaceBoxProp prop;
2682
2683       prop.width = 1;
2684       prop.color_top = prop.color_bottom
2685         = prop.color_left = prop.color_right = Mnil;
2686       prop.inner_hmargin = prop.inner_vmargin = 1;
2687       prop.outer_hmargin = prop.outer_vmargin = 0;
2688       face_input_status = mface_copy (face_default);
2689       mface_put_prop (face_input_status, Mbox, &prop);
2690     }
2691
2692     XFillRectangle (display, input_status_pixmap, gc_inv,
2693                     0, 0, input_status_width, input_status_height);
2694     XtSetArg (arg[0], XtNfromHoriz, w);
2695     XtSetArg (arg[1], XtNleft, XawRubber);
2696     XtSetArg (arg[2], XtNright, XawChainRight);
2697     XtSetArg (arg[3], XtNborderWidth, 0);
2698     XtSetArg (arg[4], XtNlabel, "          ");
2699     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2700     CurIMLang = XtCreateManagedWidget ("CurIMLang", labelWidgetClass,
2701                                        HeadWidget, arg, 6);
2702     XtSetArg (arg[0], XtNfromHoriz, CurIMLang);
2703     XtSetArg (arg[1], XtNleft, XawChainRight);
2704     XtSetArg (arg[4], XtNbitmap, input_status_pixmap);
2705     CurIMStatus = XtCreateManagedWidget ("CurIMStatus", labelWidgetClass,
2706                                          HeadWidget, arg, 5);
2707
2708     XtSetArg (arg[0], XtNborderWidth, 0);
2709     XtSetArg (arg[1], XtNleft, XawChainLeft);
2710     XtSetArg (arg[2], XtNright, XawChainLeft);
2711     w = XtCreateManagedWidget ("Face", labelWidgetClass, FaceWidget, arg, 3);
2712     for (i = 0; i < num_faces;)
2713       {
2714         char *label_menu = face_table[i++].name; /* "Menu Xxxx" */
2715         char *label = label_menu + 5;            /* "Xxxx" */
2716
2717         for (j = i; j < num_faces && face_table[j].face; j++)
2718           SetMenu (menus[j - i], 0, face_table[j].name, NULL,
2719                    FaceProc, j, -1);
2720         w = create_menu_button (ShellWidget, FaceWidget, w,
2721                                 label, label_menu,
2722                                 menus, j - i, "Push face property");
2723         i = j;
2724       }
2725
2726     XtSetArg (arg[0], XtNfromHoriz, w);
2727     XtSetArg (arg[1], XtNleft, XawChainLeft);
2728     XtSetArg (arg[2], XtNright, XawChainLeft);
2729     XtSetArg (arg[3], XtNhorizDistance, 10);
2730     XtSetArg (arg[4], XtNlabel, "Pop");
2731     XtSetArg (arg[5], XtNtranslations,
2732               XtParseTranslationTable (pop_face_trans));
2733     w = XtCreateManagedWidget ("Pop Face", commandWidgetClass,
2734                                FaceWidget, arg, 6);
2735     XtAddCallback (w, XtNcallback, FaceProc, (void *) -1);
2736
2737     XtSetArg (arg[0], XtNfromHoriz, w);
2738     XtSetArg (arg[1], XtNleft, XawChainLeft);
2739     XtSetArg (arg[2], XtNright, XawChainRight);
2740     XtSetArg (arg[3], XtNlabel, "");
2741     XtSetArg (arg[4], XtNborderWidth, 0);
2742     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2743     CurFaceWidget = XtCreateManagedWidget ("Current Face", labelWidgetClass,
2744                                            FaceWidget, arg, 6);
2745
2746     XtSetArg (arg[0], XtNborderWidth, 0);
2747     XtSetArg (arg[1], XtNleft, XawChainLeft);
2748     XtSetArg (arg[2], XtNright, XawChainLeft);
2749     w = XtCreateManagedWidget ("Lang", labelWidgetClass, LangWidget, arg, 3);
2750     {
2751       MPlist *plist[11], *pl;
2752       char langname[3];
2753
2754       for (i = 0; i < 11; i++) plist[i] = NULL;
2755       langname[2] = '\0';
2756       for (langname[0] = 'a'; langname[0] <= 'z'; langname[0]++)
2757         for (langname[1] = 'a'; langname[1] <= 'z'; langname[1]++)
2758           {
2759             MSymbol sym = msymbol_exist (langname);
2760             MSymbol fullname;
2761
2762             if (sym != Mnil
2763                 && ((fullname = msymbol_get (sym, Mlanguage)) != Mnil))
2764               {
2765                 char *name = msymbol_name (fullname);
2766                 char c = name[0];
2767
2768                 if (c >= 'A' && c <= 'Z')
2769                   {
2770                     int idx = (c < 'U') ? (c - 'A') / 2 : 10;
2771
2772                     pl = plist[idx];
2773                     if (! pl)
2774                       pl = plist[idx] = mplist ();
2775                     for (; mplist_next (pl); pl = mplist_next (pl))
2776                       if (strcmp (name, (char *) mplist_value (pl)) < 0)
2777                         break;
2778                     mplist_push (pl, sym, fullname);
2779                   }
2780               }
2781           }
2782
2783       for (i = 0; i < 11; i++)
2784         if (plist[i])
2785           {
2786             char *name = alloca (9);
2787
2788             sprintf (name, "Menu %c-%c", 'A' + i * 2, 'A' + i * 2 + 1);
2789             if (i == 10)
2790               name[7] = 'Z';
2791             for (j = 0, pl = plist[i]; mplist_next (pl);
2792                  j++, pl = mplist_next (pl))
2793               SetMenu (menus[j], 0, msymbol_name ((MSymbol) mplist_value (pl)),
2794                        msymbol_name (mplist_key (pl)),
2795                        LangProc, mplist_key (pl), -1);
2796             w = create_menu_button (ShellWidget, LangWidget, w, name + 5, name,
2797                                     menus, j, "Push language property");
2798           }
2799       for (i = 0; i < 11; i++)
2800         if (plist[i])
2801           m17n_object_unref (plist[i]);
2802     }
2803     XtSetArg (arg[0], XtNfromHoriz, w);
2804     XtSetArg (arg[1], XtNleft, XawChainLeft);
2805     XtSetArg (arg[2], XtNright, XawChainLeft);
2806     XtSetArg (arg[3], XtNhorizDistance, 10);
2807     XtSetArg (arg[4], XtNlabel, "Pop");
2808     XtSetArg (arg[5], XtNtranslations,
2809               XtParseTranslationTable (pop_lang_trans));
2810     w = XtCreateManagedWidget ("Pop Lang", commandWidgetClass,
2811                                LangWidget, arg, 6);
2812     XtAddCallback (w, XtNcallback, LangProc, Mnil);
2813
2814     XtSetArg (arg[0], XtNfromHoriz, w);
2815     XtSetArg (arg[1], XtNleft, XawChainLeft);
2816     XtSetArg (arg[2], XtNright, XawChainRight);
2817     XtSetArg (arg[3], XtNlabel, "");
2818     XtSetArg (arg[4], XtNborderWidth, 0);
2819     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2820     CurLangWidget = XtCreateManagedWidget ("Current Lang", labelWidgetClass,
2821                                            LangWidget, arg, 6);
2822   }
2823
2824   XtSetArg (arg[0], XtNheight, win_height);
2825   XtSetArg (arg[1], XtNwidth, 10);
2826   XtSetArg (arg[2], XtNleft, XawChainLeft);
2827   XtSetArg (arg[3], XtNright, XawChainLeft);
2828   SbarWidget = XtCreateManagedWidget ("sbar", scrollbarWidgetClass, BodyWidget,
2829                                       arg, 4);
2830   XtAddCallback (SbarWidget, XtNscrollProc, ScrollProc, NULL);
2831   XtAddCallback (SbarWidget, XtNjumpProc, JumpProc, NULL);
2832
2833   XtSetArg (arg[0], XtNheight, win_height);
2834   XtSetArg (arg[1], XtNwidth, win_width);
2835   XtSetArg (arg[2], XtNtranslations, XtParseTranslationTable (trans));
2836   XtSetArg (arg[3], XtNfromHoriz, SbarWidget);
2837   XtSetArg (arg[4], XtNleft, XawChainLeft);
2838   XtSetArg (arg[5], XtNright, XawChainRight);
2839   TextWidget = XtCreateManagedWidget ("text", simpleWidgetClass, BodyWidget,
2840                                       arg, 5);
2841
2842   XtSetArg (arg[0], XtNborderWidth, 0);
2843   XtSetArg (arg[1], XtNleft, XawChainLeft);
2844   XtSetArg (arg[2], XtNright, XawChainRight);
2845   XtSetArg (arg[3], XtNresizable, True);
2846   XtSetArg (arg[4], XtNjustify, XtJustifyLeft);
2847   MessageWidget = XtCreateManagedWidget ("message", labelWidgetClass,
2848                                          TailWidget, arg, 5);
2849
2850   memset (&control, 0, sizeof control);
2851   control.two_dimensional = 1;
2852   control.enable_bidi = 1;
2853   control.anti_alias = 1;
2854   control.min_line_ascent = font_ascent;
2855   control.min_line_descent = font_descent;
2856   control.max_line_width = win_width;
2857   control.with_cursor = 1;
2858   control.cursor_width = 2;
2859   control.partial_update = 1;
2860   control.ignore_formatting_char = 1;
2861
2862   memset (&input_status_control, 0, sizeof input_status_control);
2863   input_status_control.enable_bidi = 1;
2864
2865   XtAppAddActions (context, actions, XtNumber (actions));
2866   XtRealizeWidget (ShellWidget);
2867
2868   win = XtWindow (TextWidget);
2869
2870   XtAppMainLoop (context);
2871
2872   if (current_input_context)
2873     minput_destroy_ic (current_input_context);
2874   for (i = 0; i < num_input_methods; i++)
2875     if (input_method_table[i].im)
2876       minput_close_im (input_method_table[i].im);
2877   m17n_object_unref (frame);
2878   m17n_object_unref (mt);
2879   m17n_object_unref (face_xxx_large);
2880   m17n_object_unref (face_box);
2881   m17n_object_unref (face_courier);
2882   m17n_object_unref (face_helvetica);
2883   m17n_object_unref (face_times);
2884   m17n_object_unref (face_dv_ttyogesh);
2885   m17n_object_unref (face_freesans);
2886   m17n_object_unref (face_freeserif);
2887   m17n_object_unref (face_freemono);
2888   m17n_object_unref (face_default_fontset);
2889   m17n_object_unref (face_no_ctl_fontset);
2890   m17n_object_unref (face_input_status);
2891   m17n_object_unref (face_default);
2892   m17n_object_unref (default_face_list);
2893   m17n_object_unref (selection);
2894
2895   XFreeGC (display, mono_gc);
2896   XFreeGC (display, mono_gc_inv);
2897   XFreeGC (display, gc_inv);
2898   XtUninstallTranslations (form);
2899   XtUninstallTranslations (TextWidget);
2900   XtDestroyWidget (ShellWidget);
2901   XtDestroyApplicationContext (context);
2902
2903   M17N_FINI ();
2904
2905   free (fontset_name);
2906   free (filename);
2907   free (input_method_table);
2908   free (InputMethodMenus);
2909
2910   exit (0);
2911 }
2912 #endif /* not FOR_DOXYGEN */