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/>.
10 /** @file plans_gui.cpp The GUI for planning. */
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"
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
),
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
),
46 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_PLN_SCROLLBAR
),
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
),
61 static WindowDesc
_plans_desc(
62 WDP_AUTO
, "plans", 300, 100,
65 _nested_plans_widgets
, lengthof(_nested_plans_widgets
)
68 struct PlansWindow
: Window
{
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
;
99 virtual void OnClick(Point pt
, int widget
, int click_count
)
103 DoCommandP(0, _local_company
, 0, CMD_ADD_PLAN
, CcAddPlan
);
105 case WID_PLN_ADD_LINES
:
106 if (_current_plan
) HandlePlacePushButton(this, widget
, SPR_CURSOR_MOUSE
, HT_POINT
);
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
);
114 DoCommandP(0, this->list
[this->selected
].plan_id
, this->list
[this->selected
].line_id
, CMD_REMOVE_PLAN_LINE
);
118 case WID_PLN_HIDE_ALL
: {
121 if (p
->IsListable()) p
->SetVisibility(false);
123 this->SetWidgetDirty(WID_PLN_LIST
);
126 case WID_PLN_VISIBILITY
:
127 if (_current_plan
) _current_plan
->ToggleVisibilityByAll();
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();
141 _current_plan
= Plan::Get(this->list
[new_selected
].plan_id
);
142 PlanLine
*pl
= _current_plan
->lines
[this->list
[new_selected
].line_id
];
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
);
155 _current_plan
->SetFocus(false);
156 _current_plan
= NULL
;
159 this->selected
= new_selected
;
167 virtual void OnPaint()
169 this->SetWidgetDisabledState(WID_PLN_HIDE_ALL
, this->vscroll
->GetCount() == 0);
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
;
175 this->SetWidgetsDisabledState(true, WID_PLN_ADD_LINES
, WID_PLN_VISIBILITY
, WID_PLN_DELETE
, WIDGET_LIST_END
);
180 virtual void DrawWidget(const Rect
&r
, int widget
) const
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
);
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
);
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
;
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
)
232 resize
->height
= FONT_HEIGHT_NORMAL
;
233 size
->height
= resize
->height
* 5 + WD_FRAMERECT_TOP
+ WD_FRAMERECT_BOTTOM
;
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()
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
);
276 int old_focused_plan_id
= this->selected
== INT_MAX
? INT_MAX
: this->list
[this->selected
].plan_id
;
282 if (!p
->IsListable()) continue;
286 li
.plan_id
= p
->index
;
287 this->list
.push_back(li
);
288 if (old_focused_plan_id
== p
->index
) this->selected
= sbcnt
;
292 const int sz
= (int)p
->lines
.size();
295 for (int i
= 0; i
< sz
; 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();
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
;
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
;
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);
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
);