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