NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / win / X11 / wintext.c
blobb9484b110aac6ef463d1b11c73897f9d350e789c
1 /* aNetHack 0.0.1 wintext.c $ANH-Date: 1450453309 2015/12/18 15:41:49 $ $ANH-Branch: aNetHack-3.6.0 $:$ANH-Revision: 1.15 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* aNetHack may be freely redistributed. See license for details. */
5 /*
6 * File for dealing with text windows.
8 * + No global functions.
9 */
11 #ifndef SYSV
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 #endif
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #include <X11/Shell.h>
18 #include <X11/Xos.h>
19 #include <X11/Xaw/Form.h>
20 #include <X11/Xaw/AsciiText.h>
21 #include <X11/Xaw/Cardinals.h>
22 #include <X11/Xatom.h>
24 #ifdef PRESERVE_NO_SYSV
25 #ifdef SYSV
26 #undef SYSV
27 #endif
28 #undef PRESERVE_NO_SYSV
29 #endif
31 #include "hack.h"
32 #include "winX.h"
33 #include "xwindow.h"
35 #ifdef GRAPHIC_TOMBSTONE
36 #include <X11/xpm.h>
37 #endif
39 #define TRANSIENT_TEXT /* text window is a transient window (no positioning) \
42 static const char text_translations[] = "#override\n\
43 <BtnDown>: dismiss_text()\n\
44 <Key>: key_dismiss_text()";
46 #ifdef GRAPHIC_TOMBSTONE
47 static const char rip_translations[] = "#override\n\
48 <BtnDown>: rip_dismiss_text()\n\
49 <Key>: rip_dismiss_text()";
51 static Widget FDECL(create_ripout_widget, (Widget));
52 #endif
54 /*ARGSUSED*/
55 void
56 delete_text(w, event, params, num_params)
57 Widget w;
58 XEvent *event;
59 String *params;
60 Cardinal *num_params;
62 struct xwindow *wp;
63 struct text_info_t *text_info;
65 nhUse(event);
66 nhUse(params);
67 nhUse(num_params);
69 wp = find_widget(w);
70 text_info = wp->text_information;
72 nh_XtPopdown(wp->popup);
74 if (text_info->blocked) {
75 exit_x_event = TRUE;
76 } else if (text_info->destroy_on_ack) {
77 destroy_text_window(wp);
82 * Callback used for all text windows. The window is poped down on any key
83 * or button down event. It is destroyed if the main anethack code is done
84 * with it.
86 /*ARGSUSED*/
87 void
88 dismiss_text(w, event, params, num_params)
89 Widget w;
90 XEvent *event;
91 String *params;
92 Cardinal *num_params;
94 struct xwindow *wp;
95 struct text_info_t *text_info;
97 nhUse(event);
98 nhUse(params);
99 nhUse(num_params);
101 wp = find_widget(w);
102 text_info = wp->text_information;
104 nh_XtPopdown(wp->popup);
106 if (text_info->blocked) {
107 exit_x_event = TRUE;
108 } else if (text_info->destroy_on_ack) {
109 destroy_text_window(wp);
113 /* Dismiss when a non-modifier key pressed. */
114 void
115 key_dismiss_text(w, event, params, num_params)
116 Widget w;
117 XEvent *event;
118 String *params;
119 Cardinal *num_params;
121 char ch = key_event_to_char((XKeyEvent *) event);
122 if (ch)
123 dismiss_text(w, event, params, num_params);
126 #ifdef GRAPHIC_TOMBSTONE
127 /* Dismiss from clicking on rip image. */
128 void
129 rip_dismiss_text(w, event, params, num_params)
130 Widget w;
131 XEvent *event;
132 String *params;
133 Cardinal *num_params;
135 dismiss_text(XtParent(w), event, params, num_params);
137 #endif
139 /* ARGSUSED */
140 void
141 add_to_text_window(wp, attr, str)
142 struct xwindow *wp;
143 int attr; /* currently unused */
144 const char *str;
146 struct text_info_t *text_info = wp->text_information;
147 int width;
149 nhUse(attr);
151 append_text_buffer(&text_info->text, str, FALSE);
153 /* Calculate text width and save longest line */
154 width = XTextWidth(text_info->fs, str, (int) strlen(str));
155 if (width > text_info->max_width)
156 text_info->max_width = width;
159 void
160 display_text_window(wp, blocking)
161 struct xwindow *wp;
162 boolean blocking;
164 struct text_info_t *text_info;
165 Arg args[8];
166 Cardinal num_args;
167 Dimension width, height, font_height;
168 int nlines;
170 text_info = wp->text_information;
171 width = text_info->max_width + text_info->extra_width;
172 text_info->blocked = blocking;
173 text_info->destroy_on_ack = FALSE;
174 font_height = nhFontHeight(wp->w);
177 * Calculate the number of lines to use. First, find the number of
178 * lines that would fit on the screen. Next, remove four of these
179 * lines to give room for a possible window manager titlebar (some
180 * wm's put a titlebar on transient windows). Make sure we have
181 * _some_ lines. Finally, use the number of lines in the text if
182 * there are fewer than the max.
184 nlines =
185 (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
186 nlines -= 4;
188 if (nlines > text_info->text.num_lines)
189 nlines = text_info->text.num_lines;
190 if (nlines <= 0)
191 nlines = 1;
193 height = nlines * font_height + text_info->extra_height;
195 num_args = 0;
197 if (nlines < text_info->text.num_lines) {
198 /* add on width of scrollbar. Really should look this up,
199 * but can't until the window is realized. Chicken-and-egg problem.
201 width += 20;
204 #ifdef GRAPHIC_TOMBSTONE
205 if (text_info->is_rip) {
206 Widget rip = create_ripout_widget(XtParent(wp->w));
207 XtSetArg(args[num_args], XtNfromVert, rip);
208 num_args++;
210 #endif
212 if (width
213 > (Dimension) XtScreen(wp->w)->width) { /* too wide for screen */
214 /* Back off some amount - we really need to back off the scrollbar */
215 /* width plus some extra. */
216 width = XtScreen(wp->w)->width - 20;
218 XtSetArg(args[num_args], XtNstring, text_info->text.text);
219 num_args++;
220 XtSetArg(args[num_args], XtNwidth, width);
221 num_args++;
222 XtSetArg(args[num_args], XtNheight, height);
223 num_args++;
224 XtSetValues(wp->w, args, num_args);
226 #ifdef TRANSIENT_TEXT
227 XtRealizeWidget(wp->popup);
228 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
229 &wm_delete_window, 1);
230 positionpopup(wp->popup, FALSE);
231 #endif
233 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
235 /* Kludge alert. Scrollbars are not sized correctly by the Text widget */
236 /* if added before the window is displayed, so do it afterward. */
237 num_args = 0;
238 if (nlines < text_info->text.num_lines) { /* add vert scrollbar */
239 XtSetArg(args[num_args], nhStr(XtNscrollVertical),
240 XawtextScrollAlways);
241 num_args++;
243 if (width >= (Dimension)(XtScreen(wp->w)->width - 20)) { /* too wide */
244 XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
245 XawtextScrollAlways);
246 num_args++;
248 if (num_args)
249 XtSetValues(wp->w, args, num_args);
251 /* We want the user to acknowlege. */
252 if (blocking) {
253 (void) x_event(EXIT_ON_EXIT);
254 nh_XtPopdown(wp->popup);
258 void
259 create_text_window(wp)
260 struct xwindow *wp;
262 struct text_info_t *text_info;
263 Arg args[8];
264 Cardinal num_args;
265 Position top_margin, bottom_margin, left_margin, right_margin;
266 Widget form;
268 wp->type = NHW_TEXT;
270 wp->text_information = text_info =
271 (struct text_info_t *) alloc(sizeof(struct text_info_t));
273 init_text_buffer(&text_info->text);
274 text_info->max_width = 0;
275 text_info->extra_width = 0;
276 text_info->extra_height = 0;
277 text_info->blocked = FALSE;
278 text_info->destroy_on_ack = TRUE; /* Ok to destroy before display */
279 #ifdef GRAPHIC_TOMBSTONE
280 text_info->is_rip = FALSE;
281 #endif
283 num_args = 0;
284 XtSetArg(args[num_args], XtNallowShellResize, True);
285 num_args++;
286 XtSetArg(args[num_args], XtNtranslations,
287 XtParseTranslationTable(text_translations));
288 num_args++;
290 #ifdef TRANSIENT_TEXT
291 wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
292 toplevel, args, num_args);
293 #else
294 wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass, toplevel,
295 args, num_args);
296 #endif
297 XtOverrideTranslations(
298 wp->popup,
299 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
301 num_args = 0;
302 XtSetArg(args[num_args], XtNallowShellResize, True);
303 num_args++;
304 form = XtCreateManagedWidget("form", formWidgetClass, wp->popup, args,
305 num_args);
307 num_args = 0;
308 XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
309 num_args++;
310 XtSetArg(args[num_args], XtNresize, XawtextResizeBoth);
311 num_args++;
312 XtSetArg(args[num_args], XtNtranslations,
313 XtParseTranslationTable(text_translations));
314 num_args++;
316 wp->w = XtCreateManagedWidget(killer.name[0] && WIN_MAP == WIN_ERR
317 ? "tombstone"
318 : "text_text", /* name */
319 asciiTextWidgetClass,
320 form, /* parent widget */
321 args, /* set some values */
322 num_args); /* number of values to set */
324 /* Get the font and margin information. */
325 num_args = 0;
326 XtSetArg(args[num_args], XtNfont, &text_info->fs);
327 num_args++;
328 XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
329 num_args++;
330 XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
331 num_args++;
332 XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
333 num_args++;
334 XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
335 num_args++;
336 XtGetValues(wp->w, args, num_args);
338 text_info->extra_width = left_margin + right_margin;
339 text_info->extra_height = top_margin + bottom_margin;
342 void
343 destroy_text_window(wp)
344 struct xwindow *wp;
346 /* Don't need to pop down, this only called from dismiss_text(). */
348 struct text_info_t *text_info = wp->text_information;
351 * If the text window was blocked, then the user has already ACK'ed
352 * it and we are free to really destroy the window. Otherwise, don't
353 * destroy until the user dismisses the window via a key or button
354 * press.
356 if (text_info->blocked || text_info->destroy_on_ack) {
357 XtDestroyWidget(wp->popup);
358 free_text_buffer(&text_info->text);
359 free((genericptr_t) text_info), wp->text_information = 0;
360 wp->type = NHW_NONE; /* allow reuse */
361 } else {
362 text_info->destroy_on_ack = TRUE; /* destroy on next ACK */
366 void
367 clear_text_window(wp)
368 struct xwindow *wp;
370 clear_text_buffer(&wp->text_information->text);
373 /* text buffer routines ----------------------------------------------------
376 /* Append a line to the text buffer. */
377 void
378 append_text_buffer(tb, str, concat)
379 struct text_buffer *tb;
380 const char *str;
381 boolean concat;
383 char *copy;
384 int length;
386 if (!tb->text)
387 panic("append_text_buffer: null text buffer");
389 if (str) {
390 length = strlen(str);
391 } else {
392 length = 0;
395 if (length + tb->text_last + 1 >= tb->text_size) {
396 /* we need to go to a bigger buffer! */
397 #ifdef VERBOSE
398 printf(
399 "append_text_buffer: text buffer growing from %d to %d bytes\n",
400 tb->text_size, 2 * tb->text_size);
401 #endif
402 copy = (char *) alloc((unsigned) tb->text_size * 2);
403 (void) memcpy(copy, tb->text, tb->text_last);
404 free(tb->text);
405 tb->text = copy;
406 tb->text_size *= 2;
409 if (tb->num_lines) { /* not first --- append a newline */
410 char appchar = '\n';
412 if (concat && !index("!.?'\")", tb->text[tb->text_last - 1])) {
413 appchar = ' ';
414 tb->num_lines--; /* offset increment at end of function */
417 *(tb->text + tb->text_last) = appchar;
418 tb->text_last++;
421 if (str) {
422 (void) memcpy((tb->text + tb->text_last), str, length + 1);
423 if (length) {
424 /* Remove all newlines. Otherwise we have a confused line count.
426 copy = (tb->text + tb->text_last);
427 while ((copy = index(copy, '\n')) != (char *) 0)
428 *copy = ' ';
431 tb->text_last += length;
433 tb->text[tb->text_last] = '\0';
434 tb->num_lines++;
437 /* Initialize text buffer. */
438 void
439 init_text_buffer(tb)
440 struct text_buffer *tb;
442 tb->text = (char *) alloc(START_SIZE);
443 tb->text[0] = '\0';
444 tb->text_size = START_SIZE;
445 tb->text_last = 0;
446 tb->num_lines = 0;
449 /* Empty the text buffer */
450 void
451 clear_text_buffer(tb)
452 struct text_buffer *tb;
454 tb->text_last = 0;
455 tb->text[0] = '\0';
456 tb->num_lines = 0;
459 /* Free up allocated memory. */
460 void
461 free_text_buffer(tb)
462 struct text_buffer *tb;
464 free(tb->text);
465 tb->text = (char *) 0;
466 tb->text_size = 0;
467 tb->text_last = 0;
468 tb->num_lines = 0;
471 #ifdef GRAPHIC_TOMBSTONE
473 static void FDECL(rip_exposed, (Widget, XtPointer, XtPointer));
475 static XImage *rip_image = 0;
477 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
478 #define NAME_LINE 0 /* line # for player name */
479 #define GOLD_LINE 1 /* line # for amount of gold */
480 #define DEATH_LINE 2 /* line # for death description */
481 #define YEAR_LINE 6 /* line # for year */
483 static char rip_line[YEAR_LINE + 1][STONE_LINE_LEN + 1];
485 void
486 calculate_rip_text(int how, time_t when)
488 /* Follows same algorithm as genl_outrip() */
490 char buf[BUFSZ];
491 char *dpx;
492 int line;
493 long year;
495 /* Put name on stone */
496 Sprintf(rip_line[NAME_LINE], "%s", plname);
498 /* Put $ on stone */
499 Sprintf(rip_line[GOLD_LINE], "%ld Au", done_money);
500 /* Put together death description */
501 formatkiller(buf, sizeof buf, how, FALSE);
503 /* Put death type on stone */
504 for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) {
505 register int i, i0;
506 char tmpchar;
508 if ((i0 = strlen(dpx)) > STONE_LINE_LEN) {
509 for (i = STONE_LINE_LEN; ((i0 > STONE_LINE_LEN) && i); i--)
510 if (dpx[i] == ' ')
511 i0 = i;
512 if (!i)
513 i0 = STONE_LINE_LEN;
515 tmpchar = dpx[i0];
516 dpx[i0] = 0;
517 strcpy(rip_line[line], dpx);
518 if (tmpchar != ' ') {
519 dpx[i0] = tmpchar;
520 dpx = &dpx[i0];
521 } else
522 dpx = &dpx[i0 + 1];
525 /* Put year on stone */
526 year = yyyymmdd(when) / 10000L;
527 Sprintf(rip_line[YEAR_LINE], "%4ld", year);
531 * RIP image expose callback.
533 /*ARGSUSED*/
534 static void
535 rip_exposed(w, client_data, widget_data)
536 Widget w;
537 XtPointer client_data; /* unused */
538 XtPointer widget_data; /* expose event from Window widget */
540 XExposeEvent *event = (XExposeEvent *) widget_data;
541 Display *dpy = XtDisplay(w);
542 Arg args[8];
543 XGCValues values;
544 XtGCMask mask;
545 GC gc;
546 static Pixmap rip_pixmap = None;
547 int i, x, y;
549 if (!XtIsRealized(w) || event->count > 0)
550 return;
552 if (rip_pixmap == None && rip_image) {
553 rip_pixmap = XCreatePixmap(dpy, XtWindow(w), rip_image->width,
554 rip_image->height,
555 DefaultDepth(dpy, DefaultScreen(dpy)));
556 XPutImage(dpy, rip_pixmap, DefaultGC(dpy, DefaultScreen(dpy)),
557 rip_image, 0, 0, 0, 0, /* src, dest top left */
558 rip_image->width, rip_image->height);
559 XDestroyImage(rip_image); /* data bytes free'd also */
562 mask = GCFunction | GCForeground | GCGraphicsExposures | GCFont;
563 values.graphics_exposures = False;
564 XtSetArg(args[0], XtNforeground, &values.foreground);
565 XtGetValues(w, args, 1);
566 values.function = GXcopy;
567 values.font = WindowFont(w);
568 gc = XtGetGC(w, mask, &values);
570 if (rip_pixmap != None) {
571 XCopyArea(dpy, rip_pixmap, XtWindow(w), gc, event->x, event->y,
572 event->width, event->height, event->x, event->y);
575 x = appResources.tombtext_x;
576 y = appResources.tombtext_y;
577 for (i = 0; i <= YEAR_LINE; i++) {
578 int len = strlen(rip_line[i]);
579 XFontStruct *font = WindowFontStruct(w);
580 int width = XTextWidth(font, rip_line[i], len);
581 XDrawString(dpy, XtWindow(w), gc, x - width / 2, y, rip_line[i], len);
582 x += appResources.tombtext_dx;
583 y += appResources.tombtext_dy;
586 XtReleaseGC(w, gc);
590 * The ripout window creation routine.
592 static Widget
593 create_ripout_widget(Widget parent)
595 Widget imageport;
596 Arg args[16];
597 Cardinal num_args;
599 static int rip_width, rip_height;
601 if (!rip_image) {
602 XpmAttributes attributes;
603 int errorcode;
605 attributes.valuemask = XpmCloseness;
606 attributes.closeness = 65535; /* Try anything */
607 errorcode =
608 XpmReadFileToImage(XtDisplay(parent), appResources.tombstone,
609 &rip_image, 0, &attributes);
610 if (errorcode != XpmSuccess) {
611 char buf[BUFSZ];
612 Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
613 XpmGetErrorString(errorcode));
614 X11_raw_print(buf);
616 rip_width = rip_image->width;
617 rip_height = rip_image->height;
620 num_args = 0;
621 XtSetArg(args[num_args], XtNwidth, rip_width);
622 num_args++;
623 XtSetArg(args[num_args], XtNheight, rip_height);
624 num_args++;
625 XtSetArg(args[num_args], XtNtranslations,
626 XtParseTranslationTable(rip_translations));
627 num_args++;
629 imageport = XtCreateManagedWidget("rip", windowWidgetClass, parent, args,
630 num_args);
632 XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
634 return imageport;
637 #endif /* GRAPHIC_TOMBSTONE */
639 /*wintext.c*/