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