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