1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
22 #include "i18n/L10n.h"
23 #include "ps/CLogger.h"
24 #include "soundmanager/ISoundManager.h"
26 COList::COList() : CList()
28 AddSetting(GUIST_CGUISpriteInstance
, "sprite_heading");
29 AddSetting(GUIST_float
, "heading_height");
30 AddSetting(GUIST_bool
, "sortable"); // The actual sorting is done in JS for more versatility
31 AddSetting(GUIST_CStr
, "selected_column");
32 AddSetting(GUIST_int
, "selected_column_order");
33 AddSetting(GUIST_CGUISpriteInstance
, "sprite_asc"); // Show the order of sorting
34 AddSetting(GUIST_CGUISpriteInstance
, "sprite_desc");
35 AddSetting(GUIST_CGUISpriteInstance
, "sprite_not_sorted");
38 void COList::SetupText()
44 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
46 m_ItemsYPositions
.resize(pList
->m_Items
.size() + 1);
48 // Delete all generated texts. Some could probably be saved,
49 // but this is easier, and this function will never be called
50 // continuously, or even often, so it'll probably be okay.
51 for (SGUIText
* const& t
: m_GeneratedTexts
)
53 m_GeneratedTexts
.clear();
56 if (GUI
<CStrW
>::GetSetting(this, "font", font
) != PSRETURN_OK
|| font
.empty())
57 // Use the default if none is specified
58 // TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
62 GUI
<bool>::GetSetting(this, "scrollbar", scrollbar
);
64 float width
= GetListRect().GetWidth();
65 // remove scrollbar if applicable
66 if (scrollbar
&& GetScrollBar(0).GetStyle())
67 width
-= GetScrollBar(0).GetStyle()->m_Width
;
69 m_TotalAvailableColumnWidth
= width
;
71 float buffer_zone
= 0.f
;
72 GUI
<float>::GetSetting(this, "buffer_zone", buffer_zone
);
74 for (COListColumn column
: m_Columns
)
76 SGUIText
* text
= new SGUIText();
77 CGUIString gui_string
;
78 gui_string
.SetValue(column
.m_Heading
);
79 *text
= GetGUI()->GenerateText(gui_string
, font
, width
, buffer_zone
, this);
84 float buffered_y
= 0.f
;
86 for (size_t i
= 0; i
< pList
->m_Items
.size(); ++i
)
88 m_ItemsYPositions
[i
] = buffered_y
;
90 for (size_t c
= 0; c
< m_Columns
.size(); ++c
)
93 GUI
<CGUIList
>::GetSettingPointer(this, "list_" + m_Columns
[c
].m_Id
, pList_c
);
94 SGUIText
* text
= new SGUIText();
95 if (!pList_c
->m_Items
[i
].GetOriginalString().empty())
96 *text
= GetGUI()->GenerateText(pList_c
->m_Items
[i
], font
, width
, buffer_zone
, this);
99 // Minimum height of a space character of the current font size
100 CGUIString align_string
;
101 align_string
.SetValue(L
" ");
102 *text
= GetGUI()->GenerateText(align_string
, font
, width
, buffer_zone
, this);
104 shift
= std::max(shift
, text
->m_Size
.cy
);
110 m_ItemsYPositions
[pList
->m_Items
.size()] = buffered_y
;
114 CRect rect
= GetListRect();
115 GetScrollBar(0).SetScrollRange(m_ItemsYPositions
.back());
116 GetScrollBar(0).SetScrollSpace(rect
.GetHeight());
118 GetScrollBar(0).SetX(rect
.right
);
119 GetScrollBar(0).SetY(rect
.top
);
120 GetScrollBar(0).SetZ(GetBufferedZ());
121 GetScrollBar(0).SetLength(rect
.bottom
- rect
.top
);
125 CRect
COList::GetListRect() const
128 GUI
<float>::GetSetting(this, "heading_height", headingHeight
);
129 return m_CachedActualSize
+ CRect(0, headingHeight
, 0, 0);
132 void COList::HandleMessage(SGUIMessage
& Message
)
134 CList::HandleMessage(Message
);
136 switch (Message
.type
)
138 // If somebody clicks on the column heading
139 case GUIM_MOUSE_PRESS_LEFT
:
142 GUI
<bool>::GetSetting(this, "sortable", sortable
);
146 CPos mouse
= GetMousePos();
147 if (!m_CachedActualSize
.PointInside(mouse
))
151 GUI
<CStr
>::GetSetting(this, "selected_column", selectedColumn
);
152 int selectedColumnOrder
;
153 GUI
<int>::GetSetting(this, "selected_column_order", selectedColumnOrder
);
155 GUI
<float>::GetSetting(this, "heading_height", headingHeight
);
158 for (COListColumn column
: m_Columns
)
161 GUI
<bool>::GetSetting(this, "hidden_" + column
.m_Id
, hidden
);
165 float width
= column
.m_Width
;
166 // Check if it's a decimal value, and if so, assume relative positioning.
167 if (column
.m_Width
< 1 && column
.m_Width
> 0)
168 width
*= m_TotalAvailableColumnWidth
;
169 CPos leftTopCorner
= m_CachedActualSize
.TopLeft() + CPos(xpos
, 0);
170 if (mouse
.x
>= leftTopCorner
.x
&&
171 mouse
.x
< leftTopCorner
.x
+ width
&&
172 mouse
.y
< leftTopCorner
.y
+ headingHeight
)
174 if (column
.m_Id
!= selectedColumn
)
176 selectedColumnOrder
= 1;
177 selectedColumn
= column
.m_Id
;
180 selectedColumnOrder
= -selectedColumnOrder
;
181 GUI
<CStr
>::SetSetting(this, "selected_column", column
.m_Id
);
182 GUI
<int>::SetSetting(this, "selected_column_order", selectedColumnOrder
);
183 ScriptEvent("selectioncolumnchange");
186 if (g_SoundManager
&& GUI
<CStrW
>::GetSetting(this, "sound_selected", soundPath
) == PSRETURN_OK
&& !soundPath
.empty())
187 g_SoundManager
->PlayAsUI(soundPath
.c_str(), false);
200 bool COList::HandleAdditionalChildren(const XMBElement
& child
, CXeromyces
* pFile
)
202 #define ELMT(x) int elmt_##x = pFile->GetElementID(#x)
203 #define ATTR(x) int attr_##x = pFile->GetAttributeID(#x)
206 ELMT(translatableAttribute
);
210 if (child
.GetNodeName() == elmt_item
)
212 AddItem(child
.GetText().FromUTF8(), child
.GetText().FromUTF8());
215 else if (child
.GetNodeName() == elmt_column
)
220 for (XMBAttribute attr
: child
.GetAttributes())
222 CStr
attr_name(pFile
->GetAttributeString(attr
.Name
));
223 CStr
attr_value(attr
.Value
);
225 if (attr_name
== "color")
228 if (!GUI
<CColor
>::ParseString(attr_value
.FromUTF8(), color
))
229 LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name
.c_str(), attr_value
.c_str());
231 column
.m_TextColor
= color
;
233 else if (attr_name
== "id")
235 column
.m_Id
= attr_value
;
237 else if (attr_name
== "hidden")
239 if (!GUI
<bool>::ParseString(attr_value
.FromUTF8(), hidden
))
240 LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name
.c_str(), attr_value
.c_str());
242 else if (attr_name
== "width")
245 if (!GUI
<float>::ParseString(attr_value
.FromUTF8(), width
))
246 LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name
.c_str(), attr_value
.c_str());
249 // Check if it's a relative value, and save as decimal if so.
250 if (attr_value
.find("%") != std::string::npos
)
251 width
= width
/ 100.f
;
252 column
.m_Width
= width
;
255 else if (attr_name
== "heading")
257 column
.m_Heading
= attr_value
.FromUTF8();
261 for (XMBElement grandchild
: child
.GetChildNodes())
263 if (grandchild
.GetNodeName() != elmt_translatableAttribute
)
266 CStr
attributeName(grandchild
.GetAttributes().GetNamedItem(attr_id
));
267 // only the heading is translatable for list column
268 if (attributeName
.empty() || attributeName
!= "heading")
270 LOGERROR("GUI: translatable attribute in olist column that isn't a heading. (object: %s)", this->GetPresentableName().c_str());
274 CStr
value(grandchild
.GetText());
278 CStr
context(grandchild
.GetAttributes().GetNamedItem(attr_context
)); // Read the context if any.
279 if (!context
.empty())
281 CStr
translatedValue(g_L10n
.TranslateWithContext(context
, value
));
282 column
.m_Heading
= translatedValue
.FromUTF8();
286 CStr
translatedValue(g_L10n
.Translate(value
));
287 column
.m_Heading
= translatedValue
.FromUTF8();
291 m_Columns
.push_back(column
);
293 AddSetting(GUIST_CGUIList
, "list_" + column
.m_Id
);
294 AddSetting(GUIST_bool
, "hidden_" + column
.m_Id
);
295 GUI
<bool>::SetSetting(this, "hidden_" + column
.m_Id
, hidden
);
307 void COList::DrawList(const int& selected
, const CStr
& _sprite
, const CStr
& _sprite_selected
, const CStr
& _textcolor
)
309 float bz
= GetBufferedZ();
312 GUI
<bool>::GetSetting(this, "scrollbar", scrollbar
);
315 IGUIScrollBarOwner::Draw();
320 CRect rect
= GetListRect();
322 CGUISpriteInstance
* sprite
= NULL
;
323 CGUISpriteInstance
* sprite_selectarea
= NULL
;
325 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, _sprite
, sprite
);
326 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, _sprite_selected
, sprite_selectarea
);
327 GUI
<int>::GetSetting(this, "cell_id", cell_id
);
330 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
332 GetGUI()->DrawSprite(*sprite
, cell_id
, bz
, rect
);
336 scroll
= GetScrollBar(0).GetPos();
338 // Draw item selection
341 ENSURE(selected
>= 0 && selected
+1 < (int)m_ItemsYPositions
.size());
343 // Get rectangle of selection:
344 CRect
rect_sel(rect
.left
, rect
.top
+ m_ItemsYPositions
[selected
] - scroll
,
345 rect
.right
, rect
.top
+ m_ItemsYPositions
[selected
+1] - scroll
);
347 if (rect_sel
.top
<= rect
.bottom
&&
348 rect_sel
.bottom
>= rect
.top
)
350 if (rect_sel
.bottom
> rect
.bottom
)
351 rect_sel
.bottom
= rect
.bottom
;
352 if (rect_sel
.top
< rect
.top
)
353 rect_sel
.top
= rect
.top
;
357 // Remove any overlapping area of the scrollbar.
358 if (rect_sel
.right
> GetScrollBar(0).GetOuterRect().left
&&
359 rect_sel
.right
<= GetScrollBar(0).GetOuterRect().right
)
360 rect_sel
.right
= GetScrollBar(0).GetOuterRect().left
;
362 if (rect_sel
.left
>= GetScrollBar(0).GetOuterRect().left
&&
363 rect_sel
.left
< GetScrollBar(0).GetOuterRect().right
)
364 rect_sel
.left
= GetScrollBar(0).GetOuterRect().right
;
367 // Draw item selection
368 GetGUI()->DrawSprite(*sprite_selectarea
, cell_id
, bz
+0.05f
, rect_sel
);
373 GUI
<float>::GetSetting(this, "heading_height", headingHeight
);
375 // Draw line above column header
376 CGUISpriteInstance
* sprite_heading
= NULL
;
377 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, "sprite_heading", sprite_heading
);
378 CRect
rect_head(m_CachedActualSize
.left
, m_CachedActualSize
.top
, m_CachedActualSize
.right
,
379 m_CachedActualSize
.top
+ headingHeight
);
380 GetGUI()->DrawSprite(*sprite_heading
, cell_id
, bz
, rect_head
);
382 // Draw column headers
384 GUI
<bool>::GetSetting(this, "sortable", sortable
);
387 GUI
<CStr
>::GetSetting(this, "selected_column", selectedColumn
);
389 int selectedColumnOrder
;
390 GUI
<int>::GetSetting(this, "selected_column_order", selectedColumnOrder
);
393 GUI
<CColor
>::GetSetting(this, _textcolor
, color
);
396 for (size_t col
= 0; col
< m_Columns
.size(); ++col
)
399 GUI
<bool>::GetSetting(this, "hidden_" + m_Columns
[col
].m_Id
, hidden
);
403 // Check if it's a decimal value, and if so, assume relative positioning.
404 float width
= m_Columns
[col
].m_Width
;
405 if (m_Columns
[col
].m_Width
< 1 && m_Columns
[col
].m_Width
> 0)
406 width
*= m_TotalAvailableColumnWidth
;
408 CPos leftTopCorner
= m_CachedActualSize
.TopLeft() + CPos(xpos
, 0);
410 // Draw sort arrows in colum header
413 CGUISpriteInstance
* sprite
;
414 if (selectedColumn
== m_Columns
[col
].m_Id
)
416 if (selectedColumnOrder
== 0)
417 LOGERROR("selected_column_order must not be 0");
419 if (selectedColumnOrder
!= -1)
420 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, "sprite_asc", sprite
);
422 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, "sprite_desc", sprite
);
425 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, "sprite_not_sorted", sprite
);
427 GetGUI()->DrawSprite(*sprite
, cell_id
, bz
+ 0.1f
, CRect(leftTopCorner
+ CPos(width
- 16, 0), leftTopCorner
+ CPos(width
, 16)));
430 // Draw column header text
431 DrawText(col
, color
, leftTopCorner
+ CPos(0, 4), bz
+ 0.1f
, rect_head
);
435 // Draw list items for each column
436 const size_t objectsCount
= m_Columns
.size();
437 for (size_t i
= 0; i
< pList
->m_Items
.size(); ++i
)
439 if (m_ItemsYPositions
[i
+1] - scroll
< 0 ||
440 m_ItemsYPositions
[i
] - scroll
> rect
.GetHeight())
443 const float rowHeight
= m_ItemsYPositions
[i
+1] - m_ItemsYPositions
[i
];
445 // Clipping area (we'll have to substract the scrollbar)
446 CRect cliparea
= GetListRect();
450 if (cliparea
.right
> GetScrollBar(0).GetOuterRect().left
&&
451 cliparea
.right
<= GetScrollBar(0).GetOuterRect().right
)
452 cliparea
.right
= GetScrollBar(0).GetOuterRect().left
;
454 if (cliparea
.left
>= GetScrollBar(0).GetOuterRect().left
&&
455 cliparea
.left
< GetScrollBar(0).GetOuterRect().right
)
456 cliparea
.left
= GetScrollBar(0).GetOuterRect().right
;
459 // Draw all items for that column
461 for (size_t col
= 0; col
< objectsCount
; ++col
)
464 GUI
<bool>::GetSetting(this, "hidden_" + m_Columns
[col
].m_Id
, hidden
);
468 // Determine text position and width
469 const CPos textPos
= rect
.TopLeft() + CPos(xpos
, -scroll
+ m_ItemsYPositions
[i
]);
471 float width
= m_Columns
[col
].m_Width
;
472 // Check if it's a decimal value, and if so, assume relative positioning.
473 if (m_Columns
[col
].m_Width
< 1 && m_Columns
[col
].m_Width
> 0)
474 width
*= m_TotalAvailableColumnWidth
;
476 // Clip text to the column (to prevent drawing text into the neighboring column)
477 CRect cliparea2
= cliparea
;
478 cliparea2
.right
= std::min(cliparea2
.right
, textPos
.x
+ width
);
479 cliparea2
.bottom
= std::min(cliparea2
.bottom
, textPos
.y
+ rowHeight
);
482 DrawText(objectsCount
* (i
+/*Heading*/1) + col
, m_Columns
[col
].m_TextColor
, textPos
, bz
+ 0.1f
, cliparea2
);