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