Maintain a circular buffer of recent commands, add to crashlog.
[openttd-joker.git] / src / plans_gui.cpp
blobd3d3f1152a93fda133fd99bbc245b56c1e1d587b
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
10 /** @file plans_gui.cpp The GUI for planning. */
12 #include "stdafx.h"
13 #include "plans_func.h"
14 #include "plans_base.h"
15 #include "command_func.h"
16 #include "company_func.h"
17 #include "company_gui.h"
18 #include "settings_gui.h"
19 #include "window_gui.h"
20 #include "window_func.h"
21 #include "viewport_func.h"
22 #include "gfx_func.h"
23 #include "tilehighlight_func.h"
24 #include "strings_func.h"
25 #include "core/pool_func.hpp"
26 #include "widgets/plans_widget.h"
27 #include "table/strings.h"
28 #include "table/sprites.h"
30 static const NWidgetPart _nested_plans_widgets[] = {
31 NWidget(NWID_HORIZONTAL),
32 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
33 NWidget(WWT_CAPTION, COLOUR_GREY, WID_PLN_CAPTION), SetDataTip(STR_PLANS_CAPTION, STR_NULL),
34 NWidget(WWT_SHADEBOX, COLOUR_GREY),
35 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
36 NWidget(WWT_STICKYBOX, COLOUR_GREY),
37 EndContainer(),
39 NWidget(NWID_HORIZONTAL),
40 NWidget(WWT_PANEL, COLOUR_GREY),
41 NWidget(NWID_HORIZONTAL),
42 NWidget(WWT_INSET, COLOUR_GREY, WID_PLN_LIST), SetFill(1, 1), SetPadding(2, 1, 2, 2), SetResize(1, 0), SetScrollbar(WID_PLN_SCROLLBAR), SetDataTip(STR_NULL, STR_PLANS_LIST_TOOLTIP),
43 EndContainer(),
44 EndContainer(),
45 EndContainer(),
46 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_PLN_SCROLLBAR),
47 EndContainer(),
49 NWidget(WWT_PANEL, COLOUR_GREY),
50 NWidget(NWID_HORIZONTAL),
51 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_NEW), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_NEW_PLAN, STR_NULL),
52 NWidget(WWT_TEXTBTN_2, COLOUR_GREY, WID_PLN_ADD_LINES), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_ADD_LINES, STR_PLANS_ADDING_LINES),
53 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_PLN_VISIBILITY), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_VISIBILITY_PUBLIC, STR_PLANS_VISIBILITY_TOOLTIP),
54 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_HIDE_ALL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_HIDE_ALL, STR_NULL),
55 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_DELETE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_DELETE, STR_PLANS_DELETE_TOOLTIP),
56 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
57 EndContainer(),
58 EndContainer(),
61 static WindowDesc _plans_desc(
62 WDP_AUTO, "plans", 300, 100,
63 WC_PLANS, WC_NONE,
64 WDF_CONSTRUCTION,
65 _nested_plans_widgets, lengthof(_nested_plans_widgets)
68 struct PlansWindow : Window {
69 typedef struct {
70 bool is_plan;
71 int plan_id;
72 int line_id;
73 } ListItem;
75 Scrollbar *vscroll;
76 int text_offset;
77 std::vector<ListItem> list; ///< The translation table linking panel indices to their related PlanID.
78 int selected; ///< What item is currently selected in the panel.
80 PlansWindow(WindowDesc *desc) : Window(desc)
82 this->CreateNestedTree();
83 this->vscroll = this->GetScrollbar(WID_PLN_SCROLLBAR);
84 this->FinishInitNested();
86 Dimension spr_dim = GetSpriteSize(SPR_COMPANY_ICON);
87 this->text_offset = WD_FRAMETEXT_LEFT + spr_dim.width + 2 + SETTING_BUTTON_WIDTH;
89 this->selected = INT_MAX;
90 RebuildList();
93 ~PlansWindow()
95 this->list.clear();
96 _current_plan = NULL;
99 virtual void OnClick(Point pt, int widget, int click_count)
101 switch (widget) {
102 case WID_PLN_NEW:
103 DoCommandP(0, _local_company, 0, CMD_ADD_PLAN, CcAddPlan);
104 break;
105 case WID_PLN_ADD_LINES:
106 if (_current_plan) HandlePlacePushButton(this, widget, SPR_CURSOR_MOUSE, HT_POINT);
107 break;
108 case WID_PLN_DELETE:
109 if (this->selected != INT_MAX) {
110 if (this->list[this->selected].is_plan) {
111 DoCommandP(0, this->list[this->selected].plan_id, 0, CMD_REMOVE_PLAN);
113 else {
114 DoCommandP(0, this->list[this->selected].plan_id, this->list[this->selected].line_id, CMD_REMOVE_PLAN_LINE);
117 break;
118 case WID_PLN_HIDE_ALL: {
119 Plan *p;
120 FOR_ALL_PLANS(p) {
121 if (p->IsListable()) p->SetVisibility(false);
123 this->SetWidgetDirty(WID_PLN_LIST);
124 break;
126 case WID_PLN_VISIBILITY:
127 if (_current_plan) _current_plan->ToggleVisibilityByAll();
128 break;
129 case WID_PLN_LIST: {
130 int new_selected = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_PLN_LIST, WD_FRAMERECT_TOP);
131 if (this->selected != INT_MAX) {
132 _current_plan->SetFocus(false);
134 if (new_selected != INT_MAX) {
135 if (this->list[new_selected].is_plan) {
136 _current_plan = Plan::Get(this->list[new_selected].plan_id);
137 _current_plan->SetFocus(true);
138 if (pt.x >= 22 && pt.x < 41) _current_plan->ToggleVisibility();
140 else {
141 _current_plan = Plan::Get(this->list[new_selected].plan_id);
142 PlanLine *pl = _current_plan->lines[this->list[new_selected].line_id];
143 pl->SetFocus(true);
144 if (pt.x >= 22 && pt.x < 41) {
145 if (pl->ToggleVisibility()) _current_plan->SetVisibility(true, false);
148 if (click_count > 1 && (pt.x < 22 || pt.x >= 41)) {
149 _current_plan->show_lines = !_current_plan->show_lines;
150 this->InvalidateData(INVALID_PLAN);
153 else {
154 if (_current_plan) {
155 _current_plan->SetFocus(false);
156 _current_plan = NULL;
159 this->selected = new_selected;
160 this->SetDirty();
161 break;
163 default: break;
167 virtual void OnPaint()
169 this->SetWidgetDisabledState(WID_PLN_HIDE_ALL, this->vscroll->GetCount() == 0);
170 if (_current_plan) {
171 this->SetWidgetsDisabledState(_current_plan->owner != _local_company, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WIDGET_LIST_END);
172 this->GetWidget<NWidgetCore>(WID_PLN_VISIBILITY)->widget_data = _current_plan->visible_by_all ? STR_PLANS_VISIBILITY_PRIVATE : STR_PLANS_VISIBILITY_PUBLIC;
174 else {
175 this->SetWidgetsDisabledState(true, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WIDGET_LIST_END);
177 this->DrawWidgets();
180 virtual void DrawWidget(const Rect &r, int widget) const
182 switch (widget) {
183 case WID_PLN_LIST: {
184 uint y = r.top + WD_FRAMERECT_TOP; // Offset from top of widget.
185 if (this->vscroll->GetCount() == 0) {
186 DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, y, STR_STATION_LIST_NONE);
187 return;
190 bool rtl = _current_text_dir == TD_RTL;
191 uint icon_left = 4 + (rtl ? r.right - this->text_offset : r.left);
192 uint btn_left = (rtl ? icon_left - SETTING_BUTTON_WIDTH + 4 : icon_left + SETTING_BUTTON_WIDTH - 4);
193 uint text_left = r.left + (rtl ? WD_FRAMERECT_LEFT : this->text_offset);
194 uint text_right = r.right - (rtl ? this->text_offset : WD_FRAMERECT_RIGHT);
196 for (uint16 i = this->vscroll->GetPosition(); this->vscroll->IsVisible(i) && i < this->vscroll->GetCount(); i++) {
197 Plan *p = Plan::Get(list[i].plan_id);
199 if (i == this->selected) GfxFillRect(r.left + 1, y, r.right, y + this->resize.step_height, PC_DARK_GREY);
201 if (list[i].is_plan) {
202 DrawCompanyIcon(p->owner, icon_left, y + (FONT_HEIGHT_NORMAL - 10) / 2 + 1);
203 DrawBoolButton(btn_left, y + (FONT_HEIGHT_NORMAL - 10) / 2, p->visible, true);
204 SetDParam(0, list[i].plan_id + 1);
205 SetDParam(1, p->lines.size());
206 SetDParam(2, p->creation_date);
207 DrawString(text_left, text_right, y, STR_PLANS_LIST_ITEM_PLAN, p->visible_by_all ? TC_LIGHT_BLUE : TC_YELLOW);
209 else {
210 PlanLine *pl = p->lines[list[i].line_id];
211 DrawBoolButton(btn_left, y + (FONT_HEIGHT_NORMAL - 10) / 2, pl->visible, true);
212 SetDParam(0, list[i].line_id + 1);
213 SetDParam(1, pl->tiles.size() - 1);
214 DrawString(text_left, text_right, y, STR_PLANS_LIST_ITEM_LINE, TC_WHITE);
216 y += this->resize.step_height;
218 break;
223 virtual void OnResize()
225 this->vscroll->SetCapacityFromWidget(this, WID_PLN_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
228 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
230 switch (widget) {
231 case WID_PLN_LIST:
232 resize->height = FONT_HEIGHT_NORMAL;
233 size->height = resize->height * 5 + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
234 break;
238 /** The drawing of a line starts. */
239 virtual void OnPlaceObject(Point pt, TileIndex tile)
241 /* A player can't add lines to a public plan of another company. */
242 if (_current_plan && _current_plan->owner == _local_company) VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_DRAW_PLANLINE);
245 /** The drawing of a line is in progress. */
246 virtual void OnPlaceDrag(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt)
248 const Point p = GetTileBelowCursor();
249 const TileIndex tile = TileVirtXY(p.x, p.y);
250 if (_current_plan && tile < MapSize()) {
251 _current_plan->StoreTempTile(tile);
252 _thd.selstart = _thd.selend;
256 /** The drawing of a line ends up normally. */
257 virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, ViewportDragDropSelectionProcess select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
259 if (_current_plan) _current_plan->ValidateNewLine();
262 /** The drawing of a line is aborted. */
263 virtual void OnPlaceObjectAbort()
265 if (_current_plan) {
266 _current_plan->temp_line->MarkDirty();
267 _current_plan->temp_line->Clear();
270 this->RaiseWidget(WID_PLN_ADD_LINES);
271 this->SetWidgetDirty(WID_PLN_ADD_LINES);
274 void RebuildList()
276 int old_focused_plan_id = this->selected == INT_MAX ? INT_MAX : this->list[this->selected].plan_id;
278 int sbcnt = 0;
279 this->list.clear();
280 Plan *p;
281 FOR_ALL_PLANS(p) {
282 if (!p->IsListable()) continue;
284 ListItem li;
285 li.is_plan = true;
286 li.plan_id = p->index;
287 this->list.push_back(li);
288 if (old_focused_plan_id == p->index) this->selected = sbcnt;
289 sbcnt++;
291 if (p->show_lines) {
292 const int sz = (int)p->lines.size();
293 sbcnt += sz;
294 li.is_plan = false;
295 for (int i = 0; i < sz; i++) {
296 li.line_id = i;
297 this->list.push_back(li);
302 if (this->selected == INT_MAX) ResetObjectToPlace();
304 this->vscroll->SetCount(sbcnt);
307 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
309 if (data != INVALID_PLAN && this->selected != INT_MAX) {
310 if (this->list[this->selected].plan_id == data) {
311 /* Invalidate the selection if the selected plan has been modified or deleted. */
312 this->selected = INT_MAX;
314 /* Cancel drawing associated to the deleted plan. */
315 ResetObjectToPlace();
319 RebuildList();
322 void SelectPlan(PlanID plan_index)
324 if (this->selected != INT_MAX) {
325 if (plan_index == this->list[this->selected].plan_id) return;
326 Plan::Get(this->list[this->selected].plan_id)->SetFocus(false);
329 if (plan_index == INVALID_PLAN) {
330 this->selected = INT_MAX;
331 return;
333 Plan::Get(plan_index)->SetFocus(true);
335 for (size_t i = 0; i < this->list.size(); i++) {
336 if (this->list[i].is_plan && this->list[i].plan_id == plan_index) {
337 this->selected = (int)i;
338 return;
344 /** Show the window to manage plans. */
345 void ShowPlansWindow()
347 if (BringWindowToFrontById(WC_PLANS, 0) != NULL) return;
348 new PlansWindow(&_plans_desc);
352 * Only the creator of a plan executes this function.
353 * The other players should not be bothered with these changes.
355 void CcAddPlan(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
357 if (result.Failed()) return;
359 _current_plan = _new_plan;
360 _current_plan->SetVisibility(true);
362 Window *w = FindWindowById(WC_PLANS, 0);
363 if (w) {
364 w->InvalidateData(INVALID_PLAN, false);
365 ((PlansWindow *)w)->SelectPlan(_current_plan->index);
366 if (!w->IsWidgetLowered(WID_PLN_ADD_LINES)) {
367 w->SetWidgetDisabledState(WID_PLN_ADD_LINES, false);
368 HandlePlacePushButton(w, WID_PLN_ADD_LINES, SPR_CURSOR_MOUSE, HT_POINT);