1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2008-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 // file selection dialogs
17
18 #include "C4Include.h"
19 #include "gui/C4FileSelDlg.h"
20
21 #include "C4Version.h"
22 #include "graphics/C4Draw.h"
23 #include "graphics/C4GraphicsResource.h" // only for single use of ::GraphicsResource.fctOKCancel below...
24
25 #ifdef _WIN32
26 #ifndef _WIN32_IE
27 #define _WIN32_IE 0x0400
28 #endif
29 #undef MK_ALT
30 #include <shlobj.h>
31 #ifndef CSIDL_MYPICTURES
32 #define CSIDL_MYPICTURES 0x0027
33 #endif
34 #endif
35
36 // def 1 if gfx loading works in background thread. Right now, it doesn't
37 // C4Group and C4Surface just don't like it
38 // So for now, the loading will be done in 1/10 of the frames in OnIdle of the dialog
39 #define USE_BACKGROUND_THREAD_LOAD 0
40
41
42 // ---------------------------------------------------
43 // C4FileSelDlg::ListItem
44
ListItem(const char * szFilename)45 C4FileSelDlg::ListItem::ListItem(const char *szFilename) : C4GUI::Control(C4Rect(0,0,0,0))
46 {
47 if (szFilename) sFilename.Copy(szFilename); else sFilename.Clear();
48 }
49
50 C4FileSelDlg::ListItem::~ListItem() = default;
51
52 // ---------------------------------------------------
53 // C4FileSelDlg::DefaultListItem
54
DefaultListItem(const char * szFilename,bool fTruncateExtension,bool fCheckbox,bool fGrayed,C4GUI::Icons eIcon)55 C4FileSelDlg::DefaultListItem::DefaultListItem(const char *szFilename, bool fTruncateExtension, bool fCheckbox, bool fGrayed, C4GUI::Icons eIcon)
56 : C4FileSelDlg::ListItem(szFilename), pLbl(nullptr), pCheck(nullptr), pKeyCheck(nullptr), fGrayed(fGrayed)
57 {
58 StdStrBuf sLabel; if (szFilename) sLabel.Ref(::GetFilename(szFilename)); else sLabel.Ref(LoadResStr("IDS_CTL_NONE"));
59 if (szFilename && fTruncateExtension)
60 {
61 sLabel.Copy();
62 char *szFilename = sLabel.GrabPointer();
63 RemoveExtension(szFilename);
64 sLabel.Take(szFilename);
65 }
66 rcBounds.Hgt = ::GraphicsResource.TextFont.GetLineHeight();
67 UpdateSize();
68 C4GUI::ComponentAligner caMain(GetContainedClientRect(),0,0);
69 int32_t iHeight = caMain.GetInnerHeight();
70 if (fCheckbox)
71 {
72 pCheck = new C4GUI::CheckBox(caMain.GetFromLeft(iHeight), nullptr, false);
73 if (fGrayed) pCheck->SetEnabled(false);
74 AddElement(pCheck);
75 pKeyCheck = new C4KeyBinding(C4KeyCodeEx(K_SPACE), "FileSelToggleFileActive", KEYSCOPE_Gui,
76 new C4GUI::ControlKeyCB<ListItem>(*this, &ListItem::UserToggleCheck), C4CustomKey::PRIO_Ctrl);
77 }
78 C4GUI::Icon *pIco = new C4GUI::Icon(caMain.GetFromLeft(iHeight), eIcon);
79 AddElement(pIco);
80 pLbl = new C4GUI::Label(sLabel.getData(), caMain.GetAll(), ALeft, fGrayed ? C4GUI_CheckboxDisabledFontClr : C4GUI_CheckboxFontClr);
81 AddElement(pLbl);
82 }
83
~DefaultListItem()84 C4FileSelDlg::DefaultListItem::~DefaultListItem()
85 {
86 if (pKeyCheck) delete pKeyCheck;
87 }
88
UpdateOwnPos()89 void C4FileSelDlg::DefaultListItem::UpdateOwnPos()
90 {
91 BaseClass::UpdateOwnPos();
92 if (!pLbl) return;
93 C4GUI::ComponentAligner caMain(GetContainedClientRect(),0,0);
94 caMain.GetFromLeft(caMain.GetInnerHeight()*(1+!!pCheck));
95 pLbl->SetBounds(caMain.GetAll());
96 }
97
IsChecked() const98 bool C4FileSelDlg::DefaultListItem::IsChecked() const
99 {
100 return pCheck ? pCheck->GetChecked() : false;
101 }
102
SetChecked(bool fChecked)103 void C4FileSelDlg::DefaultListItem::SetChecked(bool fChecked)
104 {
105 // store new state in checkbox
106 if (pCheck) pCheck->SetChecked(fChecked);
107 }
108
UserToggleCheck()109 bool C4FileSelDlg::DefaultListItem::UserToggleCheck()
110 {
111 // toggle if possible
112 if (pCheck && !IsGrayed())
113 {
114 pCheck->ToggleCheck(true);
115 return true;
116 }
117 return false;
118 }
119
120
121
122
123 // ---------------------------------------------------
124 // C4FileSelDlg
125
C4FileSelDlg(const char * szRootPath,const char * szTitle,C4FileSel_BaseCB * pSelCallback,bool fInitElements)126 C4FileSelDlg::C4FileSelDlg(const char *szRootPath, const char *szTitle, C4FileSel_BaseCB *pSelCallback, bool fInitElements)
127 : C4GUI::Dialog(Clamp(C4GUI::GetScreenWdt()*2/3+10, 300,600), Clamp(C4GUI::GetScreenHgt()*2/3+10, 220,500), szTitle, false),
128 pLocationComboBox(nullptr), pFileListBox(nullptr), pSelectionInfoBox(nullptr), btnOK(nullptr), pLocations(nullptr), iLocationCount(0), pSelection(nullptr), pSelCallback(pSelCallback)
129 {
130 sTitle.Copy(szTitle);
131 // key bindings
132 pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "FileSelReload", KEYSCOPE_Gui,
133 new C4GUI::DlgKeyCB<C4FileSelDlg>(*this, &C4FileSelDlg::KeyRefresh), C4CustomKey::PRIO_CtrlOverride);
134 pKeyEnterOverride = new C4KeyBinding(C4KeyCodeEx(K_RETURN), "FileSelConfirm", KEYSCOPE_Gui,
135 new C4GUI::DlgKeyCB<C4FileSelDlg>(*this, &C4FileSelDlg::KeyEnter), C4CustomKey::PRIO_CtrlOverride);
136 if (fInitElements) InitElements();
137 sPath.Copy(szRootPath);
138 }
139
InitElements()140 void C4FileSelDlg::InitElements()
141 {
142 UpdateSize();
143 CStdFont *pUseFont = &(::GraphicsResource.TextFont);
144 // main calcs
145 bool fHasOptions = HasExtraOptions();
146 C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
147 C4Rect rcOptions;
148 C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt, 2*C4GUI_DefButton2Wdt+4*C4GUI_DefButton2HSpace),C4GUI_DefButton2HSpace,(C4GUI_ButtonAreaHgt-C4GUI_ButtonHgt)/2);
149 if (fHasOptions) rcOptions = caMain.GetFromBottom(pUseFont->GetLineHeight() + 2*C4GUI_DefDlgSmallIndent);
150 C4GUI::ComponentAligner caUpperArea(caMain.GetAll(), C4GUI_DefDlgIndent, C4GUI_DefDlgIndent, true);
151 // create file selection area
152 if (iLocationCount)
153 {
154 C4GUI::ComponentAligner caLocations(caUpperArea.GetFromTop(C4GUI::ComboBox::GetDefaultHeight() + 2*C4GUI_DefDlgSmallIndent), C4GUI_DefDlgIndent,C4GUI_DefDlgSmallIndent, false);
155 StdStrBuf sText(LoadResStr("IDS_TEXT_LOCATION"));
156 AddElement(new C4GUI::Label(sText.getData(), caLocations.GetFromLeft(pUseFont->GetTextWidth(sText.getData())), ALeft));
157 pLocationComboBox = new C4GUI::ComboBox(caLocations.GetAll());
158 pLocationComboBox->SetComboCB(new C4GUI::ComboBox_FillCallback<C4FileSelDlg>(this, &C4FileSelDlg::OnLocationComboFill, &C4FileSelDlg::OnLocationComboSelChange));
159 pLocationComboBox->SetText(pLocations[0].sName.getData());
160 }
161 // create file selection area
162 bool fHasPreview = HasPreviewArea();
163 pFileListBox = new C4GUI::ListBox(fHasPreview ? caUpperArea.GetFromLeft(caUpperArea.GetWidth()/2) : caUpperArea.GetAll(), GetFileSelColWidth());
164 pFileListBox ->SetSelectionChangeCallbackFn(new C4GUI::CallbackHandler<C4FileSelDlg>(this, &C4FileSelDlg::OnSelChange));
165 pFileListBox ->SetSelectionDblClickFn(new C4GUI::CallbackHandler<C4FileSelDlg>(this, &C4FileSelDlg::OnSelDblClick));
166 if (fHasPreview)
167 {
168 caUpperArea.ExpandLeft(C4GUI_DefDlgIndent);
169 pSelectionInfoBox = new C4GUI::TextWindow(caUpperArea.GetAll());
170 pSelectionInfoBox->SetDecoration(true, true, nullptr, true);
171 }
172 // create button area
173 C4GUI::Button *btnAbort = new C4GUI::CancelButton(caButtonArea.GetFromRight(C4GUI_DefButton2Wdt));
174 btnOK = new C4GUI::OKButton(caButtonArea.GetFromRight(C4GUI_DefButton2Wdt));
175 // add components in tab order
176 if (pLocationComboBox) AddElement(pLocationComboBox);
177 AddElement(pFileListBox);
178 if (pSelectionInfoBox) AddElement(pSelectionInfoBox);
179 if (fHasOptions) AddExtraOptions(rcOptions);
180 AddElement(btnOK);
181 AddElement(btnAbort);
182 SetFocus(pFileListBox, false);
183 // no selection yet
184 UpdateSelection();
185 }
186
~C4FileSelDlg()187 C4FileSelDlg::~C4FileSelDlg()
188 {
189 delete [] pLocations;
190 if (pSelCallback) delete pSelCallback;
191 delete pKeyEnterOverride;
192 delete pKeyRefresh;
193 }
194
OnLocationComboFill(C4GUI::ComboBox_FillCB * pFiller)195 void C4FileSelDlg::OnLocationComboFill(C4GUI::ComboBox_FillCB *pFiller)
196 {
197 // Add all locations
198 for (int32_t i=0; i<iLocationCount; ++i)
199 pFiller->AddEntry(pLocations[i].sName.getData(), i);
200 }
201
OnLocationComboSelChange(C4GUI::ComboBox * pForCombo,int32_t idNewSelection)202 bool C4FileSelDlg::OnLocationComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
203 {
204 SetCurrentLocation(idNewSelection, true);
205 // No text change by caller; text alread changed by SetCurrentLocation
206 return true;
207 }
208
SetPath(const char * szNewPath,bool fRefresh)209 void C4FileSelDlg::SetPath(const char *szNewPath, bool fRefresh)
210 {
211 sPath.Copy(szNewPath);
212 if (fRefresh && IsShown()) UpdateFileList();
213 }
214
OnShown()215 void C4FileSelDlg::OnShown()
216 {
217 BaseClass::OnShown();
218 // load files
219 UpdateFileList();
220 }
221
UserClose(bool fOK)222 void C4FileSelDlg::UserClose(bool fOK)
223 {
224 if (!fOK || pSelection)
225 {
226 // allow OK only if something is sth is selected
227 Close(fOK);
228 }
229 else
230 {
231 GetScreen()->ShowErrorMessage(LoadResStr("IDS_ERR_PLEASESELECTAFILEFIRST"));
232 }
233 }
234
OnClosed(bool fOK)235 void C4FileSelDlg::OnClosed(bool fOK)
236 {
237 if (fOK && pSelection && pSelCallback)
238 pSelCallback->OnFileSelected(pSelection->GetFilename());
239 // base call: Might delete dlg
240 BaseClass::OnClosed(fOK);
241 }
242
OnSelDblClick(class C4GUI::Element * pEl)243 void C4FileSelDlg::OnSelDblClick(class C4GUI::Element *pEl)
244 {
245 // item double-click: confirms with this file in single mode; toggles selection in multi mode
246 if (IsMultiSelection())
247 {
248 ListItem *pItem = static_cast<ListItem *>(pEl);
249 pItem->UserToggleCheck();
250 }
251 else
252 UserClose(true);
253 }
254
CreateListItem(const char * szFilename)255 C4FileSelDlg::ListItem *C4FileSelDlg::CreateListItem(const char *szFilename)
256 {
257 // Default list item
258 if (szFilename)
259 return new DefaultListItem(szFilename, !!GetFileMask(), IsMultiSelection(), IsItemGrayed(szFilename), GetFileItemIcon());
260 else
261 return new DefaultListItem(nullptr, false, IsMultiSelection(), false, GetFileItemIcon());
262 }
263
UpdateFileList()264 void C4FileSelDlg::UpdateFileList()
265 {
266 BeginFileListUpdate();
267 // reload files
268 C4GUI::Element *pEl;
269 while ((pEl = pFileListBox->GetFirst())) delete pEl;
270 // file items
271 StdStrBuf sSearch;
272 const char *szFileMask = GetFileMask();
273 for (DirectoryIterator iter(sPath.getData()); *iter; ++iter)
274 if (!szFileMask || WildcardListMatch(szFileMask, *iter))
275 pFileListBox->AddElement(CreateListItem(*iter));
276 // none-item?
277 if (HasNoneItem())
278 {
279 pFileListBox->AddElement(CreateListItem(nullptr));
280 }
281 // list now done
282 EndFileListUpdate();
283 // path into title
284 const char *szPath = sPath.getData();
285 SetTitle(*szPath ? FormatString("%s [%s]", sTitle.getData(), szPath).getData() : sTitle.getData());
286 // initial no-selection
287 UpdateSelection();
288 }
289
UpdateSelection()290 void C4FileSelDlg::UpdateSelection()
291 {
292 // update selection from list
293 pSelection = static_cast<ListItem *>(pFileListBox->GetSelectedItem());
294 // OK button only available if selection
295 // SetEnabled would look a lot better here, but it doesn't exist yet :(
296 // selection preview, if enabled
297 if (pSelectionInfoBox)
298 {
299 // default empty
300 pSelectionInfoBox->ClearText(false);
301 if (!pSelection) { pSelectionInfoBox->UpdateHeight(); return; }
302 // add selection description
303 if (pSelection->GetFilename())
304 pSelectionInfoBox->AddTextLine(pSelection->GetFilename(), &::GraphicsResource.TextFont, C4GUI_MessageFontClr, true, false);
305 }
306 }
307
SetSelection(const char * szNewSelection,bool fFilenameOnly)308 void C4FileSelDlg::SetSelection(const char *szNewSelection, bool fFilenameOnly)
309 {
310 // check all selected definitions
311 for (ListItem *pFileItem = static_cast<ListItem *>(pFileListBox->GetFirst()); pFileItem; pFileItem = static_cast<ListItem *>(pFileItem->GetNext()))
312 {
313 const char *szFileItemFilename = pFileItem->GetFilename();
314 if (fFilenameOnly) szFileItemFilename = GetFilename(szFileItemFilename);
315 pFileItem->SetChecked(SIsModule(szNewSelection, szFileItemFilename));
316 }
317 }
318
GetSelection(const char * szFixedSelection,bool fFilenameOnly) const319 StdStrBuf C4FileSelDlg::GetSelection(const char *szFixedSelection, bool fFilenameOnly) const
320 {
321 StdStrBuf sResult;
322 if (!IsMultiSelection())
323 {
324 // get single selected file for single selection dlg
325 if (pSelection) sResult.Copy(fFilenameOnly ? GetFilename(pSelection->GetFilename()) : pSelection->GetFilename());
326 }
327 else
328 {
329 // force fixed selection first
330 if (szFixedSelection) sResult.Append(szFixedSelection);
331 // get ';'-separated list for multi selection dlg
332 for (ListItem *pFileItem = static_cast<ListItem *>(pFileListBox->GetFirst()); pFileItem; pFileItem = static_cast<ListItem *>(pFileItem->GetNext()))
333 if (pFileItem->IsChecked())
334 {
335 const char *szAppendFilename = pFileItem->GetFilename();
336 if (fFilenameOnly) szAppendFilename = GetFilename(szAppendFilename);
337 // prevent adding entries twice (especially those from the fixed selection list)
338 if (!SIsModule(sResult.getData(), szAppendFilename))
339 {
340 if (sResult.getLength()) sResult.AppendChar(';');
341 sResult.Append(szAppendFilename);
342 }
343 }
344 }
345 return sResult;
346 }
347
AddLocation(const char * szName,const char * szPath)348 void C4FileSelDlg::AddLocation(const char *szName, const char *szPath)
349 {
350 // add to list
351 int32_t iNewLocCount = iLocationCount+1;
352 Location *pNewLocations = new Location[iNewLocCount];
353 for (int32_t i=0; i<iLocationCount; ++i) pNewLocations[i] = pLocations[i];
354 pNewLocations[iLocationCount].sName.Copy(szName);
355 pNewLocations[iLocationCount].sPath.Copy(szPath);
356 delete [] pLocations; pLocations = pNewLocations; iLocationCount = iNewLocCount;
357 // first location? Then set path to this
358 if (iLocationCount == 1) SetPath(szPath, false);
359 }
360
AddCheckedLocation(const char * szName,const char * szPath)361 void C4FileSelDlg::AddCheckedLocation(const char *szName, const char *szPath)
362 {
363 // check location
364 // path must exit
365 if (!szPath || !*szPath) return;
366 if (!DirectoryExists(szPath)) return;
367 // path must not be in list yet
368 for (int32_t i=0; i<iLocationCount; ++i)
369 if (ItemIdentical(szPath, pLocations[i].sPath.getData()))
370 return;
371 // OK; add it!
372 AddLocation(szName, szPath);
373 }
374
GetCurrentLocationIndex() const375 int32_t C4FileSelDlg::GetCurrentLocationIndex() const
376 {
377 return iCurrentLocationIndex;
378 }
379
SetCurrentLocation(int32_t idx,bool fRefresh)380 void C4FileSelDlg::SetCurrentLocation(int32_t idx, bool fRefresh)
381 {
382 // safety
383 if (!Inside<int32_t>(idx, 0,iLocationCount)) return;
384 // update ComboBox-text
385 iCurrentLocationIndex = idx;
386 if (pLocationComboBox) pLocationComboBox->SetText(pLocations[idx].sName.getData());
387 // set new path
388 SetPath(pLocations[idx].sPath.getData(), fRefresh);
389 }
390
391
392 // ---------------------------------------------------
393 // C4PlayerSelDlg
394
C4PlayerSelDlg(C4FileSel_BaseCB * pSelCallback)395 C4PlayerSelDlg::C4PlayerSelDlg(C4FileSel_BaseCB *pSelCallback)
396 : C4FileSelDlg(Config.General.UserDataPath, LoadResStr("IDS_MSG_SELECTPLR"), pSelCallback)
397 {
398 }
399
400
401 // ---------------------------------------------------
402 // C4DefinitionSelDlg
403
C4DefinitionSelDlg(C4FileSel_BaseCB * pSelCallback,const char * szFixedSelection)404 C4DefinitionSelDlg::C4DefinitionSelDlg(C4FileSel_BaseCB *pSelCallback, const char *szFixedSelection)
405 : C4FileSelDlg(Config.General.UserDataPath, FormatString(LoadResStr("IDS_MSG_SELECT"), LoadResStr("IDS_DLG_DEFINITIONS")).getData(), pSelCallback)
406 {
407 if (szFixedSelection) sFixedSelection.Copy(szFixedSelection);
408 }
409
OnShown()410 void C4DefinitionSelDlg::OnShown()
411 {
412 // base call: load file list
413 C4FileSelDlg::OnShown();
414 // initial selection
415 if (sFixedSelection) SetSelection(sFixedSelection.getData(), true);
416 }
417
IsItemGrayed(const char * szFilename) const418 bool C4DefinitionSelDlg::IsItemGrayed(const char *szFilename) const
419 {
420 // cannot change initial selection
421 if (!sFixedSelection) return false;
422 return SIsModule(sFixedSelection.getData(), GetFilename(szFilename));
423 }
424
SelectDefinitions(C4GUI::Screen * pOnScreen,StdStrBuf * pSelection)425 bool C4DefinitionSelDlg::SelectDefinitions(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
426 {
427 // let the user select definitions by showing a modal selection dialog
428 C4DefinitionSelDlg *pDlg = new C4DefinitionSelDlg(nullptr, pSelection->getData());
429 bool fResult;
430 if ((fResult = pOnScreen->ShowModalDlg(pDlg, false)))
431 {
432 pSelection->Copy(pDlg->GetSelection(pSelection->getData(), true));
433 }
434 delete pDlg;
435 return fResult;
436 }
437
438
439
440 // ---------------------------------------------------
441 // C4PortraitSelDlg::ListItem
442
ListItem(const char * szFilename)443 C4PortraitSelDlg::ListItem::ListItem(const char *szFilename) : C4FileSelDlg::ListItem(szFilename)
444 , fError(false), fLoaded(false)
445 {
446 CStdFont *pUseFont = &(::GraphicsResource.MiniFont);
447 // determine label text
448 StdStrBuf sDisplayLabel;
449 if (szFilename)
450 {
451 sDisplayLabel.Copy(::GetFilename(szFilename));
452 ::RemoveExtension(&sDisplayLabel);
453 }
454 else
455 {
456 sDisplayLabel.Ref(LoadResStr("IDS_MSG_NOPORTRAIT"));
457 }
458 // insert linebreaks into label text
459 int32_t iLineHgt = std::max<int32_t>(pUseFont->BreakMessage(sDisplayLabel.getData(), ImagePreviewSize-6, &sFilenameLabelText, false), 1);
460 // set size
461 SetBounds(C4Rect(0,0,ImagePreviewSize,ImagePreviewSize+iLineHgt));
462 }
463
Load()464 void C4PortraitSelDlg::ListItem::Load()
465 {
466 if (sFilename)
467 {
468 // safety
469 fLoaded = false;
470 // load image file
471 C4Group SrcGrp;
472 StdStrBuf sParentPath;
473 GetParentPath(sFilename.getData(), &sParentPath);
474 bool fLoadError = true;
475 if (SrcGrp.Open(sParentPath.getData()))
476 if (fctLoadedImage.Load(SrcGrp, ::GetFilename(sFilename.getData()), C4FCT_Full, C4FCT_Full, false, 0))
477 {
478 // image loaded. Can only be put into facet by main thread, because those operations aren't thread safe
479 fLoaded = true;
480 fLoadError = false;
481 }
482 SrcGrp.Close();
483 fError = fLoadError;
484 }
485 }
486
DrawElement(C4TargetFacet & cgo)487 void C4PortraitSelDlg::ListItem::DrawElement(C4TargetFacet &cgo)
488 {
489 // Scale down newly loaded image?
490 if (fLoaded)
491 {
492 fLoaded = false;
493 if (!fctImage.CopyFromSfcMaxSize(fctLoadedImage.GetFace(), ImagePreviewSize))
494 fError = true;
495 fctLoadedImage.GetFace().Clear();
496 fctLoadedImage.Clear();
497 }
498 // Draw picture
499 CStdFont *pUseFont = &(::GraphicsResource.MiniFont);
500 C4Facet cgoPicture(cgo.Surface, cgo.TargetX+rcBounds.x, cgo.TargetY+rcBounds.y, ImagePreviewSize, ImagePreviewSize);
501 if (fError || !sFilename)
502 {
503 C4Facet &fctNoneImg = ::GraphicsResource.fctOKCancel;
504 fctNoneImg.Draw(cgoPicture.Surface, cgoPicture.X+(cgoPicture.Wdt-fctNoneImg.Wdt)/2, cgoPicture.Y+(cgoPicture.Hgt-fctNoneImg.Hgt)/2, 1,0);
505 }
506 else
507 {
508 if (!fctImage.Surface)
509 {
510 // not loaded yet
511 pDraw->TextOut(LoadResStr("IDS_PRC_INITIALIZE"), ::GraphicsResource.MiniFont, 1.0f, cgo.Surface, cgoPicture.X+cgoPicture.Wdt/2, cgoPicture.Y+(cgoPicture.Hgt-::GraphicsResource.MiniFont.GetLineHeight())/2, C4GUI_StatusFontClr, ACenter, false);
512 }
513 else
514 {
515 fctImage.Draw(cgoPicture);
516 }
517 }
518 // draw filename
519 pDraw->TextOut(sFilenameLabelText.getData(), *pUseFont, 1.0f, cgo.Surface, cgoPicture.X+rcBounds.Wdt/2, cgoPicture.Y+cgoPicture.Hgt, C4GUI_MessageFontClr, ACenter, false);
520 }
521
522
523 // ---------------------------------------------------
524 // C4PortraitSelDlg::LoaderThread
525
ClearLoadItems()526 void C4PortraitSelDlg::LoaderThread::ClearLoadItems()
527 {
528 // stop thread so list can be accessed
529 Stop();
530 // clear list
531 LoadItems.clear();
532 }
533
AddLoadItem(ListItem * pItem)534 void C4PortraitSelDlg::LoaderThread::AddLoadItem(ListItem *pItem)
535 {
536 // not to be called when thread is running!
537 assert(!IsStarted());
538 LoadItems.push_back(pItem);
539 }
540
Execute()541 void C4PortraitSelDlg::LoaderThread::Execute()
542 {
543 // list empty?
544 if (!LoadItems.size())
545 {
546 // then we're done!
547 SignalStop();
548 return;
549 }
550 // load one item at the time
551 ListItem *pLoadItem = LoadItems.front();
552 pLoadItem->Load();
553 LoadItems.erase(LoadItems.begin());
554 }
555
556 // ---------------------------------------------------
557 // C4PortraitSelDlg
558
C4PortraitSelDlg(C4FileSel_BaseCB * pSelCallback)559 C4PortraitSelDlg::C4PortraitSelDlg(C4FileSel_BaseCB *pSelCallback)
560 : C4FileSelDlg(Config.General.SystemDataPath, FormatString(LoadResStr("IDS_MSG_SELECT"), LoadResStr("IDS_TYPE_PORTRAIT")).getData(), pSelCallback, false)
561 {
562 char path[_MAX_PATH+1];
563 // add common picture locations
564 StdStrBuf strLocation;
565 SCopy(Config.General.UserDataPath, path, _MAX_PATH); TruncateBackslash(path);
566 strLocation.Format("%s %s", C4ENGINECAPTION, LoadResStr("IDS_TEXT_USERPATH"));
567 AddLocation(strLocation.getData(), path);
568 SCopy(Config.General.SystemDataPath, path, _MAX_PATH); TruncateBackslash(path);
569 strLocation.Format("%s %s", C4ENGINECAPTION, LoadResStr("IDS_TEXT_PROGRAMDIRECTORY"));
570 AddCheckedLocation(strLocation.getData(), path);
571 #ifdef _WIN32
572 wchar_t wpath[MAX_PATH+1];
573 if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_PERSONAL, false)) AddCheckedLocation(LoadResStr("IDS_TEXT_MYDOCUMENTS"), StdStrBuf(wpath).getData());
574 if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_MYPICTURES, false)) AddCheckedLocation(LoadResStr("IDS_TEXT_MYPICTURES"), StdStrBuf(wpath).getData());
575 if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_DESKTOPDIRECTORY, false)) AddCheckedLocation(LoadResStr("IDS_TEXT_DESKTOP"), StdStrBuf(wpath).getData());
576 #endif
577 #ifdef __APPLE__
578 AddCheckedLocation(LoadResStr("IDS_TEXT_HOME"), getenv("HOME"));
579 #else
580 AddCheckedLocation(LoadResStr("IDS_TEXT_HOMEFOLDER"), getenv("HOME"));
581 #endif
582 #ifndef _WIN32
583 sprintf(path, "%s%c%s", getenv("HOME"), (char)DirectorySeparator, (const char *)"Desktop");
584 AddCheckedLocation(LoadResStr("IDS_TEXT_DESKTOP"), path);
585 #endif
586 // build dialog
587 InitElements();
588 // select last location
589 SetCurrentLocation(Config.Startup.LastPortraitFolderIdx, false);
590 }
591
OnClosed(bool fOK)592 void C4PortraitSelDlg::OnClosed(bool fOK)
593 {
594 // remember location
595 Config.Startup.LastPortraitFolderIdx = GetCurrentLocationIndex();
596 // inherited
597 C4FileSelDlg::OnClosed(fOK);
598 }
599
CreateListItem(const char * szFilename)600 C4FileSelDlg::ListItem *C4PortraitSelDlg::CreateListItem(const char *szFilename)
601 {
602 // use own list item type
603 ListItem *pNew = new ListItem(szFilename);;
604 // schedule image loading
605 ImageLoader.AddLoadItem(pNew);
606 return pNew;
607 }
608
BeginFileListUpdate()609 void C4PortraitSelDlg::BeginFileListUpdate()
610 {
611 // new file list. Stop loading current
612 ImageLoader.ClearLoadItems();
613 }
614
EndFileListUpdate()615 void C4PortraitSelDlg::EndFileListUpdate()
616 {
617 #if USE_BACKGROUND_THREAD_LOAD
618 // Begin loading images
619 ImageLoader.Start();
620 #endif
621 }
622
OnIdle()623 void C4PortraitSelDlg::OnIdle()
624 {
625 #if !USE_BACKGROUND_THREAD_LOAD
626 // no multithreading? Workaround for image loading then...
627 static int32_t i = 0;
628 if (!(i++%10)) ImageLoader.Execute();
629 #endif
630 }
631
SelectPortrait(C4GUI::Screen * pOnScreen,StdStrBuf * pSelection)632 bool C4PortraitSelDlg::SelectPortrait(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
633 {
634 // let the user select a portrait by showing a modal selection dialog
635 C4PortraitSelDlg *pDlg = new C4PortraitSelDlg(nullptr);
636 bool fResult;
637 if ((fResult = pOnScreen->ShowModalDlg(pDlg, false)))
638 {
639 pSelection->Take(pDlg->GetSelection(nullptr, false));
640 }
641 delete pDlg;
642 return fResult;
643 }
644