# signs_gui.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "company_gui.h"
    00014 #include "company_func.h"
    00015 #include "signs_base.h"
    00016 #include "signs_func.h"
    00017 #include "debug.h"
    00018 #include "command_func.h"
    00019 #include "strings_func.h"
    00020 #include "window_func.h"
    00021 #include "map_func.h"
    00022 #include "viewport_func.h"
    00023 #include "querystring_gui.h"
    00024 #include "sortlist_type.h"
    00025 #include "stringfilter_type.h"
    00026 #include "string_func.h"
    00027 #include "core/geometry_func.hpp"
    00028 #include "hotkeys.h"
    00029 #include "transparency.h"
    00030
    00031 #include "widgets/sign_widget.h"
    00032
    00033 #include "table/strings.h"
    00034 #include "table/sprites.h"
    00035
    00036 struct SignList {
    00040   typedef GUIList<const Sign *, StringFilter &> GUISignList;
    00041
    00042   static const Sign *last_sign;
    00043   GUISignList signs;
    00044
    00045   StringFilter string_filter;
    00046   static bool match_case;
    00047
    00051   SignList() : string_filter(&match_case)
    00052   {
    00053   }
    00054
    00055   void BuildSignsList()
    00056   {
    00057     if (!this->signs.NeedRebuild()) return;
    00058
    00059     DEBUG(misc, 3, "Building sign list");
    00060
    00061     this->signs.Clear();
    00062
    00063     const Sign *si;
    00064     FOR_ALL_SIGNS(si) *this->signs.Append() = si;
    00065
    00066     this->signs.SetFilterState(true);
    00067     this->FilterSignList();
    00068     this->signs.Compact();
    00069     this->signs.RebuildDone();
    00070   }
    00071
    00073   static int CDECL SignNameSorter(const Sign * const *a, const Sign * const *b)
    00074   {
    00075     static char buf_cache[64];
    00076     char buf[64];
    00077
    00078     SetDParam(0, (*a)->index);
    00079     GetString(buf, STR_SIGN_NAME, lastof(buf));
    00080
    00081     if (*b != last_sign) {
    00082       last_sign = *b;
    00083       SetDParam(0, (*b)->index);
    00084       GetString(buf_cache, STR_SIGN_NAME, lastof(buf_cache));
    00085     }
    00086
    00087     int r = strnatcmp(buf, buf_cache); // Sort by name (natural sorting).
    00088
    00089     return r != 0 ? r : ((*a)->index - (*b)->index);
    00090   }
    00091
    00092   void SortSignsList()
    00093   {
    00094     if (!this->signs.Sort(&SignNameSorter)) return;
    00095
    00096     /* Reset the name sorter sort cache */
    00097     this->last_sign = NULL;
    00098   }
    00099
    00101   static bool CDECL SignNameFilter(const Sign * const *a, StringFilter &filter)
    00102   {
    00103     /* Get sign string */
    00104     char buf1[MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH];
    00105     SetDParam(0, (*a)->index);
    00106     GetString(buf1, STR_SIGN_NAME, lastof(buf1));
    00107
    00108     filter.ResetState();
    00109     filter.AddLine(buf1);
    00110     return filter.GetState();
    00111   }
    00112
    00114   static bool CDECL OwnerDeityFilter(const Sign * const *a, StringFilter &filter)
    00115   {
    00116     /* You should never be able to edit signs of owner DEITY */
    00117     return (*a)->owner != OWNER_DEITY;
    00118   }
    00119
    00121   static bool CDECL OwnerVisibilityFilter(const Sign * const *a, StringFilter &filter)
    00122   {
    00123     assert(!HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS));
    00124     /* Hide sign if non-own signs are hidden in the viewport */
    00125     return (*a)->owner == _local_company || (*a)->owner == OWNER_DEITY;
    00126   }
    00127
    00129   void FilterSignList()
    00130   {
    00131     this->signs.Filter(&SignNameFilter, this->string_filter);
    00132     if (_game_mode != GM_EDITOR) this->signs.Filter(&OwnerDeityFilter, this->string_filter);
    00133     if (!HasBit(_display_opt, DO_SHOW_COMPETITOR_SIGNS)) {
    00134       this->signs.Filter(&OwnerVisibilityFilter, this->string_filter);
    00135     }
    00136   }
    00137 };
    00138
    00139 const Sign *SignList::last_sign = NULL;
    00140 bool SignList::match_case = false;
    00141
    00143 enum SignListHotkeys {
    00144   SLHK_FOCUS_FILTER_BOX,
    00145 };
    00146
    00147 struct SignListWindow : QueryStringBaseWindow, SignList {
    00148   int text_offset;
    00149   Scrollbar *vscroll;
    00150
    00151   SignListWindow(const WindowDesc *desc, WindowNumber window_number) : QueryStringBaseWindow(MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_SIGN_NAME_CHARS)
    00152   {
    00153     this->CreateNestedTree(desc);
    00154     this->vscroll = this->GetScrollbar(WID_SIL_SCROLLBAR);
    00155     this->FinishInitNested(desc, window_number);
    00156     this->SetWidgetLoweredState(WID_SIL_FILTER_MATCH_CASE_BTN, SignList::match_case);
    00157
    00158     /* Initialize the text edit widget */
    00159     this->afilter = CS_ALPHANUMERAL;
    00160     this->text.Initialize(this->edit_str_buf, MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_SIGN_NAME_CHARS);
    00161     ClearFilterTextWidget();
    00162
    00163     /* Initialize the filtering variables */
    00164     this->SetFilterString("");
    00165
    00166     /* Create initial list. */
    00167     this->signs.ForceRebuild();
    00168     this->signs.ForceResort();
    00169     this->BuildSortSignList();
    00170   }
    00171
    00176   void ClearFilterTextWidget()
    00177   {
    00178     this->edit_str_buf[0] = '\0';
    00179     this->text.UpdateSize();
    00180
    00181     this->SetWidgetDirty(WID_SIL_FILTER_TEXT);
    00182   }
    00183
    00190   void SetFilterString(const char *new_filter_string)
    00191   {
    00192     /* check if there is a new filter string */
    00193     this->string_filter.SetFilterTerm(new_filter_string);
    00194     this->SetWidgetDisabledState(WID_SIL_FILTER_CLEAR_BTN, StrEmpty(new_filter_string));
    00195
    00196     /* Repaint the clear button since its disabled state may have changed */
    00197     this->SetWidgetDirty(WID_SIL_FILTER_CLEAR_BTN);
    00198
    00199     /* Rebuild the list of signs */
    00200     this->InvalidateData();
    00201   }
    00202
    00203   virtual void OnPaint()
    00204   {
    00205     if (this->signs.NeedRebuild()) this->BuildSortSignList();
    00206     this->DrawWidgets();
    00207     if (!this->IsShaded()) this->DrawEditBox(WID_SIL_FILTER_TEXT);
    00208   }
    00209
    00210   virtual void DrawWidget(const Rect &r, int widget) const
    00211   {
    00212     switch (widget) {
    00213       case WID_SIL_LIST: {
    00214         uint y = r.top + WD_FRAMERECT_TOP; // Offset from top of widget.
    00215         /* No signs? */
    00216         if (this->vscroll->GetCount() == 0) {
    00217           DrawString(r.left + WD_FRAMETEXT_LEFT, r.right, y, STR_STATION_LIST_NONE);
    00218           return;
    00219         }
    00220
    00221         bool rtl = _current_text_dir == TD_RTL;
    00222         int sprite_offset_y = (FONT_HEIGHT_NORMAL - 10) / 2 + 1;
    00223         uint icon_left  = 4 + (rtl ? r.right - this->text_offset : r.left);
    00224         uint text_left  = r.left + (rtl ? WD_FRAMERECT_LEFT : this->text_offset);
    00225         uint text_right = r.right - (rtl ? this->text_offset : WD_FRAMERECT_RIGHT);
    00226
    00227         /* At least one sign available. */
    00228         for (uint16 i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) {
    00229           const Sign *si = this->signs[i];
    00230
    00231           if (si->owner != OWNER_NONE) DrawCompanyIcon(si->owner, icon_left, y + sprite_offset_y);
    00232
    00233           SetDParam(0, si->index);
    00234           DrawString(text_left, text_right, y, STR_SIGN_NAME, TC_YELLOW);
    00235           y += this->resize.step_height;
    00236         }
    00237         break;
    00238       }
    00239     }
    00240   }
    00241
    00242   virtual void SetStringParameters(int widget) const
    00243   {
    00244     if (widget == WID_SIL_CAPTION) SetDParam(0, this->vscroll->GetCount());
    00245   }
    00246
    00247   virtual void OnClick(Point pt, int widget, int click_count)
    00248   {
    00249     switch (widget) {
    00250       case WID_SIL_LIST: {
    00251         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SIL_LIST, WD_FRAMERECT_TOP);
    00252         if (id_v == INT_MAX) return;
    00253
    00254         const Sign *si = this->signs[id_v];
    00255         ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
    00256         break;
    00257       }
    00258       case WID_SIL_FILTER_CLEAR_BTN:
    00259         this->ClearFilterTextWidget(); // Empty the text in the EditBox widget
    00260         this->SetFilterString("");     // Use empty text as filter text (= view all signs)
    00261         break;
    00262
    00263       case WID_SIL_FILTER_MATCH_CASE_BTN:
    00264         SignList::match_case = !SignList::match_case; // Toggle match case
    00265         this->SetWidgetLoweredState(WID_SIL_FILTER_MATCH_CASE_BTN, SignList::match_case); // Toggle button pushed state
    00266         this->InvalidateData(); // Rebuild the list of signs
    00267         break;
    00268     }
    00269   }
    00270
    00271   virtual void OnResize()
    00272   {
    00273     this->vscroll->SetCapacityFromWidget(this, WID_SIL_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
    00274   }
    00275
    00276   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00277   {
    00278     switch (widget) {
    00279       case WID_SIL_LIST: {
    00280         Dimension spr_dim = GetSpriteSize(SPR_COMPANY_ICON);
    00281         this->text_offset = WD_FRAMETEXT_LEFT + spr_dim.width + 2; // 2 pixels space between icon and the sign text.
    00282         resize->height = max<uint>(FONT_HEIGHT_NORMAL, spr_dim.height);
    00283         Dimension d = {this->text_offset + WD_FRAMETEXT_RIGHT, WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM};
    00284         *size = maxdim(*size, d);
    00285         break;
    00286       }
    00287
    00288       case WID_SIL_CAPTION:
    00289         SetDParam(0, max<size_t>(1000, Sign::GetPoolSize()));
    00290         *size = GetStringBoundingBox(STR_SIGN_LIST_CAPTION);
    00291         size->height += padding.height;
    00292         size->width  += padding.width;
    00293         break;
    00294     }
    00295   }
    00296
    00297   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00298   {
    00299     EventState state = ES_NOT_HANDLED;
    00300     switch (this->HandleEditBoxKey(WID_SIL_FILTER_TEXT, key, keycode, state)) {
    00301       case HEBR_EDITING:
    00302         this->SetFilterString(this->text.buf);
    00303         break;
    00304
    00305       case HEBR_CONFIRM: // Enter pressed -> goto first sign in list
    00306         if (this->signs.Length() >= 1) {
    00307           const Sign *si = this->signs[0];
    00308           ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
    00309         }
    00310         return state;
    00311
    00312       case HEBR_CANCEL: // ESC pressed, clear filter.
    00313         this->OnClick(Point(), WID_SIL_FILTER_CLEAR_BTN, 1); // Simulate click on clear button.
    00314         this->UnfocusFocusedWidget();                    // Unfocus the text box.
    00315         return state;
    00316
    00317       case HEBR_NOT_FOCUSED: // The filter text box is not globaly focused.
    00318         if (CheckHotkeyMatch(signlist_hotkeys, keycode, this) == SLHK_FOCUS_FILTER_BOX) {
    00319           this->SetFocusedWidget(WID_SIL_FILTER_TEXT);
    00320           SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
    00321           state = ES_HANDLED;
    00322         }
    00323         break;
    00324
    00325       default:
    00326         NOT_REACHED();
    00327     }
    00328
    00329     if (state == ES_HANDLED) OnOSKInput(WID_SIL_FILTER_TEXT);
    00330
    00331     return state;
    00332   }
    00333
    00334   virtual void OnOSKInput(int widget)
    00335   {
    00336     if (widget == WID_SIL_FILTER_TEXT) this->SetFilterString(this->text.buf);
    00337   }
    00338
    00339   virtual void OnMouseLoop()
    00340   {
    00341     this->HandleEditBox(WID_SIL_FILTER_TEXT);
    00342   }
    00343
    00344   void BuildSortSignList()
    00345   {
    00346     if (this->signs.NeedRebuild()) {
    00347       this->BuildSignsList();
    00348       this->vscroll->SetCount(this->signs.Length());
    00349       this->SetWidgetDirty(WID_SIL_CAPTION);
    00350     }
    00351     this->SortSignsList();
    00352   }
    00353
    00354   virtual void OnHundredthTick()
    00355   {
    00356     this->BuildSortSignList();
    00357     this->SetDirty();
    00358   }
    00359
    00365   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00366   {
    00367     /* When there is a filter string, we always need to rebuild the list even if
    00368      * the amount of signs in total is unchanged, as the subset of signs that is
    00369      * accepted by the filter might has changed. */
    00370     if (data == 0 || data == -1 || !this->string_filter.IsEmpty()) { // New or deleted sign, changed visibility setting or there is a filter string
    00371       /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
    00372       this->signs.ForceRebuild();
    00373     } else { // Change of sign contents while there is no filter string
    00374       this->signs.ForceResort();
    00375     }
    00376   }
    00377
    00378   static Hotkey<SignListWindow> signlist_hotkeys[];
    00379 };
    00380
    00381 Hotkey<SignListWindow> SignListWindow::signlist_hotkeys[] = {
    00382   Hotkey<SignListWindow>('F', "focus_filter_box", SLHK_FOCUS_FILTER_BOX),
    00383   HOTKEY_LIST_END(SignListWindow)
    00384 };
    00385 Hotkey<SignListWindow> *_signlist_hotkeys = SignListWindow::signlist_hotkeys;
    00386
    00387 static const NWidgetPart _nested_sign_list_widgets[] = {
    00388   NWidget(NWID_HORIZONTAL),
    00389     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00390     NWidget(WWT_CAPTION, COLOUR_GREY, WID_SIL_CAPTION), SetDataTip(STR_SIGN_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00391     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00392     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00393   EndContainer(),
    00394   NWidget(NWID_HORIZONTAL),
    00395     NWidget(NWID_VERTICAL),
    00396       NWidget(WWT_PANEL, COLOUR_GREY, WID_SIL_LIST), SetMinimalSize(WD_FRAMETEXT_LEFT + 16 + 255 + WD_FRAMETEXT_RIGHT, 50),
    00397                 SetResize(1, 10), SetFill(1, 0), SetScrollbar(WID_SIL_SCROLLBAR), EndContainer(),
    00398       NWidget(NWID_HORIZONTAL),
    00399         NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1),
    00400           NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SIL_FILTER_TEXT), SetMinimalSize(80, 12), SetResize(1, 0), SetFill(1, 0), SetPadding(2, 2, 2, 2),
    00401               SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
    00402         EndContainer(),
    00403         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SIL_FILTER_MATCH_CASE_BTN), SetDataTip(STR_SIGN_LIST_MATCH_CASE, STR_SIGN_LIST_MATCH_CASE_TOOLTIP),
    00404         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SIL_FILTER_CLEAR_BTN), SetDataTip(STR_SIGN_LIST_CLEAR, STR_SIGN_LIST_CLEAR_TOOLTIP),
    00405       EndContainer(),
    00406     EndContainer(),
    00407     NWidget(NWID_VERTICAL),
    00408       NWidget(NWID_VERTICAL), SetFill(0, 1),
    00409         NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SIL_SCROLLBAR),
    00410       EndContainer(),
    00411       NWidget(WWT_RESIZEBOX, COLOUR_GREY),
    00412     EndContainer(),
    00413   EndContainer(),
    00414 };
    00415
    00416 static const WindowDesc _sign_list_desc(
    00417   WDP_AUTO, 358, 138,
    00418   WC_SIGN_LIST, WC_NONE,
    00419   WDF_UNCLICK_BUTTONS,
    00420   _nested_sign_list_widgets, lengthof(_nested_sign_list_widgets)
    00421 );
    00422
    00428 Window *ShowSignList()
    00429 {
    00430   return AllocateWindowDescFront<SignListWindow>(&_sign_list_desc, 0);
    00431 }
    00432
    00433 EventState SignListGlobalHotkeys(uint16 key, uint16 keycode)
    00434 {
    00435   int num = CheckHotkeyMatch<SignListWindow>(_signlist_hotkeys, keycode, NULL, true);
    00436   if (num == -1) return ES_NOT_HANDLED;
    00437   Window *w = ShowSignList();
    00438   if (w == NULL) return ES_NOT_HANDLED;
    00439   return w->OnKeyPress(key, keycode);
    00440 }
    00441
    00448 static bool RenameSign(SignID index, const char *text)
    00449 {
    00450   bool remove = StrEmpty(text);
    00451   DoCommandP(0, index, 0, CMD_RENAME_SIGN | (StrEmpty(text) ? CMD_MSG(STR_ERROR_CAN_T_DELETE_SIGN) : CMD_MSG(STR_ERROR_CAN_T_CHANGE_SIGN_NAME)), NULL, text);
    00452   return remove;
    00453 }
    00454
    00455 struct SignWindow : QueryStringBaseWindow, SignList {
    00456   SignID cur_sign;
    00457
    00458   SignWindow(const WindowDesc *desc, const Sign *si) : QueryStringBaseWindow(MAX_LENGTH_SIGN_NAME_CHARS * MAX_CHAR_LENGTH, MAX_LENGTH_SIGN_NAME_CHARS)
    00459   {
    00460     this->caption = STR_EDIT_SIGN_CAPTION;
    00461     this->afilter = CS_ALPHANUMERAL;
    00462
    00463     this->InitNested(desc, WN_QUERY_STRING_SIGN);
    00464
    00465     this->LowerWidget(WID_QES_TEXT);
    00466     UpdateSignEditWindow(si);
    00467     this->SetFocusedWidget(WID_QES_TEXT);
    00468   }
    00469
    00470   void UpdateSignEditWindow(const Sign *si)
    00471   {
    00472     char *last_of = &this->edit_str_buf[this->edit_str_size - 1]; // points to terminating '\0'
    00473
    00474     /* Display an empty string when the sign hasnt been edited yet */
    00475     if (si->name != NULL) {
    00476       SetDParam(0, si->index);
    00477       GetString(this->edit_str_buf, STR_SIGN_NAME, last_of);
    00478     } else {
    00479       GetString(this->edit_str_buf, STR_EMPTY, last_of);
    00480     }
    00481     *last_of = '\0';
    00482
    00483     this->cur_sign = si->index;
    00484     this->text.Initialize(this->edit_str_buf, this->edit_str_size, this->max_chars);
    00485
    00486     this->SetWidgetDirty(WID_QES_TEXT);
    00487     this->SetFocusedWidget(WID_QES_TEXT);
    00488   }
    00489
    00495   const Sign *PrevNextSign(bool next)
    00496   {
    00497     /* Rebuild the sign list */
    00498     this->signs.ForceRebuild();
    00499     this->signs.NeedResort();
    00500     this->BuildSignsList();
    00501     this->SortSignsList();
    00502
    00503     /* Search through the list for the current sign, excluding
    00504      * - the first sign if we want the previous sign or
    00505      * - the last sign if we want the next sign */
    00506     uint end = this->signs.Length() - (next ? 1 : 0);
    00507     for (uint i = next ? 0 : 1; i < end; i++) {
    00508       if (this->cur_sign == this->signs[i]->index) {
    00509         /* We've found the current sign, so return the sign before/after it */
    00510         return this->signs[i + (next ? 1 : -1)];
    00511       }
    00512     }
    00513     /* If we haven't found the current sign by now, return the last/first sign */
    00514     return this->signs[next ? 0 : this->signs.Length() - 1];
    00515   }
    00516
    00517   virtual void SetStringParameters(int widget) const
    00518   {
    00519     switch (widget) {
    00520       case WID_QES_CAPTION:
    00521         SetDParam(0, this->caption);
    00522         break;
    00523     }
    00524   }
    00525
    00526   virtual void OnPaint()
    00527   {
    00528     this->DrawWidgets();
    00529     if (!this->IsShaded()) this->DrawEditBox(WID_QES_TEXT);
    00530   }
    00531
    00532   virtual void OnClick(Point pt, int widget, int click_count)
    00533   {
    00534     switch (widget) {
    00535       case WID_QES_PREVIOUS:
    00536       case WID_QES_NEXT: {
    00537         const Sign *si = this->PrevNextSign(widget == WID_QES_NEXT);
    00538
    00539         /* Rebuild the sign list */
    00540         this->signs.ForceRebuild();
    00541         this->signs.NeedResort();
    00542         this->BuildSignsList();
    00543         this->SortSignsList();
    00544
    00545         /* Scroll to sign and reopen window */
    00546         ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
    00547         UpdateSignEditWindow(si);
    00548         break;
    00549       }
    00550
    00551       case WID_QES_DELETE:
    00552         /* Only need to set the buffer to null, the rest is handled as the OK button */
    00553         RenameSign(this->cur_sign, "");
    00554         /* don't delete this, we are deleted in Sign::~Sign() -> DeleteRenameSignWindow() */
    00555         break;
    00556
    00557       case WID_QES_OK:
    00558         if (RenameSign(this->cur_sign, this->text.buf)) break;
    00559         /* FALL THROUGH */
    00560
    00561       case WID_QES_CANCEL:
    00562         delete this;
    00563         break;
    00564     }
    00565   }
    00566
    00567   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00568   {
    00569     EventState state = ES_NOT_HANDLED;
    00570     switch (this->HandleEditBoxKey(WID_QES_TEXT, key, keycode, state)) {
    00571       default: break;
    00572
    00573       case HEBR_CONFIRM:
    00574         if (RenameSign(this->cur_sign, this->text.buf)) break;
    00575         /* FALL THROUGH */
    00576
    00577       case HEBR_CANCEL: // close window, abandon changes
    00578         delete this;
    00579         break;
    00580     }
    00581     return state;
    00582   }
    00583
    00584   virtual void OnMouseLoop()
    00585   {
    00586     this->HandleEditBox(WID_QES_TEXT);
    00587   }
    00588
    00589   virtual void OnOpenOSKWindow(int wid)
    00590   {
    00591     ShowOnScreenKeyboard(this, wid, WID_QES_CANCEL, WID_QES_OK);
    00592   }
    00593 };
    00594
    00595 static const NWidgetPart _nested_query_sign_edit_widgets[] = {
    00596   NWidget(NWID_HORIZONTAL),
    00597     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00598     NWidget(WWT_CAPTION, COLOUR_GREY, WID_QES_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00599   EndContainer(),
    00600   NWidget(WWT_PANEL, COLOUR_GREY),
    00601     NWidget(WWT_EDITBOX, COLOUR_GREY, WID_QES_TEXT), SetMinimalSize(256, 12), SetDataTip(STR_EDIT_SIGN_SIGN_OSKTITLE, STR_NULL), SetPadding(2, 2, 2, 2),
    00602   EndContainer(),
    00603   NWidget(NWID_HORIZONTAL),
    00604     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_OK), SetMinimalSize(61, 12), SetDataTip(STR_BUTTON_OK, STR_NULL),
    00605     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_CANCEL), SetMinimalSize(60, 12), SetDataTip(STR_BUTTON_CANCEL, STR_NULL),
    00606     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_QES_DELETE), SetMinimalSize(60, 12), SetDataTip(STR_TOWN_VIEW_DELETE_BUTTON, STR_NULL),
    00607     NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(),
    00608     NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_QES_PREVIOUS), SetMinimalSize(11, 12), SetDataTip(AWV_DECREASE, STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP),
    00609     NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_QES_NEXT), SetMinimalSize(11, 12), SetDataTip(AWV_INCREASE, STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP),
    00610   EndContainer(),
    00611 };
    00612
    00613 static const WindowDesc _query_sign_edit_desc(
    00614   WDP_AUTO, 0, 0,
    00615   WC_QUERY_STRING, WC_NONE,
    00616   WDF_CONSTRUCTION | WDF_UNCLICK_BUTTONS,
    00617   _nested_query_sign_edit_widgets, lengthof(_nested_query_sign_edit_widgets)
    00618 );
    00619
    00624 void HandleClickOnSign(const Sign *si)
    00625 {
    00626   if (_ctrl_pressed && (si->owner == _local_company || (si->owner == OWNER_DEITY && _game_mode == GM_EDITOR))) {
    00627     RenameSign(si->index, NULL);
    00628     return;
    00629   }
    00630   ShowRenameSignWindow(si);
    00631 }
    00632
    00637 void ShowRenameSignWindow(const Sign *si)
    00638 {
    00639   /* Delete all other edit windows */
    00640   DeleteWindowByClass(WC_QUERY_STRING);
    00641
    00642   new SignWindow(&_query_sign_edit_desc, si);
    00643 }
    00644
    00649 void DeleteRenameSignWindow(SignID sign)
    00650 {
    00651   SignWindow *w = dynamic_cast<SignWindow *>(FindWindowById(WC_QUERY_STRING, WN_QUERY_STRING_SIGN));
    00652
    00653   if (w != NULL && w->cur_sign == sign) delete w;
    00654 }