/* NetHack 3.5 mhtxtbuf.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */ /* NetHack 3.5 mhtxtbuf.c $Date: 2009/10/13 01:55:12 $ $Revision: 1.6 $ */ /* Copyright (C) 2003 by Alex Kompel */ /* NetHack may be freely redistributed. See license for details. */ #include "mhtxtbuf.h" /* Collect Nethack text messages and render text into edit box. Wrap text if necessary. Recognize formatted lines as having more that 4 consecutive. spaces inside the string. Strip leading and trailing spaces. Always break at the original line end (do not merge text that comes from NetHack engine) */ /*----------------------------------------------------------------*/ #define NHTEXT_BUFFER_INCREMENT 10 /*----------------------------------------------------------------*/ struct text_buffer_line { int attr; short beg_padding; short end_padding; BOOL formatted; char* text; }; /*----------------------------------------------------------------*/ typedef struct mswin_nethack_text_buffer { BOOL b_wrap_text; int n_size; int n_used; struct text_buffer_line *text_buffer_line; } NHTextBuffer, *PNHTextBuffer; /*----------------------------------------------------------------*/ #define NHTextLine(pb,i) ((pb)->text_buffer_line[(i)]) static TCHAR* nh_append( TCHAR* s, int* size, const char* ap ); /*----------------------------------------------------------------*/ PNHTextBuffer mswin_init_text_buffer(BOOL wrap_text) { PNHTextBuffer pb = (PNHTextBuffer)malloc(sizeof(NHTextBuffer)); if( !pb ) panic("Out of memory"); ZeroMemory(pb, sizeof(NHTextBuffer)); pb->b_wrap_text = wrap_text; pb->n_size = 0; pb->n_used = 0; pb->text_buffer_line = NULL; return pb; } /*----------------------------------------------------------------*/ void mswin_free_text_buffer(PNHTextBuffer pb) { int i; if( !pb ) return; for(i=0; in_used; i++ ) { free(pb->text_buffer_line[i].text); } free( pb->text_buffer_line ); free( pb ); } /*----------------------------------------------------------------*/ void mswin_add_text(PNHTextBuffer pb, int attr, const char* text) { char* p; struct text_buffer_line* new_line; /* grow buffer */ if( pb->n_used >= pb->n_size ) { pb->n_size += NHTEXT_BUFFER_INCREMENT; pb->text_buffer_line = (struct text_buffer_line*)realloc( pb->text_buffer_line, pb->n_size*sizeof(struct text_buffer_line) ); if( !pb->text_buffer_line ) panic("Memory allocation error"); } /* analyze the new line of text */ new_line = &NHTextLine(pb, pb->n_used); new_line->attr = attr; new_line->beg_padding = 0; new_line->text = strdup(text); for( p = new_line->text; *p && isspace(*p); p++ ) { new_line->beg_padding++; } if( *p ) { memmove(new_line->text, new_line->text + new_line->beg_padding, strlen(new_line->text) - new_line->beg_padding + 1 ); for( p = new_line->text+strlen(new_line->text); p>=new_line->text && isspace(*p); p-- ) { new_line->end_padding++; *p = 0; } /* if there are 3 (or more) consecutive spaces inside the string consider it formatted */ new_line->formatted = (strstr(new_line->text, " ")!=NULL) || (new_line->beg_padding>8); } else { new_line->end_padding = 0; new_line->text[0] = 0; new_line->formatted = FALSE; } pb->n_used++; } /*----------------------------------------------------------------*/ void mswin_set_text_wrap(PNHTextBuffer pb, BOOL wrap_text) { pb->b_wrap_text = wrap_text; } /*----------------------------------------------------------------*/ BOOL mswin_get_text_wrap(PNHTextBuffer pb) { return pb->b_wrap_text; } /*----------------------------------------------------------------*/ static TCHAR* nh_append( TCHAR* s, int* size, const char* ap ) { int tlen, tnewlen; if( !(ap && *ap) ) return s; /* append the calculated line to the text buffer */ tlen = s? _tcslen(s) : 0; tnewlen = tlen+strlen(ap); if( tnewlen>=*size ) { *size = max(tnewlen, *size + BUFSZ); s = (TCHAR*)realloc(s, *size * sizeof(TCHAR)); if( !s ) panic("Out of memory"); ZeroMemory(s+tlen, (*size-tlen)*sizeof(TCHAR)); } if( strcmp(ap, "\r\n")==0 ) { _tcscat(s, TEXT("\r\n")); } else { NH_A2W(ap, s+tlen, strlen(ap)); s[tnewlen] = 0; } return s; } /*----------------------------------------------------------------*/ void mswin_render_text(PNHTextBuffer pb, HWND edit_control) { RECT rt_client; /* boundaries of the client area of the edit control */ SIZE size_text; /* size of the edit control */ RECT rt_text; /* calculated text rectangle for the visible line */ char buf[BUFSZ]; /* buffer for the visible line */ TCHAR tbuf[BUFSZ]; /* temp buffer for DrawText */ TCHAR* pText = NULL; /* resulting text (formatted) */ int pTextSize = 0; /* resulting text size */ char* p_cur = NULL; /* current position in the NHTextBuffer->text_buffer_line->text */ char* p_buf_cur = NULL; /* current position in the visible line buffer */ int i; HDC hdcEdit; /* device context for the edit control */ HFONT hFont, hOldFont; /* edit control font */ GetClientRect(edit_control, &rt_client ); size_text.cx = rt_client.right - rt_client.left; size_text.cy = rt_client.bottom - rt_client.top; size_text.cx -= GetSystemMetrics(SM_CXVSCROLL); /* add a slight right margin - the text looks better that way */ hdcEdit = GetDC(edit_control); hFont = (HFONT)SendMessage(edit_control, WM_GETFONT, 0, 0); if( hFont ) hOldFont = SelectObject(hdcEdit, hFont); /* loop through each line (outer loop) and wrap it around (inner loop) */ ZeroMemory(buf, sizeof(buf)); p_buf_cur = buf; for( i=0; in_used; i++ ) { if( pb->b_wrap_text ) { p_cur = NHTextLine(pb,i).text; /* insert an line break for the empty string */ if( !NHTextLine(pb,i).text[0] ) { pText = nh_append(pText, &pTextSize, "\r\n"); continue; } /* add margin to the "formatted" line of text */ if( NHTextLine(pb,i).formatted ) { strcpy(buf, " "); p_buf_cur += 3; } /* scroll thourgh the current line of text and wrap it so it fits to width of the edit control */ while( *p_cur ) { char *p_word_pos = p_buf_cur; /* copy one word into the buffer */ while( *p_cur && isspace(*p_cur) ) if( p_buf_cur!=buf ) *p_buf_cur++ = *p_cur++; else p_cur++; while( *p_cur && !isspace(*p_cur) ) *p_buf_cur++ = *p_cur++; /* check if it fits */ SetRect( &rt_text, 0, 0, size_text.cx, size_text.cy ); DrawText(hdcEdit, NH_A2W(buf, tbuf, p_buf_cur-buf), p_buf_cur-buf, &rt_text, DT_CALCRECT | DT_LEFT | DT_SINGLELINE | DT_NOCLIP); if( (rt_text.right - rt_text.left)>=size_text.cx ) { /* Backtrack. Only backtrack if the last word caused the overflow - do not backtrack if the entire current line does not fit the visible area. Otherwise it is a infinite loop. */ if( p_word_pos>buf ) { p_cur -= (p_buf_cur-p_word_pos); p_buf_cur = p_word_pos; } *p_buf_cur = 0; /* break the line */ /* append the calculated line to the text buffer */ pText = nh_append(pText, &pTextSize, buf); pText = nh_append(pText, &pTextSize, "\r\n"); ZeroMemory(buf, sizeof(buf)); p_buf_cur = buf; } } /* always break the line at the end of the buffer text */ if( p_buf_cur != buf ) { /* flush the current buffrer */ *p_buf_cur = 0; /* break the line */ pText = nh_append(pText, &pTextSize, buf); pText = nh_append(pText, &pTextSize, "\r\n"); ZeroMemory(buf, sizeof(buf)); p_buf_cur = buf; } } else { /* do not wrap text */ int j; for( j=0; j