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 }

Generated on Fri Jul 31 22:33:18 2009 for OpenTTD by  doxygen 1.5.6