mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-12 10:30:28 +00:00
(svn r24337) -Feature: Allow filtering for multiple words (separated by whitespace resp. quoted) in the sign list, content- and NewGRF-guis.
This commit is contained in:
parent
db709aff32
commit
03046f614f
@ -360,6 +360,7 @@
|
|||||||
<ClCompile Include="..\src\station.cpp" />
|
<ClCompile Include="..\src\station.cpp" />
|
||||||
<ClCompile Include="..\src\strgen\strgen_base.cpp" />
|
<ClCompile Include="..\src\strgen\strgen_base.cpp" />
|
||||||
<ClCompile Include="..\src\string.cpp" />
|
<ClCompile Include="..\src\string.cpp" />
|
||||||
|
<ClCompile Include="..\src\stringfilter.cpp" />
|
||||||
<ClCompile Include="..\src\strings.cpp" />
|
<ClCompile Include="..\src\strings.cpp" />
|
||||||
<ClCompile Include="..\src\subsidy.cpp" />
|
<ClCompile Include="..\src\subsidy.cpp" />
|
||||||
<ClCompile Include="..\src\textbuf.cpp" />
|
<ClCompile Include="..\src\textbuf.cpp" />
|
||||||
@ -566,6 +567,7 @@
|
|||||||
<ClInclude Include="..\src\strgen\strgen.h" />
|
<ClInclude Include="..\src\strgen\strgen.h" />
|
||||||
<ClInclude Include="..\src\string_func.h" />
|
<ClInclude Include="..\src\string_func.h" />
|
||||||
<ClInclude Include="..\src\string_type.h" />
|
<ClInclude Include="..\src\string_type.h" />
|
||||||
|
<ClInclude Include="..\src\stringfilter_type.h" />
|
||||||
<ClInclude Include="..\src\strings_func.h" />
|
<ClInclude Include="..\src\strings_func.h" />
|
||||||
<ClInclude Include="..\src\strings_type.h" />
|
<ClInclude Include="..\src\strings_type.h" />
|
||||||
<ClInclude Include="..\src\subsidy_base.h" />
|
<ClInclude Include="..\src\subsidy_base.h" />
|
||||||
|
@ -309,6 +309,9 @@
|
|||||||
<ClCompile Include="..\src\string.cpp">
|
<ClCompile Include="..\src\string.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\stringfilter.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\src\strings.cpp">
|
<ClCompile Include="..\src\strings.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -927,6 +930,9 @@
|
|||||||
<ClInclude Include="..\src\string_type.h">
|
<ClInclude Include="..\src\string_type.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\stringfilter_type.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\src\strings_func.h">
|
<ClInclude Include="..\src\strings_func.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -710,6 +710,10 @@
|
|||||||
RelativePath=".\..\src\string.cpp"
|
RelativePath=".\..\src\string.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\stringfilter.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\strings.cpp"
|
RelativePath=".\..\src\strings.cpp"
|
||||||
>
|
>
|
||||||
@ -1538,6 +1542,10 @@
|
|||||||
RelativePath=".\..\src\string_type.h"
|
RelativePath=".\..\src\string_type.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\stringfilter_type.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\strings_func.h"
|
RelativePath=".\..\src\strings_func.h"
|
||||||
>
|
>
|
||||||
|
@ -707,6 +707,10 @@
|
|||||||
RelativePath=".\..\src\string.cpp"
|
RelativePath=".\..\src\string.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\stringfilter.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\strings.cpp"
|
RelativePath=".\..\src\strings.cpp"
|
||||||
>
|
>
|
||||||
@ -1535,6 +1539,10 @@
|
|||||||
RelativePath=".\..\src\string_type.h"
|
RelativePath=".\..\src\string_type.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\stringfilter_type.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\strings_func.h"
|
RelativePath=".\..\src\strings_func.h"
|
||||||
>
|
>
|
||||||
|
@ -70,6 +70,7 @@ spritecache.cpp
|
|||||||
station.cpp
|
station.cpp
|
||||||
strgen/strgen_base.cpp
|
strgen/strgen_base.cpp
|
||||||
string.cpp
|
string.cpp
|
||||||
|
stringfilter.cpp
|
||||||
strings.cpp
|
strings.cpp
|
||||||
subsidy.cpp
|
subsidy.cpp
|
||||||
textbuf.cpp
|
textbuf.cpp
|
||||||
@ -299,6 +300,7 @@ stdafx.h
|
|||||||
strgen/strgen.h
|
strgen/strgen.h
|
||||||
string_func.h
|
string_func.h
|
||||||
string_type.h
|
string_type.h
|
||||||
|
stringfilter_type.h
|
||||||
strings_func.h
|
strings_func.h
|
||||||
strings_type.h
|
strings_type.h
|
||||||
subsidy_base.h
|
subsidy_base.h
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "../game/game.hpp"
|
#include "../game/game.hpp"
|
||||||
#include "../base_media_base.h"
|
#include "../base_media_base.h"
|
||||||
#include "../sortlist_type.h"
|
#include "../sortlist_type.h"
|
||||||
|
#include "../stringfilter_type.h"
|
||||||
#include "../querystring_gui.h"
|
#include "../querystring_gui.h"
|
||||||
#include "../core/geometry_func.hpp"
|
#include "../core/geometry_func.hpp"
|
||||||
#include "network_content_gui.h"
|
#include "network_content_gui.h"
|
||||||
@ -234,7 +235,7 @@ public:
|
|||||||
/** Window that lists the content that's at the content server */
|
/** Window that lists the content that's at the content server */
|
||||||
class NetworkContentListWindow : public QueryStringBaseWindow, ContentCallback {
|
class NetworkContentListWindow : public QueryStringBaseWindow, ContentCallback {
|
||||||
/** List with content infos. */
|
/** List with content infos. */
|
||||||
typedef GUIList<const ContentInfo*> GUIContentList;
|
typedef GUIList<const ContentInfo *, StringFilter &> GUIContentList;
|
||||||
|
|
||||||
static const uint EDITBOX_MAX_SIZE = 50; ///< Maximum size of the editbox in characters.
|
static const uint EDITBOX_MAX_SIZE = 50; ///< Maximum size of the editbox in characters.
|
||||||
static const uint EDITBOX_MAX_LENGTH = 300; ///< Maximum size of the editbox in pixels.
|
static const uint EDITBOX_MAX_LENGTH = 300; ///< Maximum size of the editbox in pixels.
|
||||||
@ -245,6 +246,7 @@ class NetworkContentListWindow : public QueryStringBaseWindow, ContentCallback {
|
|||||||
static GUIContentList::FilterFunction * const filter_funcs[]; ///< Filter functions.
|
static GUIContentList::FilterFunction * const filter_funcs[]; ///< Filter functions.
|
||||||
GUIContentList content; ///< List with content
|
GUIContentList content; ///< List with content
|
||||||
bool auto_select; ///< Automatically select all content when the meta-data becomes available
|
bool auto_select; ///< Automatically select all content when the meta-data becomes available
|
||||||
|
StringFilter string_filter; ///< Filter for content list
|
||||||
|
|
||||||
const ContentInfo *selected; ///< The selected content info
|
const ContentInfo *selected; ///< The selected content info
|
||||||
int list_pos; ///< Our position in the list
|
int list_pos; ///< Our position in the list
|
||||||
@ -318,18 +320,20 @@ class NetworkContentListWindow : public QueryStringBaseWindow, ContentCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Filter content by tags/name */
|
/** Filter content by tags/name */
|
||||||
static bool CDECL TagNameFilter(const ContentInfo * const *a, const char *filter_string)
|
static bool CDECL TagNameFilter(const ContentInfo * const *a, StringFilter &filter)
|
||||||
{
|
{
|
||||||
|
filter.ResetState();
|
||||||
for (int i = 0; i < (*a)->tag_count; i++) {
|
for (int i = 0; i < (*a)->tag_count; i++) {
|
||||||
if (strcasestr((*a)->tags[i], filter_string) != NULL) return true;
|
filter.AddLine((*a)->tags[i]);
|
||||||
}
|
}
|
||||||
return strcasestr((*a)->name, filter_string) != NULL;
|
filter.AddLine((*a)->name);
|
||||||
|
return filter.GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filter the content list */
|
/** Filter the content list */
|
||||||
void FilterContentList()
|
void FilterContentList()
|
||||||
{
|
{
|
||||||
if (!this->content.Filter(this->edit_str_buf)) return;
|
if (!this->content.Filter(this->string_filter)) return;
|
||||||
|
|
||||||
/* update list position */
|
/* update list position */
|
||||||
for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
|
for (ConstContentIterator iter = this->content.Begin(); iter != this->content.End(); iter++) {
|
||||||
@ -742,7 +746,8 @@ public:
|
|||||||
|
|
||||||
virtual void OnOSKInput(int wid)
|
virtual void OnOSKInput(int wid)
|
||||||
{
|
{
|
||||||
this->content.SetFilterState(!StrEmpty(this->edit_str_buf));
|
this->string_filter.SetFilterTerm(this->edit_str_buf);
|
||||||
|
this->content.SetFilterState(!this->string_filter.IsEmpty());
|
||||||
this->content.ForceRebuild();
|
this->content.ForceRebuild();
|
||||||
this->InvalidateData();
|
this->InvalidateData();
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include "network/network_content.h"
|
#include "network/network_content.h"
|
||||||
#include "sortlist_type.h"
|
#include "sortlist_type.h"
|
||||||
|
#include "stringfilter_type.h"
|
||||||
#include "querystring_gui.h"
|
#include "querystring_gui.h"
|
||||||
#include "core/geometry_func.hpp"
|
#include "core/geometry_func.hpp"
|
||||||
#include "newgrf_text.h"
|
#include "newgrf_text.h"
|
||||||
@ -587,7 +588,7 @@ static void NewGRFConfirmationCallback(Window *w, bool confirmed);
|
|||||||
* Window for showing NewGRF files
|
* Window for showing NewGRF files
|
||||||
*/
|
*/
|
||||||
struct NewGRFWindow : public QueryStringBaseWindow, NewGRFScanCallback {
|
struct NewGRFWindow : public QueryStringBaseWindow, NewGRFScanCallback {
|
||||||
typedef GUIList<const GRFConfig *> GUIGRFConfigList;
|
typedef GUIList<const GRFConfig *, StringFilter &> GUIGRFConfigList;
|
||||||
|
|
||||||
static const uint EDITBOX_MAX_SIZE = 50;
|
static const uint EDITBOX_MAX_SIZE = 50;
|
||||||
|
|
||||||
@ -599,6 +600,7 @@ struct NewGRFWindow : public QueryStringBaseWindow, NewGRFScanCallback {
|
|||||||
GUIGRFConfigList avails; ///< Available (non-active) grfs.
|
GUIGRFConfigList avails; ///< Available (non-active) grfs.
|
||||||
const GRFConfig *avail_sel; ///< Currently selected available grf. \c NULL is none is selected.
|
const GRFConfig *avail_sel; ///< Currently selected available grf. \c NULL is none is selected.
|
||||||
int avail_pos; ///< Index of #avail_sel if existing, else \c -1.
|
int avail_pos; ///< Index of #avail_sel if existing, else \c -1.
|
||||||
|
StringFilter string_filter; ///< Filter for available grf.
|
||||||
|
|
||||||
GRFConfig *actives; ///< Temporary active grf list to which changes are made.
|
GRFConfig *actives; ///< Temporary active grf list to which changes are made.
|
||||||
GRFConfig *active_sel; ///< Selected active grf item.
|
GRFConfig *active_sel; ///< Selected active grf item.
|
||||||
@ -1297,7 +1299,8 @@ struct NewGRFWindow : public QueryStringBaseWindow, NewGRFScanCallback {
|
|||||||
{
|
{
|
||||||
if (!this->editable) return;
|
if (!this->editable) return;
|
||||||
|
|
||||||
this->avails.SetFilterState(!StrEmpty(this->edit_str_buf));
|
string_filter.SetFilterTerm(this->edit_str_buf);
|
||||||
|
this->avails.SetFilterState(!string_filter.IsEmpty());
|
||||||
this->avails.ForceRebuild();
|
this->avails.ForceRebuild();
|
||||||
this->InvalidateData(0);
|
this->InvalidateData(0);
|
||||||
}
|
}
|
||||||
@ -1387,12 +1390,13 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Filter grfs by tags/name */
|
/** Filter grfs by tags/name */
|
||||||
static bool CDECL TagNameFilter(const GRFConfig * const *a, const char *filter_string)
|
static bool CDECL TagNameFilter(const GRFConfig * const *a, StringFilter &filter)
|
||||||
{
|
{
|
||||||
if (strcasestr((*a)->GetName(), filter_string) != NULL) return true;
|
filter.ResetState();
|
||||||
if ((*a)->filename != NULL && strcasestr((*a)->filename, filter_string) != NULL) return true;
|
filter.AddLine((*a)->GetName());
|
||||||
if ((*a)->GetDescription() != NULL && strcasestr((*a)->GetDescription(), filter_string) != NULL) return true;
|
filter.AddLine((*a)->filename);
|
||||||
return false;
|
filter.AddLine((*a)->GetDescription());
|
||||||
|
return filter.GetState();;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildAvailables()
|
void BuildAvailables()
|
||||||
@ -1423,7 +1427,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->avails.Filter(this->edit_str_buf);
|
this->avails.Filter(this->string_filter);
|
||||||
this->avails.Compact();
|
this->avails.Compact();
|
||||||
this->avails.RebuildDone();
|
this->avails.RebuildDone();
|
||||||
this->avails.Sort();
|
this->avails.Sort();
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "viewport_func.h"
|
#include "viewport_func.h"
|
||||||
#include "querystring_gui.h"
|
#include "querystring_gui.h"
|
||||||
#include "sortlist_type.h"
|
#include "sortlist_type.h"
|
||||||
|
#include "stringfilter_type.h"
|
||||||
#include "string_func.h"
|
#include "string_func.h"
|
||||||
#include "core/geometry_func.hpp"
|
#include "core/geometry_func.hpp"
|
||||||
#include "hotkeys.h"
|
#include "hotkeys.h"
|
||||||
@ -32,35 +33,23 @@
|
|||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
#include "table/sprites.h"
|
#include "table/sprites.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains the necessary information to decide if a sign should
|
|
||||||
* be filtered out or not. This struct is sent as parameter to the
|
|
||||||
* sort functions of the GUISignList.
|
|
||||||
*/
|
|
||||||
struct FilterInfo {
|
|
||||||
const char *string; ///< String to match sign names against
|
|
||||||
bool case_sensitive; ///< Should case sensitive matching be used?
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SignList {
|
struct SignList {
|
||||||
/**
|
/**
|
||||||
* A GUIList contains signs and uses a custom data structure called #FilterInfo for
|
* A GUIList contains signs and uses a StringFilter for filtering.
|
||||||
* passing data to the sort functions.
|
|
||||||
*/
|
*/
|
||||||
typedef GUIList<const Sign *, FilterInfo> GUISignList;
|
typedef GUIList<const Sign *, StringFilter &> GUISignList;
|
||||||
|
|
||||||
static const Sign *last_sign;
|
static const Sign *last_sign;
|
||||||
GUISignList signs;
|
GUISignList signs;
|
||||||
|
|
||||||
char filter_string[MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH]; ///< The match string to be used when the GUIList is (re)-sorted.
|
StringFilter string_filter; ///< The match string to be used when the GUIList is (re)-sorted.
|
||||||
static bool match_case; ///< Should case sensitive matching be used?
|
static bool match_case; ///< Should case sensitive matching be used?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a SignList with filtering disabled by default.
|
* Creates a SignList with filtering disabled by default.
|
||||||
*/
|
*/
|
||||||
SignList()
|
SignList() : string_filter(&match_case)
|
||||||
{
|
{
|
||||||
filter_string[0] = '\0';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildSignsList()
|
void BuildSignsList()
|
||||||
@ -108,26 +97,28 @@ struct SignList {
|
|||||||
this->last_sign = NULL;
|
this->last_sign = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filter sign list by sign name (case sensitive setting in FilterInfo) */
|
/** Filter sign list by sign name */
|
||||||
static bool CDECL SignNameFilter(const Sign * const *a, FilterInfo filter_info)
|
static bool CDECL SignNameFilter(const Sign * const *a, StringFilter &filter)
|
||||||
{
|
{
|
||||||
/* Get sign string */
|
/* Get sign string */
|
||||||
char buf1[MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH];
|
char buf1[MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH];
|
||||||
SetDParam(0, (*a)->index);
|
SetDParam(0, (*a)->index);
|
||||||
GetString(buf1, STR_SIGN_NAME, lastof(buf1));
|
GetString(buf1, STR_SIGN_NAME, lastof(buf1));
|
||||||
|
|
||||||
return (filter_info.case_sensitive ? strstr(buf1, filter_info.string) : strcasestr(buf1, filter_info.string)) != NULL;
|
filter.ResetState();
|
||||||
|
filter.AddLine(buf1);
|
||||||
|
return filter.GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filter sign list excluding OWNER_DEITY */
|
/** Filter sign list excluding OWNER_DEITY */
|
||||||
static bool CDECL OwnerDeityFilter(const Sign * const *a, FilterInfo filter_info)
|
static bool CDECL OwnerDeityFilter(const Sign * const *a, StringFilter &filter)
|
||||||
{
|
{
|
||||||
/* You should never be able to edit signs of owner DEITY */
|
/* You should never be able to edit signs of owner DEITY */
|
||||||
return (*a)->owner != OWNER_DEITY;
|
return (*a)->owner != OWNER_DEITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Filter sign list by owner */
|
/** Filter sign list by owner */
|
||||||
static bool CDECL OwnerVisibilityFilter(const Sign * const *a, FilterInfo filter_info)
|
static bool CDECL OwnerVisibilityFilter(const Sign * const *a, StringFilter &filter)
|
||||||
{
|
{
|
||||||
assert(!HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS));
|
assert(!HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS));
|
||||||
/* Hide sign if non-own signs are hidden in the viewport */
|
/* Hide sign if non-own signs are hidden in the viewport */
|
||||||
@ -137,11 +128,10 @@ struct SignList {
|
|||||||
/** Filter out signs from the sign list that does not match the name filter */
|
/** Filter out signs from the sign list that does not match the name filter */
|
||||||
void FilterSignList()
|
void FilterSignList()
|
||||||
{
|
{
|
||||||
FilterInfo filter_info = {this->filter_string, this->match_case};
|
this->signs.Filter(&SignNameFilter, this->string_filter);
|
||||||
this->signs.Filter(&SignNameFilter, filter_info);
|
if (_game_mode != GM_EDITOR) this->signs.Filter(&OwnerDeityFilter, this->string_filter);
|
||||||
if (_game_mode != GM_EDITOR) this->signs.Filter(&OwnerDeityFilter, filter_info);
|
|
||||||
if (!HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS)) {
|
if (!HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS)) {
|
||||||
this->signs.Filter(&OwnerVisibilityFilter, filter_info);
|
this->signs.Filter(&OwnerVisibilityFilter, this->string_filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -200,17 +190,8 @@ struct SignListWindow : QueryStringBaseWindow, SignList {
|
|||||||
void SetFilterString(const char *new_filter_string)
|
void SetFilterString(const char *new_filter_string)
|
||||||
{
|
{
|
||||||
/* check if there is a new filter string */
|
/* check if there is a new filter string */
|
||||||
if (!StrEmpty(new_filter_string)) {
|
this->string_filter.SetFilterTerm(new_filter_string);
|
||||||
/* Copy new filter string */
|
this->SetWidgetDisabledState(WID_SIL_FILTER_CLEAR_BTN, StrEmpty(new_filter_string));
|
||||||
strecpy(this->filter_string, new_filter_string, lastof(this->filter_string));
|
|
||||||
|
|
||||||
this->EnableWidget(WID_SIL_FILTER_CLEAR_BTN);
|
|
||||||
} else {
|
|
||||||
/* There is no new string -> clear this->filter_string */
|
|
||||||
this->filter_string[0] = '\0';
|
|
||||||
|
|
||||||
this->DisableWidget(WID_SIL_FILTER_CLEAR_BTN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Repaint the clear button since its disabled state may have changed */
|
/* Repaint the clear button since its disabled state may have changed */
|
||||||
this->SetWidgetDirty(WID_SIL_FILTER_CLEAR_BTN);
|
this->SetWidgetDirty(WID_SIL_FILTER_CLEAR_BTN);
|
||||||
@ -386,7 +367,7 @@ struct SignListWindow : QueryStringBaseWindow, SignList {
|
|||||||
/* When there is a filter string, we always need to rebuild the list even if
|
/* When there is a filter string, we always need to rebuild the list even if
|
||||||
* the amount of signs in total is unchanged, as the subset of signs that is
|
* the amount of signs in total is unchanged, as the subset of signs that is
|
||||||
* accepted by the filter might has changed. */
|
* accepted by the filter might has changed. */
|
||||||
if (data == 0 || data == -1 || !StrEmpty(this->filter_string)) { // New or deleted sign, changed visibility setting or there is a filter string
|
if (data == 0 || data == -1 || !this->string_filter.IsEmpty()) { // New or deleted sign, changed visibility setting or there is a filter string
|
||||||
/* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
|
/* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
|
||||||
this->signs.ForceRebuild();
|
this->signs.ForceRebuild();
|
||||||
} else { // Change of sign contents while there is no filter string
|
} else { // Change of sign contents while there is no filter string
|
||||||
|
120
src/stringfilter.cpp
Normal file
120
src/stringfilter.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||||
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file stringfilter.cpp Searching and filtering using a stringterm. */
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "string_func.h"
|
||||||
|
#include "stringfilter_type.h"
|
||||||
|
|
||||||
|
static const WChar STATE_WHITESPACE = ' ';
|
||||||
|
static const WChar STATE_WORD = 'w';
|
||||||
|
static const WChar STATE_QUOTE1 = '\'';
|
||||||
|
static const WChar STATE_QUOTE2 = '"';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the term to filter on.
|
||||||
|
* @param str Filter term
|
||||||
|
*/
|
||||||
|
void StringFilter::SetFilterTerm(const char *str)
|
||||||
|
{
|
||||||
|
this->word_index.Reset();
|
||||||
|
this->word_matches = 0;
|
||||||
|
free(this->filter_buffer);
|
||||||
|
|
||||||
|
assert(str != NULL);
|
||||||
|
|
||||||
|
char *dest = (char *)malloc(strlen(str) + 1);
|
||||||
|
this->filter_buffer = dest;
|
||||||
|
|
||||||
|
WChar state = STATE_WHITESPACE;
|
||||||
|
const char *pos = str;
|
||||||
|
WordState *word = NULL;
|
||||||
|
size_t len;
|
||||||
|
for (;; pos += len) {
|
||||||
|
WChar c;
|
||||||
|
len = Utf8Decode(&c, pos);
|
||||||
|
|
||||||
|
if (c == 0 || (state == STATE_WORD && IsWhitespace(c))) {
|
||||||
|
/* Finish word */
|
||||||
|
if (word != NULL) {
|
||||||
|
*(dest++) = '\0';
|
||||||
|
word = NULL;
|
||||||
|
}
|
||||||
|
state = STATE_WHITESPACE;
|
||||||
|
if (c != 0) continue; else break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == STATE_WHITESPACE) {
|
||||||
|
/* Skip whitespace */
|
||||||
|
if (IsWhitespace(c)) continue;
|
||||||
|
state = STATE_WORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == STATE_QUOTE1 || c == STATE_QUOTE2) {
|
||||||
|
if (state == c) {
|
||||||
|
/* Stop quoting */
|
||||||
|
state = STATE_WORD;
|
||||||
|
continue;
|
||||||
|
} else if (state == STATE_WORD) {
|
||||||
|
/* Start quoting */
|
||||||
|
state = c;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add to word */
|
||||||
|
if (word == NULL) {
|
||||||
|
word = this->word_index.Append();
|
||||||
|
word->start = dest;
|
||||||
|
word->match = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest, pos, len);
|
||||||
|
dest += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the matching state to process a new item.
|
||||||
|
*/
|
||||||
|
void StringFilter::ResetState()
|
||||||
|
{
|
||||||
|
this->word_matches = 0;
|
||||||
|
const WordState *end = this->word_index.End();
|
||||||
|
for (WordState *it = this->word_index.Begin(); it != end; ++it) {
|
||||||
|
it->match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass another text line from the current item to the filter.
|
||||||
|
*
|
||||||
|
* You can call this multiple times for a single item, if the filter shall apply to multiple things.
|
||||||
|
* Before processing the next item you have to call ResetState().
|
||||||
|
*
|
||||||
|
* @param str Another line from the item.
|
||||||
|
*/
|
||||||
|
void StringFilter::AddLine(const char *str)
|
||||||
|
{
|
||||||
|
if (str == NULL) return;
|
||||||
|
|
||||||
|
bool match_case = this->case_sensitive != NULL && *this->case_sensitive;
|
||||||
|
const WordState *end = this->word_index.End();
|
||||||
|
for (WordState *it = this->word_index.Begin(); it != end; ++it) {
|
||||||
|
if (!it->match) {
|
||||||
|
if ((match_case ? strstr(str, it->start) : strcasestr(str, it->start)) != NULL) {
|
||||||
|
it->match = true;
|
||||||
|
this->word_matches++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
72
src/stringfilter_type.h
Normal file
72
src/stringfilter_type.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of OpenTTD.
|
||||||
|
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||||
|
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file stringfilter_type.h Searching and filtering using a stringterm. */
|
||||||
|
|
||||||
|
#ifndef STRINGFILTER_TYPE_H
|
||||||
|
#define STRINGFILTER_TYPE_H
|
||||||
|
|
||||||
|
#include "core/smallvec_type.hpp"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String filter and state.
|
||||||
|
*
|
||||||
|
* The filter takes a stringterm and parses it into words separated by whitespace.
|
||||||
|
* The whitespace-separation can be avoided by quoting words in the searchterm using " or '.
|
||||||
|
* The quotation characters can be nested or concatenated in a unix-shell style.
|
||||||
|
*
|
||||||
|
* When filtering an item, all words are checked for matches, and the filter matches if every word
|
||||||
|
* matched. So, effectively this is a AND search for all entered words.
|
||||||
|
*
|
||||||
|
* Once the filter is set up using SetFilterTerm, multiple items can be filtered consecutively.
|
||||||
|
* 1. For every item first call ResetState() which resets the matching-state.
|
||||||
|
* 2. Pass all lines of the item via AddLine() to the filter.
|
||||||
|
* 3. Check the matching-result for the item via GetState().
|
||||||
|
*/
|
||||||
|
struct StringFilter {
|
||||||
|
private:
|
||||||
|
/** State of a single filter word */
|
||||||
|
struct WordState {
|
||||||
|
const char *start; ///< Word to filter for.
|
||||||
|
bool match; ///< Already matched?
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *filter_buffer; ///< Parsed filter string. Words separated by 0.
|
||||||
|
SmallVector<WordState, 4> word_index; ///< Word index and filter state.
|
||||||
|
uint word_matches; ///< Summary of filter state: Number of words matched.
|
||||||
|
|
||||||
|
const bool *case_sensitive; ///< Match case-sensitively (usually a static variable).
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor for filter.
|
||||||
|
* @param case_sensitive Pointer to a (usually static) variable controlling the case-sensitivity. NULL means always case-insensitive.
|
||||||
|
*/
|
||||||
|
StringFilter(const bool *case_sensitive = NULL) : filter_buffer(NULL), word_matches(0), case_sensitive(case_sensitive) {}
|
||||||
|
~StringFilter() { free(this->filter_buffer); }
|
||||||
|
|
||||||
|
void SetFilterTerm(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether any filter words were entered.
|
||||||
|
* @return true if no words were entered.
|
||||||
|
*/
|
||||||
|
bool IsEmpty() const { return this->word_index.Length() == 0; }
|
||||||
|
|
||||||
|
void ResetState();
|
||||||
|
void AddLine(const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the matching state of the current item.
|
||||||
|
* @return true if matched.
|
||||||
|
*/
|
||||||
|
bool GetState() const { return this->word_matches == this->word_index.Length(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* STRINGFILTER_TYPE_H */
|
Loading…
Reference in New Issue
Block a user