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