OSDN Git Service

unify eol to lf
[jnethack/source.git] / win / X11 / wintext.c
1 /* NetHack 3.6  wintext.c       $NHDT-Date: 1450453309 2015/12/18 15:41:49 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.15 $ */
2 /* Copyright (c) Dean Luick, 1992                                 */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /*
6  * File for dealing with text windows.
7  *
8  *      + No global functions.
9  */
10
11 /*
12 **      Japanese version Copyright (C) Issei Numata, 1994-1999
13 **      changing point is marked `JP' (94/6/7) or XI18N (96/7/19)
14 **      For 3.4, Copyright (c) Kentaro Shirakata, 2002-2003
15 **      JNetHack may be freely redistributed.  See license for details. 
16 */
17
18 #ifndef SYSV
19 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
20 #endif
21
22 #include <X11/Intrinsic.h>
23 #include <X11/StringDefs.h>
24 #include <X11/Shell.h>
25 #include <X11/Xos.h>
26 #include <X11/Xaw/Form.h>
27 #include <X11/Xaw/AsciiText.h>
28 #include <X11/Xaw/Cardinals.h>
29 #include <X11/Xatom.h>
30
31 #ifdef PRESERVE_NO_SYSV
32 #ifdef SYSV
33 #undef SYSV
34 #endif
35 #undef PRESERVE_NO_SYSV
36 #endif
37
38 #include "hack.h"
39 #include "winX.h"
40 #include "xwindow.h"
41
42 #ifdef GRAPHIC_TOMBSTONE
43 #include <X11/xpm.h>
44 #endif
45
46 #ifdef INSTALLCOLORMAP
47 extern Colormap     cmap;
48 #endif
49  
50 #define TRANSIENT_TEXT /* text window is a transient window (no positioning) \
51                           */
52
53 static const char text_translations[] = "#override\n\
54      <BtnDown>: dismiss_text()\n\
55      <Key>: key_dismiss_text()";
56
57 #ifdef GRAPHIC_TOMBSTONE
58 static const char rip_translations[] = "#override\n\
59      <BtnDown>: rip_dismiss_text()\n\
60      <Key>: rip_dismiss_text()";
61
62 static Widget FDECL(create_ripout_widget, (Widget));
63 #endif
64
65 /*ARGSUSED*/
66 void
67 delete_text(w, event, params, num_params)
68 Widget w;
69 XEvent *event;
70 String *params;
71 Cardinal *num_params;
72 {
73     struct xwindow *wp;
74     struct text_info_t *text_info;
75
76     nhUse(event);
77     nhUse(params);
78     nhUse(num_params);
79
80     wp = find_widget(w);
81     text_info = wp->text_information;
82
83     nh_XtPopdown(wp->popup);
84
85     if (text_info->blocked) {
86         exit_x_event = TRUE;
87     } else if (text_info->destroy_on_ack) {
88         destroy_text_window(wp);
89     }
90 }
91
92 /*
93  * Callback used for all text windows.  The window is poped down on any key
94  * or button down event.  It is destroyed if the main nethack code is done
95  * with it.
96  */
97 /*ARGSUSED*/
98 void
99 dismiss_text(w, event, params, num_params)
100 Widget w;
101 XEvent *event;
102 String *params;
103 Cardinal *num_params;
104 {
105     struct xwindow *wp;
106     struct text_info_t *text_info;
107
108     nhUse(event);
109     nhUse(params);
110     nhUse(num_params);
111
112     wp = find_widget(w);
113     text_info = wp->text_information;
114
115     nh_XtPopdown(wp->popup);
116
117     if (text_info->blocked) {
118         exit_x_event = TRUE;
119     } else if (text_info->destroy_on_ack) {
120         destroy_text_window(wp);
121     }
122 }
123
124 /* Dismiss when a non-modifier key pressed. */
125 void
126 key_dismiss_text(w, event, params, num_params)
127 Widget w;
128 XEvent *event;
129 String *params;
130 Cardinal *num_params;
131 {
132     char ch = key_event_to_char((XKeyEvent *) event);
133     if (ch)
134         dismiss_text(w, event, params, num_params);
135 }
136
137 #ifdef GRAPHIC_TOMBSTONE
138 /* Dismiss from clicking on rip image. */
139 void
140 rip_dismiss_text(w, event, params, num_params)
141 Widget w;
142 XEvent *event;
143 String *params;
144 Cardinal *num_params;
145 {
146     dismiss_text(XtParent(w), event, params, num_params);
147 }
148 #endif
149
150 /* ARGSUSED */
151 void
152 add_to_text_window(wp, attr, str)
153 struct xwindow *wp;
154 int attr; /* currently unused */
155 const char *str;
156 {
157     struct text_info_t *text_info = wp->text_information;
158     int width;
159
160     nhUse(attr);
161
162     append_text_buffer(&text_info->text, str, FALSE);
163
164     /* Calculate text width and save longest line */
165 #ifndef XI18N
166     width = XTextWidth(text_info->fs, str, (int) strlen(str));
167 #else
168     width = XmbTextEscapement(text_info->fontset, str, (int) strlen(str));
169 #endif
170     if (width > text_info->max_width)
171         text_info->max_width = width;
172 }
173
174 void
175 display_text_window(wp, blocking)
176 struct xwindow *wp;
177 boolean blocking;
178 {
179     struct text_info_t *text_info;
180     Arg args[8];
181     Cardinal num_args;
182     Dimension width, height, font_height;
183     int nlines;
184 /*JP*/
185 #ifdef XI18N
186     XFontSetExtents *extent;
187 #endif
188
189     text_info = wp->text_information;
190     width = text_info->max_width + text_info->extra_width;
191     text_info->blocked = blocking;
192     text_info->destroy_on_ack = FALSE;
193     font_height = nhFontHeight(wp->w);
194
195 /*JP*/
196 #ifdef XI18N
197     extent = XExtentsOfFontSet(text_info->fontset);
198 #endif
199
200     /*
201      * Calculate the number of lines to use.  First, find the number of
202      * lines that would fit on the screen.  Next, remove four of these
203      * lines to give room for a possible window manager titlebar (some
204      * wm's put a titlebar on transient windows).  Make sure we have
205      * _some_ lines.  Finally, use the number of lines in the text if
206      * there are fewer than the max.
207      */
208 /*JP*/
209 #ifndef XI18N
210     nlines =
211         (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
212 #else
213     nlines =
214         (XtScreen(wp->w)->height - text_info->extra_height) / extent->max_logical_extent.height;
215 #endif
216     nlines -= 4;
217
218     if (nlines > text_info->text.num_lines)
219         nlines = text_info->text.num_lines;
220     if (nlines <= 0)
221         nlines = 1;
222
223 /*JP*/
224 #ifndef XI18N
225     height = nlines * font_height + text_info->extra_height;
226 #else
227     height = (nlines * extent->max_logical_extent.height) + text_info->extra_height;
228 #endif
229
230     num_args = 0;
231
232     if (nlines < text_info->text.num_lines) {
233         /* add on width of scrollbar.  Really should look this up,
234          * but can't until the window is realized.  Chicken-and-egg problem.
235          */
236         width += 20;
237     }
238
239 #ifdef GRAPHIC_TOMBSTONE
240     if (text_info->is_rip) {
241         Widget rip = create_ripout_widget(XtParent(wp->w));
242         XtSetArg(args[num_args], XtNfromVert, rip);
243         num_args++;
244     }
245 #endif
246
247     if (width
248         > (Dimension) XtScreen(wp->w)->width) { /* too wide for screen */
249         /* Back off some amount - we really need to back off the scrollbar */
250         /* width plus some extra.                                          */
251         width = XtScreen(wp->w)->width - 20;
252     }
253     XtSetArg(args[num_args], XtNstring, text_info->text.text);
254     num_args++;
255     XtSetArg(args[num_args], XtNwidth, width);
256     num_args++;
257     XtSetArg(args[num_args], XtNheight, height);
258     num_args++;
259     XtSetValues(wp->w, args, num_args);
260
261 #ifdef TRANSIENT_TEXT
262     XtRealizeWidget(wp->popup);
263     XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
264                     &wm_delete_window, 1);
265     positionpopup(wp->popup, FALSE);
266 #endif
267
268     nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
269
270     /* Kludge alert.  Scrollbars are not sized correctly by the Text widget */
271     /* if added before the window is displayed, so do it afterward. */
272     num_args = 0;
273     if (nlines < text_info->text.num_lines) { /* add vert scrollbar */
274         XtSetArg(args[num_args], nhStr(XtNscrollVertical),
275                  XawtextScrollAlways);
276         num_args++;
277     }
278     if (width >= (Dimension)(XtScreen(wp->w)->width - 20)) { /* too wide */
279         XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
280                  XawtextScrollAlways);
281         num_args++;
282     }
283     if (num_args)
284         XtSetValues(wp->w, args, num_args);
285
286     /* We want the user to acknowlege. */
287     if (blocking) {
288         (void) x_event(EXIT_ON_EXIT);
289         nh_XtPopdown(wp->popup);
290     }
291 }
292
293 void
294 create_text_window(wp)
295 struct xwindow *wp;
296 {
297     struct text_info_t *text_info;
298     Arg args[8];
299     Cardinal num_args;
300     Position top_margin, bottom_margin, left_margin, right_margin;
301     Widget form;
302
303     wp->type = NHW_TEXT;
304
305     wp->text_information = text_info =
306         (struct text_info_t *) alloc(sizeof(struct text_info_t));
307
308     init_text_buffer(&text_info->text);
309     text_info->max_width = 0;
310     text_info->extra_width = 0;
311     text_info->extra_height = 0;
312     text_info->blocked = FALSE;
313     text_info->destroy_on_ack = TRUE; /* Ok to destroy before display */
314 #ifdef GRAPHIC_TOMBSTONE
315     text_info->is_rip = FALSE;
316 #endif
317
318     num_args = 0;
319     XtSetArg(args[num_args], XtNallowShellResize, True);
320     num_args++;
321     XtSetArg(args[num_args], XtNtranslations,
322              XtParseTranslationTable(text_translations));
323     num_args++;
324
325 #ifdef TRANSIENT_TEXT
326     wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
327                                    toplevel, args, num_args);
328 #else
329     wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass, toplevel,
330                                    args, num_args);
331 #endif
332     XtOverrideTranslations(
333         wp->popup,
334         XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
335
336     num_args = 0;
337     XtSetArg(args[num_args], XtNallowShellResize, True);
338     num_args++;
339     form = XtCreateManagedWidget("form", formWidgetClass, wp->popup, args,
340                                  num_args);
341
342     num_args = 0;
343     XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
344     num_args++;
345     XtSetArg(args[num_args], XtNresize, XawtextResizeBoth);
346     num_args++;
347     XtSetArg(args[num_args], XtNtranslations,
348              XtParseTranslationTable(text_translations));
349     num_args++;
350 /*JP*/
351 #if defined(X11R6) && defined(XI18N)
352     XtSetArg(args[num_args], XtNinternational, True);   num_args++;
353 #endif
354
355     wp->w = XtCreateManagedWidget(killer.name[0] && WIN_MAP == WIN_ERR
356                                       ? "tombstone"
357                                       : "text_text", /* name */
358                                   asciiTextWidgetClass,
359                                   form,      /* parent widget */
360                                   args,      /* set some values */
361                                   num_args); /* number of values to set */
362
363     /* Get the font and margin information. */
364     num_args = 0;
365 #ifndef XI18N
366     XtSetArg(args[num_args], XtNfont, &text_info->fs);
367 #else
368     XtSetArg(args[num_args], XtNfontSet, &text_info->fontset);
369 #endif
370     num_args++;
371     XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
372     num_args++;
373     XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
374     num_args++;
375     XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
376     num_args++;
377     XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
378     num_args++;
379     XtGetValues(wp->w, args, num_args);
380
381     text_info->extra_width = left_margin + right_margin;
382     text_info->extra_height = top_margin + bottom_margin;
383
384 #if 1 /*JP*/
385     /* Send events to "text" which is where the translations are. */
386     XtSetKeyboardFocus (form, wp->w);
387 #endif
388 }
389
390 void
391 destroy_text_window(wp)
392 struct xwindow *wp;
393 {
394     /* Don't need to pop down, this only called from dismiss_text(). */
395
396     struct text_info_t *text_info = wp->text_information;
397
398     /*
399      * If the text window was blocked, then the user has already ACK'ed
400      * it and we are free to really destroy the window.  Otherwise, don't
401      * destroy until the user dismisses the window via a key or button
402      * press.
403      */
404     if (text_info->blocked || text_info->destroy_on_ack) {
405         XtDestroyWidget(wp->popup);
406         free_text_buffer(&text_info->text);
407         free((genericptr_t) text_info), wp->text_information = 0;
408         wp->type = NHW_NONE; /* allow reuse */
409     } else {
410         text_info->destroy_on_ack = TRUE; /* destroy on next ACK */
411     }
412 }
413
414 void
415 clear_text_window(wp)
416 struct xwindow *wp;
417 {
418     clear_text_buffer(&wp->text_information->text);
419 }
420
421 /* text buffer routines ----------------------------------------------------
422  */
423
424 /* Append a line to the text buffer. */
425 void
426 append_text_buffer(tb, str, concat)
427 struct text_buffer *tb;
428 const char *str;
429 boolean concat;
430 {
431     char *copy;
432     int length;
433
434     if (!tb->text)
435         panic("append_text_buffer:  null text buffer");
436
437     if (str) {
438         length = strlen(str);
439     } else {
440         length = 0;
441     }
442
443     if (length + tb->text_last + 1 >= tb->text_size) {
444 /* we need to go to a bigger buffer! */
445 #ifdef VERBOSE
446         printf(
447             "append_text_buffer: text buffer growing from %d to %d bytes\n",
448             tb->text_size, 2 * tb->text_size);
449 #endif
450         copy = (char *) alloc((unsigned) tb->text_size * 2);
451         (void) memcpy(copy, tb->text, tb->text_last);
452         free(tb->text);
453         tb->text = copy;
454         tb->text_size *= 2;
455     }
456
457     if (tb->num_lines) { /* not first --- append a newline */
458         char appchar = '\n';
459
460         if (concat && !index("!.?'\")", tb->text[tb->text_last - 1])) {
461             appchar = ' ';
462             tb->num_lines--; /* offset increment at end of function */
463         }
464
465         *(tb->text + tb->text_last) = appchar;
466         tb->text_last++;
467     }
468
469     if (str) {
470         (void) memcpy((tb->text + tb->text_last), str, length + 1);
471         if (length) {
472             /* Remove all newlines. Otherwise we have a confused line count.
473              */
474             copy = (tb->text + tb->text_last);
475             while ((copy = index(copy, '\n')) != (char *) 0)
476                 *copy = ' ';
477         }
478
479         tb->text_last += length;
480     }
481     tb->text[tb->text_last] = '\0';
482     tb->num_lines++;
483 }
484
485 /* Initialize text buffer. */
486 void
487 init_text_buffer(tb)
488 struct text_buffer *tb;
489 {
490     tb->text = (char *) alloc(START_SIZE);
491     tb->text[0] = '\0';
492     tb->text_size = START_SIZE;
493     tb->text_last = 0;
494     tb->num_lines = 0;
495 }
496
497 /* Empty the text buffer */
498 void
499 clear_text_buffer(tb)
500 struct text_buffer *tb;
501 {
502     tb->text_last = 0;
503     tb->text[0] = '\0';
504     tb->num_lines = 0;
505 }
506
507 /* Free up allocated memory. */
508 void
509 free_text_buffer(tb)
510 struct text_buffer *tb;
511 {
512     free(tb->text);
513     tb->text = (char *) 0;
514     tb->text_size = 0;
515     tb->text_last = 0;
516     tb->num_lines = 0;
517 }
518
519 #ifdef GRAPHIC_TOMBSTONE
520
521 static void FDECL(rip_exposed, (Widget, XtPointer, XtPointer));
522
523 static XImage *rip_image = 0;
524
525 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
526 #define NAME_LINE 0       /* line # for player name */
527 #define GOLD_LINE 1       /* line # for amount of gold */
528 #define DEATH_LINE 2      /* line # for death description */
529 #define YEAR_LINE 6       /* line # for year */
530
531 static char rip_line[YEAR_LINE + 1][STONE_LINE_LEN + 1];
532
533 void
534 calculate_rip_text(int how, time_t when)
535 {
536     /* Follows same algorithm as genl_outrip() */
537
538     char buf[BUFSZ];
539     char *dpx;
540     int line;
541     long year;
542
543     /* Put name on stone */
544     Sprintf(rip_line[NAME_LINE], "%s", plname);
545
546     /* Put $ on stone */
547     Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money);
548     /* Put together death description */
549     formatkiller(buf, sizeof buf, how, FALSE);
550
551     /* Put death type on stone */
552     for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) {
553         register int i, i0;
554         char tmpchar;
555 #if 1 /*JP*/
556         unsigned char *uc;
557         int jstone_line;
558
559         if ((i0 = strlen(dpx)) <= STONE_LINE_LEN)
560                   jstone_line = STONE_LINE_LEN;
561         else if (i0 / 2 <= STONE_LINE_LEN )
562             jstone_line = ((i0 + 3) / 4) * 2;
563         else if (i0 / 3 <= STONE_LINE_LEN )
564             jstone_line = ((i0 + 5) / 6) * 2;
565         else
566             jstone_line = ((i0+7)/8)*2;
567 #endif
568
569 #if 0 /*JP*/
570         if ((i0 = strlen(dpx)) > STONE_LINE_LEN) {
571             for (i = STONE_LINE_LEN; ((i0 > STONE_LINE_LEN) && i); i--)
572 #else
573         if (i0 > jstone_line) {
574             for(i = jstone_line; ((i0 > jstone_line) && i); i--)
575 #endif
576                 if (dpx[i] == ' ')
577                     i0 = i;
578 #if 0 /*JP*/
579             if (!i)
580                 i0 = STONE_LINE_LEN;
581 #else
582             if (!i) {
583                 i0 = 0;
584                 while(i0 < jstone_line){
585                     uc = (unsigned char *)(dpx + i0);
586                     if(*uc < 128)
587                         ++i0;
588                     else
589                         i0 += 2;
590                 }
591             }
592 #endif
593         }
594         tmpchar = dpx[i0];
595         dpx[i0] = 0;
596         strcpy(rip_line[line], dpx);
597         if (tmpchar != ' ') {
598             dpx[i0] = tmpchar;
599             dpx = &dpx[i0];
600         } else
601             dpx = &dpx[i0 + 1];
602     }
603
604     /* Put year on stone */
605     year = yyyymmdd(when) / 10000L;
606     Sprintf(rip_line[YEAR_LINE], "%4ld", year);
607 }
608
609 /*
610  * RIP image expose callback.
611  */
612 /*ARGSUSED*/
613 static void
614 rip_exposed(w, client_data, widget_data)
615 Widget w;
616 XtPointer client_data; /* unused */
617 XtPointer widget_data; /* expose event from Window widget */
618 {
619     XExposeEvent *event = (XExposeEvent *) widget_data;
620     Display *dpy = XtDisplay(w);
621     Arg args[8];
622     XGCValues values;
623     XtGCMask mask;
624     GC gc;
625     static Pixmap rip_pixmap = None;
626     int i, x, y;
627
628     if (!XtIsRealized(w) || event->count > 0)
629         return;
630
631     if (rip_pixmap == None && rip_image) {
632         rip_pixmap = XCreatePixmap(dpy, XtWindow(w), rip_image->width,
633                                    rip_image->height,
634                                    DefaultDepth(dpy, DefaultScreen(dpy)));
635         XPutImage(dpy, rip_pixmap, DefaultGC(dpy, DefaultScreen(dpy)),
636                   rip_image, 0, 0, 0, 0, /* src, dest top left */
637                   rip_image->width, rip_image->height);
638         XDestroyImage(rip_image); /* data bytes free'd also */
639     }
640
641     mask = GCFunction | GCForeground | GCGraphicsExposures | GCFont;
642     values.graphics_exposures = False;
643     XtSetArg(args[0], XtNforeground, &values.foreground);
644     XtGetValues(w, args, 1);
645     values.function = GXcopy;
646     values.font = WindowFont(w);
647     gc = XtGetGC(w, mask, &values);
648
649     if (rip_pixmap != None) {
650         XCopyArea(dpy, rip_pixmap, XtWindow(w), gc, event->x, event->y,
651                   event->width, event->height, event->x, event->y);
652     }
653
654     x = appResources.tombtext_x;
655     y = appResources.tombtext_y;
656     for (i = 0; i <= YEAR_LINE; i++) {
657         int len = strlen(rip_line[i]);
658 #ifndef XI18N
659         XFontStruct *font = WindowFontStruct(w);
660         int width = XTextWidth(font, rip_line[i], len);
661         XDrawString(dpy, XtWindow(w), gc, x - width / 2, y, rip_line[i], len);
662 #else
663         XFontSet fontset = WindowFontSet(w);
664         int width = XmbTextEscapement(fontset, rip_line[i], len);
665         XmbDrawString(dpy, XtWindow(w), fontset, gc, x - width / 2, y, rip_line[i], len);
666 #endif
667         x += appResources.tombtext_dx;
668         y += appResources.tombtext_dy;
669     }
670
671     XtReleaseGC(w, gc);
672 }
673
674 /*
675  * The ripout window creation routine.
676  */
677 static Widget
678 create_ripout_widget(Widget parent)
679 {
680     Widget imageport;
681     Arg args[16];
682     Cardinal num_args;
683
684     static int rip_width, rip_height;
685
686     if (!rip_image) {
687         XpmAttributes attributes;
688         int errorcode;
689
690 #if 0 /*JP*/
691         attributes.valuemask = XpmCloseness;
692 #else
693         attributes.valuemask = XpmCloseness | XpmColormap;
694 #endif
695 #ifdef INSTALLCOLORMAP
696         attributes.colormap = cmap;
697 #endif
698         attributes.closeness = 65535; /* Try anything */
699         errorcode =
700             XpmReadFileToImage(XtDisplay(parent), appResources.tombstone,
701                                &rip_image, 0, &attributes);
702         if (errorcode != XpmSuccess) {
703             char buf[BUFSZ];
704             Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
705                     XpmGetErrorString(errorcode));
706             X11_raw_print(buf);
707         }
708         rip_width = rip_image->width;
709         rip_height = rip_image->height;
710     }
711
712     num_args = 0;
713     XtSetArg(args[num_args], XtNwidth, rip_width);
714     num_args++;
715     XtSetArg(args[num_args], XtNheight, rip_height);
716     num_args++;
717     XtSetArg(args[num_args], XtNtranslations,
718              XtParseTranslationTable(rip_translations));
719     num_args++;
720
721     imageport = XtCreateManagedWidget("rip", windowWidgetClass, parent, args,
722                                       num_args);
723
724     XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
725
726     return imageport;
727 }
728
729 #endif /* GRAPHIC_TOMBSTONE */
730
731 /*wintext.c*/