(delete_char): Fix the case of deleting the previous
[m17n/m17n-lib.git] / example / mview.c
1 /* mview.c -- File viewer                               -*- coding: euc-jp; -*-
2    Copyright (C) 2003, 2004
3      National Institute of Advanced Industrial Science and Technology (AIST)
4      Registration Number H15PRO112
5
6    This file is part of the m17n library.
7
8    The m17n library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public License
10    as published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    The m17n library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with the m17n library; if not, write to the Free
20    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 /***en
24     @enpage m17n-view view file
25
26     @section m17n-view-synopsis SYNOPSIS
27
28     m17n-view [ XT-OPTION ...] [ OPTION ... ] [ FILE ]
29
30     @section m17n-view-description DESCRIPTION
31
32     Display FILE on a window.
33
34     If FILE is omitted, the input is taken from standard input.
35
36     XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).
37
38     The following OPTIONs are available.
39
40     <ul>
41
42     <li> -e ENCODING
43
44     ENCODING is the encoding of FILE (defaults to UTF-8).
45
46     <li> -s FONTSIZE
47
48     FONTSIZE is the fontsize in point.  If ommited, it defaults to the
49     size of the default font defined in X resource.
50
51     <li> --version
52
53     Print version number.
54
55     <li> -h, --help
56
57     Print this message.
58
59     </ul>
60 */
61 /***ja
62     @japage m17n-view ¥Õ¥¡¥¤¥ë¤ò¸«¤ë
63
64     @section m17n-view-synopsis SYNOPSIS
65
66     m17n-view [ XT-OPTION ...] [ OPTION ... ] [ FILE ]
67
68     @section m17n-view-description DESCRIPTION
69
70     FILE ¤ò¥¦¥£¥ó¥É¥¦¤Ëɽ¼¨¤¹¤ë¡£ 
71
72     FILE ¤¬¾Êά¤µ¤ì¤¿¾ì¹ç¤Ï¡¢É¸½àÆþÎϤ«¤é¤È¤ë¡£ 
73
74     XT-OPTIONs ¤Ï Xt ¤Îɸ½à¤Î°ú¿ô¤Ç¤¢¤ë¡£ (e.g. -fn, -fg). 
75
76     °Ê²¼¤Î¥ª¥×¥·¥ç¥ó¤¬ÍøÍѤǤ­¤ë¡£ 
77
78     <ul>
79
80     <li> -e ENCODING
81
82     ENCODING ¤Ï FILE ¤Î¥³¡¼¥É·Ï¤Ç¤¢¤ë¡£(¥Ç¥Õ¥©¥ë¥È¤Ï UTF-8) 
83
84     <li> -s FONTSIZE
85
86     FONTSIZE ¤Ï¥Õ¥©¥ó¥È¤ÎÂ礭¤µ¤ò¥Ý¥¤¥ó¥Èñ°Ì¤Ç¼¨¤·¤¿¤â¤Î¤Ç¤¢¤ë¡£¾Êά 
87     ¤µ¤ì¤¿¾ì¹ç¤Ï¡¢X ¤Î¥ê¥½¡¼¥¹¤ÇÄêµÁ¤µ¤ì¤¿¥Ç¥Õ¥©¥ë¥È¥Õ¥©¥ó¥È¤ÎÂ礭¤µ¤È 
88     ¤Ê¤ë¡£
89
90     <li> --version
91
92     ¥Ð¡¼¥¸¥ç¥óÈÖ¹æ¤òɽ¼¨¤¹¤ë¡£
93
94     <li> -h, --help
95
96    ¤³¤Î¥á¥Ã¥»¡¼¥¸¤òɽ¼¨¤¹¤ë¡£ 
97
98     </ul>
99 */
100 #ifndef FOR_DOXYGEN
101
102 #include <stdio.h>
103
104 #ifdef HAVE_X11_XAW_COMMAND_H
105
106 #include <X11/Intrinsic.h>
107 #include <X11/StringDefs.h>
108 #include <X11/Shell.h>
109
110 #include <m17n-gui.h>
111 #include <m17n-misc.h>
112 #include <m17n-X.h>
113
114 #include <X11/Xaw/Form.h>
115 #include <X11/Xaw/Command.h>
116 #include <X11/Xaw/Viewport.h>
117
118 #define VERSION "1.2.0"
119
120 /* Global m17n variables.  */
121 MFrame *frame;
122 MText *mt;
123 MDrawMetric metric;
124 MDrawControl control;
125
126
127 /* Callback procedure for "quit".  */
128
129 void
130 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
131 {
132   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
133 }
134
135
136 /* Move POS to the next line head in M-text MT whose length is LEN.
137    If POS is already on the last line, set POS to LEN.  */
138
139 #define NEXTLINE(pos, len)                      \
140   do {                                          \
141     pos = mtext_character (mt, pos, len, '\n'); \
142     if (pos < 0)                                \
143       pos = len;                                \
144     else                                        \
145       pos++;                                    \
146   } while (0)
147
148
149 /* Action procedure for expose event.  Redraw partial area of the
150    widget W.  The area is specified in EVENT.  */
151
152 static void
153 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
154 {
155   XExposeEvent *expose = (XExposeEvent *) event;
156   int len = mtext_len (mt);
157   int pos, from, to;
158   int y = 0, yoff = 0;
159   MDrawMetric rect;
160
161   /* We must update the area between the Y-positions expose->y and
162      (expose->y + expose->height).  We ignore X-positions.  */
163
164   /* At first, find the line that occupies the Y-position expose->y.
165      That is the first line to draw.  */
166   to = 0;
167   while (1)
168     {
169       from = to;
170       NEXTLINE (to, len);
171       mdraw_text_extents (frame, mt, from, to, &control, NULL, NULL, &rect);
172       if (to == len || y + rect.height > expose->y)
173         break;
174       y += rect.height;
175     }
176   /* The first character to draw is at position FROM.  Remeber the
177      Y-position to start drawing.  */
178   yoff = y - rect.y;
179
180   /* Next, find the line that occupies the Y-position (expose->y +
181      expose->height).  That is the last line to draw.  This time, we
182      enable caching to utilize it in the later drawing.  */
183   y += rect.height;
184   control.disable_caching = 0;
185   while (to < len && y < expose->y + expose->height)
186     {
187       pos = to;
188       NEXTLINE (to, len);
189       mdraw_text_extents (frame, mt, pos, to, &control, NULL, NULL, &rect);
190       y += rect.height;
191     }
192
193   /* It is decided that we must draw from FROM to the previous
194      character of TO.  */
195   mdraw_text_with_control (frame, (MDrawWindow) XtWindow (w),
196                            0, yoff, mt, from, to, &control);
197
198   /* Disable caching again.  */
199   control.disable_caching = 1;
200
201   /* If the widget was vertically enlarged too much, shrink it.  */
202   if (metric.height < expose->y + expose->height)
203     {
204       Arg arg;
205
206       XtSetArg (arg, XtNheight, metric.height);
207       XtSetValues (w, &arg, 0);
208     }
209 }
210
211
212 /* Print the usage of this program (the name is PROG), and exit with
213    EXIT_CODE.  */
214
215 void
216 help_exit (char *prog, int exit_code)
217 {
218   char *p = prog;
219
220   while (*p)
221     if (*p++ == '/')
222       prog = p;
223
224   printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] [ FILE ]\n", prog);
225   printf ("Display FILE on a window.\n");
226   printf ("  If FILE is omitted, the input is taken from standard input.\n");
227   printf ("  XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
228   printf ("The following OPTIONs are available.\n");
229   printf ("  %-13s %s", "-e ENCODING",
230           "ENCODING is the encoding of FILE (defaults to UTF-8).\n");
231   printf ("  %-13s %s", "-s FONTSIZE",
232           "FONTSIZE is the fontsize in point.\n");
233   printf ("\t\tIf ommited, it defaults to the size\n");
234   printf ("\t\tof the default font defined in X resource.\n");
235   printf ("  %-13s %s", "--version", "print version number\n");
236   printf ("  %-13s %s", "-h, --help", "print this message\n");
237   exit (exit_code);
238 }
239
240
241 /* Format MSG by FMT and print the result to the stderr, and exit.  */
242
243 #define FATAL_ERROR(fmt, arg)   \
244   do {                          \
245     fprintf (stderr, fmt, arg); \
246     exit (1);                   \
247   } while (0)
248
249
250 /* Adjust FONTSIZE for the resolution of the screen of widget W.  */
251
252 int
253 adjust_fontsize (Widget w, int fontsize)
254 {
255   Display *display = XtDisplay (w);
256   int screen_number = XScreenNumberOfScreen (XtScreen (w));
257   double pixels = DisplayHeight (display, screen_number);
258   double mm = DisplayHeightMM (display, screen_number);
259
260   return (fontsize * pixels * 25.4 / mm / 100);
261 }
262
263
264 int
265 main (int argc, char **argv)
266 {
267   XtAppContext context;
268   Widget shell, form, quit, viewport, text;
269   String quit_action = "<KeyPress>q: set() notify() unset()";
270   XtActionsRec actions[] = { {"Expose", ExposeProc} };
271   Arg arg[10];
272   int i;
273   int viewport_width, viewport_height;
274   char *coding_name = NULL;
275   FILE *fp = stdin;
276   MSymbol coding;
277   int fontsize = 0;
278
279   /* Open an application context.  */
280   XtSetLanguageProc (NULL, NULL, NULL);
281   shell = XtOpenApplication (&context, "M17NView", NULL, 0, &argc, argv, NULL,
282                              sessionShellWidgetClass, NULL, 0);
283   XtAppAddActions (context, actions, XtNumber (actions));
284
285   /* Parse the remaining command line arguments.  */
286   for (i = 1; i < argc; i++)
287     {
288       if (! strcmp (argv[i], "--help")
289           || ! strcmp (argv[i], "-h"))
290         help_exit (argv[0], 0);
291       else if (! strcmp (argv[i], "--version"))
292         {
293           printf ("m17n-view (m17n library) %s\n", VERSION);
294           printf ("Copyright (C) 2003 AIST, JAPAN\n");
295           exit (0);
296         }
297       else if (! strcmp (argv[i], "-e"))
298         {
299           i++;
300           coding_name = argv[i];
301         }
302       else if (! strcmp (argv[i], "-s"))
303         {
304           double n;
305           i++;
306           n = atof (argv[i]);
307           if (n <= 0)
308             FATAL_ERROR ("Invalid fontsize %s!\n", argv[i]);
309           fontsize = adjust_fontsize (shell, (int) (n * 10));
310         }
311       else if (argv[i][0] != '-')
312         {
313           fp = fopen (argv[i], "r");
314           if (! fp)
315             FATAL_ERROR ("Fail to open the file %s!\n", argv[i]);
316         }
317       else
318         {
319           printf ("Unknown option: %s", argv[i]);
320           help_exit (argv[0], 1);
321         }
322     }
323
324   /* Initialize the m17n library.  */
325   M17N_INIT ();
326   if (merror_code != MERROR_NONE)
327     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library.");
328
329   /* Decide how to decode the input stream.  */
330   if (coding_name)
331     {
332       coding = mconv_resolve_coding (msymbol (coding_name));
333       if (coding == Mnil)
334         FATAL_ERROR ("Invalid coding: %s\n", coding_name);
335     }
336   else
337     coding = Mcoding_utf_8;
338
339   mt = mconv_decode_stream (coding, fp);
340   fclose (fp);
341   if (! mt)
342     FATAL_ERROR ("%s\n", "Fail to decode the input file or stream!");
343
344   {
345     MPlist *param = mplist ();
346     MFace *face = mface ();
347
348     if (fontsize)
349       mface_put_prop (face, Msize, (void *) fontsize);
350     mplist_put (param, Mwidget, shell);
351     mplist_put (param, Mface, face);
352     frame = mframe (param);
353     m17n_object_unref (param);
354     m17n_object_unref (face);
355   }
356
357   /* Create this widget hierarchy.
358      Shell - form -+- quit
359                    |
360                    +- viewport - text  */
361
362   form = XtCreateManagedWidget ("form", formWidgetClass, shell, NULL, 0);
363   XtSetArg (arg[0], XtNleft, XawChainLeft);
364   XtSetArg (arg[1], XtNright, XawChainLeft);
365   XtSetArg (arg[2], XtNtop, XawChainTop);
366   XtSetArg (arg[3], XtNbottom, XawChainTop);
367   XtSetArg (arg[4], XtNaccelerators, XtParseAcceleratorTable (quit_action));
368   quit = XtCreateManagedWidget ("quit", commandWidgetClass, form, arg, 5);
369   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
370
371   viewport_width = (int) mframe_get_prop (frame, Mfont_width) * 80;
372   viewport_height
373     = ((int) mframe_get_prop (frame, Mfont_ascent)
374        + (int) mframe_get_prop (frame, Mfont_descent)) * 24;
375   XtSetArg (arg[0], XtNallowVert, True);
376   XtSetArg (arg[1], XtNforceBars, False);
377   XtSetArg (arg[2], XtNfromVert, quit);
378   XtSetArg (arg[3], XtNtop, XawChainTop);
379   XtSetArg (arg[4], XtNbottom, XawChainBottom);
380   XtSetArg (arg[5], XtNright, XawChainRight);
381   XtSetArg (arg[6], XtNwidth, viewport_width);
382   XtSetArg (arg[7], XtNheight, viewport_height);
383   viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, form,
384                                     arg, 8);
385
386   /* Before creating the text widget, we must calculate the height of
387      the M-text to draw.  */
388   control.two_dimensional = 1;
389   control.enable_bidi = 1;
390   control.disable_caching = 1;
391   control.max_line_width = viewport_width;
392   mdraw_text_extents (frame, mt, 0, mtext_len (mt), &control,
393                       NULL, NULL, &metric);
394
395   {
396     /* Decide the size of the text widget.  */
397     XtSetArg (arg[0], XtNwidth, viewport_width);
398     if (viewport_height > metric.height)
399       /* The outer viewport is tall enough.  */
400       XtSetArg (arg[1], XtNheight, viewport_height);
401     else if (metric.height < 0x8000)
402       /* The M-text height is within the limit of X.  */
403       XtSetArg (arg[1], XtNheight, metric.height);
404     else
405       /* We can't make such a tall widget.  Truncate it.  */
406       XtSetArg (arg[1], XtNheight, 0x7FFF);
407
408     /* We must provide our own expose event handler.  */
409     XtSetArg (arg[2], XtNtranslations,
410               XtParseTranslationTable ((String) "<Expose>: Expose()"));
411     text = XtCreateManagedWidget ("text", simpleWidgetClass, viewport, arg, 3);
412   }
413
414   /* Realize the top widget, and dive into an even loop.  */
415   XtInstallAllAccelerators (form, form);
416   XtRealizeWidget (shell);
417   XtAppMainLoop (context);
418
419   /* Clear away.  */
420   m17n_object_unref (mt);
421   m17n_object_unref (frame);
422   M17N_FINI ();
423
424   exit (0);
425 }
426
427 #else  /* not HAVE_X11_XAW_COMMAND_H */
428
429 int
430 main (int argc, char **argv)
431 {
432   fprintf (stderr,
433            "Building of this program failed (lack of some header files)\n");
434   exit (1);
435 }
436
437 #endif /* not HAVE_X11_XAW_COMMAND_H */
438
439 #endif /* not FOR_DOXYGEN */