(input_status_control): New variable.
[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, input_status_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                           &input_status_control, NULL, NULL, &rect);
1796       mdraw_text_with_control (frame, (MDrawWindow) input_status_pixmap,
1797                                input_status_width - rect.width - 2, - rect.y,
1798                                ic->status, 0, mtext_len (ic->status),
1799                                &input_status_control);
1800     }
1801   XtSetArg (arg[0], XtNbitmap, input_status_pixmap);
1802   XtSetValues (CurIMStatus, arg, 1);
1803 }
1804
1805 int
1806 compare_input_method (const void *elt1, const void *elt2)
1807 {
1808   const MInputMethod *im1 = *(MInputMethod **) elt1;
1809   const MInputMethod *im2 = *(MInputMethod **) elt2;
1810   MSymbol lang1, lang2;
1811
1812   if (im1->language == Mnil)
1813     return 1;
1814   if (im1->language == im2->language)
1815     return strcmp (msymbol_name (im1->name), msymbol_name (im2->name));
1816   if (im1->language == Mt)
1817     return 1;
1818   if (im2->language == Mt)
1819     return -1;
1820   lang1 = msymbol_get (im1->language, Mlanguage);
1821   lang2 = msymbol_get (im2->language, Mlanguage);
1822   return strcmp (msymbol_name (lang1), msymbol_name (lang2));
1823 }
1824
1825 void
1826 setup_input_methods (int with_xim)
1827 {
1828   MInputMethod *im = NULL;
1829   MInputXIMArgIM arg_xim;
1830   MPlist *plist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
1831   MPlist *pl;
1832   int i = 0;
1833
1834   num_input_methods = mplist_length (plist);
1835
1836   if (with_xim)
1837     {
1838       arg_xim.display = display;
1839       arg_xim.db = NULL;  
1840       arg_xim.res_name = arg_xim.res_class = NULL;
1841       arg_xim.locale = NULL;
1842       arg_xim.modifier_list = NULL;
1843       im = minput_open_im (Mnil, msymbol ("xim"), &arg_xim);
1844       if (im)
1845         num_input_methods++;
1846     }
1847   input_method_table = calloc (num_input_methods, sizeof (MInputMethod *));
1848   if (im)
1849     input_method_table[i++] = im;
1850   for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl))
1851     {
1852       MDatabase *mdb = mplist_value (pl);
1853       MSymbol *tag = mdatabase_tag (mdb);
1854
1855       if (tag[1] != Mnil)
1856         {
1857           im = minput_open_im (tag[1], tag[2], NULL);
1858           if (im)
1859             input_method_table[i++] = im;
1860         }
1861     }
1862
1863   m17n_object_unref (plist);
1864   num_input_methods = i;
1865   qsort (input_method_table, num_input_methods, sizeof input_method_table[0],
1866          compare_input_method);
1867   current_input_context = NULL;
1868
1869   mplist_put (minput_driver->callback_list, Minput_status_start,
1870               (void *) input_status);
1871   mplist_put (minput_driver->callback_list, Minput_status_draw,
1872               (void *) input_status);
1873   mplist_put (minput_driver->callback_list, Minput_status_done,
1874               (void *) input_status);
1875 }
1876
1877
1878 static void
1879 MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num)
1880 {
1881   char *msg;
1882
1883   if (num && *num > 0)
1884     {
1885       int bytes = 0, i;
1886
1887       for (i = 0; i < *num; i++)
1888         bytes += strlen (str[i]) + 1;
1889       msg = alloca (bytes);
1890       strcpy (msg, str[0]);
1891       for (i = 1; i < *num; i++)
1892         strcat (msg, " "), strcat (msg, str[i]);
1893     }
1894   else if (cursor.from < nchars)
1895     {
1896       int c = mtext_ref_char (mt, cursor.from);
1897       char *name = mchar_get_prop (c, Mname);
1898
1899       if (! name)
1900         name = "";
1901       msg = alloca (10 + strlen (name));
1902       sprintf (msg, "U+%04X %s", c, name);
1903     }
1904   else
1905     {
1906       msg = "";
1907     }
1908   XtSetArg (arg[0], XtNlabel, msg);
1909   XtSetValues (MessageWidget, arg, 1);
1910 }
1911
1912 typedef struct
1913 {
1914   int type;
1915   char *name1, *name2;
1916   XtCallbackProc proc;
1917   XtPointer client_data;
1918   int status;
1919   Widget w;
1920 } MenuRec;
1921
1922 void PopupProc (Widget w, XtPointer client_data, XtPointer call_data);
1923
1924 void SaveProc (Widget w, XtPointer client_data, XtPointer call_data);
1925
1926 MenuRec FileMenu[] =
1927   { { 0, "Open", NULL, PopupProc, FileMenu + 0, -1 },
1928     { 0, "Save", NULL, SaveProc, NULL, -1 },
1929     { 0, "Save as", NULL, PopupProc, FileMenu + 2, -1 },
1930     { 1 },
1931     { 0, "Serialize", NULL, SerializeProc, (void *) 1, -1 },
1932     { 0, "Deserialize", NULL, SerializeProc, (void *) 0, -1 },
1933     { 1 },
1934     { 0, "Dump Image Buffer", NULL, DumpImageProc, (void *) 0, -1 },
1935     { 0, "Dump Image Region", NULL, DumpImageProc, (void *) 1, -1 },
1936     { 1 },
1937     { 0, "Quit", NULL, QuitProc, NULL, -1 } };
1938
1939 void
1940 PopupProc (Widget w, XtPointer client_data, XtPointer call_data)
1941 {
1942   MenuRec *rec = (MenuRec *) client_data;
1943   Position x, y;
1944
1945   XtSetArg (arg[0], XtNvalue, "");
1946   XtSetArg (arg[1], XtNlabel, rec->name1);
1947   XtSetValues (FileDialogWidget, arg, 2);
1948   XtTranslateCoords (w, (Position) 0, (Position) 0, &x, &y);
1949   XtSetArg (arg[0], XtNx, x + 20);
1950   XtSetArg (arg[1], XtNy, y + 10);
1951   XtSetValues (FileShellWidget, arg, 2);
1952   XtPopup (FileShellWidget, XtGrabExclusive);
1953 }
1954
1955 void
1956 FileDialogProc (Widget w, XtPointer client_data, XtPointer call_data)
1957 {
1958   FILE *fp;
1959   char *label;
1960
1961   XtPopdown (FileShellWidget);
1962   if ((int) client_data == 1)
1963     return;
1964   XtSetArg (arg[0], XtNlabel, &label);
1965   XtGetValues (FileDialogWidget, arg, 1);
1966   if (strcmp (label, FileMenu[0].name1) == 0)
1967     {
1968       /* Open a file */
1969       free (filename);
1970       filename = strdup ((char *) XawDialogGetValueString (FileDialogWidget));
1971       fp = fopen (filename, "r");
1972       hide_cursor ();
1973       m17n_object_unref (mt);
1974       if (fp)
1975         {
1976           mt = mconv_decode_stream (Mcoding_utf_8, fp);
1977           fclose (fp);
1978           if (! mt)
1979             mt = mtext ();
1980         }
1981       else
1982         mt = mtext ();
1983       serialized = 0;
1984       nchars = mtext_len (mt);
1985       update_top (0);
1986       update_cursor (0, 1);
1987       redraw (0, win_height, 1, 1);
1988     }
1989   else if (strcmp (label, FileMenu[2].name1) == 0)
1990     SaveProc (w, (XtPointer) XawDialogGetValueString (FileDialogWidget), NULL);
1991   else
1992     fprintf (stderr, "Invalid calling sequence: FileDialogProc\n");
1993 }
1994
1995 #define SetMenu(MENU, TYPE, NAME1, NAME2, PROC, DATA, STATUS)            \
1996   ((MENU).type = (TYPE), (MENU).name1 = (NAME1), (MENU).name2 = (NAME2), \
1997    (MENU).proc = (PROC), (MENU).client_data = (XtPointer) (DATA),        \
1998    (MENU).status = (STATUS))
1999
2000
2001 Widget
2002 create_menu_button (Widget top, Widget parent, Widget left, char *button_name,
2003                     char *menu_name, MenuRec *menus, int num_menus, char *help)
2004 {
2005   Widget button, menu;
2006   char *fmt = "<EnterWindow>: highlight() MenuHelp(%s)\n\
2007                <LeaveWindow>: reset() MenuHelp()\n\
2008                <BtnDown>: reset() PopupMenu()\n\
2009                <BtnUp>: highlight()"; 
2010   int i;
2011   MenuRec *m;
2012   char *trans;
2013   int max_width = 0;
2014
2015   menu = XtCreatePopupShell (menu_name, simpleMenuWidgetClass, top, NULL, 0);
2016   for (i = 0; i < num_menus; i++)
2017     {
2018       m = menus + i;
2019       if (m->type == 0)
2020         {
2021           if (m->proc)
2022             {
2023               int n = 0;
2024
2025               if (m->status >= 0)
2026                 {
2027                   XtSetArg (arg[n], XtNleftMargin, 20), n++;
2028                   if (m->status > 0)
2029                     XtSetArg (arg[n], XtNleftBitmap, CheckPixmap), n++;
2030                 }
2031               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2032                                             menu, arg, n);
2033               XtAddCallback (m->w, XtNcallback, m->proc, m->client_data);
2034             }
2035           else
2036             {
2037               XtSetArg (arg[0], XtNsensitive, False);
2038               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2039                                             menu, arg, 2);
2040             }
2041         }
2042       else
2043         {
2044           XtCreateManagedWidget (m->name1, smeLineObjectClass, menu, NULL, 0);
2045         }
2046       if (m->name2)
2047         max_width = 1;
2048     }
2049   trans = alloca (strlen (fmt) + strlen (help));
2050   sprintf (trans, fmt, help);
2051   XtSetArg (arg[0], XtNmenuName, menu_name);
2052   XtSetArg (arg[1], XtNtranslations, XtParseTranslationTable ((String) trans));
2053   XtSetArg (arg[2], XtNinternalWidth, 2);
2054   XtSetArg (arg[3], XtNhighlightThickness, 1);
2055   XtSetArg (arg[4], XtNleft, XawChainLeft);
2056   XtSetArg (arg[5], XtNright, XawChainLeft);
2057   i = 6;
2058   if (left)
2059     XtSetArg (arg[i], XtNfromHoriz, left), i++;
2060   button = XtCreateManagedWidget (button_name, menuButtonWidgetClass, parent,
2061                                   arg, i);
2062
2063   if (max_width)
2064     {
2065       int height, ascent, *width = alloca (sizeof (int) * num_menus);
2066       int *len = alloca (sizeof (int) * num_menus);
2067
2068       XFontSet font_set;
2069       XFontSetExtents *fontset_extents;
2070
2071       XtSetArg (arg[0], XtNfontSet, &font_set);
2072       XtGetValues (button, arg, 1);
2073
2074       fontset_extents = XExtentsOfFontSet (font_set);
2075       height = fontset_extents->max_logical_extent.height;
2076       ascent = - fontset_extents->max_logical_extent.y;
2077
2078       for (i = 0; i < num_menus; i++)
2079         if (menus[i].name2)
2080           {
2081             len[i] = strlen (menus[i].name2);
2082             width[i] = XmbTextEscapement (font_set, menus[i].name2, len[i]);
2083             if (max_width < width[i])
2084               max_width = width[i];
2085           }
2086       for (i = 0; i < num_menus; i++)
2087         if (menus[i].name2)
2088           {
2089             Pixmap pixmap = XCreatePixmap (display,
2090                                            RootWindow (display, screen),
2091                                            max_width, height, 1);
2092             XFillRectangle (display, pixmap, mono_gc_inv,
2093                             0, 0, max_width, height);
2094             XmbDrawString (display, pixmap, font_set, mono_gc,
2095                            max_width - width[i], ascent,
2096                            menus[i].name2, len[i]);
2097             XtSetArg (arg[0], XtNrightBitmap, pixmap);
2098             XtSetArg (arg[1], XtNrightMargin, max_width + 20);
2099             XtSetValues (menus[i].w, arg, 2);
2100           }
2101     }
2102
2103   return button;
2104 }
2105
2106
2107 XtActionsRec actions[] = {
2108   {"Expose", ExposeProc},
2109   {"Configure", ConfigureProc},
2110   {"Key", KeyProc},
2111   {"ButtonPress", ButtonProc},
2112   {"ButtonRelease", ButtonReleaseProc},
2113   {"ButtonMotion", ButtonMoveProc},
2114   {"Button2Press", Button2Proc},
2115   {"MenuHelp", MenuHelpProc}
2116 };
2117
2118
2119 /* Print the usage of this program (the name is PROG), and exit with
2120    EXIT_CODE.  */
2121
2122 void
2123 help_exit (char *prog, int exit_code)
2124 {
2125   char *p = prog;
2126
2127   while (*p)
2128     if (*p++ == '/')
2129       prog = p;
2130
2131   printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] FILE\n", prog);
2132   printf ("Display FILE on a window and allow users to edit it.\n");
2133   printf ("XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
2134   printf ("The following OPTIONs are available.\n");
2135   printf ("  %-13s %s", "--version", "print version number\n");
2136   printf ("  %-13s %s", "-h, --help", "print this message\n");
2137   exit (exit_code);
2138 }
2139
2140 int
2141 main (int argc, char **argv)
2142 {
2143   Widget form, BodyWidget, w;
2144   char *fontset_name = NULL;
2145   int col = 80, row = 32;
2146   /* Translation table for TextWidget.  */
2147   String trans = "<Expose>: Expose()\n\
2148                   <Configure>: Configure()\n\
2149                   <Key>: Key()\n\
2150                   <KeyUp>: Key()\n\
2151                   <Btn1Down>: ButtonPress()\n\
2152                   <Btn1Up>: ButtonRelease()\n\
2153                   <Btn1Motion>: ButtonMotion()\n\
2154                   <Btn2Down>: Button2Press()";
2155   /* Translation table for the top form widget.  */
2156   String trans2 = "<Key>: Key()\n\
2157                    <KeyUp>: Key()";
2158   String pop_face_trans
2159     = "<EnterWindow>: MenuHelp(Pop face property) highlight()\n\
2160        <LeaveWindow>: MenuHelp() reset()\n\
2161        <Btn1Down>: set()\n\
2162        <Btn1Up>: notify() unset()"; 
2163   String pop_lang_trans
2164     = "<EnterWindow>: MenuHelp(Pop language property) highlight()\n\
2165        <LeaveWindow>: MenuHelp() reset()\n\
2166        <Btn1Down>: set()\n\
2167        <Btn1Up>: notify() unset()"; 
2168   int font_width, font_ascent, font_descent;
2169   int with_xim = 0;
2170   int i, j;
2171
2172   setlocale (LC_ALL, "");
2173   /* Create the top shell.  */
2174   XtSetLanguageProc (NULL, NULL, NULL);
2175   ShellWidget = XtOpenApplication (&context, "MEdit", NULL, 0, &argc, argv,
2176                                    NULL, sessionShellWidgetClass, NULL, 0);
2177   display = XtDisplay (ShellWidget);
2178   screen = XScreenNumberOfScreen (XtScreen (ShellWidget));
2179
2180   /* Parse the remaining command line arguments.  */
2181   for (i = 1; i < argc; i++)
2182     {
2183       if (! strcmp (argv[i], "--help")
2184           || ! strcmp (argv[i], "-h"))
2185         help_exit (argv[0], 0);
2186       else if (! strcmp (argv[i], "--version"))
2187         {
2188           printf ("medit (m17n library) %s\n", VERSION);
2189           printf ("Copyright (C) 2003 AIST, JAPAN\n");
2190           exit (0);
2191         }
2192       else if (! strcmp (argv[i], "--geometry"))
2193         {
2194           i++;
2195           if (sscanf (argv[i], "%dx%d", &col, &row) != 2)
2196             help_exit (argv[0], 1);
2197         }
2198       else if (! strcmp (argv[i], "--fontset"))
2199         {
2200           i++;
2201           fontset_name = strdup (argv[i]);
2202         }
2203       else if (! strcmp (argv[i], "--with-xim"))
2204         {
2205           with_xim = 1;
2206         }
2207       else if (argv[i][0] != '-')
2208         {
2209           filename = strdup (argv[i]);
2210         }
2211       else
2212         {
2213           fprintf (stderr, "Unknown option: %s", argv[i]);
2214           help_exit (argv[0], 1);
2215         }
2216     }
2217   if (! filename)
2218     help_exit (argv[0], 1);
2219
2220   mdatabase_dir = ".";
2221   /* Initialize the m17n library.  */
2222   M17N_INIT ();
2223   if (merror_code != MERROR_NONE)
2224     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library!");
2225
2226   mt = read_file (filename);
2227   serialized = 0;
2228
2229   nchars = mtext_len (mt);
2230
2231   {
2232     MFace *face = mface ();
2233
2234     mface_put_prop (face, Mbackground, msymbol ("blue"));
2235     mface_put_prop (face, Mforeground, msymbol ("yellow"));
2236     selection = mtext_property (Mface, face, MTEXTPROP_NO_MERGE);
2237     m17n_object_unref (face);
2238   }
2239
2240   /* This tells ExposeProc to initialize everything.  */
2241   top.from = -1;
2242   
2243   XA_TEXT = XInternAtom (display, "TEXT", False);
2244   XA_COMPOUND_TEXT = XInternAtom (display, "COMPOUND_TEXT", False);
2245   XA_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
2246   {
2247     MPlist *plist = mplist ();
2248     MFace *face;
2249     MFont *font;
2250
2251     mplist_put (plist, msymbol ("widget"), ShellWidget);
2252     if (fontset_name)
2253       {
2254         MFontset *fontset = mfontset (fontset_name);
2255         
2256         face = mface ();
2257         mface_put_prop (face, Mfontset, fontset);
2258         m17n_object_unref (fontset);
2259         mplist_add (plist, Mface, face);
2260         m17n_object_unref (face);
2261       }
2262     frame = mframe (plist);
2263     m17n_object_unref (plist);
2264     face_default = (MFace *) mframe_get_prop (frame, Mface);
2265     face_default_fontset = mface ();
2266     mface_put_prop (face_default_fontset, Mfontset,
2267                     mface_get_prop (face_default, Mfontset));
2268
2269     font = (MFont *) mframe_get_prop (frame, Mfont);
2270     default_font_size = (int) mfont_get_prop (font, Msize);
2271   }
2272
2273   font_width = (int) mframe_get_prop (frame, Mfont_width);
2274   font_ascent = (int) mframe_get_prop (frame, Mfont_ascent);
2275   font_descent = (int) mframe_get_prop (frame, Mfont_descent);
2276   win_width = font_width * col;
2277   win_height = (font_ascent + font_descent) * row;
2278
2279   {
2280     MFaceBoxProp prop;
2281
2282     prop.width = 4;
2283     prop.color_top = prop.color_left = msymbol ("magenta");
2284     prop.color_bottom = prop.color_right = msymbol ("red");
2285     prop.inner_hmargin = prop.inner_vmargin = 1;
2286     prop.outer_hmargin = prop.outer_vmargin = 2;
2287
2288     face_box = mface ();
2289     mface_put_prop (face_box, Mbox, &prop);
2290   }
2291
2292   face_courier = mface ();
2293   mface_put_prop (face_courier, Mfamily, msymbol ("courier"));
2294   face_helvetica = mface ();
2295   mface_put_prop (face_helvetica, Mfamily, msymbol ("helvetica"));
2296   face_times = mface ();
2297   mface_put_prop (face_times, Mfamily, msymbol ("times"));
2298   face_dv_ttyogesh = mface ();
2299   mface_put_prop (face_dv_ttyogesh, Mfamily, msymbol ("dv-ttyogesh"));
2300   face_freesans = mface ();
2301   mface_put_prop (face_freesans, Mfamily, msymbol ("freesans"));
2302   face_freemono = mface ();
2303   mface_put_prop (face_freemono, Mfamily, msymbol ("freemono"));
2304
2305   face_xxx_large = mface ();
2306   mface_put_prop (face_xxx_large, Mratio, (void *) 300);
2307   {
2308     MFont *latin_font = mframe_get_prop (frame, Mfont);
2309     MFont *dev_font = mfont ();
2310     MFont *thai_font = mfont ();
2311     MFont *tib_font = mfont ();
2312     MFontset *fontset;
2313     MSymbol unicode_bmp = msymbol ("unicode-bmp");
2314     MSymbol no_ctl = msymbol ("no-ctl");
2315
2316     mfont_put_prop (dev_font, Mfamily, msymbol ("raghindi"));
2317     mfont_put_prop (dev_font, Mregistry, unicode_bmp);
2318     mfont_put_prop (thai_font, Mfamily, msymbol ("norasi"));
2319     mfont_put_prop (thai_font, Mregistry, unicode_bmp);
2320     mfont_put_prop (tib_font, Mfamily, msymbol ("mtib"));
2321     mfont_put_prop (tib_font, Mregistry, unicode_bmp);
2322
2323     fontset = mfontset_copy (mfontset (fontset_name), "no-ctl");
2324     mfontset_modify_entry (fontset, msymbol ("latin"), Mnil, Mnil,
2325                            latin_font, Mnil, 0);
2326     mfontset_modify_entry (fontset, msymbol ("devanagari"), Mnil, Mnil,
2327                            dev_font, no_ctl, 0);
2328     mfontset_modify_entry (fontset, msymbol ("thai"), Mnil, Mnil,
2329                            thai_font, no_ctl, 0);
2330     mfontset_modify_entry (fontset, msymbol ("tibetan"), Mnil, Mnil,
2331                            tib_font, no_ctl, 0);
2332     face_no_ctl_fontset = mface ();
2333     mface_put_prop (face_no_ctl_fontset, Mfontset, fontset);
2334     m17n_object_unref (fontset);
2335
2336     free (dev_font);
2337     free (thai_font);
2338     free (tib_font);
2339   }
2340
2341   setup_input_methods (with_xim);
2342
2343   gc = DefaultGC (display, screen);
2344
2345   XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans2));
2346   XtSetArg (arg[1], XtNdefaultDistance, 2);
2347   form = XtCreateManagedWidget ("form", formWidgetClass, ShellWidget, arg, 2);
2348
2349   XtSetArg (arg[0], XtNborderWidth, 0);
2350   XtSetArg (arg[1], XtNdefaultDistance, 2);
2351   XtSetArg (arg[2], XtNtop, XawChainTop);
2352   XtSetArg (arg[3], XtNbottom, XawChainTop);
2353   XtSetArg (arg[4], XtNleft, XawChainLeft);
2354   XtSetArg (arg[5], XtNright, XawChainRight);
2355   XtSetArg (arg[6], XtNresizable, True);
2356   HeadWidget = XtCreateManagedWidget ("head", formWidgetClass, form, arg, 7);
2357   XtSetArg (arg[7], XtNfromVert, HeadWidget);
2358   FaceWidget = XtCreateManagedWidget ("face", formWidgetClass, form, arg, 8);
2359   XtSetArg (arg[7], XtNfromVert, FaceWidget);
2360   LangWidget = XtCreateManagedWidget ("lang", formWidgetClass, form, arg, 8);
2361   XtSetArg (arg[3], XtNbottom, XawChainBottom);
2362   XtSetArg (arg[7], XtNfromVert, LangWidget);
2363   BodyWidget = XtCreateManagedWidget ("body", formWidgetClass, form, arg, 8);
2364   XtSetArg (arg[2], XtNtop, XawChainBottom);
2365   XtSetArg (arg[7], XtNfromVert, BodyWidget);
2366   TailWidget = XtCreateManagedWidget ("tail", formWidgetClass, form, arg, 8);
2367
2368   FileShellWidget = XtCreatePopupShell ("FileShell", transientShellWidgetClass,
2369                                         HeadWidget, NULL, 0);
2370   XtSetArg (arg[0], XtNvalue, "");
2371   FileDialogWidget = XtCreateManagedWidget ("File", dialogWidgetClass,
2372                                             FileShellWidget, arg, 1);
2373   XawDialogAddButton (FileDialogWidget, "OK",
2374                       FileDialogProc, (XtPointer) 0);
2375   XawDialogAddButton (FileDialogWidget, "CANCEL",
2376                       FileDialogProc, (XtPointer) 1);
2377
2378   CheckPixmap = XCreateBitmapFromData (display, RootWindow (display, screen),
2379                                        (char *) check_bits,
2380                                        check_width, check_height);
2381   {
2382     unsigned long valuemask = GCForeground;
2383     XGCValues values;
2384
2385     values.foreground = 1;
2386     mono_gc = XCreateGC (display, CheckPixmap, valuemask, &values);
2387     values.foreground = 0;
2388     mono_gc_inv = XCreateGC (display, CheckPixmap, valuemask, &values);
2389   }
2390
2391   {
2392     MenuRec *menus;
2393     int num_menus = 10;
2394
2395     if (num_menus < num_input_methods + 2)
2396       num_menus = num_input_methods + 2;
2397     if (num_menus < num_faces + 1)
2398       num_menus = num_faces + 1;
2399     menus = alloca (sizeof (MenuRec) * num_menus);
2400
2401     w = create_menu_button (ShellWidget, HeadWidget, NULL, "File", "File Menu",
2402                             FileMenu, sizeof FileMenu / sizeof (MenuRec),
2403                             "File I/O, Serialization, Image, Quit");
2404
2405     SetMenu (menus[0], 0, "Logical Move", NULL, CursorProc, 0, 1);
2406     SetMenu (menus[1], 0, "Visual Move", NULL, CursorProc, 1, 0);
2407     SetMenu (menus[2], 1, "", NULL, NULL, NULL, 0);
2408     SetMenu (menus[3], 0, "Box type", NULL, CursorProc, 2, 0);
2409     SetMenu (menus[4], 0, "Bar type", NULL, CursorProc, 3, 1);
2410     SetMenu (menus[5], 0, "Bidi type", NULL, CursorProc, 4, 0);
2411     w = create_menu_button (ShellWidget, HeadWidget, w,
2412                             "Cursor", "Cursor Menu",
2413                             menus, 6, "Cursor Movement Mode, Cursor Shape");
2414     CursorMenus[0] = menus[0].w;
2415     CursorMenus[1] = menus[1].w;
2416     CursorMenus[2] = menus[3].w;
2417     CursorMenus[3] = menus[4].w;
2418     CursorMenus[4] = menus[5].w;
2419
2420     SetMenu (menus[0], 0, "disable", NULL, BidiProc, 0, 0);
2421     SetMenu (menus[1], 0, "Left  (|--> |)", NULL, BidiProc, 1, 1);
2422     SetMenu (menus[2], 0, "Right (| <--|)", NULL, BidiProc, 2, 0);
2423     w = create_menu_button (ShellWidget, HeadWidget, w, "Bidi", "Bidi Menu",
2424                             menus, 3, "BIDI Processing Mode");
2425     for (i = 0; i < 3; i++)
2426       BidiMenus[i] = menus[i].w;
2427
2428     SetMenu (menus[0], 0, "truncate", NULL, LineBreakProc, 0, 0);
2429     SetMenu (menus[1], 0, "break at edge", NULL, LineBreakProc, 1, 1);
2430     SetMenu (menus[2], 0, "break at word boundary", NULL, LineBreakProc, 2, 0);
2431     w = create_menu_button (ShellWidget, HeadWidget, w, "LineBreak",
2432                             "LineBreak Menu",
2433                             menus, 3, "How to break lines");
2434     for (i = 0; i < 3; i++)
2435       LineBreakMenus[i] = menus[i].w;
2436
2437     SetMenu (menus[0], 0, "none", NULL, InputMethodProc, -2, 1);
2438     SetMenu (menus[1], 0, "auto", NULL, InputMethodProc, -1, 0);
2439     for (i = 0; i < num_input_methods; i++)
2440       {
2441         MInputMethod *im = input_method_table[i];
2442         char *name1, *name2;
2443
2444         if (im->language != Mnil && im->language != Mt)
2445           {
2446             MSymbol sym = msymbol_get (im->language, Mlanguage);
2447             if (sym == Mnil)
2448               name1 = msymbol_name (im->language);
2449             else
2450               name1 = msymbol_name (sym);
2451             name2 = msymbol_name (im->name);
2452           }
2453         else
2454           name1 = msymbol_name (im->name), name2 = NULL;
2455
2456         SetMenu (menus[i + 2], 0, name1, name2, InputMethodProc, i, 0);
2457       }
2458     w = create_menu_button (ShellWidget, HeadWidget, w, "InputMethod",
2459                             "Input Method Menu", menus, i + 2,
2460                             "Select input method");
2461
2462     {
2463       unsigned long valuemask = GCForeground;
2464       XGCValues values;
2465
2466       XtSetArg (arg[0], XtNbackground, &values.foreground);
2467       XtGetValues (w, arg, 1);
2468       gc_inv = XCreateGC (display, RootWindow (display, screen),
2469                           valuemask, &values);
2470     }
2471
2472     InputMethodMenus = malloc (sizeof (Widget) * (num_input_methods + 2));
2473     for (i = 0; i < num_input_methods + 2; i++)
2474       InputMethodMenus[i] = menus[i].w;
2475
2476     input_status_width = font_width * 8;
2477     input_status_height = (font_ascent + font_descent) * 2.4;
2478     input_status_pixmap = XCreatePixmap (display, RootWindow (display, screen),
2479                                          input_status_width,
2480                                          input_status_height,
2481                                          DefaultDepth (display, screen));
2482     {
2483       MFaceBoxProp prop;
2484
2485       prop.width = 1;
2486       prop.color_top = prop.color_bottom
2487         = prop.color_left = prop.color_right = Mnil;
2488       prop.inner_hmargin = prop.inner_vmargin = 1;
2489       prop.outer_hmargin = prop.outer_vmargin = 0;
2490       face_input_status = mface ();
2491       mface_put_prop (face_input_status, Mbox, &prop);
2492     }
2493
2494     XFillRectangle (display, input_status_pixmap, gc_inv,
2495                     0, 0, input_status_width, input_status_height);
2496     XtSetArg (arg[0], XtNfromHoriz, w);
2497     XtSetArg (arg[1], XtNleft, XawRubber);
2498     XtSetArg (arg[2], XtNright, XawChainRight);
2499     XtSetArg (arg[3], XtNborderWidth, 0);
2500     XtSetArg (arg[4], XtNlabel, "          ");
2501     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2502     CurIMLang = XtCreateManagedWidget ("CurIMLang", labelWidgetClass,
2503                                        HeadWidget, arg, 6);
2504     XtSetArg (arg[0], XtNfromHoriz, CurIMLang);
2505     XtSetArg (arg[1], XtNleft, XawChainRight);
2506     XtSetArg (arg[4], XtNbitmap, input_status_pixmap);
2507     CurIMStatus = XtCreateManagedWidget ("CurIMStatus", labelWidgetClass,
2508                                          HeadWidget, arg, 5);
2509
2510     XtSetArg (arg[0], XtNborderWidth, 0);
2511     XtSetArg (arg[1], XtNleft, XawChainLeft);
2512     XtSetArg (arg[2], XtNright, XawChainLeft);
2513     w = XtCreateManagedWidget ("Face", labelWidgetClass, FaceWidget, arg, 3);
2514     for (i = 0; i < num_faces;)
2515       {
2516         char *label_menu = face_table[i++].name; /* "Menu Xxxx" */
2517         char *label = label_menu + 5;            /* "Xxxx" */
2518
2519         for (j = i; j < num_faces && face_table[j].face; j++)
2520           SetMenu (menus[j - i], 0, face_table[j].name, NULL,
2521                    FaceProc, j, -1);
2522         w = create_menu_button (ShellWidget, FaceWidget, w,
2523                                 label, label_menu,
2524                                 menus, j - i, "Push face property");
2525         i = j;
2526       }
2527
2528     XtSetArg (arg[0], XtNfromHoriz, w);
2529     XtSetArg (arg[1], XtNleft, XawChainLeft);
2530     XtSetArg (arg[2], XtNright, XawChainLeft);
2531     XtSetArg (arg[3], XtNhorizDistance, 10);
2532     XtSetArg (arg[4], XtNlabel, "Pop");
2533     XtSetArg (arg[5], XtNtranslations,
2534               XtParseTranslationTable (pop_face_trans));
2535     w = XtCreateManagedWidget ("Pop Face", commandWidgetClass,
2536                                FaceWidget, arg, 6);
2537     XtAddCallback (w, XtNcallback, FaceProc, (void *) -1);
2538
2539     XtSetArg (arg[0], XtNfromHoriz, w);
2540     XtSetArg (arg[1], XtNleft, XawChainLeft);
2541     XtSetArg (arg[2], XtNright, XawChainRight);
2542     XtSetArg (arg[3], XtNlabel, "");
2543     XtSetArg (arg[4], XtNborderWidth, 0);
2544     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2545     CurFaceWidget = XtCreateManagedWidget ("Current Face", labelWidgetClass,
2546                                            FaceWidget, arg, 6);
2547
2548     XtSetArg (arg[0], XtNborderWidth, 0);
2549     XtSetArg (arg[1], XtNleft, XawChainLeft);
2550     XtSetArg (arg[2], XtNright, XawChainLeft);
2551     w = XtCreateManagedWidget ("Lang", labelWidgetClass, LangWidget, arg, 3);
2552     {
2553       MPlist *plist[11], *pl;
2554       char langname[3];
2555
2556       for (i = 0; i < 11; i++) plist[i] = NULL;
2557       langname[2] = '\0';
2558       for (langname[0] = 'a'; langname[0] <= 'z'; langname[0]++)
2559         for (langname[1] = 'a'; langname[1] <= 'z'; langname[1]++)
2560           {
2561             MSymbol sym = msymbol_exist (langname);
2562             MSymbol fullname;
2563
2564             if (sym != Mnil
2565                 && ((fullname = msymbol_get (sym, Mlanguage)) != Mnil))
2566               {
2567                 char *name = msymbol_name (fullname);
2568                 char c = name[0];
2569
2570                 if (c >= 'A' && c <= 'Z')
2571                   {
2572                     int idx = (c < 'U') ? (c - 'A') / 2 : 10;
2573
2574                     pl = plist[idx];
2575                     if (! pl)
2576                       pl = plist[idx] = mplist ();
2577                     for (; mplist_next (pl); pl = mplist_next (pl))
2578                       if (strcmp (name, (char *) mplist_value (pl)) < 0)
2579                         break;
2580                     mplist_push (pl, sym, fullname);
2581                   }
2582               }
2583           }
2584
2585       for (i = 0; i < 11; i++)
2586         if (plist[i])
2587           {
2588             char *name = malloc (9);
2589
2590             sprintf (name, "Menu %c-%c", 'A' + i * 2, 'A' + i * 2 + 1);
2591             if (i == 10)
2592               name[7] = 'Z';
2593             for (j = 0, pl = plist[i]; mplist_next (pl);
2594                  j++, pl = mplist_next (pl))
2595               SetMenu (menus[j], 0, msymbol_name ((MSymbol) mplist_value (pl)),
2596                        msymbol_name (mplist_key (pl)),
2597                        LangProc, mplist_key (pl), -1);
2598             w = create_menu_button (ShellWidget, LangWidget, w, name + 5, name,
2599                                     menus, j, "Push language property");
2600           }
2601       for (i = 0; i < 11; i++)
2602         if (plist[i])
2603           m17n_object_unref (plist[i]);
2604     }
2605     XtSetArg (arg[0], XtNfromHoriz, w);
2606     XtSetArg (arg[1], XtNleft, XawChainLeft);
2607     XtSetArg (arg[2], XtNright, XawChainLeft);
2608     XtSetArg (arg[3], XtNhorizDistance, 10);
2609     XtSetArg (arg[4], XtNlabel, "Pop");
2610     XtSetArg (arg[5], XtNtranslations,
2611               XtParseTranslationTable (pop_lang_trans));
2612     w = XtCreateManagedWidget ("Pop Lang", commandWidgetClass,
2613                                LangWidget, arg, 6);
2614     XtAddCallback (w, XtNcallback, LangProc, Mnil);
2615
2616     XtSetArg (arg[0], XtNfromHoriz, w);
2617     XtSetArg (arg[1], XtNleft, XawChainLeft);
2618     XtSetArg (arg[2], XtNright, XawChainRight);
2619     XtSetArg (arg[3], XtNlabel, "");
2620     XtSetArg (arg[4], XtNborderWidth, 0);
2621     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2622     CurLangWidget = XtCreateManagedWidget ("Current Lang", labelWidgetClass,
2623                                            LangWidget, arg, 6);
2624   }
2625
2626   XtSetArg (arg[0], XtNheight, win_height);
2627   XtSetArg (arg[1], XtNwidth, 10);
2628   XtSetArg (arg[2], XtNleft, XawChainLeft);
2629   XtSetArg (arg[3], XtNright, XawChainLeft);
2630   SbarWidget = XtCreateManagedWidget ("sbar", scrollbarWidgetClass, BodyWidget,
2631                                       arg, 4);
2632   XtAddCallback (SbarWidget, XtNscrollProc, ScrollProc, NULL);
2633   XtAddCallback (SbarWidget, XtNjumpProc, JumpProc, NULL);
2634
2635   XtSetArg (arg[0], XtNheight, win_height);
2636   XtSetArg (arg[1], XtNwidth, win_width);
2637   XtSetArg (arg[2], XtNtranslations, XtParseTranslationTable (trans));
2638   XtSetArg (arg[3], XtNfromHoriz, SbarWidget);
2639   XtSetArg (arg[4], XtNleft, XawChainLeft);
2640   XtSetArg (arg[5], XtNright, XawChainRight);
2641   TextWidget = XtCreateManagedWidget ("text", simpleWidgetClass, BodyWidget,
2642                                       arg, 5);
2643
2644   XtSetArg (arg[0], XtNborderWidth, 0);
2645   XtSetArg (arg[1], XtNleft, XawChainLeft);
2646   XtSetArg (arg[2], XtNright, XawChainRight);
2647   XtSetArg (arg[3], XtNresizable, True);
2648   XtSetArg (arg[4], XtNjustify, XtJustifyLeft);
2649   MessageWidget = XtCreateManagedWidget ("message", labelWidgetClass,
2650                                          TailWidget, arg, 5);
2651
2652   memset (&control, 0, sizeof control);
2653   control.two_dimensional = 1;
2654   control.enable_bidi = 1;
2655   control.min_line_ascent = font_ascent;
2656   control.min_line_descent = font_descent;
2657   control.max_line_width = win_width;
2658   control.with_cursor = 1;
2659   control.cursor_width = 2;
2660   control.partial_update = 1;
2661   control.ignore_formatting_char = 1;
2662
2663   memset (&input_status_control, 0, sizeof input_status_control);
2664   input_status_control.enable_bidi = 1;
2665
2666   XtAppAddActions (context, actions, XtNumber (actions));
2667   XtRealizeWidget (ShellWidget);
2668
2669   win = XtWindow (TextWidget);
2670
2671   XtAppMainLoop (context);
2672
2673   if (current_input_context)
2674     minput_destroy_ic (current_input_context);
2675   for (i = 0; i < num_input_methods; i++)
2676     minput_close_im (input_method_table[i]);
2677   m17n_object_unref (frame);
2678
2679   m17n_object_unref (mt);
2680   m17n_object_unref (face_xxx_large);
2681   m17n_object_unref (face_box);
2682   m17n_object_unref (face_courier);
2683   m17n_object_unref (face_helvetica);
2684   m17n_object_unref (face_times);
2685   m17n_object_unref (face_dv_ttyogesh);
2686   m17n_object_unref (face_freesans);
2687   m17n_object_unref (face_freemono);
2688   m17n_object_unref (face_default_fontset);
2689   m17n_object_unref (face_no_ctl_fontset);
2690   m17n_object_unref (face_input_status);
2691   m17n_object_unref (selection);
2692
2693   M17N_FINI ();
2694
2695   free (fontset_name);
2696
2697   XtUninstallTranslations (form);
2698   XtUninstallTranslations (TextWidget);
2699   XtDestroyWidget (ShellWidget);
2700   XtDestroyApplicationContext (context);
2701
2702   exit (0);
2703 }
2704 #endif /* not FOR_DOXYGEN */