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