diff --git a/include/winX.h b/include/winX.h index 1c1521074..88d61df9a 100644 --- a/include/winX.h +++ b/include/winX.h @@ -531,8 +531,10 @@ extern void X11_update_label(Widget); extern void X11_set_attrs(Widget, unsigned); extern void X11_set_highlight(Widget, boolean); extern void X11_set_percent(Widget, unsigned, Pixel); +extern void X11_set_column_widths(Widget, const int *, unsigned); extern void X11_blink_labels(void); extern int X11_font_height(X11_Font *); +extern int X11_column_width(Display *, X11_Font *, const char *, size_t); /* * These are for widgets that use the enhanced label services only for text diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad index f12110f6f..10f1e7a73 100644 --- a/win/X11/NetHack.ad +++ b/win/X11/NetHack.ad @@ -31,7 +31,7 @@ NetHack*text*borderWidth: 0 ! These correspond to the .nethackrc settings font_map, font_menu, ! font_message, font_status and font_text NetHack.font_map: mono-10 -NetHack.font_menu: mono-10 +NetHack.font_menu: sans-10 NetHack.font_message: sans-10 NetHack.font_status: sans-10 NetHack.font_text: mono-10 diff --git a/win/X11/winX.c b/win/X11/winX.c index 0f63a5761..ace3f1124 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -1571,7 +1571,7 @@ static XtResource resources[] = { { nhStr("font_map"), nhStr("Font_map"), XtRString, sizeof(String), XtOffset(AppResources *, font_map), XtRString, nhStr("mono-10") }, { nhStr("font_menu"), nhStr("Font_menu"), XtRString, sizeof(String), - XtOffset(AppResources *, font_menu), XtRString, nhStr("mono-10") }, + XtOffset(AppResources *, font_menu), XtRString, nhStr("sans-10") }, { nhStr("font_message"), nhStr("Font_message"), XtRString, sizeof(String), XtOffset(AppResources *, font_message), XtRString, nhStr("sans-10") }, { nhStr("font_status"), nhStr("Font_status"), XtRString, sizeof(String), @@ -1646,6 +1646,9 @@ X11_init_nhwindows(int *argcp, char **argv) Arg args[4]; uid_t savuid; + /* Request tabbed menu columns */ + iflags.menu_tab_sep = TRUE; + /* Init windows to nothing. */ for (i = 0; i < MAX_WINDOWS; i++) window_list[i].type = NHW_NONE; diff --git a/win/X11/winlabel.c b/win/X11/winlabel.c index 662644f13..e045f2007 100644 --- a/win/X11/winlabel.c +++ b/win/X11/winlabel.c @@ -19,7 +19,8 @@ #include "winX.h" /* Declarations depending on Xft support or none */ -static int X11_text_width(Display *, X11_Font *, const char *, size_t); +static int X11_text_width(Display *, X11_Font *, const char *, size_t, + const int *, unsigned); /* Data attached to a wrapped widget */ typedef struct WidgetData { @@ -41,6 +42,10 @@ typedef struct WidgetData { /* Percentage bar */ unsigned percent; Pixel bar_color; + + /* Columns */ + int *columns; + unsigned num_cols; } WidgetData; /* Bits for WidgetData::font */ @@ -119,6 +124,7 @@ delete_callback(Widget w, XtPointer client_data, XtPointer call_data) XFreePixmap(display, data->pixmap); } free_fonts(w, data); + free(data->columns); free(data); } @@ -173,6 +179,25 @@ X11_set_percent(Widget w, unsigned percent, Pixel color) } } +void +X11_set_column_widths(Widget w, const int *col_widths, unsigned num_cols) +{ + WidgetData *data = get_widget_data(w); + if (data != NULL) { + int col_spacing = X11_font_height(data->font[0]) * 2; + free(data->columns); + data->columns = (int *) alloc(sizeof(data->columns[0]) * num_cols); + if (num_cols != 0) { + data->columns[0] = 0; + for (unsigned i = 1; i < num_cols; ++i) { + data->columns[i] = data->columns[i-1] + col_spacing + col_widths[i-1]; + } + } + data->num_cols = num_cols; + update_label(w, data); + } +} + /* Update the label's pixmap */ /* This is called from functions that have altered the data block, and already have a pointer to it */ @@ -235,7 +260,8 @@ update_label(Widget w, WidgetData *data) } /* Get the width of the line */ - int lwidth = X11_text_width(display, font, label + i, line2); + int lwidth = X11_text_width(display, font, label + i, line2, + data->columns, data->num_cols); /* Update pixmap dimensions */ width = max(lwidth, width); @@ -301,6 +327,7 @@ update_label(Widget w, WidgetData *data) if ((attrs & HL_BLINK) && X11_blink) { values.foreground = values.background; } + #ifdef USE_XFT Visual *visual = DefaultVisualOfScreen(screen); Colormap cmap = DefaultColormapOfScreen(screen); @@ -357,7 +384,8 @@ update_label(Widget w, WidgetData *data) } /* Get the width of the line */ - int lwidth = X11_text_width(display, font, label + i, line2); + int lwidth = X11_text_width(display, font, label + i, line2, + data->columns, data->num_cols); /* Place the line horizontally */ int x = 0; @@ -378,13 +406,38 @@ update_label(Widget w, WidgetData *data) /* Render the line */ #ifdef USE_XFT XftDrawRect(draw, &bgcolor, x, y - font->ascent, lwidth, X11_font_height(font)); - XftDrawString8(draw, &fgcolor, font, x, y, - (const FcChar8 *) (label + i), line2); #else - XDrawImageString(display, new_pixmap, ggc, - x, y, - label + i, line2); + XSetForeground(display, ggc, values.background); + XFillRectangle(display, new_pixmap, ggc, x, y - font->ascent, lwidth, X11_font_height(font)); + XSetForeground(display, ggc, values.foreground); #endif + size_t j = 0; + unsigned col = 0; + while (j < line2) { + size_t line3; + int pos; + if (data->num_cols == 0) { + /* Columns not set */ + line3 = line2; + pos = 0; + } else { + line3 = min(strcspn(label + i + j, "\t\n"), line2); + pos = data->columns[min(col, data->num_cols-1)]; + } +#ifdef USE_XFT + XftDrawString8(draw, &fgcolor, font, x + pos, y, + (const FcChar8 *) (label + i + j), line3); +#else + XDrawString(display, new_pixmap, ggc, + x + pos, y, + label + i + j, line3); +#endif + j += line3; + if (j < line2) { + ++j; + } + ++col; + } /* Draw the underline if requested */ if (attrs & HL_ULINE) { @@ -488,13 +541,41 @@ free_fonts(Widget w, WidgetData *data) } } +static int +X11_text_width(Display *display, X11_Font *font, const char *text, size_t length, + const int *columns, unsigned num_cols) +{ + if (num_cols == 0) { + /* Columns have not been set */ + return X11_column_width(display, font, text, length); + } + + /* Width is the position of the last column, plus the width of the string + in the last column */ + + unsigned col = 0; + size_t i = 0; + while (col < num_cols) { + size_t len = strcspn(text + i, "\t\n"); + if (i+len >= length || text[i+len] != '\t') { + break; + } + ++col; + i += len + 1; + } + + int pos = columns[col] + X11_column_width(display, font, text + i, length - i); + + return pos; +} + ////////////////////////////////////////////////////////////////////////////// // Functions that depend on the font rendering API // ////////////////////////////////////////////////////////////////////////////// #ifdef USE_XFT -static int -X11_text_width(Display *display, X11_Font *font, const char *text, size_t length) +int +X11_column_width(Display *display, X11_Font *font, const char *text, size_t length) { XGlyphInfo extents; XftTextExtents8(display, font, (const FcChar8*) text, length, &extents); @@ -507,8 +588,8 @@ X11_font_height(X11_Font *font) return max(font->height, font->ascent + font->descent); } #else /* !USE_XFT */ -static int -X11_text_width(Display *display, X11_Font *font, const char *text, size_t length) +int +X11_column_width(Display *display, X11_Font *font, const char *text, size_t length) { nhUse(display); return XTextWidth(font, text, length); diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c index 4604b78a0..371124a62 100644 --- a/win/X11/winmenu.c +++ b/win/X11/winmenu.c @@ -60,6 +60,7 @@ static unsigned menu_scrollmask(struct xwindow *); static void menu_unscroll(struct xwindow *); static Widget menu_create_buttons(struct xwindow *, Widget, Widget); static void menu_create_entries(struct xwindow *, struct menu *); +static unsigned get_col_widths(Widget, X11_Font *, const char *, int **); static void destroy_menu_entry_widgets(struct xwindow *); static void create_menu_translation_tables(void); @@ -1317,6 +1318,13 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) Cardinal num_args; Dimension cwidth, maxwidth = 0; + int *col_widths = NULL; + unsigned num_cols = 0; + X11_Font *font; +#ifdef USE_XFT + font = X11_new_font(wp->w, 0, NHW_MENU); +#endif + for (curr = curr_menu->base; curr; curr = curr->next) { char tmpbuf[BUFSZ]; Widget linewidget; @@ -1376,13 +1384,42 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) wp->w, args, num_args); X11_wrap_widget(curr->w, NHW_MENU); X11_set_attrs(curr->w, 0x1 << attr); - XtManageChild(curr->w); if (canpick) XtAddCallback(linewidget, XtNcallback, menu_select, (XtPointer) curr); prevlinewidget = linewidget; +#ifndef USE_XFT /* If Xft, the font is acquired at the start of the loop */ + num_args = 0; + XtSetArg(args[num_args], XtNfont, &font); num_args++; + XtGetValues(curr->w, args, num_args); +#endif + /* Get column widths for this line */ + if (strchr(str, '\t') != NULL) { /* Might be a header if no tab */ + int *col_widths0; + unsigned num_cols0 = get_col_widths(curr->w, font, str, &col_widths0); + if (num_cols0 > num_cols) { + col_widths = (int *) re_alloc((long *) col_widths, num_cols0 * sizeof(col_widths[0])); + memset(col_widths + num_cols, 0, sizeof(col_widths[0]) * (num_cols0 - num_cols)); + num_cols = num_cols0; + } + for (unsigned i = 0; i < num_cols0; ++i) { + col_widths[i] = max(col_widths[i], col_widths0[i]); + } + free(col_widths0); + } + } +#ifdef USE_XFT + X11_release_font(wp->w, font); +#endif + + /* Set the column widths */ + for (curr = curr_menu->base; curr; curr = curr->next) { + X11_set_column_widths(curr->w, col_widths, num_cols); + XtManageChild(curr->w); + + boolean canpick = (how != PICK_NONE && curr->identifier.a_void); if (canpick) { /* get the current line width */ XtSetArg(args[0], XtNwidth, &cwidth); @@ -1392,6 +1429,8 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) } } + free(col_widths); + /* set all selectable menu entries to the maximum width */ if (how != PICK_NONE) { XtSetArg(args[0], XtNwidth, maxwidth); @@ -1401,6 +1440,43 @@ menu_create_entries(struct xwindow *wp, struct menu *curr_menu) } } +/* Determine widths of columns */ +static unsigned +get_col_widths(Widget w, X11_Font *font, const char *str, int **col_widths) +{ + /* Determine the number of columns */ + unsigned num_cols = 1; + size_t i = 0; + while (str[i] != '\0') { + size_t len = strcspn(str + i, "\t"); + if (str[i+len] == '\0') { + break; + } + ++num_cols; + i += len + 1; + } + + /* Allocate width array */ + int *cwidths = (int *) alloc(sizeof(cwidths[0]) * num_cols); + memset(cwidths, 0, sizeof(cwidths[0]) * num_cols); + + /* Get the widths of the columns */ + unsigned col = 0; + i = 0; + while (str[i] != '\0') { + size_t len = strcspn(str + i, "\t"); + cwidths[col++] = X11_column_width(XtDisplay(w), font, str + i, len); + i += len; + if (str[i] == '\t') { + ++i; + } + } + + /* Return */ + *col_widths = cwidths; + return num_cols; +} + static void destroy_menu_entry_widgets(struct xwindow *wp) {