diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index 3a4868208c..35c2aa2058 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -168,7 +168,7 @@ public: switch (widget) { case BBSW_DROPDOWN_ORDER: { Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); - d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the word is centered, also looks nice. + d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; *size = maxdim(*size, d); break; diff --git a/src/console_gui.cpp b/src/console_gui.cpp index ac0e6103c9..3000954763 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -281,7 +281,10 @@ struct IConsoleWindow : Window break; case WKC_RETURN: case WKC_NUM_ENTER: { - IConsolePrintF(CC_COMMAND, "] %s", _iconsole_cmdline.buf); + /* We always want the ] at the left side; we always force these strings to be left + * aligned anyway. So enforce this in all cases by addding a left-to-right marker, + * otherwise it will be drawn at the wrong side with right-to-left texts. */ + IConsolePrintF(CC_COMMAND, LRM "] %s", _iconsole_cmdline.buf); const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf); IConsoleClearCommand(); diff --git a/src/gfx.cpp b/src/gfx.cpp index fb8f3f88ee..58ea7a71d0 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -345,7 +345,7 @@ static int TruncateString(char *str, int maxw, bool ignore_setxy) ddd_w = ddd = GetCharacterWidth(size, '.') * 3; for (ddd_pos = str; (c = Utf8Consume(const_cast(&str))) != '\0'; ) { - if (IsPrintable(c)) { + if (IsPrintable(c) && !IsTextDirectionChar(c)) { w += GetCharacterWidth(size, c); if (w > maxw) { @@ -401,7 +401,7 @@ static int GetStringWidth(const UChar *str) for (;;) { c = *str++; if (c == 0) break; - if (IsPrintable(c)) { + if (IsPrintable(c) && !IsTextDirectionChar(c)) { width += GetCharacterWidth(size, c); } else { switch (c) { @@ -654,7 +654,7 @@ uint32 FormatStringLinebreaks(char *str, const char *last, int maxw) /* whitespace is where we will insert the line-break */ if (IsWhitespace(c)) last_space = str; - if (IsPrintable(c)) { + if (IsPrintable(c) && !IsTextDirectionChar(c)) { int char_w = GetCharacterWidth(size, c); w += char_w; if (w > maxw) { @@ -869,7 +869,7 @@ Dimension GetStringBoundingBox(const char *str) for (;;) { c = Utf8Consume(&str); if (c == 0) break; - if (IsPrintable(c)) { + if (IsPrintable(c) && !IsTextDirectionChar(c)) { br.width += GetCharacterWidth(size, c); } else { switch (c) { @@ -974,7 +974,7 @@ skip_cont:; _last_fontsize = size; return x; // Nothing more to draw, get out. And here is the new x position } - if (IsPrintable(c)) { + if (IsPrintable(c) && !IsTextDirectionChar(c)) { if (x >= dpi->left + dpi->width) goto skip_char; if (x + _max_char_width >= dpi->left) { GfxMainBlitter(GetGlyph(size, c), x, y, BM_COLOUR_REMAP); @@ -998,7 +998,7 @@ skip_cont:; size = FS_SMALL; } else if (c == SCC_BIGFONT) { // {BIGFONT} size = FS_LARGE; - } else { + } else if (!IsTextDirectionChar(c)) { DEBUG(misc, 0, "[utf8] unknown string command character %d", c); } } diff --git a/src/group_gui.cpp b/src/group_gui.cpp index ebd2292d39..88b14f66cd 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -258,6 +258,14 @@ public: size->width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT + 8 + 8; break; + case GRP_WIDGET_SORT_BY_ORDER: { + Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); + d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better. + d.height += padding.height; + *size = maxdim(*size, d); + break; + } + case GRP_WIDGET_LIST_VEHICLE: resize->height = GetVehicleListHeight(this->vehicle_type, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP); size->height = 4 * resize->height; diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 6de0309f68..15f1d91825 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -1066,7 +1066,7 @@ public: switch (widget) { case IDW_DROPDOWN_ORDER: { Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); - d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the word is centered, also looks nice. + d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; *size = maxdim(*size, d); break; diff --git a/src/network/network.cpp b/src/network/network.cpp index 082dd18a41..c833090bf8 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -250,7 +250,13 @@ void NetworkTextMessage(NetworkAction action, ConsoleColour colour, bool self_se SetDParamStr(0, name); SetDParamStr(1, str); SetDParam(2, data); - GetString(message, strid, lastof(message)); + + /* All of these strings start with "***". These characters are interpreted as both left-to-right and + * right-to-left characters depending on the context. As the next text might be an user's name, the + * user name's characters will influence the direction of the "***" instead of the language setting + * of the game. Manually set the direction of the "***" by inserting a text-direction marker. */ + char *msg_ptr = message + Utf8Encode(message, _dynlang.text_dir == TD_LTR ? CHAR_TD_LRM : CHAR_TD_RLM); + GetString(msg_ptr, strid, lastof(message)); DEBUG(desync, 1, "msg: %08x; %02x; %s", _date, _date_fract, message); IConsolePrintF(colour, "%s", message); diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp index c5a2ebea90..873b99eacb 100644 --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -351,13 +351,13 @@ uint32 GetTerrainType(TileIndex tile, TileContext context) } } -TileIndex GetNearbyTile(byte parameter, TileIndex tile) +TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets) { int8 x = GB(parameter, 0, 4); int8 y = GB(parameter, 4, 4); - if (x >= 8) x -= 16; - if (y >= 8) y -= 16; + if (signed_offsets && x >= 8) x -= 16; + if (signed_offsets && y >= 8) y -= 16; /* Swap width and height depending on axis for railway stations */ if (HasStationTileRail(tile) && GetRailStationAxis(tile) == AXIS_Y) Swap(x, y); diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h index 3c0ae571ef..c00936802c 100644 --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -109,7 +109,7 @@ extern IndustryOverrideManager _industry_mngr; extern IndustryTileOverrideManager _industile_mngr; uint32 GetTerrainType(TileIndex tile, TileContext context = TCX_NORMAL); -TileIndex GetNearbyTile(byte parameter, TileIndex tile); +TileIndex GetNearbyTile(byte parameter, TileIndex tile, bool signed_offsets = true); uint32 GetNearbyTileInformation(TileIndex tile); #endif /* NEWGRF_COMMONS_H */ diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index e8e997426a..52211e17f0 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -233,19 +233,19 @@ uint32 IndustryGetVariable(const ResolverObject *object, byte variable, byte par case 0x46: return industry->construction_date; // Date when built - long format - (in days) /* Get industry ID at offset param */ - case 0x60: return GetIndustryIDAtOffset(GetNearbyTile(parameter, industry->location.tile), industry, object->grffile->grfid); + case 0x60: return GetIndustryIDAtOffset(GetNearbyTile(parameter, industry->location.tile, false), industry, object->grffile->grfid); /* Get random tile bits at offset param */ case 0x61: - tile = GetNearbyTile(parameter, tile); + tile = GetNearbyTile(parameter, tile, false); return (IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile) == industry) ? GetIndustryRandomBits(tile) : 0; /* Land info of nearby tiles */ - case 0x62: return GetNearbyIndustryTileInformation(parameter, tile, INVALID_INDUSTRY); + case 0x62: return GetNearbyIndustryTileInformation(parameter, tile, INVALID_INDUSTRY, false); /* Animation stage of nearby tiles */ case 0x63: - tile = GetNearbyTile(parameter, tile); + tile = GetNearbyTile(parameter, tile, false); if (IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile) == industry) { return GetIndustryAnimationState(tile); } diff --git a/src/newgrf_industries.h b/src/newgrf_industries.h index 3013f9170f..164305c065 100644 --- a/src/newgrf_industries.h +++ b/src/newgrf_industries.h @@ -42,6 +42,6 @@ bool CheckIfCallBackAllowsAvailability(IndustryType type, IndustryAvailabilityCa IndustryType MapNewGRFIndustryType(IndustryType grf_type, uint32 grf_id); /* in newgrf_industrytiles.cpp*/ -uint32 GetNearbyIndustryTileInformation(byte parameter, TileIndex tile, IndustryID index); +uint32 GetNearbyIndustryTileInformation(byte parameter, TileIndex tile, IndustryID index, bool signed_offsets = true); #endif /* NEWGRF_INDUSTRIES_H */ diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 9f8f79c5bb..e94a5317cb 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -35,11 +35,12 @@ * @param parameter from callback. It's in fact a pair of coordinates * @param tile TileIndex from which the callback was initiated * @param index of the industry been queried for + * @param signed_offsets Are the x and y offset encoded in parameter signed? * @return a construction of bits obeying the newgrf format */ -uint32 GetNearbyIndustryTileInformation(byte parameter, TileIndex tile, IndustryID index) +uint32 GetNearbyIndustryTileInformation(byte parameter, TileIndex tile, IndustryID index, bool signed_offsets) { - if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required + if (parameter != 0) tile = GetNearbyTile(parameter, tile, signed_offsets); // only perform if it is required bool is_same_industry = (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == index); return GetNearbyTileInformation(tile) | (is_same_industry ? 1 : 0) << 8; diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index 8b87233897..415cb00580 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -1643,6 +1643,7 @@ static const SaveLoadFormat _saveload_formats[] = { #else {"zlib", TO_BE32X('OTTZ'), NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, #endif + {"lzma", TO_BE32X('OTTX'), NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, }; /** diff --git a/src/station_gui.cpp b/src/station_gui.cpp index dfbdc4a54c..929fca954f 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -378,7 +378,7 @@ public: switch (widget) { case SLW_SORTBY: { Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); - d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the word is centered, also looks nice. + d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; *size = maxdim(*size, d); break; diff --git a/src/string_func.h b/src/string_func.h index f921397a0b..33b4581145 100644 --- a/src/string_func.h +++ b/src/string_func.h @@ -222,6 +222,28 @@ static inline char *Utf8PrevChar(char *s) return ret; } +/** + * Is the given character a text direction character. + * @param c The character to test. + * @return true iff the character is used to influence + * the text direction. + */ +static inline bool IsTextDirectionChar(WChar c) +{ + switch (c) { + case CHAR_TD_LRM: + case CHAR_TD_RLM: + case CHAR_TD_LRE: + case CHAR_TD_RLE: + case CHAR_TD_LRO: + case CHAR_TD_RLO: + case CHAR_TD_PDF: + return true; + + default: + return false; + } +} static inline bool IsPrintable(WChar c) { diff --git a/src/string_type.h b/src/string_type.h index 4afbaa9d30..0078c20543 100644 --- a/src/string_type.h +++ b/src/string_type.h @@ -15,6 +15,9 @@ /** A non-breaking space. */ #define NBSP "\xC2\xA0" +/** A left-to-right marker, marks the next character as left-to-right. */ +#define LRM "\xE2\x80\x8E" + /** * Valid filter types for IsValidChar. */ @@ -27,4 +30,14 @@ enum CharSetFilter { typedef uint32 WChar; +/* The following are directional formatting codes used to get the LTR and RTL strings right: + * http://www.unicode.org/unicode/reports/tr9/#Directional_Formatting_Codes */ +static const WChar CHAR_TD_LRM = 0x200E; ///< The next character acts like a left-to-right character. +static const WChar CHAR_TD_RLM = 0x200F; ///< The next character acts like a right-to-left character. +static const WChar CHAR_TD_LRE = 0x202A; ///< The following text is embedded left-to-right. +static const WChar CHAR_TD_RLE = 0x202B; ///< The following text is embedded right-to-left. +static const WChar CHAR_TD_LRO = 0x202D; ///< Force the following characters to be treated as left-to-right characters. +static const WChar CHAR_TD_RLO = 0x202E; ///< Force the following characters to be treated as right-to-left characters. +static const WChar CHAR_TD_PDF = 0x202C; ///< Restore the text-direction state to before the last LRE, RLE, LRO or RLO. + #endif /* STRING_TYPE_H */ diff --git a/src/table/strgen_tables.h b/src/table/strgen_tables.h index b37133d538..3472a1d9af 100644 --- a/src/table/strgen_tables.h +++ b/src/table/strgen_tables.h @@ -134,13 +134,13 @@ static const CmdStruct _cmd_structs[] = { /* The following are directional formatting codes used to get the RTL strings right: * http://www.unicode.org/unicode/reports/tr9/#Directional_Formatting_Codes */ - {"LRM", EmitSingleChar, 0x200E, 0, C_DONTCOUNT}, - {"RLM", EmitSingleChar, 0x200F, 0, C_DONTCOUNT}, - {"LRE", EmitSingleChar, 0x202A, 0, C_DONTCOUNT}, - {"RLE", EmitSingleChar, 0x202B, 0, C_DONTCOUNT}, - {"LRO", EmitSingleChar, 0x202D, 0, C_DONTCOUNT}, - {"RLO", EmitSingleChar, 0x202E, 0, C_DONTCOUNT}, - {"PDF", EmitSingleChar, 0x202C, 0, C_DONTCOUNT}, + {"LRM", EmitSingleChar, CHAR_TD_LRM, 0, C_DONTCOUNT}, + {"RLM", EmitSingleChar, CHAR_TD_RLM, 0, C_DONTCOUNT}, + {"LRE", EmitSingleChar, CHAR_TD_LRE, 0, C_DONTCOUNT}, + {"RLE", EmitSingleChar, CHAR_TD_RLE, 0, C_DONTCOUNT}, + {"LRO", EmitSingleChar, CHAR_TD_LRO, 0, C_DONTCOUNT}, + {"RLO", EmitSingleChar, CHAR_TD_RLO, 0, C_DONTCOUNT}, + {"PDF", EmitSingleChar, CHAR_TD_PDF, 0, C_DONTCOUNT}, }; /** Description of a plural form */ diff --git a/src/town_gui.cpp b/src/town_gui.cpp index d453748b72..d9b2ca02b9 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -817,7 +817,7 @@ public: case TDW_SORTNAME: case TDW_SORTPOPULATION: { Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); - d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the word is centered, also looks nice. + d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better. d.height += padding.height; *size = maxdim(*size, d); break; diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index fa6a49e920..c428b42819 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -999,20 +999,30 @@ public: virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { - if (widget != VLW_WIDGET_LIST) return; + switch (widget) { + case VLW_WIDGET_LIST: + resize->height = GetVehicleListHeight(this->vehicle_type, 1); + switch (this->vehicle_type) { + case VEH_TRAIN: + case VEH_ROAD: + size->height = 6 * resize->height; - resize->height = GetVehicleListHeight(this->vehicle_type, 1); + break; + case VEH_SHIP: + case VEH_AIRCRAFT: + size->height = 4 * resize->height; + break; + default: NOT_REACHED(); + } + break; - switch (this->vehicle_type) { - case VEH_TRAIN: - case VEH_ROAD: - size->height = 6 * resize->height; + case VLW_WIDGET_SORT_ORDER: { + Dimension d = GetStringBoundingBox(this->GetWidget(widget)->widget_data); + d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better. + d.height += padding.height; + *size = maxdim(*size, d); break; - case VEH_SHIP: - case VEH_AIRCRAFT: - size->height = 4 * resize->height; - break; - default: NOT_REACHED(); + } } }