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