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