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