OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4StartupPlrSelDlg.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // Startup screen for non-parameterized engine start: Player selection dialog
17 // Also contains player creation, editing and crew management
18 
19 #include "C4Include.h"
20 #include "gui/C4StartupPlrSelDlg.h"
21 
22 #include "graphics/C4Draw.h"
24 #include "gui/C4FileSelDlg.h"
25 #include "gui/C4MouseControl.h"
26 #include "gui/C4StartupMainDlg.h"
27 #include "lib/C4Random.h"
28 #include "lib/StdColors.h"
29 #include "player/C4RankSystem.h"
30 
31 // font clrs
32 const uint32_t ClrPlayerItem = 0xffffffff;
33 
34 // Arbitrary cut-off value for player color value. This avoids pitch black
35 // colors which look ugly. Note that this limit is only applied in the UI,
36 // it's still possible to edit the Player.txt by hand.
37 const uint32_t PlayerColorValueLowBound = 64;
38 
39 // ----- C4Utilities
40 
41 StdStrBuf TimeString(int iSeconds)
42 {
43  int iHours = iSeconds / 3600; iSeconds -= 3600*iHours;
44  int iMinutes = iSeconds / 60; iSeconds -= 60*iMinutes;
45  return FormatString("%02d:%02d:%02d",iHours,iMinutes,iSeconds);
46 }
47 
49 {
50  if (!iTime) return StdStrBuf("", true);
51  time_t tTime = iTime;
52  struct tm *pLocalTime;
53  pLocalTime=localtime(&tTime);
54  return FormatString( "%02d.%02d.%d %02d:%02d",
55  pLocalTime->tm_mday,
56  pLocalTime->tm_mon+1,
57  pLocalTime->tm_year+1900,
58  pLocalTime->tm_hour,
59  pLocalTime->tm_min);
60 }
61 
62 // ------------------------------------------------
63 // --- C4StartupPlrSelDlg::ListItem
64 C4StartupPlrSelDlg::ListItem::ListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement, bool fActivated)
65  : Control(C4Rect(0,0,0,0)), pCheck(nullptr), pNameLabel(nullptr), pPlrSelDlg(pForDlg), pIcon(nullptr)
66 {
68  // calc height
69  int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing;
70  // create subcomponents
71  pCheck = new C4GUI::CheckBox(C4Rect(0, 0, iHeight, iHeight), nullptr, fActivated);
73  pKeyCheck = new C4KeyBinding(C4KeyCodeEx(K_SPACE), "StartupPlrSelTogglePlayerActive", KEYSCOPE_Gui,
74  new C4GUI::ControlKeyCB<ListItem>(*this, &ListItem::KeyCheck), C4CustomKey::PRIO_Ctrl);
75  pIcon = new C4GUI::Icon(C4Rect(iHeight + IconLabelSpacing, 0, iHeight, iHeight), C4GUI::Ico_Player);
76  pNameLabel = new C4GUI::Label("Q", (iHeight + IconLabelSpacing)*2, IconLabelSpacing, ALeft, ClrPlayerItem, &rUseFont, false, false);
77  pNameLabel->SetAutosize(false);
78  // calc own bounds - use icon bounds only, because only the height is used when the item is added
79  SetBounds(pIcon->GetBounds());
80  // add components
81  AddElement(pCheck);
82  AddElement(pIcon); AddElement(pNameLabel);
83  // add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog
84  pForListBox->InsertElement(this, pInsertBeforeElement, 0);
85  // update name label width to stretch max listbox width
86  C4Rect rcNameLabelBounds = pNameLabel->GetBounds();
87  rcNameLabelBounds.Wdt = GetClientRect().Wdt - rcNameLabelBounds.x - IconLabelSpacing;
88  pNameLabel->SetBounds(rcNameLabelBounds);
89  // context menu
90  SetContextHandler(new C4GUI::CBContextHandler<C4StartupPlrSelDlg::ListItem>(this, &C4StartupPlrSelDlg::ListItem::ContextMenu));
91 }
92 
93 C4StartupPlrSelDlg::ListItem::~ListItem()
94 {
95  delete pKeyCheck;
96 }
97 
98 const char *C4StartupPlrSelDlg::ListItem::GetName() const
99 {
100  // name is stored in label only
101  return pNameLabel->GetText();
102 }
103 
104 void C4StartupPlrSelDlg::ListItem::SetName(const char *szNewName)
105 {
106  // update name in label
107  pNameLabel->SetText(szNewName);
108  // tooltip by name, so long names can be read via tooltip
109  SetToolTip(szNewName);
110 }
111 
112 void C4StartupPlrSelDlg::ListItem::GrabIcon(C4FacetSurface &rFromFacet)
113 {
114  // take over icon gfx from facet - deletes them from source facet!
115  if (rFromFacet.Surface)
116  {
117  pIcon->GetMFacet().GrabFrom(rFromFacet);
118  }
119  else
120  {
121  // reset custom icon
122  // following update-call will reset to default icon
123  pIcon->GetMFacet().Clear();
124  }
125 }
126 
127 void C4StartupPlrSelDlg::ListItem::SetIcon(C4GUI::Icons icoNew)
128 {
129  pIcon->SetIcon(icoNew);
130 }
131 
132 void C4StartupPlrSelDlg::ListItem::UpdateOwnPos()
133 {
134  // parent for client rect
135  typedef C4GUI::Window ParentClass;
136  ParentClass::UpdateOwnPos();
137  // reposition items
138  C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing);
139  // nothing to reposition for now...
140 }
141 
142 void C4StartupPlrSelDlg::ListItem::SetFilename(const StdStrBuf &sNewFN)
143 {
144  // just set fn - UpdateCore-call will follow later
145  Filename.Copy(sNewFN);
146 }
147 
148 bool C4StartupPlrSelDlg::ListItem::CheckNameHotkey(const char * c)
149 {
150  // return whether this item can be selected by entering given char:
151  // first char of name must match
152  // FIXME: Unicode
153  if (!pNameLabel) return false;
154  const char *szName = pNameLabel->GetText();
155  return szName && (toupper(*szName) == toupper(c[0]));
156 }
157 
158 
159 // ------------------------------------------------
160 // --- C4StartupPlrSelDlg::PlayerListItem
161 C4StartupPlrSelDlg::PlayerListItem::PlayerListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement, bool fActivated)
162  : ListItem(pForDlg, pForListBox, pInsertBeforeElement, fActivated), fHasCustomIcon(false)
163 {
164 }
165 
167 {
168  int32_t iHeight = GetBounds().Hgt;
169  // backup filename
170  SetFilename(rsFilename);
171  // load player info
172  C4Group PlrGroup;
173  if (!PlrGroup.Open(Config.AtUserDataPath(rsFilename.getData())))
174  throw LoadError(FormatString("Error loading player file from %s: Error opening group: %s", rsFilename.getData(), PlrGroup.GetError()));
175  if (!Core.Load(PlrGroup))
176  throw LoadError(FormatString("Error loading player file from %s: Core data invalid or missing (Group: %s)!", rsFilename.getData(), PlrGroup.GetError()));
177  // load icon
178  C4FacetSurface fctIcon;
179  if (PlrGroup.FindEntry(C4CFN_BigIcon) && fctIcon.Load(PlrGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0))
180  fHasCustomIcon = true;
181  else
182  {
183  // no custom icon: create default by player color
184  fctIcon.Create(iHeight,iHeight);
185  ::GraphicsResource.fctPlayerClr.DrawClr(fctIcon, true, Core.PrefColorDw);
186  }
187  GrabIcon(fctIcon);
188  // done loading
189  if (!PlrGroup.Close())
190  throw LoadError(FormatString("Error loading player file from %s: Error closing group: %s", rsFilename.getData(), PlrGroup.GetError()));
191  // default name
192  if (!*Core.PrefName) SCopy(GetFilenameOnly(rsFilename.getData()), Core.PrefName, sizeof(Core.PrefName)-1);
193  SetName(Core.PrefName);
194 }
195 
197 {
198  // menu operations work on selected item only
199  pPlrSelDlg->SetSelection(this);
200  // context menu operations
202  pCtx->AddItem(LoadResStr("IDS_BTN_PROPERTIES"), LoadResStr("IDS_DLGTIP_PLAYERPROPERTIES"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnPropertyCtx));
203  pCtx->AddItem(LoadResStr("IDS_BTN_DELETE"), LoadResStr("IDS_DLGTIP_PLAYERDELETE"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnDelCtx));
204  return pCtx;
205 }
206 
208 {
209  // set flag
210  fHasCustomIcon = !!fctGrabFrom.Surface;
211  // base class grab
212  GrabIcon(fctGrabFrom);
213 }
214 
216 {
217  C4Group PlrGroup;
218  if (!PlrGroup.Open(Config.AtUserDataPath(GetFilename().getData()))
219  || !NewCore.Save(PlrGroup)
220  || !PlrGroup.Close())
221  {
222  GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_MODIFY"), "", C4GUI::Ico_Error);
223  return;
224  }
225  Core = NewCore;
226  SetName(Core.PrefName);
227  // re-set non-custom icons
228  if (!fHasCustomIcon)
229  {
230  fHasCustomIcon = false;
231  int32_t iHeight = GetBounds().Hgt;
232  C4FacetSurface fctIcon; fctIcon.Create(iHeight,iHeight);
233  ::GraphicsResource.fctPlayerClr.DrawClr(fctIcon, true, Core.PrefColorDw);
234  GrabIcon(fctIcon);
235  }
236  // update in selection
237  C4StartupPlrSelDlg *pDlg = static_cast<C4StartupPlrSelDlg *>(GetDlg());
238  if (pDlg && pDlg->GetSelection() == this) pDlg->UpdateSelection();
239 }
240 
242 {
243  // write info text for player
244  pSelectionInfo->ClearText(false);
245  pSelectionInfo->AddTextLine(FormatString("%s", Core.PrefName).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false);
246  pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_PLAYER"), (int)Core.TotalScore, (int)Core.Rounds, (int)Core.RoundsWon, (int)Core.RoundsLost, TimeString(Core.TotalPlayingTime).getData(), Core.Comment).getData(), &C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false);
247  if (Core.LastRound.Title[0])
248  pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_LASTGAME"),Core.LastRound.Title.getData(),DateString(Core.LastRound.Date).getData(),TimeString(Core.LastRound.Duration).getData(),(int)Core.LastRound.FinalScore).getData(), &C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false);
249  pSelectionInfo->UpdateHeight();
250 }
251 
253 {
254  StdStrBuf sWarning;
255  sWarning.Format(LoadResStr("IDS_MSG_DELETEPLR"), Core.PrefName);
256  int32_t iPlrTime = Core.TotalPlayingTime;
257  if (iPlrTime > 60*60*10)
258  sWarning.Append(FormatString(LoadResStr("IDS_MSG_DELETEPLR_PLAYTIME"),
259  TimeString(iPlrTime).getData()).getData());
260  return sWarning;
261 }
262 
264 {
265  // anything to do?
266  if (ItemIdentical(GetFilename().getData(), szToFilename)) return true;
267  // do it
269  PathFrom.Append(GetFilename());
271  PathTo.Append(szToFilename);
272  if (!MoveItem(PathFrom.getData(), PathTo.getData())) return false;
273  // reflect change in class
274  SetFilename(StdStrBuf(szToFilename));
275  return true;
276 }
277 
278 
279 // ------------------------------------------------
280 // --- C4StartupPlrSelDlg::CrewListItem
281 
282 C4StartupPlrSelDlg::CrewListItem::CrewListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, uint32_t dwPlrClr)
283  : ListItem(pForDlg, pForListBox, nullptr, false), fLoaded(false), dwPlrClr(dwPlrClr), pParentGrp(nullptr)
284 {
285  SetIcon(C4GUI::Ico_Wait);
286 }
287 
288 void C4StartupPlrSelDlg::CrewListItem::UpdateClonkEnabled()
289 {
290  if (!fLoaded) return;
291  Core.Participation = pCheck->GetChecked();
292  // immediate save of changes
293  RewriteCore();
294 }
295 
296 void C4StartupPlrSelDlg::CrewListItem::Load(C4Group &rGrp, const StdStrBuf &rsFilename)
297 {
298  // backup filename (doesn't include path)
299  SetFilename(rsFilename);
300  // load core
301  C4Group CrewGroup;
302  if (!CrewGroup.OpenAsChild(&rGrp, rsFilename.getData()))
303  throw LoadError(FormatString("Error loading crew file from %s in %s: Error opening group: %s",
304  rsFilename.getData(), rGrp.GetFullName().getData(), CrewGroup.GetError()));
305  if (!Core.Load(CrewGroup))
306  throw LoadError(FormatString("Error loading crew file from %s: Core data invalid or missing (Group: %s)!",
307  CrewGroup.GetFullName().getData(), CrewGroup.GetError()));
308  ListItem::SetName(Core.Name);
309  pCheck->SetChecked(!!Core.Participation);
310  // load rank as icon
311  C4FacetSurface fctIcon;
312  if (fctIcon.Load(CrewGroup, C4CFN_ClonkRank, C4FCT_Full, C4FCT_Full, false, true))
313  {
314  GrabIcon(fctIcon);
315  }
316  else
317  {
318  // no custom icon: create default by rank system
320  GrabIcon(fctIcon);
321  }
322  // backup group loaded from - assumes it stays valid!
323  pParentGrp = &rGrp;
324  // load success!
325  fLoaded=true;
326 }
327 
328 C4GUI::ContextMenu *C4StartupPlrSelDlg::CrewListItem::ContextMenu()
329 {
330  // menu operations work on selected item only
331  pPlrSelDlg->SetSelection(this);
332  // context menu operations
334  pCtx->AddItem(LoadResStr("IDS_BTN_RENAME"), LoadResStr("IDS_DESC_CREWRENAME"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnPropertyCtx));
335  pCtx->AddItem(LoadResStr("IDS_BTN_DELETE"), LoadResStr("IDS_MSG_DELETECLONK_DESC"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg>(pPlrSelDlg, &C4StartupPlrSelDlg::OnDelCtx));
336  pCtx->AddItem(LoadResStr("IDS_MSG_SETDEATHMESSAGE"), LoadResStr("IDS_MSG_SETTHEMESSAGETHATAPPEARWH"), C4GUI::Ico_None, new C4GUI::CBMenuHandler<C4StartupPlrSelDlg::CrewListItem>(this, &C4StartupPlrSelDlg::CrewListItem::OnDeathMessageCtx));
337  return pCtx;
338 }
339 
340 void C4StartupPlrSelDlg::CrewListItem::OnDeathMessageCtx(C4GUI::Element *el)
341 {
342  // Death message dialog
343  C4GUI::InputDialog *pDlg;
344  GetScreen()->ShowRemoveDlg(pDlg=new C4GUI::InputDialog(LoadResStr("IDS_MSG_ENTERNEWDEATHMESSAGE"), LoadResStr("IDS_MSG_SETDEATHMESSAGE"), C4GUI::Ico_Ex_Comment, new C4GUI::InputCallback<C4StartupPlrSelDlg::CrewListItem>(this, &C4StartupPlrSelDlg::CrewListItem::OnDeathMessageSet), false));
345  pDlg->SetMaxText(C4MaxDeathMsg);
346  pDlg->SetInputText(Core.DeathMessage);
347 }
348 
349 void C4StartupPlrSelDlg::CrewListItem::OnDeathMessageSet(const StdStrBuf &rsNewMessage)
350 {
351  // copy msg
352  if (!rsNewMessage) *Core.DeathMessage='\0'; else SCopy(rsNewMessage.getData(), Core.DeathMessage, C4MaxDeathMsg);
353  // save
354  RewriteCore();
355  // acoustic feedback
356  C4GUI::GUISound("UI::Confirmed");
357 }
358 
359 void C4StartupPlrSelDlg::CrewListItem::RewriteCore()
360 {
361  if (!fLoaded) return;
362  C4Group CrewGroup;
363  if (!CrewGroup.OpenAsChild(pParentGrp, GetFilename().getData())
364  || !Core.Save(CrewGroup, nullptr)
365  || !CrewGroup.Close() || !pParentGrp->Save(true))
366  {
367  GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_MODIFY"), "", C4GUI::Ico_Error);
368  return;
369  }
370 }
371 
372 bool C4StartupPlrSelDlg::CrewListItem::SetName(const char *szNewName)
373 {
374  if (!fLoaded) return false;
375  // validate name
376  if (!szNewName || !*szNewName) return false;
377  if (SEqual(szNewName, Core.Name)) return true;
378  // generate filename from new name
379  char fn[_MAX_PATH+1];
380  SCopy(szNewName, fn, _MAX_PATH);
382  if (!*fn) return false;
383  SAppend(".oci", fn, _MAX_PATH);
384  // check if a rename is due
385  if (!ItemIdentical(fn, GetFilename().getData()))
386  {
387  // check for duplicate filename
388  if (pParentGrp->FindEntry(fn))
389  {
390  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_CLONKCOLLISION"), fn);
392  return false;
393  }
394  // OK; then rename
395  if (!pParentGrp->Rename(GetFilename().getData(), fn) || !pParentGrp->Save(true))
396  {
397  StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), GetFilename().getData(), fn);
399  return false;
400  }
401  const char *szConstFn = fn;
402  SetFilename(StdStrBuf(szConstFn));
403  }
404  // update clonk name and core
405  ListItem::SetName(szNewName);
406  SCopy(szNewName, Core.Name, C4MaxName);
407  RewriteCore();
408  return true;
409 }
410 
411 void C4StartupPlrSelDlg::CrewListItem::SetSelectionInfo(C4GUI::TextWindow *pSelectionInfo)
412 {
413  // write info text for player
414  pSelectionInfo->ClearText(false);
415  pSelectionInfo->AddTextLine(FormatString("%s %s", Core.sRankName.getData(), Core.Name).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false);
416  StdStrBuf sPromo;
417  int32_t iNextRankExp; StdStrBuf sNextRankName;
418  if (Core.GetNextRankInfo(::DefaultRanks, &iNextRankExp, &sNextRankName))
419  sPromo.Format(LoadResStr("IDS_DESC_PROMO"),sNextRankName.getData(),(int) iNextRankExp);
420  else
421  sPromo.Copy(LoadResStr("IDS_DESC_NOPROMO"));
422  pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_OBJECT"),
423  Core.TypeName, Core.Experience, Core.Rounds, Core.DeathCount,
424  sPromo.getData(), TimeString(Core.TotalPlayingTime).getData(), DateString(Core.Birthday).getData()).getData(),
425  &C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false);
426  pSelectionInfo->UpdateHeight();
427 }
428 
429 StdStrBuf C4StartupPlrSelDlg::CrewListItem::GetDelWarning()
430 {
431  StdStrBuf sWarning;
432  sWarning.Format(LoadResStr("IDS_MSG_DELETECLONK"),
433  Core.sRankName.getData(), Core.Name, GetFilename().getData());
434  int32_t iPlrTime = Core.TotalPlayingTime;
435  if (iPlrTime > 60*60*10)
436  sWarning.Append(static_cast<const StdStrBuf &>(FormatString(LoadResStr("IDS_MSG_DELETECLONK_PLAYTIME"), TimeString(iPlrTime).getData())));
437  return sWarning;
438 }
439 
440 void C4StartupPlrSelDlg::CrewListItem::CrewRename()
441 {
442  if (pPlrSelDlg->pRenameEdit) return;
443  // rename this entry
444  pPlrSelDlg->pRenameEdit = new C4GUI::CallbackRenameEdit<C4StartupPlrSelDlg::CrewListItem, RenameParams>(pNameLabel, this, RenameParams(), &C4StartupPlrSelDlg::CrewListItem::DoRenaming, &C4StartupPlrSelDlg::CrewListItem::AbortRenaming);
445 }
446 
447 void C4StartupPlrSelDlg::CrewListItem::AbortRenaming(RenameParams par)
448 {
449  // no renaming
450  pPlrSelDlg->pRenameEdit = nullptr;
451 }
452 
453 C4GUI::RenameEdit::RenameResult C4StartupPlrSelDlg::CrewListItem::DoRenaming(RenameParams par, const char *szNewName)
454 {
455  // accept if name can be set; will fail if name is invalid or already given to another Crew member
456  if (!SetName(szNewName)) return C4GUI::RenameEdit::RR_Invalid;
457  pPlrSelDlg->pRenameEdit = nullptr;
458  // update in selection
459  C4StartupPlrSelDlg *pDlg = static_cast<C4StartupPlrSelDlg *>(GetDlg());
460  if (pDlg && pDlg->GetSelection() == this) pDlg->UpdateSelection();
462 }
463 
464 
465 
466 // ------------------------------------------------
467 // --- C4StartupPlrSelDlg
468 
470 {
471  // ctor
472  UpdateSize(); // for clientrect
473 
474  // screen calculations
475  int iButtonHeight = C4GUI_ButtonHgt;
476  int iButtonXSpacing = (GetClientRect().Wdt > 700) ? GetClientRect().Wdt/58 : 2;
477  int iButtonCount = 6;
478  C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
479  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(std::max(caMain.GetHeight()/15, iButtonHeight)),0,0);
480  rcBottomButtons = caButtonArea.GetCentered(caMain.GetWidth(), iButtonHeight);
481  iBottomButtonWidth = (caButtonArea.GetWidth() - iButtonXSpacing * (iButtonCount-1)) / iButtonCount;
482  C4Rect rcMain = caMain.GetAll();
483  C4Rect rcPlrList = C4Rect(rcMain.Wdt/8, rcMain.Hgt/8, rcMain.Wdt*5/16, rcMain.Hgt*6/8);
484  C4Rect rcInfoWindow = C4Rect(rcMain.Wdt*9/16, rcMain.Hgt/8, rcMain.Wdt*5/16, rcMain.Hgt*6/8);
485 
486  AddElement(pPlrListBox = new C4GUI::ListBox(rcPlrList));
487  pPlrListBox->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERFILES"));
488  pPlrListBox->SetDecoration(true, &C4Startup::Get()->Graphics.sfctBookScroll, true);
489  pPlrListBox->UpdateElementPositions();
490  pPlrListBox->SetSelectionChangeCallbackFn(new C4GUI::CallbackHandler<C4StartupPlrSelDlg>(this, &C4StartupPlrSelDlg::OnSelChange));
491  pPlrListBox->SetSelectionDblClickFn(new C4GUI::CallbackHandler<C4StartupPlrSelDlg>(this, &C4StartupPlrSelDlg::OnSelDblClick));
492  AddElement(pSelectionInfo = new C4GUI::TextWindow(rcInfoWindow,0,0,0,100,4096," ",false,nullptr,0,true));
493  pSelectionInfo->SetDecoration(true, true, &C4Startup::Get()->Graphics.sfctBookScroll, true);
494  pSelectionInfo->UpdateHeight();
495 
496  // bottom line buttons - positioning done in UpdateBottomButtons by UpdatePlayerList
497  C4Rect rcDefault(0,0,10,10);
500  btnNew->SetToolTip(LoadResStr("IDS_DLGTIP_NEWPLAYER"));
502  AddElement(btnDelete = new C4GUI::CallbackButton<C4StartupPlrSelDlg>(LoadResStr("IDS_BTN_DELETE"), rcDefault, &C4StartupPlrSelDlg::OnDelBtn));
505  btnCrew->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERCREW"));
506 
507  // refill listboxes
508  UpdatePlayerList();
509  // Just safety incase listbox was empty, in which case no selection change callback will have been done:
510  // Update current listbox selection to that of no selected item
511  if (!pPlrListBox->GetFirst()) UpdateSelection();
512 
513  // initial focus on player list
514  SetFocus(pPlrListBox, false);
515 
516  // key bindings
518  keys.emplace_back(K_BACK);
519  keys.emplace_back(K_LEFT);
520  keys.emplace_back(K_ESCAPE);
522  {
524  }
525  pKeyBack = new C4KeyBinding(keys, "StartupPlrSelBack", KEYSCOPE_Gui,
527  pKeyProperties = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupPlrSelProp", KEYSCOPE_Gui,
529  pKeyCrew = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupPlrSelCrew", KEYSCOPE_Gui,
531  pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupPlrSelDelete", KEYSCOPE_Gui,
533  pKeyNew = new C4KeyBinding(C4KeyCodeEx(K_INSERT), "StartupPlrSelNew", KEYSCOPE_Gui,
535 }
536 
538 {
539  delete pKeyDelete;
540  delete pKeyCrew;
541  delete pKeyProperties;
542  delete pKeyBack;
543  delete pKeyNew;
544 }
545 
547 {
548  if (pRenameEdit) pRenameEdit->Abort();
549 }
550 
552 {
553  // draw background
554  typedef C4GUI::FullscreenDialog Base;
555  Base::DrawElement(cgo);
556 }
557 
558 void C4StartupPlrSelDlg::UpdateBottomButtons()
559 {
560  // bottom line buttons depend on list mode
561  C4GUI::ComponentAligner caBottomButtons(rcBottomButtons,0,0);
562  switch (eMode)
563  {
564  case PSDM_Player:
565  {
566  // update some buttons for player mode
567  btnProperties->SetText(LoadResStr("IDS_BTN_PROPERTIES"));
568  btnProperties->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERPROPERTIES"));
569  btnNew->SetVisibility(true);
570  btnCrew->SetVisibility(true);
571  btnDelete->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERDELETE"));
572  btnBack->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN"));
573  btnBack ->SetBounds(caBottomButtons.GetGridCell(0,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
574  btnNew ->SetBounds(caBottomButtons.GetGridCell(1,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
575  btnActivatePlr->SetBounds(caBottomButtons.GetGridCell(2,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
576  btnDelete ->SetBounds(caBottomButtons.GetGridCell(3,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
577  btnProperties ->SetBounds(caBottomButtons.GetGridCell(4,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
578  btnCrew ->SetBounds(caBottomButtons.GetGridCell(5,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
579  }
580  break;
581 
582  case PSDM_Crew:
583  {
584  // update some buttons for player mode
585  btnProperties->SetText(LoadResStr("IDS_BTN_RENAME"));
586  btnProperties->SetToolTip(LoadResStr("IDS_DESC_CREWRENAME"));
587  btnNew->SetVisibility(false);
588  btnCrew->SetVisibility(false);
589  btnDelete->SetToolTip(LoadResStr("IDS_MSG_DELETECLONK_DESC"));
590  btnBack->SetToolTip(LoadResStr("IDS_MSG_BACKTOPLAYERDLG"));
591  btnBack ->SetBounds(caBottomButtons.GetGridCell(0,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
592  btnActivatePlr->SetBounds(caBottomButtons.GetGridCell(1,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
593  btnDelete ->SetBounds(caBottomButtons.GetGridCell(2,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
594  btnProperties ->SetBounds(caBottomButtons.GetGridCell(3,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true));
595  }
596  break;
597  };
598 }
599 
600 void C4StartupPlrSelDlg::UpdatePlayerList()
601 {
602  // something has changed!
603  AbortRenaming();
604  // refill pPlrListBox with players in player folder or crew
605  // clear old items
606  C4GUI::Element *pEl;
607  while ((pEl = pPlrListBox->GetFirst())) delete pEl;
608  // update command buttons
609  UpdateBottomButtons();
610  // create new
611  switch (eMode)
612  {
613  case PSDM_Player:
614  {
615  SetTitle(LoadResStrNoAmp("IDS_DLG_PLAYERSELECTION"));
616  // player mode: insert all players
617  const char *szFn;
618  StdStrBuf sSearchPath(Config.General.UserDataPath);
619  PlayerListItem *pFirstActivatedPlrItem=nullptr, *pFirstDeactivatedPlrItem=nullptr, *pPlrItem=nullptr;
620  for (DirectoryIterator i(sSearchPath.getData()); (szFn=*i); i++)
621  {
622  szFn = Config.AtRelativePath(szFn);
623  if (*GetFilename(szFn) == '.') continue; // ignore ".", ".." and private files (".*")
624  if (!WildcardMatch(C4CFN_PlayerFiles, GetFilename(szFn))) continue;
625  bool fIsParticipating = !!SIsModule(Config.General.Participants, szFn, nullptr, false);
626  pPlrItem = new PlayerListItem(this, pPlrListBox, nullptr, fIsParticipating);
627  try
628  {
629  pPlrItem->Load(StdStrBuf(szFn));
630  }
631  catch (ListItem::LoadError & e)
632  {
633  // invalid player: ignore but log error message
634  DebugLog(e.getData());
635  delete pPlrItem;
636  continue;
637  }
638  if (fIsParticipating)
639  {
640  if (!pFirstActivatedPlrItem) pFirstActivatedPlrItem = pPlrItem;
641  }
642  else if (!pFirstDeactivatedPlrItem) pFirstDeactivatedPlrItem = pPlrItem;
643  }
644  // select first element; prefer activated player
645  if (!(pPlrItem = pFirstActivatedPlrItem))
646  pPlrItem = pFirstDeactivatedPlrItem;
647  if (pPlrItem)
648  pPlrListBox->SelectEntry(pPlrItem, false);
649  // re-validate Game.PlayerFilename
650  UpdateActivatedPlayers();
651  break;
652  }
653 
654  case PSDM_Crew:
655  {
656  SetTitle(FormatString("%s %s", LoadResStrNoAmp("IDS_CTL_CREW"), CurrPlayer.Core.PrefName).getData());
657  // crew mode: Insert complete crew of player (2do: sort)
658  bool fSucc; char szFn[_MAX_PATH+1];
659  for (fSucc=CurrPlayer.Grp.FindEntry(C4CFN_ObjectInfoFiles, szFn); fSucc; fSucc=CurrPlayer.Grp.FindNextEntry(C4CFN_ObjectInfoFiles, szFn, nullptr, true))
660  {
661  CrewListItem *pCrewItem = new CrewListItem(this, pPlrListBox, CurrPlayer.Core.PrefColorDw);
662  try
663  {
664  pCrewItem->Load(CurrPlayer.Grp, StdStrBuf(szFn));
665  }
666  catch (ListItem::LoadError & e)
667  {
668  // invalid player: ignore but log error message
669  DebugLog(e.getData());
670  delete pCrewItem;
671  continue;
672  }
673  }
674  // resort crew by type and experience
675  ResortCrew();
676  pPlrListBox->SelectFirstEntry(false);
677  break;
678  }
679  }
680 }
681 
683 {
684  // get selected item: There's only instances of PlrListItem in this list
685  return static_cast<ListItem *>(pPlrListBox->GetSelectedItem());
686 }
687 
688 void C4StartupPlrSelDlg::SetSelection(ListItem *pNewItem)
689 {
690  // update selection in listbox
691  pPlrListBox->SelectEntry(pNewItem, false);
692 }
693 
694 void C4StartupPlrSelDlg::UpdateSelection()
695 {
696  // something has changed!
697  AbortRenaming();
698  // get currently selected player
699  ListItem *pSel = GetSelection();
700  // button text 'activate' if current player is deactivated; 'deactivate' otherwise
701  if (pSel && pSel->IsActivated())
702  {
703  btnActivatePlr->SetText(LoadResStr("IDS_BTN_DEACTIVATE"));
704  btnActivatePlr->SetToolTip(FormatString(LoadResStr("IDS_MSG_NOPARTICIPATE_DESC"), pSel->GetName()).getData());
705  }
706  else
707  {
708  btnActivatePlr->SetText(LoadResStr("IDS_BTN_ACTIVATE"));
709  btnActivatePlr->SetToolTip(FormatString(LoadResStr("IDS_MSG_PARTICIPATE_DESC"), pSel ? pSel->GetName() : "").getData());
710  }
711  // no item selected?
712  if (!pSel)
713  {
714  pSelectionInfo->ClearText(true);
715  // 2do: disable buttons
716  return;
717  }
718  // info text for selection
719  pSel->SetSelectionInfo(pSelectionInfo);
720 }
721 
723 {
724  switch (eMode)
725  {
726  case PSDM_Player:
727  // Deselect all other players
728  for (ListItem* pEl = static_cast<ListItem*>(pPlrListBox->GetFirst()); pEl != nullptr; pEl = pEl->GetNext())
729  if (pCheckBox && pEl != pCheckBox->GetParent())
730  pEl->SetActivated(false);
731  // update Config.General.Participants
732  UpdateActivatedPlayers();
733  break;
734  case PSDM_Crew:
735  // update affected crew item
736  if (pCheckBox) static_cast<CrewListItem *>(pCheckBox->GetParent())->UpdateClonkEnabled();
737  break;
738  }
739  // update player selection text
740  UpdateSelection();
741 }
742 
743 void C4StartupPlrSelDlg::UpdateActivatedPlayers()
744 {
745  assert(eMode == PSDM_Player);
746  // refill Config.General.Participants-list
747  *Config.General.Participants = '\0';
748  for (ListItem *pPlrItem = static_cast<ListItem *>(pPlrListBox->GetFirst()); pPlrItem; pPlrItem = pPlrItem->GetNext())
749  if (pPlrItem->IsActivated())
750  {
751  const char *szAddFilename = pPlrItem->GetFilename().getData();
752  if (std::strlen(Config.General.Participants) + 1 + std::strlen(szAddFilename) < sizeof(Config.General.Participants))
753  SAddModule(Config.General.Participants, szAddFilename);
754  else
755  {
756  pPlrItem->SetActivated(false);
757  GetScreen()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLAYERSTOOLONG"), pPlrItem->GetName()).getData(), LoadResStr("IDS_ERR_TITLE"), C4GUI::Ico_Error);
758  }
759  }
760 }
761 
763 {
764  // toggle activation state of current item
765  // get currently selected player
766  ListItem *pSel = GetSelection();
767  if (!pSel) return;
768  pSel->SetActivated(!pSel->IsActivated());
769  // update stuff
770  OnItemCheckChange(pSel->GetCheckBox());
771 }
772 
774 {
775  switch (eMode)
776  {
777  case PSDM_Player:
778  {
779  // back 2 main
781  break;
782  }
783 
784  case PSDM_Crew:
785  // back 2 player list
786  SetPlayerMode();
787  break;
788  }
789 }
790 
792 {
793  if (eMode != PSDM_Player) return;
794  C4GUI::Dialog *pDlg;
795  GetScreen()->ShowRemoveDlg(pDlg=new C4StartupPlrPropertiesDlg(nullptr, this));
796  pDlg->SetPos(std::min<int32_t>(GetBounds().Wdt/10, GetBounds().Wdt - pDlg->GetBounds().Wdt), std::min<int32_t>(GetBounds().Hgt/4, GetBounds().Hgt - pDlg->GetBounds().Hgt));
797 }
798 
799 bool C4StartupPlrSelDlg::CheckPlayerName(const StdStrBuf &Playername, StdStrBuf &Filename, const StdStrBuf *pPrevFilename, bool fWarnEmpty)
800 {
801  // must not be empty
802  if (!Playername.getLength())
803  {
804  if (fWarnEmpty) C4GUI::Screen::GetScreenS()->ShowMessage(LoadResStr("IDS_ERR_PLRNAME_EMPTY"), "", C4GUI::Ico_Error);
805  return false;
806  }
807  // generate valid filename
808  Filename.Copy(Playername);
809  // Slashes in Filenames are no good
810  SReplaceChar(Filename.getMData(), '\\', '_');
811  SReplaceChar(Filename.getMData(), '/', '_');
812  SReplaceChar(Filename.getMData(), ':', '_');
813  SReplaceChar(Filename.getMData(), '*', '_');
814  SReplaceChar(Filename.getMData(), '?', '_');
815  SReplaceChar(Filename.getMData(), '"', '_');
816  SReplaceChar(Filename.getMData(), '<', '_');
817  SReplaceChar(Filename.getMData(), '>', '_');
818  SReplaceChar(Filename.getMData(), '|', '_');
819  if (*Filename.getData() == '.') *Filename.getMData() = '_';
820  Filename.Append(".ocp");
821  StdStrBuf Path(Config.General.UserDataPath); // start at local path
822 
823  Path.Append(Filename);
824  // validity check: Must not exist yet if renamed
825  if (!pPrevFilename || !ItemIdentical(Path.getData(), Config.AtUserDataPath(pPrevFilename->getData()))) if (ItemExists(Path.getData()))
826  {
827  C4GUI::Screen::GetScreenS()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLRNAME_TAKEN"),
828  Playername.getData()).getData(), "", C4GUI::Ico_Error);
829  return false;
830  }
831 
832  return true;
833 }
834 
836 {
837  // display crew for activated player
838  if (eMode != PSDM_Player) return;
839  PlayerListItem *pSel = static_cast<PlayerListItem *>(GetSelection());
840  if (!pSel) return;
841  SetCrewMode(pSel);
842 }
843 
844 void C4StartupPlrSelDlg::SetPlayerMode()
845 {
846  // change view to listing players
847  C4GUI::GUISound("UI::Close");
848  StdStrBuf LastPlrFilename;
849  LastPlrFilename.Copy(static_cast<const StdStrBuf &>(CurrPlayer.Grp.GetFullName()));
850  CurrPlayer.Grp.Close();
851  eMode = PSDM_Player;
852  UpdatePlayerList();
853  SelectItem(LastPlrFilename, false);
854  UpdateSelection();
855 }
856 
857 void C4StartupPlrSelDlg::SetCrewMode(PlayerListItem *pSel)
858 {
859  // change view to listing crew of a player
860  CurrPlayer.Core = pSel->GetCore();
861 
862  StdStrBuf Path(Config.General.UserDataPath); // start at local path
863 // Path.Append(Config.General.PlayerPath);
864  Path.Append(pSel->GetFilename());
865 
866  if (!CurrPlayer.Grp.Open(Path.getData())) return;
867  if (!CurrPlayer.Grp.FindEntry(C4CFN_ObjectInfoFiles))
868  {
869  StdCopyStrBuf strCrew(FormatString("%s %s", LoadResStrNoAmp("IDS_CTL_CREW"), CurrPlayer.Core.PrefName));
870  // player has no crew!
871  GetScreen()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLRNOCREW"),
872  CurrPlayer.Core.PrefName).getData(),
873  strCrew.getData(), C4GUI::Ico_Player);
874  return;
875  }
876  C4GUI::GUISound("UI::Open");
877  eMode = PSDM_Crew;
878  UpdatePlayerList();
879  UpdateSelection();
880 }
881 
883 {
884  // something has changed!
885  AbortRenaming();
886  // delete selected player
887  ListItem *pSel = GetSelection();
888  if (!pSel) return;
889  StdStrBuf sWarning; sWarning.Take(pSel->GetDelWarning());
890  GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_BTN_DELETE"),
892 }
893 
895 {
896  StdStrBuf Path;
897 
898  switch (eMode)
899  {
900  case PSDM_Player:
901  Path.Append(Config.General.UserDataPath); // start at local path
902  Path.Append(pSel->GetFilename());
903  if (!C4Group_DeleteItem(Path.getData()))
904  {
905  StdStrBuf sMsg; sMsg.Copy(LoadResStr("IDS_FAIL_DELETE"));
906  GetScreen()->ShowMessage(sMsg.getData(), LoadResStr("IDS_DLG_CLEAR"), C4GUI::Ico_Error);
907  }
908  break;
909 
910  case PSDM_Crew:
911  if (!CurrPlayer.Grp.DeleteEntry(pSel->GetFilename().getData()))
912  {
913  StdStrBuf sMsg; sMsg.Copy(LoadResStr("IDS_FAIL_DELETE"));
914  GetScreen()->ShowMessage(sMsg.getData(), LoadResStr("IDS_DLG_CLEAR"), C4GUI::Ico_Error);
915  }
916  break;
917  }
918  // update buttons 'n stuff
919  UpdatePlayerList();
920 }
921 
922 void C4StartupPlrSelDlg::SelectItem(const StdStrBuf &Filename, bool fActivate)
923 {
924  // find item
925  for (ListItem *pPlrItem = static_cast<ListItem *>(pPlrListBox->GetFirst()); pPlrItem; pPlrItem = pPlrItem->GetNext())
926  if (ItemIdentical(pPlrItem->GetFilename().getData(), Filename.getData()))
927  {
928  // select it
929  pPlrListBox->SelectEntry(pPlrItem, false);
930  // activate it
931  if (fActivate)
932  {
933  pPlrItem->SetActivated(true);
934  // player activation updates
935  OnItemCheckChange(pPlrItem->GetCheckBox());
936  }
937  // max one
938  return;
939  }
940 }
941 
943 {
944  // something has changed!
945  AbortRenaming();
946  switch (eMode)
947  {
948  case PSDM_Player:
949  {
950  // show property dialog for selected player
951  PlayerListItem *pSel = static_cast<PlayerListItem *>(GetSelection());
952  if (!pSel) return;
953  C4GUI::Dialog *pDlg;
954  GetScreen()->ShowRemoveDlg(pDlg=new C4StartupPlrPropertiesDlg(pSel, this));
955  pDlg->SetPos(std::min<int32_t>(GetBounds().Wdt/10, GetBounds().Wdt - pDlg->GetBounds().Wdt),
956  (GetBounds().Hgt - pDlg->GetBounds().Hgt) / 2);
957  }
958  break;
959 
960  case PSDM_Crew:
961  // rename crew
962  CrewListItem *pSel = static_cast<CrewListItem *>(GetSelection());
963  if (!pSel) return;
964  pSel->CrewRename();
965  break;
966  }
967 }
968 
969 
970 /* -- Crew sorting -- */
971 
973 {
974  int32_t iMaxExp;
976 
977  C4StartupPlrSelDlg_CrewSortDataEntry(int32_t iMaxExp, C4ID idType) : iMaxExp(iMaxExp), idType(idType) {}
978 };
979 
981 {
982  C4ID idType;
983 
984 public:
985  C4StartupPlrSelDlg_CrewSortDataMatchType(C4ID idType) : idType(idType) {}
986  bool operator()(C4StartupPlrSelDlg_CrewSortDataEntry Check) { return Check.idType == idType; }
987 };
988 
989 typedef std::vector<C4StartupPlrSelDlg_CrewSortDataEntry> C4StartupPlrSelDlg_CrewSortData;
990 
991 int32_t C4StartupPlrSelDlg::CrewSortFunc(const C4GUI::Element *pEl1, const C4GUI::Element *pEl2, void *par)
992 {
993  const auto *pItem1 = static_cast<const CrewListItem *>(pEl1);
994  const auto *pItem2 = static_cast<const CrewListItem *>(pEl2);
995  C4StartupPlrSelDlg_CrewSortData &rSortData = *static_cast<C4StartupPlrSelDlg_CrewSortData *>(par);
996  C4StartupPlrSelDlg_CrewSortData::iterator i = std::find_if(rSortData.begin(), rSortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pItem1->GetCore().id)),
997  j = std::find_if(rSortData.begin(), rSortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pItem2->GetCore().id));
998  // primary sort: By Clonk type, where high exp Clonk types are sorted atop low exp Clonk types
999  if (i != j)
1000  {
1001  if (i == rSortData.end()) return -1; else if (j == rSortData.end()) return +1; // can't really happen
1002  return (*i).iMaxExp - (*j).iMaxExp;
1003  }
1004  // secondary: By experience
1005  return pItem1->GetCore().Experience - pItem2->GetCore().Experience;
1006 }
1007 
1008 void C4StartupPlrSelDlg::ResortCrew()
1009 {
1010  assert(eMode == PSDM_Crew);
1011  // create a list of Clonk types and their respective maximum experience
1012  C4StartupPlrSelDlg_CrewSortData SortData;
1013  for (CrewListItem *pCrewItem = static_cast<CrewListItem *>(pPlrListBox->GetFirst()); pCrewItem; pCrewItem = pCrewItem->GetNext())
1014  {
1015  C4StartupPlrSelDlg_CrewSortData::iterator i = std::find_if(SortData.begin(), SortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pCrewItem->GetCore().id));
1016  if (i == SortData.end())
1017  SortData.emplace_back(pCrewItem->GetCore().Experience, pCrewItem->GetCore().id);
1018  else
1019  (*i).iMaxExp = std::max<int32_t>((*i).iMaxExp, pCrewItem->GetCore().Experience);
1020  }
1021  pPlrListBox->SortElements(&CrewSortFunc, &SortData);
1022 }
1023 
1024 // ------------------------------------------------
1025 // --- Player color HSV chooser
1027 {
1028 public:
1030 
1031 protected:
1032  // Event handler
1033  void OnClosed(bool commit) override;
1034 
1035 private:
1036  class Picker : public C4GUI::Control
1037  {
1038  public:
1039  Picker(const C4Rect &bounds);
1040 
1041  // Set/retrieve current color value
1042  void SetColor(uint32_t rgb);
1043  uint32_t GetColor() const;
1044 
1045  protected:
1046  // Event handlers, overridden from C4GUI::Control
1047  void DrawElement(C4TargetFacet &cgo) override;
1048  void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override;
1049  void DoDragging(C4GUI::CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) override;
1050 
1051  private:
1052  static const unsigned int HSPickerCursorSize = 5;
1053  static const unsigned int VPickerCursorSize = 7;
1054  C4FacetSurface hsFacet, vFacet; // chooser backgrounds
1055  C4Rect hsPickerRect, vPickerRect;
1056  C4GUI::Picture *flagPreview, *crewPreview;
1057  uint32_t hsv; // current color
1058  enum {
1059  PS_Idle, // user isn't dragging anything
1060  PS_IdleDragging, // user started the drag on empty space
1061  PS_DragHS, // user started the drag over the HS picker
1062  PS_DragV // user started the drag over the V picker
1063  } state;
1064 
1065  bool HandleMouseDown(int32_t x, int32_t y);
1066  void UpdateVFacet(uint32_t h, uint32_t s);
1067  void UpdatePreview();
1068  };
1069 
1070  C4PlayerInfoCore *plrcore;
1071  Picker *picker;
1072 
1073  static uint32_t HSV2RGB(uint32_t hsv)
1074  {
1075  float h = GetRedValue(hsv) / 255.f * 6.f;
1076  float s = GetGreenValue(hsv) / 255.f;
1077  float v = GetBlueValue(hsv) / 255.f;
1078 
1079  float chroma = s * v;
1080  float x = chroma * (1.f - std::abs(std::fmod(h, 2.f) - 1.f));
1081 
1082  float r = 0;
1083  float g = 0;
1084  float b = 0;
1085 
1086  switch (static_cast<int>(h))
1087  {
1088  case 0: case 6:
1089  r = chroma; g = x; break;
1090  case 1:
1091  r = x; g = chroma; break;
1092  case 2:
1093  g = chroma; b = x; break;
1094  case 3:
1095  g = x; b = chroma; break;
1096  case 4:
1097  b = chroma; r = x; break;
1098  case 5:
1099  b = x; r = chroma; break;
1100  }
1101  r += v-chroma;
1102  g += v-chroma;
1103  b += v-chroma;
1104 
1105  return RGBA(r * 255.f, g * 255.f, b * 255.f, hsv >> 24);
1106  }
1107  static uint32_t RGB2HSV(uint32_t rgb)
1108  {
1109  float r = GetRedValue(rgb) / 255.f;
1110  float g = GetGreenValue(rgb) / 255.f;
1111  float b = GetBlueValue(rgb) / 255.f;
1112 
1113  float min = std::min(r, std::min(g, b));
1114  float max = std::max(r, std::max(g, b));
1115 
1116  float chroma = max - min;
1117 
1118  float hue = 0;
1119  if (r == max)
1120  hue = std::fmod((g-b) / chroma, 6.f);
1121  else if (g == max)
1122  hue = (b-r) / chroma + 2.f;
1123  else
1124  hue = (r-g) / chroma + 4.f;
1125 
1126  float h = hue / 6.f;
1127  float s = max == 0 ? 0.f : chroma / max;
1128  float v = max;
1129 
1130  return RGBA(h * 255.f, s * 255.f, v * 255.f, rgb >> 24);
1131  }
1132 };
1133 
1135  : Dialog(400, 296 + C4GUI_ButtonAreaHgt, LoadResStr("IDS_DLG_PLAYERCOLORSELECTION"), false), plrcore(plrcore)
1136 {
1137  C4GUI::ComponentAligner caMain(GetClientRect(), 0, 1, true);
1138 
1139  picker = new Picker(caMain.GetFromTop(280));
1140  picker->SetColor(plrcore->PrefColorDw);
1141  AddElement(picker);
1142 
1143  // buttons
1144  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt), 0, 0);
1145  caButtonArea = C4GUI::ComponentAligner(caButtonArea.GetCentered(2*128 + 4*8, C4GUI_ButtonAreaHgt), 8, 8);
1146  C4GUI::Button *cancelButton = new C4GUI::CancelButton(caButtonArea.GetFromRight(128));
1147  C4GUI::Button *okButton = new C4GUI::OKButton(caButtonArea.GetFromRight(128));
1148  AddElement(okButton);
1149  AddElement(cancelButton);
1150 }
1151 
1153 {
1154  // Write chosen color back to player core
1155  if (commit)
1156  plrcore->PrefColorDw = picker->GetColor();
1157 }
1158 
1159 C4StartupPlrColorPickerDlg::Picker::Picker(const C4Rect &bounds)
1160  : Control(bounds), state(PS_Idle)
1161 {
1162  C4GUI::ComponentAligner caMain(bounds, 8, 8, true);
1163  caMain.ExpandBottom(-(caMain.GetInnerHeight() - 256));
1164  hsPickerRect = caMain.GetFromLeft(256);
1165  vPickerRect = caMain.GetFromLeft(16 + VPickerCursorSize);
1166  vPickerRect.Hgt = 256 - PlayerColorValueLowBound;
1167 
1168  C4Facet &flagPreviewPic = ::GraphicsResource.fctFlagClr;
1169  int preview_width = std::min<int>(flagPreviewPic.Wdt, caMain.GetInnerWidth());
1170  flagPreview = new C4GUI::Picture(caMain.GetFromTop(flagPreviewPic.GetHeightByWidth(preview_width), preview_width), true);
1171  flagPreview->SetFacet(flagPreviewPic);
1172  AddElement(flagPreview);
1173 
1174  C4Facet &crewPreviewPic = ::GraphicsResource.fctCrewClr;
1175  preview_width = std::min<int>(crewPreviewPic.Wdt, caMain.GetInnerWidth());
1176  crewPreview = new C4GUI::Picture(caMain.GetFromTop(crewPreviewPic.GetHeightByWidth(preview_width), preview_width), true);
1177  crewPreview->SetFacet(crewPreviewPic);
1178  AddElement(crewPreview);
1179 
1180  // Pre-draw the H+S chooser background, it never changes anyway
1181  hsFacet.Create(hsPickerRect.Wdt, hsPickerRect.Hgt);
1182  hsFacet.Surface->Lock();
1183  for (int y = 0; y < hsFacet.Hgt; ++y)
1184  for (int x = 0; x < hsFacet.Wdt; ++x)
1185  hsFacet.Surface->SetPixDw(x, y, HSV2RGB(C4RGB(x, 255-y, 255)));
1186  hsFacet.Surface->Unlock();
1187 
1188  vFacet.Create(vPickerRect.Wdt - VPickerCursorSize, vPickerRect.Hgt);
1189  UpdateVFacet(255, 255);
1190 }
1191 
1192 void C4StartupPlrColorPickerDlg::Picker::UpdateVFacet(uint32_t h, uint32_t s)
1193 {
1194  // Draw the V chooser background according to current H+S values
1195  vFacet.Surface->Lock();
1196  for (int y = 0; y < vPickerRect.Hgt; ++y)
1197  for (int x = 0; x < vFacet.Wdt; ++x)
1198  vFacet.Surface->SetPixDw(x, y, HSV2RGB(C4RGB(h, s, 255-y)));
1199  vFacet.Surface->Unlock();
1200 }
1201 
1202 void C4StartupPlrColorPickerDlg::Picker::UpdatePreview()
1203 {
1204  flagPreview->SetDrawColor(HSV2RGB(hsv));
1205  crewPreview->SetDrawColor(HSV2RGB(hsv));
1206 }
1207 
1208 void C4StartupPlrColorPickerDlg::Picker::SetColor(uint32_t rgb)
1209 {
1210  hsv = RGB2HSV(rgb);
1211  UpdateVFacet(GetRedValue(hsv), GetGreenValue(hsv));
1212  UpdatePreview();
1213 }
1214 
1215 uint32_t C4StartupPlrColorPickerDlg::Picker::GetColor() const
1216 {
1217  return HSV2RGB(hsv);
1218 }
1219 
1220 void C4StartupPlrColorPickerDlg::Picker::DrawElement(C4TargetFacet &cgo)
1221 {
1222  // H+S chooser background
1223  C4Facet cgoPicker(cgo.Surface, cgo.TargetX + hsPickerRect.x, cgo.TargetY + hsPickerRect.y, hsPickerRect.Wdt, hsPickerRect.Hgt);
1224  hsFacet.Draw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y);
1225  // H+S cursor
1226  cgoPicker.Wdt = cgoPicker.Hgt = HSPickerCursorSize;
1227  cgoPicker.X += GetRedValue(hsv) - cgoPicker.Wdt / 2;
1228  cgoPicker.Y += 255 - GetGreenValue(hsv) - cgoPicker.Hgt / 2;
1229  pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y, cgoPicker.X + cgoPicker.Wdt, cgoPicker.Y + cgoPicker.Hgt, C4RGB(0, 0, 0));
1230  pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X + cgoPicker.Wdt, cgoPicker.Y, cgoPicker.X, cgoPicker.Y + cgoPicker.Hgt, C4RGB(0, 0, 0));
1231 
1232  // V chooser background
1233  cgoPicker.Set(cgo.Surface, cgo.TargetX + vPickerRect.x + VPickerCursorSize, cgo.TargetY + vPickerRect.y, vPickerRect.Wdt - VPickerCursorSize, vPickerRect.Hgt);
1234  vFacet.Draw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y);
1235  // V cursor
1236  cgoPicker.Wdt = cgoPicker.Hgt = VPickerCursorSize;
1237  cgoPicker.X -= cgoPicker.Wdt / 2 + 1;
1238  cgoPicker.Y += 255 - GetBlueValue(hsv) - cgoPicker.Hgt / 2;
1239  for (int i = 0; i < cgoPicker.Hgt / 2 + 1; ++i)
1240  pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X + i, cgoPicker.Y + i, cgoPicker.X + i, cgoPicker.Y + cgoPicker.Hgt - i, C4RGB(255, 255, 255));
1241 }
1242 
1243 bool C4StartupPlrColorPickerDlg::Picker::HandleMouseDown(int32_t x, int32_t y)
1244 {
1245  if (state == PS_IdleDragging)
1246  {
1247  // User is dragging something that is neither of the pickers. Ignore.
1248  return false;
1249  }
1250  // Check if a drag starts or was originally started over a picker
1251  else if (state == PS_DragHS || (state == PS_Idle && hsPickerRect.Contains(x, y)))
1252  {
1253  int h = Clamp(x - hsPickerRect.x, 0, hsPickerRect.Wdt - 1);
1254  assert(Inside(h, 0, 255));
1255  int s = 255 - Clamp(y - hsPickerRect.y, 0, hsPickerRect.Hgt - 1);
1256  assert(Inside(s, 0, 255));
1257  hsv = C4RGB(h, s, GetBlueValue(hsv));
1258  UpdateVFacet(h, s);
1259  UpdatePreview();
1260  state = PS_DragHS;
1261  return true;
1262  }
1263  else if (state == PS_DragV || (state == PS_Idle && vPickerRect.Contains(x, y)))
1264  {
1265  int v = 255 - Clamp(y - vPickerRect.y, 0, vPickerRect.Hgt - 1);
1266  assert(Inside(v, 0, 255));
1267  hsv = (hsv & 0xFFFFFF00) | v;
1268  UpdatePreview();
1269  state = PS_DragV;
1270  return true;
1271  }
1272  else
1273  {
1274  // Drag started outside of all picker areas; ignore movement until user releases mouse button.
1275  state = PS_IdleDragging;
1276  return false;
1277  }
1278 }
1279 
1280 void C4StartupPlrColorPickerDlg::Picker::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam)
1281 {
1282  Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam);
1283 
1284  if (!rMouse.IsLDown()) state = PS_Idle;
1285  if (rMouse.pDragElement) return;
1286  if (rMouse.IsLDown())
1287  {
1288  if (HandleMouseDown(iX, iY))
1289  {
1290  rMouse.pDragElement = this;
1291  C4GUI::GUISound("UI::Select");
1292  }
1293  else
1294  {
1295  rMouse.pDragElement = nullptr;
1296  }
1297  }
1298 }
1299 
1300 void C4StartupPlrColorPickerDlg::Picker::DoDragging(C4GUI::CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam)
1301 {
1302  HandleMouseDown(iX, iY);
1303 }
1304 
1305 /* ---- Player property dlg ---- */
1306 
1308  : Dialog(C4Startup::Get()->Graphics.fctPlrPropBG.Wdt, C4Startup::Get()->Graphics.fctPlrPropBG.Hgt, "", false), pMainDlg(pParentDlg), pForPlayer(pForPlayer), fClearBigIcon(false)
1309 {
1310  if (pForPlayer)
1311  {
1312  // edit existing player
1313  C4P = pForPlayer->GetCore();
1314  }
1315  else
1316  {
1317  // create new player: Use default C4P values, with a few exceptions
1318  // FIXME: Use Player, not Clonkranks
1319  C4P.Default(&::DefaultRanks);
1320  // Set name, color, comment
1321  SCopy(LoadResStr("IDS_PLR_NEWCOMMENT"), C4P.Comment, C4MaxComment);
1326  C4P.OldPrefControl = 0;
1327  }
1328  const int32_t BetweenElementDist = 2;
1329  // use black fonts here
1330  CStdFont *pUseFont = &C4Startup::Get()->Graphics.BookFont;
1331  CStdFont *pSmallFont = &C4Startup::Get()->Graphics.BookSmallFont;
1332  // get positions
1333  UpdateSize();
1334  C4GUI::ComponentAligner caMain(GetClientRect(), 0, 1, true);
1335  C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt), 0,0);
1336  // dlg title
1337  const char *szTitle;
1338  if (pForPlayer)
1339  {
1340  szTitle = LoadResStr("IDS_DLG_PLAYER2");
1341  }
1342  else
1343  {
1344  szTitle = LoadResStr("IDS_PLR_NEWPLAYER");
1345  }
1346  C4GUI::Label *pLbl = new C4GUI::Label(szTitle, caMain.GetFromTop(pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false);
1347  AddElement(pLbl);
1348  caMain.ExpandTop(-BetweenElementDist);
1349  // place name label
1350  AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_NAME2"), caMain.GetFromTop(pSmallFont->GetLineHeight()), ALeft, C4StartupFontClr, pSmallFont, false));
1351  // place name edit
1353  pNameEdit->SetFont(pUseFont);
1355  pNameEdit->InsertText(C4P.PrefName, false);
1356  pNameEdit->SetMaxText(C4MaxName);
1357  AddElement(pNameEdit);
1358  SetFocus(pNameEdit, false);
1359  caMain.ExpandTop(-BetweenElementDist);
1360 
1361  int32_t iControlPicSize = C4GUI::ArrowButton::GetDefaultHeight(); // GetGridCell(0,3,0,1,-1,-1,false,2)
1362  int32_t label_hgt = pSmallFont->GetLineHeight();
1363 
1364  // place color label
1365  C4GUI::ComponentAligner caColorArea(caMain.GetFromTop(iControlPicSize + BetweenElementDist + label_hgt), 2, 0);
1366  C4GUI::ComponentAligner caPictureArea(caColorArea.GetFromRight(iControlPicSize, iControlPicSize + BetweenElementDist + label_hgt), 2,0);
1367  caColorArea.ExpandLeft(2);
1368  AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr("IDS_CTL_COLOR")).getData(), caColorArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
1369  caColorArea.ExpandTop(-BetweenElementDist);
1370  // place picture label
1371  AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_PICTURE"), caPictureArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
1372  caPictureArea.ExpandTop(-BetweenElementDist);
1373  // place color controls
1374  C4GUI::Button *pBtn; const char *szTip;
1375  szTip = LoadResStr("IDS_DLGTIP_PLAYERCOLORS");
1377  pBtn->SetToolTip(szTip);
1378  C4Facet &rfctClrPreviewPic = ::GraphicsResource.fctFlagClr;
1380  pClrPreview->SetFacet(rfctClrPreviewPic);
1383  pBtn->SetToolTip(szTip);
1384  if (!C4P.PrefColorDw) C4P.PrefColorDw=0xff;
1385  // Place picture controls
1387  pPictureBtn->SetToolTip(LoadResStr("IDS_DESC_SELECTAPICTUREANDORLOBBYI"));
1388  UpdateBigIcon();
1389  UpdatePlayerColor(true);
1390  caMain.ExpandTop(-BetweenElementDist);
1391  // place control label
1392  C4GUI::ComponentAligner caControlArea(caMain.GetFromTop(iControlPicSize + label_hgt + BetweenElementDist), 0,0, false);
1393  C4GUI::ComponentAligner caSkinArea(caControlArea.GetFromRight(iControlPicSize + label_hgt + BetweenElementDist), 0,0, false);
1394  AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr("IDS_CTL_CONTROL")).getData(), caControlArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
1395  caControlArea.ExpandTop(-BetweenElementDist);
1396  // place clonk style label
1397  AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_CLONKSKIN"), caSkinArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false));
1398  caSkinArea.ExpandTop(-BetweenElementDist);
1399  // place control controls
1400  C4GUI::ComponentAligner caControl(caControlArea.GetFromTop(iControlPicSize), 2,0);
1401  szTip = LoadResStr("IDS_DLGTIP_PLAYERCONTROL");
1403  pBtn->SetToolTip(szTip);
1404  caControl.ExpandBottom(label_hgt); C4Rect ctrl_name_rect = caControl.GetFromBottom(label_hgt);
1405  C4Facet &rfctCtrlPic = ::GraphicsResource.fctKeyboard; // UpdatePlayerControl() will alternatively set fctGamepad
1406  AddElement(pCtrlImg = new C4GUI::Picture(caControl.GetFromLeft(rfctCtrlPic.GetWidthByHeight(caControl.GetHeight())), true));
1407  pCtrlImg->SetToolTip(szTip);
1409  pBtn->SetToolTip(szTip);
1410  caControl.ExpandLeft(-10);
1411  C4P.OldPrefControl = Clamp<int32_t>(C4P.OldPrefControl, 0, C4MaxControlSet-1);
1412  ctrl_name_lbl = new C4GUI::Label("CtrlName", ctrl_name_rect, ALeft, C4StartupFontClr, pSmallFont, false, false, true);
1413  AddElement(ctrl_name_lbl);
1414  UpdatePlayerControl();
1415 
1416  C4GUI::ComponentAligner caSkin(caSkinArea.GetFromTop(iControlPicSize), 2,0);
1417  szTip = LoadResStr("IDS_DLGTIP_PLAYERCREWSKIN");
1419  pBtn->SetToolTip(szTip);
1421  AddElement(pSkinImg = new C4GUI::Picture(caSkin.GetFromLeft(rfctSkinPic.GetWidthByHeight(caSkin.GetHeight())), true));
1422  pSkinImg->SetToolTip(szTip);
1425  pBtn->SetToolTip(szTip);
1426  caSkin.ExpandLeft(-10);
1427  UpdatePlayerSkin();
1428 
1429  caMain.ExpandTop(-BetweenElementDist);
1430  // place buttons
1431  // OK
1432  C4GUI::Button *pBtnOK = new C4GUI::OKIconButton(C4Rect(147-GetMarginLeft(), 295+35-GetMarginTop(), 54, 33), C4GUI::Ico_None);
1433  AddElement(pBtnOK);
1434  // Cancel
1435  C4GUI::Button *pBtnAbort = new C4GUI::CancelIconButton(C4Rect(317-GetMarginLeft(), 16-GetMarginTop(), 21, 21), C4GUI::Ico_None);
1436  AddElement(pBtnAbort);
1437  // when called from player selection screen: input dlg always closed in the end
1438  // otherwise, modal proc will delete
1439  if (pMainDlg) SetDelOnClose();
1440 }
1441 
1443 {
1445 }
1446 
1447 bool IsColorConflict(DWORD dwClr1, DWORD dwClr2);
1448 
1449 void C4StartupPlrPropertiesDlg::UpdatePlayerColor(bool fUpdateSliders)
1450 {
1451  C4P.PrefColorDw = C4P.PrefColorDw | 0xFF000000; // Ensure full opacity
1454 }
1455 
1457 {
1458  // previous standard color in list
1459  C4P.PrefColor = C4P.PrefColor ? C4P.PrefColor - 1 : 11;
1461  UpdatePlayerColor(true);
1462 }
1464 {
1466  UpdatePlayerColor(true);
1467 }
1468 
1470 {
1471  // next standard color in list
1472  C4P.PrefColor = (C4P.PrefColor + 1) % 12;
1474  UpdatePlayerColor(true);
1475 }
1476 
1477 void C4StartupPlrPropertiesDlg::UpdatePlayerControl()
1478 {
1480  if (!control_set) control_set = Game.PlayerControlUserAssignmentSets.GetDefaultSet();
1481  // update keyboard image of selected control
1482  C4Facet fctCtrlPic;
1483  if (control_set) fctCtrlPic = control_set->GetPicture();
1484  pCtrlImg->SetFacet(fctCtrlPic);
1485  if (control_set)
1486  ctrl_name_lbl->SetText(control_set->GetGUIName());
1487  else
1488  ctrl_name_lbl->SetText("???");
1489 }
1490 
1492 {
1493  // previous control set in list
1495  int32_t index = Game.PlayerControlUserAssignmentSets.GetSetIndex(control_set);
1496  if (index < 0) index = 0; // defined control set not found - probably an old CR player file
1497  if (!index--) index = Game.PlayerControlUserAssignmentSets.GetSetCount() - 1;
1498  control_set = Game.PlayerControlUserAssignmentSets.GetSetByIndex(index);
1499  if (control_set) C4P.PrefControl = control_set->GetName();
1500  UpdatePlayerControl();
1501 }
1502 
1504 {
1505  // next control set in list
1507  int32_t index = Game.PlayerControlUserAssignmentSets.GetSetIndex(control_set);
1508  if (index < 0) index = 0; // defined control set not found - probably an old CR player file
1509  if (++index >= int32_t(Game.PlayerControlUserAssignmentSets.GetSetCount())) index = 0;
1510  control_set = Game.PlayerControlUserAssignmentSets.GetSetByIndex(index);
1511  if (control_set) C4P.PrefControl = control_set->GetName();
1512  UpdatePlayerControl();
1513 }
1514 
1515 void C4StartupPlrPropertiesDlg::UpdatePlayerSkin()
1516 {
1518 }
1519 
1521 {
1522  // previous skin in list
1524  UpdatePlayerSkin();
1525 }
1527 {
1528  // next skin in list
1529  C4P.PrefClonkSkin = (C4P.PrefClonkSkin + 1) % 4;
1530  UpdatePlayerSkin();
1531 }
1532 
1534 {
1535  // check name validity
1536  if (fOK)
1537  {
1538  StdStrBuf PlrName(pNameEdit->GetText()), Filename;
1539  if (!C4StartupPlrSelDlg::CheckPlayerName(PlrName, Filename, pForPlayer ? &pForPlayer->GetFilename() : nullptr, true)) return;
1540  }
1541  Close(fOK);
1542 }
1543 
1545 {
1546  if (fOK)
1547  {
1548  // store selected data if desired
1549  StdStrBuf PlrName(pNameEdit->GetText()), Filename;
1550  if (C4StartupPlrSelDlg::CheckPlayerName(PlrName, Filename, pForPlayer ? &pForPlayer->GetFilename() : nullptr, true))
1551  {
1552  SCopy(PlrName.getData(), C4P.PrefName, C4MaxName);
1553  C4Group PlrGroup;
1554  bool fSucc=false;
1555  // existant player: update file
1556  if (pForPlayer)
1557  {
1558  if (!pForPlayer->MoveFilename(Filename.getData()))
1559  GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_RENAME"), "", C4GUI::Ico_Error);
1560  // update bigicon
1562  {
1563  C4Group PlrGroup;
1564  if (PlrGroup.Open(Config.AtUserDataPath(Filename.getData())))
1565  {
1568  if (PlrGroup.Close()) fSucc = true;
1570  }
1571  }
1572  else
1573  {
1574  fSucc = true;
1575  }
1577  // player may have been activated: Make sure any new filename is reflected in participants list
1578  if (pMainDlg) pMainDlg->UpdateActivatedPlayers();
1579  }
1580  else
1581  {
1582  // NewPlayer: Open new player group
1583  if (PlrGroup.Open(Config.AtUserDataPath(Filename.getData()), true))
1584  {
1585  // Do not overwrite (should have been caught earlier anyway)
1586  if (PlrGroup.FindEntry(C4CFN_PlayerInfoCore)) return;
1587  // Save info core
1588  C4P.Save(PlrGroup);
1589  // Add BigIcon
1590  if (fctNewBigIcon.Surface)
1591  {
1593  }
1594  // Close group
1595  if (PlrGroup.Close()) fSucc=true;
1596  // update activate button text
1597  if (pMainDlg)
1598  {
1599  pMainDlg->UpdatePlayerList();
1600  pMainDlg->SelectItem(Filename, true);
1601  }
1602  else
1603  {
1604  // no main player selection dialog: This means that this dlg was shown as a creation dialog from the main startup dlg
1605  // Just set the newly created player as current selection
1607  }
1608  }
1609  }
1610  if (!fSucc) GetScreen()->ShowErrorMessage(PlrGroup.GetError());
1611  }
1612  }
1613  // Make the dialog go away
1614  Dialog::OnClosed(fOK);
1615 }
1616 
1617 bool C4StartupPlrPropertiesDlg::SetNewPicture(C4Surface &srcSfc, C4FacetSurface *trgFct, int32_t iMaxSize, bool fColorize)
1618 {
1619  if (fColorize)
1620  {
1621  C4Surface srcSfcClr;
1622  if (!srcSfcClr.CreateColorByOwner(&srcSfc)) return false;
1623  return trgFct->CopyFromSfcMaxSize(srcSfcClr, iMaxSize, C4P.PrefColorDw);
1624  }
1625  else
1626  {
1627  return trgFct->CopyFromSfcMaxSize(srcSfc, iMaxSize);
1628  }
1629 }
1630 
1631 void C4StartupPlrPropertiesDlg::SetNewPicture(const char *szFromFilename)
1632 {
1633  if (!szFromFilename)
1634  {
1635  // If szFromFilename==nullptr, clear bigicon
1636  fClearBigIcon = true;
1637  fctNewBigIcon.Clear();
1638  }
1639  else
1640  {
1641  // else set new bigicon by loading and scaling if necessary.
1642  C4Surface sfcNewPic;
1643  C4Group SrcGrp;
1644  StdStrBuf sParentPath;
1645  GetParentPath(szFromFilename, &sParentPath);
1646  bool fSucc = false;
1647  if (SrcGrp.Open(sParentPath.getData()))
1648  {
1649  if (sfcNewPic.Load(SrcGrp, GetFilename(szFromFilename), false, false, 0))
1650  {
1651  fSucc = true;
1652  if (!SetNewPicture(sfcNewPic, &fctNewBigIcon, C4MaxBigIconSize, true)) fSucc = false;
1653  }
1654  }
1655  if (!fSucc)
1656  {
1657  // error!
1658  GetScreen()->ShowErrorMessage(FormatString(LoadResStr("IDS_PRC_NOGFXFILE"), szFromFilename, SrcGrp.GetError()).getData());
1659  }
1660  }
1661  // update icon
1662  UpdateBigIcon();
1663 }
1664 
1666 {
1667  StdStrBuf sNewPic;
1669  {
1670  SetNewPicture(sNewPic.getData());
1671  }
1672 }
1673 
1674 void C4StartupPlrPropertiesDlg::UpdateBigIcon()
1675 {
1676  // new icon?
1677  bool fHasIcon = false;
1678  if (fctNewBigIcon.Surface)
1679  {
1681  fHasIcon = true;
1682  }
1683  // old icon in existing player?
1684  else if (!fClearBigIcon && pForPlayer)
1685  {
1686  C4Group PlrGroup;
1687  if (PlrGroup.Open(Config.AtUserDataPath(pForPlayer->GetFilename().getData())))
1688  {
1689  if (PlrGroup.FindEntry(C4CFN_BigIcon))
1690  {
1691  if (fctOldBigIcon.Load(PlrGroup, C4CFN_BigIcon, C4FCT_Full, C4FCT_Full, false, 0))
1692  {
1694  fHasIcon = true;
1695  }
1696  }
1697  }
1698  }
1699  // no icon: Set default
1700  if (!fHasIcon)
1701  {
1703  }
1704 }
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
int32_t OldPrefControlStyle
Definition: C4InfoCore.h:104
const char * getData() const
Definition: StdBuf.h:442
const char * GetName() const
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
int32_t GetHeight() const
Definition: C4Gui.h:2801
int32_t OldPrefAutoContextMenu
Definition: C4InfoCore.h:105
void OnCtrlChangeRight(C4GUI::Control *pBtn)
void SetText(const char *szToText)
Definition: C4GuiButton.cpp:55
void AddItem(const char *szText, const char *szToolTip=nullptr, Icons icoIcon=Ico_None, MenuHandler *pMenuHandler=nullptr, ContextHandler *pSubmenuHandler=nullptr)
Definition: C4Gui.h:1873
void DrawElement(C4TargetFacet &cgo) override
#define C4GUI_ButtonAreaHgt
Definition: C4Gui.h:113
C4Config Config
Definition: C4Config.cpp:833
void ExpandTop(int32_t iByHgt)
Definition: C4Gui.h:2808
int GetLineHeight() const
Definition: C4FontLoader.h:125
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1175
void OnClosed(bool fOK) override
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
void GetAll(C4Rect &rcOut)
Definition: C4Gui.cpp:1125
void SetFont(CStdFont *pToFont)
Definition: C4Gui.h:1347
void OnDelBtnConfirm(ListItem *pSel)
C4PlayerControlAssignmentSet * GetDefaultSet()
Element * pDragElement
Definition: C4Gui.h:2553
void SetDecoration(bool fDrawBG, bool fDrawFrame, ScrollBarFacets *pToGfx, bool fAutoScroll)
Definition: C4Gui.h:1752
#define C4CFN_ClonkRank
Definition: C4Components.h:121
C4PlayerControlAssignmentSet * GetSetByName(const char *szName)
void SetPos(int32_t iXPos, int32_t iYPos)
Definition: C4Gui.h:860
int32_t GetMarginLeft() override
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:257
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:855
C4Game Game
Definition: C4Globals.cpp:52
#define GetGreenValue(rgb)
Definition: StdColors.h:28
C4StartupPlrPropertiesDlg(C4StartupPlrSelDlg::PlayerListItem *pForPlayer, C4StartupPlrSelDlg *pMainDlg)
uint32_t UnsyncedRandom()
Definition: C4Random.cpp:58
int32_t OldPrefControl
Definition: C4InfoCore.h:103
#define b
C4ConfigGeneral General
Definition: C4Config.h:251
bool Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fNoErrIfNotFound, int iFlags)
Definition: C4FacetEx.cpp:78
bool CopyFromSfcMaxSize(C4Surface &srcSfc, int32_t iMaxSize, uint32_t dwColor=0u)
Definition: C4FacetEx.cpp:105
C4StartupPlrSelDlg::PlayerListItem * pForPlayer
std::vector< C4StartupPlrSelDlg_CrewSortDataEntry > C4StartupPlrSelDlg_CrewSortData
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
void Cancel(T &keys)
bool DebugLog(const char *strMessage)
Definition: C4Log.cpp:270
void OnCrewBtn(C4GUI::Control *btn)
void SetFacet(const C4Facet &fct)
Definition: C4Gui.h:612
char PrefName[C4MaxName+1]
Definition: C4InfoCore.h:85
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:409
int32_t PrefClonkSkin
Definition: C4InfoCore.h:100
bool CreateColorByOwner(C4Surface *pBySurface)
Definition: C4Surface.cpp:255
void UpdateElementPositions()
void MouseInput(CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) override
bool InsertElement(Element *pChild, Element *pInsertBefore, int32_t iIndent=0)
StdCopyStrBuf PrefControl
Definition: C4InfoCore.h:96
void OnPropertyCtx(C4GUI::Element *el)
void GrabCustomIcon(C4FacetSurface &fctGrabFrom)
C4FacetID fctPlrPropBG
Definition: C4Startup.h:43
void Close(bool fOK)
const char * GetText()
Definition: C4Gui.h:1338
bool ShowRemoveDlg(Dialog *pDlg)
void DrawClr(C4Facet &cgo, bool fAspect=true, DWORD dwClr=0)
Definition: C4Facet.cpp:213
C4RankSystem DefaultRanks
Definition: C4Rect.h:27
void UserClose(bool fOK) override
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
static Screen * GetScreenS()
Definition: C4Gui.h:2626
void AddElement(Element *pChild)
bool SIsModule(const char *szList, const char *szString, int *ipIndex, bool fCaseSensitive)
Definition: Standard.cpp:541
const int32_t C4StartupEditBorderColor
Definition: C4Startup.h:30
Element * GetFirst()
Definition: C4Gui.h:1571
bool Delete(const char *szFiles, bool fRecursive=false)
Definition: C4Group.cpp:1334
#define _MAX_PATH
#define GetRedValue(rgb)
Definition: StdColors.h:29
const char * GetFilenameOnly(const char *strFilename)
Definition: StdFile.cpp:60
void SetFacet(const C4Facet &rCpy, uint32_t dwClr=0u)
Definition: C4Gui.h:1149
#define C4CFN_ObjectInfoFiles
Definition: C4Components.h:170
void SetSelectionChangeCallbackFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1548
StdStrBuf DateString(int iTime)
C4GraphicsResource GraphicsResource
void SetDecoration(bool fDrawBG, ScrollBarFacets *pToGfx, bool fAutoScroll, bool fDrawBorder=false)
Definition: C4Gui.h:1566
int32_t iMaxExp
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
const int32_t C4StartupFontClr
Definition: C4Startup.h:27
friend class C4StartupPlrPropertiesDlg
void SetDelOnClose(bool fToVal=true)
Definition: C4Gui.h:2190
void DrawLineDw(C4Surface *sfcTarget, float x1, float y1, float x2, float y2, DWORD dwClr, float width=1.0f)
Definition: C4Draw.cpp:614
void OnClrChangeCustom(C4GUI::Control *pBtn)
const int C4MaxControlSet
Definition: C4Constants.h:32
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
Element * GetSelectedItem()
Definition: C4Gui.h:1580
T Clamp(T bval, T lbound, T rbound)
Definition: Standard.h:44
void MakeFilenameFromTitle(char *szTitle)
Definition: StdFile.cpp:402
bool GetParentPath(const char *szFilename, char *szBuffer)
Definition: StdFile.cpp:189
C4StartupPlrSelDlg * pMainDlg
#define C4CFN_PlayerFiles
Definition: C4Components.h:168
char * getMData()
Definition: StdBuf.h:443
char Comment[C4MaxComment+1]
Definition: C4InfoCore.h:86
void SortElements(SortFunction SortFunc, void *par)
int32_t Wdt
Definition: C4Rect.h:30
void OnSkinChangeRight(C4GUI::Control *pBtn)
C4PlayerControlAssignmentSet * GetSetByIndex(int32_t index)
std::vector< C4KeyCodeEx > CodeList
void OnDelCtx(C4GUI::Element *el)
void SetMaxText(int32_t iMaxLen)
Definition: C4Gui.h:2419
bool IsLDown()
Definition: C4Gui.h:2562
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
static DWORD GetPrefColorValue(int32_t iPrefColor)
Definition: C4InfoCore.cpp:67
const unsigned int C4MaxComment
void SelectFirstEntry(bool fByUser)
Definition: C4Gui.h:1584
int32_t GetSetIndex(const C4PlayerControlAssignmentSet *set) const
CStdFont BookSmallFont
Definition: C4Startup.h:65
bool MoveFilename(const char *szToFilename)
bool GetFromTop(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1059
const char * LoadResStrNoAmp(const char *id)
Definition: StdResStr2.cpp:23
int32_t y
Definition: C4Rect.h:30
char Participants[CFG_MaxString+1]
Definition: C4Config.h:38
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
#define GetBlueValue(rgb)
Definition: StdColors.h:27
char UserDataPath[CFG_MaxString+1]
Definition: C4Config.h:54
static bool CheckPlayerName(const StdStrBuf &Playername, StdStrBuf &Filename, const StdStrBuf *pPrevFilename, bool fWarnEmpty)
void Default(C4RankSystem *pRanks=nullptr)
Definition: C4InfoCore.cpp:40
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
C4StartupPlrColorPickerDlg(C4PlayerInfoCore *plrcore)
C4GameControl Control
static bool DrawRankSymbol(C4FacetSurface *fctSymbol, int32_t iRank, C4Facet *pfctRankSymbols, int32_t iRankSymbolCount, bool fOwnSurface, int32_t iXOff=0, C4Facet *cgoDrawDirect=nullptr)
bool Save(C4Group &hGroup)
Definition: C4InfoCore.cpp:107
void SetColor(uint32_t dwClr)
Definition: C4Gui.h:1150
bool ShowErrorMessage(const char *szMessage)
#define C4CFN_PlayerInfoCore
Definition: C4Components.h:45
void ExpandLeft(int32_t iByWdt)
Definition: C4Gui.h:2809
void DrawElement(C4TargetFacet &cgo) override
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
void SetSelection(ListItem *pNewItem)
static int32_t GetDefaultHeight()
const int32_t C4MaxDeathMsg
Definition: C4InfoCore.h:29
C4Rect rcBounds
Definition: C4Gui.h:385
void Take(char *pnData)
Definition: StdBuf.h:457
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
C4Rect & GetClientRect() override
Definition: C4Gui.h:864
static int32_t GetDefaultWidth()
void Draw(C4Facet &cgo, bool fAspect=true, int32_t iPhaseX=0, int32_t iPhaseY=0, bool fTransparent=true)
Definition: C4Facet.cpp:154
void UpdateHeight()
Definition: C4Gui.h:1750
Container * GetParent()
Definition: C4Gui.h:429
class C4StartupDlg * SwitchDialog(DialogID eToDlg, bool fFade=true, const char *szSubDialog=nullptr)
Definition: C4Startup.cpp:137
friend class PlayerListItem
const unsigned int C4MaxName
bool Close()
Definition: C4Group.cpp:755
C4Draw * pDraw
Definition: C4Draw.cpp:42
const uint32_t PlayerColorValueLowBound
void OnClrChangeLeft(C4GUI::Control *pBtn)
C4GUI::RenameEdit * pRenameEdit
int32_t PrefColor
Definition: C4InfoCore.h:98
#define C4RGB(r, g, b)
Definition: StdColors.h:26
void UpdateSize() override
PlayerListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement=nullptr, bool fActivated=false)
void SetInputText(const char *szToText)
uint32_t PrefColorDw
Definition: C4InfoCore.h:99
Definition: C4Id.h:25
CStdFont BookFont
Definition: C4Startup.h:65
const int C4MaxBigIconSize
Definition: C4Constants.h:61
const char * GetGUIName() const
C4ID idType
StdStrBuf TimeString(int iSeconds)
C4Rect & GetBounds()
Definition: C4Gui.h:445
int32_t GetWidth() const
Definition: C4Gui.h:2800
Icons
Definition: C4Gui.h:637
bool Load(C4Group &hGroup, const char *szFilename, bool fOwnPal, bool fNoErrIfNotFound, int iFlags)
const char * GetError()
Definition: C4Group.cpp:504
void SelectEntry(Element *pNewSel, bool fByUser)
int32_t x
Definition: C4Rect.h:30
bool ShowMessage(const char *szMessage, const char *szCaption, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
void SetTitle(const char *szToTitle)
const C4PlayerInfoCore & GetCore() const
int32_t GamepadGuiControl
Definition: C4Config.h:229
void OnPropertyBtn(C4GUI::Control *btn)
static int32_t GetCustomEditHeight(CStdFont *pUseFont)
Definition: C4GuiEdit.cpp:119
Element * GetNext() const
Definition: C4Gui.h:449
float TargetX
Definition: C4Facet.h:165
void SetFocus(Control *pCtrl, bool fByMouse)
static bool SelectPortrait(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
C4ConfigControls Controls
Definition: C4Config.h:259
#define C4CFN_BigIcon
Definition: C4Components.h:111
const uint32_t ClrPlayerItem
int32_t GetWidthByHeight(int32_t iHeight)
Definition: C4Facet.h:153
const int32_t C4StartupEditBGColor
Definition: C4Startup.h:29
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
void SetVisibility(bool fToValue) override
C4GUI::IconButton * pPictureBtn
void Load(const StdStrBuf &rsFilename)
bool Create(int iWdt, int iHgt, int iWdt2=C4FCT_Full, int iHgt2=C4FCT_Full)
Definition: C4FacetEx.cpp:54
const char * AtUserDataPath(const char *szFilename)
Definition: C4Config.cpp:526
const int ALeft
Definition: C4Surface.h:41
void UpdateCore(C4PlayerInfoCore &NewCore)
Dialog * GetDlg() override
Definition: C4Gui.h:2116
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:374
C4GUI::IconButton * pClrPreview
void OnBackBtn(C4GUI::Control *btn)
bool MoveItem(const char *szSource, const char *szTarget)
Definition: StdFile.cpp:849
void OnClrChangeRight(C4GUI::Control *pBtn)
static C4Startup * Get()
Definition: C4Startup.h:133
int32_t GetMarginTop() override
C4StartupPlrSelDlg_CrewSortDataEntry(int32_t iMaxExp, C4ID idType)
void AddTextLine(const char *szText, CStdFont *pFont, DWORD dwClr, bool fDoUpdate, bool fMakeReadableOnBlack, CStdFont *pCaptionFont=nullptr)
Definition: C4Gui.h:1740
void DrawElement(C4TargetFacet &cgo) override
size_t getLength() const
Definition: StdBuf.h:445
C4GUI::ContextMenu * ContextMenu() override
virtual void DoDragging(CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam)
Definition: C4Gui.cpp:269
void OnNewBtn(C4GUI::Control *btn)
bool ShowModalDlg(Dialog *pDlg, bool fDestruct=true)
virtual Screen * GetScreen()
Definition: C4Gui.cpp:289
void OnPictureBtn(C4GUI::Control *pBtn)
void OnDelBtn(C4GUI::Control *btn)
bool operator()(C4StartupPlrSelDlg_CrewSortDataEntry Check)
int32_t Hgt
Definition: C4Rect.h:30
const char * AtRelativePath(const char *szFilename)
Definition: C4Config.cpp:656
uint32_t RGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
Definition: StdColors.h:22
int32_t GetHeightByWidth(int32_t iWidth)
Definition: C4Facet.h:155
C4Surface * Surface
Definition: C4Facet.h:117
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
float TargetY
Definition: C4Facet.h:165
bool IsColorConflict(DWORD dwClr1, DWORD dwClr2)
void OnActivateBtn(C4GUI::Control *btn)
CStdFont BookFontCapt
Definition: C4Startup.h:65
bool C4Group_DeleteItem(const char *szItem, bool fRecycle)
Definition: C4Group.cpp:194
void SetSelectionInfo(C4GUI::TextWindow *pSelectionInfo) override
uint32_t DWORD
const int C4FCT_Full
Definition: C4FacetEx.h:26
void Copy()
Definition: StdBuf.h:467
float Wdt
Definition: C4Facet.h:118
C4Facet GetPhase(int iPhaseX=0, int iPhaseY=0)
Definition: C4Facet.cpp:59
#define s
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:43
C4PlayerControlAssignmentSets PlayerControlUserAssignmentSets
Definition: C4Game.h:93
void OnClosed(bool commit) override
C4StartupGraphics Graphics
Definition: C4Startup.h:100
bool SAddModule(char *szList, const char *szModule, bool fCaseSensitive)
Definition: Standard.cpp:557
void Clear()
Definition: C4FacetEx.h:44
C4Surface & GetFace()
Definition: C4FacetEx.h:52
void OnCtrlChangeLeft(C4GUI::Control *pBtn)
bool GetFromBottom(int32_t iHgt, int32_t iWdt, C4Rect &rcOut)
Definition: C4Gui.cpp:1109
bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false)
Definition: C4Group.cpp:1585
void SReplaceChar(char *str, char fc, char tc)
Definition: Standard.cpp:348
void OnItemCheckChange(C4GUI::Element *pCheckBox)
void SetText(const char *szToText, bool fAllowHotkey=true)
Definition: C4GuiLabels.cpp:74
void OnSkinChangeLeft(C4GUI::Control *pBtn)
void ClearText(bool fDoUpdate)
Definition: C4Gui.h:1744
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
void SetSelectionDblClickFn(BaseCallbackHandler *pToHandler)
Definition: C4Gui.h:1553