/* medit.c -- simple multilingual editor. -*- coding: euc-jp; -*-
Copyright (C) 2003, 2004, 2005, 2006, 2007
National Institute of Advanced Industrial Science and Technology (AIST)
Registration Number H15PRO112
This file is part of the m17n library.
The m17n library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
The m17n library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the m17n library; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
02111-1307, USA. */
/***en
@enpage m17n-edit edit multilingual text
@section m17n-edit-synopsis SYNOPSIS
m17n-edit [ XT-OPTION ...] [ OPTION ... ] FILE
@section m17n-edit-description DESCRIPTION
Display FILE on a window and allow users to edit it.
XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).
The following OPTIONs are available.
- --version
Print version number.
- -h, --help
Print this message.
This program is to demonstrate how to use the m17n GUI API.
Although m17n-edit directly uses the GUI API, the API is mainly
for toolkit libraries or to implement XOM (X Output Method), not
for direct use from application programs.
*/
/***ja
@japage m17n-edit 多言語テキストの編集
@section m17n-edit-synopsis SYNOPSIS
m17n-edit [ XT-OPTION ...] [ OPTION ... ] FILE
@section m17n-edit-description DESCRIPTION
FILE をウィンドウに表示し、ユーザが編集できるようにする。
XT-OPTIONs は Xt の標準の引数である。 (e.g. -fn, -fg).
以下のオプションが利用できる。
- --version
バージョン番号を表示する。
- -h, --help
このメッセージを表示する。
このプログラムは m17n GUI API の使い方を示すものである。m17n-edit
は直接 GUI API を使っているが、この API は主にツールキットライブラ
リやXOM (X Output Method) の実装用であり、アプリケーションプログラ
ムからの直接の利用を意図していない。
*/
#ifndef FOR_DOXYGEN
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_X11_XAW_COMMAND_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Global variables. */
char *filename;
int serialized;
/* For the X Window System. */
Display *display;
int screen;
/* GCs for normal drawing, filling by background color, normal drawing
on bitmap (i.e. pixmap of depth 1), filling bitmap by background
color. */
GC gc, gc_inv, mono_gc, mono_gc_inv;
Window win;
Atom XA_TEXT, XA_COMPOUND_TEXT, XA_UTF8_STRING; /* X Selection types. */
XtAppContext context;
int default_font_size;
/* Widget hierarchy
Shell - Form -+- Head -- File, Cursor, Bidi, LineBreak, InputMethod, CurIM;
+- Face -- Size, Family, Style, Color, Misc, Pop, CurFace
+- Lang -- A-B, C-D, ..., U-Z, Pop, CurLang
+- Body -- Sbar, Text
+- Tail -- Message
*/
Widget ShellWidget, HeadWidget, TailWidget, MessageWidget;
Widget CursorMenus[5], BidiMenus[3], LineBreakMenus[3], *InputMethodMenus;
Widget SbarWidget, TextWidget;
Widget FileShellWidget, FileDialogWidget;
Widget FaceWidget, CurFaceWidget, LangWidget, CurLangWidget;
Widget CurIMLang, CurIMStatus;
int win_width, win_height; /* Size of TextWidget. */
Arg arg[10];
Pixmap input_status_pixmap;
int input_status_width, input_status_height;
/* Bitmap for "check" glyph. */
#define check_width 9
#define check_height 8
static unsigned char check_bits[] = {
0x00, 0x01, 0x80, 0x01, 0xc0, 0x00, 0x60, 0x00,
0x31, 0x00, 0x1b, 0x00, 0x0e, 0x00, 0x04, 0x00 };
Pixmap CheckPixmap;
/* For the m17n library. */
MFrame *frame;
MText *mt;
int nchars; /* == mtext_len (mt) */
int mt_modified;
MDrawControl control, input_status_control;
MTextProperty *selection;
MSymbol Mword;
MFace *face_default;
MFace *face_xxx_large;
MFace *face_box;
MFace *face_courier, *face_helvetica, *face_times;
MFace *face_dv_ttyogesh, *face_freesans, *face_freeserif, *face_freemono;
MFace *face_default_fontset, *face_no_ctl_fontset;
MFace *face_input_status;
MSymbol Mcoding_compound_text;
int logical_move = 1; /* If 0, move cursor visually. */
typedef struct {
int available;
MSymbol language, name;
MInputMethod *im;
} InputMethodInfo;
InputMethodInfo *input_method_table;
int num_input_methods;
int current_input_method = -1; /* i.e. none */
int auto_input_method = 0;
MInputContext *current_input_context;
struct FaceRec
{
char *name;
MFace **face;
} face_table[] =
{ {"Menu Size", NULL},
{"xx-small", &mface_xx_small},
{"x-small", &mface_x_small},
{"small", &mface_small},
{"normalsize", &mface_normalsize},
{"large", &mface_large},
{"x-large", &mface_x_large},
{"xx-large", &mface_xx_large},
{"xxx-large", &face_xxx_large},
{"Menu Family", NULL},
{"courier", &face_courier},
{"helvetica", &face_helvetica},
{"times", &face_times},
{"dv-ttyogesh", &face_dv_ttyogesh},
{"freesans", &face_freesans},
{"freeserif", &face_freeserif},
{"freemono", &face_freemono},
{"Menu Style", NULL},
{"medium", &mface_medium},
{"bold", &mface_bold},
{"italic", &mface_italic},
{"Menu Color", NULL},
{"black", &mface_black},
{"white", &mface_white},
{"red", &mface_red},
{"green", &mface_green},
{"blue", &mface_blue},
{"cyan", &mface_cyan},
{"yello", &mface_yellow},
{"magenta", &mface_magenta},
{"Menu Misc", NULL},
{"normal", &mface_normal_video},
{"reverse", &mface_reverse_video},
{"underline", &mface_underline},
{"box", &face_box},
{"No CTL", &face_no_ctl_fontset} };
int num_faces = sizeof (face_table) / sizeof (struct FaceRec);
/* Information about a physical line metric. */
struct LineInfo
{
int from; /* BOL position of the line. */
int to; /* BOL position of the next line. */
int y0, y1; /* Top and bottom Y position of the line. */
int ascent; /* Height of the top Y position. */
};
struct LineInfo top; /* Topmost line. */
struct LineInfo cur; /* Line containing cursor. */
struct LineInfo sel_start; /* Line containing selection start. */
struct LineInfo sel_end; /* Line containing selection end. */
MDrawGlyphInfo cursor; /* Information about the cursor glyph. */
/* X position to keep on vertical (up and down) cursor motion. */
int target_x_position;
/* Interface macros for m17n-lib drawing routines. */
/* Draw a text in the range $FROM to $TO of the M-text #MT at the
coordinate ($X, $Y) */
#define DRAW_TEXT(x, y, from, to) \
mdraw_text_with_control \
(frame, (MDrawWindow) win, \
control.orientation_reversed ? x + win_width : x, y, \
mt, from, to, &control)
/* Store the extents of a text in the range $FROM to $TO in the
structure $RECT (type MDrawMetric). */
#define TEXT_EXTENTS(from, to, rect) \
mdraw_text_extents (frame, mt, from, (to), &control, NULL, NULL, &(rect))
/* Store the glyph information of a character at the position $POS in
the struct $INFO (type MDrawGlyphInfo) assuming that the text from
$FROM is written at the coordinate (0, 0). */
#define GLYPH_INFO(from, pos, info) \
mdraw_glyph_info (frame, mt, from, (pos), &control, &(info))
/* Set $X and $Y to the coordinate of character at position $POS
assuming that the text from $FROM is written at the coordinate (0,
0). */
#define COORDINATES_POSITION(from, pos, x, y) \
mdraw_coordinates_position (frame, mt, (from), (pos), (x), (y), &control)
/* Interface macros for X library. */
#define COPY_AREA(y0, y1, to) \
XCopyArea (display, win, win, gc, 0, (y0), win_width, (y1) - (y0), 0, (to))
#define CLEAR_AREA(x, y, w, h) \
XClearArea (display, win, (x), (y), (w), (h), False)
#define SELECTEDP() \
mtext_property_mtext (selection)
/* Format MSG by FMT and print the result to the stderr, and exit. */
#define FATAL_ERROR(fmt, arg) \
do { \
fprintf (stderr, fmt, arg); \
exit (1); \
} while (0)
/* If POS is greater than zero, move POS back to the beginning of line
(BOL) position. If FORWARD is nonzero, move POS forward instead.
Return the new position. */
int
bol (int pos, int forward)
{
int limit = forward ? nchars : 0;
pos = mtext_character (mt, pos, limit, '\n');
return (pos < 0 ? limit : pos + 1);
}
/* Update the structure #TOP (struct LineInfo) to make $POS the first
character position of the screen. */
void
update_top (int pos)
{
int from = bol (pos, 0);
MDrawGlyphInfo info;
GLYPH_INFO (from, pos, info);
top.from = info.line_from;
top.to = info.line_to;
top.y0 = 0;
top.y1 = info.metrics.height;
top.ascent = - info.metrics.y;
}
/* Update the scroll bar so that the text of the range $FROM to $TO
are shown on the window. */
void
update_scroll_bar (int from, int to)
{
float top = (float) from / nchars;
float shown = (float) (to - from) / nchars;
XtArgVal *l_top = (XtArgVal *) ⊤
XtArgVal *l_shown = (XtArgVal *) &shown;
XtSetArg (arg[0], XtNtopOfThumb, *l_top);
XtSetArg (arg[1], XtNshown, *l_shown);
XtSetValues (SbarWidget, arg, 2);
}
/* Redraw the window area between $Y0 and $Y1 (both Y-codinates). If
$CLEAR is nonzero, clear the area before drawing. If $SCROLL_BAR
is nonzero, update the scoll bar. */
void
redraw (int y0, int y1, int clear, int scroll_bar)
{
int from, to;
int y;
MDrawGlyphInfo info;
int sel_y0 = SELECTEDP () ? sel_start.y0 : 0;
struct LineInfo *line;
if (clear || control.anti_alias)
CLEAR_AREA (0, y0, win_width, y1 - y0);
/* Find a line closest to y0. It is a cursor line if the cursor is
Y0, otherwise the top line. */
if (y0 >= cur.y0)
line = &cur;
else
line = ⊤
/* If there exists a selected region, check it too. */
if (sel_y0 > line->y0 && y0 >= sel_y0)
line = &sel_start;
from = line->from;
y = line->y0;
info.metrics.height = line->y1 - y;
info.metrics.y = - line->ascent;
info.line_to = line->to;
while (y + info.metrics.height <= y0)
{
y += info.metrics.height;
from = info.line_to;
if (from >= nchars)
break;
GLYPH_INFO (from, from, info);
}
if (y + info.metrics.height <= y0)
return;
y0 = y - info.metrics.y;
to = from;
while (to < nchars && y < y1)
{
GLYPH_INFO (to, to, info);
y += info.metrics.height;
to = info.line_to;
}
if (to == nchars)
to++;
if (from < to)
DRAW_TEXT (0, y0, from, to);
if (scroll_bar)
{
while (to < nchars)
{
GLYPH_INFO (to, to, info);
if (y + info.metrics.height >= win_height)
break;
to = info.line_to;
y += info.metrics.height;
}
update_scroll_bar (top.from, to);
}
}
/* Set the current input method spot to the correct position. */
void
set_input_method_spot ()
{
int x = cursor.x + (control.orientation_reversed ? win_width : 0);
int pos = cursor.from > 0 ? cursor.from - 1 : 0;
MFace *faces[256];
int n = 0;
int size = 0, ratio = 0, i;
if (nchars > 0)
n = mtext_get_prop_values (mt, pos, Mface, (void **) faces, 256);
if (n > 0)
for (i = n - 1; i >= 0; i--)
{
if (! size)
size = (int) mface_get_prop (faces[i], Msize);
if (! ratio)
ratio = (int) mface_get_prop (faces[i], Mratio);
}
if (! size)
size = default_font_size;
if (ratio)
size = size * ratio / 100;
minput_set_spot (current_input_context, x, cur.y0 + cur.ascent,
cur.ascent, cur.y1 - (cur.y0 + cur.ascent), size,
mt, cursor.from);
}
/* Redraw the cursor. If $CLEAR is nonzero, clear the cursor area
before drawing. */
void
redraw_cursor (int clear)
{
if (control.cursor_bidi)
{
/* We must update the whole line of the cursor. */
int beg = bol (cur.from, 0);
int end = bol (cur.to - 1, 1);
MDrawMetric rect;
int y0 = cur.y0, y1 = cur.y1;
if (beg < cur.from)
{
TEXT_EXTENTS (beg, cur.from, rect);
y0 -= rect.height;
}
if (end > cur.to)
{
TEXT_EXTENTS (cur.to, end, rect);
y1 += rect.height;
}
redraw (y0, y1, clear, 0);
}
else
{
if (clear)
{
int x = cursor.x;
if (control.orientation_reversed)
x += win_width - cursor.logical_width;
CLEAR_AREA (x, cur.y0, cursor.logical_width, cursor.metrics.height);
}
DRAW_TEXT (cursor.x, cur.y0 + cur.ascent, cursor.from, cursor.to);
}
}
/* Update the information about the location of cursor to the position
$POS. If $FULL is nonzero, update the information fully only from
the information about the top line. Otherwise, trust the current
information in the structure $CUR. */
void
update_cursor (int pos, int full)
{
MDrawMetric rect;
if (full)
{
/* CUR is inaccurate. We can trust only TOP. */
GLYPH_INFO (top.from, pos, cursor);
cur.y0 = top.ascent + cursor.y + cursor.metrics.y;
}
else if (pos < cur.from)
{
int from = bol (pos, 0);
TEXT_EXTENTS (from, cur.from, rect);
GLYPH_INFO (from, pos, cursor);
cur.y0 -= (rect.height + rect.y) - (cursor.y + cursor.metrics.y);
}
else if (pos < cur.to)
{
GLYPH_INFO (cur.from, pos, cursor);
}
else
{
GLYPH_INFO (cur.from, pos, cursor);
cur.y0 += cur.ascent + cursor.y + cursor.metrics.y;
}
cur.from = cursor.line_from;
cur.to = cursor.line_to;
cur.y1 = cur.y0 + cursor.metrics.height;
cur.ascent = - cursor.metrics.y;
}
/* Update the information about the selected region. */
void
update_selection ()
{
int from, to;
MDrawMetric rect;
MDrawGlyphInfo info;
if (! SELECTEDP ())
return;
from = mtext_property_start (selection);
to = mtext_property_end (selection);
if (from < top.from)
{
int pos = bol (from, 0);
TEXT_EXTENTS (pos, top.from, rect);
sel_start.y0 = top.y0 - rect.height;
sel_start.ascent = - rect.y;
GLYPH_INFO (pos, from, info);
if (pos < info.line_from)
sel_start.y0 += - rect.y + info.y + info.metrics.y;
}
else
{
GLYPH_INFO (top.from, from, info);
sel_start.y0 = top.ascent + info.y + info.metrics.y;
}
sel_start.ascent = -info.metrics.y;
sel_start.y1 = sel_start.y0 + info.metrics.height;
sel_start.from = info.line_from;
sel_start.to = info.line_to;
if (to <= sel_start.to)
{
sel_end = sel_start;
}
else
{
GLYPH_INFO (sel_start.from, to, info);
sel_end.y0 = sel_start.y0 + sel_start.ascent + info.y + info.metrics.y;
sel_end.y1 = sel_end.y0 + info.metrics.height;
sel_end.ascent = - info.metrics.y;
sel_end.from = info.line_from;
sel_end.to = info.line_to;
}
}
/* Select the text in the region from $FROM to $TO. */
void
select_region (int from, int to)
{
int pos;
if (from > to)
pos = from, from = to, to = pos;
mtext_push_property (mt, from, to, selection);
update_selection ();
}
/* Setup the window to display the character of $POS at the top left
of the window. */
void
reseat (int pos)
{
MDrawMetric rect;
/* Top and bottom Y positions to redraw. */
int y0, y1;
if (pos + 1000 < top.from)
y0 = 0, y1 = win_height;
else if (pos < top.from)
{
y0 = 0;
TEXT_EXTENTS (pos, top.from, rect);
if (rect.height >= win_height * 0.9)
y1 = win_height;
else
{
y1 = rect.height;
COPY_AREA (0, win_height - y1, y1);
}
}
else if (pos < top.to)
{
/* No need of redrawing. */
y0 = y1 = 0;
}
else if (pos < top.from + 1000)
{
TEXT_EXTENTS (top.from, pos, rect);
if (rect.height >= win_height * 0.9)
y0 = 0;
else
{
y0 = win_height - rect.height;
COPY_AREA (rect.height, win_height, 0);
}
y1 = win_height;
}
else
y0 = 0, y1 = win_height;
if (y0 < y1)
{
update_top (pos);
if (cur.to <= pos)
update_cursor (pos, 1);
else
update_cursor (cursor.from, 1);
update_selection ();
redraw (y0, y1, 1, 1);
}
}
static void MenuHelpProc (Widget, XEvent *, String *, Cardinal *);
/* Select an input method accoding to $IDX. If $IDX is negative, turn
off the current input method, otherwide turn on the input method
input_method_table[$IDX]. */
void
select_input_method (idx)
{
int previous_input_method = current_input_method;
if (idx == current_input_method)
return;
if (current_input_method >= 0)
{
minput_destroy_ic (current_input_context);
current_input_context = NULL;
current_input_method = -1;
}
if (idx >= 0
&& input_method_table[idx].available >= 0)
{
InputMethodInfo *im = input_method_table + idx;
if (im->available == 0)
{
if (im->language)
im->im = minput_open_im (im->language, im->name, NULL);
else
{
MInputXIMArgIM arg_xim;
arg_xim.display = display;
arg_xim.db = NULL;
arg_xim.res_name = arg_xim.res_class = NULL;
arg_xim.locale = NULL;
arg_xim.modifier_list = NULL;
im->im = minput_open_im (Mnil, im->name, &arg_xim);
}
im->available = im->im ? 1 : -1;
}
if (im->im)
{
if (im->language == Mnil)
{
MInputXIMArgIC arg_xic;
Window win = XtWindow (TextWidget);
arg_xic.input_style = 0;
arg_xic.client_win = arg_xic.focus_win = win;
arg_xic.preedit_attrs = arg_xic.status_attrs = NULL;
current_input_context = minput_create_ic (im->im, &arg_xic);
}
else
{
MInputGUIArgIC arg_ic;
arg_ic.frame = frame;
arg_ic.client = (MDrawWindow) XtWindow (ShellWidget);
arg_ic.focus = (MDrawWindow) XtWindow (TextWidget);
current_input_context = minput_create_ic (im->im, &arg_ic);
}
if (current_input_context)
{
current_input_method = idx;
set_input_method_spot ();
}
else
{
minput_close_im (im->im);
im->im = NULL;
im->available = -1;
current_input_method = -1;
}
}
}
if (! auto_input_method)
{
XtSetArg (arg[0], XtNleftBitmap, None);
if (previous_input_method >= 0)
XtSetValues (InputMethodMenus[previous_input_method + 2], arg, 1);
else
XtSetValues (InputMethodMenus[0], arg, 1);
XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
if (current_input_method >= 0)
XtSetValues (InputMethodMenus[current_input_method + 2], arg, 1);
else
XtSetValues (InputMethodMenus[0], arg, 1);
}
if (current_input_method >= 0)
{
char *label;
XtSetArg (arg[0], XtNlabel, &label);
XtGetValues (InputMethodMenus[current_input_method + 2], arg, 1);
XtSetArg (arg[0], XtNlabel, label);
}
else
{
XtSetArg (arg[0], XtNlabel, "");
}
XtSetValues (CurIMLang, arg, 1);
}
static void MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num);
/* Display cursor according to the current information of #CUR.
$CLIENT_DATA is ignore. Most callback functions add this function
as a background processing procedure the current application (by
XtAppAddWorkProc) via the function hide_cursor. */
Boolean
show_cursor (XtPointer client_data)
{
MFaceHLineProp *hline;
MFaceBoxProp *box;
if (cur.y0 < 0)
{
reseat (cur.from);
update_cursor (cursor.from, 1);
}
while (cur.y1 > win_height)
{
reseat (top.to);
update_cursor (cursor.from, 1);
}
control.cursor_pos = cursor.from;
if (! SELECTEDP ())
{
control.with_cursor = 1;
redraw_cursor (0);
}
if (current_input_context)
set_input_method_spot ();
if (nchars > 0)
{
int pos = (SELECTEDP () ? mtext_property_start (selection)
: cursor.from > 0 ? cursor.from - 1
: cursor.from);
MFace *face = mface ();
MTextProperty *props[256];
int n = mtext_get_properties (mt, pos, Mface, props, 256);
int i;
char buf[256], *p = buf;
MSymbol sym;
buf[0] = '\0';
if (cursor.font)
{
int size = (int) mfont_get_prop (cursor.font, Msize);
MSymbol family = mfont_get_prop (cursor.font, Mfamily);
MSymbol weight = mfont_get_prop (cursor.font, Mweight);
MSymbol style = mfont_get_prop (cursor.font, Mstyle);
MSymbol registry = mfont_get_prop (cursor.font, Mregistry);
sprintf (p, "%dpt", size / 10), p += strlen (p);
if (family)
strcat (p, ","), strcat (p, msymbol_name (family)), p += strlen (p);
if (weight)
strcat (p, ","), strcat (p, msymbol_name (weight)), p += strlen (p);
if (style)
strcat (p, ","), strcat (p, msymbol_name (style)), p += strlen (p);
if (registry)
strcat (p, ","), strcat (p, msymbol_name (registry)), p += strlen (p);
p += strlen (p);
}
mface_merge (face, face_default);
for (i = 0; i < n; i++)
if (props[i] != selection)
mface_merge (face, (MFace *) mtext_property_value (props[i]));
sym = (MSymbol) mface_get_prop (face, Mforeground);
if (sym != Mnil)
strcat (p, ","), strcat (p, msymbol_name (sym)), p += strlen (p);
if ((MSymbol) mface_get_prop (face, Mvideomode) == Mreverse)
strcat (p, ",rev"), p += strlen (p);
hline = mface_get_prop (face, Mhline);
if (hline && hline->width > 0)
strcat (p, ",ul"), p += strlen (p);
box = mface_get_prop (face, Mbox);
if (box && box->width > 0)
strcat (p, ",box"), p += strlen (p);
m17n_object_unref (face);
XtSetArg (arg[0], XtNborderWidth, 1);
XtSetArg (arg[1], XtNlabel, buf);
XtSetValues (CurFaceWidget, arg, 2);
}
if (control.cursor_pos < nchars)
{
MSymbol sym = mtext_get_prop (mt, control.cursor_pos, Mlanguage);
if (sym == Mnil)
{
XtSetArg (arg[0], XtNborderWidth, 0);
XtSetArg (arg[1], XtNlabel, "");
}
else
{
XtSetArg (arg[0], XtNborderWidth, 1);
sym = mlanguage_name (sym);
XtSetArg (arg[1], XtNlabel, msymbol_name (sym));
XtSetValues (CurLangWidget, arg, 2);
}
XtSetValues (CurLangWidget, arg, 2);
if (auto_input_method)
{
if (sym == Mnil)
select_input_method (-1);
else
{
int i;
for (i = 0; i < num_input_methods; i++)
if (input_method_table[i].language == sym
&& input_method_table[i].available >= 0)
break;
if (i < num_input_methods)
select_input_method (i);
else
select_input_method (-1);
}
}
}
MenuHelpProc (MessageWidget, NULL, NULL, NULL);
return True;
}
/* Hide the cursor. */
void
hide_cursor ()
{
control.with_cursor = 0;
redraw_cursor (1);
XtAppAddWorkProc (context, show_cursor, NULL);
}
/* Update the window area between the Y-positions $Y0 and $OLD_Y1 to
$Y1 and $NEW_Y1 assuming that the text in the other area is not
changed. */
void
update_region (int y0, int old_y1, int new_y1)
{
if (y0 < 0)
y0 = 0;
if (new_y1 < old_y1)
{
if (old_y1 < win_height)
{
COPY_AREA (old_y1, win_height, new_y1);
redraw (win_height - (old_y1 - new_y1), win_height, 1, 0);
}
else
redraw (new_y1, win_height, 1, 0);
}
else if (new_y1 > old_y1)
{
if (new_y1 < win_height)
COPY_AREA (old_y1, win_height, new_y1);
}
if (new_y1 > win_height)
new_y1 = win_height;
redraw (y0, new_y1, 1, 1);
}
/* Delete the next $N characters. If $N is negative delete the
precious (- $N) characters. */
void
delete_char (int n)
{
MDrawMetric rect;
MDrawGlyphInfo info;
int y0, old_y1, new_y1;
int from, to;
int line_from = cursor.line_from;
if (n > 0)
from = cursor.from, to = from + n;
else
{
from = cursor.from + n;
to = cursor.from;
if (cursor.from == cur.from)
{
/* We are at the beginning of line. */
int pos = cursor.prev_from;
if (cursor.from == top.from)
{
/* We are at the beginning of screen. We must scroll
down. */
GLYPH_INFO (bol (top.from - 1, 0), top.from - 1, info);
reseat (info.line_from);
}
update_cursor (pos, 1);
}
}
TEXT_EXTENTS (cur.from, bol (to + 1, 1), rect);
old_y1 = cur.y0 + rect.height;
/* Now delete a character. */
mtext_del (mt, from, to);
nchars -= to - from;
if (from >= top.from && from < top.to)
update_top (top.from);
update_cursor (from, 1);
y0 = cur.y0;
if (line_from != cursor.line_from)
y0 -= 1;
TEXT_EXTENTS (cur.from, bol (to, 1), rect);
new_y1 = cur.y0 + rect.height;
update_region (cur.y0, old_y1, new_y1);
}
/* Insert M-text $NEWTEXT at the current cursor position. */
void
insert_chars (MText *newtext)
{
int n = mtext_len (newtext);
MDrawMetric rect;
int y0, old_y1, new_y1;
int line_from;
if (SELECTEDP ())
{
int n = (mtext_property_end (selection)
- mtext_property_start (selection));
mtext_detach_property (selection);
delete_char (n);
}
y0 = cur.y0;
if (cursor.line_from > 0
&& mtext_ref_char (mt, cursor.line_from - 1) != '\n')
y0 -= control.min_line_descent;
TEXT_EXTENTS (cur.from, bol (cur.to - 1, 1), rect);
old_y1 = y0 + rect.height;
line_from = cursor.line_from;
/* Now insert chars. */
mtext_ins (mt, cursor.from, newtext);
nchars += n;
if (cur.from == top.from)
update_top (top.from);
update_cursor (cursor.from + n, 1);
TEXT_EXTENTS (cur.from, bol (cur.to - 1, 1), rect);
new_y1 = cur.y0 + rect.height;
update_region (y0, old_y1, new_y1);
update_selection ();
}
int
word_constituent_p (int c)
{
MSymbol category = (MSymbol) mchar_get_prop (c, Mcategory);
char *name = category != Mnil ? msymbol_name (category) : NULL;
return (name && (name[0] == 'L' || name[0] == 'M'));
}
void
forward_word ()
{
int pos = cursor.from;
while (pos < nchars && ! word_constituent_p (mtext_ref_char (mt, pos)))
pos++;
if (pos < nchars)
{
MTextProperty *prop = mtext_get_property (mt, pos, Mword);
if (prop)
pos = mtext_property_end (prop);
else
while (pos < nchars && word_constituent_p (mtext_ref_char (mt, pos)))
pos++;
}
update_cursor (pos, 0);
}
void
backward_word ()
{
int pos = cursor.from;
while (pos > 0 && ! word_constituent_p (mtext_ref_char (mt, pos - 1)))
pos--;
if (pos > 0)
{
MTextProperty *prop = mtext_get_property (mt, pos - 1, Mword);
if (prop)
pos = mtext_property_start (prop);
else
while (pos > 0 && word_constituent_p (mtext_ref_char (mt, pos - 1)))
pos--;
}
update_cursor (pos, 0);
}
/* Convert the currently selected text to UTF8-STRING or
COMPOUND-TEXT. It is called when someone requests the current
value of the selection. */
Boolean
covert_selection (Widget w, Atom *selection_atom,
Atom *target, Atom *return_type,
XtPointer *value, unsigned long *length, int *format)
{
unsigned char *buf = (unsigned char *) XtMalloc (4096);
MText *this_mt = mtext ();
int from = mtext_property_start (selection);
int to = mtext_property_end (selection);
MSymbol coding;
int len;
mtext_copy (this_mt, 0, mt, from, to);
if (*target == XA_TEXT)
{
#ifdef X_HAVE_UTF8_STRING
coding = Mcoding_utf_8;
*return_type = XA_UTF8_STRING;
#else
coding = Mcoding_compound_text;
*return_type = XA_COMPOUND_TEXT;
#endif
}
else if (*target == XA_UTF8_STRING)
{
coding = Mcoding_utf_8;
*return_type = XA_UTF8_STRING;
}
else if (*target == XA_STRING)
{
int i;
len = to - from;
for (i = 0; i < len; i++)
if (mtext_ref_char (this_mt, i) >= 0x100)
/* Can't encode in XA_STRING */
return False;
coding = Mcoding_iso_8859_1;
*return_type = XA_STRING;
}
else if (*target == XA_COMPOUND_TEXT)
{
coding = Mcoding_compound_text;
*return_type = XA_COMPOUND_TEXT;
}
else
return False;
len = mconv_encode_buffer (coding, this_mt, buf, 4096);
m17n_object_unref (this_mt);
if (len < 0)
return False;
*length = len;
*value = (XtPointer) buf;
*format = 8;
return True;
}
/* Unselect the text. It is called when we loose the selection. */
void
lose_selection (Widget w, Atom *selection_atom)
{
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);
}
}
void
get_selection (Widget w, XtPointer cliend_data, Atom *selection, Atom *type,
XtPointer value, unsigned long *length, int *format)
{
MText *this_mt;
MSymbol coding;
if (*type == XT_CONVERT_FAIL || ! value)
goto err;
if (*type == XA_STRING)
coding = Mnil;
else if (*type == XA_COMPOUND_TEXT)
coding = msymbol ("compound-text");
#ifdef X_HAVE_UTF8_STRING
else if (*type == XA_UTF8_STRING)
coding = msymbol ("utf-8");
#endif
else
goto err;
this_mt = mconv_decode_buffer (coding, (unsigned char *) value, *length);
if (! this_mt && *type != XA_UTF8_STRING)
{
XtGetSelectionValue (w, XA_PRIMARY, XA_UTF8_STRING, get_selection, NULL,
CurrentTime);
goto err;
}
if (this_mt)
{
hide_cursor ();
insert_chars (this_mt);
m17n_object_unref (this_mt);
}
err:
if (value)
XtFree (value);
}
static void
ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
XExposeEvent *expose = (XExposeEvent *) event;
if (top.from < 0)
{
Dimension width_max, width;
XtSetArg (arg[0], XtNwidth, &width);
XtGetValues (XtParent (w), arg, 1);
width_max = width;
XtGetValues (HeadWidget, arg, 1);
if (width_max < width)
width_max = width;
XtGetValues (FaceWidget, arg, 1);
if (width_max < width)
width_max = width;
XtGetValues (LangWidget, arg, 1);
if (width_max < width)
width_max = width;
XtSetArg (arg[0], XtNwidth, width_max);
XtSetValues (HeadWidget, arg, 1);
XtSetValues (FaceWidget, arg, 1);
XtSetValues (LangWidget, arg, 1);
XtSetValues (XtParent (w), arg, 1);
XtSetValues (TailWidget, arg, 1);
update_top (0);
update_cursor (0, 1);
redraw (0, win_height, 0, 1);
if (current_input_method >= 0)
{
int idx = current_input_method;
current_input_method = -1;
select_input_method (idx);
}
show_cursor (NULL);
}
else
{
redraw (expose->y, expose->y + expose->height, 0, 0);
if (current_input_context
&& expose->y < cur.y0 && expose->y + expose->height < cur.y1)
set_input_method_spot ();
}
}
static void
ConfigureProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
XConfigureEvent *configure = (XConfigureEvent *) event;
hide_cursor ();
control.max_line_width = win_width = configure->width;
win_height = configure->height;
mdraw_clear_cache (mt);
update_top (0);
update_cursor (0, 1);
redraw (0, win_height, 1, 1);
if (current_input_context)
set_input_method_spot ();
}
static void
ButtonProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
int pos;
int x = event->xbutton.x;
int y = event->xbutton.y - top.ascent;
if (control.orientation_reversed)
x -= win_width;
pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
if (SELECTEDP ())
{
XtDisownSelection (w, XA_PRIMARY, CurrentTime);
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);
}
hide_cursor ();
if (current_input_context
&& minput_filter (current_input_context, Minput_focus_move, NULL) == 0)
{
MText *produced = mtext ();
minput_lookup (current_input_context, Mnil, NULL, produced);
if (mtext_len (produced) > 0)
{
insert_chars (produced);
if (pos >= cursor.from)
pos += mtext_len (produced);
}
m17n_object_unref (produced);
}
update_cursor (pos, 0);
}
static void
ButtonReleaseProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
if (! SELECTEDP ())
return;
XtOwnSelection (w, XA_PRIMARY, CurrentTime,
covert_selection, lose_selection, NULL);
update_cursor (mtext_property_start (selection), 0);
}
static
void
Button2Proc (Widget w, XEvent *event, String *str, Cardinal *num)
{
if (! SELECTEDP ())
{
/* We don't have a local selection. */
XtGetSelectionValue (w, XA_PRIMARY, XA_TEXT, get_selection, NULL,
CurrentTime);
}
else
{
int from = mtext_property_start (selection);
int to = mtext_property_end (selection);
MText *this_mt;
int pos;
int x = event->xbutton.x;
int y = event->xbutton.y - top.ascent;
if (control.orientation_reversed)
x -= win_width;
pos = COORDINATES_POSITION (top.from, nchars + 1, x, y);
XtDisownSelection (w, XA_PRIMARY, CurrentTime);
mtext_detach_property (selection);
hide_cursor ();
this_mt = mtext_copy (mtext (), 0, mt, from, to);
update_cursor (pos, 0);
insert_chars (this_mt);
m17n_object_unref (this_mt);
}
}
static void
ButtonMoveProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
int pos;
int x = event->xbutton.x;
int y = event->xbutton.y;
if (control.orientation_reversed)
x -= win_width;
if (y < cur.y0)
pos = top.from, y -= top.ascent;
else
pos = cur.from, y -= cur.y0 + cur.ascent;
pos = COORDINATES_POSITION (pos, nchars + 1, x, y);
if (pos == cursor.from)
return;
hide_cursor ();
if (SELECTEDP ())
{
/* Selection range changed. */
int from = mtext_property_start (selection);
int to = mtext_property_end (selection);
int start_y0 = sel_start.y0, start_y1 = sel_start.y1;
int end_y0 = sel_end.y0, end_y1 = sel_end.y1;
if (cursor.from == from)
{
/* Starting position changed. */
if (pos <= from)
{
/* Enlarged. We can simply overdraw. */
select_region (pos, to);
redraw (sel_start.y0, start_y1, 0, 0);
}
else if (pos < to)
{
/* Shrunken. Previous selection face must be cleared. */
select_region (pos, to);
redraw (start_y0, sel_start.y1, 1, 0);
}
else if (pos == to)
{
/* Shrunken to zero. */
XtDisownSelection (w, XA_PRIMARY, CurrentTime);
mtext_detach_property (selection);
redraw (start_y0, end_y1, 1, 0);
}
else
{
/* Full update is necessary. */
select_region (to, pos);
redraw (start_y0, sel_end.y1, 1, 0);
}
}
else
{
/* Ending position changed. */
if (pos < from)
{
/* Full update is necessary. */
select_region (pos, from);
redraw (sel_start.y0, end_y1, 1, 0);
}
else if (pos == from)
{
/* Shrunken to zero. */
XtDisownSelection (w, XA_PRIMARY, CurrentTime);
mtext_detach_property (selection);
redraw (start_y0, end_y1, 1, 0);
}
else if (pos < to)
{
/* Shrunken. Previous selection face must be cleared. */
select_region (from, pos);
redraw (sel_end.y0, end_y1, 1, 0);
}
else
{
/* Enlarged. We can simply overdraw. */
select_region (from, pos);
redraw (end_y0, sel_end.y1, 0, 0);
}
}
}
else
{
/* Newly selected. */
select_region (pos, cursor.from);
redraw (sel_start.y0, sel_end.y1, 0, 0);
}
update_cursor (pos, 1);
}
void
FocusInProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
if (current_input_context
&& minput_filter (current_input_context, Minput_focus_in, NULL) == 0)
{
MText *produced = mtext ();
minput_lookup (current_input_context, Mnil, NULL, produced);
if (mtext_len (produced) > 0)
{
hide_cursor ();
insert_chars (produced);
}
m17n_object_unref (produced);
}
}
void
FocusOutProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
if (current_input_context
&& minput_filter (current_input_context, Minput_focus_out, NULL) == 0)
{
MText *produced = mtext ();
minput_lookup (current_input_context, Mnil, NULL, produced);
if (mtext_len (produced) > 0)
{
hide_cursor ();
insert_chars (produced);
}
m17n_object_unref (produced);
}
}
void
ScrollProc (Widget w, XtPointer client_data, XtPointer position)
{
int from;
MDrawGlyphInfo info;
int height;
int cursor_pos = cursor.from;
if (((int) position) < 0)
{
/* Scroll down. */
int pos;
from = top.from;
height = top.y1 - top.y0;
while (from > 0)
{
pos = bol (from - 1, 0);
GLYPH_INFO (pos, from - 1, info);
if (height + info.metrics.height > win_height)
break;
height += info.metrics.height;
from = info.line_from;
}
if (cursor_pos >= top.to)
{
cursor_pos = top.from;
pos = top.to;
while (cursor_pos < nchars)
{
GLYPH_INFO (pos, pos, info);
if (height + info.metrics.height > win_height)
break;
height += info.metrics.height;
cursor_pos = pos;
pos = info.line_to;
}
}
}
else if (cur.to < nchars)
{
/* Scroll up, but leave at least one line. */
from = cur.to;
height = cur.y1;
while (from < nchars)
{
GLYPH_INFO (from, from, info);
if (height + info.metrics.height > win_height
|| info.line_to >= nchars)
break;
height += info.metrics.height;
from = info.line_to;
}
if (from == nchars)
from = info.line_from;
if (cursor_pos < from)
cursor_pos = from;
}
else
/* Scroll up to make the cursor line top. */
from = cur.from;
hide_cursor ();
reseat (from);
update_cursor (cursor_pos, 1);
}
void
JumpProc (Widget w, XtPointer client_data, XtPointer persent_ptr)
{
float persent = *(float *) persent_ptr;
int pos1, pos2 = nchars * persent;
MDrawGlyphInfo info;
hide_cursor ();
pos1 = bol (pos2, 0);
GLYPH_INFO (pos1, pos2, info);
pos1 = info.line_from;
reseat (pos1);
update_cursor (pos1, 1);
}
static void
KeyProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
XKeyEvent *key_event = (XKeyEvent *) event;
char buf[512];
KeySym keysym = NoSymbol;
int ret;
/* If set to 1, do not update target_x_position. */
int keep_target_x_position = 0;
MText *produced;
int y0, old_y1, new_y1;
hide_cursor ();
mt_modified = 0;
y0 = cur.y0;
old_y1 = cur.y1;
if (current_input_context
&& minput_filter (current_input_context, Mnil, event))
{
if (mt_modified)
{
new_y1 = cur.y1;
update_region (y0, old_y1, new_y1);
}
return;
}
if (event->type == KeyRelease)
return;
produced = mtext ();
ret = minput_lookup (current_input_context, Mnil, event, produced);
if (mtext_len (produced) > 0)
insert_chars (produced);
if (ret)
ret = XLookupString (key_event, buf, sizeof (buf), &keysym, NULL);
m17n_object_unref (produced);
switch (keysym)
{
case XK_Delete:
{
int n = 0;
if (SELECTEDP ())
{
n = (mtext_property_end (selection)
- mtext_property_start (selection));
mtext_detach_property (selection);
}
else if (cursor.from < nchars)
{
/* Delete the following grapheme cluster. */
n = cursor.to - cursor.from;
}
if (n != 0)
delete_char (n);
}
break;
case XK_BackSpace:
{
int n = 0;
if (SELECTEDP ())
{
/* Delete selected region. */
n = (mtext_property_end (selection)
- mtext_property_start (selection));
mtext_detach_property (selection);
}
else if (cursor.from > 0)
{
/* Delete the preceding character. */
n = -1;
}
if (n != 0)
delete_char (n);
}
break;
case XK_Left:
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);;
}
if (logical_move)
{
if (cursor.prev_from >= 0)
update_cursor (cursor.prev_from, 0);
}
else
{
if (cursor.left_from >= 0)
update_cursor (cursor.left_from, 0);
}
break;
case XK_Right:
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);;
}
if (logical_move)
{
if (cursor.next_to >= 0)
update_cursor (cursor.to, 0);
}
else
{
if (cursor.right_from >= 0)
update_cursor (cursor.right_from, 0);
}
break;
case XK_Down:
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);;
}
if (cur.to <= nchars)
{
MDrawGlyphInfo info;
int pos;
GLYPH_INFO (cur.from, cur.to, info);
pos = COORDINATES_POSITION (cur.from, nchars + 1,
target_x_position, info.y);
keep_target_x_position = 1;
update_cursor (pos, 0);
}
break;
case XK_Up:
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);;
}
if (cur.from > 0)
{
MDrawMetric rect;
int y;
int pos = bol (cur.from - 1, 0);
TEXT_EXTENTS (pos, cur.from - 1, rect);
y = rect.height + rect.y - 1;
pos = COORDINATES_POSITION (pos, nchars,
target_x_position, y);
keep_target_x_position = 1;
update_cursor (pos, 0);
}
break;
case XK_Page_Down:
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);;
}
if (top.from < nchars)
ScrollProc (w, NULL, (XtPointer) 1);
break;
case XK_Page_Up:
if (SELECTEDP ())
{
mtext_detach_property (selection);
redraw (sel_start.y0, sel_end.y1, 1, 0);;
}
if (top.from > 0)
ScrollProc (w, NULL, (XtPointer) -1);
break;
case XK_b:
if (key_event->state >= Mod1Mask)
{
lose_selection (NULL, NULL);
backward_word ();
break;
}
case XK_f:
if (key_event->state >= Mod1Mask)
{
lose_selection (NULL, NULL);
forward_word ();
break;
}
default:
if (ret > 0)
{
if (buf[0] == 17) /* C-q */
{
XtAppSetExitFlag (context);
return;
}
else if (buf[0] == 12) /* C-l */
{
redraw (0, win_height, 1, 1);
return;
}
else
{
MText *temp = mtext ();
mtext_cat_char (temp, buf[0] == '\r' ? '\n'
: ((unsigned char *) buf)[0]);
if (current_input_context)
mtext_put_prop (temp, 0, 1, Mlanguage,
current_input_context->im->language);
insert_chars (temp);
m17n_object_unref (temp);
}
}
}
if (! keep_target_x_position)
target_x_position = cursor.x;
}
void
SaveProc (Widget w, XtPointer client_data, XtPointer call_data)
{
char *name = (char *) client_data;
FILE *fp;
int from = -1, to = 0;
if (name)
{
free (filename);
filename = strdup (name);
}
fp = fopen (filename, "w");
if (! fp)
{
fprintf (stderr, "Open for write fail: %s", filename);
return;
}
if (SELECTEDP ())
{
from = mtext_property_start (selection);
to = mtext_property_end (selection);
mtext_detach_property (selection);
}
mconv_encode_stream (Mcoding_utf_8_full, mt, fp);
fclose (fp);
if (from >= 0)
select_region (from, to);
}
void
SerializeProc (Widget w, XtPointer client_data, XtPointer call_data)
{
MText *new;
hide_cursor ();
if (SELECTEDP ())
mtext_detach_property (selection);
serialized = (int) client_data;
if (! serialized)
new = mtext_deserialize (mt);
else
{
MPlist *plist = mplist ();
mplist_push (plist, Mt, Mface);
mplist_push (plist, Mt, Mlanguage);
new = mtext_serialize (mt, 0, mtext_len (mt), plist);
m17n_object_unref (plist);
}
if (new)
{
m17n_object_unref (mt);
mt = new;
serialized = ! serialized;
nchars = mtext_len (mt);
update_top (0);
}
update_cursor (0, 1);
redraw (0, win_height, 1, 1);
}
void
QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
{
XtAppSetExitFlag (context);
}
MText *
read_file ()
{
FILE *fp = fopen (filename, "r");
if (! fp)
FATAL_ERROR ("Can't read \"%s\"!\n", filename);
mt = mconv_decode_stream (Mcoding_utf_8_full, fp);
fclose (fp);
if (! mt)
FATAL_ERROR ("Can't decode \"%s\" by UTF-8!\n", filename);
return mt;
}
void
BidiProc (Widget w, XtPointer client_data, XtPointer call_data)
{
int data = (int) client_data;
int i;
if (data == 0)
{
control.enable_bidi = 0;
control.orientation_reversed = 0;
}
else
{
control.enable_bidi = 1;
control.orientation_reversed = data == 2;
}
for (i = 0; i < 3; i++)
{
if (i == data)
XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
else
XtSetArg (arg[0], XtNleftBitmap, None);
XtSetValues (BidiMenus[i], arg, 1);
}
update_cursor (cursor.from, 1);
redraw (0, win_height, 1, 0);
}
void
LineBreakProc (Widget w, XtPointer client_data, XtPointer call_data)
{
int data = (int) client_data;
int i;
if (data == 0)
control.max_line_width = 0;
else
{
control.max_line_width = win_width;
control.line_break = (data == 1 ? NULL : mdraw_default_line_break);
}
for (i = 0; i < 3; i++)
{
if (i == data)
XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
else
XtSetArg (arg[0], XtNleftBitmap, None);
XtSetValues (LineBreakMenus[i], arg, 1);
}
update_cursor (cursor.from, 1);
redraw (0, win_height, 1, 0);
}
void
FilterProc (Widget w, XtPointer client_data, XtPointer call_data)
{
char *filter_module = (char *) client_data;
void *handle;
void (*func) (MText *, int, int);
if (! SELECTEDP ())
return;
handle = dlopen (filter_module, RTLD_NOW);
if (! handle)
return;
*(void **) (&func) = dlsym (handle, "filter");
if (func)
(*func) (mt, mtext_property_start (selection),
mtext_property_end (selection));
dlclose (handle);
}
void
CursorProc (Widget w, XtPointer client_data, XtPointer call_data)
{
int data = (int) client_data;
int i, from, to;
switch (data)
{
case 0:
logical_move = 1;
from = 0, to = 2;
break;
case 1:
logical_move = 0;
from = 0, to = 2;
break;
case 2:
control.cursor_bidi = 0, control.cursor_width = -1;
from = 2, to = 5;
break;
case 3:
control.cursor_bidi = 0, control.cursor_width = 2;
from = 2, to = 5;
break;
default:
control.cursor_bidi = 1;
from = 2, to = 5;
break;
}
for (i = from; i < to; i++)
{
if (i == data)
XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
else
XtSetArg (arg[0], XtNleftBitmap, None);
XtSetValues (CursorMenus[i], arg, 1);
}
update_cursor (cursor.from, 0);
redraw (0, win_height, 1, 0);
}
static void
InputMethodProc (Widget w, XtPointer client_data, XtPointer call_data)
{
int idx = (int) client_data;
if (idx == -2 ? (! auto_input_method && current_input_method < 0)
: idx == -1 ? auto_input_method
: idx == current_input_method)
return;
if (auto_input_method)
{
select_input_method (-1);
XtSetArg (arg[0], XtNleftBitmap, None);
XtSetValues (InputMethodMenus[1], arg, 1);
auto_input_method = 0;
}
if (idx == -1)
{
select_input_method (-1);
XtSetArg (arg[0], XtNleftBitmap, None);
XtSetValues (InputMethodMenus[0], arg, 1);
XtSetArg (arg[0], XtNleftBitmap, CheckPixmap);
XtSetValues (InputMethodMenus[1], arg, 1);
auto_input_method = 1;
hide_cursor ();
}
else
{
select_input_method (idx);
}
}
MPlist *default_face_list;
void
FaceProc (Widget w, XtPointer client_data, XtPointer call_data)
{
int idx = (int) client_data;
int from, to;
int old_y1;
hide_cursor ();
if (! SELECTEDP ())
{
MPlist *plist;
if (idx >= 0)
{
MFace *face = mframe_get_prop (frame, Mface);
for (plist = default_face_list; mplist_key (plist) != Mnil;
plist = mplist_next (plist))
mface_merge (face, mplist_value (plist));
mplist_add (plist, Mt, *face_table[idx].face);
mface_merge (face, *face_table[idx].face);
}
else if (mplist_key (mplist_next (default_face_list)) != Mnil)
{
MFace *face = mframe_get_prop (frame, Mface);
for (plist = default_face_list;
mplist_key (mplist_next (plist)) != Mnil;
plist = mplist_next (plist))
mface_merge (face, mplist_value (plist));
mplist_pop (plist);
}
update_top (0);
update_cursor (0, 1);
redraw (0, win_height, 1, 1);
show_cursor (NULL);
return;
}
XtAppAddWorkProc (context, show_cursor, NULL);
from = mtext_property_start (selection);
to = mtext_property_end (selection);
old_y1 = sel_end.y1;
mtext_detach_property (selection);
if (idx >= 0)
{
MTextProperty *prop = mtext_property (Mface, *face_table[idx].face,
MTEXTPROP_REAR_STICKY);
mtext_push_property (mt, from, to, prop);
m17n_object_unref (prop);
}
else
mtext_pop_prop (mt, from, to, Mface);
if (from < top.to)
update_top (top.from);
update_cursor (cursor.from, 1);
select_region (from, to);
update_region (sel_start.y0, old_y1, sel_end.y1);
if (cur.y1 > win_height)
{
while (cur.y1 > win_height)
{
reseat (top.to);
update_cursor (cursor.from, 1);
}
}
}
void
LangProc (Widget w, XtPointer client_data, XtPointer call_data)
{
MSymbol sym = (MSymbol) client_data;
int from, to;
int old_y1;
if (! SELECTEDP ())
return;
XtAppAddWorkProc (context, show_cursor, NULL);
from = mtext_property_start (selection);
to = mtext_property_end (selection);
old_y1 = sel_end.y1;
mtext_detach_property (selection);
if (sym != Mnil)
mtext_put_prop (mt, from, to, Mlanguage, sym);
else
mtext_pop_prop (mt, from, to, Mlanguage);
if (from < top.to)
update_top (top.from);
update_cursor (cursor.from, 1);
select_region (from, to);
update_region (sel_start.y0, old_y1, sel_end.y1);
if (cur.y1 > win_height)
{
while (cur.y1 > win_height)
{
reseat (top.to);
update_cursor (cursor.from, 1);
}
}
}
void
DumpImageProc (Widget w, XtPointer client_data, XtPointer call_data)
{
int narrowed = (int) client_data;
FILE *mdump;
int from, to;
MConverter *converter;
if (narrowed)
{
if (! SELECTEDP ())
return;
from = mtext_property_start (selection);
to = mtext_property_end (selection);
}
else
{
from = 0;
to = nchars;
}
if (! narrowed)
mdump = popen ("mdump -q -p a4", "w");
else
mdump = popen ("mdump -q", "w");
if (! mdump)
return;
converter = mconv_stream_converter (Mcoding_utf_8_full, mdump);
mconv_encode_range (converter, mt, from, to);
mconv_free_converter (converter);
fclose (mdump);
}
void
input_status (MInputContext *ic, MSymbol command)
{
XFillRectangle (display, input_status_pixmap, gc_inv,
0, 0, input_status_width, input_status_height);
if (command == Minput_status_draw)
{
MDrawMetric rect;
mtext_put_prop (ic->status, 0, mtext_len (ic->status),
Mface, face_input_status);
if (ic->im->language != Mnil)
mtext_put_prop (ic->status, 0, mtext_len (ic->status),
Mlanguage, ic->im->language);
mdraw_text_extents (frame, ic->status, 0, mtext_len (ic->status),
&input_status_control, NULL, NULL, &rect);
mdraw_text_with_control (frame, (MDrawWindow) input_status_pixmap,
input_status_width - rect.width - 2, - rect.y,
ic->status, 0, mtext_len (ic->status),
&input_status_control);
}
XtSetArg (arg[0], XtNbitmap, input_status_pixmap);
XtSetValues (CurIMStatus, arg, 1);
}
void
surrounding_text_handler (MInputContext *ic, MSymbol command)
{
if (command == Minput_get_surrounding_text)
{
int len = (int) mplist_value (ic->plist);
int pos;
MText *surround;
if (len < 0)
{
pos = cursor.from + len;
if (pos < 0)
pos = 0;
surround = mtext_duplicate (mt, pos, cursor.from);
}
else if (len > 0)
{
pos = cursor.from + len;
if (pos > nchars)
pos = nchars;
surround = mtext_duplicate (mt, cursor.from, pos);
}
else
surround = mtext ();
mplist_set (ic->plist, Mtext, surround);
m17n_object_unref (surround);
}
else if (command == Minput_delete_surrounding_text)
{
int len = (int) mplist_value (ic->plist);
if (len < 0)
{
if (cursor.from + len < 0)
len = - cursor.from;
mtext_del (mt, cursor.from + len, cursor.from);
nchars += len;
update_cursor (cursor.from + len, 1);
}
else if (len > 0)
{
if (cursor.from + len > nchars)
len = nchars - cursor.from;
mtext_del (mt, cursor.from, cursor.from + len);
nchars -= len;
update_cursor (cursor.from, 1);
}
if (len)
mt_modified = 1;
}
}
int
compare_input_method (const void *elt1, const void *elt2)
{
const InputMethodInfo *im1 = elt1;
const InputMethodInfo *im2 = elt2;
MSymbol lang1, lang2;
if (im1->language == Mnil)
return 1;
if (im1->language == im2->language)
return strcmp (msymbol_name (im1->name), msymbol_name (im2->name));
if (im1->language == Mt)
return 1;
if (im2->language == Mt)
return -1;
lang1 = mlanguage_name (im1->language);
lang2 = mlanguage_name (im2->language);
return strcmp (msymbol_name (lang1), msymbol_name (lang2));
}
void
setup_input_methods (int with_xim, char *initial_input_method)
{
MPlist *plist = mdatabase_list (msymbol ("input-method"), Mnil, Mnil, Mnil);
MPlist *pl;
int i;
num_input_methods = plist ? mplist_length (plist) : 0;
if (with_xim)
num_input_methods++;
input_method_table = calloc (num_input_methods, sizeof (InputMethodInfo));
i = 0;
if (plist)
{
for (pl = plist; mplist_key (pl) != Mnil; pl = mplist_next (pl), i++)
{
MDatabase *mdb = mplist_value (pl);
MSymbol *tag = mdatabase_tag (mdb);
if (tag[2] == Mnil)
i--, num_input_methods--;
else
{
input_method_table[i].language = tag[1];
input_method_table[i].name = tag[2];
}
}
m17n_object_unref (plist);
}
if (with_xim)
{
input_method_table[i].language = Mnil;
input_method_table[i].name = msymbol ("xim");
i++;
}
qsort (input_method_table, num_input_methods, sizeof input_method_table[0],
compare_input_method);
mplist_put_func (minput_driver->callback_list, Minput_status_start,
M17N_FUNC (input_status));
mplist_put_func (minput_driver->callback_list, Minput_status_draw,
M17N_FUNC (input_status));
mplist_put_func (minput_driver->callback_list, Minput_status_done,
M17N_FUNC (input_status));
mplist_put_func (minput_driver->callback_list, Minput_get_surrounding_text,
M17N_FUNC (surrounding_text_handler));
mplist_put_func (minput_driver->callback_list, Minput_delete_surrounding_text,
M17N_FUNC (surrounding_text_handler));
current_input_context = NULL;
current_input_method = -1;
if (initial_input_method)
{
char *lang_name, *method_name;
char *p = strchr (initial_input_method, '-');
if (p && p[1])
lang_name = initial_input_method, *p = '\0', method_name = p + 1;
else
lang_name = "t", method_name = initial_input_method;
for (i = 0; i < num_input_methods; i++)
if ((strcmp (method_name, msymbol_name (input_method_table[i].name))
== 0)
&& (strcmp (lang_name, msymbol_name (input_method_table[i].language)) == 0))
{
current_input_method = i;
break;
}
}
}
static void
MenuHelpProc (Widget w, XEvent *event, String *str, Cardinal *num)
{
char *msg;
if (num && *num > 0)
{
int bytes = 0, i;
for (i = 0; i < *num; i++)
bytes += strlen (str[i]) + 1;
msg = alloca (bytes);
strcpy (msg, str[0]);
for (i = 1; i < *num; i++)
strcat (msg, " "), strcat (msg, str[i]);
}
else if (cursor.from < nchars)
{
int c = mtext_ref_char (mt, cursor.from);
char *name = mchar_get_prop (c, Mname);
if (! name)
name = "";
msg = alloca (10 + strlen (name));
sprintf (msg, "U+%04X %s", c, name);
}
else
{
msg = "";
}
XtSetArg (arg[0], XtNlabel, msg);
XtSetValues (MessageWidget, arg, 1);
}
typedef struct
{
int type;
char *name1, *name2;
XtCallbackProc proc;
XtPointer client_data;
int status;
Widget w;
} MenuRec;
void PopupProc (Widget w, XtPointer client_data, XtPointer call_data);
void SaveProc (Widget w, XtPointer client_data, XtPointer call_data);
MenuRec FileMenu[] =
{ { 0, "Open", NULL, PopupProc, FileMenu + 0, -1 },
{ 0, "Save", NULL, SaveProc, NULL, -1 },
{ 0, "Save as", NULL, PopupProc, FileMenu + 2, -1 },
{ 1 },
{ 0, "Serialize", NULL, SerializeProc, (void *) 1, -1 },
{ 0, "Deserialize", NULL, SerializeProc, (void *) 0, -1 },
{ 1 },
{ 0, "Dump Image Buffer", NULL, DumpImageProc, (void *) 0, -1 },
{ 0, "Dump Image Region", NULL, DumpImageProc, (void *) 1, -1 },
{ 1 },
{ 0, "Quit", NULL, QuitProc, NULL, -1 } };
void
PopupProc (Widget w, XtPointer client_data, XtPointer call_data)
{
MenuRec *rec = (MenuRec *) client_data;
Position x, y;
XtSetArg (arg[0], XtNvalue, "");
XtSetArg (arg[1], XtNlabel, rec->name1);
XtSetValues (FileDialogWidget, arg, 2);
XtTranslateCoords (w, (Position) 0, (Position) 0, &x, &y);
XtSetArg (arg[0], XtNx, x + 20);
XtSetArg (arg[1], XtNy, y + 10);
XtSetValues (FileShellWidget, arg, 2);
XtPopup (FileShellWidget, XtGrabExclusive);
}
void
FileDialogProc (Widget w, XtPointer client_data, XtPointer call_data)
{
FILE *fp;
char *label;
XtPopdown (FileShellWidget);
if ((int) client_data == 1)
return;
XtSetArg (arg[0], XtNlabel, &label);
XtGetValues (FileDialogWidget, arg, 1);
if (strcmp (label, FileMenu[0].name1) == 0)
{
/* Open a file */
free (filename);
filename = strdup ((char *) XawDialogGetValueString (FileDialogWidget));
fp = fopen (filename, "r");
hide_cursor ();
m17n_object_unref (mt);
if (fp)
{
mt = mconv_decode_stream (Mcoding_utf_8_full, fp);
fclose (fp);
if (! mt)
mt = mtext ();
}
else
mt = mtext ();
serialized = 0;
nchars = mtext_len (mt);
update_top (0);
update_cursor (0, 1);
redraw (0, win_height, 1, 1);
}
else if (strcmp (label, FileMenu[2].name1) == 0)
SaveProc (w, (XtPointer) XawDialogGetValueString (FileDialogWidget), NULL);
else
fprintf (stderr, "Invalid calling sequence: FileDialogProc\n");
}
#define SetMenu(MENU, TYPE, NAME1, NAME2, PROC, DATA, STATUS) \
((MENU).type = (TYPE), (MENU).name1 = (NAME1), (MENU).name2 = (NAME2), \
(MENU).proc = (PROC), (MENU).client_data = (XtPointer) (DATA), \
(MENU).status = (STATUS))
Widget
create_menu_button (Widget top, Widget parent, Widget left, char *button_name,
char *menu_name, MenuRec *menus, int num_menus, char *help)
{
Widget button, menu;
char *fmt = ": highlight() MenuHelp(%s)\n\
: reset() MenuHelp()\n\
: reset() PopupMenu()\n\
: highlight()";
int i;
MenuRec *m;
char *trans;
int max_width = 0;
menu = XtCreatePopupShell (menu_name, simpleMenuWidgetClass, top, NULL, 0);
for (i = 0; i < num_menus; i++)
{
m = menus + i;
if (m->type == 0)
{
if (m->proc)
{
int n = 0;
if (m->status >= 0)
{
XtSetArg (arg[n], XtNleftMargin, 20), n++;
if (m->status > 0)
XtSetArg (arg[n], XtNleftBitmap, CheckPixmap), n++;
}
m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
menu, arg, n);
XtAddCallback (m->w, XtNcallback, m->proc, m->client_data);
}
else
{
XtSetArg (arg[0], XtNsensitive, False);
m->w = XtCreateManagedWidget (m->name1, smeBSBObjectClass,
menu, arg, 2);
}
}
else
{
XtCreateManagedWidget (m->name1, smeLineObjectClass, menu, NULL, 0);
}
if (m->name2)
max_width = 1;
}
trans = alloca (strlen (fmt) + strlen (help));
sprintf (trans, fmt, help);
XtSetArg (arg[0], XtNmenuName, menu_name);
XtSetArg (arg[1], XtNtranslations, XtParseTranslationTable ((String) trans));
XtSetArg (arg[2], XtNinternalWidth, 2);
XtSetArg (arg[3], XtNhighlightThickness, 1);
XtSetArg (arg[4], XtNleft, XawChainLeft);
XtSetArg (arg[5], XtNright, XawChainLeft);
XtSetArg (arg[6], XtNinternational, True);
i = 7;
if (left)
XtSetArg (arg[i], XtNfromHoriz, left), i++;
button = XtCreateManagedWidget (button_name, menuButtonWidgetClass, parent,
arg, i);
if (max_width)
{
int height, ascent, *width = alloca (sizeof (int) * num_menus);
int *len = alloca (sizeof (int) * num_menus);
XFontSet font_set;
XFontSetExtents *fontset_extents;
XtSetArg (arg[0], XtNfontSet, &font_set);
XtGetValues (button, arg, 1);
fontset_extents = XExtentsOfFontSet (font_set);
height = fontset_extents->max_logical_extent.height;
ascent = - fontset_extents->max_logical_extent.y;
for (i = 0; i < num_menus; i++)
if (menus[i].name2)
{
len[i] = strlen (menus[i].name2);
width[i] = XmbTextEscapement (font_set, menus[i].name2, len[i]);
if (max_width < width[i])
max_width = width[i];
}
for (i = 0; i < num_menus; i++)
if (menus[i].name2)
{
Pixmap pixmap = XCreatePixmap (display,
RootWindow (display, screen),
max_width, height, 1);
XFillRectangle (display, pixmap, mono_gc_inv,
0, 0, max_width, height);
XmbDrawString (display, pixmap, font_set, mono_gc,
max_width - width[i], ascent,
menus[i].name2, len[i]);
XtSetArg (arg[0], XtNrightBitmap, pixmap);
XtSetArg (arg[1], XtNrightMargin, max_width + 20);
XtSetValues (menus[i].w, arg, 2);
}
}
return button;
}
XtActionsRec actions[] = {
{"Expose", ExposeProc},
{"Configure", ConfigureProc},
{"Key", KeyProc},
{"ButtonPress", ButtonProc},
{"ButtonRelease", ButtonReleaseProc},
{"ButtonMotion", ButtonMoveProc},
{"Button2Press", Button2Proc},
{"MenuHelp", MenuHelpProc},
{"FocusIn", FocusInProc},
{"FocusOut", FocusOutProc}
};
/* Print the usage of this program (the name is PROG), and exit with
EXIT_CODE. */
void
help_exit (char *prog, int exit_code)
{
char *p = prog;
while (*p)
if (*p++ == '/')
prog = p;
printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] FILE\n", prog);
printf ("Display FILE on a window and allow users to edit it.\n");
printf ("XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
printf ("The following OPTIONs are available.\n");
printf (" %-13s\n\t\t%s", "--fontset FONTSET",
"Use the specified fontset\n");
printf (" %-13s %s", "-s SIZE", "Font size in 1/10 point (default 120).\n");
printf (" %-13s\n\t\t%s", "--im INPUT-METHOD",
"Input method activated initially.\n");
printf (" %-13s %s", "--version", "print version number\n");
printf (" %-13s %s", "-h, --help", "print this message\n");
exit (exit_code);
}
int
main (int argc, char **argv)
{
Widget form, BodyWidget, w;
char *fontset_name = NULL;
char *font_name = NULL;
int fontsize = 0;
char *initial_input_method = NULL;
int col = 80, row = 32;
/* Translation table for TextWidget. */
String trans = ": Expose()\n\
: Configure()\n\
: Key()\n\
: Key()\n\
: ButtonPress()\n\
: ButtonRelease()\n\
: ButtonMotion()\n\
: Button2Press()";
/* Translation table for the top form widget. */
String trans2 = ": Key()\n\
: Key()\n\
: FocusIn()\n\
: FocusOut()";
String pop_face_trans
= ": MenuHelp(Pop face property) highlight()\n\
: MenuHelp() reset()\n\
: set()\n\
: notify() unset()";
String pop_lang_trans
= ": MenuHelp(Pop language property) highlight()\n\
: MenuHelp() reset()\n\
: set()\n\
: notify() unset()";
int font_width, font_ascent, font_descent;
int with_xim = 0;
int i, j;
char *filter = NULL;
MFont *font = NULL;
setlocale (LC_ALL, "");
/* Create the top shell. */
XtSetLanguageProc (NULL, NULL, NULL);
ShellWidget = XtOpenApplication (&context, "M17NEdit", NULL, 0, &argc, argv,
NULL, sessionShellWidgetClass, NULL, 0);
display = XtDisplay (ShellWidget);
screen = XScreenNumberOfScreen (XtScreen (ShellWidget));
/* Parse the remaining command line arguments. */
for (i = 1; i < argc; i++)
{
if (! strcmp (argv[i], "--help")
|| ! strcmp (argv[i], "-h"))
help_exit (argv[0], 0);
else if (! strcmp (argv[i], "--version"))
{
printf ("m17n-edit (m17n library) %s\n", M17NLIB_VERSION_NAME);
printf ("Copyright (C) 2003, 2004, 2005, 2006, 2007 AIST, JAPAN\n");
exit (0);
}
else if (! strcmp (argv[i], "--geometry"))
{
i++;
if (sscanf (argv[i], "%dx%d", &col, &row) != 2)
help_exit (argv[0], 1);
}
else if (! strcmp (argv[i], "-s"))
{
i++;
fontsize = atoi (argv[i]);
if (fontsize < 0)
fontsize = 120;
}
else if (! strcmp (argv[i], "--fontset"))
{
i++;
fontset_name = strdup (argv[i]);
}
else if (! strcmp (argv[i], "--font"))
{
i++;
font_name = strdup (argv[i]);
}
else if (! strcmp (argv[i], "--im"))
{
i++;
initial_input_method = strdup (argv[i]);
}
else if (! strcmp (argv[i], "--with-xim"))
{
with_xim = 1;
}
else if (! strcmp (argv[i], "--filter"))
{
i++;
filter = argv[i];
}
else if (argv[i][0] != '-')
{
filename = strdup (argv[i]);
}
else
{
fprintf (stderr, "Unknown option: %s\n", argv[i]);
help_exit (argv[0], 1);
}
}
if (! filename)
filename = strdup ("/dev/null");
mdatabase_dir = ".";
/* Initialize the m17n library. */
M17N_INIT ();
if (merror_code != MERROR_NONE)
FATAL_ERROR ("%s\n", "Fail to initialize the m17n library!");
minput_driver = &minput_gui_driver;
mt = read_file (filename);
serialized = 0;
nchars = mtext_len (mt);
Mword = msymbol ("word");
{
MFace *face = mface ();
mface_put_prop (face, Mforeground, msymbol ("blue"));
mface_put_prop (face, Mbackground, msymbol ("yellow"));
mface_put_prop (face, Mvideomode, Mreverse);
selection = mtext_property (Mface, face, MTEXTPROP_NO_MERGE);
m17n_object_unref (face);
}
/* This tells ExposeProc to initialize everything. */
top.from = -1;
XA_TEXT = XInternAtom (display, "TEXT", False);
XA_COMPOUND_TEXT = XInternAtom (display, "COMPOUND_TEXT", False);
XA_UTF8_STRING = XInternAtom (display, "UTF8_STRING", False);
Mcoding_compound_text = mconv_resolve_coding (msymbol ("compound-text"));
if (Mcoding_compound_text == Mnil)
FATAL_ERROR ("%s\n", "Don't know about COMPOUND-TEXT encoding!");
{
MPlist *plist = mplist ();
MFont *font;
mplist_put (plist, msymbol ("widget"), ShellWidget);
if (fontset_name || font_name || fontsize > 0)
{
MFace *face;
if (font_name)
{
font = mfont_parse_name (font_name, Mnil);
if (font)
face = mface_from_font (font);
else
face = mface ();
}
else
face = mface ();
if (fontset_name)
{
MFontset *fontset = mfontset (fontset_name);
mface_put_prop (face, Mfontset, fontset);
m17n_object_unref (fontset);
}
if (fontsize > 0)
mface_put_prop (face, Msize, (void *) fontsize);
mplist_add (plist, Mface, face);
m17n_object_unref (face);
}
frame = mframe (plist);
if (! frame)
FATAL_ERROR ("%s\n", "Fail to create a frame!");
m17n_object_unref (plist);
face_default = mface_copy ((MFace *) mframe_get_prop (frame, Mface));
default_face_list = mplist ();
mplist_add (default_face_list, Mt, face_default);
face_default_fontset = mface ();
mface_put_prop (face_default_fontset, Mfontset,
mface_get_prop (face_default, Mfontset));
font = (MFont *) mframe_get_prop (frame, Mfont);
default_font_size = (int) mfont_get_prop (font, Msize);
}
font_width = (int) mframe_get_prop (frame, Mfont_width);
font_ascent = (int) mframe_get_prop (frame, Mfont_ascent);
font_descent = (int) mframe_get_prop (frame, Mfont_descent);
win_width = font_width * col;
win_height = (font_ascent + font_descent) * row;
{
MFaceBoxProp prop;
prop.width = 4;
prop.color_top = prop.color_left = msymbol ("magenta");
prop.color_bottom = prop.color_right = msymbol ("red");
prop.inner_hmargin = prop.inner_vmargin = 1;
prop.outer_hmargin = prop.outer_vmargin = 2;
face_box = mface ();
mface_put_prop (face_box, Mbox, &prop);
}
face_courier = mface ();
mface_put_prop (face_courier, Mfamily, msymbol ("courier"));
face_helvetica = mface ();
mface_put_prop (face_helvetica, Mfamily, msymbol ("helvetica"));
face_times = mface ();
mface_put_prop (face_times, Mfamily, msymbol ("times"));
face_dv_ttyogesh = mface ();
mface_put_prop (face_dv_ttyogesh, Mfamily, msymbol ("dv-ttyogesh"));
face_freesans = mface ();
mface_put_prop (face_freesans, Mfamily, msymbol ("freesans"));
face_freeserif = mface ();
mface_put_prop (face_freeserif, Mfamily, msymbol ("freeserif"));
face_freemono = mface ();
mface_put_prop (face_freemono, Mfamily, msymbol ("freemono"));
face_xxx_large = mface ();
mface_put_prop (face_xxx_large, Mratio, (void *) 300);
{
MFont *latin_font = mframe_get_prop (frame, Mfont);
MFont *dev_font = mfont ();
MFont *thai_font = mfont ();
MFont *tib_font = mfont ();
MFontset *fontset, *fontset_no_ctl;
MSymbol unicode_bmp = msymbol ("unicode-bmp");
MSymbol no_ctl = msymbol ("no-ctl");
mfont_put_prop (dev_font, Mfamily, msymbol ("raghindi"));
mfont_put_prop (dev_font, Mregistry, unicode_bmp);
mfont_put_prop (thai_font, Mfamily, msymbol ("norasi"));
mfont_put_prop (thai_font, Mregistry, unicode_bmp);
mfont_put_prop (tib_font, Mfamily, msymbol ("mtib"));
mfont_put_prop (tib_font, Mregistry, unicode_bmp);
fontset = mfontset (fontset_name);
fontset_no_ctl = mfontset_copy (fontset, "no-ctl");
m17n_object_unref (fontset);
mfontset_modify_entry (fontset_no_ctl, msymbol ("latin"), Mnil, Mnil,
latin_font, Mnil, 0);
mfontset_modify_entry (fontset_no_ctl, msymbol ("devanagari"), Mnil, Mnil,
dev_font, no_ctl, 0);
mfontset_modify_entry (fontset_no_ctl, msymbol ("thai"), Mnil, Mnil,
thai_font, no_ctl, 0);
mfontset_modify_entry (fontset_no_ctl, msymbol ("tibetan"), Mnil, Mnil,
tib_font, no_ctl, 0);
face_no_ctl_fontset = mface ();
mface_put_prop (face_no_ctl_fontset, Mfontset, fontset_no_ctl);
m17n_object_unref (fontset_no_ctl);
free (dev_font);
free (thai_font);
free (tib_font);
}
setup_input_methods (with_xim, initial_input_method);
gc = DefaultGC (display, screen);
XtSetArg (arg[0], XtNtranslations, XtParseTranslationTable (trans2));
XtSetArg (arg[1], XtNdefaultDistance, 2);
form = XtCreateManagedWidget ("form", formWidgetClass, ShellWidget, arg, 2);
XtSetArg (arg[0], XtNborderWidth, 0);
XtSetArg (arg[1], XtNdefaultDistance, 2);
XtSetArg (arg[2], XtNtop, XawChainTop);
XtSetArg (arg[3], XtNbottom, XawChainTop);
XtSetArg (arg[4], XtNleft, XawChainLeft);
XtSetArg (arg[5], XtNright, XawChainRight);
XtSetArg (arg[6], XtNresizable, True);
HeadWidget = XtCreateManagedWidget ("head", formWidgetClass, form, arg, 7);
XtSetArg (arg[7], XtNfromVert, HeadWidget);
FaceWidget = XtCreateManagedWidget ("face", formWidgetClass, form, arg, 8);
XtSetArg (arg[7], XtNfromVert, FaceWidget);
LangWidget = XtCreateManagedWidget ("lang", formWidgetClass, form, arg, 8);
XtSetArg (arg[3], XtNbottom, XawChainBottom);
XtSetArg (arg[7], XtNfromVert, LangWidget);
BodyWidget = XtCreateManagedWidget ("body", formWidgetClass, form, arg, 8);
XtSetArg (arg[2], XtNtop, XawChainBottom);
XtSetArg (arg[7], XtNfromVert, BodyWidget);
TailWidget = XtCreateManagedWidget ("tail", formWidgetClass, form, arg, 8);
FileShellWidget = XtCreatePopupShell ("FileShell", transientShellWidgetClass,
HeadWidget, NULL, 0);
XtSetArg (arg[0], XtNvalue, "");
FileDialogWidget = XtCreateManagedWidget ("File", dialogWidgetClass,
FileShellWidget, arg, 1);
XawDialogAddButton (FileDialogWidget, "OK",
FileDialogProc, (XtPointer) 0);
XawDialogAddButton (FileDialogWidget, "CANCEL",
FileDialogProc, (XtPointer) 1);
CheckPixmap = XCreateBitmapFromData (display, RootWindow (display, screen),
(char *) check_bits,
check_width, check_height);
{
unsigned long valuemask = GCForeground;
XGCValues values;
values.foreground = 1;
mono_gc = XCreateGC (display, CheckPixmap, valuemask, &values);
values.foreground = 0;
mono_gc_inv = XCreateGC (display, CheckPixmap, valuemask, &values);
}
{
MenuRec *menus;
int num_menus = 10;
if (num_menus < num_input_methods + 2)
num_menus = num_input_methods + 2;
if (num_menus < num_faces + 1)
num_menus = num_faces + 1;
menus = alloca (sizeof (MenuRec) * num_menus);
w = create_menu_button (ShellWidget, HeadWidget, NULL, "File", "File Menu",
FileMenu, sizeof FileMenu / sizeof (MenuRec),
"File I/O, Serialization, Image, Quit");
SetMenu (menus[0], 0, "Logical Move", NULL, CursorProc, 0, 1);
SetMenu (menus[1], 0, "Visual Move", NULL, CursorProc, 1, 0);
SetMenu (menus[2], 1, "", NULL, NULL, NULL, 0);
SetMenu (menus[3], 0, "Box type", NULL, CursorProc, 2, 0);
SetMenu (menus[4], 0, "Bar type", NULL, CursorProc, 3, 1);
SetMenu (menus[5], 0, "Bidi type", NULL, CursorProc, 4, 0);
w = create_menu_button (ShellWidget, HeadWidget, w,
"Cursor", "Cursor Menu",
menus, 6, "Cursor Movement Mode, Cursor Shape");
CursorMenus[0] = menus[0].w;
CursorMenus[1] = menus[1].w;
CursorMenus[2] = menus[3].w;
CursorMenus[3] = menus[4].w;
CursorMenus[4] = menus[5].w;
SetMenu (menus[0], 0, "disable", NULL, BidiProc, 0, 0);
SetMenu (menus[1], 0, "Left (|--> |)", NULL, BidiProc, 1, 1);
SetMenu (menus[2], 0, "Right (| <--|)", NULL, BidiProc, 2, 0);
w = create_menu_button (ShellWidget, HeadWidget, w, "Bidi", "Bidi Menu",
menus, 3, "BIDI Processing Mode");
for (i = 0; i < 3; i++)
BidiMenus[i] = menus[i].w;
SetMenu (menus[0], 0, "truncate", NULL, LineBreakProc, 0, 0);
SetMenu (menus[1], 0, "break at edge", NULL, LineBreakProc, 1, 1);
SetMenu (menus[2], 0, "break at word boundary", NULL, LineBreakProc, 2, 0);
w = create_menu_button (ShellWidget, HeadWidget, w, "LineBreak",
"LineBreak Menu",
menus, 3, "How to break lines");
for (i = 0; i < 3; i++)
LineBreakMenus[i] = menus[i].w;
SetMenu (menus[0], 0, "none", NULL, InputMethodProc, -2, 1);
SetMenu (menus[1], 0, "auto", NULL, InputMethodProc, -1, 0);
for (i = 0; i < num_input_methods; i++)
{
InputMethodInfo *im = input_method_table + i;
char *name1, *name2;
if (im->language != Mnil && im->language != Mt)
{
MSymbol sym = mlanguage_name (im->language);
if (sym == Mnil)
name1 = msymbol_name (im->language);
else
name1 = msymbol_name (sym);
name2 = msymbol_name (im->name);
}
else
name1 = msymbol_name (im->name), name2 = NULL;
SetMenu (menus[i + 2], 0, name1, name2, InputMethodProc, i, 0);
}
w = create_menu_button (ShellWidget, HeadWidget, w, "InputMethod",
"Input Method Menu", menus, i + 2,
"Select input method");
{
unsigned long valuemask = GCForeground;
XGCValues values;
XtSetArg (arg[0], XtNbackground, &values.foreground);
XtGetValues (w, arg, 1);
gc_inv = XCreateGC (display, RootWindow (display, screen),
valuemask, &values);
}
InputMethodMenus = malloc (sizeof (Widget) * (num_input_methods + 2));
for (i = 0; i < num_input_methods + 2; i++)
InputMethodMenus[i] = menus[i].w;
if (filter)
{
SetMenu (menus[0], 0, filter, NULL, FilterProc, filter, 0);
w = create_menu_button (ShellWidget, HeadWidget, w, "Filter",
"Filter Menu", menus, 1,
"Select filter to run");
}
input_status_width = font_width * 8;
input_status_height = (font_ascent + font_descent) * 2.4;
input_status_pixmap = XCreatePixmap (display, RootWindow (display, screen),
input_status_width,
input_status_height,
DefaultDepth (display, screen));
{
MFaceBoxProp prop;
prop.width = 1;
prop.color_top = prop.color_bottom
= prop.color_left = prop.color_right = Mnil;
prop.inner_hmargin = prop.inner_vmargin = 1;
prop.outer_hmargin = prop.outer_vmargin = 0;
face_input_status = mface_copy (face_default);
mface_put_prop (face_input_status, Mbox, &prop);
}
XFillRectangle (display, input_status_pixmap, gc_inv,
0, 0, input_status_width, input_status_height);
XtSetArg (arg[0], XtNfromHoriz, w);
XtSetArg (arg[1], XtNleft, XawRubber);
XtSetArg (arg[2], XtNright, XawChainRight);
XtSetArg (arg[3], XtNborderWidth, 0);
XtSetArg (arg[4], XtNlabel, " ");
XtSetArg (arg[5], XtNjustify, XtJustifyRight);
CurIMLang = XtCreateManagedWidget ("CurIMLang", labelWidgetClass,
HeadWidget, arg, 6);
XtSetArg (arg[0], XtNfromHoriz, CurIMLang);
XtSetArg (arg[1], XtNleft, XawChainRight);
XtSetArg (arg[4], XtNbitmap, input_status_pixmap);
CurIMStatus = XtCreateManagedWidget ("CurIMStatus", labelWidgetClass,
HeadWidget, arg, 5);
XtSetArg (arg[0], XtNborderWidth, 0);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainLeft);
w = XtCreateManagedWidget ("Face", labelWidgetClass, FaceWidget, arg, 3);
for (i = 0; i < num_faces;)
{
char *label_menu = face_table[i++].name; /* "Menu Xxxx" */
char *label = label_menu + 5; /* "Xxxx" */
for (j = i; j < num_faces && face_table[j].face; j++)
SetMenu (menus[j - i], 0, face_table[j].name, NULL,
FaceProc, j, -1);
w = create_menu_button (ShellWidget, FaceWidget, w,
label, label_menu,
menus, j - i, "Push face property");
i = j;
}
XtSetArg (arg[0], XtNfromHoriz, w);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainLeft);
XtSetArg (arg[3], XtNhorizDistance, 10);
XtSetArg (arg[4], XtNlabel, "Pop");
XtSetArg (arg[5], XtNtranslations,
XtParseTranslationTable (pop_face_trans));
w = XtCreateManagedWidget ("Pop Face", commandWidgetClass,
FaceWidget, arg, 6);
XtAddCallback (w, XtNcallback, FaceProc, (void *) -1);
XtSetArg (arg[0], XtNfromHoriz, w);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainRight);
XtSetArg (arg[3], XtNlabel, "");
XtSetArg (arg[4], XtNborderWidth, 0);
XtSetArg (arg[5], XtNjustify, XtJustifyRight);
CurFaceWidget = XtCreateManagedWidget ("Current Face", labelWidgetClass,
FaceWidget, arg, 6);
XtSetArg (arg[0], XtNborderWidth, 0);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainLeft);
w = XtCreateManagedWidget ("Lang", labelWidgetClass, LangWidget, arg, 3);
{
MPlist *plist[11], *pl;
char langname[3];
for (i = 0; i < 11; i++) plist[i] = NULL;
langname[2] = '\0';
for (langname[0] = 'a'; langname[0] <= 'z'; langname[0]++)
for (langname[1] = 'a'; langname[1] <= 'z'; langname[1]++)
{
MSymbol sym = msymbol_exist (langname);
MSymbol fullname;
if (sym != Mnil
&& ((fullname = mlanguage_name (sym)) != Mnil))
{
char *name = msymbol_name (fullname);
char c = name[0];
if (c >= 'a' && c <= 'z')
{
int idx = (c < 'u') ? (c - 'a') / 2 : 10;
pl = plist[idx];
if (! pl)
pl = plist[idx] = mplist ();
for (; mplist_next (pl); pl = mplist_next (pl))
if (strcmp (name, (char *) mplist_value (pl)) < 0)
break;
mplist_push (pl, sym, fullname);
}
}
}
for (i = 0; i < 11; i++)
if (plist[i])
{
char *name = alloca (9);
sprintf (name, "Menu %c-%c", 'A' + i * 2, 'A' + i * 2 + 1);
if (i == 10)
name[7] = 'Z';
for (j = 0, pl = plist[i]; mplist_next (pl);
j++, pl = mplist_next (pl))
SetMenu (menus[j], 0, msymbol_name ((MSymbol) mplist_value (pl)),
msymbol_name (mplist_key (pl)),
LangProc, mplist_key (pl), -1);
w = create_menu_button (ShellWidget, LangWidget, w, name + 5, name,
menus, j, "Push language property");
}
for (i = 0; i < 11; i++)
if (plist[i])
m17n_object_unref (plist[i]);
}
XtSetArg (arg[0], XtNfromHoriz, w);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainLeft);
XtSetArg (arg[3], XtNhorizDistance, 10);
XtSetArg (arg[4], XtNlabel, "Pop");
XtSetArg (arg[5], XtNtranslations,
XtParseTranslationTable (pop_lang_trans));
w = XtCreateManagedWidget ("Pop Lang", commandWidgetClass,
LangWidget, arg, 6);
XtAddCallback (w, XtNcallback, LangProc, Mnil);
XtSetArg (arg[0], XtNfromHoriz, w);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainRight);
XtSetArg (arg[3], XtNlabel, "");
XtSetArg (arg[4], XtNborderWidth, 0);
XtSetArg (arg[5], XtNjustify, XtJustifyRight);
CurLangWidget = XtCreateManagedWidget ("Current Lang", labelWidgetClass,
LangWidget, arg, 6);
}
XtSetArg (arg[0], XtNheight, win_height);
XtSetArg (arg[1], XtNwidth, 10);
XtSetArg (arg[2], XtNleft, XawChainLeft);
XtSetArg (arg[3], XtNright, XawChainLeft);
SbarWidget = XtCreateManagedWidget ("sbar", scrollbarWidgetClass, BodyWidget,
arg, 4);
XtAddCallback (SbarWidget, XtNscrollProc, ScrollProc, NULL);
XtAddCallback (SbarWidget, XtNjumpProc, JumpProc, NULL);
XtSetArg (arg[0], XtNheight, win_height);
XtSetArg (arg[1], XtNwidth, win_width);
XtSetArg (arg[2], XtNtranslations, XtParseTranslationTable (trans));
XtSetArg (arg[3], XtNfromHoriz, SbarWidget);
XtSetArg (arg[4], XtNleft, XawChainLeft);
XtSetArg (arg[5], XtNright, XawChainRight);
TextWidget = XtCreateManagedWidget ("text", simpleWidgetClass, BodyWidget,
arg, 5);
XtSetArg (arg[0], XtNborderWidth, 0);
XtSetArg (arg[1], XtNleft, XawChainLeft);
XtSetArg (arg[2], XtNright, XawChainRight);
XtSetArg (arg[3], XtNresizable, True);
XtSetArg (arg[4], XtNjustify, XtJustifyLeft);
MessageWidget = XtCreateManagedWidget ("message", labelWidgetClass,
TailWidget, arg, 5);
memset (&control, 0, sizeof control);
control.two_dimensional = 1;
control.enable_bidi = 1;
control.anti_alias = 1;
control.min_line_ascent = font_ascent;
control.min_line_descent = font_descent;
control.max_line_width = win_width;
control.with_cursor = 1;
control.cursor_width = 2;
control.partial_update = 1;
control.ignore_formatting_char = 1;
memset (&input_status_control, 0, sizeof input_status_control);
input_status_control.enable_bidi = 1;
XtAppAddActions (context, actions, XtNumber (actions));
XtRealizeWidget (ShellWidget);
win = XtWindow (TextWidget);
XtAppMainLoop (context);
if (current_input_context)
minput_destroy_ic (current_input_context);
for (i = 0; i < num_input_methods; i++)
if (input_method_table[i].im)
minput_close_im (input_method_table[i].im);
m17n_object_unref (frame);
m17n_object_unref (mt);
m17n_object_unref (face_xxx_large);
m17n_object_unref (face_box);
m17n_object_unref (face_courier);
m17n_object_unref (face_helvetica);
m17n_object_unref (face_times);
m17n_object_unref (face_dv_ttyogesh);
m17n_object_unref (face_freesans);
m17n_object_unref (face_freeserif);
m17n_object_unref (face_freemono);
m17n_object_unref (face_default_fontset);
m17n_object_unref (face_no_ctl_fontset);
m17n_object_unref (face_input_status);
m17n_object_unref (face_default);
m17n_object_unref (default_face_list);
m17n_object_unref (selection);
if (font)
free (font);
XFreeGC (display, mono_gc);
XFreeGC (display, mono_gc_inv);
XFreeGC (display, gc_inv);
XtUninstallTranslations (form);
XtUninstallTranslations (TextWidget);
XtDestroyWidget (ShellWidget);
XtDestroyApplicationContext (context);
M17N_FINI ();
free (font_name);
free (fontset_name);
free (filename);
free (input_method_table);
free (InputMethodMenus);
exit (0);
}
#else /* not HAVE_X11_XAW_COMMAND_H */
int
main (int argc, char **argv)
{
fprintf (stderr,
"Building of this program failed (lack of some header files)\n");
exit (1);
}
#endif /* not HAVE_X11_XAW_COMMAND_H */
#endif /* not FOR_DOXYGEN */