/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2013, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ // Startup screen for non-parameterized engine start: Player selection dialog // Also contains player creation, editing and crew management #include #include #include #include #include #include #include #include #include #include #include #include "gui/C4MouseControl.h" #include #include // font clrs const uint32_t ClrPlayerItem = 0xffffffff; // Arbitrary cut-off value for player color value. This avoids pitch black // colors which look ugly. Note that this limit is only applied in the UI, // it's still possible to edit the Player.txt by hand. const uint32_t PlayerColorValueLowBound = 64; // ----- C4Utilities StdStrBuf TimeString(int iSeconds) { int iHours = iSeconds / 3600; iSeconds -= 3600*iHours; int iMinutes = iSeconds / 60; iSeconds -= 60*iMinutes; return FormatString("%02d:%02d:%02d",iHours,iMinutes,iSeconds); } StdStrBuf DateString(int iTime) { if (!iTime) return StdStrBuf("", true); time_t tTime = iTime; //time(&tTime); struct tm *pLocalTime; pLocalTime=localtime(&tTime); return FormatString( "%02d.%02d.%d %02d:%02d", pLocalTime->tm_mday, pLocalTime->tm_mon+1, pLocalTime->tm_year+1900, pLocalTime->tm_hour, pLocalTime->tm_min); } // ------------------------------------------------ // --- C4StartupPlrSelDlg::ListItem C4StartupPlrSelDlg::ListItem::ListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement, bool fActivated) : Control(C4Rect(0,0,0,0)), pCheck(NULL), pNameLabel(NULL), pPlrSelDlg(pForDlg), pIcon(NULL) { CStdFont &rUseFont = GraphicsResource.FontRegular; // calc height int32_t iHeight = rUseFont.GetLineHeight() + 2 * IconLabelSpacing; // create subcomponents pCheck = new C4GUI::CheckBox(C4Rect(0, 0, iHeight, iHeight), NULL, fActivated); pCheck->SetOnChecked(new C4GUI::CallbackHandler(pForDlg, &C4StartupPlrSelDlg::OnItemCheckChange)); pKeyCheck = new C4KeyBinding(C4KeyCodeEx(K_SPACE), "StartupPlrSelTogglePlayerActive", KEYSCOPE_Gui, new C4GUI::ControlKeyCB(*this, &ListItem::KeyCheck), C4CustomKey::PRIO_Ctrl); pIcon = new C4GUI::Icon(C4Rect(iHeight + IconLabelSpacing, 0, iHeight, iHeight), C4GUI::Ico_Player); pNameLabel = new C4GUI::Label("Q", (iHeight + IconLabelSpacing)*2, IconLabelSpacing, ALeft, ClrPlayerItem, &rUseFont, false, false); pNameLabel->SetAutosize(false); // calc own bounds - use icon bounds only, because only the height is used when the item is added SetBounds(pIcon->GetBounds()); // add components AddElement(pCheck); AddElement(pIcon); AddElement(pNameLabel); // add to listbox (will get resized horizontally and moved) - zero indent; no tree structure in this dialog pForListBox->InsertElement(this, pInsertBeforeElement, 0); // update name label width to stretch max listbox width C4Rect rcNameLabelBounds = pNameLabel->GetBounds(); rcNameLabelBounds.Wdt = GetClientRect().Wdt - rcNameLabelBounds.x - IconLabelSpacing; pNameLabel->SetBounds(rcNameLabelBounds); // context menu SetContextHandler(new C4GUI::CBContextHandler(this, &C4StartupPlrSelDlg::ListItem::ContextMenu)); } C4StartupPlrSelDlg::ListItem::~ListItem() { delete pKeyCheck; } const char *C4StartupPlrSelDlg::ListItem::GetName() const { // name is stored in label only return pNameLabel->GetText(); } void C4StartupPlrSelDlg::ListItem::SetName(const char *szNewName) { // update name in label pNameLabel->SetText(szNewName); // tooltip by name, so long names can be read via tooltip SetToolTip(szNewName); } void C4StartupPlrSelDlg::ListItem::GrabIcon(C4FacetSurface &rFromFacet) { // take over icon gfx from facet - deletes them from source facet! if (rFromFacet.Surface) { pIcon->GetMFacet().GrabFrom(rFromFacet); } else { // reset custom icon // following update-call will reset to default icon pIcon->GetMFacet().Clear(); } } void C4StartupPlrSelDlg::ListItem::SetIcon(C4GUI::Icons icoNew) { pIcon->SetIcon(icoNew); } void C4StartupPlrSelDlg::ListItem::UpdateOwnPos() { // parent for client rect typedef C4GUI::Window ParentClass; ParentClass::UpdateOwnPos(); // reposition items C4GUI::ComponentAligner caBounds(GetContainedClientRect(), IconLabelSpacing, IconLabelSpacing); // nothing to reposition for now... } void C4StartupPlrSelDlg::ListItem::SetFilename(const StdStrBuf &sNewFN) { // just set fn - UpdateCore-call will follow later Filename.Copy(sNewFN); } bool C4StartupPlrSelDlg::ListItem::CheckNameHotkey(const char * c) { // return whether this item can be selected by entering given char: // first char of name must match // FIXME: Unicode if (!pNameLabel) return false; const char *szName = pNameLabel->GetText(); return szName && (toupper(*szName) == toupper(c[0])); } // ------------------------------------------------ // --- C4StartupPlrSelDlg::PlayerListItem C4StartupPlrSelDlg::PlayerListItem::PlayerListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, C4GUI::Element *pInsertBeforeElement, bool fActivated) : ListItem(pForDlg, pForListBox, pInsertBeforeElement, fActivated), fHasCustomIcon(false) { } void C4StartupPlrSelDlg::PlayerListItem::Load(const StdStrBuf &rsFilename) { int32_t iHeight = GetBounds().Hgt; // backup filename SetFilename(rsFilename); // load player info C4Group PlrGroup; if (!PlrGroup.Open(Config.AtUserDataPath(rsFilename.getData()))) throw LoadError(FormatString("Error loading player file from %s: Error opening group: %s", rsFilename.getData(), PlrGroup.GetError())); if (!Core.Load(PlrGroup)) throw LoadError(FormatString("Error loading player file from %s: Core data invalid or missing (Group: %s)!", rsFilename.getData(), PlrGroup.GetError())); // load icon C4FacetSurface fctIcon; if (PlrGroup.FindEntry(C4CFN_BigIcon) && fctIcon.Load(PlrGroup, C4CFN_BigIcon)) fHasCustomIcon = true; else { // no custom icon: create default by player color fctIcon.Create(iHeight,iHeight); ::GraphicsResource.fctPlayerClr.DrawClr(fctIcon, true, Core.PrefColorDw); } GrabIcon(fctIcon); // done loading if (!PlrGroup.Close()) throw LoadError(FormatString("Error loading player file from %s: Error closing group: %s", rsFilename.getData(), PlrGroup.GetError())); // default name if (!*Core.PrefName) SCopy(GetFilenameOnly(rsFilename.getData()), Core.PrefName, sizeof(Core.PrefName)-1); SetName(Core.PrefName); } C4GUI::ContextMenu *C4StartupPlrSelDlg::PlayerListItem::ContextMenu() { // menu operations work on selected item only pPlrSelDlg->SetSelection(this); // context menu operations C4GUI::ContextMenu *pCtx = new C4GUI::ContextMenu(); pCtx->AddItem(LoadResStr("IDS_BTN_PROPERTIES"), LoadResStr("IDS_DLGTIP_PLAYERPROPERTIES"), C4GUI::Ico_None, new C4GUI::CBMenuHandler(pPlrSelDlg, &C4StartupPlrSelDlg::OnPropertyCtx)); pCtx->AddItem(LoadResStr("IDS_BTN_DELETE"), LoadResStr("IDS_DLGTIP_PLAYERDELETE"), C4GUI::Ico_None, new C4GUI::CBMenuHandler(pPlrSelDlg, &C4StartupPlrSelDlg::OnDelCtx)); return pCtx; } void C4StartupPlrSelDlg::PlayerListItem::GrabCustomIcon(C4FacetSurface &fctGrabFrom) { // set flag fHasCustomIcon = !!fctGrabFrom.Surface; // base class grab GrabIcon(fctGrabFrom); } void C4StartupPlrSelDlg::PlayerListItem::UpdateCore(C4PlayerInfoCore & NewCore) { C4Group PlrGroup; if (!PlrGroup.Open(Config.AtUserDataPath(GetFilename().getData())) || !NewCore.Save(PlrGroup) || !PlrGroup.Close()) { GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_MODIFY"), "", C4GUI::Ico_Error); return; } Core = NewCore; SetName(Core.PrefName); // re-set non-custom icons if (!fHasCustomIcon) { fHasCustomIcon = false; int32_t iHeight = GetBounds().Hgt; C4FacetSurface fctIcon; fctIcon.Create(iHeight,iHeight); ::GraphicsResource.fctPlayerClr.DrawClr(fctIcon, true, Core.PrefColorDw); GrabIcon(fctIcon); } // update in selection C4StartupPlrSelDlg *pDlg = static_cast(GetDlg()); if (pDlg && pDlg->GetSelection() == this) pDlg->UpdateSelection(); } void C4StartupPlrSelDlg::PlayerListItem::SetSelectionInfo(C4GUI::TextWindow *pSelectionInfo) { // write info text for player pSelectionInfo->ClearText(false); //pSelectionInfo->AddTextLine(FormatString("%s %s", Core.RankName, Core.PrefName).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false); pSelectionInfo->AddTextLine(FormatString("%s", Core.PrefName).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false); 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); if (Core.LastRound.Title[0]) 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); pSelectionInfo->UpdateHeight(); } StdStrBuf C4StartupPlrSelDlg::PlayerListItem::GetDelWarning() { StdStrBuf sWarning; sWarning.Format(LoadResStr("IDS_MSG_DELETEPLR"), Core.PrefName); int32_t iPlrTime = Core.TotalPlayingTime; if (iPlrTime > 60*60*10) sWarning.Append(FormatString(LoadResStr("IDS_MSG_DELETEPLR_PLAYTIME"), TimeString(iPlrTime).getData()).getData()); return sWarning; } bool C4StartupPlrSelDlg::PlayerListItem::MoveFilename(const char *szToFilename) { // anything to do? if (ItemIdentical(GetFilename().getData(), szToFilename)) return true; // do it StdStrBuf PathFrom(Config.General.UserDataPath); PathFrom.Append(GetFilename()); StdStrBuf PathTo(Config.General.UserDataPath); PathTo.Append(szToFilename); if (!MoveItem(PathFrom.getData(), PathTo.getData())) return false; // reflect change in class SetFilename(StdStrBuf(szToFilename)); return true; } // ------------------------------------------------ // --- C4StartupPlrSelDlg::CrewListItem C4StartupPlrSelDlg::CrewListItem::CrewListItem(C4StartupPlrSelDlg *pForDlg, C4GUI::ListBox *pForListBox, uint32_t dwPlrClr) : ListItem(pForDlg, pForListBox, NULL, false), fLoaded(false), dwPlrClr(dwPlrClr), pParentGrp(NULL) { SetIcon(C4GUI::Ico_Wait); } void C4StartupPlrSelDlg::CrewListItem::UpdateClonkEnabled() { if (!fLoaded) return; Core.Participation = pCheck->GetChecked(); // immediate save of changes RewriteCore(); } void C4StartupPlrSelDlg::CrewListItem::Load(C4Group &rGrp, const StdStrBuf &rsFilename) { // backup filename (doesn't include path) SetFilename(rsFilename); // load core C4Group CrewGroup; if (!CrewGroup.OpenAsChild(&rGrp, rsFilename.getData())) throw LoadError(FormatString("Error loading crew file from %s in %s: Error opening group: %s", rsFilename.getData(), rGrp.GetFullName().getData(), CrewGroup.GetError())); if (!Core.Load(CrewGroup)) throw LoadError(FormatString("Error loading crew file from %s: Core data invalid or missing (Group: %s)!", CrewGroup.GetFullName().getData(), CrewGroup.GetError())); ListItem::SetName(Core.Name); pCheck->SetChecked(!!Core.Participation); // load rank as icon C4FacetSurface fctIcon; if (fctIcon.Load(CrewGroup, C4CFN_ClonkRank, C4FCT_Full, C4FCT_Full, false, true)) { GrabIcon(fctIcon); } else { // no custom icon: create default by rank system if (C4RankSystem::DrawRankSymbol(&fctIcon, Core.Rank, &::GraphicsResource.fctRank, ::GraphicsResource.iNumRanks, true)) GrabIcon(fctIcon); } // backup group loaded from - assumes it stays valid! pParentGrp = &rGrp; // load success! fLoaded=true; } C4GUI::ContextMenu *C4StartupPlrSelDlg::CrewListItem::ContextMenu() { // menu operations work on selected item only pPlrSelDlg->SetSelection(this); // context menu operations C4GUI::ContextMenu *pCtx = new C4GUI::ContextMenu(); pCtx->AddItem(LoadResStr("IDS_BTN_RENAME"), LoadResStr("IDS_DESC_CREWRENAME"), C4GUI::Ico_None, new C4GUI::CBMenuHandler(pPlrSelDlg, &C4StartupPlrSelDlg::OnPropertyCtx)); pCtx->AddItem(LoadResStr("IDS_BTN_DELETE"), LoadResStr("IDS_MSG_DELETECLONK_DESC"), C4GUI::Ico_None, new C4GUI::CBMenuHandler(pPlrSelDlg, &C4StartupPlrSelDlg::OnDelCtx)); pCtx->AddItem(LoadResStr("IDS_MSG_SETDEATHMESSAGE"), LoadResStr("IDS_MSG_SETTHEMESSAGETHATAPPEARWH"), C4GUI::Ico_None, new C4GUI::CBMenuHandler(this, &C4StartupPlrSelDlg::CrewListItem::OnDeathMessageCtx)); return pCtx; } void C4StartupPlrSelDlg::CrewListItem::OnDeathMessageCtx(C4GUI::Element *el) { // Death message dialog C4GUI::InputDialog *pDlg; GetScreen()->ShowRemoveDlg(pDlg=new C4GUI::InputDialog(LoadResStr("IDS_MSG_ENTERNEWDEATHMESSAGE"), LoadResStr("IDS_MSG_SETDEATHMESSAGE"), C4GUI::Ico_Ex_Comment, new C4GUI::InputCallback(this, &C4StartupPlrSelDlg::CrewListItem::OnDeathMessageSet), false)); pDlg->SetMaxText(C4MaxDeathMsg); pDlg->SetInputText(Core.DeathMessage); } void C4StartupPlrSelDlg::CrewListItem::OnDeathMessageSet(const StdStrBuf &rsNewMessage) { // copy msg if (!rsNewMessage) *Core.DeathMessage='\0'; else SCopy(rsNewMessage.getData(), Core.DeathMessage, C4MaxDeathMsg); // save RewriteCore(); // acoustic feedback C4GUI::GUISound("Connect"); } void C4StartupPlrSelDlg::CrewListItem::RewriteCore() { if (!fLoaded) return; C4Group CrewGroup; if (!CrewGroup.OpenAsChild(pParentGrp, GetFilename().getData()) || !Core.Save(CrewGroup, NULL) || !CrewGroup.Close() || !pParentGrp->Save(true)) { GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_MODIFY"), "", C4GUI::Ico_Error); return; } } bool C4StartupPlrSelDlg::CrewListItem::SetName(const char *szNewName) { if (!fLoaded) return false; // validate name if (!szNewName || !*szNewName) return false; if (SEqual(szNewName, Core.Name)) return true; // generate filename from new name char fn[_MAX_PATH+1]; SCopy(szNewName, fn, _MAX_PATH); MakeFilenameFromTitle(fn); if (!*fn) return false; SAppend(".oci", fn, _MAX_PATH); // check if a rename is due if (!ItemIdentical(fn, GetFilename().getData())) { // check for duplicate filename if (pParentGrp->FindEntry(fn)) { StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_CLONKCOLLISION"), fn); ::pGUI->ShowMessageModal(sMsg.getData(), LoadResStr("IDS_FAIL_RENAME"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error); return false; } // OK; then rename if (!pParentGrp->Rename(GetFilename().getData(), fn) || !pParentGrp->Save(true)) { StdStrBuf sMsg; sMsg.Format(LoadResStr("IDS_ERR_RENAMEFILE"), GetFilename().getData(), fn); ::pGUI->ShowMessageModal(sMsg.getData(), LoadResStr("IDS_FAIL_RENAME"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Error); return false; } const char *szConstFn = fn; SetFilename(StdStrBuf(szConstFn)); } // update clonk name and core ListItem::SetName(szNewName); SCopy(szNewName, Core.Name, C4MaxName); RewriteCore(); return true; } void C4StartupPlrSelDlg::CrewListItem::SetSelectionInfo(C4GUI::TextWindow *pSelectionInfo) { // write info text for player pSelectionInfo->ClearText(false); pSelectionInfo->AddTextLine(FormatString("%s %s", Core.sRankName.getData(), Core.Name).getData(), &C4Startup::Get()->Graphics.BookFontCapt, ClrPlayerItem, false, false); StdStrBuf sPromo; int32_t iNextRankExp; StdStrBuf sNextRankName; if (Core.GetNextRankInfo(::DefaultRanks, &iNextRankExp, &sNextRankName)) sPromo.Format(LoadResStr("IDS_DESC_PROMO"),sNextRankName.getData(),(int) iNextRankExp); else sPromo.Copy(LoadResStr("IDS_DESC_NOPROMO")); pSelectionInfo->AddTextLine(FormatString(LoadResStr("IDS_DESC_OBJECT"), Core.TypeName, Core.Experience, Core.Rounds, Core.DeathCount, sPromo.getData(), TimeString(Core.TotalPlayingTime).getData(), DateString(Core.Birthday).getData()).getData(), &C4Startup::Get()->Graphics.BookFont, ClrPlayerItem, false, false); pSelectionInfo->UpdateHeight(); } StdStrBuf C4StartupPlrSelDlg::CrewListItem::GetDelWarning() { StdStrBuf sWarning; sWarning.Format(LoadResStr("IDS_MSG_DELETECLONK"), Core.sRankName.getData(), Core.Name, GetFilename().getData()); int32_t iPlrTime = Core.TotalPlayingTime; if (iPlrTime > 60*60*10) sWarning.Append(static_cast(FormatString(LoadResStr("IDS_MSG_DELETECLONK_PLAYTIME"), TimeString(iPlrTime).getData()))); return sWarning; } void C4StartupPlrSelDlg::CrewListItem::CrewRename() { if (pPlrSelDlg->pRenameEdit) return; // rename this entry pPlrSelDlg->pRenameEdit = new C4GUI::CallbackRenameEdit(pNameLabel, this, RenameParams(), &C4StartupPlrSelDlg::CrewListItem::DoRenaming, &C4StartupPlrSelDlg::CrewListItem::AbortRenaming); } void C4StartupPlrSelDlg::CrewListItem::AbortRenaming(RenameParams par) { // no renaming pPlrSelDlg->pRenameEdit = NULL; } C4GUI::RenameEdit::RenameResult C4StartupPlrSelDlg::CrewListItem::DoRenaming(RenameParams par, const char *szNewName) { // accept if name can be set; will fail if name is invalid or already given to another Crew member if (!SetName(szNewName)) return C4GUI::RenameEdit::RR_Invalid; pPlrSelDlg->pRenameEdit = NULL; // update in selection C4StartupPlrSelDlg *pDlg = static_cast(GetDlg()); if (pDlg && pDlg->GetSelection() == this) pDlg->UpdateSelection(); return C4GUI::RenameEdit::RR_Accepted; } // ------------------------------------------------ // --- C4StartupPlrSelDlg C4StartupPlrSelDlg::C4StartupPlrSelDlg() : C4StartupDlg("W"), eMode(PSDM_Player), pRenameEdit(NULL) { // ctor UpdateSize(); // for clientrect // screen calculations int iButtonHeight = C4GUI_ButtonHgt; int iButtonXSpacing = (GetClientRect().Wdt > 700) ? GetClientRect().Wdt/58 : 2; int iButtonCount = 6; C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true); C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(Max(caMain.GetHeight()/15, iButtonHeight)),0,0); rcBottomButtons = caButtonArea.GetCentered(caMain.GetWidth(), iButtonHeight); iBottomButtonWidth = (caButtonArea.GetWidth() - iButtonXSpacing * (iButtonCount-1)) / iButtonCount; C4Rect rcMain = caMain.GetAll(); C4Rect rcPlrList = C4Rect(rcMain.Wdt/8, rcMain.Hgt/8, rcMain.Wdt*5/16, rcMain.Hgt*6/8); C4Rect rcInfoWindow = C4Rect(rcMain.Wdt*9/16, rcMain.Hgt/8, rcMain.Wdt*5/16, rcMain.Hgt*6/8); AddElement(pPlrListBox = new C4GUI::ListBox(rcPlrList)); pPlrListBox->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERFILES")); pPlrListBox->SetDecoration(true, &C4Startup::Get()->Graphics.sfctBookScroll, true); pPlrListBox->UpdateElementPositions(); pPlrListBox->SetSelectionChangeCallbackFn(new C4GUI::CallbackHandler(this, &C4StartupPlrSelDlg::OnSelChange)); pPlrListBox->SetSelectionDblClickFn(new C4GUI::CallbackHandler(this, &C4StartupPlrSelDlg::OnSelDblClick)); AddElement(pSelectionInfo = new C4GUI::TextWindow(rcInfoWindow,0,0,0,100,4096," ",false,NULL,0,true)); pSelectionInfo->SetDecoration(true, true, &C4Startup::Get()->Graphics.sfctBookScroll, true); pSelectionInfo->UpdateHeight(); // bottom line buttons - positioning done in UpdateBottomButtons by UpdatePlayerList C4Rect rcDefault(0,0,10,10); AddElement(btnBack = new C4GUI::CallbackButton(LoadResStr("IDS_BTN_BACK"), rcDefault, &C4StartupPlrSelDlg::OnBackBtn)); AddElement(btnNew = new C4GUI::CallbackButton(LoadResStr("IDS_BTN_NEW"), rcDefault, &C4StartupPlrSelDlg::OnNewBtn)); btnNew->SetToolTip(LoadResStr("IDS_DLGTIP_NEWPLAYER")); AddElement(btnActivatePlr = new C4GUI::CallbackButton(NULL, rcDefault, &C4StartupPlrSelDlg::OnActivateBtn)); AddElement(btnDelete = new C4GUI::CallbackButton(LoadResStr("IDS_BTN_DELETE"), rcDefault, &C4StartupPlrSelDlg::OnDelBtn)); AddElement(btnProperties = new C4GUI::CallbackButton(NULL, rcDefault, &C4StartupPlrSelDlg::OnPropertyBtn)); AddElement(btnCrew = new C4GUI::CallbackButton(LoadResStr("IDS_SELECT_CREW"), rcDefault, &C4StartupPlrSelDlg::OnCrewBtn)); btnCrew->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERCREW")); // refill listboxes UpdatePlayerList(); // Just safety incase listbox was empty, in which case no selection change callback will have been done: // Update current listbox selection to that of no selected item if (!pPlrListBox->GetFirst()) UpdateSelection(); // initial focus on player list SetFocus(pPlrListBox, false); // key bindings C4CustomKey::CodeList keys; keys.push_back(C4KeyCodeEx(K_BACK)); keys.push_back(C4KeyCodeEx(K_LEFT)); keys.push_back(C4KeyCodeEx(K_ESCAPE)); if (Config.Controls.GamepadGuiControl) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton))); } pKeyBack = new C4KeyBinding(keys, "StartupPlrSelBack", KEYSCOPE_Gui, new C4GUI::DlgKeyCB(*this, &C4StartupPlrSelDlg::KeyBack), C4CustomKey::PRIO_CtrlOverride); pKeyProperties = new C4KeyBinding(C4KeyCodeEx(K_F2), "StartupPlrSelProp", KEYSCOPE_Gui, new C4GUI::DlgKeyCB(*this, &C4StartupPlrSelDlg::KeyProperties), C4CustomKey::PRIO_CtrlOverride); pKeyCrew = new C4KeyBinding(C4KeyCodeEx(K_RIGHT), "StartupPlrSelCrew", KEYSCOPE_Gui, new C4GUI::ControlKeyDlgCB(pPlrListBox, *this, &C4StartupPlrSelDlg::KeyCrew), C4CustomKey::PRIO_CtrlOverride); pKeyDelete = new C4KeyBinding(C4KeyCodeEx(K_DELETE), "StartupPlrSelDelete", KEYSCOPE_Gui, new C4GUI::DlgKeyCB(*this, &C4StartupPlrSelDlg::KeyDelete), C4CustomKey::PRIO_CtrlOverride); pKeyNew = new C4KeyBinding(C4KeyCodeEx(K_INSERT), "StartupPlrSelNew", KEYSCOPE_Gui, new C4GUI::DlgKeyCB(*this, &C4StartupPlrSelDlg::KeyNew), C4CustomKey::PRIO_CtrlOverride); } C4StartupPlrSelDlg::~C4StartupPlrSelDlg() { delete pKeyDelete; delete pKeyCrew; delete pKeyProperties; delete pKeyBack; delete pKeyNew; } void C4StartupPlrSelDlg::AbortRenaming() { if (pRenameEdit) pRenameEdit->Abort(); } void C4StartupPlrSelDlg::DrawElement(C4TargetFacet &cgo) { // draw background typedef C4GUI::FullscreenDialog Base; Base::DrawElement(cgo); } void C4StartupPlrSelDlg::UpdateBottomButtons() { // bottom line buttons depend on list mode C4GUI::ComponentAligner caBottomButtons(rcBottomButtons,0,0); switch (eMode) { case PSDM_Player: { // update some buttons for player mode btnProperties->SetText(LoadResStr("IDS_BTN_PROPERTIES")); btnProperties->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERPROPERTIES")); btnNew->SetVisibility(true); btnCrew->SetVisibility(true); btnDelete->SetToolTip(LoadResStr("IDS_DLGTIP_PLAYERDELETE")); btnBack->SetToolTip(LoadResStr("IDS_DLGTIP_BACKMAIN")); btnBack ->SetBounds(caBottomButtons.GetGridCell(0,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnNew ->SetBounds(caBottomButtons.GetGridCell(1,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnActivatePlr->SetBounds(caBottomButtons.GetGridCell(2,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnDelete ->SetBounds(caBottomButtons.GetGridCell(3,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnProperties ->SetBounds(caBottomButtons.GetGridCell(4,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnCrew ->SetBounds(caBottomButtons.GetGridCell(5,6,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); } break; case PSDM_Crew: { // update some buttons for player mode btnProperties->SetText(LoadResStr("IDS_BTN_RENAME")); btnProperties->SetToolTip(LoadResStr("IDS_DESC_CREWRENAME")); btnNew->SetVisibility(false); btnCrew->SetVisibility(false); btnDelete->SetToolTip(LoadResStr("IDS_MSG_DELETECLONK_DESC")); btnBack->SetToolTip(LoadResStr("IDS_MSG_BACKTOPLAYERDLG")); btnBack ->SetBounds(caBottomButtons.GetGridCell(0,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnActivatePlr->SetBounds(caBottomButtons.GetGridCell(1,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnDelete ->SetBounds(caBottomButtons.GetGridCell(2,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); btnProperties ->SetBounds(caBottomButtons.GetGridCell(3,4,0,1,iBottomButtonWidth,C4GUI_ButtonHgt,true)); } break; }; } void C4StartupPlrSelDlg::UpdatePlayerList() { // something has changed! AbortRenaming(); // refill pPlrListBox with players in player folder or crew // clear old items C4GUI::Element *pEl; while ((pEl = pPlrListBox->GetFirst())) delete pEl; // update command buttons UpdateBottomButtons(); // create new switch (eMode) { case PSDM_Player: { SetTitle(LoadResStrNoAmp("IDS_DLG_PLAYERSELECTION")); // player mode: insert all players const char *szFn; StdStrBuf sSearchPath(Config.General.UserDataPath); PlayerListItem *pFirstActivatedPlrItem=NULL, *pFirstDeactivatedPlrItem=NULL, *pPlrItem=NULL; for (DirectoryIterator i(sSearchPath.getData()); (szFn=*i); i++) { szFn = Config.AtRelativePath(szFn); if (*GetFilename(szFn) == '.') continue; // ignore ".", ".." and private files (".*") if (!WildcardMatch(C4CFN_PlayerFiles, GetFilename(szFn))) continue; bool fIsParticipating = !!SIsModule(Config.General.Participants, szFn, NULL, false); pPlrItem = new PlayerListItem(this, pPlrListBox, NULL, fIsParticipating); try { pPlrItem->Load(StdStrBuf(szFn)); } catch (ListItem::LoadError & e) { // invalid player: ignore but log error message DebugLog(e.getData()); delete pPlrItem; continue; } if (fIsParticipating) { if (!pFirstActivatedPlrItem) pFirstActivatedPlrItem = pPlrItem; } else if (!pFirstDeactivatedPlrItem) pFirstDeactivatedPlrItem = pPlrItem; } // select first element; prefer activated player if (!(pPlrItem = pFirstActivatedPlrItem)) pPlrItem = pFirstDeactivatedPlrItem; if (pPlrItem) pPlrListBox->SelectEntry(pPlrItem, false); // re-validate Game.PlayerFilename UpdateActivatedPlayers(); break; } case PSDM_Crew: { SetTitle(FormatString("%s %s", LoadResStrNoAmp("IDS_CTL_CREW"), CurrPlayer.Core.PrefName).getData()); // crew mode: Insert complete crew of player (2do: sort) bool fSucc; char szFn[_MAX_PATH+1]; for (fSucc=CurrPlayer.Grp.FindEntry(C4CFN_ObjectInfoFiles, szFn); fSucc; fSucc=CurrPlayer.Grp.FindNextEntry(C4CFN_ObjectInfoFiles, szFn, NULL, true)) { CrewListItem *pCrewItem = new CrewListItem(this, pPlrListBox, CurrPlayer.Core.PrefColorDw); try { pCrewItem->Load(CurrPlayer.Grp, StdStrBuf(szFn)); } catch (ListItem::LoadError & e) { // invalid player: ignore but log error message DebugLog(e.getData()); delete pCrewItem; continue; } } // resort crew by type and experience ResortCrew(); pPlrListBox->SelectFirstEntry(false); break; } } } C4StartupPlrSelDlg::ListItem *C4StartupPlrSelDlg::GetSelection() { // get selected item: There's only instances of PlrListItem in this list return static_cast(pPlrListBox->GetSelectedItem()); } void C4StartupPlrSelDlg::SetSelection(ListItem *pNewItem) { // update selection in listbox pPlrListBox->SelectEntry(pNewItem, false); } void C4StartupPlrSelDlg::UpdateSelection() { // something has changed! AbortRenaming(); // get currently selected player ListItem *pSel = GetSelection(); // button text 'activate' if current player is deactivated; 'deactivate' otherwise if (pSel && pSel->IsActivated()) { btnActivatePlr->SetText(LoadResStr("IDS_BTN_DEACTIVATE")); btnActivatePlr->SetToolTip(FormatString(LoadResStr("IDS_MSG_NOPARTICIPATE_DESC"), pSel->GetName()).getData()); } else { btnActivatePlr->SetText(LoadResStr("IDS_BTN_ACTIVATE")); btnActivatePlr->SetToolTip(FormatString(LoadResStr("IDS_MSG_PARTICIPATE_DESC"), pSel ? pSel->GetName() : "").getData()); } // no item selected? if (!pSel) { pSelectionInfo->ClearText(true); // 2do: disable buttons return; } // info text for selection pSel->SetSelectionInfo(pSelectionInfo); } void C4StartupPlrSelDlg::OnItemCheckChange(C4GUI::Element *pCheckBox) { switch (eMode) { case PSDM_Player: // update Config.General.Participants UpdateActivatedPlayers(); break; case PSDM_Crew: // update affected crew item if (pCheckBox) static_cast(pCheckBox->GetParent())->UpdateClonkEnabled(); break; } // update player selection text UpdateSelection(); } void C4StartupPlrSelDlg::UpdateActivatedPlayers() { assert(eMode == PSDM_Player); // refill Config.General.Participants-list *Config.General.Participants = '\0'; for (ListItem *pPlrItem = static_cast(pPlrListBox->GetFirst()); pPlrItem; pPlrItem = pPlrItem->GetNext()) if (pPlrItem->IsActivated()) { const char *szAddFilename = pPlrItem->GetFilename().getData(); if (std::strlen(Config.General.Participants) + 1 + std::strlen(szAddFilename) < sizeof(Config.General.Participants)) SAddModule(Config.General.Participants, szAddFilename); else { pPlrItem->SetActivated(false); GetScreen()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLAYERSTOOLONG"), pPlrItem->GetName()).getData(), LoadResStr("IDS_ERR_TITLE"), C4GUI::Ico_Error); } } } void C4StartupPlrSelDlg::OnActivateBtn(C4GUI::Control *btn) { // toggle activation state of current item // get currently selected player ListItem *pSel = GetSelection(); if (!pSel) return; pSel->SetActivated(!pSel->IsActivated()); // update stuff OnItemCheckChange(NULL); } void C4StartupPlrSelDlg::DoBack() { switch (eMode) { case PSDM_Player: { // back 2 main C4Startup::Get()->SwitchDialog(C4Startup::SDID_Back); break; } case PSDM_Crew: // back 2 player list SetPlayerMode(); break; } } void C4StartupPlrSelDlg::OnNewBtn(C4GUI::Control *btn) { if (eMode != PSDM_Player) return; C4GUI::Dialog *pDlg; GetScreen()->ShowRemoveDlg(pDlg=new C4StartupPlrPropertiesDlg(NULL, this)); pDlg->SetPos(Min(GetBounds().Wdt/10, GetBounds().Wdt - pDlg->GetBounds().Wdt), Min(GetBounds().Hgt/4, GetBounds().Hgt - pDlg->GetBounds().Hgt)); } bool C4StartupPlrSelDlg::CheckPlayerName(const StdStrBuf &Playername, StdStrBuf &Filename, const StdStrBuf *pPrevFilename, bool fWarnEmpty) { // must not be empty if (!Playername.getLength()) { if (fWarnEmpty) C4GUI::Screen::GetScreenS()->ShowMessage(LoadResStr("IDS_ERR_PLRNAME_EMPTY"), "", C4GUI::Ico_Error); return false; } // generate valid filename Filename.Copy(Playername); // Slashes in Filenames are no good SReplaceChar(Filename.getMData(), '\\', '_'); SReplaceChar(Filename.getMData(), '/', '_'); SReplaceChar(Filename.getMData(), ':', '_'); SReplaceChar(Filename.getMData(), '*', '_'); SReplaceChar(Filename.getMData(), '?', '_'); SReplaceChar(Filename.getMData(), '"', '_'); SReplaceChar(Filename.getMData(), '<', '_'); SReplaceChar(Filename.getMData(), '>', '_'); SReplaceChar(Filename.getMData(), '|', '_'); if (*Filename.getData() == '.') *Filename.getMData() = '_'; Filename.Append(".ocp"); StdStrBuf Path(Config.General.UserDataPath); // start at local path Path.Append(Filename); // validity check: Must not exist yet if renamed if (!pPrevFilename || !ItemIdentical(Path.getData(), Config.AtUserDataPath(pPrevFilename->getData()))) if (ItemExists(Path.getData())) { C4GUI::Screen::GetScreenS()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLRNAME_TAKEN"), Playername.getData()).getData(), "", C4GUI::Ico_Error); return false; } return true; } void C4StartupPlrSelDlg::OnCrewBtn(C4GUI::Control *btn) { // display crew for activated player if (eMode != PSDM_Player) return; PlayerListItem *pSel = static_cast(GetSelection()); if (!pSel) return; SetCrewMode(pSel); } void C4StartupPlrSelDlg::SetPlayerMode() { // change view to listing players C4GUI::GUISound("DoorClose"); StdStrBuf LastPlrFilename; LastPlrFilename.Copy(static_cast(CurrPlayer.Grp.GetFullName())); CurrPlayer.Grp.Close(); eMode = PSDM_Player; UpdatePlayerList(); SelectItem(LastPlrFilename, false); UpdateSelection(); } void C4StartupPlrSelDlg::SetCrewMode(PlayerListItem *pSel) { // change view to listing crew of a player CurrPlayer.Core = pSel->GetCore(); StdStrBuf Path(Config.General.UserDataPath); // start at local path // Path.Append(Config.General.PlayerPath); Path.Append(pSel->GetFilename()); if (!CurrPlayer.Grp.Open(Path.getData())) return; if (!CurrPlayer.Grp.FindEntry(C4CFN_ObjectInfoFiles)) { StdCopyStrBuf strCrew(FormatString("%s %s", LoadResStrNoAmp("IDS_CTL_CREW"), CurrPlayer.Core.PrefName)); // player has no crew! GetScreen()->ShowMessage(FormatString(LoadResStr("IDS_ERR_PLRNOCREW"), CurrPlayer.Core.PrefName).getData(), strCrew.getData(), C4GUI::Ico_Player); return; } C4GUI::GUISound("DoorOpen"); eMode = PSDM_Crew; UpdatePlayerList(); UpdateSelection(); } void C4StartupPlrSelDlg::OnDelBtn(C4GUI::Control *btn) { // something has changed! AbortRenaming(); // delete selected player ListItem *pSel = GetSelection(); if (!pSel) return; StdStrBuf sWarning; sWarning.Take(pSel->GetDelWarning()); GetScreen()->ShowRemoveDlg(new C4GUI::ConfirmationDialog(sWarning.getData(), LoadResStr("IDS_BTN_DELETE"), new C4GUI::CallbackHandlerExPar(this, &C4StartupPlrSelDlg::OnDelBtnConfirm, pSel), C4GUI::MessageDialog::btnYesNo)); } void C4StartupPlrSelDlg::OnDelBtnConfirm(ListItem *pSel) { StdStrBuf Path; switch (eMode) { case PSDM_Player: Path.Append(Config.General.UserDataPath); // start at local path Path.Append(pSel->GetFilename()); if (!C4Group_DeleteItem(Path.getData())) { StdStrBuf sMsg; sMsg.Copy(LoadResStr("IDS_FAIL_DELETE")); GetScreen()->ShowMessage(sMsg.getData(), LoadResStr("IDS_DLG_CLEAR"), C4GUI::Ico_Error); } break; case PSDM_Crew: if (!CurrPlayer.Grp.DeleteEntry(pSel->GetFilename().getData())) { StdStrBuf sMsg; sMsg.Copy(LoadResStr("IDS_FAIL_DELETE")); GetScreen()->ShowMessage(sMsg.getData(), LoadResStr("IDS_DLG_CLEAR"), C4GUI::Ico_Error); } break; } // update buttons 'n stuff UpdatePlayerList(); } void C4StartupPlrSelDlg::SelectItem(const StdStrBuf &Filename, bool fActivate) { // find item for (ListItem *pPlrItem = static_cast(pPlrListBox->GetFirst()); pPlrItem; pPlrItem = pPlrItem->GetNext()) if (ItemIdentical(pPlrItem->GetFilename().getData(), Filename.getData())) { // select it pPlrListBox->SelectEntry(pPlrItem, false); // activate it if (fActivate) { pPlrItem->SetActivated(true); // player activation updates OnItemCheckChange(NULL); } // max one return; } } void C4StartupPlrSelDlg::OnPropertyBtn(C4GUI::Control *btn) { // something has changed! AbortRenaming(); switch (eMode) { case PSDM_Player: { // show property dialog for selected player PlayerListItem *pSel = static_cast(GetSelection()); if (!pSel) return; C4GUI::Dialog *pDlg; GetScreen()->ShowRemoveDlg(pDlg=new C4StartupPlrPropertiesDlg(pSel, this)); pDlg->SetPos(Min(GetBounds().Wdt/10, GetBounds().Wdt - pDlg->GetBounds().Wdt), (GetBounds().Hgt - pDlg->GetBounds().Hgt) / 2); } break; case PSDM_Crew: // rename crew CrewListItem *pSel = static_cast(GetSelection()); if (!pSel) return; pSel->CrewRename(); break; } } /* -- Crew sorting -- */ struct C4StartupPlrSelDlg_CrewSortDataEntry { int32_t iMaxExp; C4ID idType; C4StartupPlrSelDlg_CrewSortDataEntry(int32_t iMaxExp, C4ID idType) : iMaxExp(iMaxExp), idType(idType) {} }; class C4StartupPlrSelDlg_CrewSortDataMatchType { C4ID idType; public: C4StartupPlrSelDlg_CrewSortDataMatchType(C4ID idType) : idType(idType) {} bool operator()(C4StartupPlrSelDlg_CrewSortDataEntry Check) { return Check.idType == idType; } }; typedef std::vector C4StartupPlrSelDlg_CrewSortData; int32_t C4StartupPlrSelDlg::CrewSortFunc(const C4GUI::Element *pEl1, const C4GUI::Element *pEl2, void *par) { const CrewListItem *pItem1 = static_cast(pEl1); const CrewListItem *pItem2 = static_cast(pEl2); C4StartupPlrSelDlg_CrewSortData &rSortData = *static_cast(par); C4StartupPlrSelDlg_CrewSortData::iterator i = std::find_if(rSortData.begin(), rSortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pItem1->GetCore().id)), j = std::find_if(rSortData.begin(), rSortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pItem2->GetCore().id)); // primary sort: By Clonk type, where high exp Clonk types are sorted atop low exp Clonk types if (i != j) { if (i == rSortData.end()) return -1; else if (j == rSortData.end()) return +1; // can't really happen return (*i).iMaxExp - (*j).iMaxExp; } // secondary: By experience return pItem1->GetCore().Experience - pItem2->GetCore().Experience; } void C4StartupPlrSelDlg::ResortCrew() { assert(eMode == PSDM_Crew); // create a list of Clonk types and their respective maximum experience C4StartupPlrSelDlg_CrewSortData SortData; for (CrewListItem *pCrewItem = static_cast(pPlrListBox->GetFirst()); pCrewItem; pCrewItem = pCrewItem->GetNext()) { C4StartupPlrSelDlg_CrewSortData::iterator i = std::find_if(SortData.begin(), SortData.end(), C4StartupPlrSelDlg_CrewSortDataMatchType(pCrewItem->GetCore().id)); if (i == SortData.end()) SortData.push_back(C4StartupPlrSelDlg_CrewSortDataEntry(pCrewItem->GetCore().Experience, pCrewItem->GetCore().id)); else (*i).iMaxExp = Max((*i).iMaxExp, pCrewItem->GetCore().Experience); } pPlrListBox->SortElements(&CrewSortFunc, &SortData); } // ------------------------------------------------ // --- Player color HSV chooser class C4StartupPlrColorPickerDlg : public C4GUI::Dialog { public: C4StartupPlrColorPickerDlg(C4PlayerInfoCore *plrcore); protected: // Event handler virtual void OnClosed(bool commit); private: class Picker : public C4GUI::Control { public: Picker(const C4Rect &bounds); // Set/retrieve current color value void SetColor(uint32_t rgb); uint32_t GetColor() const; protected: // Event handlers, overridden from C4GUI::Control virtual void DrawElement(C4TargetFacet &cgo); virtual void MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam); virtual void DoDragging(C4GUI::CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam); private: static const unsigned int HSPickerCursorSize = 5; static const unsigned int VPickerCursorSize = 7; C4FacetSurface hsFacet, vFacet; // chooser backgrounds C4Rect hsPickerRect, vPickerRect; C4GUI::Picture *flagPreview, *crewPreview; uint32_t hsv; // current color enum { PS_Idle, // user isn't dragging anything PS_IdleDragging, // user started the drag on empty space PS_DragHS, // user started the drag over the HS picker PS_DragV // user started the drag over the V picker } state; bool HandleMouseDown(int32_t x, int32_t y); void UpdateVFacet(uint32_t h, uint32_t s); void UpdatePreview(); }; C4PlayerInfoCore *plrcore; Picker *picker; static uint32_t HSV2RGB(uint32_t hsv) { float h = GetRedValue(hsv) / 255.f * 6.f; float s = GetGreenValue(hsv) / 255.f; float v = GetBlueValue(hsv) / 255.f; float chroma = s * v; float x = chroma * (1.f - std::abs(std::fmod(h, 2.f) - 1.f)); float r = 0; float g = 0; float b = 0; switch (static_cast(h)) { case 0: case 6: r = chroma; g = x; break; case 1: r = x; g = chroma; break; case 2: g = chroma; b = x; break; case 3: g = x; b = chroma; break; case 4: b = chroma; r = x; break; case 5: b = x; r = chroma; break; } r += v-chroma; g += v-chroma; b += v-chroma; return RGBA(r * 255.f, g * 255.f, b * 255.f, hsv >> 24); } static uint32_t RGB2HSV(uint32_t rgb) { float r = GetRedValue(rgb) / 255.f; float g = GetGreenValue(rgb) / 255.f; float b = GetBlueValue(rgb) / 255.f; float min = std::min(r, std::min(g, b)); float max = std::max(r, std::max(g, b)); float chroma = max - min; float hue = 0; if (r == max) hue = std::fmod((g-b) / chroma, 6.f); else if (g == max) hue = (b-r) / chroma + 2.f; else hue = (r-g) / chroma + 4.f; float h = hue / 6.f; float s = max == 0 ? 0.f : chroma / max; float v = max; return RGBA(h * 255.f, s * 255.f, v * 255.f, rgb >> 24); } }; C4StartupPlrColorPickerDlg::C4StartupPlrColorPickerDlg(C4PlayerInfoCore *plrcore) : Dialog(400, 296 + C4GUI_ButtonAreaHgt, LoadResStr("IDS_DLG_PLAYERCOLORSELECTION"), false), plrcore(plrcore) { C4GUI::ComponentAligner caMain(GetClientRect(), 0, 1, true); picker = new Picker(caMain.GetFromTop(280)); picker->SetColor(plrcore->PrefColorDw); AddElement(picker); // buttons C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt), 0, 0); caButtonArea = C4GUI::ComponentAligner(caButtonArea.GetCentered(2*128 + 4*8, C4GUI_ButtonAreaHgt), 8, 8); C4GUI::Button *cancelButton = new C4GUI::CancelButton(caButtonArea.GetFromRight(128)); C4GUI::Button *okButton = new C4GUI::OKButton(caButtonArea.GetFromRight(128)); AddElement(okButton); AddElement(cancelButton); } void C4StartupPlrColorPickerDlg::OnClosed(bool commit) { // Write chosen color back to player core if (commit) plrcore->PrefColorDw = picker->GetColor(); } C4StartupPlrColorPickerDlg::Picker::Picker(const C4Rect &bounds) : Control(bounds), state(PS_Idle) { C4GUI::ComponentAligner caMain(bounds, 8, 8, true); caMain.ExpandBottom(-(caMain.GetInnerHeight() - 256)); hsPickerRect = caMain.GetFromLeft(256); vPickerRect = caMain.GetFromLeft(16 + VPickerCursorSize); vPickerRect.Hgt = 256 - PlayerColorValueLowBound; C4Facet &flagPreviewPic = ::GraphicsResource.fctFlagClr; int preview_width = std::min(flagPreviewPic.Wdt, caMain.GetInnerWidth()); flagPreview = new C4GUI::Picture(caMain.GetFromTop(flagPreviewPic.GetHeightByWidth(preview_width), preview_width), true); flagPreview->SetFacet(flagPreviewPic); AddElement(flagPreview); C4Facet &crewPreviewPic = ::GraphicsResource.fctCrewClr; preview_width = std::min(crewPreviewPic.Wdt, caMain.GetInnerWidth()); crewPreview = new C4GUI::Picture(caMain.GetFromTop(crewPreviewPic.GetHeightByWidth(preview_width), preview_width), true); crewPreview->SetFacet(crewPreviewPic); AddElement(crewPreview); // Pre-draw the H+S chooser background, it never changes anyway hsFacet.Create(hsPickerRect.Wdt, hsPickerRect.Hgt); hsFacet.Surface->Lock(); for (int y = 0; y < hsFacet.Hgt; ++y) for (int x = 0; x < hsFacet.Wdt; ++x) hsFacet.Surface->SetPixDw(x, y, HSV2RGB(C4RGB(x, 255-y, 255))); hsFacet.Surface->Unlock(); vFacet.Create(vPickerRect.Wdt - VPickerCursorSize, vPickerRect.Hgt); UpdateVFacet(255, 255); } void C4StartupPlrColorPickerDlg::Picker::UpdateVFacet(uint32_t h, uint32_t s) { // Draw the V chooser background according to current H+S values vFacet.Surface->Lock(); for (int y = 0; y < vPickerRect.Hgt; ++y) for (int x = 0; x < vFacet.Wdt; ++x) vFacet.Surface->SetPixDw(x, y, HSV2RGB(C4RGB(h, s, 255-y))); vFacet.Surface->Unlock(); } void C4StartupPlrColorPickerDlg::Picker::UpdatePreview() { flagPreview->SetDrawColor(HSV2RGB(hsv)); crewPreview->SetDrawColor(HSV2RGB(hsv)); } void C4StartupPlrColorPickerDlg::Picker::SetColor(uint32_t rgb) { hsv = RGB2HSV(rgb); UpdateVFacet(GetRedValue(hsv), GetGreenValue(hsv)); UpdatePreview(); } uint32_t C4StartupPlrColorPickerDlg::Picker::GetColor() const { return HSV2RGB(hsv); } void C4StartupPlrColorPickerDlg::Picker::DrawElement(C4TargetFacet &cgo) { // H+S chooser background C4Facet cgoPicker(cgo.Surface, cgo.TargetX + hsPickerRect.x, cgo.TargetY + hsPickerRect.y, hsPickerRect.Wdt, hsPickerRect.Hgt); hsFacet.Draw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y); // H+S cursor cgoPicker.Wdt = cgoPicker.Hgt = HSPickerCursorSize; cgoPicker.X += GetRedValue(hsv) - cgoPicker.Wdt / 2; cgoPicker.Y += 255 - GetGreenValue(hsv) - cgoPicker.Hgt / 2; pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y, cgoPicker.X + cgoPicker.Wdt, cgoPicker.Y + cgoPicker.Hgt, C4RGB(0, 0, 0)); pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X + cgoPicker.Wdt, cgoPicker.Y, cgoPicker.X, cgoPicker.Y + cgoPicker.Hgt, C4RGB(0, 0, 0)); // V chooser background cgoPicker.Set(cgo.Surface, cgo.TargetX + vPickerRect.x + VPickerCursorSize, cgo.TargetY + vPickerRect.y, vPickerRect.Wdt - VPickerCursorSize, vPickerRect.Hgt); vFacet.Draw(cgoPicker.Surface, cgoPicker.X, cgoPicker.Y); // V cursor cgoPicker.Wdt = cgoPicker.Hgt = VPickerCursorSize; cgoPicker.X -= cgoPicker.Wdt / 2 + 1; cgoPicker.Y += 255 - GetBlueValue(hsv) - cgoPicker.Hgt / 2; for (int i = 0; i < cgoPicker.Hgt / 2 + 1; ++i) pDraw->DrawLineDw(cgoPicker.Surface, cgoPicker.X + i, cgoPicker.Y + i, cgoPicker.X + i, cgoPicker.Y + cgoPicker.Hgt - i, C4RGB(255, 255, 255)); } bool C4StartupPlrColorPickerDlg::Picker::HandleMouseDown(int32_t x, int32_t y) { if (state == PS_IdleDragging) { // User is dragging something that is neither of the pickers. Ignore. return false; } // Check if a drag starts or was originally started over a picker else if (state == PS_DragHS || (state == PS_Idle && hsPickerRect.Contains(x, y))) { int h = Clamp(x - hsPickerRect.x, 0, hsPickerRect.Wdt - 1); assert(Inside(h, 0, 255)); int s = 255 - Clamp(y - hsPickerRect.y, 0, hsPickerRect.Hgt - 1); assert(Inside(s, 0, 255)); hsv = C4RGB(h, s, GetBlueValue(hsv)); UpdateVFacet(h, s); UpdatePreview(); state = PS_DragHS; return true; } else if (state == PS_DragV || (state == PS_Idle && vPickerRect.Contains(x, y))) { int v = 255 - Clamp(y - vPickerRect.y, 0, vPickerRect.Hgt - 1); assert(Inside(v, 0, 255)); hsv = (hsv & 0xFFFFFF00) | v; UpdatePreview(); state = PS_DragV; return true; } else { // Drag started outside of all picker areas; ignore movement until user releases mouse button. state = PS_IdleDragging; return false; } } void C4StartupPlrColorPickerDlg::Picker::MouseInput(C4GUI::CMouse &rMouse, int32_t iButton, int32_t iX, int32_t iY, DWORD dwKeyParam) { Control::MouseInput(rMouse, iButton, iX, iY, dwKeyParam); if (!rMouse.IsLDown()) state = PS_Idle; if (rMouse.pDragElement) return; if (rMouse.IsLDown()) { if (HandleMouseDown(iX, iY)) { rMouse.pDragElement = this; C4GUI::GUISound("Command"); } else { rMouse.pDragElement = NULL; } } } void C4StartupPlrColorPickerDlg::Picker::DoDragging(C4GUI::CMouse &rMouse, int32_t iX, int32_t iY, DWORD dwKeyParam) { HandleMouseDown(iX, iY); } /* ---- Player property dlg ---- */ C4StartupPlrPropertiesDlg::C4StartupPlrPropertiesDlg(C4StartupPlrSelDlg::PlayerListItem * pForPlayer, C4StartupPlrSelDlg *pParentDlg) : Dialog(C4Startup::Get()->Graphics.fctPlrPropBG.Wdt, C4Startup::Get()->Graphics.fctPlrPropBG.Hgt, "", false), pMainDlg(pParentDlg), pForPlayer(pForPlayer), fClearBigIcon(false) { if (pForPlayer) { // edit existing player C4P = pForPlayer->GetCore(); } else { // create new player: Use default C4P values, with a few exceptions // FIXME: Use Player, not Clonkranks C4P.Default(&::DefaultRanks); // Set name, color, comment SCopy(LoadResStr("IDS_PLR_NEWCOMMENT"), C4P.Comment, C4MaxComment); C4P.PrefColor = SafeRandom(8); C4P.PrefColorDw = C4P.GetPrefColorValue(C4P.PrefColor); C4P.OldPrefControlStyle = 1; C4P.OldPrefAutoContextMenu = 1; C4P.OldPrefControl = C4P_Control_Keyboard1; } const int32_t BetweenElementDist = 2; // use black fonts here CStdFont *pUseFont = &C4Startup::Get()->Graphics.BookFont; CStdFont *pSmallFont = &C4Startup::Get()->Graphics.BookSmallFont; // Title //SetTitle(FormatString("%s %s", C4P.RankName, C4P.Name)); // get positions UpdateSize(); C4GUI::ComponentAligner caMain(GetClientRect(), 0, 1, true); C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt), 0,0); // dlg title const char *szTitle; if (pForPlayer) { szTitle = LoadResStr("IDS_DLG_PLAYER2"); } else { szTitle = LoadResStr("IDS_PLR_NEWPLAYER"); } C4GUI::Label *pLbl = new C4GUI::Label(szTitle, caMain.GetFromTop(pUseFont->GetLineHeight()), ALeft, C4StartupFontClr, pUseFont, false); AddElement(pLbl); caMain.ExpandTop(-BetweenElementDist); // place name label AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_NAME2"), caMain.GetFromTop(pSmallFont->GetLineHeight()), ALeft, C4StartupFontClr, pSmallFont, false)); // place name edit pNameEdit = new C4GUI::Edit(caMain.GetFromTop(C4GUI::Edit::GetCustomEditHeight(pUseFont))); pNameEdit->SetFont(pUseFont); pNameEdit->SetColors(C4StartupEditBGColor, C4StartupFontClr, C4StartupEditBorderColor); pNameEdit->InsertText(C4P.PrefName, false); pNameEdit->SetMaxText(C4MaxName); AddElement(pNameEdit); SetFocus(pNameEdit, false); caMain.ExpandTop(-BetweenElementDist); int32_t iControlPicSize = C4GUI::ArrowButton::GetDefaultHeight(); // GetGridCell(0,3,0,1,-1,-1,false,2) int32_t label_hgt = pSmallFont->GetLineHeight(); // place color label C4GUI::ComponentAligner caColorArea(caMain.GetFromTop(iControlPicSize + BetweenElementDist + label_hgt), 2, 0); C4GUI::ComponentAligner caPictureArea(caColorArea.GetFromRight(iControlPicSize, iControlPicSize + BetweenElementDist + label_hgt), 2,0); caColorArea.ExpandLeft(2); AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr("IDS_CTL_COLOR")).getData(), caColorArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false)); caColorArea.ExpandTop(-BetweenElementDist); // place picture label AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_PICTURE"), caPictureArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false)); caPictureArea.ExpandTop(-BetweenElementDist); // place color controls C4GUI::Button *pBtn; const char *szTip; szTip = LoadResStr("IDS_DLGTIP_PLAYERCOLORS"); AddElement(pBtn = new C4GUI::CallbackButton(C4GUI::ArrowButton::Left, caColorArea.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnClrChangeLeft)); pBtn->SetToolTip(szTip); C4Facet &rfctClrPreviewPic = ::GraphicsResource.fctFlagClr; //C4Startup::Get()->Graphics.fctCrewClr; //::GraphicsResource.fctCrewClr; pClrPreview = new C4GUI::CallbackButton(C4GUI::Ico_None, caColorArea.GetFromLeft(rfctClrPreviewPic.GetWidthByHeight(caColorArea.GetHeight())), 'C', &C4StartupPlrPropertiesDlg::OnClrChangeCustom); pClrPreview->SetFacet(rfctClrPreviewPic); AddElement(pClrPreview); AddElement(pBtn = new C4GUI::CallbackButton(C4GUI::ArrowButton::Right, caColorArea.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnClrChangeRight)); pBtn->SetToolTip(szTip); if (!C4P.PrefColorDw) C4P.PrefColorDw=0xff; // Place picture controls AddElement(pPictureBtn = new C4GUI::CallbackButton(C4GUI::Ico_Player, caPictureArea.GetAll(), 'P' /* 2do */, &C4StartupPlrPropertiesDlg::OnPictureBtn)); pPictureBtn->SetToolTip(LoadResStr("IDS_DESC_SELECTAPICTUREANDORLOBBYI")); UpdateBigIcon(); UpdatePlayerColor(true); caMain.ExpandTop(-BetweenElementDist); // place control label C4GUI::ComponentAligner caControlArea(caMain.GetFromTop(iControlPicSize + label_hgt + BetweenElementDist), 0,0, false); C4GUI::ComponentAligner caSkinArea(caControlArea.GetFromRight(iControlPicSize + label_hgt + BetweenElementDist), 0,0, false); AddElement(new C4GUI::Label(FormatString("%s:", LoadResStr("IDS_CTL_CONTROL")).getData(), caControlArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false)); caControlArea.ExpandTop(-BetweenElementDist); // place clonk style label AddElement(new C4GUI::Label(LoadResStr("IDS_CTL_CLONKSKIN"), caSkinArea.GetFromTop(label_hgt), ALeft, C4StartupFontClr, pSmallFont, false)); caSkinArea.ExpandTop(-BetweenElementDist); // place control controls C4GUI::ComponentAligner caControl(caControlArea.GetFromTop(iControlPicSize), 2,0); szTip = LoadResStr("IDS_DLGTIP_PLAYERCONTROL"); AddElement(pBtn = new C4GUI::CallbackButton(C4GUI::ArrowButton::Left, caControl.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnCtrlChangeLeft)); pBtn->SetToolTip(szTip); caControl.ExpandBottom(label_hgt); C4Rect ctrl_name_rect = caControl.GetFromBottom(label_hgt); C4Facet &rfctCtrlPic = ::GraphicsResource.fctKeyboard; // UpdatePlayerControl() will alternatively set fctGamepad AddElement(pCtrlImg = new C4GUI::Picture(caControl.GetFromLeft(rfctCtrlPic.GetWidthByHeight(caControl.GetHeight())), true)); pCtrlImg->SetToolTip(szTip); AddElement(pBtn = new C4GUI::CallbackButton(C4GUI::ArrowButton::Right, caControl.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnCtrlChangeRight)); pBtn->SetToolTip(szTip); caControl.ExpandLeft(-10); C4P.OldPrefControl = Clamp(C4P.OldPrefControl, 0, C4MaxControlSet-1); ctrl_name_lbl = new C4GUI::Label("CtrlName", ctrl_name_rect, ALeft, C4StartupFontClr, pSmallFont, false, false, true); AddElement(ctrl_name_lbl); UpdatePlayerControl(); C4GUI::ComponentAligner caSkin(caSkinArea.GetFromTop(iControlPicSize), 2,0); szTip = LoadResStr("IDS_DLGTIP_PLAYERCREWSKIN"); AddElement(pBtn = new C4GUI::CallbackButton(C4GUI::ArrowButton::Left, caSkin.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnSkinChangeLeft)); pBtn->SetToolTip(szTip); C4Facet rfctSkinPic = ::GraphicsResource.fctClonkSkin.GetPhase(0); AddElement(pSkinImg = new C4GUI::Picture(caSkin.GetFromLeft(rfctSkinPic.GetWidthByHeight(caSkin.GetHeight())), true)); pSkinImg->SetToolTip(szTip); pSkinImg->SetFacet(::GraphicsResource.fctClonkSkin.GetPhase(0)); AddElement(pBtn = new C4GUI::CallbackButton(C4GUI::ArrowButton::Right, caSkin.GetFromLeft(C4GUI::ArrowButton::GetDefaultWidth()), &C4StartupPlrPropertiesDlg::OnSkinChangeRight)); pBtn->SetToolTip(szTip); caSkin.ExpandLeft(-10); UpdatePlayerSkin(); caMain.ExpandTop(-BetweenElementDist); // place buttons // OK C4GUI::Button *pBtnOK = new C4GUI::OKIconButton(C4Rect(147-GetMarginLeft(), 295+35-GetMarginTop(), 54, 33), C4GUI::Ico_None); AddElement(pBtnOK); //pBtnOK->SetToolTip(LoadResStr("IDS_DLGTIP_OK")); // Cancel C4GUI::Button *pBtnAbort = new C4GUI::CancelIconButton(C4Rect(317-GetMarginLeft(), 16-GetMarginTop(), 21, 21), C4GUI::Ico_None); AddElement(pBtnAbort); //pBtnAbort->SetToolTip(LoadResStr("IDS_DLGTIP_CANCEL")); // when called from player selection screen: input dlg always closed in the end // otherwise, modal proc will delete if (pMainDlg) SetDelOnClose(); } void C4StartupPlrPropertiesDlg::DrawElement(C4TargetFacet &cgo) { C4Startup::Get()->Graphics.fctPlrPropBG.Draw(cgo.Surface, rcBounds.x+cgo.TargetX, rcBounds.y+cgo.TargetY); } bool IsColorConflict(DWORD dwClr1, DWORD dwClr2); void C4StartupPlrPropertiesDlg::UpdatePlayerColor(bool fUpdateSliders) { C4P.PrefColorDw = C4P.PrefColorDw | 0xFF000000; // Ensure full opacity pClrPreview->SetColor(C4P.PrefColorDw); pPictureBtn->SetColor(C4P.PrefColorDw); } void C4StartupPlrPropertiesDlg::OnClrChangeLeft(C4GUI::Control *pBtn) { // previous standard color in list C4P.PrefColor = C4P.PrefColor ? C4P.PrefColor - 1 : 11; C4P.PrefColorDw = C4PlayerInfoCore::GetPrefColorValue(C4P.PrefColor); UpdatePlayerColor(true); } void C4StartupPlrPropertiesDlg::OnClrChangeCustom(C4GUI::Control *pBtn) { GetScreen()->ShowModalDlg(new C4StartupPlrColorPickerDlg(&C4P)); UpdatePlayerColor(true); } void C4StartupPlrPropertiesDlg::OnClrChangeRight(C4GUI::Control *pBtn) { // next standard color in list C4P.PrefColor = (C4P.PrefColor + 1) % 12; C4P.PrefColorDw = C4PlayerInfoCore::GetPrefColorValue(C4P.PrefColor); UpdatePlayerColor(true); } void C4StartupPlrPropertiesDlg::UpdatePlayerControl() { C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData()); if (!control_set) control_set = Game.PlayerControlUserAssignmentSets.GetDefaultSet(); // update keyboard image of selected control C4Facet fctCtrlPic; if (control_set) fctCtrlPic = control_set->GetPicture(); pCtrlImg->SetFacet(fctCtrlPic); if (control_set) ctrl_name_lbl->SetText(control_set->GetGUIName()); else ctrl_name_lbl->SetText("???"); } void C4StartupPlrPropertiesDlg::OnCtrlChangeLeft(C4GUI::Control *pBtn) { // previous control set in list C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData()); int32_t index = Game.PlayerControlUserAssignmentSets.GetSetIndex(control_set); if (index < 0) index = 0; // defined control set not found - probably an old CR player file if (!index--) index = Game.PlayerControlUserAssignmentSets.GetSetCount() - 1; control_set = Game.PlayerControlUserAssignmentSets.GetSetByIndex(index); if (control_set) C4P.PrefControl = control_set->GetName(); UpdatePlayerControl(); } void C4StartupPlrPropertiesDlg::OnCtrlChangeRight(C4GUI::Control *pBtn) { // next control set in list C4PlayerControlAssignmentSet *control_set = Game.PlayerControlUserAssignmentSets.GetSetByName(C4P.PrefControl.getData()); int32_t index = Game.PlayerControlUserAssignmentSets.GetSetIndex(control_set); if (index < 0) index = 0; // defined control set not found - probably an old CR player file if (++index >= int32_t(Game.PlayerControlUserAssignmentSets.GetSetCount())) index = 0; control_set = Game.PlayerControlUserAssignmentSets.GetSetByIndex(index); if (control_set) C4P.PrefControl = control_set->GetName(); UpdatePlayerControl(); } void C4StartupPlrPropertiesDlg::UpdatePlayerSkin() { pSkinImg->SetFacet(::GraphicsResource.fctClonkSkin.GetPhase(C4P.PrefClonkSkin)); } void C4StartupPlrPropertiesDlg::OnSkinChangeLeft(C4GUI::Control *pBtn) { // previous skin in list C4P.PrefClonkSkin = C4P.PrefClonkSkin ? C4P.PrefClonkSkin - 1 : 3; UpdatePlayerSkin(); } void C4StartupPlrPropertiesDlg::OnSkinChangeRight(C4GUI::Control *pBtn) { // next skin in list C4P.PrefClonkSkin = (C4P.PrefClonkSkin + 1) % 4; UpdatePlayerSkin(); } void C4StartupPlrPropertiesDlg::UserClose(bool fOK) { // check name validity if (fOK) { StdStrBuf PlrName(pNameEdit->GetText()), Filename; if (!C4StartupPlrSelDlg::CheckPlayerName(PlrName, Filename, pForPlayer ? &pForPlayer->GetFilename() : NULL, true)) return; } Close(fOK); } void C4StartupPlrPropertiesDlg::OnClosed(bool fOK) { if (fOK) { // store selected data if desired StdStrBuf PlrName(pNameEdit->GetText()), Filename; if (C4StartupPlrSelDlg::CheckPlayerName(PlrName, Filename, pForPlayer ? &pForPlayer->GetFilename() : NULL, true)) { SCopy(PlrName.getData(), C4P.PrefName, C4MaxName); C4Group PlrGroup; bool fSucc=false; // existant player: update file if (pForPlayer) { if (!pForPlayer->MoveFilename(Filename.getData())) GetScreen()->ShowMessage(LoadResStr("IDS_FAIL_RENAME"), "", C4GUI::Ico_Error); // update bigicon if (fClearBigIcon || fctNewBigIcon.Surface) { C4Group PlrGroup; if (PlrGroup.Open(Config.AtUserDataPath(Filename.getData()))) { if (fClearBigIcon || fctNewBigIcon.Surface) PlrGroup.Delete(C4CFN_BigIcon); if (fctNewBigIcon.Surface) fctNewBigIcon.GetFace().SavePNG(PlrGroup, C4CFN_BigIcon); if (PlrGroup.Close()) fSucc = true; if (fClearBigIcon || fctNewBigIcon.Surface) pForPlayer->GrabCustomIcon(fctNewBigIcon); } } else { fSucc = true; } pForPlayer->UpdateCore(C4P); // player may have been activated: Make sure any new filename is reflected in participants list if (pMainDlg) pMainDlg->UpdateActivatedPlayers(); } else { // NewPlayer: Open new player group if (PlrGroup.Open(Config.AtUserDataPath(Filename.getData()), true)) { // Do not overwrite (should have been caught earlier anyway) if (PlrGroup.FindEntry(C4CFN_PlayerInfoCore)) return; // Save info core C4P.Save(PlrGroup); // Add BigIcon if (fctNewBigIcon.Surface) { fctNewBigIcon.GetFace().SavePNG(PlrGroup, C4CFN_BigIcon); } // Close group if (PlrGroup.Close()) fSucc=true; // update activate button text if (pMainDlg) { pMainDlg->UpdatePlayerList(); pMainDlg->SelectItem(Filename, true); } else { // no main player selection dialog: This means that this dlg was shown as a creation dialog from the main startup dlg // Just set the newly created player as current selection SCopy(Config.AtRelativePath(Filename.getData()), Config.General.Participants, sizeof Config.General.Participants); } } } if (!fSucc) GetScreen()->ShowErrorMessage(PlrGroup.GetError()); } } // Make the dialog go away Dialog::OnClosed(fOK); } bool C4StartupPlrPropertiesDlg::SetNewPicture(C4Surface &srcSfc, C4FacetSurface *trgFct, int32_t iMaxSize, bool fColorize) { if (fColorize) { C4Surface srcSfcClr; if (!srcSfcClr.CreateColorByOwner(&srcSfc)) return false; return trgFct->CopyFromSfcMaxSize(srcSfcClr, iMaxSize, C4P.PrefColorDw); } else { return trgFct->CopyFromSfcMaxSize(srcSfc, iMaxSize); } } void C4StartupPlrPropertiesDlg::SetNewPicture(const char *szFromFilename) { if (!szFromFilename) { // If szFromFilename==NULL, clear bigicon fClearBigIcon = true; fctNewBigIcon.Clear(); } else { // else set new bigicon by loading and scaling if necessary. C4Surface sfcNewPic; C4Group SrcGrp; StdStrBuf sParentPath; GetParentPath(szFromFilename, &sParentPath); bool fSucc = false; if (SrcGrp.Open(sParentPath.getData())) { if (sfcNewPic.Load(SrcGrp, GetFilename(szFromFilename))) { fSucc = true; if (!SetNewPicture(sfcNewPic, &fctNewBigIcon, C4MaxBigIconSize, true)) fSucc = false; } } if (!fSucc) { // error! GetScreen()->ShowErrorMessage(FormatString(LoadResStr("IDS_PRC_NOGFXFILE"), szFromFilename, SrcGrp.GetError()).getData()); } } // update icon UpdateBigIcon(); } void C4StartupPlrPropertiesDlg::OnPictureBtn(C4GUI::Control *pBtn) { StdStrBuf sNewPic; if (C4PortraitSelDlg::SelectPortrait(GetScreen(), &sNewPic)) { SetNewPicture(sNewPic.getData()); } } void C4StartupPlrPropertiesDlg::UpdateBigIcon() { // new icon? bool fHasIcon = false; if (fctNewBigIcon.Surface) { pPictureBtn->SetFacet(fctNewBigIcon); fHasIcon = true; } // old icon in existing player? else if (!fClearBigIcon && pForPlayer) { C4Group PlrGroup; if (PlrGroup.Open(Config.AtUserDataPath(pForPlayer->GetFilename().getData()))) { if (PlrGroup.FindEntry(C4CFN_BigIcon)) { if (fctOldBigIcon.Load(PlrGroup, C4CFN_BigIcon)) { pPictureBtn->SetFacet(fctOldBigIcon); fHasIcon = true; } } } } // no icon: Set default if (!fHasIcon) { pPictureBtn->SetFacet(::GraphicsResource.fctPlayerClr); } }