(redraw_cursor): Fix checking of redrawing area.
[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 && *type != XA_UTF8_STRING)
1074     {
1075       XtGetSelectionValue (w, XA_PRIMARY, XA_UTF8_STRING, get_selection, NULL,
1076                            CurrentTime);
1077       goto err;
1078     }
1079   if (this_mt)
1080     {
1081       hide_cursor ();
1082       insert_chars (this_mt);
1083       m17n_object_unref (this_mt);
1084     }
1085
1086  err:
1087   if (value)
1088     XtFree (value);
1089 }
1090
1091 static void
1092 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
1093 {
1094   XExposeEvent *expose = (XExposeEvent *) event;
1095
1096   if (top.from < 0)
1097     {
1098       Dimension width_max, width;
1099
1100       XtSetArg (arg[0], XtNwidth, &width);
1101       XtGetValues (XtParent (w), arg, 1);
1102       width_max = width;
1103       XtGetValues (HeadWidget, arg, 1);
1104       if (width_max < width)
1105         width_max = width;
1106       XtGetValues (FaceWidget, arg, 1);
1107       if (width_max < width)
1108         width_max = width;
1109       XtGetValues (LangWidget, arg, 1);
1110       if (width_max < width)
1111         width_max = width;
1112       XtSetArg (arg[0], XtNwidth, width_max);
1113       XtSetValues (HeadWidget, arg, 1);
1114       XtSetValues (FaceWidget, arg, 1);
1115       XtSetValues (LangWidget, arg, 1);
1116       XtSetValues (XtParent (w), arg, 1);
1117       XtSetValues (TailWidget, arg, 1);
1118
1119       update_top (0);
1120       update_cursor (0, 1);
1121       redraw (0, win_height, 0, 1);
1122       show_cursor (NULL);
1123     }
1124   else
1125     {
1126       redraw (expose->y, expose->y + expose->height, 0, 0);
1127       if (current_input_context
1128           && expose->y < cur.y0 && expose->y + expose->height < cur.y1)
1129         set_input_method_spot ();
1130     }
1131 }
1132
1133 static void
1134 ConfigureProc (Widget w, XEvent *event, String *str, Cardinal *num)
1135 {
1136   XConfigureEvent *configure = (XConfigureEvent *) event;
1137   
1138   hide_cursor ();
1139   control.max_line_width = win_width = configure->width;
1140   win_height = configure->height;
1141   mdraw_clear_cache (mt);
1142   update_top (0);
1143   update_cursor (0, 1);
1144   redraw (0, win_height, 1, 1);
1145   if (current_input_context)
1146     set_input_method_spot ();
1147 }
1148
1149 static void
1150 ButtonProc (Widget w, XEvent *event, String *str, Cardinal *num)
1151 {
1152   int pos;
1153   int x = event->xbutton.x;
1154   int y = event->xbutton.y - top.ascent;
1155
1156   if (control.orientation_reversed)
1157     x -= win_width;
1158   pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
1159   if (SELECTEDP ())
1160     {
1161       XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1162       mtext_detach_property (selection);
1163       redraw (sel_start.y0, sel_end.y1, 1, 0);
1164     }
1165   hide_cursor ();
1166   update_cursor (pos, 0);
1167 }
1168
1169
1170 static void
1171 ButtonReleaseProc (Widget w, XEvent *event, String *str, Cardinal *num)
1172 {
1173   if (! SELECTEDP ())
1174     return;
1175
1176   XtOwnSelection (w, XA_PRIMARY, CurrentTime,
1177                   covert_selection, lose_selection, NULL);
1178   update_cursor (mtext_property_start (selection), 0);
1179 }
1180
1181 static
1182 void
1183 Button2Proc (Widget w, XEvent *event, String *str, Cardinal *num)
1184 {
1185   if (! SELECTEDP ())
1186     {
1187       /* We don't have a local selection.  */
1188       XtGetSelectionValue (w, XA_PRIMARY, XA_TEXT, get_selection, NULL,
1189                            CurrentTime);
1190     }
1191   else
1192     {
1193       int from = mtext_property_start (selection);
1194       int to = mtext_property_end (selection);
1195       MText *this_mt;
1196       int pos;
1197       int x = event->xbutton.x;
1198       int y = event->xbutton.y - top.ascent;
1199
1200       if (control.orientation_reversed)
1201         x -= win_width;
1202       pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
1203       
1204       XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1205       mtext_detach_property (selection);
1206       hide_cursor ();
1207       this_mt = mtext_copy (mtext (), 0, mt, from, to);
1208       update_cursor (pos, 0);
1209       insert_chars (this_mt);
1210       m17n_object_unref (this_mt);
1211     }
1212 }
1213
1214 static void
1215 ButtonMoveProc (Widget w, XEvent *event, String *str, Cardinal *num)
1216 {
1217   int pos;
1218   int x = event->xbutton.x;
1219   int y = event->xbutton.y;
1220
1221   if (control.orientation_reversed)
1222     x -= win_width;
1223   if (y < cur.y0)
1224     pos = top.from, y -= top.ascent;
1225   else
1226     pos = cur.from, y -= cur.y0 + cur.ascent;
1227   pos = COORDINATES_POSITION (pos, nchars + 1, x, y);
1228
1229   if (pos == cursor.from)
1230     return;
1231
1232   hide_cursor ();
1233   if (SELECTEDP ())
1234     {
1235       /* Selection range changed.  */
1236       int from = mtext_property_start (selection);
1237       int to = mtext_property_end (selection);
1238       int start_y0 = sel_start.y0, start_y1 = sel_start.y1;
1239       int end_y0 = sel_end.y0, end_y1 = sel_end.y1;
1240
1241       if (cursor.from == from)
1242         {
1243           /* Starting position changed.  */
1244           if (pos <= from)
1245             {
1246               /* Enlarged.  We can simply overdraw.  */
1247               select_region (pos, to);
1248               redraw (sel_start.y0, start_y1, 0, 0);
1249             }
1250           else if (pos < to)
1251             {
1252               /* Shrunken.  Previous selection face must be cleared.  */
1253               select_region (pos, to);
1254               redraw (start_y0, sel_start.y1, 1, 0);
1255             }
1256           else if (pos == to)
1257             {
1258               /* Shrunken to zero.  */
1259               XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1260               mtext_detach_property (selection);
1261               redraw (start_y0, end_y1, 1, 0);
1262             }
1263           else
1264             {
1265               /* Full update is necessary.  */
1266               select_region (to, pos);
1267               redraw (start_y0, sel_end.y1, 1, 0);
1268             }
1269         }
1270       else
1271         {
1272           /* Ending position changed.  */
1273           if (pos < from)
1274             {
1275               /* Full update is necessary.  */
1276               select_region (pos, from);
1277               redraw (sel_start.y0, end_y1, 1, 0);
1278             }
1279           else if (pos == from)
1280             {
1281               /* Shrunken to zero.  */
1282               XtDisownSelection (w, XA_PRIMARY, CurrentTime);
1283               mtext_detach_property (selection);
1284               redraw (start_y0, end_y1, 1, 0);
1285             }
1286           else if (pos < to)
1287             {
1288               /* Shrunken.  Previous selection face must be cleared.  */
1289               select_region (from, pos);
1290               redraw (sel_end.y0, end_y1, 1, 0);
1291             }
1292           else
1293             {
1294               /* Enlarged.  We can simply overdraw.  */
1295               select_region (from, pos);
1296               redraw (end_y0, sel_end.y1, 0, 0);
1297             }
1298         }
1299     }
1300   else
1301     {
1302       /* Newly selected.  */
1303       select_region (pos, cursor.from);
1304       redraw (sel_start.y0, sel_end.y1, 0, 0);
1305     }
1306   update_cursor (pos, 1);
1307 }
1308
1309 void
1310 ScrollProc (Widget w, XtPointer client_data, XtPointer position)
1311 {
1312   int from;
1313   MDrawGlyphInfo info;
1314   int height;
1315   int cursor_pos = cursor.from;
1316
1317   if (((int) position) < 0)
1318     {
1319       /* Scroll down.  */
1320       int pos;
1321
1322       from = top.from;
1323       height = top.y1 - top.y0;
1324       while (from > 0)
1325         {
1326           pos = bol (from - 1, 0);
1327           GLYPH_INFO (pos, from - 1, info);
1328           if (height + info.this.height > win_height)
1329             break;
1330           height += info.this.height;
1331           from = info.line_from;
1332         }
1333       if (cursor_pos >= top.to)
1334         {
1335           cursor_pos = top.from;
1336           pos = top.to;
1337           while (cursor_pos < nchars)
1338             {
1339               GLYPH_INFO (pos, pos, info);
1340               if (height + info.this.height > win_height)
1341                 break;
1342               height += info.this.height;
1343               cursor_pos = pos;
1344               pos = info.line_to;
1345             }
1346         }
1347     }
1348   else if (cur.to < nchars)
1349     {
1350       /* Scroll up, but leave at least one line.  */
1351       from = cur.to;
1352       height = cur.y1;
1353       while (from < nchars)
1354         {
1355           GLYPH_INFO (from, from, info);
1356           if (height + info.this.height > win_height
1357               || info.line_to >= nchars)
1358             break;
1359           height += info.this.height;
1360           from = info.line_to;
1361         }
1362       if (from == nchars)
1363         from = info.line_from;
1364       if (cursor_pos < from)
1365         cursor_pos = from;
1366     }
1367   else
1368     /* Scroll up to make the cursor line top.  */
1369     from = cur.from;
1370   hide_cursor ();
1371   reseat (from);
1372   update_cursor (cursor_pos, 1);
1373 }
1374
1375 void
1376 JumpProc (Widget w, XtPointer client_data, XtPointer persent_ptr)
1377 {
1378   float persent = *(float *) persent_ptr;
1379   int pos1, pos2 = nchars * persent;
1380   MDrawGlyphInfo info;
1381
1382   hide_cursor ();
1383   pos1 = bol (pos2, 0);
1384   GLYPH_INFO (pos1, pos2, info);
1385   pos1 = info.line_from;
1386   reseat (pos1);
1387   update_cursor (pos1, 1);
1388 }
1389
1390
1391 static void
1392 KeyProc (Widget w, XEvent *event, String *str, Cardinal *num)
1393 {
1394   XKeyEvent *key_event = (XKeyEvent *) event;
1395   char buf[512];
1396   KeySym keysym = NoSymbol;
1397   int ret;
1398   /* If set to 1, do not update target_x_position.  */
1399   int keep_target_x_position = 0;
1400   MText *produced;
1401
1402   if (current_input_context
1403       && minput_filter (current_input_context, Mnil, event))
1404     return;
1405   if (event->type == KeyRelease)
1406     return;
1407
1408   hide_cursor ();
1409
1410   produced = mtext ();
1411   ret = minput_lookup (current_input_context, Mnil, event, produced);
1412   if (mtext_len (produced) > 0)
1413     insert_chars (produced);
1414   if (ret)
1415     ret = XLookupString (key_event, buf, sizeof (buf), &keysym, NULL);
1416   m17n_object_unref (produced);
1417
1418   switch (keysym)
1419     {
1420     case XK_Delete:
1421       {
1422         int n = 0;
1423
1424         if (SELECTEDP ())
1425           {
1426             n = (mtext_property_end (selection)
1427                  - mtext_property_start (selection));
1428             mtext_detach_property (selection);
1429           }
1430         else if (cursor.from < nchars)
1431           {
1432             /* Delete the following grapheme cluster.  */
1433             n = cursor.to - cursor.from;
1434           }
1435         if (n != 0)
1436           delete_char (n);
1437       }
1438       break;
1439
1440     case XK_BackSpace:
1441       {
1442         int n = 0;
1443
1444         if (SELECTEDP ())
1445           {
1446             /* Delete selected region.  */
1447             n = (mtext_property_end (selection)
1448                  - mtext_property_start (selection));
1449             mtext_detach_property (selection);
1450           }
1451         else if (cursor.from > 0)
1452           {
1453             /* Delete the preceding character.  */
1454             n = -1;
1455           }
1456         if (n != 0)
1457           delete_char (n);
1458       }
1459       break;
1460
1461     case XK_Left:
1462       if (SELECTEDP ())
1463         {
1464           mtext_detach_property (selection);
1465           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1466         }
1467       if (logical_move)
1468         {
1469           if (cursor.prev_from >= 0)
1470             update_cursor (cursor.prev_from, 0);
1471         }
1472       else
1473         {
1474           if (cursor.left_from >= 0)
1475             update_cursor (cursor.left_from, 0);
1476         }
1477       break;
1478
1479     case XK_Right:
1480       if (SELECTEDP ())
1481         {
1482           mtext_detach_property (selection);
1483           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1484         }
1485       if (logical_move)
1486         {
1487           if (cursor.next_to >= 0)
1488             update_cursor (cursor.to, 0);
1489         }
1490       else
1491         {
1492           if (cursor.right_from >= 0)
1493             update_cursor (cursor.right_from, 0);
1494         }
1495       break;
1496
1497     case XK_Down:
1498       if (SELECTEDP ())
1499         {
1500           mtext_detach_property (selection);
1501           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1502         }
1503       if (cur.to <= nchars)
1504         {
1505           MDrawGlyphInfo info;
1506           int pos;
1507
1508           GLYPH_INFO (cur.from, cur.to, info);
1509           pos = COORDINATES_POSITION (cur.from, nchars + 1,
1510                                       target_x_position, info.y);
1511           keep_target_x_position = 1;
1512           update_cursor (pos, 0);
1513         }
1514       break;
1515
1516     case XK_Up:
1517       if (SELECTEDP ())
1518         {
1519           mtext_detach_property (selection);
1520           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1521         }
1522       if (cur.from > 0)
1523         {
1524           MDrawMetric rect;
1525           int y;
1526           int pos = bol (cur.from - 1, 0);
1527
1528           TEXT_EXTENTS (pos, cur.from - 1, rect);
1529           y = rect.height + rect.y - 1;
1530           pos = COORDINATES_POSITION (pos, nchars,
1531                                       target_x_position, y);
1532           keep_target_x_position = 1;
1533           update_cursor (pos, 0);
1534         }
1535       break;
1536
1537     case XK_Page_Down:
1538       if (SELECTEDP ())
1539         {
1540           mtext_detach_property (selection);
1541           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1542         }
1543       if (top.from < nchars)
1544         ScrollProc (w, NULL, (XtPointer) 1);
1545       break;
1546
1547     case XK_Page_Up:
1548       if (SELECTEDP ())
1549         {
1550           mtext_detach_property (selection);
1551           redraw (sel_start.y0, sel_end.y1, 1, 0);;
1552         }
1553       if (top.from > 0)
1554         ScrollProc (w, NULL, (XtPointer) -1);
1555       break;
1556
1557     default:
1558       if (ret > 0)
1559         {
1560           if (buf[0] == 17) /* C-q */
1561             {
1562               XtAppSetExitFlag (context);
1563               return;
1564             }
1565           else if (buf[0] == 12) /* C-l */
1566             {
1567               redraw (0, win_height, 1, 1);
1568               return;
1569             }
1570           else
1571             {
1572               MText *temp = mtext ();
1573
1574               mtext_cat_char (temp, buf[0] == '\r' ? '\n' : buf[0]);
1575               if (current_input_context)
1576                 mtext_put_prop (temp, 0, 1, Mlanguage,
1577                                 current_input_context->im->language);
1578               insert_chars (temp);
1579               m17n_object_unref (temp);
1580             }
1581         }
1582     }
1583
1584   if (! keep_target_x_position)
1585     target_x_position = cursor.x;
1586 }
1587
1588 void
1589 SaveProc (Widget w, XtPointer client_data, XtPointer call_data)
1590 {
1591   char *name = (char *) client_data;
1592   FILE *fp;
1593   int from = -1, to = 0;
1594   
1595   if (name)
1596     {
1597       free (filename);
1598       filename = strdup (name);
1599     }
1600
1601   fp = fopen (filename, "w");
1602   if (! fp)
1603     {
1604       fprintf (stderr, "Open for write fail: %s", filename);
1605       return;
1606     }
1607
1608   if (SELECTEDP ())
1609     {
1610       from = mtext_property_start (selection);
1611       to = mtext_property_end (selection);
1612       mtext_detach_property (selection);
1613     }
1614
1615   mconv_encode_stream (Mcoding_utf_8, mt, fp);
1616   fclose (fp);
1617   if (from >= 0)
1618     select_region (from, to);
1619 }
1620
1621 void
1622 SerializeProc (Widget w, XtPointer client_data, XtPointer call_data)
1623 {
1624   MText *new;
1625
1626   hide_cursor ();
1627   if (SELECTEDP ())
1628     mtext_detach_property (selection);
1629   serialized = (int) client_data;
1630   if (! serialized)
1631     new = mtext_deserialize (mt);
1632   else
1633     {
1634       MPlist *plist = mplist ();
1635
1636       mplist_push (plist, Mt, Mface);
1637       mplist_push (plist, Mt, Mlanguage);
1638       new = mtext_serialize (mt, 0, mtext_len (mt), plist);
1639       m17n_object_unref (plist);
1640     }
1641   if (new)
1642     {
1643       m17n_object_unref (mt);
1644       mt = new;
1645       serialized = ! serialized;
1646       nchars = mtext_len (mt);
1647       update_top (0);
1648     }
1649   update_cursor (0, 1);
1650   redraw (0, win_height, 1, 1);
1651 }
1652
1653 void
1654 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
1655 {
1656   XtAppSetExitFlag (context);
1657 }
1658
1659 MText *
1660 read_file ()
1661 {
1662   FILE *fp = fopen (filename, "r");
1663
1664   if (! fp)
1665     FATAL_ERROR ("Can't read \"%s\"!\n", filename);
1666   mt = mconv_decode_stream (Mcoding_utf_8, fp);
1667   fclose (fp);
1668   if (! mt)
1669     FATAL_ERROR ("Can't decode \"%s\" by UTF-8!\n", filename);
1670   return mt;
1671 }
1672
1673 void
1674 BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
1675 {
1676   int data = (int) client_data;
1677   int i;
1678
1679   if (data == 0)
1680     {
1681       control.enable_bidi = 0;
1682       control.orientation_reversed = 0;
1683     }
1684   else
1685     {
1686       control.enable_bidi = 1;
1687       control.orientation_reversed = data == 2;
1688     }
1689   for (i = 0; i < 3; i++)
1690     {
1691       if (i == data)
1692         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1693       else
1694         XtSetArg (arg[0], XtNleftBitmap, None);
1695       XtSetValues (BidiMenus[i], arg, 1);
1696     }
1697
1698   update_cursor (cursor.from, 1);
1699   redraw (0, win_height, 1, 0);
1700 }
1701
1702 extern int line_break (MText *mt, int pos, int from, int to, int line, int y);
1703
1704 void
1705 LineBreakProc (Widget w, XtPointer client_data, XtPointer call_data)
1706 {
1707   int data = (int) client_data;
1708   int i;
1709
1710   if (data == 0)
1711     control.max_line_width = 0;
1712   else
1713     {
1714       control.max_line_width = win_width;
1715       control.line_break = (data == 1 ? NULL : line_break);
1716     }
1717   for (i = 0; i < 3; i++)
1718     {
1719       if (i == data)
1720         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1721       else
1722         XtSetArg (arg[0], XtNleftBitmap, None);
1723       XtSetValues (LineBreakMenus[i], arg, 1);
1724     }
1725
1726   update_cursor (cursor.from, 1);
1727   redraw (0, win_height, 1, 0);
1728 }
1729
1730 void
1731 CursorProc (Widget w, XtPointer client_data, XtPointer call_data)
1732 {
1733   int data = (int) client_data;
1734   int i, from, to;
1735
1736   switch (data)
1737     {
1738     case 0:
1739       logical_move = 1;
1740       from = 0, to = 2;
1741       break;
1742     case 1:
1743       logical_move = 0;
1744       from = 0, to = 2;
1745       break;
1746     case 2:
1747       control.cursor_bidi = 0, control.cursor_width = -1;
1748       from = 2, to = 5;
1749       break;
1750     case 3:
1751       control.cursor_bidi = 0, control.cursor_width = 2;
1752       from = 2, to = 5;
1753       break;
1754     default:
1755       control.cursor_bidi = 1;
1756       from = 2, to = 5;
1757       break;
1758     }
1759
1760   for (i = from; i < to; i++)
1761     {
1762       if (i == data)
1763         XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1764       else
1765         XtSetArg (arg[0], XtNleftBitmap, None);
1766       XtSetValues (CursorMenus[i], arg, 1);
1767     }
1768
1769   redraw (0, win_height, 1, 0);
1770 }
1771
1772 static void
1773 InputMethodProc (Widget w, XtPointer client_data, XtPointer call_data)
1774 {
1775   int idx = (int) client_data;
1776
1777   if (idx == -2 ? current_input_method < 0
1778       : idx == -1 ? auto_input_method
1779       : idx == current_input_method)
1780     return;
1781
1782   XtSetArg (arg[0], XtNleftBitmap, None);
1783   if (auto_input_method)
1784     {
1785       XtSetValues (InputMethodMenus[1], arg, 1);
1786       auto_input_method = 0;
1787     }
1788   else if (current_input_method < 0)
1789     XtSetValues (InputMethodMenus[0], arg, 1);
1790   else
1791     XtSetValues (InputMethodMenus[current_input_method + 2], arg, 1);
1792
1793   if (idx == -1)
1794     {
1795       auto_input_method = 1;
1796       hide_cursor ();
1797     }
1798   else if (input_method_table[idx].available >= 0)
1799     {
1800       if (! input_method_table[idx].im)
1801         {
1802           input_method_table[idx].im = 
1803             minput_open_im (input_method_table[idx].language,
1804                             input_method_table[idx].name, NULL);
1805           if (! input_method_table[idx].im)
1806             input_method_table[idx].available = -1;
1807         }
1808       if (input_method_table[idx].im)
1809         select_input_method (idx);
1810     }
1811   XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
1812   XtSetValues (InputMethodMenus[idx + 2], arg, 1);
1813 }
1814
1815 MPlist *default_face_list;
1816
1817 void
1818 FaceProc (Widget w, XtPointer client_data, XtPointer call_data)
1819 {
1820   int idx = (int) client_data;
1821   int from, to;
1822   int old_y1;
1823
1824   if (! SELECTEDP ())
1825     {
1826       MPlist *plist;
1827
1828       if (idx >= 0)
1829         {
1830           MFace *face = mframe_get_prop (frame, Mface);
1831
1832           for (plist = default_face_list; mplist_key (plist) != Mnil;
1833                plist = mplist_next (plist)) 
1834             mface_merge (face, mplist_value (plist));
1835           mplist_add (plist, Mt, *face_table[idx].face);
1836           mface_merge (face, *face_table[idx].face);
1837         }
1838       else if (mplist_key (mplist_next (default_face_list)) != Mnil)
1839         {
1840           MFace *face = mframe_get_prop (frame, Mface);
1841
1842           for (plist = default_face_list;
1843                mplist_key (mplist_next (plist)) != Mnil;
1844                plist = mplist_next (plist)) 
1845             mface_merge (face, mplist_value (plist));
1846           mplist_pop (plist);
1847         }
1848       update_top (0);
1849       update_cursor (0, 1);
1850       redraw (0, win_height, 1, 1);
1851       show_cursor (NULL);
1852       return;
1853     }
1854
1855   XtAppAddWorkProc (context, show_cursor, NULL);
1856   from = mtext_property_start (selection);
1857   to = mtext_property_end (selection);
1858   old_y1 = sel_end.y1;
1859
1860   mtext_detach_property (selection);
1861   if (idx >= 0)
1862     {
1863       MTextProperty *prop = mtext_property (Mface, *face_table[idx].face,
1864                                             MTEXTPROP_REAR_STICKY);
1865       mtext_push_property (mt, from, to, prop);
1866       m17n_object_unref (prop);
1867     }
1868   else
1869     mtext_pop_prop (mt, from, to, Mface);
1870   if (from < top.to)
1871     update_top (top.from);
1872   update_cursor (cursor.from, 1);
1873   select_region (from, to);
1874   update_region (sel_start.y0, old_y1, sel_end.y1);
1875   if (cur.y1 > win_height)
1876     {
1877       while (cur.y1 > win_height)
1878         {
1879           reseat (top.to);
1880           update_cursor (cursor.from, 1);
1881         }
1882     }
1883 }
1884
1885 void
1886 LangProc (Widget w, XtPointer client_data, XtPointer call_data)
1887 {
1888   MSymbol sym = (MSymbol) client_data;
1889   int from, to;
1890   int old_y1;
1891
1892   if (! SELECTEDP ())
1893     return;
1894
1895   XtAppAddWorkProc (context, show_cursor, NULL);
1896   from = mtext_property_start (selection);
1897   to = mtext_property_end (selection);
1898   old_y1 = sel_end.y1;
1899
1900   mtext_detach_property (selection);
1901   if (sym != Mnil)
1902     mtext_put_prop (mt, from, to, Mlanguage, sym);
1903   else
1904     mtext_pop_prop (mt, from, to, Mlanguage);
1905
1906   if (from < top.to)
1907     update_top (top.from);
1908   update_cursor (cursor.from, 1);
1909   select_region (from, to);
1910   update_region (sel_start.y0, old_y1, sel_end.y1);
1911   if (cur.y1 > win_height)
1912     {
1913       while (cur.y1 > win_height)
1914         {
1915           reseat (top.to);
1916           update_cursor (cursor.from, 1);
1917         }
1918     }
1919 }
1920
1921 void
1922 DumpImageProc (Widget w, XtPointer client_data, XtPointer call_data)
1923 {
1924   int narrowed = (int) client_data;
1925   FILE *mdump;
1926   int from, to;
1927   MConverter *converter;
1928
1929   if (narrowed)
1930     {
1931       if (! SELECTEDP ())
1932         return;
1933       from = mtext_property_start (selection);
1934       to = mtext_property_end (selection);
1935     }
1936   else
1937     {
1938       from = 0;
1939       to = nchars;
1940     }
1941
1942   if (! narrowed)
1943     mdump = popen ("mdump -q -p a4", "w");
1944   else
1945     mdump = popen ("mdump -q", "w");
1946   if (! mdump)
1947     return;
1948   converter = mconv_stream_converter (Mcoding_utf_8, mdump);
1949   mconv_encode_range (converter, mt, from, to);
1950   mconv_free_converter (converter);
1951   fclose (mdump);
1952 }
1953
1954 void
1955 input_status (MInputContext *ic, MSymbol command)
1956 {
1957   XFillRectangle (display, input_status_pixmap, gc_inv,
1958                   0, 0, input_status_width, input_status_height);
1959   if (command == Minput_status_draw)
1960     {
1961       MDrawMetric rect;
1962
1963       mtext_put_prop (ic->status, 0, mtext_len (ic->status),
1964                       Mface, face_input_status);
1965       if (ic->im->language != Mnil)
1966         mtext_put_prop (ic->status, 0, mtext_len (ic->status),
1967                         Mlanguage, ic->im->language);
1968       mdraw_text_extents (frame, ic->status, 0, mtext_len (ic->status),
1969                           &input_status_control, NULL, NULL, &rect);
1970       mdraw_text_with_control (frame, (MDrawWindow) input_status_pixmap,
1971                                input_status_width - rect.width - 2, - rect.y,
1972                                ic->status, 0, mtext_len (ic->status),
1973                                &input_status_control);
1974     }
1975   XtSetArg (arg[0], XtNbitmap, input_status_pixmap);
1976   XtSetValues (CurIMStatus, arg, 1);
1977 }
1978
1979 int
1980 compare_input_method (const void *elt1, const void *elt2)
1981 {
1982   const InputMethodInfo *im1 = elt1;
1983   const InputMethodInfo *im2 = elt2;
1984   MSymbol lang1, lang2;
1985
1986   if (im1->language == Mnil)
1987     return 1;
1988   if (im1->language == im2->language)
1989     return strcmp (msymbol_name (im1->name), msymbol_name (im2->name));
1990   if (im1->language == Mt)
1991     return 1;
1992   if (im2->language == Mt)
1993     return -1;
1994   lang1 = msymbol_get (im1->language, Mlanguage);
1995   lang2 = msymbol_get (im2->language, Mlanguage);
1996   return strcmp (msymbol_name (lang1), msymbol_name (lang2));
1997 }
1998
1999 void
2000 setup_input_methods (int with_xim)
2001 {
2002   MInputMethod *im = NULL;
2003   MPlist *plist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
2004   MPlist *pl;
2005   int i = 0;
2006
2007   num_input_methods = mplist_length (plist);
2008
2009   if (with_xim)
2010     {
2011       MInputXIMArgIM arg_xim;
2012
2013       arg_xim.display = display;
2014       arg_xim.db = NULL;  
2015       arg_xim.res_name = arg_xim.res_class = NULL;
2016       arg_xim.locale = NULL;
2017       arg_xim.modifier_list = NULL;
2018       im = minput_open_im (Mnil, msymbol ("xim"), &arg_xim);
2019       if (im)
2020         num_input_methods++;
2021     }
2022   input_method_table = calloc (num_input_methods, sizeof (InputMethodInfo));
2023   if (im)
2024     {
2025       input_method_table[i].available = 1;
2026       input_method_table[i].language = Mnil;
2027       input_method_table[i].name = im->name;
2028       input_method_table[i].im = im;
2029       i++;
2030     }
2031
2032   for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl))
2033     {
2034       MDatabase *mdb = mplist_value (pl);
2035       MSymbol *tag = mdatabase_tag (mdb);
2036
2037       if (tag[1] != Mnil)
2038         {
2039           input_method_table[i].language = tag[1];
2040           input_method_table[i].name = tag[2];
2041           i++;
2042         }
2043     }
2044
2045   m17n_object_unref (plist);
2046   num_input_methods = i;
2047   qsort (input_method_table, num_input_methods, sizeof input_method_table[0],
2048          compare_input_method);
2049   current_input_context = NULL;
2050
2051   mplist_put (minput_driver->callback_list, Minput_status_start,
2052               (void *) input_status);
2053   mplist_put (minput_driver->callback_list, Minput_status_draw,
2054               (void *) input_status);
2055   mplist_put (minput_driver->callback_list, Minput_status_done,
2056               (void *) input_status);
2057 }
2058
2059
2060 static void
2061 MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num)
2062 {
2063   char *msg;
2064
2065   if (num && *num > 0)
2066     {
2067       int bytes = 0, i;
2068
2069       for (i = 0; i < *num; i++)
2070         bytes += strlen (str[i]) + 1;
2071       msg = alloca (bytes);
2072       strcpy (msg, str[0]);
2073       for (i = 1; i < *num; i++)
2074         strcat (msg, " "), strcat (msg, str[i]);
2075     }
2076   else if (cursor.from < nchars)
2077     {
2078       int c = mtext_ref_char (mt, cursor.from);
2079       char *name = mchar_get_prop (c, Mname);
2080
2081       if (! name)
2082         name = "";
2083       msg = alloca (10 + strlen (name));
2084       sprintf (msg, "U+%04X %s", c, name);
2085     }
2086   else
2087     {
2088       msg = "";
2089     }
2090   XtSetArg (arg[0], XtNlabel, msg);
2091   XtSetValues (MessageWidget, arg, 1);
2092 }
2093
2094 typedef struct
2095 {
2096   int type;
2097   char *name1, *name2;
2098   XtCallbackProc proc;
2099   XtPointer client_data;
2100   int status;
2101   Widget w;
2102 } MenuRec;
2103
2104 void PopupProc (Widget w, XtPointer client_data, XtPointer call_data);
2105
2106 void SaveProc (Widget w, XtPointer client_data, XtPointer call_data);
2107
2108 MenuRec FileMenu[] =
2109   { { 0, "Open", NULL, PopupProc, FileMenu + 0, -1 },
2110     { 0, "Save", NULL, SaveProc, NULL, -1 },
2111     { 0, "Save as", NULL, PopupProc, FileMenu + 2, -1 },
2112     { 1 },
2113     { 0, "Serialize", NULL, SerializeProc, (void *) 1, -1 },
2114     { 0, "Deserialize", NULL, SerializeProc, (void *) 0, -1 },
2115     { 1 },
2116     { 0, "Dump Image Buffer", NULL, DumpImageProc, (void *) 0, -1 },
2117     { 0, "Dump Image Region", NULL, DumpImageProc, (void *) 1, -1 },
2118     { 1 },
2119     { 0, "Quit", NULL, QuitProc, NULL, -1 } };
2120
2121 void
2122 PopupProc (Widget w, XtPointer client_data, XtPointer call_data)
2123 {
2124   MenuRec *rec = (MenuRec *) client_data;
2125   Position x, y;
2126
2127   XtSetArg (arg[0], XtNvalue, "");
2128   XtSetArg (arg[1], XtNlabel, rec->name1);
2129   XtSetValues (FileDialogWidget, arg, 2);
2130   XtTranslateCoords (w, (Position) 0, (Position) 0, &x, &y);
2131   XtSetArg (arg[0], XtNx, x + 20);
2132   XtSetArg (arg[1], XtNy, y + 10);
2133   XtSetValues (FileShellWidget, arg, 2);
2134   XtPopup (FileShellWidget, XtGrabExclusive);
2135 }
2136
2137 void
2138 FileDialogProc (Widget w, XtPointer client_data, XtPointer call_data)
2139 {
2140   FILE *fp;
2141   char *label;
2142
2143   XtPopdown (FileShellWidget);
2144   if ((int) client_data == 1)
2145     return;
2146   XtSetArg (arg[0], XtNlabel, &label);
2147   XtGetValues (FileDialogWidget, arg, 1);
2148   if (strcmp (label, FileMenu[0].name1) == 0)
2149     {
2150       /* Open a file */
2151       free (filename);
2152       filename = strdup ((char *) XawDialogGetValueString (FileDialogWidget));
2153       fp = fopen (filename, "r");
2154       hide_cursor ();
2155       m17n_object_unref (mt);
2156       if (fp)
2157         {
2158           mt = mconv_decode_stream (Mcoding_utf_8, fp);
2159           fclose (fp);
2160           if (! mt)
2161             mt = mtext ();
2162         }
2163       else
2164         mt = mtext ();
2165       serialized = 0;
2166       nchars = mtext_len (mt);
2167       update_top (0);
2168       update_cursor (0, 1);
2169       redraw (0, win_height, 1, 1);
2170     }
2171   else if (strcmp (label, FileMenu[2].name1) == 0)
2172     SaveProc (w, (XtPointer) XawDialogGetValueString (FileDialogWidget), NULL);
2173   else
2174     fprintf (stderr, "Invalid calling sequence: FileDialogProc\n");
2175 }
2176
2177 #define SetMenu(MENU, TYPE, NAME1, NAME2, PROC, DATA, STATUS)            \
2178   ((MENU).type = (TYPE), (MENU).name1 = (NAME1), (MENU).name2 = (NAME2), \
2179    (MENU).proc = (PROC), (MENU).client_data = (XtPointer) (DATA),        \
2180    (MENU).status = (STATUS))
2181
2182
2183 Widget
2184 create_menu_button (Widget top, Widget parent, Widget left, char *button_name,
2185                     char *menu_name, MenuRec *menus, int num_menus, char *help)
2186 {
2187   Widget button, menu;
2188   char *fmt = "<EnterWindow>: highlight() MenuHelp(%s)\n\
2189                <LeaveWindow>: reset() MenuHelp()\n\
2190                <BtnDown>: reset() PopupMenu()\n\
2191                <BtnUp>: highlight()"; 
2192   int i;
2193   MenuRec *m;
2194   char *trans;
2195   int max_width = 0;
2196
2197   menu = XtCreatePopupShell (menu_name, simpleMenuWidgetClass, top, NULL, 0);
2198   for (i = 0; i < num_menus; i++)
2199     {
2200       m = menus + i;
2201       if (m->type == 0)
2202         {
2203           if (m->proc)
2204             {
2205               int n = 0;
2206
2207               if (m->status >= 0)
2208                 {
2209                   XtSetArg (arg[n], XtNleftMargin, 20), n++;
2210                   if (m->status > 0)
2211                     XtSetArg (arg[n], XtNleftBitmap, CheckPixmap), n++;
2212                 }
2213               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2214                                             menu, arg, n);
2215               XtAddCallback (m->w, XtNcallback, m->proc, m->client_data);
2216             }
2217           else
2218             {
2219               XtSetArg (arg[0], XtNsensitive, False);
2220               m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
2221                                             menu, arg, 2);
2222             }
2223         }
2224       else
2225         {
2226           XtCreateManagedWidget (m->name1, smeLineObjectClass, menu, NULL, 0);
2227         }
2228       if (m->name2)
2229         max_width = 1;
2230     }
2231   trans = alloca (strlen (fmt) + strlen (help));
2232   sprintf (trans, fmt, help);
2233   XtSetArg (arg[0], XtNmenuName, menu_name);
2234   XtSetArg (arg[1], XtNtranslations, XtParseTranslationTable ((String) trans));
2235   XtSetArg (arg[2], XtNinternalWidth, 2);
2236   XtSetArg (arg[3], XtNhighlightThickness, 1);
2237   XtSetArg (arg[4], XtNleft, XawChainLeft);
2238   XtSetArg (arg[5], XtNright, XawChainLeft);
2239   i = 6;
2240   if (left)
2241     XtSetArg (arg[i], XtNfromHoriz, left), i++;
2242   button = XtCreateManagedWidget (button_name, menuButtonWidgetClass, parent,
2243                                   arg, i);
2244
2245   if (max_width)
2246     {
2247       int height, ascent, *width = alloca (sizeof (int) * num_menus);
2248       int *len = alloca (sizeof (int) * num_menus);
2249
2250       XFontSet font_set;
2251       XFontSetExtents *fontset_extents;
2252
2253       XtSetArg (arg[0], XtNfontSet, &font_set);
2254       XtGetValues (button, arg, 1);
2255
2256       fontset_extents = XExtentsOfFontSet (font_set);
2257       height = fontset_extents->max_logical_extent.height;
2258       ascent = - fontset_extents->max_logical_extent.y;
2259
2260       for (i = 0; i < num_menus; i++)
2261         if (menus[i].name2)
2262           {
2263             len[i] = strlen (menus[i].name2);
2264             width[i] = XmbTextEscapement (font_set, menus[i].name2, len[i]);
2265             if (max_width < width[i])
2266               max_width = width[i];
2267           }
2268       for (i = 0; i < num_menus; i++)
2269         if (menus[i].name2)
2270           {
2271             Pixmap pixmap = XCreatePixmap (display,
2272                                            RootWindow (display, screen),
2273                                            max_width, height, 1);
2274             XFillRectangle (display, pixmap, mono_gc_inv,
2275                             0, 0, max_width, height);
2276             XmbDrawString (display, pixmap, font_set, mono_gc,
2277                            max_width - width[i], ascent,
2278                            menus[i].name2, len[i]);
2279             XtSetArg (arg[0], XtNrightBitmap, pixmap);
2280             XtSetArg (arg[1], XtNrightMargin, max_width + 20);
2281             XtSetValues (menus[i].w, arg, 2);
2282           }
2283     }
2284
2285   return button;
2286 }
2287
2288
2289 XtActionsRec actions[] = {
2290   {"Expose", ExposeProc},
2291   {"Configure", ConfigureProc},
2292   {"Key", KeyProc},
2293   {"ButtonPress", ButtonProc},
2294   {"ButtonRelease", ButtonReleaseProc},
2295   {"ButtonMotion", ButtonMoveProc},
2296   {"Button2Press", Button2Proc},
2297   {"MenuHelp", MenuHelpProc}
2298 };
2299
2300
2301 /* Print the usage of this program (the name is PROG), and exit with
2302    EXIT_CODE.  */
2303
2304 void
2305 help_exit (char *prog, int exit_code)
2306 {
2307   char *p = prog;
2308
2309   while (*p)
2310     if (*p++ == '/')
2311       prog = p;
2312
2313   printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] FILE\n", prog);
2314   printf ("Display FILE on a window and allow users to edit it.\n");
2315   printf ("XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
2316   printf ("The following OPTIONs are available.\n");
2317   printf ("  %-13s %s", "--version", "print version number\n");
2318   printf ("  %-13s %s", "-h, --help", "print this message\n");
2319   exit (exit_code);
2320 }
2321
2322 int
2323 main (int argc, char **argv)
2324 {
2325   Widget form, BodyWidget, w;
2326   char *fontset_name = NULL;
2327   int col = 80, row = 32;
2328   /* Translation table for TextWidget.  */
2329   String trans = "<Expose>: Expose()\n\
2330                   <Configure>: Configure()\n\
2331                   <Key>: Key()\n\
2332                   <KeyUp>: Key()\n\
2333                   <Btn1Down>: ButtonPress()\n\
2334                   <Btn1Up>: ButtonRelease()\n\
2335                   <Btn1Motion>: ButtonMotion()\n\
2336                   <Btn2Down>: Button2Press()";
2337   /* Translation table for the top form widget.  */
2338   String trans2 = "<Key>: Key()\n\
2339                    <KeyUp>: Key()";
2340   String pop_face_trans
2341     = "<EnterWindow>: MenuHelp(Pop face property) highlight()\n\
2342        <LeaveWindow>: MenuHelp() reset()\n\
2343        <Btn1Down>: set()\n\
2344        <Btn1Up>: notify() unset()"; 
2345   String pop_lang_trans
2346     = "<EnterWindow>: MenuHelp(Pop language property) highlight()\n\
2347        <LeaveWindow>: MenuHelp() reset()\n\
2348        <Btn1Down>: set()\n\
2349        <Btn1Up>: notify() unset()"; 
2350   int font_width, font_ascent, font_descent;
2351   int with_xim = 0;
2352   int i, j;
2353
2354   setlocale (LC_ALL, "");
2355   /* Create the top shell.  */
2356   XtSetLanguageProc (NULL, NULL, NULL);
2357   ShellWidget = XtOpenApplication (&context, "MEdit", NULL, 0, &argc, argv,
2358                                    NULL, sessionShellWidgetClass, NULL, 0);
2359   display = XtDisplay (ShellWidget);
2360   screen = XScreenNumberOfScreen (XtScreen (ShellWidget));
2361
2362   /* Parse the remaining command line arguments.  */
2363   for (i = 1; i < argc; i++)
2364     {
2365       if (! strcmp (argv[i], "--help")
2366           || ! strcmp (argv[i], "-h"))
2367         help_exit (argv[0], 0);
2368       else if (! strcmp (argv[i], "--version"))
2369         {
2370           printf ("medit (m17n library) %s\n", VERSION);
2371           printf ("Copyright (C) 2003 AIST, JAPAN\n");
2372           exit (0);
2373         }
2374       else if (! strcmp (argv[i], "--geometry"))
2375         {
2376           i++;
2377           if (sscanf (argv[i], "%dx%d", &col, &row) != 2)
2378             help_exit (argv[0], 1);
2379         }
2380       else if (! strcmp (argv[i], "--fontset"))
2381         {
2382           i++;
2383           fontset_name = strdup (argv[i]);
2384         }
2385       else if (! strcmp (argv[i], "--with-xim"))
2386         {
2387           with_xim = 1;
2388         }
2389       else if (argv[i][0] != '-')
2390         {
2391           filename = strdup (argv[i]);
2392         }
2393       else
2394         {
2395           fprintf (stderr, "Unknown option: %s", argv[i]);
2396           help_exit (argv[0], 1);
2397         }
2398     }
2399   if (! filename)
2400     help_exit (argv[0], 1);
2401
2402   mdatabase_dir = ".";
2403   /* Initialize the m17n library.  */
2404   M17N_INIT ();
2405   if (merror_code != MERROR_NONE)
2406     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library!");
2407
2408   mt = read_file (filename);
2409   serialized = 0;
2410
2411   nchars = mtext_len (mt);
2412
2413   {
2414     MFace *face = mface ();
2415
2416     mface_put_prop (face, Mforeground, msymbol ("blue"));
2417     mface_put_prop (face, Mbackground, msymbol ("yellow"));
2418     mface_put_prop (face, Mvideomode, Mreverse);
2419     selection = mtext_property (Mface, face, MTEXTPROP_NO_MERGE);
2420     m17n_object_unref (face);
2421   }
2422
2423   /* This tells ExposeProc to initialize everything.  */
2424   top.from = -1;
2425   
2426   XA_TEXT = XInternAtom (display, "TEXT", False);
2427   XA_COMPOUND_TEXT = XInternAtom (display, "COMPOUND_TEXT", False);
2428   XA_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
2429   Mcoding_compound_text = mconv_resolve_coding (msymbol ("compound-text"));
2430   if (Mcoding_compound_text == Mnil)
2431     FATAL_ERROR ("%s\n", "Don't know about COMPOUND-TEXT encoding!");
2432
2433   {
2434     MPlist *plist = mplist ();
2435     MFace *face;
2436     MFont *font;
2437
2438     mplist_put (plist, msymbol ("widget"), ShellWidget);
2439     if (fontset_name)
2440       {
2441         MFontset *fontset = mfontset (fontset_name);
2442         
2443         face = mface ();
2444         mface_put_prop (face, Mfontset, fontset);
2445         m17n_object_unref (fontset);
2446         mplist_add (plist, Mface, face);
2447         m17n_object_unref (face);
2448       }
2449     frame = mframe (plist);
2450     if (! frame)
2451       FATAL_ERROR ("%s\n", "Fail to create a frame!");
2452     m17n_object_unref (plist);
2453     face_default = mface_copy ((MFace *) mframe_get_prop (frame, Mface));
2454     default_face_list = mplist ();
2455     mplist_add (default_face_list, Mt, face_default);
2456     face_default_fontset = mface ();
2457     mface_put_prop (face_default_fontset, Mfontset,
2458                     mface_get_prop (face_default, Mfontset));
2459
2460     font = (MFont *) mframe_get_prop (frame, Mfont);
2461     default_font_size = (int) mfont_get_prop (font, Msize);
2462   }
2463
2464   font_width = (int) mframe_get_prop (frame, Mfont_width);
2465   font_ascent = (int) mframe_get_prop (frame, Mfont_ascent);
2466   font_descent = (int) mframe_get_prop (frame, Mfont_descent);
2467   win_width = font_width * col;
2468   win_height = (font_ascent + font_descent) * row;
2469
2470   {
2471     MFaceBoxProp prop;
2472
2473     prop.width = 4;
2474     prop.color_top = prop.color_left = msymbol ("magenta");
2475     prop.color_bottom = prop.color_right = msymbol ("red");
2476     prop.inner_hmargin = prop.inner_vmargin = 1;
2477     prop.outer_hmargin = prop.outer_vmargin = 2;
2478
2479     face_box = mface ();
2480     mface_put_prop (face_box, Mbox, &prop);
2481   }
2482
2483   face_courier = mface ();
2484   mface_put_prop (face_courier, Mfamily, msymbol ("courier"));
2485   face_helvetica = mface ();
2486   mface_put_prop (face_helvetica, Mfamily, msymbol ("helvetica"));
2487   face_times = mface ();
2488   mface_put_prop (face_times, Mfamily, msymbol ("times"));
2489   face_dv_ttyogesh = mface ();
2490   mface_put_prop (face_dv_ttyogesh, Mfamily, msymbol ("dv-ttyogesh"));
2491   face_freesans = mface ();
2492   mface_put_prop (face_freesans, Mfamily, msymbol ("freesans"));
2493   face_freeserif = mface ();
2494   mface_put_prop (face_freeserif, Mfamily, msymbol ("freeserif"));
2495   face_freemono = mface ();
2496   mface_put_prop (face_freemono, Mfamily, msymbol ("freemono"));
2497
2498   face_xxx_large = mface ();
2499   mface_put_prop (face_xxx_large, Mratio, (void *) 300);
2500   {
2501     MFont *latin_font = mframe_get_prop (frame, Mfont);
2502     MFont *dev_font = mfont ();
2503     MFont *thai_font = mfont ();
2504     MFont *tib_font = mfont ();
2505     MFontset *fontset;
2506     MSymbol unicode_bmp = msymbol ("unicode-bmp");
2507     MSymbol no_ctl = msymbol ("no-ctl");
2508
2509     mfont_put_prop (dev_font, Mfamily, msymbol ("raghindi"));
2510     mfont_put_prop (dev_font, Mregistry, unicode_bmp);
2511     mfont_put_prop (thai_font, Mfamily, msymbol ("norasi"));
2512     mfont_put_prop (thai_font, Mregistry, unicode_bmp);
2513     mfont_put_prop (tib_font, Mfamily, msymbol ("mtib"));
2514     mfont_put_prop (tib_font, Mregistry, unicode_bmp);
2515
2516     fontset = mfontset_copy (mfontset (fontset_name), "no-ctl");
2517     mfontset_modify_entry (fontset, msymbol ("latin"), Mnil, Mnil,
2518                            latin_font, Mnil, 0);
2519     mfontset_modify_entry (fontset, msymbol ("devanagari"), Mnil, Mnil,
2520                            dev_font, no_ctl, 0);
2521     mfontset_modify_entry (fontset, msymbol ("thai"), Mnil, Mnil,
2522                            thai_font, no_ctl, 0);
2523     mfontset_modify_entry (fontset, msymbol ("tibetan"), Mnil, Mnil,
2524                            tib_font, no_ctl, 0);
2525     face_no_ctl_fontset = mface ();
2526     mface_put_prop (face_no_ctl_fontset, Mfontset, fontset);
2527     m17n_object_unref (fontset);
2528
2529     free (dev_font);
2530     free (thai_font);
2531     free (tib_font);
2532   }
2533
2534   setup_input_methods (with_xim);
2535
2536   gc = DefaultGC (display, screen);
2537
2538   XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans2));
2539   XtSetArg (arg[1], XtNdefaultDistance, 2);
2540   form = XtCreateManagedWidget ("form", formWidgetClass, ShellWidget, arg, 2);
2541
2542   XtSetArg (arg[0], XtNborderWidth, 0);
2543   XtSetArg (arg[1], XtNdefaultDistance, 2);
2544   XtSetArg (arg[2], XtNtop, XawChainTop);
2545   XtSetArg (arg[3], XtNbottom, XawChainTop);
2546   XtSetArg (arg[4], XtNleft, XawChainLeft);
2547   XtSetArg (arg[5], XtNright, XawChainRight);
2548   XtSetArg (arg[6], XtNresizable, True);
2549   HeadWidget = XtCreateManagedWidget ("head", formWidgetClass, form, arg, 7);
2550   XtSetArg (arg[7], XtNfromVert, HeadWidget);
2551   FaceWidget = XtCreateManagedWidget ("face", formWidgetClass, form, arg, 8);
2552   XtSetArg (arg[7], XtNfromVert, FaceWidget);
2553   LangWidget = XtCreateManagedWidget ("lang", formWidgetClass, form, arg, 8);
2554   XtSetArg (arg[3], XtNbottom, XawChainBottom);
2555   XtSetArg (arg[7], XtNfromVert, LangWidget);
2556   BodyWidget = XtCreateManagedWidget ("body", formWidgetClass, form, arg, 8);
2557   XtSetArg (arg[2], XtNtop, XawChainBottom);
2558   XtSetArg (arg[7], XtNfromVert, BodyWidget);
2559   TailWidget = XtCreateManagedWidget ("tail", formWidgetClass, form, arg, 8);
2560
2561   FileShellWidget = XtCreatePopupShell ("FileShell", transientShellWidgetClass,
2562                                         HeadWidget, NULL, 0);
2563   XtSetArg (arg[0], XtNvalue, "");
2564   FileDialogWidget = XtCreateManagedWidget ("File", dialogWidgetClass,
2565                                             FileShellWidget, arg, 1);
2566   XawDialogAddButton (FileDialogWidget, "OK",
2567                       FileDialogProc, (XtPointer) 0);
2568   XawDialogAddButton (FileDialogWidget, "CANCEL",
2569                       FileDialogProc, (XtPointer) 1);
2570
2571   CheckPixmap = XCreateBitmapFromData (display, RootWindow (display, screen),
2572                                        (char *) check_bits,
2573                                        check_width, check_height);
2574   {
2575     unsigned long valuemask = GCForeground;
2576     XGCValues values;
2577
2578     values.foreground = 1;
2579     mono_gc = XCreateGC (display, CheckPixmap, valuemask, &values);
2580     values.foreground = 0;
2581     mono_gc_inv = XCreateGC (display, CheckPixmap, valuemask, &values);
2582   }
2583
2584   {
2585     MenuRec *menus;
2586     int num_menus = 10;
2587
2588     if (num_menus < num_input_methods + 2)
2589       num_menus = num_input_methods + 2;
2590     if (num_menus < num_faces + 1)
2591       num_menus = num_faces + 1;
2592     menus = alloca (sizeof (MenuRec) * num_menus);
2593
2594     w = create_menu_button (ShellWidget, HeadWidget, NULL, "File", "File Menu",
2595                             FileMenu, sizeof FileMenu / sizeof (MenuRec),
2596                             "File I/O, Serialization, Image, Quit");
2597
2598     SetMenu (menus[0], 0, "Logical Move", NULL, CursorProc, 0, 1);
2599     SetMenu (menus[1], 0, "Visual Move", NULL, CursorProc, 1, 0);
2600     SetMenu (menus[2], 1, "", NULL, NULL, NULL, 0);
2601     SetMenu (menus[3], 0, "Box type", NULL, CursorProc, 2, 0);
2602     SetMenu (menus[4], 0, "Bar type", NULL, CursorProc, 3, 1);
2603     SetMenu (menus[5], 0, "Bidi type", NULL, CursorProc, 4, 0);
2604     w = create_menu_button (ShellWidget, HeadWidget, w,
2605                             "Cursor", "Cursor Menu",
2606                             menus, 6, "Cursor Movement Mode, Cursor Shape");
2607     CursorMenus[0] = menus[0].w;
2608     CursorMenus[1] = menus[1].w;
2609     CursorMenus[2] = menus[3].w;
2610     CursorMenus[3] = menus[4].w;
2611     CursorMenus[4] = menus[5].w;
2612
2613     SetMenu (menus[0], 0, "disable", NULL, BidiProc, 0, 0);
2614     SetMenu (menus[1], 0, "Left  (|--> |)", NULL, BidiProc, 1, 1);
2615     SetMenu (menus[2], 0, "Right (| <--|)", NULL, BidiProc, 2, 0);
2616     w = create_menu_button (ShellWidget, HeadWidget, w, "Bidi", "Bidi Menu",
2617                             menus, 3, "BIDI Processing Mode");
2618     for (i = 0; i < 3; i++)
2619       BidiMenus[i] = menus[i].w;
2620
2621     SetMenu (menus[0], 0, "truncate", NULL, LineBreakProc, 0, 0);
2622     SetMenu (menus[1], 0, "break at edge", NULL, LineBreakProc, 1, 1);
2623     SetMenu (menus[2], 0, "break at word boundary", NULL, LineBreakProc, 2, 0);
2624     w = create_menu_button (ShellWidget, HeadWidget, w, "LineBreak",
2625                             "LineBreak Menu",
2626                             menus, 3, "How to break lines");
2627     for (i = 0; i < 3; i++)
2628       LineBreakMenus[i] = menus[i].w;
2629
2630     SetMenu (menus[0], 0, "none", NULL, InputMethodProc, -2, 1);
2631     SetMenu (menus[1], 0, "auto", NULL, InputMethodProc, -1, 0);
2632     for (i = 0; i < num_input_methods; i++)
2633       {
2634         InputMethodInfo *im = input_method_table + i;
2635         char *name1, *name2;
2636
2637         if (im->language != Mnil && im->language != Mt)
2638           {
2639             MSymbol sym = msymbol_get (im->language, Mlanguage);
2640             if (sym == Mnil)
2641               name1 = msymbol_name (im->language);
2642             else
2643               name1 = msymbol_name (sym);
2644             name2 = msymbol_name (im->name);
2645           }
2646         else
2647           name1 = msymbol_name (im->name), name2 = NULL;
2648
2649         SetMenu (menus[i + 2], 0, name1, name2, InputMethodProc, i, 0);
2650       }
2651     w = create_menu_button (ShellWidget, HeadWidget, w, "InputMethod",
2652                             "Input Method Menu", menus, i + 2,
2653                             "Select input method");
2654
2655     {
2656       unsigned long valuemask = GCForeground;
2657       XGCValues values;
2658
2659       XtSetArg (arg[0], XtNbackground, &values.foreground);
2660       XtGetValues (w, arg, 1);
2661       gc_inv = XCreateGC (display, RootWindow (display, screen),
2662                           valuemask, &values);
2663     }
2664
2665     InputMethodMenus = malloc (sizeof (Widget) * (num_input_methods + 2));
2666     for (i = 0; i < num_input_methods + 2; i++)
2667       InputMethodMenus[i] = menus[i].w;
2668
2669     input_status_width = font_width * 8;
2670     input_status_height = (font_ascent + font_descent) * 2.4;
2671     input_status_pixmap = XCreatePixmap (display, RootWindow (display, screen),
2672                                          input_status_width,
2673                                          input_status_height,
2674                                          DefaultDepth (display, screen));
2675     {
2676       MFaceBoxProp prop;
2677
2678       prop.width = 1;
2679       prop.color_top = prop.color_bottom
2680         = prop.color_left = prop.color_right = Mnil;
2681       prop.inner_hmargin = prop.inner_vmargin = 1;
2682       prop.outer_hmargin = prop.outer_vmargin = 0;
2683       face_input_status = mface_copy (face_default);
2684       mface_put_prop (face_input_status, Mbox, &prop);
2685     }
2686
2687     XFillRectangle (display, input_status_pixmap, gc_inv,
2688                     0, 0, input_status_width, input_status_height);
2689     XtSetArg (arg[0], XtNfromHoriz, w);
2690     XtSetArg (arg[1], XtNleft, XawRubber);
2691     XtSetArg (arg[2], XtNright, XawChainRight);
2692     XtSetArg (arg[3], XtNborderWidth, 0);
2693     XtSetArg (arg[4], XtNlabel, "          ");
2694     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2695     CurIMLang = XtCreateManagedWidget ("CurIMLang", labelWidgetClass,
2696                                        HeadWidget, arg, 6);
2697     XtSetArg (arg[0], XtNfromHoriz, CurIMLang);
2698     XtSetArg (arg[1], XtNleft, XawChainRight);
2699     XtSetArg (arg[4], XtNbitmap, input_status_pixmap);
2700     CurIMStatus = XtCreateManagedWidget ("CurIMStatus", labelWidgetClass,
2701                                          HeadWidget, arg, 5);
2702
2703     XtSetArg (arg[0], XtNborderWidth, 0);
2704     XtSetArg (arg[1], XtNleft, XawChainLeft);
2705     XtSetArg (arg[2], XtNright, XawChainLeft);
2706     w = XtCreateManagedWidget ("Face", labelWidgetClass, FaceWidget, arg, 3);
2707     for (i = 0; i < num_faces;)
2708       {
2709         char *label_menu = face_table[i++].name; /* "Menu Xxxx" */
2710         char *label = label_menu + 5;            /* "Xxxx" */
2711
2712         for (j = i; j < num_faces && face_table[j].face; j++)
2713           SetMenu (menus[j - i], 0, face_table[j].name, NULL,
2714                    FaceProc, j, -1);
2715         w = create_menu_button (ShellWidget, FaceWidget, w,
2716                                 label, label_menu,
2717                                 menus, j - i, "Push face property");
2718         i = j;
2719       }
2720
2721     XtSetArg (arg[0], XtNfromHoriz, w);
2722     XtSetArg (arg[1], XtNleft, XawChainLeft);
2723     XtSetArg (arg[2], XtNright, XawChainLeft);
2724     XtSetArg (arg[3], XtNhorizDistance, 10);
2725     XtSetArg (arg[4], XtNlabel, "Pop");
2726     XtSetArg (arg[5], XtNtranslations,
2727               XtParseTranslationTable (pop_face_trans));
2728     w = XtCreateManagedWidget ("Pop Face", commandWidgetClass,
2729                                FaceWidget, arg, 6);
2730     XtAddCallback (w, XtNcallback, FaceProc, (void *) -1);
2731
2732     XtSetArg (arg[0], XtNfromHoriz, w);
2733     XtSetArg (arg[1], XtNleft, XawChainLeft);
2734     XtSetArg (arg[2], XtNright, XawChainRight);
2735     XtSetArg (arg[3], XtNlabel, "");
2736     XtSetArg (arg[4], XtNborderWidth, 0);
2737     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2738     CurFaceWidget = XtCreateManagedWidget ("Current Face", labelWidgetClass,
2739                                            FaceWidget, arg, 6);
2740
2741     XtSetArg (arg[0], XtNborderWidth, 0);
2742     XtSetArg (arg[1], XtNleft, XawChainLeft);
2743     XtSetArg (arg[2], XtNright, XawChainLeft);
2744     w = XtCreateManagedWidget ("Lang", labelWidgetClass, LangWidget, arg, 3);
2745     {
2746       MPlist *plist[11], *pl;
2747       char langname[3];
2748
2749       for (i = 0; i < 11; i++) plist[i] = NULL;
2750       langname[2] = '\0';
2751       for (langname[0] = 'a'; langname[0] <= 'z'; langname[0]++)
2752         for (langname[1] = 'a'; langname[1] <= 'z'; langname[1]++)
2753           {
2754             MSymbol sym = msymbol_exist (langname);
2755             MSymbol fullname;
2756
2757             if (sym != Mnil
2758                 && ((fullname = msymbol_get (sym, Mlanguage)) != Mnil))
2759               {
2760                 char *name = msymbol_name (fullname);
2761                 char c = name[0];
2762
2763                 if (c >= 'A' && c <= 'Z')
2764                   {
2765                     int idx = (c < 'U') ? (c - 'A') / 2 : 10;
2766
2767                     pl = plist[idx];
2768                     if (! pl)
2769                       pl = plist[idx] = mplist ();
2770                     for (; mplist_next (pl); pl = mplist_next (pl))
2771                       if (strcmp (name, (char *) mplist_value (pl)) < 0)
2772                         break;
2773                     mplist_push (pl, sym, fullname);
2774                   }
2775               }
2776           }
2777
2778       for (i = 0; i < 11; i++)
2779         if (plist[i])
2780           {
2781             char *name = alloca (9);
2782
2783             sprintf (name, "Menu %c-%c", 'A' + i * 2, 'A' + i * 2 + 1);
2784             if (i == 10)
2785               name[7] = 'Z';
2786             for (j = 0, pl = plist[i]; mplist_next (pl);
2787                  j++, pl = mplist_next (pl))
2788               SetMenu (menus[j], 0, msymbol_name ((MSymbol) mplist_value (pl)),
2789                        msymbol_name (mplist_key (pl)),
2790                        LangProc, mplist_key (pl), -1);
2791             w = create_menu_button (ShellWidget, LangWidget, w, name + 5, name,
2792                                     menus, j, "Push language property");
2793           }
2794       for (i = 0; i < 11; i++)
2795         if (plist[i])
2796           m17n_object_unref (plist[i]);
2797     }
2798     XtSetArg (arg[0], XtNfromHoriz, w);
2799     XtSetArg (arg[1], XtNleft, XawChainLeft);
2800     XtSetArg (arg[2], XtNright, XawChainLeft);
2801     XtSetArg (arg[3], XtNhorizDistance, 10);
2802     XtSetArg (arg[4], XtNlabel, "Pop");
2803     XtSetArg (arg[5], XtNtranslations,
2804               XtParseTranslationTable (pop_lang_trans));
2805     w = XtCreateManagedWidget ("Pop Lang", commandWidgetClass,
2806                                LangWidget, arg, 6);
2807     XtAddCallback (w, XtNcallback, LangProc, Mnil);
2808
2809     XtSetArg (arg[0], XtNfromHoriz, w);
2810     XtSetArg (arg[1], XtNleft, XawChainLeft);
2811     XtSetArg (arg[2], XtNright, XawChainRight);
2812     XtSetArg (arg[3], XtNlabel, "");
2813     XtSetArg (arg[4], XtNborderWidth, 0);
2814     XtSetArg (arg[5], XtNjustify, XtJustifyRight);
2815     CurLangWidget = XtCreateManagedWidget ("Current Lang", labelWidgetClass,
2816                                            LangWidget, arg, 6);
2817   }
2818
2819   XtSetArg (arg[0], XtNheight, win_height);
2820   XtSetArg (arg[1], XtNwidth, 10);
2821   XtSetArg (arg[2], XtNleft, XawChainLeft);
2822   XtSetArg (arg[3], XtNright, XawChainLeft);
2823   SbarWidget = XtCreateManagedWidget ("sbar", scrollbarWidgetClass, BodyWidget,
2824                                       arg, 4);
2825   XtAddCallback (SbarWidget, XtNscrollProc, ScrollProc, NULL);
2826   XtAddCallback (SbarWidget, XtNjumpProc, JumpProc, NULL);
2827
2828   XtSetArg (arg[0], XtNheight, win_height);
2829   XtSetArg (arg[1], XtNwidth, win_width);
2830   XtSetArg (arg[2], XtNtranslations, XtParseTranslationTable (trans));
2831   XtSetArg (arg[3], XtNfromHoriz, SbarWidget);
2832   XtSetArg (arg[4], XtNleft, XawChainLeft);
2833   XtSetArg (arg[5], XtNright, XawChainRight);
2834   TextWidget = XtCreateManagedWidget ("text", simpleWidgetClass, BodyWidget,
2835                                       arg, 5);
2836
2837   XtSetArg (arg[0], XtNborderWidth, 0);
2838   XtSetArg (arg[1], XtNleft, XawChainLeft);
2839   XtSetArg (arg[2], XtNright, XawChainRight);
2840   XtSetArg (arg[3], XtNresizable, True);
2841   XtSetArg (arg[4], XtNjustify, XtJustifyLeft);
2842   MessageWidget = XtCreateManagedWidget ("message", labelWidgetClass,
2843                                          TailWidget, arg, 5);
2844
2845   memset (&control, 0, sizeof control);
2846   control.two_dimensional = 1;
2847   control.enable_bidi = 1;
2848   control.anti_alias = 1;
2849   control.min_line_ascent = font_ascent;
2850   control.min_line_descent = font_descent;
2851   control.max_line_width = win_width;
2852   control.with_cursor = 1;
2853   control.cursor_width = 2;
2854   control.partial_update = 1;
2855   control.ignore_formatting_char = 1;
2856
2857   memset (&input_status_control, 0, sizeof input_status_control);
2858   input_status_control.enable_bidi = 1;
2859
2860   XtAppAddActions (context, actions, XtNumber (actions));
2861   XtRealizeWidget (ShellWidget);
2862
2863   win = XtWindow (TextWidget);
2864
2865   XtAppMainLoop (context);
2866
2867   if (current_input_context)
2868     minput_destroy_ic (current_input_context);
2869   for (i = 0; i < num_input_methods; i++)
2870     if (input_method_table[i].im)
2871       minput_close_im (input_method_table[i].im);
2872   m17n_object_unref (frame);
2873   m17n_object_unref (mt);
2874   m17n_object_unref (face_xxx_large);
2875   m17n_object_unref (face_box);
2876   m17n_object_unref (face_courier);
2877   m17n_object_unref (face_helvetica);
2878   m17n_object_unref (face_times);
2879   m17n_object_unref (face_dv_ttyogesh);
2880   m17n_object_unref (face_freesans);
2881   m17n_object_unref (face_freeserif);
2882   m17n_object_unref (face_freemono);
2883   m17n_object_unref (face_default_fontset);
2884   m17n_object_unref (face_no_ctl_fontset);
2885   m17n_object_unref (face_input_status);
2886   m17n_object_unref (face_default);
2887   m17n_object_unref (default_face_list);
2888   m17n_object_unref (selection);
2889
2890   XFreeGC (display, mono_gc);
2891   XFreeGC (display, mono_gc_inv);
2892   XFreeGC (display, gc_inv);
2893   XtUninstallTranslations (form);
2894   XtUninstallTranslations (TextWidget);
2895   XtDestroyWidget (ShellWidget);
2896   XtDestroyApplicationContext (context);
2897
2898   M17N_FINI ();
2899
2900   free (fontset_name);
2901   free (filename);
2902   free (input_method_table);
2903   free (InputMethodMenus);
2904
2905   exit (0);
2906 }
2907 #endif /* not FOR_DOXYGEN */