OSDN Git Service

import nethack-3.6.0
[jnethack/source.git] / win / X11 / wintext.c
1 /* NetHack 3.6  wintext.c       $NHDT-Date: 1432512807 2015/05/25 00:13:27 $  $NHDT-Branch: master $:$NHDT-Revision: 1.14 $ */
2 /* Copyright (c) Dean Luick, 1992                                 */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /*
6  * File for dealing with text windows.
7  *
8  *      + No global functions.
9  */
10
11 #ifndef SYSV
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 #endif
14
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>
23
24 #ifdef PRESERVE_NO_SYSV
25 #ifdef SYSV
26 #undef SYSV
27 #endif
28 #undef PRESERVE_NO_SYSV
29 #endif
30
31 #include "hack.h"
32 #include "winX.h"
33 #include "xwindow.h"
34
35 #ifdef GRAPHIC_TOMBSTONE
36 #include <X11/xpm.h>
37 #endif
38
39 #define TRANSIENT_TEXT /* text window is a transient window (no positioning) \
40                           */
41
42 static const char text_translations[] = "#override\n\
43      <BtnDown>: dismiss_text()\n\
44      <Key>: key_dismiss_text()";
45
46 #ifdef GRAPHIC_TOMBSTONE
47 static const char rip_translations[] = "#override\n\
48      <BtnDown>: rip_dismiss_text()\n\
49      <Key>: rip_dismiss_text()";
50
51 static Widget FDECL(create_ripout_widget, (Widget));
52 #endif
53
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;
61 {
62     struct xwindow *wp;
63     struct text_info_t *text_info;
64
65     nhUse(event);
66     nhUse(params);
67     nhUse(num_params);
68
69     wp = find_widget(w);
70     text_info = wp->text_information;
71
72     nh_XtPopdown(wp->popup);
73
74     if (text_info->blocked) {
75         exit_x_event = TRUE;
76     } else if (text_info->destroy_on_ack) {
77         destroy_text_window(wp);
78     }
79 }
80
81 /*
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 nethack code is done
84  * with it.
85  */
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;
93 {
94     struct xwindow *wp;
95     struct text_info_t *text_info;
96
97     nhUse(event);
98     nhUse(params);
99     nhUse(num_params);
100
101     wp = find_widget(w);
102     text_info = wp->text_information;
103
104     nh_XtPopdown(wp->popup);
105
106     if (text_info->blocked) {
107         exit_x_event = TRUE;
108     } else if (text_info->destroy_on_ack) {
109         destroy_text_window(wp);
110     }
111 }
112
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;
120 {
121     char ch = key_event_to_char((XKeyEvent *) event);
122     if (ch)
123         dismiss_text(w, event, params, num_params);
124 }
125
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;
134 {
135     dismiss_text(XtParent(w), event, params, num_params);
136 }
137 #endif
138
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;
145 {
146     struct text_info_t *text_info = wp->text_information;
147     int width;
148
149     nhUse(attr);
150
151     append_text_buffer(&text_info->text, str, FALSE);
152
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;
157 }
158
159 void
160 display_text_window(wp, blocking)
161 struct xwindow *wp;
162 boolean blocking;
163 {
164     struct text_info_t *text_info;
165     Arg args[8];
166     Cardinal num_args;
167     Dimension width, height, font_height;
168     int nlines;
169
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);
175
176     /*
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.
183      */
184     nlines =
185         (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
186     nlines -= 4;
187
188     if (nlines > text_info->text.num_lines)
189         nlines = text_info->text.num_lines;
190     if (nlines <= 0)
191         nlines = 1;
192
193     height = nlines * font_height + text_info->extra_height;
194
195     num_args = 0;
196
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.
200          */
201         width += 20;
202     }
203
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++;
209     }
210 #endif
211
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;
217     }
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);
225
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
232
233     nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
234
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++;
242     }
243     if (width >= (Dimension)(XtScreen(wp->w)->width - 20)) { /* too wide */
244         XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
245                  XawtextScrollAlways);
246         num_args++;
247     }
248     if (num_args)
249         XtSetValues(wp->w, args, num_args);
250
251     /* We want the user to acknowlege. */
252     if (blocking) {
253         (void) x_event(EXIT_ON_EXIT);
254         nh_XtPopdown(wp->popup);
255     }
256 }
257
258 void
259 create_text_window(wp)
260 struct xwindow *wp;
261 {
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;
267
268     wp->type = NHW_TEXT;
269
270     wp->text_information = text_info =
271         (struct text_info_t *) alloc(sizeof(struct text_info_t));
272
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
282
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++;
289
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()"));
300
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);
306
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++;
315
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 */
323
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);
337
338     text_info->extra_width = left_margin + right_margin;
339     text_info->extra_height = top_margin + bottom_margin;
340 }
341
342 void
343 destroy_text_window(wp)
344 struct xwindow *wp;
345 {
346     /* Don't need to pop down, this only called from dismiss_text(). */
347
348     struct text_info_t *text_info = wp->text_information;
349
350     /*
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.
355      */
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 */
363     }
364 }
365
366 void
367 clear_text_window(wp)
368 struct xwindow *wp;
369 {
370     clear_text_buffer(&wp->text_information->text);
371 }
372
373 /* text buffer routines ----------------------------------------------------
374  */
375
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;
382 {
383     char *copy;
384     int length;
385
386     if (!tb->text)
387         panic("append_text_buffer:  null text buffer");
388
389     if (str) {
390         length = strlen(str);
391     } else {
392         length = 0;
393     }
394
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;
407     }
408
409     if (tb->num_lines) { /* not first --- append a newline */
410         char appchar = '\n';
411
412         if (concat && !index("!.?'\")", tb->text[tb->text_last - 1])) {
413             appchar = ' ';
414             tb->num_lines--; /* offset increment at end of function */
415         }
416
417         *(tb->text + tb->text_last) = appchar;
418         tb->text_last++;
419     }
420
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.
425              */
426             copy = (tb->text + tb->text_last);
427             while ((copy = index(copy, '\n')) != (char *) 0)
428                 *copy = ' ';
429         }
430
431         tb->text_last += length;
432     }
433     tb->text[tb->text_last] = '\0';
434     tb->num_lines++;
435 }
436
437 /* Initialize text buffer. */
438 void
439 init_text_buffer(tb)
440 struct text_buffer *tb;
441 {
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;
447 }
448
449 /* Empty the text buffer */
450 void
451 clear_text_buffer(tb)
452 struct text_buffer *tb;
453 {
454     tb->text_last = 0;
455     tb->text[0] = '\0';
456     tb->num_lines = 0;
457 }
458
459 /* Free up allocated memory. */
460 void
461 free_text_buffer(tb)
462 struct text_buffer *tb;
463 {
464     free(tb->text);
465     tb->text = (char *) 0;
466     tb->text_size = 0;
467     tb->text_last = 0;
468     tb->num_lines = 0;
469 }
470
471 #ifdef GRAPHIC_TOMBSTONE
472
473 static void FDECL(rip_exposed, (Widget, XtPointer, XtPointer));
474
475 static XImage *rip_image = 0;
476
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 */
482
483 static char rip_line[YEAR_LINE + 1][STONE_LINE_LEN + 1];
484
485 void
486 calculate_rip_text(int how, time_t when)
487 {
488     /* Follows same algorithm as genl_outrip() */
489
490     char buf[BUFSZ];
491     char *dpx;
492     int line;
493     long year;
494
495     /* Put name on stone */
496     Sprintf(rip_line[NAME_LINE], "%s", plname);
497
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);
502
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;
507
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;
514         }
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];
523     }
524
525     /* Put year on stone */
526     year = yyyymmdd(when) / 10000L;
527     Sprintf(rip_line[YEAR_LINE], "%4ld", year);
528 }
529
530 /*
531  * RIP image expose callback.
532  */
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 */
539 {
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;
548
549     if (!XtIsRealized(w) || event->count > 0)
550         return;
551
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 */
560     }
561
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);
569
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);
573     }
574
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;
584     }
585
586     XtReleaseGC(w, gc);
587 }
588
589 /*
590  * The ripout window creation routine.
591  */
592 static Widget
593 create_ripout_widget(Widget parent)
594 {
595     Widget imageport;
596     Arg args[16];
597     Cardinal num_args;
598
599     static int rip_width, rip_height;
600
601     if (!rip_image) {
602         XpmAttributes attributes;
603         int errorcode;
604
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);
615         }
616         rip_width = rip_image->width;
617         rip_height = rip_image->height;
618     }
619
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++;
628
629     imageport = XtCreateManagedWidget("rip", windowWidgetClass, parent, args,
630                                       num_args);
631
632     XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
633
634     return imageport;
635 }
636
637 #endif /* GRAPHIC_TOMBSTONE */
638
639 /*wintext.c*/