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. */
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. */
11 * File for dealing with text windows.
13 * + No global functions.
17 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
20 #include <X11/Intrinsic.h>
21 #include <X11/StringDefs.h>
22 #include <X11/Shell.h>
24 #include <X11/Xaw/Form.h>
25 #include <X11/Xaw/AsciiText.h>
26 #include <X11/Xaw/Cardinals.h>
27 #include <X11/Xatom.h>
29 #ifdef PRESERVE_NO_SYSV
33 #undef PRESERVE_NO_SYSV
40 #ifdef GRAPHIC_TOMBSTONE
44 #ifdef INSTALLCOLORMAP
48 #define TRANSIENT_TEXT /* text window is a transient window (no positioning) \
51 static const char text_translations[] = "#override\n\
52 <BtnDown>: dismiss_text()\n\
53 <Key>: key_dismiss_text()";
55 #ifdef GRAPHIC_TOMBSTONE
56 static const char rip_translations[] = "#override\n\
57 <BtnDown>: rip_dismiss_text()\n\
58 <Key>: rip_dismiss_text()";
60 static Widget FDECL(create_ripout_widget, (Widget));
65 delete_text(w, event, params, num_params)
72 struct text_info_t *text_info;
79 text_info = wp->text_information;
81 nh_XtPopdown(wp->popup);
83 if (text_info->blocked) {
85 } else if (text_info->destroy_on_ack) {
86 destroy_text_window(wp);
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
97 dismiss_text(w, event, params, num_params)
101 Cardinal *num_params;
104 struct text_info_t *text_info;
111 text_info = wp->text_information;
113 nh_XtPopdown(wp->popup);
115 if (text_info->blocked) {
117 } else if (text_info->destroy_on_ack) {
118 destroy_text_window(wp);
122 /* Dismiss when a non-modifier key pressed. */
124 key_dismiss_text(w, event, params, num_params)
128 Cardinal *num_params;
130 char ch = key_event_to_char((XKeyEvent *) event);
132 dismiss_text(w, event, params, num_params);
135 #ifdef GRAPHIC_TOMBSTONE
136 /* Dismiss from clicking on rip image. */
138 rip_dismiss_text(w, event, params, num_params)
142 Cardinal *num_params;
144 dismiss_text(XtParent(w), event, params, num_params);
150 add_to_text_window(wp, attr, str)
152 int attr; /* currently unused */
155 struct text_info_t *text_info = wp->text_information;
160 append_text_buffer(&text_info->text, str, FALSE);
162 /* Calculate text width and save longest line */
164 width = XTextWidth(text_info->fs, str, (int) strlen(str));
166 width = XmbTextEscapement(text_info->fontset, str, (int) strlen(str));
168 if (width > text_info->max_width)
169 text_info->max_width = width;
173 display_text_window(wp, blocking)
177 struct text_info_t *text_info;
180 Dimension width, height, font_height;
184 XFontSetExtents *extent;
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);
195 extent = XExtentsOfFontSet(text_info->fontset);
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.
208 nlines = (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
211 (XtScreen(wp->w)->height - text_info->extra_height) / extent->max_logical_extent.height;
215 if (nlines > text_info->text.num_lines)
216 nlines = text_info->text.num_lines;
222 height = nlines * font_height + text_info->extra_height;
224 height = (nlines * extent->max_logical_extent.height) + text_info->extra_height;
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.
236 #ifdef GRAPHIC_TOMBSTONE
237 if (text_info->is_rip) {
238 Widget rip = create_ripout_widget(XtParent(wp->w));
241 XtSetArg(args[num_args], nhStr(XtNfromVert), rip);
244 text_info->is_rip = FALSE;
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;
253 XtSetArg(args[num_args], XtNstring, text_info->text.text);
255 XtSetArg(args[num_args], XtNwidth, width);
257 XtSetArg(args[num_args], XtNheight, height);
259 XtSetValues(wp->w, args, num_args);
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);
268 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
270 /* Kludge alert. Scrollbars are not sized correctly by the Text widget */
271 /* if added before the window is displayed, so do it afterward. */
273 if (nlines < text_info->text.num_lines) { /* add vert scrollbar */
274 XtSetArg(args[num_args], nhStr(XtNscrollVertical),
275 XawtextScrollAlways);
278 if (width >= (Dimension)(XtScreen(wp->w)->width - 20)) { /* too wide */
279 XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
280 XawtextScrollAlways);
284 XtSetValues(wp->w, args, num_args);
286 /* We want the user to acknowlege. */
288 (void) x_event(EXIT_ON_EXIT);
289 nh_XtPopdown(wp->popup);
294 create_text_window(wp)
297 struct text_info_t *text_info;
300 Position top_margin, bottom_margin, left_margin, right_margin;
305 wp->text_information = text_info =
306 (struct text_info_t *) alloc(sizeof(struct text_info_t));
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;
319 XtSetArg(args[num_args], XtNallowShellResize, True), num_args++;
320 XtSetArg(args[num_args], XtNtranslations,
321 XtParseTranslationTable(text_translations)), num_args++;
323 #ifdef TRANSIENT_TEXT
324 wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
325 toplevel, args, num_args);
327 wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass, toplevel,
330 XtOverrideTranslations(
332 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
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,
342 XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
344 XtSetArg(args[num_args], XtNresize, XawtextResizeBoth);
346 XtSetArg(args[num_args], XtNtranslations,
347 XtParseTranslationTable(text_translations));
350 #if defined(X11R6) && defined(XI18N)
351 XtSetArg(args[num_args], XtNinternational, True); num_args++;
354 wp->w = XtCreateManagedWidget(killer.name[0] && WIN_MAP == WIN_ERR
356 : "text_text", /* name */
357 asciiTextWidgetClass,
358 form, /* parent widget */
359 args, /* set some values */
360 num_args); /* number of values to set */
362 /* Get the font and margin information. */
365 XtSetArg(args[num_args], XtNfont, &text_info->fs);
367 XtSetArg(args[num_args], XtNfontSet, &text_info->fontset);
370 XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
372 XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
374 XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
376 XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
378 XtGetValues(wp->w, args, num_args);
380 text_info->extra_width = left_margin + right_margin;
381 text_info->extra_height = top_margin + bottom_margin;
384 /* Send events to "text" which is where the translations are. */
385 XtSetKeyboardFocus (form, wp->w);
390 destroy_text_window(wp)
393 /* Don't need to pop down, this only called from dismiss_text(). */
395 struct text_info_t *text_info = wp->text_information;
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
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 */
409 text_info->destroy_on_ack = TRUE; /* destroy on next ACK */
414 clear_text_window(wp)
417 clear_text_buffer(&wp->text_information->text);
420 /* text buffer routines ----------------------------------------------------
423 /* Append a line to the text buffer. */
425 append_text_buffer(tb, str, concat)
426 struct text_buffer *tb;
434 panic("append_text_buffer: null text buffer");
437 length = strlen(str);
442 if (length + tb->text_last + 1 >= tb->text_size) {
443 /* we need to go to a bigger buffer! */
446 "append_text_buffer: text buffer growing from %d to %d bytes\n",
447 tb->text_size, 2 * tb->text_size);
449 copy = (char *) alloc((unsigned) tb->text_size * 2);
450 (void) memcpy(copy, tb->text, tb->text_last);
456 if (tb->num_lines) { /* not first --- append a newline */
459 if (concat && !index("!.?'\")", tb->text[tb->text_last - 1])) {
461 tb->num_lines--; /* offset increment at end of function */
464 *(tb->text + tb->text_last) = appchar;
469 (void) memcpy((tb->text + tb->text_last), str, length + 1);
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)
477 tb->text_last += length;
479 tb->text[tb->text_last] = '\0';
483 /* Initialize text buffer. */
486 struct text_buffer *tb;
488 tb->text = (char *) alloc(START_SIZE);
490 tb->text_size = START_SIZE;
495 /* Empty the text buffer */
497 clear_text_buffer(tb)
498 struct text_buffer *tb;
505 /* Free up allocated memory. */
508 struct text_buffer *tb;
511 tb->text = (char *) 0;
517 #ifdef GRAPHIC_TOMBSTONE
519 static void FDECL(rip_exposed, (Widget, XtPointer, XtPointer));
521 static XImage *rip_image = 0;
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 */
529 static char rip_line[YEAR_LINE + 1][STONE_LINE_LEN + 1];
532 calculate_rip_text(int how, time_t when)
534 /* Follows same algorithm as genl_outrip() */
541 /* Put name on stone */
542 Sprintf(rip_line[NAME_LINE], "%s", plname);
545 Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money);
546 /* Put together death description */
547 formatkiller(buf, sizeof buf, how, FALSE);
549 /* Put death type on stone */
550 for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) {
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;
564 jstone_line = ((i0+7)/8)*2;
568 if ((i0 = strlen(dpx)) > STONE_LINE_LEN) {
569 for (i = STONE_LINE_LEN; ((i0 > STONE_LINE_LEN) && i); i--)
571 if (i0 > jstone_line) {
572 for(i = jstone_line; ((i0 > jstone_line) && i); i--)
582 while(i0 < jstone_line){
583 uc = (unsigned char *)(dpx + i0);
594 strcpy(rip_line[line], dpx);
595 if (tmpchar != ' ') {
602 /* Put year on stone */
603 year = yyyymmdd(when) / 10000L;
604 Sprintf(rip_line[YEAR_LINE], "%4ld", year);
608 * RIP image expose callback.
612 rip_exposed(w, client_data, widget_data)
614 XtPointer client_data UNUSED;
615 XtPointer widget_data; /* expose event from Window widget */
617 XExposeEvent *event = (XExposeEvent *) widget_data;
618 Display *dpy = XtDisplay(w);
623 static Pixmap rip_pixmap = None;
626 if (!XtIsRealized(w) || event->count > 0)
629 if (rip_pixmap == None && rip_image) {
630 rip_pixmap = XCreatePixmap(dpy, XtWindow(w), rip_image->width,
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 */
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);
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);
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]);
657 XFontStruct *font = WindowFontStruct(w);
658 int width = XTextWidth(font, rip_line[i], len);
660 XDrawString(dpy, XtWindow(w), gc, x - width / 2, y, rip_line[i], len);
662 XFontSet fontset = WindowFontSet(w);
663 int width = XmbTextEscapement(fontset, rip_line[i], len);
665 XmbDrawString(dpy, XtWindow(w), fontset, gc, x - width / 2, y, rip_line[i], len);
667 x += appResources.tombtext_dx;
668 y += appResources.tombtext_dy;
675 * The ripout window creation routine.
678 create_ripout_widget(Widget parent)
684 static int rip_width, rip_height;
687 XpmAttributes attributes;
691 attributes.valuemask = XpmCloseness;
693 attributes.valuemask = XpmCloseness | XpmColormap;
695 #ifdef INSTALLCOLORMAP
696 attributes.colormap = cmap;
698 attributes.closeness = 65535; /* Try anything */
699 errorcode = XpmReadFileToImage(XtDisplay(parent),
700 appResources.tombstone,
701 &rip_image, 0, &attributes);
702 if (errorcode != XpmSuccess) {
705 Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
706 XpmGetErrorString(errorcode));
710 rip_width = rip_image->width;
711 rip_height = rip_image->height;
715 XtSetArg(args[num_args], XtNwidth, rip_width);
717 XtSetArg(args[num_args], XtNheight, rip_height);
719 XtSetArg(args[num_args], XtNtranslations,
720 XtParseTranslationTable(rip_translations));
723 imageport = XtCreateManagedWidget("rip", windowWidgetClass, parent, args,
726 XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
731 #endif /* GRAPHIC_TOMBSTONE */