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. */
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
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
));
56 delete_text(w
, event
, params
, num_params
)
63 struct text_info_t
*text_info
;
70 text_info
= wp
->text_information
;
72 nh_XtPopdown(wp
->popup
);
74 if (text_info
->blocked
) {
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
88 dismiss_text(w
, event
, params
, num_params
)
95 struct text_info_t
*text_info
;
102 text_info
= wp
->text_information
;
104 nh_XtPopdown(wp
->popup
);
106 if (text_info
->blocked
) {
108 } else if (text_info
->destroy_on_ack
) {
109 destroy_text_window(wp
);
113 /* Dismiss when a non-modifier key pressed. */
115 key_dismiss_text(w
, event
, params
, num_params
)
119 Cardinal
*num_params
;
121 char ch
= key_event_to_char((XKeyEvent
*) event
);
123 dismiss_text(w
, event
, params
, num_params
);
126 #ifdef GRAPHIC_TOMBSTONE
127 /* Dismiss from clicking on rip image. */
129 rip_dismiss_text(w
, event
, params
, num_params
)
133 Cardinal
*num_params
;
135 dismiss_text(XtParent(w
), event
, params
, num_params
);
141 add_to_text_window(wp
, attr
, str
)
143 int attr
; /* currently unused */
146 struct text_info_t
*text_info
= wp
->text_information
;
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
;
160 display_text_window(wp
, blocking
)
164 struct text_info_t
*text_info
;
167 Dimension width
, height
, font_height
;
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.
185 (XtScreen(wp
->w
)->height
- text_info
->extra_height
) / font_height
;
188 if (nlines
> text_info
->text
.num_lines
)
189 nlines
= text_info
->text
.num_lines
;
193 height
= nlines
* font_height
+ text_info
->extra_height
;
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.
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
);
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
);
220 XtSetArg(args
[num_args
], XtNwidth
, width
);
222 XtSetArg(args
[num_args
], XtNheight
, height
);
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
);
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. */
238 if (nlines
< text_info
->text
.num_lines
) { /* add vert scrollbar */
239 XtSetArg(args
[num_args
], nhStr(XtNscrollVertical
),
240 XawtextScrollAlways
);
243 if (width
>= (Dimension
)(XtScreen(wp
->w
)->width
- 20)) { /* too wide */
244 XtSetArg(args
[num_args
], nhStr(XtNscrollHorizontal
),
245 XawtextScrollAlways
);
249 XtSetValues(wp
->w
, args
, num_args
);
251 /* We want the user to acknowlege. */
253 (void) x_event(EXIT_ON_EXIT
);
254 nh_XtPopdown(wp
->popup
);
259 create_text_window(wp
)
262 struct text_info_t
*text_info
;
265 Position top_margin
, bottom_margin
, left_margin
, right_margin
;
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
;
284 XtSetArg(args
[num_args
], XtNallowShellResize
, True
);
286 XtSetArg(args
[num_args
], XtNtranslations
,
287 XtParseTranslationTable(text_translations
));
290 #ifdef TRANSIENT_TEXT
291 wp
->popup
= XtCreatePopupShell("text", transientShellWidgetClass
,
292 toplevel
, args
, num_args
);
294 wp
->popup
= XtCreatePopupShell("text", topLevelShellWidgetClass
, toplevel
,
297 XtOverrideTranslations(
299 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
302 XtSetArg(args
[num_args
], XtNallowShellResize
, True
);
304 form
= XtCreateManagedWidget("form", formWidgetClass
, wp
->popup
, args
,
308 XtSetArg(args
[num_args
], nhStr(XtNdisplayCaret
), False
);
310 XtSetArg(args
[num_args
], XtNresize
, XawtextResizeBoth
);
312 XtSetArg(args
[num_args
], XtNtranslations
,
313 XtParseTranslationTable(text_translations
));
316 wp
->w
= XtCreateManagedWidget(killer
.name
[0] && WIN_MAP
== WIN_ERR
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. */
326 XtSetArg(args
[num_args
], XtNfont
, &text_info
->fs
);
328 XtSetArg(args
[num_args
], nhStr(XtNtopMargin
), &top_margin
);
330 XtSetArg(args
[num_args
], nhStr(XtNbottomMargin
), &bottom_margin
);
332 XtSetArg(args
[num_args
], nhStr(XtNleftMargin
), &left_margin
);
334 XtSetArg(args
[num_args
], nhStr(XtNrightMargin
), &right_margin
);
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
;
343 destroy_text_window(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
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 */
362 text_info
->destroy_on_ack
= TRUE
; /* destroy on next ACK */
367 clear_text_window(wp
)
370 clear_text_buffer(&wp
->text_information
->text
);
373 /* text buffer routines ----------------------------------------------------
376 /* Append a line to the text buffer. */
378 append_text_buffer(tb
, str
, concat
)
379 struct text_buffer
*tb
;
387 panic("append_text_buffer: null text buffer");
390 length
= strlen(str
);
395 if (length
+ tb
->text_last
+ 1 >= tb
->text_size
) {
396 /* we need to go to a bigger buffer! */
399 "append_text_buffer: text buffer growing from %d to %d bytes\n",
400 tb
->text_size
, 2 * tb
->text_size
);
402 copy
= (char *) alloc((unsigned) tb
->text_size
* 2);
403 (void) memcpy(copy
, tb
->text
, tb
->text_last
);
409 if (tb
->num_lines
) { /* not first --- append a newline */
412 if (concat
&& !index("!.?'\")", tb
->text
[tb
->text_last
- 1])) {
414 tb
->num_lines
--; /* offset increment at end of function */
417 *(tb
->text
+ tb
->text_last
) = appchar
;
422 (void) memcpy((tb
->text
+ tb
->text_last
), str
, length
+ 1);
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)
431 tb
->text_last
+= length
;
433 tb
->text
[tb
->text_last
] = '\0';
437 /* Initialize text buffer. */
440 struct text_buffer
*tb
;
442 tb
->text
= (char *) alloc(START_SIZE
);
444 tb
->text_size
= START_SIZE
;
449 /* Empty the text buffer */
451 clear_text_buffer(tb
)
452 struct text_buffer
*tb
;
459 /* Free up allocated memory. */
462 struct text_buffer
*tb
;
465 tb
->text
= (char *) 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];
486 calculate_rip_text(int how
, time_t when
)
488 /* Follows same algorithm as genl_outrip() */
495 /* Put name on stone */
496 Sprintf(rip_line
[NAME_LINE
], "%s", plname
);
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
++) {
508 if ((i0
= strlen(dpx
)) > STONE_LINE_LEN
) {
509 for (i
= STONE_LINE_LEN
; ((i0
> STONE_LINE_LEN
) && i
); i
--)
517 strcpy(rip_line
[line
], dpx
);
518 if (tmpchar
!= ' ') {
525 /* Put year on stone */
526 year
= yyyymmdd(when
) / 10000L;
527 Sprintf(rip_line
[YEAR_LINE
], "%4ld", year
);
531 * RIP image expose callback.
535 rip_exposed(w
, client_data
, widget_data
)
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
);
546 static Pixmap rip_pixmap
= None
;
549 if (!XtIsRealized(w
) || event
->count
> 0)
552 if (rip_pixmap
== None
&& rip_image
) {
553 rip_pixmap
= XCreatePixmap(dpy
, XtWindow(w
), rip_image
->width
,
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
;
590 * The ripout window creation routine.
593 create_ripout_widget(Widget parent
)
599 static int rip_width
, rip_height
;
602 XpmAttributes attributes
;
605 attributes
.valuemask
= XpmCloseness
;
606 attributes
.closeness
= 65535; /* Try anything */
608 XpmReadFileToImage(XtDisplay(parent
), appResources
.tombstone
,
609 &rip_image
, 0, &attributes
);
610 if (errorcode
!= XpmSuccess
) {
612 Sprintf(buf
, "Failed to load %s: %s", appResources
.tombstone
,
613 XpmGetErrorString(errorcode
));
616 rip_width
= rip_image
->width
;
617 rip_height
= rip_image
->height
;
621 XtSetArg(args
[num_args
], XtNwidth
, rip_width
);
623 XtSetArg(args
[num_args
], XtNheight
, rip_height
);
625 XtSetArg(args
[num_args
], XtNtranslations
,
626 XtParseTranslationTable(rip_translations
));
629 imageport
= XtCreateManagedWidget("rip", windowWidgetClass
, parent
, args
,
632 XtAddCallback(imageport
, XtNexposeCallback
, rip_exposed
, (XtPointer
) 0);
637 #endif /* GRAPHIC_TOMBSTONE */