/* 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. 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). 以下のオプションが利用できる。 このプログラムは 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 */