OSDN Git Service

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