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