OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / win / X11 / wintext.c
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. */
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
40 #define TRANSIENT_TEXT  /* text window is a transient window (no positioning) */
41
42 static const char text_translations[] =
43     "#override\n\
44      <BtnDown>: dismiss_text()\n\
45      <Key>: key_dismiss_text()";
46
47 #ifdef GRAPHIC_TOMBSTONE
48 static const char rip_translations[] =
49     "#override\n\
50      <BtnDown>: rip_dismiss_text()\n\
51      <Key>: rip_dismiss_text()";
52
53 static Widget FDECL(create_ripout_widget, (Widget));
54 #endif
55
56 /*ARGSUSED*/
57 void
58 delete_text(w, event, params, num_params)
59     Widget w;
60     XEvent *event;
61     String *params;
62     Cardinal *num_params;
63 {
64     struct xwindow *wp;
65     struct text_info_t *text_info;
66
67     wp = find_widget(w);
68     text_info = wp->text_information;
69
70     nh_XtPopdown(wp->popup);
71
72     if (text_info->blocked) {
73         exit_x_event = TRUE;
74     } else if (text_info->destroy_on_ack) {
75         destroy_text_window(wp);
76     }
77 }
78
79 /*
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
82  * with it.
83  */
84 /*ARGSUSED*/
85 void
86 dismiss_text(w, event, params, num_params)
87     Widget w;
88     XEvent *event;
89     String *params;
90     Cardinal *num_params;
91 {
92     struct xwindow *wp;
93     struct text_info_t *text_info;
94
95     wp = find_widget(w);
96     text_info = wp->text_information;
97
98     nh_XtPopdown(wp->popup);
99
100     if (text_info->blocked) {
101         exit_x_event = TRUE;
102     } else if (text_info->destroy_on_ack) {
103         destroy_text_window(wp);
104     }
105 }
106
107 /* Dismiss when a non-modifier key pressed. */
108 void
109 key_dismiss_text(w, event, params, num_params)
110     Widget w;
111     XEvent *event;
112     String *params;
113     Cardinal *num_params;
114 {
115     char ch = key_event_to_char((XKeyEvent *) event);
116     if (ch) dismiss_text(w, event, params, num_params);
117 }
118
119 #ifdef GRAPHIC_TOMBSTONE
120 /* Dismiss from clicking on rip image. */
121 void
122 rip_dismiss_text(w, event, params, num_params)
123     Widget w;
124     XEvent *event;
125     String *params;
126     Cardinal *num_params;
127 {
128     dismiss_text(XtParent(w), event, params, num_params);
129 }
130 #endif
131
132
133 /* ARGSUSED */
134 void
135 add_to_text_window(wp, attr, str)
136     struct xwindow *wp;
137     int attr;   /* currently unused */
138     const char *str;
139 {
140     struct text_info_t *text_info = wp->text_information;
141     int width;
142
143     append_text_buffer(&text_info->text, str, FALSE);
144
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;
149 }
150
151 void
152 display_text_window(wp, blocking)
153     struct xwindow *wp;
154     boolean blocking;
155 {
156     struct text_info_t *text_info;
157     Arg args[8];
158     Cardinal num_args;
159     Dimension width, height, font_height;
160     int nlines;
161
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);
167
168     /*
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.
175      */
176     nlines = (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
177     nlines -= 4;
178
179     if (nlines > text_info->text.num_lines)
180         nlines = text_info->text.num_lines;
181     if (nlines <= 0) nlines = 1;
182
183     height = nlines * font_height + text_info->extra_height;
184
185     num_args = 0;
186
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.
190          */
191         width += 20;
192     }
193
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++;
198     }
199 #endif
200
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;
205     }
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);
210
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);
216 #endif
217
218     nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w);
219
220     /* Kludge alert.  Scrollbars are not sized correctly by the Text widget */
221     /* if added before the window is displayed, so do it afterward. */
222     num_args = 0;
223     if (nlines < text_info->text.num_lines) {   /* add vert scrollbar */
224         XtSetArg(args[num_args], XtNscrollVertical, XawtextScrollAlways);
225                                                                 num_args++;
226     }
227     if (width >= (Dimension) (XtScreen(wp->w)->width-20)) {     /* too wide */
228         XtSetArg(args[num_args], XtNscrollHorizontal, XawtextScrollAlways);
229                                                                 num_args++;
230     }
231     if (num_args) XtSetValues(wp->w, args, num_args);
232
233     /* We want the user to acknowlege. */
234     if (blocking) {
235         (void) x_event(EXIT_ON_EXIT);
236         nh_XtPopdown(wp->popup);
237     }
238 }
239
240
241 void
242 create_text_window(wp)
243     struct xwindow *wp;
244 {
245     struct text_info_t *text_info;
246     Arg args[8];
247     Cardinal num_args;
248     Position top_margin, bottom_margin, left_margin, right_margin;
249     Widget form;
250
251     wp->type = NHW_TEXT;
252
253     wp->text_information = text_info =
254                     (struct text_info_t *) alloc(sizeof(struct text_info_t));
255
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;
264 #endif
265
266     num_args = 0;
267     XtSetArg(args[num_args], XtNallowShellResize, True); num_args++;
268     XtSetArg(args[num_args], XtNtranslations,
269                 XtParseTranslationTable(text_translations));    num_args++;
270
271 #ifdef TRANSIENT_TEXT
272     wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
273                                    toplevel, args, num_args);
274 #else
275     wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass,
276                                    toplevel, args, num_args);
277 #endif
278     XtOverrideTranslations(wp->popup,
279         XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
280
281     num_args = 0;
282     XtSetArg(args[num_args], XtNallowShellResize, True);        num_args++;
283     form = XtCreateManagedWidget("form", formWidgetClass, wp->popup,
284                 args, num_args);
285
286     num_args = 0;
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++;
291
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 */
299
300     /* Get the font and margin information. */
301     num_args = 0;
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);
308
309     text_info->extra_width  = left_margin + right_margin;
310     text_info->extra_height = top_margin + bottom_margin;
311 }
312
313 void
314 destroy_text_window(wp)
315     struct xwindow *wp;
316 {
317     /* Don't need to pop down, this only called from dismiss_text(). */
318
319     struct text_info_t *text_info = wp->text_information;
320
321     /*
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
325      * press.
326      */
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 */
332     } else {
333         text_info->destroy_on_ack = TRUE;       /* destroy on next ACK */
334     }
335 }
336
337 void
338 clear_text_window(wp)
339     struct xwindow *wp;
340 {
341     clear_text_buffer(&wp->text_information->text);
342 }
343
344
345 /* text buffer routines ---------------------------------------------------- */
346
347 /* Append a line to the text buffer. */
348 void
349 append_text_buffer(tb, str, concat)
350     struct text_buffer *tb;
351     const char *str;
352     boolean concat;
353 {
354     char *copy;
355     int length;
356
357     if (!tb->text) panic("append_text_buffer:  null text buffer");
358
359     if (str) {
360         length = strlen(str);
361     } else {
362         length = 0;
363     }
364
365     if (length + tb->text_last + 1 >= tb->text_size) {
366         /* we need to go to a bigger buffer! */
367 #ifdef VERBOSE
368         printf("append_text_buffer: text buffer growing from %d to %d bytes\n",
369                                 tb->text_size, 2*tb->text_size);
370 #endif
371         copy = (char *) alloc((unsigned)tb->text_size*2);
372         (void) memcpy(copy, tb->text, tb->text_last);
373         free(tb->text);
374         tb->text = copy;
375         tb->text_size *= 2;
376     }
377
378     if (tb->num_lines) {        /* not first --- append a newline */
379         char appchar = '\n';
380
381         if(concat && !index("!.?'\")", tb->text[tb->text_last-1])) {
382             appchar = ' ';
383             tb->num_lines--; /* offset increment at end of function */
384         }
385
386         *(tb->text + tb->text_last) = appchar;
387         tb->text_last++;
388     }
389
390     if (str) {
391         (void) memcpy((tb->text+tb->text_last), str, length+1);
392         if(length) {
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)
396                 *copy = ' ';
397         }
398
399         tb->text_last += length;
400     }
401     tb->text[tb->text_last] = '\0';
402     tb->num_lines++;
403 }
404
405 /* Initialize text buffer. */
406 void
407 init_text_buffer(tb)
408     struct text_buffer *tb;
409 {
410     tb->text      = (char *) alloc(START_SIZE);
411     tb->text[0]   = '\0';
412     tb->text_size = START_SIZE;
413     tb->text_last = 0;
414     tb->num_lines = 0;
415 }
416
417 /* Empty the text buffer */
418 void
419 clear_text_buffer(tb)
420     struct text_buffer *tb;
421 {
422     tb->text_last = 0;
423     tb->text[0]   = '\0';
424     tb->num_lines = 0;
425 }
426
427 /* Free up allocated memory. */
428 void
429 free_text_buffer(tb)
430     struct text_buffer *tb;
431 {
432     free(tb->text);
433     tb->text = (char *) 0;
434     tb->text_size = 0;
435     tb->text_last = 0;
436     tb->num_lines = 0;
437 }
438
439
440 #ifdef GRAPHIC_TOMBSTONE
441
442 static void FDECL(rip_exposed, (Widget,XtPointer,XtPointer));
443
444 static XImage* rip_image=0;
445
446
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 */
452
453 static char rip_line[YEAR_LINE+1][STONE_LINE_LEN+1];
454
455 extern const char *killed_by_prefix[];
456
457 void
458 calculate_rip_text(int how)
459 {
460         /* Follows same algorithm as genl_outrip() */
461
462         char buf[BUFSZ];
463         char *dpx;
464         int line;
465
466         /* Put name on stone */
467         Sprintf(rip_line[NAME_LINE], "%s", plname);
468
469         /* Put $ on stone */
470         Sprintf(rip_line[GOLD_LINE], "%ld Au",
471 #ifndef GOLDOBJ
472                 u.ugold);
473 #else
474                 done_money);
475 #endif
476         /* Put together death description */
477         switch (killer_format) {
478                 default: impossible("bad killer format?");
479                 case KILLED_BY_AN:
480                         Strcpy(buf, killed_by_prefix[how]);
481                         Strcat(buf, an(killer));
482                         break;
483                 case KILLED_BY:
484                         Strcpy(buf, killed_by_prefix[how]);
485                         Strcat(buf, killer);
486                         break;
487                 case NO_KILLER_PREFIX:
488                         Strcpy(buf, killer);
489                         break;
490         }
491
492         /* Put death type on stone */
493         for (line=DEATH_LINE, dpx = buf; line<YEAR_LINE; line++) {
494                 register int i,i0;
495                 char tmpchar;
496
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;
502                 }
503                 tmpchar = dpx[i0];
504                 dpx[i0] = 0;
505                 strcpy(rip_line[line], dpx);
506                 if (tmpchar != ' ') {
507                         dpx[i0] = tmpchar;
508                         dpx= &dpx[i0];
509                 } else  dpx= &dpx[i0+1];
510         }
511
512         /* Put year on stone */
513         Sprintf(rip_line[YEAR_LINE], "%4d", getyear());
514 }
515
516
517 /*
518  * RIP image expose callback.
519  */
520 /*ARGSUSED*/
521 static void
522 rip_exposed(w, client_data, widget_data)
523     Widget w;
524     XtPointer client_data;      /* unused */
525     XtPointer widget_data;      /* expose event from Window widget */
526 {
527     XExposeEvent *event = (XExposeEvent *) widget_data;
528     Display* dpy=XtDisplay(w);
529     Arg args[8];
530     XGCValues values;
531     XtGCMask mask;
532     GC gc;
533     static Pixmap rip_pixmap=None;
534     int i, x, y;
535
536     if (!XtIsRealized(w) || event->count > 0) return;
537
538     if (rip_pixmap == None && rip_image) {
539         rip_pixmap = XCreatePixmap(dpy, XtWindow(w),
540                         rip_image->width,
541                         rip_image->height,
542                         DefaultDepth(dpy, DefaultScreen(dpy)));
543         XPutImage(dpy, rip_pixmap,
544                 DefaultGC(dpy, DefaultScreen(dpy)),
545                 rip_image,
546                 0,0, 0,0,               /* src, dest top left */
547                 rip_image->width,
548                 rip_image->height);
549         XDestroyImage(rip_image);       /* data bytes free'd also */
550     }
551
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);
559
560     if (rip_pixmap != None) {
561         XCopyArea(dpy, rip_pixmap, XtWindow(w), gc,
562                 event->x, event->y, event->width, event->height,
563                 event->x, event->y);
564     }
565
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;
576     }
577
578     XtReleaseGC(w, gc);
579 }
580
581 /*
582  * The ripout window creation routine.
583  */
584 static Widget
585 create_ripout_widget(Widget parent)
586 {
587     Widget imageport;
588     Arg args[16];
589     Cardinal num_args;
590
591     static int rip_width, rip_height;
592
593     if (!rip_image) {
594         XpmAttributes attributes;
595         int errorcode;
596
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) {
601             char buf[BUFSZ];
602             Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
603                         XpmGetErrorString(errorcode));
604             X11_raw_print(buf);
605         }
606         rip_width = rip_image->width;
607         rip_height = rip_image->height;
608     }
609
610     num_args = 0;
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++;
615
616     imageport = XtCreateManagedWidget("rip", windowWidgetClass,
617                 parent, args, num_args);
618
619     XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
620
621     return imageport;
622 }
623
624 #endif /* GRAPHIC_TOMBSTONE */
625
626 /*wintext.c*/