(mtext__adjust_foramt): Extern it.
[m17n/m17n-lib.git] / example / mview.c
1 /* mview.c -- File viewer
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     @page mview view file
25
26     @section mview-synopsis SYNOPSIS
27
28     mview [ XT-OPTION ...] [ OPTION ... ] [ FILE ]
29
30     @section mview-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 #ifndef FOR_DOXYGEN
62
63 #include <stdio.h>
64
65 #include <X11/Intrinsic.h>
66 #include <X11/StringDefs.h>
67 #include <X11/Shell.h>
68 #include <X11/Xaw/Form.h>
69 #include <X11/Xaw/Command.h>
70 #include <X11/Xaw/Viewport.h>
71
72 #include <m17n-gui.h>
73 #include <m17n-misc.h>
74 #include <m17n-X.h>
75
76 #define VERSION "1.0"
77
78 /* Global m17n variables.  */
79 MFrame *frame;
80 MText *mt;
81 MDrawMetric metric;
82 MDrawControl control;
83
84
85 /* Callback procedure for "quit".  */
86
87 void
88 QuitProc (Widget w, XtPointer client_data, XtPointer call_data)
89 {
90   XtAppSetExitFlag (XtWidgetToApplicationContext (w));
91 }
92
93
94 /* Move POS to the next line head in M-text MT whose length is LEN.
95    If POS is already on the last line, set POS to LEN.  */
96
97 #define NEXTLINE(pos, len)                      \
98   do {                                          \
99     pos = mtext_character (mt, pos, len, '\n'); \
100     if (pos < 0)                                \
101       pos = len;                                \
102     else                                        \
103       pos++;                                    \
104   } while (0)
105
106
107 /* Action procedure for expose event.  Redraw partial area of the
108    widget W.  The area is specified in EVENT.  */
109
110 static void
111 ExposeProc (Widget w, XEvent *event, String *str, Cardinal *num)
112 {
113   XExposeEvent *expose = (XExposeEvent *) event;
114   int len = mtext_len (mt);
115   int pos, from, to;
116   int y = 0, yoff = 0;
117   MDrawMetric rect;
118
119   /* We must update the area between the Y-positions expose->y and
120      (expose->y + expose->height).  We ignore X-positions.  */
121
122   /* At first, find the line that occupies the Y-position expose->y.
123      That is the first line to draw.  */
124   to = 0;
125   while (1)
126     {
127       from = to;
128       NEXTLINE (to, len);
129       mdraw_text_extents (frame, mt, from, to, &control, NULL, NULL, &rect);
130       if (to == len || y + rect.height > expose->y)
131         break;
132       y += rect.height;
133     }
134   /* The first character to draw is at position FROM.  Remeber the
135      Y-position to start drawing.  */
136   yoff = y - rect.y;
137
138   /* Next, find the line that occupies the Y-position (expose->y +
139      expose->height).  That is the last line to draw.  This time, we
140      enable caching to utilize it in the later drawing.  */
141   y += rect.height;
142   control.disable_caching = 0;
143   while (to < len && y < expose->y + expose->height)
144     {
145       pos = to;
146       NEXTLINE (to, len);
147       mdraw_text_extents (frame, mt, pos, to, &control, NULL, NULL, &rect);
148       y += rect.height;
149     }
150
151   /* It is decided that we must draw from FROM to the previous
152      character of TO.  */
153   mdraw_text_with_control (frame, (MDrawWindow) XtWindow (w),
154                            0, yoff, mt, from, to, &control);
155
156   /* Disable caching again.  */
157   control.disable_caching = 1;
158
159   /* If the widget was vertically enlarged too much, shrink it.  */
160   if (metric.height < expose->y + expose->height)
161     {
162       Arg arg;
163
164       XtSetArg (arg, XtNheight, metric.height);
165       XtSetValues (w, &arg, 0);
166     }
167 }
168
169
170 /* Print the usage of this program (the name is PROG), and exit with
171    EXIT_CODE.  */
172
173 void
174 help_exit (char *prog, int exit_code)
175 {
176   char *p = prog;
177
178   while (*p)
179     if (*p++ == '/')
180       prog = p;
181
182   printf ("Usage: %s [ XT-OPTION ...] [ OPTION ...] [ FILE ]\n", prog);
183   printf ("Display FILE on a window.\n");
184   printf ("  If FILE is omitted, the input is taken from standard input.\n");
185   printf ("  XT-OPTIONs are standard Xt arguments (e.g. -fn, -fg).\n");
186   printf ("The following OPTIONs are available.\n");
187   printf ("  %-13s %s", "-e ENCODING",
188           "ENCODING is the encoding of FILE (defaults to UTF-8).\n");
189   printf ("  %-13s %s", "-s FONTSIZE",
190           "FONTSIZE is the fontsize in point.\n");
191   printf ("\t\tIf ommited, it defaults to the size\n");
192   printf ("\t\tof the default font defined in X resource.\n");
193   printf ("  %-13s %s", "--version", "print version number\n");
194   printf ("  %-13s %s", "-h, --help", "print this message\n");
195   exit (exit_code);
196 }
197
198
199 /* Format MSG by FMT and print the result to the stderr, and exit.  */
200
201 #define FATAL_ERROR(fmt, arg)   \
202   do {                          \
203     fprintf (stderr, fmt, arg); \
204     exit (1);                   \
205   } while (0)
206
207
208 /* Adjust FONTSIZE for the resolution of the screen of widget W.  */
209
210 int
211 adjust_fontsize (Widget w, int fontsize)
212 {
213   Display *display = XtDisplay (w);
214   int screen_number = XScreenNumberOfScreen (XtScreen (w));
215   double pixels = DisplayHeight (display, screen_number);
216   double mm = DisplayHeightMM (display, screen_number);
217
218   return (fontsize * pixels * 25.4 / mm / 100);
219 }
220
221
222 int
223 main (int argc, char **argv)
224 {
225   XtAppContext context;
226   Widget shell, form, quit, viewport, text;
227   String quit_action = "<KeyPress>q: set() notify() unset()";
228   XtActionsRec actions[] = { {"Expose", ExposeProc} };
229   Arg arg[10];
230   int i;
231   int viewport_width, viewport_height;
232   char *coding_name = NULL;
233   FILE *fp = stdin;
234   MSymbol coding;
235   int fontsize = 0;
236
237   /* Open an application context.  */
238   XtSetLanguageProc (NULL, NULL, NULL);
239   shell = XtOpenApplication (&context, "MView", NULL, 0, &argc, argv, NULL,
240                              sessionShellWidgetClass, NULL, 0);
241   XtAppAddActions (context, actions, XtNumber (actions));
242
243   /* Parse the remaining command line arguments.  */
244   for (i = 1; i < argc; i++)
245     {
246       if (! strcmp (argv[i], "--help")
247           || ! strcmp (argv[i], "-h"))
248         help_exit (argv[0], 0);
249       else if (! strcmp (argv[i], "--version"))
250         {
251           printf ("mview (m17n library) %s\n", VERSION);
252           printf ("Copyright (C) 2003 AIST, JAPAN\n");
253           exit (0);
254         }
255       else if (! strcmp (argv[i], "-e"))
256         {
257           i++;
258           coding_name = argv[i];
259         }
260       else if (! strcmp (argv[i], "-s"))
261         {
262           double n;
263           i++;
264           n = atof (argv[i]);
265           if (n <= 0)
266             FATAL_ERROR ("Invalid fontsize %s!\n", argv[i]);
267           fontsize = adjust_fontsize (shell, (int) (n * 10));
268         }
269       else if (argv[i][0] != '-')
270         {
271           fp = fopen (argv[i], "r");
272           if (! fp)
273             FATAL_ERROR ("Fail to open the file %s!\n", argv[i]);
274         }
275       else
276         {
277           printf ("Unknown option: %s", argv[i]);
278           help_exit (argv[0], 1);
279         }
280     }
281
282   /* Initialize the m17n library.  */
283   M17N_INIT ();
284   if (merror_code != MERROR_NONE)
285     FATAL_ERROR ("%s\n", "Fail to initialize the m17n library.");
286
287   /* Decide how to decode the input stream.  */
288   if (coding_name)
289     {
290       coding = mconv_resolve_coding (msymbol (coding_name));
291       if (coding == Mnil)
292         FATAL_ERROR ("Invalid coding: %s\n", coding_name);
293     }
294   else
295     coding = Mcoding_utf_8;
296
297   mt = mconv_decode_stream (coding, fp);
298   fclose (fp);
299   if (! mt)
300     FATAL_ERROR ("%s\n", "Fail to decode the input file or stream!");
301
302   {
303     MPlist *param = mplist ();
304     MFace *face = mface ();
305
306     if (fontsize)
307       mface_put_prop (face, Msize, (void *) fontsize);
308     mplist_put (param, Mwidget, shell);
309     mplist_put (param, Mface, face);
310     frame = mframe (param);
311     m17n_object_unref (param);
312     m17n_object_unref (face);
313   }
314
315   /* Create this widget hierarchy.
316      Shell - form -+- quit
317                    |
318                    +- viewport - text  */
319
320   form = XtCreateManagedWidget ("form", formWidgetClass, shell, NULL, 0);
321   XtSetArg (arg[0], XtNleft, XawChainLeft);
322   XtSetArg (arg[1], XtNright, XawChainLeft);
323   XtSetArg (arg[2], XtNtop, XawChainTop);
324   XtSetArg (arg[3], XtNbottom, XawChainTop);
325   XtSetArg (arg[4], XtNaccelerators, XtParseAcceleratorTable (quit_action));
326   quit = XtCreateManagedWidget ("quit", commandWidgetClass, form, arg, 5);
327   XtAddCallback (quit, XtNcallback, QuitProc, NULL);
328
329   viewport_width = (int) mframe_get_prop (frame, Mfont_width) * 80;
330   viewport_height
331     = ((int) mframe_get_prop (frame, Mfont_ascent)
332        + (int) mframe_get_prop (frame, Mfont_descent)) * 24;
333   XtSetArg (arg[0], XtNallowVert, True);
334   XtSetArg (arg[1], XtNforceBars, False);
335   XtSetArg (arg[2], XtNfromVert, quit);
336   XtSetArg (arg[3], XtNtop, XawChainTop);
337   XtSetArg (arg[4], XtNbottom, XawChainBottom);
338   XtSetArg (arg[5], XtNright, XawChainRight);
339   XtSetArg (arg[6], XtNwidth, viewport_width);
340   XtSetArg (arg[7], XtNheight, viewport_height);
341   viewport = XtCreateManagedWidget ("viewport", viewportWidgetClass, form,
342                                     arg, 8);
343
344   /* Before creating the text widget, we must calculate the height of
345      the M-text to draw.  */
346   control.two_dimensional = 1;
347   control.enable_bidi = 1;
348   control.disable_caching = 1;
349   control.max_line_width = viewport_width;
350   mdraw_text_extents (frame, mt, 0, mtext_len (mt), &control,
351                       NULL, NULL, &metric);
352
353   {
354     /* Decide the size of the text widget.  */
355     XtSetArg (arg[0], XtNwidth, viewport_width);
356     if (viewport_height > metric.height)
357       /* The outer viewport is tall enough.  */
358       XtSetArg (arg[1], XtNheight, viewport_height);
359     else if (metric.height < 0x8000)
360       /* The M-text height is within the limit of X.  */
361       XtSetArg (arg[1], XtNheight, metric.height);
362     else
363       /* We can't make such a tall widget.  Truncate it.  */
364       XtSetArg (arg[1], XtNheight, 0x7FFF);
365
366     /* We must provide our own expose event handler.  */
367     XtSetArg (arg[2], XtNtranslations,
368               XtParseTranslationTable ((String) "<Expose>: Expose()"));
369     text = XtCreateManagedWidget ("text", simpleWidgetClass, viewport, arg, 3);
370   }
371
372   /* Realize the top widget, and dive into an even loop.  */
373   XtInstallAllAccelerators (form, form);
374   XtRealizeWidget (shell);
375   XtAppMainLoop (context);
376
377   /* Clear away.  */
378   m17n_object_unref (mt);
379   m17n_object_unref (frame);
380   M17N_FINI ();
381
382   exit (0);
383 }
384 #endif /* not FOR_DOXYGEN */