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