1 /* SCCS Id: @(#)wintext.c 3.4 1996/04/05 */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
6 * File for dealing with text windows.
8 * + No global functions.
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #include <X11/Shell.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
28 # undef PRESERVE_NO_SYSV
35 #ifdef GRAPHIC_TOMBSTONE
40 #define TRANSIENT_TEXT /* text window is a transient window (no positioning) */
42 static const char text_translations[] =
44 <BtnDown>: dismiss_text()\n\
45 <Key>: key_dismiss_text()";
47 #ifdef GRAPHIC_TOMBSTONE
48 static const char rip_translations[] =
50 <BtnDown>: rip_dismiss_text()\n\
51 <Key>: rip_dismiss_text()";
53 static Widget FDECL(create_ripout_widget, (Widget));
58 delete_text(w, event, params, num_params)
65 struct text_info_t *text_info;
68 text_info = wp->text_information;
70 nh_XtPopdown(wp->popup);
72 if (text_info->blocked) {
74 } else if (text_info->destroy_on_ack) {
75 destroy_text_window(wp);
80 * Callback used for all text windows. The window is poped down on any key
81 * or button down event. It is destroyed if the main nethack code is done
86 dismiss_text(w, event, params, num_params)
93 struct text_info_t *text_info;
96 text_info = wp->text_information;
98 nh_XtPopdown(wp->popup);
100 if (text_info->blocked) {
102 } else if (text_info->destroy_on_ack) {
103 destroy_text_window(wp);
107 /* Dismiss when a non-modifier key pressed. */
109 key_dismiss_text(w, event, params, num_params)
113 Cardinal *num_params;
115 char ch = key_event_to_char((XKeyEvent *) event);
116 if (ch) dismiss_text(w, event, params, num_params);
119 #ifdef GRAPHIC_TOMBSTONE
120 /* Dismiss from clicking on rip image. */
122 rip_dismiss_text(w, event, params, num_params)
126 Cardinal *num_params;
128 dismiss_text(XtParent(w), event, params, num_params);
135 add_to_text_window(wp, attr, str)
137 int attr; /* currently unused */
140 struct text_info_t *text_info = wp->text_information;
143 append_text_buffer(&text_info->text, str, FALSE);
145 /* Calculate text width and save longest line */
146 width = XTextWidth(text_info->fs, str, (int) strlen(str));
147 if (width > text_info->max_width)
148 text_info->max_width = width;
152 display_text_window(wp, blocking)
156 struct text_info_t *text_info;
159 Dimension width, height, font_height;
162 text_info = wp->text_information;
163 width = text_info->max_width + text_info->extra_width;
164 text_info->blocked = blocking;
165 text_info->destroy_on_ack = FALSE;
166 font_height = nhFontHeight(wp->w);
169 * Calculate the number of lines to use. First, find the number of
170 * lines that would fit on the screen. Next, remove four of these
171 * lines to give room for a possible window manager titlebar (some
172 * wm's put a titlebar on transient windows). Make sure we have
173 * _some_ lines. Finally, use the number of lines in the text if
174 * there are fewer than the max.
176 nlines = (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
179 if (nlines > text_info->text.num_lines)
180 nlines = text_info->text.num_lines;
181 if (nlines <= 0) nlines = 1;
183 height = nlines * font_height + text_info->extra_height;
187 if (nlines < text_info->text.num_lines) {
188 /* add on width of scrollbar. Really should look this up,
189 * but can't until the window is realized. Chicken-and-egg problem.
194 #ifdef GRAPHIC_TOMBSTONE
195 if (text_info->is_rip) {
196 Widget rip = create_ripout_widget(XtParent(wp->w));
197 XtSetArg(args[num_args], XtNfromVert, rip); num_args++;
201 if (width > (Dimension) XtScreen(wp->w)->width) { /* too wide for screen */
202 /* Back off some amount - we really need to back off the scrollbar */
203 /* width plus some extra. */
204 width = XtScreen(wp->w)->width - 20;
206 XtSetArg(args[num_args], XtNstring, text_info->text.text); num_args++;
207 XtSetArg(args[num_args], XtNwidth, width); num_args++;
208 XtSetArg(args[num_args], XtNheight, height); num_args++;
209 XtSetValues(wp->w, args, num_args);
211 #ifdef TRANSIENT_TEXT
212 XtRealizeWidget(wp->popup);
213 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
214 &wm_delete_window, 1);
215 positionpopup(wp->popup, FALSE);
218 nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
220 /* Kludge alert. Scrollbars are not sized correctly by the Text widget */
221 /* if added before the window is displayed, so do it afterward. */
223 if (nlines < text_info->text.num_lines) { /* add vert scrollbar */
224 XtSetArg(args[num_args], XtNscrollVertical, XawtextScrollAlways);
227 if (width >= (Dimension) (XtScreen(wp->w)->width-20)) { /* too wide */
228 XtSetArg(args[num_args], XtNscrollHorizontal, XawtextScrollAlways);
231 if (num_args) XtSetValues(wp->w, args, num_args);
233 /* We want the user to acknowlege. */
235 (void) x_event(EXIT_ON_EXIT);
236 nh_XtPopdown(wp->popup);
242 create_text_window(wp)
245 struct text_info_t *text_info;
248 Position top_margin, bottom_margin, left_margin, right_margin;
253 wp->text_information = text_info =
254 (struct text_info_t *) alloc(sizeof(struct text_info_t));
256 init_text_buffer(&text_info->text);
257 text_info->max_width = 0;
258 text_info->extra_width = 0;
259 text_info->extra_height = 0;
260 text_info->blocked = FALSE;
261 text_info->destroy_on_ack = TRUE; /* Ok to destroy before display */
262 #ifdef GRAPHIC_TOMBSTONE
263 text_info->is_rip = FALSE;
267 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
268 XtSetArg(args[num_args], XtNtranslations,
269 XtParseTranslationTable(text_translations)); num_args++;
271 #ifdef TRANSIENT_TEXT
272 wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
273 toplevel, args, num_args);
275 wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass,
276 toplevel, args, num_args);
278 XtOverrideTranslations(wp->popup,
279 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
282 XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
283 form = XtCreateManagedWidget("form", formWidgetClass, wp->popup,
287 XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++;
288 XtSetArg(args[num_args], XtNresize, XawtextResizeBoth); num_args++;
289 XtSetArg(args[num_args], XtNtranslations,
290 XtParseTranslationTable(text_translations)); num_args++;
292 wp->w = XtCreateManagedWidget(
293 killer && WIN_MAP == WIN_ERR ?
294 "tombstone" : "text_text", /* name */
295 asciiTextWidgetClass,
296 form, /* parent widget */
297 args, /* set some values */
298 num_args); /* number of values to set */
300 /* Get the font and margin information. */
302 XtSetArg(args[num_args], XtNfont, &text_info->fs); num_args++;
303 XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++;
304 XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++;
305 XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++;
306 XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++;
307 XtGetValues(wp->w, args, num_args);
309 text_info->extra_width = left_margin + right_margin;
310 text_info->extra_height = top_margin + bottom_margin;
314 destroy_text_window(wp)
317 /* Don't need to pop down, this only called from dismiss_text(). */
319 struct text_info_t *text_info = wp->text_information;
322 * If the text window was blocked, then the user has already ACK'ed
323 * it and we are free to really destroy the window. Otherwise, don't
324 * destroy until the user dismisses the window via a key or button
327 if (text_info->blocked || text_info->destroy_on_ack) {
328 XtDestroyWidget(wp->popup);
329 free_text_buffer(&text_info->text);
330 free((genericptr_t)text_info), wp->text_information = 0;
331 wp->type = NHW_NONE; /* allow reuse */
333 text_info->destroy_on_ack = TRUE; /* destroy on next ACK */
338 clear_text_window(wp)
341 clear_text_buffer(&wp->text_information->text);
345 /* text buffer routines ---------------------------------------------------- */
347 /* Append a line to the text buffer. */
349 append_text_buffer(tb, str, concat)
350 struct text_buffer *tb;
357 if (!tb->text) panic("append_text_buffer: null text buffer");
360 length = strlen(str);
365 if (length + tb->text_last + 1 >= tb->text_size) {
366 /* we need to go to a bigger buffer! */
368 printf("append_text_buffer: text buffer growing from %d to %d bytes\n",
369 tb->text_size, 2*tb->text_size);
371 copy = (char *) alloc((unsigned)tb->text_size*2);
372 (void) memcpy(copy, tb->text, tb->text_last);
378 if (tb->num_lines) { /* not first --- append a newline */
381 if(concat && !index("!.?'\")", tb->text[tb->text_last-1])) {
383 tb->num_lines--; /* offset increment at end of function */
386 *(tb->text + tb->text_last) = appchar;
391 (void) memcpy((tb->text+tb->text_last), str, length+1);
393 /* Remove all newlines. Otherwise we have a confused line count. */
394 copy = (tb->text+tb->text_last);
395 while ((copy = index(copy, '\n')) != (char*)0)
399 tb->text_last += length;
401 tb->text[tb->text_last] = '\0';
405 /* Initialize text buffer. */
408 struct text_buffer *tb;
410 tb->text = (char *) alloc(START_SIZE);
412 tb->text_size = START_SIZE;
417 /* Empty the text buffer */
419 clear_text_buffer(tb)
420 struct text_buffer *tb;
427 /* Free up allocated memory. */
430 struct text_buffer *tb;
433 tb->text = (char *) 0;
440 #ifdef GRAPHIC_TOMBSTONE
442 static void FDECL(rip_exposed, (Widget,XtPointer,XtPointer));
444 static XImage* rip_image=0;
447 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
448 #define NAME_LINE 0 /* line # for player name */
449 #define GOLD_LINE 1 /* line # for amount of gold */
450 #define DEATH_LINE 2 /* line # for death description */
451 #define YEAR_LINE 6 /* line # for year */
453 static char rip_line[YEAR_LINE+1][STONE_LINE_LEN+1];
455 extern const char *killed_by_prefix[];
458 calculate_rip_text(int how)
460 /* Follows same algorithm as genl_outrip() */
466 /* Put name on stone */
467 Sprintf(rip_line[NAME_LINE], "%s", plname);
470 Sprintf(rip_line[GOLD_LINE], "%ld Au",
476 /* Put together death description */
477 switch (killer_format) {
478 default: impossible("bad killer format?");
480 Strcpy(buf, killed_by_prefix[how]);
481 Strcat(buf, an(killer));
484 Strcpy(buf, killed_by_prefix[how]);
487 case NO_KILLER_PREFIX:
492 /* Put death type on stone */
493 for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
497 if ( (i0=strlen(dpx)) > STONE_LINE_LEN) {
498 for(i = STONE_LINE_LEN;
499 ((i0 > STONE_LINE_LEN) && i); i--)
500 if(dpx[i] == ' ') i0 = i;
501 if(!i) i0 = STONE_LINE_LEN;
505 strcpy(rip_line[line], dpx);
506 if (tmpchar != ' ') {
509 } else dpx= &dpx[i0+1];
512 /* Put year on stone */
513 Sprintf(rip_line[YEAR_LINE], "%4d", getyear());
518 * RIP image expose callback.
522 rip_exposed(w, client_data, widget_data)
524 XtPointer client_data; /* unused */
525 XtPointer widget_data; /* expose event from Window widget */
527 XExposeEvent *event = (XExposeEvent *) widget_data;
528 Display* dpy=XtDisplay(w);
533 static Pixmap rip_pixmap=None;
536 if (!XtIsRealized(w) || event->count > 0) return;
538 if (rip_pixmap == None && rip_image) {
539 rip_pixmap = XCreatePixmap(dpy, XtWindow(w),
542 DefaultDepth(dpy, DefaultScreen(dpy)));
543 XPutImage(dpy, rip_pixmap,
544 DefaultGC(dpy, DefaultScreen(dpy)),
546 0,0, 0,0, /* src, dest top left */
549 XDestroyImage(rip_image); /* data bytes free'd also */
552 mask = GCFunction | GCForeground | GCGraphicsExposures | GCFont;
553 values.graphics_exposures = False;
554 XtSetArg(args[0], XtNforeground, &values.foreground);
555 XtGetValues(w, args, 1);
556 values.function = GXcopy;
557 values.font = WindowFont(w);
558 gc = XtGetGC(w, mask, &values);
560 if (rip_pixmap != None) {
561 XCopyArea(dpy, rip_pixmap, XtWindow(w), gc,
562 event->x, event->y, event->width, event->height,
566 x=appResources.tombtext_x;
567 y=appResources.tombtext_y;
568 for (i=0; i<=YEAR_LINE; i++) {
569 int len=strlen(rip_line[i]);
570 XFontStruct* font=WindowFontStruct(w);
571 int width=XTextWidth(font, rip_line[i], len);
572 XDrawString(dpy, XtWindow(w), gc,
573 x-width/2, y, rip_line[i], len);
574 x+=appResources.tombtext_dx;
575 y+=appResources.tombtext_dy;
582 * The ripout window creation routine.
585 create_ripout_widget(Widget parent)
591 static int rip_width, rip_height;
594 XpmAttributes attributes;
597 attributes.valuemask = XpmCloseness;
598 attributes.closeness = 65535; /* Try anything */
599 errorcode = XpmReadFileToImage(XtDisplay(parent), appResources.tombstone, &rip_image, 0, &attributes);
600 if (errorcode != XpmSuccess) {
602 Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
603 XpmGetErrorString(errorcode));
606 rip_width = rip_image->width;
607 rip_height = rip_image->height;
611 XtSetArg(args[num_args], XtNwidth, rip_width); num_args++;
612 XtSetArg(args[num_args], XtNheight, rip_height); num_args++;
613 XtSetArg(args[num_args], XtNtranslations,
614 XtParseTranslationTable(rip_translations)); num_args++;
616 imageport = XtCreateManagedWidget("rip", windowWidgetClass,
617 parent, args, num_args);
619 XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
624 #endif /* GRAPHIC_TOMBSTONE */