# signs_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: signs_gui.cpp 15723 2009-03-15 15:12:06Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "company_gui.h"
    00007 #include "company_func.h"
    00008 #include "signs_base.h"
    00009 #include "signs_func.h"
    00010 #include "debug.h"
    00011 #include "command_func.h"
    00012 #include "strings_func.h"
    00013 #include "window_func.h"
    00014 #include "map_func.h"
    00015 #include "gfx_func.h"
    00016 #include "viewport_func.h"
    00017 #include "querystring_gui.h"
    00018 #include "sortlist_type.h"
    00019 #include "string_func.h"
    00020
    00021 #include "table/strings.h"
    00022
    00023 struct SignList {
    00024   typedef GUIList<const Sign *> GUISignList;
    00025
    00026   static const Sign *last_sign;
    00027   GUISignList signs;
    00028
    00029   void BuildSignsList()
    00030   {
    00031     if (!this->signs.NeedRebuild()) return;
    00032
    00033     DEBUG(misc, 3, "Building sign list");
    00034
    00035     this->signs.Clear();
    00036
    00037     const Sign *si;
    00038     FOR_ALL_SIGNS(si) *this->signs.Append() = si;
    00039
    00040     this->signs.Compact();
    00041     this->signs.RebuildDone();
    00042   }
    00043
    00045   static int CDECL SignNameSorter(const Sign * const *a, const Sign * const *b)
    00046   {
    00047     static char buf_cache[64];
    00048     char buf[64];
    00049
    00050     SetDParam(0, (*a)->index);
    00051     GetString(buf, STR_SIGN_NAME, lastof(buf));
    00052
    00053     if (*b != last_sign) {
    00054       last_sign = *b;
    00055       SetDParam(0, (*b)->index);
    00056       GetString(buf_cache, STR_SIGN_NAME, lastof(buf_cache));
    00057     }
    00058
    00059     return strcasecmp(buf, buf_cache);
    00060   }
    00061
    00062   void SortSignsList()
    00063   {
    00064     if (!this->signs.Sort(&SignNameSorter)) return;
    00065
    00066     /* Reset the name sorter sort cache */
    00067     this->last_sign = NULL;
    00068   }
    00069 };
    00070
    00071 const Sign *SignList::last_sign = NULL;
    00072
    00073 struct SignListWindow : Window, SignList {
    00074   SignListWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
    00075   {
    00076     this->vscroll.cap = 12;
    00077     this->resize.step_height = 10;
    00078     this->resize.height = this->height - 10 * 7; // minimum if 5 in the list
    00079
    00080     this->signs.ForceRebuild();
    00081     this->signs.NeedResort();
    00082
    00083     this->FindWindowPlacementAndResize(desc);
    00084   }
    00085
    00086   virtual void OnPaint()
    00087   {
    00088     BuildSignsList();
    00089     SortSignsList();
    00090
    00091     SetVScrollCount(this, this->signs.Length());
    00092
    00093     SetDParam(0, this->vscroll.count);
    00094     this->DrawWidgets();
    00095
    00096     /* No signs? */
    00097     int y = 16; // offset from top of widget
    00098     if (this->vscroll.count == 0) {
    00099       DrawString(2, y, STR_304A_NONE, TC_FROMSTRING);
    00100       return;
    00101     }
    00102
    00103     /* Start drawing the signs */
    00104     for (uint16 i = this->vscroll.pos; i < this->vscroll.cap + this->vscroll.pos && i < this->vscroll.count; i++) {
    00105       const Sign *si = this->signs[i];
    00106
    00107       if (si->owner != OWNER_NONE) DrawCompanyIcon(si->owner, 4, y + 1);
    00108
    00109       SetDParam(0, si->index);
    00110       DrawString(22, y, STR_SIGN_NAME, TC_YELLOW);
    00111       y += 10;
    00112     }
    00113   }
    00114
    00115   virtual void OnClick(Point pt, int widget)
    00116   {
    00117     if (widget == 3) {
    00118       uint32 id_v = (pt.y - 15) / 10;
    00119
    00120       if (id_v >= this->vscroll.cap) return;
    00121       id_v += this->vscroll.pos;
    00122       if (id_v >= this->vscroll.count) return;
    00123
    00124       const Sign *si = this->signs[id_v];
    00125       ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
    00126     }
    00127   }
    00128
    00129   virtual void OnResize(Point new_size, Point delta)
    00130   {
    00131     this->vscroll.cap += delta.y / 10;
    00132   }
    00133
    00134   virtual void OnInvalidateData(int data)
    00135   {
    00136     if (data == 0) {
    00137       this->signs.ForceRebuild();
    00138     } else {
    00139       this->signs.ForceResort();
    00140     }
    00141   }
    00142 };
    00143
    00144 static const Widget _sign_list_widget[] = {
    00145 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
    00146 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   345,     0,    13, STR_SIGN_LIST_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},
    00147 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   346,   357,     0,    13, 0x0,                   STR_STICKY_BUTTON},
    00148 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   345,    14,   137, 0x0,                   STR_NULL},
    00149 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   346,   357,    14,   125, 0x0,                   STR_0190_SCROLL_BAR_SCROLLS_LIST},
    00150 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   346,   357,   126,   137, 0x0,                   STR_RESIZE_BUTTON},
    00151 {   WIDGETS_END},
    00152 };
    00153
    00154 static const WindowDesc _sign_list_desc(
    00155   WDP_AUTO, WDP_AUTO, 358, 138, 358, 138,
    00156   WC_SIGN_LIST, WC_NONE,
    00157   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
    00158   _sign_list_widget
    00159 );
    00160
    00161
    00162 void ShowSignList()
    00163 {
    00164   AllocateWindowDescFront<SignListWindow>(&_sign_list_desc, 0);
    00165 }
    00166
    00173 static bool RenameSign(SignID index, const char *text)
    00174 {
    00175   bool remove = StrEmpty(text);
    00176   DoCommandP(0, index, 0, CMD_RENAME_SIGN | (StrEmpty(text) ? CMD_MSG(STR_CAN_T_DELETE_SIGN) : CMD_MSG(STR_280C_CAN_T_CHANGE_SIGN_NAME)), NULL, text);
    00177   return remove;
    00178 }
    00179
    00180 enum QueryEditSignWidgets {
    00181   QUERY_EDIT_SIGN_WIDGET_TEXT = 3,
    00182   QUERY_EDIT_SIGN_WIDGET_OK,
    00183   QUERY_EDIT_SIGN_WIDGET_CANCEL,
    00184   QUERY_EDIT_SIGN_WIDGET_DELETE,
    00185   QUERY_EDIT_SIGN_WIDGET_PREVIOUS = QUERY_EDIT_SIGN_WIDGET_DELETE + 2,
    00186   QUERY_EDIT_SIGN_WIDGET_NEXT,
    00187 };
    00188
    00189 struct SignWindow : QueryStringBaseWindow, SignList {
    00190   SignID cur_sign;
    00191
    00192   SignWindow(const WindowDesc *desc, const Sign *si) : QueryStringBaseWindow(MAX_LENGTH_SIGN_NAME_BYTES, desc)
    00193   {
    00194     this->caption = STR_280B_EDIT_SIGN_TEXT;
    00195     this->afilter = CS_ALPHANUMERAL;
    00196     this->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
    00197
    00198     UpdateSignEditWindow(si);
    00199     this->SetFocusedWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
    00200     this->FindWindowPlacementAndResize(desc);
    00201   }
    00202
    00203   void UpdateSignEditWindow(const Sign *si)
    00204   {
    00205     char *last_of = &this->edit_str_buf[this->edit_str_size - 1]; // points to terminating '\0'
    00206
    00207     /* Display an empty string when the sign hasnt been edited yet */
    00208     if (si->name != NULL) {
    00209       SetDParam(0, si->index);
    00210       GetString(this->edit_str_buf, STR_SIGN_NAME, last_of);
    00211     } else {
    00212       GetString(this->edit_str_buf, STR_EMPTY, last_of);
    00213     }
    00214     *last_of = '\0';
    00215
    00216     this->cur_sign = si->index;
    00217     InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size, MAX_LENGTH_SIGN_NAME_PIXELS);
    00218
    00219     this->InvalidateWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
    00220     this->SetFocusedWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
    00221   }
    00222
    00228   const Sign *PrevNextSign(bool next)
    00229   {
    00230     /* Rebuild the sign list */
    00231     this->signs.ForceRebuild();
    00232     this->signs.NeedResort();
    00233     this->BuildSignsList();
    00234     this->SortSignsList();
    00235
    00236     /* Search through the list for the current sign, excluding
    00237      * - the first sign if we want the previous sign or
    00238      * - the last sign if we want the next sign */
    00239     uint end = this->signs.Length() - (next ? 1 : 0);
    00240     for (uint i = next ? 0 : 1; i < end; i++) {
    00241       if (this->cur_sign == this->signs[i]->index) {
    00242         /* We've found the current sign, so return the sign before/after it */
    00243         return this->signs[i + (next ? 1 : -1)];
    00244       }
    00245     }
    00246     /* If we haven't found the current sign by now, return the last/first sign */
    00247     return this->signs[next ? 0 : this->signs.Length() - 1];
    00248   }
    00249
    00250   virtual void OnPaint()
    00251   {
    00252     SetDParam(0, this->caption);
    00253     this->DrawWidgets();
    00254     this->DrawEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT);
    00255   }
    00256
    00257   virtual void OnClick(Point pt, int widget)
    00258   {
    00259     switch (widget) {
    00260       case QUERY_EDIT_SIGN_WIDGET_PREVIOUS:
    00261       case QUERY_EDIT_SIGN_WIDGET_NEXT: {
    00262         const Sign *si = this->PrevNextSign(widget == QUERY_EDIT_SIGN_WIDGET_NEXT);
    00263
    00264         /* Rebuild the sign list */
    00265         this->signs.ForceRebuild();
    00266         this->signs.NeedResort();
    00267         this->BuildSignsList();
    00268         this->SortSignsList();
    00269
    00270         /* Scroll to sign and reopen window */
    00271         ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
    00272         UpdateSignEditWindow(si);
    00273         break;
    00274       }
    00275
    00276       case QUERY_EDIT_SIGN_WIDGET_DELETE:
    00277         /* Only need to set the buffer to null, the rest is handled as the OK button */
    00278         RenameSign(this->cur_sign, "");
    00279         /* don't delete this, we are deleted in Sign::~Sign() -> DeleteRenameSignWindow() */
    00280         break;
    00281
    00282       case QUERY_EDIT_SIGN_WIDGET_OK:
    00283         if (RenameSign(this->cur_sign, this->text.buf)) break;
    00284         /* FALL THROUGH */
    00285
    00286       case QUERY_EDIT_SIGN_WIDGET_CANCEL:
    00287         delete this;
    00288         break;
    00289     }
    00290   }
    00291
    00292   virtual EventState OnKeyPress(uint16 key, uint16 keycode)
    00293   {
    00294     EventState state = ES_NOT_HANDLED;
    00295     switch (this->HandleEditBoxKey(QUERY_EDIT_SIGN_WIDGET_TEXT, key, keycode, state)) {
    00296       default: break;
    00297
    00298       case HEBR_CONFIRM:
    00299         if (RenameSign(this->cur_sign, this->text.buf)) break;
    00300         /* FALL THROUGH */
    00301
    00302       case HEBR_CANCEL: // close window, abandon changes
    00303         delete this;
    00304         break;
    00305     }
    00306     return state;
    00307   }
    00308
    00309   virtual void OnMouseLoop()
    00310   {
    00311     this->HandleEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT);
    00312   }
    00313
    00314   virtual void OnOpenOSKWindow(int wid)
    00315   {
    00316     ShowOnScreenKeyboard(this, wid, QUERY_EDIT_SIGN_WIDGET_CANCEL, QUERY_EDIT_SIGN_WIDGET_OK);
    00317   }
    00318 };
    00319
    00320 static const Widget _query_sign_edit_widgets[] = {
    00321 { WWT_CLOSEBOX, RESIZE_NONE,  COLOUR_GREY,   0,  10,   0,  13, STR_00C5,          STR_018B_CLOSE_WINDOW},
    00322 {  WWT_CAPTION, RESIZE_NONE,  COLOUR_GREY,  11, 259,   0,  13, STR_012D,          STR_NULL },
    00323 {    WWT_PANEL, RESIZE_NONE,  COLOUR_GREY,   0, 259,  14,  29, STR_NULL,          STR_NULL },
    00324 {  WWT_EDITBOX, RESIZE_NONE,  COLOUR_GREY,   2, 257,  16,  27, STR_SIGN_OSKTITLE, STR_NULL },  // Text field
    00325 {  WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY,   0,  60,  30,  41, STR_012F_OK,       STR_NULL },
    00326 {  WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY,  61, 120,  30,  41, STR_012E_CANCEL,   STR_NULL },
    00327 {  WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY, 121, 180,  30,  41, STR_0290_DELETE,   STR_NULL },
    00328 {    WWT_PANEL, RESIZE_NONE,  COLOUR_GREY, 181, 237,  30,  41, STR_NULL,          STR_NULL },
    00329 {  WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY, 238, 248,  30,  41, STR_6819,          STR_PREVIOUS_SIGN_TOOLTIP },
    00330 {  WWT_TEXTBTN, RESIZE_NONE,  COLOUR_GREY, 249, 259,  30,  41, STR_681A,          STR_NEXT_SIGN_TOOLTIP },
    00331 { WIDGETS_END },
    00332 };
    00333
    00334 static const WindowDesc _query_sign_edit_desc(
    00335   190, 170, 260, 42, 260, 42,
    00336   WC_QUERY_STRING, WC_NONE,
    00337   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_CONSTRUCTION,
    00338   _query_sign_edit_widgets
    00339 );
    00340
    00341 void HandleClickOnSign(const Sign *si)
    00342 {
    00343   if (_ctrl_pressed && si->owner == _local_company) {
    00344     RenameSign(si->index, NULL);
    00345     return;
    00346   }
    00347   ShowRenameSignWindow(si);
    00348 }
    00349
    00350 void ShowRenameSignWindow(const Sign *si)
    00351 {
    00352   /* Delete all other edit windows */
    00353   DeleteWindowById(WC_QUERY_STRING, 0);
    00354
    00355   new SignWindow(&_query_sign_edit_desc, si);
    00356 }
    00357
    00358 void DeleteRenameSignWindow(SignID sign)
    00359 {
    00360   SignWindow *w = dynamic_cast<SignWindow *>(FindWindowById(WC_QUERY_STRING, 0));
    00361
    00362   if (w != NULL && w->cur_sign == sign) delete w;
    00363 }