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