1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// resource display list box
19
20#include "C4GuiResource.h"
21#include "C4Include.h"
22#include "C4GameLobby.h"
23#include "C4FullScreen.h"
24#include "C4Game.h"
25#include "C4Network2.h"
26#include "C4PlayerInfo.h"
27#include "C4Network2Players.h"
28#include "C4Network2Dialogs.h"
29
30// C4Network2ResDlg::ListItem
31
32C4Network2ResDlg::ListItem::ListItem(C4Network2ResDlg *pForResDlg, const C4Network2Res *pByRes)
33 : pSaveBtn(nullptr)
34{
35 // init by res core (2do)
36 iResID = pByRes->getResID();
37 const char *szFilename = GetFilename(path: pByRes->getCore().getFileName());
38 // get size
39 int iIconSize = C4GUI::GetRes()->TextFont.GetLineHeight();
40 int iWidth = pForResDlg->GetItemWidth();
41 int iVerticalIndent = 2;
42 SetBounds(C4Rect(0, 0, iWidth, iIconSize + 2 * iVerticalIndent));
43 C4GUI::ComponentAligner ca(GetContainedClientRect(), 0, iVerticalIndent);
44 // create subcomponents
45 pFileIcon = new C4GUI::Icon(ca.GetFromLeft(iWdt: iIconSize), C4GUI::Ico_Resource);
46 pLabel = new C4GUI::Label(szFilename, iIconSize + IconLabelSpacing, iVerticalIndent, ALeft);
47 pProgress = nullptr;
48 // add components
49 AddElement(pChild: pFileIcon); AddElement(pChild: pLabel);
50 // tooltip
51 SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_DESC_RESOURCE));
52 // add to listbox (will eventually get moved)
53 pForResDlg->AddElement(pChild: this);
54 // first-time update
55 Update(pByRes);
56}
57
58void C4Network2ResDlg::ListItem::Update(const C4Network2Res *pByRes)
59{
60 // update progress label
61 iProgress = pByRes->getPresentPercent();
62 if (iProgress < 100)
63 {
64 const auto &text = std::to_string(val: iProgress) + '%';
65 if (pProgress)
66 pProgress->SetText(szText: text.c_str());
67 else
68 {
69 pProgress = new C4GUI::Label(text.c_str(), GetBounds().Wdt - IconLabelSpacing, 0, ARight);
70 pProgress->SetToolTip(LoadResStr(id: C4ResStrTableKey::IDS_NET_RESPROGRESS_DESC));
71 AddElement(pChild: pProgress);
72 }
73 }
74 else { delete pProgress; pProgress = nullptr; }
75 // update disk icon
76 if (IsSavePossible())
77 {
78 if (!pSaveBtn)
79 {
80 pSaveBtn = new C4GUI::CallbackButtonEx<C4Network2ResDlg::ListItem, C4GUI::IconButton>(C4GUI::Ico_Save, GetToprightCornerRect(iWidth: 16, iHeight: 16, iHIndent: 2, iVIndent: 1), 0, this, &ListItem::OnButtonSave);
81 AddElement(pChild: pSaveBtn);
82 }
83 }
84 else
85 delete pSaveBtn;
86}
87
88void C4Network2ResDlg::ListItem::OnButtonSave(C4GUI::Control *pButton)
89{
90 LocalSaveResource(fDoOverwrite: false);
91}
92
93void C4Network2ResDlg::ListItem::OnButtonSaveConfirm(C4GUI::Element *pNull)
94{
95 LocalSaveResource(fDoOverwrite: true);
96}
97
98void C4Network2ResDlg::ListItem::LocalSaveResource(bool fDoOverwrite)
99{
100 // get associated resource
101 C4Network2Res::Ref pRes = GetRefRes();
102 if (!pRes) return;
103 const char *szResFile = pRes->getFile();
104 StdStrBuf strErrCopyFile(LoadResStr(id: C4ResStrTableKey::IDS_NET_ERR_COPYFILE));
105 if (!SEqual2(szStr1: szResFile, szStr2: Config.Network.WorkPath))
106 {
107 GetScreen()->ShowMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_NET_ERR_COPYFILE_LOCAL), szCaption: strErrCopyFile.getData(), icoIcon: C4GUI::Ico_Error);
108 return;
109 }
110 const char *szFilename = GetFilename(path: pRes->getCore().getFileName());
111 const char *szSpecialPath = "";
112 if (WildcardMatch(C4CFN_PlayerFiles, szFName2: szFilename))
113 // write players to player path
114 szSpecialPath = Config.General.PlayerPath;
115 const char *szTarget = Config.AtExePath(szFilename: std::format(fmt: "{}{}", args&: szSpecialPath, args&: szFilename).c_str());
116 if (!fDoOverwrite && ItemExists(szItemName: szTarget))
117 {
118 // show a confirmation dlg, asking whether the ressource should be overwritten
119 GetScreen()->ShowRemoveDlg(pDlg: new C4GUI::ConfirmationDialog(LoadResStr(id: C4ResStrTableKey::IDS_NET_RES_SAVE_OVERWRITE, args: GetFilename(path: szTarget)).c_str(), LoadResStr(id: C4ResStrTableKey::IDS_NET_RES_SAVE),
120 new C4GUI::CallbackHandler<C4Network2ResDlg::ListItem>(this, &C4Network2ResDlg::ListItem::OnButtonSaveConfirm), C4GUI::MessageDialog::btnYesNo));
121 return;
122 }
123 if (!C4Group_CopyItem(szSource: szResFile, szTarget))
124 GetScreen()->ShowMessage(szMessage: strErrCopyFile.getData(), szCaption: strErrCopyFile.getData(), icoIcon: C4GUI::Ico_Error);
125 else
126 {
127 GetScreen()->ShowMessage(szMessage: LoadResStr(id: C4ResStrTableKey::IDS_NET_RES_SAVED_DESC, args: GetFilename(path: szTarget)).c_str(), szCaption: LoadResStr(id: C4ResStrTableKey::IDS_NET_RES_SAVED), icoIcon: C4GUI::Ico_Save);
128 }
129}
130
131C4Network2Res::Ref C4Network2ResDlg::ListItem::GetRefRes()
132{
133 // forward to network reslist
134 return Game.Network.ResList.getRefRes(iResID);
135}
136
137bool C4Network2ResDlg::ListItem::IsSavePossible()
138{
139 // check ressource
140 bool fCanSave = false;
141 C4Network2Res::Ref pRes = GetRefRes();
142 if (!pRes) return false;
143 // check for local filename
144 const char *szResFile = pRes->getFile();
145 if (!pRes->isLocal() && SEqual2(szStr1: szResFile, szStr2: Config.Network.WorkPath))
146 {
147 // check type
148 C4Network2ResType eType = pRes->getType();
149 if ((eType == NRT_Player && Config.Lobby.AllowPlayerSave) || eType == NRT_Scenario || eType == NRT_Definitions)
150 // check complete
151 if (!pRes->isLoading())
152 // save OK
153 fCanSave = true;
154 }
155 return fCanSave;
156}
157
158// C4Network2ResDlg
159
160C4Network2ResDlg::C4Network2ResDlg(const C4Rect &rcBounds, bool fActive) : ListBox(rcBounds), pSec1Timer(nullptr)
161{
162 // 2do
163 // initially active?
164 if (fActive) Activate();
165}
166
167void C4Network2ResDlg::Activate()
168{
169 // create timer if necessary
170 if (!pSec1Timer) pSec1Timer = new C4Sec1TimerCallback<C4Network2ResDlg>(this);
171 // force an update
172 Update();
173}
174
175void C4Network2ResDlg::Deactivate()
176{
177 // release timer if set
178 if (pSec1Timer)
179 {
180 pSec1Timer->Release();
181 pSec1Timer = nullptr;
182 }
183}
184
185void C4Network2ResDlg::Update()
186{
187 // check through own resources and current res list
188 ListItem *pItem = static_cast<ListItem *>(pClientWindow->GetFirst()), *pNext;
189 C4Network2Res *pRes; int iResID = -1;
190 while (pRes = Game.Network.ResList.getRefNextRes(iResID: ++iResID))
191 {
192 iResID = pRes->getResID();
193 // resource checking: deleted ressource(s) present?
194 while (pItem && (pItem->GetResID() < iResID))
195 {
196 pNext = static_cast<ListItem *>(pItem->GetNext());
197 delete pItem; pItem = pNext;
198 }
199 // same resource present for update?
200 if (pItem && pItem->GetResID() == iResID)
201 {
202 pItem->Update(pByRes: pRes);
203 pItem = static_cast<ListItem *>(pItem->GetNext());
204 }
205 else
206 // not present: insert (or add if pItem=nullptr)
207 InsertElement(pChild: new ListItem(this, pRes), pInsertBefore: pItem);
208 }
209
210 // del trailing items
211 while (pItem)
212 {
213 pNext = static_cast<ListItem *>(pItem->GetNext());
214 delete pItem; pItem = pNext;
215 }
216}
217