forked from Mirrors/openclonk
644 lines
21 KiB
C++
644 lines
21 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2008-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, 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.
|
|
*/
|
|
// file selection dialogs
|
|
|
|
#include "C4Include.h"
|
|
#include "gui/C4FileSelDlg.h"
|
|
|
|
#include "C4Version.h"
|
|
#include "graphics/C4Draw.h"
|
|
#include "graphics/C4GraphicsResource.h" // only for single use of ::GraphicsResource.fctOKCancel below...
|
|
|
|
#ifdef _WIN32
|
|
#ifndef _WIN32_IE
|
|
#define _WIN32_IE 0x0400
|
|
#endif
|
|
#undef MK_ALT
|
|
#include <shlobj.h>
|
|
#ifndef CSIDL_MYPICTURES
|
|
#define CSIDL_MYPICTURES 0x0027
|
|
#endif
|
|
#endif
|
|
|
|
// def 1 if gfx loading works in background thread. Right now, it doesn't
|
|
// C4Group and C4Surface just don't like it
|
|
// So for now, the loading will be done in 1/10 of the frames in OnIdle of the dialog
|
|
#define USE_BACKGROUND_THREAD_LOAD 0
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4FileSelDlg::ListItem
|
|
|
|
C4FileSelDlg::ListItem::ListItem(const char *szFilename) : C4GUI::Control(C4Rect(0,0,0,0))
|
|
{
|
|
if (szFilename) sFilename.Copy(szFilename); else sFilename.Clear();
|
|
}
|
|
|
|
C4FileSelDlg::ListItem::~ListItem() = default;
|
|
|
|
// ---------------------------------------------------
|
|
// C4FileSelDlg::DefaultListItem
|
|
|
|
C4FileSelDlg::DefaultListItem::DefaultListItem(const char *szFilename, bool fTruncateExtension, bool fCheckbox, bool fGrayed, C4GUI::Icons eIcon)
|
|
: C4FileSelDlg::ListItem(szFilename), pLbl(nullptr), pCheck(nullptr), pKeyCheck(nullptr), fGrayed(fGrayed)
|
|
{
|
|
StdStrBuf sLabel; if (szFilename) sLabel.Ref(::GetFilename(szFilename)); else sLabel.Ref(LoadResStr("IDS_CTL_NONE"));
|
|
if (szFilename && fTruncateExtension)
|
|
{
|
|
sLabel.Copy();
|
|
char *szFilename = sLabel.GrabPointer();
|
|
RemoveExtension(szFilename);
|
|
sLabel.Take(szFilename);
|
|
}
|
|
rcBounds.Hgt = ::GraphicsResource.TextFont.GetLineHeight();
|
|
UpdateSize();
|
|
C4GUI::ComponentAligner caMain(GetContainedClientRect(),0,0);
|
|
int32_t iHeight = caMain.GetInnerHeight();
|
|
if (fCheckbox)
|
|
{
|
|
pCheck = new C4GUI::CheckBox(caMain.GetFromLeft(iHeight), nullptr, false);
|
|
if (fGrayed) pCheck->SetEnabled(false);
|
|
AddElement(pCheck);
|
|
pKeyCheck = new C4KeyBinding(C4KeyCodeEx(K_SPACE), "FileSelToggleFileActive", KEYSCOPE_Gui,
|
|
new C4GUI::ControlKeyCB<ListItem>(*this, &ListItem::UserToggleCheck), C4CustomKey::PRIO_Ctrl);
|
|
}
|
|
C4GUI::Icon *pIco = new C4GUI::Icon(caMain.GetFromLeft(iHeight), eIcon);
|
|
AddElement(pIco);
|
|
pLbl = new C4GUI::Label(sLabel.getData(), caMain.GetAll(), ALeft, fGrayed ? C4GUI_CheckboxDisabledFontClr : C4GUI_CheckboxFontClr);
|
|
AddElement(pLbl);
|
|
}
|
|
|
|
C4FileSelDlg::DefaultListItem::~DefaultListItem()
|
|
{
|
|
if (pKeyCheck) delete pKeyCheck;
|
|
}
|
|
|
|
void C4FileSelDlg::DefaultListItem::UpdateOwnPos()
|
|
{
|
|
BaseClass::UpdateOwnPos();
|
|
if (!pLbl) return;
|
|
C4GUI::ComponentAligner caMain(GetContainedClientRect(),0,0);
|
|
caMain.GetFromLeft(caMain.GetInnerHeight()*(1+!!pCheck));
|
|
pLbl->SetBounds(caMain.GetAll());
|
|
}
|
|
|
|
bool C4FileSelDlg::DefaultListItem::IsChecked() const
|
|
{
|
|
return pCheck ? pCheck->GetChecked() : false;
|
|
}
|
|
|
|
void C4FileSelDlg::DefaultListItem::SetChecked(bool fChecked)
|
|
{
|
|
// store new state in checkbox
|
|
if (pCheck) pCheck->SetChecked(fChecked);
|
|
}
|
|
|
|
bool C4FileSelDlg::DefaultListItem::UserToggleCheck()
|
|
{
|
|
// toggle if possible
|
|
if (pCheck && !IsGrayed())
|
|
{
|
|
pCheck->ToggleCheck(true);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4FileSelDlg
|
|
|
|
C4FileSelDlg::C4FileSelDlg(const char *szRootPath, const char *szTitle, C4FileSel_BaseCB *pSelCallback, bool fInitElements)
|
|
: C4GUI::Dialog(Clamp(C4GUI::GetScreenWdt()*2/3+10, 300,600), Clamp(C4GUI::GetScreenHgt()*2/3+10, 220,500), szTitle, false),
|
|
pLocationComboBox(nullptr), pFileListBox(nullptr), pSelectionInfoBox(nullptr), btnOK(nullptr), pLocations(nullptr), iLocationCount(0), pSelection(nullptr), pSelCallback(pSelCallback)
|
|
{
|
|
sTitle.Copy(szTitle);
|
|
// key bindings
|
|
pKeyRefresh = new C4KeyBinding(C4KeyCodeEx(K_F5), "FileSelReload", KEYSCOPE_Gui,
|
|
new C4GUI::DlgKeyCB<C4FileSelDlg>(*this, &C4FileSelDlg::KeyRefresh), C4CustomKey::PRIO_CtrlOverride);
|
|
pKeyEnterOverride = new C4KeyBinding(C4KeyCodeEx(K_RETURN), "FileSelConfirm", KEYSCOPE_Gui,
|
|
new C4GUI::DlgKeyCB<C4FileSelDlg>(*this, &C4FileSelDlg::KeyEnter), C4CustomKey::PRIO_CtrlOverride);
|
|
if (fInitElements) InitElements();
|
|
sPath.Copy(szRootPath);
|
|
}
|
|
|
|
void C4FileSelDlg::InitElements()
|
|
{
|
|
UpdateSize();
|
|
CStdFont *pUseFont = &(::GraphicsResource.TextFont);
|
|
// main calcs
|
|
bool fHasOptions = HasExtraOptions();
|
|
C4GUI::ComponentAligner caMain(GetClientRect(), 0,0, true);
|
|
C4Rect rcOptions;
|
|
C4GUI::ComponentAligner caButtonArea(caMain.GetFromBottom(C4GUI_ButtonAreaHgt, 2*C4GUI_DefButton2Wdt+4*C4GUI_DefButton2HSpace),C4GUI_DefButton2HSpace,(C4GUI_ButtonAreaHgt-C4GUI_ButtonHgt)/2);
|
|
if (fHasOptions) rcOptions = caMain.GetFromBottom(pUseFont->GetLineHeight() + 2*C4GUI_DefDlgSmallIndent);
|
|
C4GUI::ComponentAligner caUpperArea(caMain.GetAll(), C4GUI_DefDlgIndent, C4GUI_DefDlgIndent, true);
|
|
// create file selection area
|
|
if (iLocationCount)
|
|
{
|
|
C4GUI::ComponentAligner caLocations(caUpperArea.GetFromTop(C4GUI::ComboBox::GetDefaultHeight() + 2*C4GUI_DefDlgSmallIndent), C4GUI_DefDlgIndent,C4GUI_DefDlgSmallIndent, false);
|
|
StdStrBuf sText(LoadResStr("IDS_TEXT_LOCATION"));
|
|
AddElement(new C4GUI::Label(sText.getData(), caLocations.GetFromLeft(pUseFont->GetTextWidth(sText.getData())), ALeft));
|
|
pLocationComboBox = new C4GUI::ComboBox(caLocations.GetAll());
|
|
pLocationComboBox->SetComboCB(new C4GUI::ComboBox_FillCallback<C4FileSelDlg>(this, &C4FileSelDlg::OnLocationComboFill, &C4FileSelDlg::OnLocationComboSelChange));
|
|
pLocationComboBox->SetText(pLocations[0].sName.getData());
|
|
}
|
|
// create file selection area
|
|
bool fHasPreview = HasPreviewArea();
|
|
pFileListBox = new C4GUI::ListBox(fHasPreview ? caUpperArea.GetFromLeft(caUpperArea.GetWidth()/2) : caUpperArea.GetAll(), GetFileSelColWidth());
|
|
pFileListBox ->SetSelectionChangeCallbackFn(new C4GUI::CallbackHandler<C4FileSelDlg>(this, &C4FileSelDlg::OnSelChange));
|
|
pFileListBox ->SetSelectionDblClickFn(new C4GUI::CallbackHandler<C4FileSelDlg>(this, &C4FileSelDlg::OnSelDblClick));
|
|
if (fHasPreview)
|
|
{
|
|
caUpperArea.ExpandLeft(C4GUI_DefDlgIndent);
|
|
pSelectionInfoBox = new C4GUI::TextWindow(caUpperArea.GetAll());
|
|
pSelectionInfoBox->SetDecoration(true, true, nullptr, true);
|
|
}
|
|
// create button area
|
|
C4GUI::Button *btnAbort = new C4GUI::CancelButton(caButtonArea.GetFromRight(C4GUI_DefButton2Wdt));
|
|
btnOK = new C4GUI::OKButton(caButtonArea.GetFromRight(C4GUI_DefButton2Wdt));
|
|
// add components in tab order
|
|
if (pLocationComboBox) AddElement(pLocationComboBox);
|
|
AddElement(pFileListBox);
|
|
if (pSelectionInfoBox) AddElement(pSelectionInfoBox);
|
|
if (fHasOptions) AddExtraOptions(rcOptions);
|
|
AddElement(btnOK);
|
|
AddElement(btnAbort);
|
|
SetFocus(pFileListBox, false);
|
|
// no selection yet
|
|
UpdateSelection();
|
|
}
|
|
|
|
C4FileSelDlg::~C4FileSelDlg()
|
|
{
|
|
delete [] pLocations;
|
|
if (pSelCallback) delete pSelCallback;
|
|
delete pKeyEnterOverride;
|
|
delete pKeyRefresh;
|
|
}
|
|
|
|
void C4FileSelDlg::OnLocationComboFill(C4GUI::ComboBox_FillCB *pFiller)
|
|
{
|
|
// Add all locations
|
|
for (int32_t i=0; i<iLocationCount; ++i)
|
|
pFiller->AddEntry(pLocations[i].sName.getData(), i);
|
|
}
|
|
|
|
bool C4FileSelDlg::OnLocationComboSelChange(C4GUI::ComboBox *pForCombo, int32_t idNewSelection)
|
|
{
|
|
SetCurrentLocation(idNewSelection, true);
|
|
// No text change by caller; text alread changed by SetCurrentLocation
|
|
return true;
|
|
}
|
|
|
|
void C4FileSelDlg::SetPath(const char *szNewPath, bool fRefresh)
|
|
{
|
|
sPath.Copy(szNewPath);
|
|
if (fRefresh && IsShown()) UpdateFileList();
|
|
}
|
|
|
|
void C4FileSelDlg::OnShown()
|
|
{
|
|
BaseClass::OnShown();
|
|
// load files
|
|
UpdateFileList();
|
|
}
|
|
|
|
void C4FileSelDlg::UserClose(bool fOK)
|
|
{
|
|
if (!fOK || pSelection)
|
|
{
|
|
// allow OK only if something is sth is selected
|
|
Close(fOK);
|
|
}
|
|
else
|
|
{
|
|
GetScreen()->ShowErrorMessage(LoadResStr("IDS_ERR_PLEASESELECTAFILEFIRST"));
|
|
}
|
|
}
|
|
|
|
void C4FileSelDlg::OnClosed(bool fOK)
|
|
{
|
|
if (fOK && pSelection && pSelCallback)
|
|
pSelCallback->OnFileSelected(pSelection->GetFilename());
|
|
// base call: Might delete dlg
|
|
BaseClass::OnClosed(fOK);
|
|
}
|
|
|
|
void C4FileSelDlg::OnSelDblClick(class C4GUI::Element *pEl)
|
|
{
|
|
// item double-click: confirms with this file in single mode; toggles selection in multi mode
|
|
if (IsMultiSelection())
|
|
{
|
|
ListItem *pItem = static_cast<ListItem *>(pEl);
|
|
pItem->UserToggleCheck();
|
|
}
|
|
else
|
|
UserClose(true);
|
|
}
|
|
|
|
C4FileSelDlg::ListItem *C4FileSelDlg::CreateListItem(const char *szFilename)
|
|
{
|
|
// Default list item
|
|
if (szFilename)
|
|
return new DefaultListItem(szFilename, !!GetFileMask(), IsMultiSelection(), IsItemGrayed(szFilename), GetFileItemIcon());
|
|
else
|
|
return new DefaultListItem(nullptr, false, IsMultiSelection(), false, GetFileItemIcon());
|
|
}
|
|
|
|
void C4FileSelDlg::UpdateFileList()
|
|
{
|
|
BeginFileListUpdate();
|
|
// reload files
|
|
C4GUI::Element *pEl;
|
|
while ((pEl = pFileListBox->GetFirst())) delete pEl;
|
|
// file items
|
|
StdStrBuf sSearch;
|
|
const char *szFileMask = GetFileMask();
|
|
for (DirectoryIterator iter(sPath.getData()); *iter; ++iter)
|
|
if (!szFileMask || WildcardListMatch(szFileMask, *iter))
|
|
pFileListBox->AddElement(CreateListItem(*iter));
|
|
// none-item?
|
|
if (HasNoneItem())
|
|
{
|
|
pFileListBox->AddElement(CreateListItem(nullptr));
|
|
}
|
|
// list now done
|
|
EndFileListUpdate();
|
|
// path into title
|
|
const char *szPath = sPath.getData();
|
|
SetTitle(*szPath ? FormatString("%s [%s]", sTitle.getData(), szPath).getData() : sTitle.getData());
|
|
// initial no-selection
|
|
UpdateSelection();
|
|
}
|
|
|
|
void C4FileSelDlg::UpdateSelection()
|
|
{
|
|
// update selection from list
|
|
pSelection = static_cast<ListItem *>(pFileListBox->GetSelectedItem());
|
|
// OK button only available if selection
|
|
// SetEnabled would look a lot better here, but it doesn't exist yet :(
|
|
// selection preview, if enabled
|
|
if (pSelectionInfoBox)
|
|
{
|
|
// default empty
|
|
pSelectionInfoBox->ClearText(false);
|
|
if (!pSelection) { pSelectionInfoBox->UpdateHeight(); return; }
|
|
// add selection description
|
|
if (pSelection->GetFilename())
|
|
pSelectionInfoBox->AddTextLine(pSelection->GetFilename(), &::GraphicsResource.TextFont, C4GUI_MessageFontClr, true, false);
|
|
}
|
|
}
|
|
|
|
void C4FileSelDlg::SetSelection(const char *szNewSelection, bool fFilenameOnly)
|
|
{
|
|
// check all selected definitions
|
|
for (ListItem *pFileItem = static_cast<ListItem *>(pFileListBox->GetFirst()); pFileItem; pFileItem = static_cast<ListItem *>(pFileItem->GetNext()))
|
|
{
|
|
const char *szFileItemFilename = pFileItem->GetFilename();
|
|
if (fFilenameOnly) szFileItemFilename = GetFilename(szFileItemFilename);
|
|
pFileItem->SetChecked(SIsModule(szNewSelection, szFileItemFilename));
|
|
}
|
|
}
|
|
|
|
StdStrBuf C4FileSelDlg::GetSelection(const char *szFixedSelection, bool fFilenameOnly) const
|
|
{
|
|
StdStrBuf sResult;
|
|
if (!IsMultiSelection())
|
|
{
|
|
// get single selected file for single selection dlg
|
|
if (pSelection) sResult.Copy(fFilenameOnly ? GetFilename(pSelection->GetFilename()) : pSelection->GetFilename());
|
|
}
|
|
else
|
|
{
|
|
// force fixed selection first
|
|
if (szFixedSelection) sResult.Append(szFixedSelection);
|
|
// get ';'-separated list for multi selection dlg
|
|
for (ListItem *pFileItem = static_cast<ListItem *>(pFileListBox->GetFirst()); pFileItem; pFileItem = static_cast<ListItem *>(pFileItem->GetNext()))
|
|
if (pFileItem->IsChecked())
|
|
{
|
|
const char *szAppendFilename = pFileItem->GetFilename();
|
|
if (fFilenameOnly) szAppendFilename = GetFilename(szAppendFilename);
|
|
// prevent adding entries twice (especially those from the fixed selection list)
|
|
if (!SIsModule(sResult.getData(), szAppendFilename))
|
|
{
|
|
if (sResult.getLength()) sResult.AppendChar(';');
|
|
sResult.Append(szAppendFilename);
|
|
}
|
|
}
|
|
}
|
|
return sResult;
|
|
}
|
|
|
|
void C4FileSelDlg::AddLocation(const char *szName, const char *szPath)
|
|
{
|
|
// add to list
|
|
int32_t iNewLocCount = iLocationCount+1;
|
|
Location *pNewLocations = new Location[iNewLocCount];
|
|
for (int32_t i=0; i<iLocationCount; ++i) pNewLocations[i] = pLocations[i];
|
|
pNewLocations[iLocationCount].sName.Copy(szName);
|
|
pNewLocations[iLocationCount].sPath.Copy(szPath);
|
|
delete [] pLocations; pLocations = pNewLocations; iLocationCount = iNewLocCount;
|
|
// first location? Then set path to this
|
|
if (iLocationCount == 1) SetPath(szPath, false);
|
|
}
|
|
|
|
void C4FileSelDlg::AddCheckedLocation(const char *szName, const char *szPath)
|
|
{
|
|
// check location
|
|
// path must exit
|
|
if (!szPath || !*szPath) return;
|
|
if (!DirectoryExists(szPath)) return;
|
|
// path must not be in list yet
|
|
for (int32_t i=0; i<iLocationCount; ++i)
|
|
if (ItemIdentical(szPath, pLocations[i].sPath.getData()))
|
|
return;
|
|
// OK; add it!
|
|
AddLocation(szName, szPath);
|
|
}
|
|
|
|
int32_t C4FileSelDlg::GetCurrentLocationIndex() const
|
|
{
|
|
return iCurrentLocationIndex;
|
|
}
|
|
|
|
void C4FileSelDlg::SetCurrentLocation(int32_t idx, bool fRefresh)
|
|
{
|
|
// safety
|
|
if (!Inside<int32_t>(idx, 0,iLocationCount)) return;
|
|
// update ComboBox-text
|
|
iCurrentLocationIndex = idx;
|
|
if (pLocationComboBox) pLocationComboBox->SetText(pLocations[idx].sName.getData());
|
|
// set new path
|
|
SetPath(pLocations[idx].sPath.getData(), fRefresh);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4PlayerSelDlg
|
|
|
|
C4PlayerSelDlg::C4PlayerSelDlg(C4FileSel_BaseCB *pSelCallback)
|
|
: C4FileSelDlg(Config.General.UserDataPath, LoadResStr("IDS_MSG_SELECTPLR"), pSelCallback)
|
|
{
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4DefinitionSelDlg
|
|
|
|
C4DefinitionSelDlg::C4DefinitionSelDlg(C4FileSel_BaseCB *pSelCallback, const char *szFixedSelection)
|
|
: C4FileSelDlg(Config.General.UserDataPath, FormatString(LoadResStr("IDS_MSG_SELECT"), LoadResStr("IDS_DLG_DEFINITIONS")).getData(), pSelCallback)
|
|
{
|
|
if (szFixedSelection) sFixedSelection.Copy(szFixedSelection);
|
|
}
|
|
|
|
void C4DefinitionSelDlg::OnShown()
|
|
{
|
|
// base call: load file list
|
|
C4FileSelDlg::OnShown();
|
|
// initial selection
|
|
if (sFixedSelection) SetSelection(sFixedSelection.getData(), true);
|
|
}
|
|
|
|
bool C4DefinitionSelDlg::IsItemGrayed(const char *szFilename) const
|
|
{
|
|
// cannot change initial selection
|
|
if (!sFixedSelection) return false;
|
|
return SIsModule(sFixedSelection.getData(), GetFilename(szFilename));
|
|
}
|
|
|
|
bool C4DefinitionSelDlg::SelectDefinitions(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
|
|
{
|
|
// let the user select definitions by showing a modal selection dialog
|
|
C4DefinitionSelDlg *pDlg = new C4DefinitionSelDlg(nullptr, pSelection->getData());
|
|
bool fResult;
|
|
if ((fResult = pOnScreen->ShowModalDlg(pDlg, false)))
|
|
{
|
|
pSelection->Copy(pDlg->GetSelection(pSelection->getData(), true));
|
|
}
|
|
delete pDlg;
|
|
return fResult;
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4PortraitSelDlg::ListItem
|
|
|
|
C4PortraitSelDlg::ListItem::ListItem(const char *szFilename) : C4FileSelDlg::ListItem(szFilename)
|
|
, fError(false), fLoaded(false)
|
|
{
|
|
CStdFont *pUseFont = &(::GraphicsResource.MiniFont);
|
|
// determine label text
|
|
StdStrBuf sDisplayLabel;
|
|
if (szFilename)
|
|
{
|
|
sDisplayLabel.Copy(::GetFilename(szFilename));
|
|
::RemoveExtension(&sDisplayLabel);
|
|
}
|
|
else
|
|
{
|
|
sDisplayLabel.Ref(LoadResStr("IDS_MSG_NOPORTRAIT"));
|
|
}
|
|
// insert linebreaks into label text
|
|
int32_t iLineHgt = std::max<int32_t>(pUseFont->BreakMessage(sDisplayLabel.getData(), ImagePreviewSize-6, &sFilenameLabelText, false), 1);
|
|
// set size
|
|
SetBounds(C4Rect(0,0,ImagePreviewSize,ImagePreviewSize+iLineHgt));
|
|
}
|
|
|
|
void C4PortraitSelDlg::ListItem::Load()
|
|
{
|
|
if (sFilename)
|
|
{
|
|
// safety
|
|
fLoaded = false;
|
|
// load image file
|
|
C4Group SrcGrp;
|
|
StdStrBuf sParentPath;
|
|
GetParentPath(sFilename.getData(), &sParentPath);
|
|
bool fLoadError = true;
|
|
if (SrcGrp.Open(sParentPath.getData()))
|
|
if (fctLoadedImage.Load(SrcGrp, ::GetFilename(sFilename.getData()), C4FCT_Full, C4FCT_Full, false, 0))
|
|
{
|
|
// image loaded. Can only be put into facet by main thread, because those operations aren't thread safe
|
|
fLoaded = true;
|
|
fLoadError = false;
|
|
}
|
|
SrcGrp.Close();
|
|
fError = fLoadError;
|
|
}
|
|
}
|
|
|
|
void C4PortraitSelDlg::ListItem::DrawElement(C4TargetFacet &cgo)
|
|
{
|
|
// Scale down newly loaded image?
|
|
if (fLoaded)
|
|
{
|
|
fLoaded = false;
|
|
if (!fctImage.CopyFromSfcMaxSize(fctLoadedImage.GetFace(), ImagePreviewSize))
|
|
fError = true;
|
|
fctLoadedImage.GetFace().Clear();
|
|
fctLoadedImage.Clear();
|
|
}
|
|
// Draw picture
|
|
CStdFont *pUseFont = &(::GraphicsResource.MiniFont);
|
|
C4Facet cgoPicture(cgo.Surface, cgo.TargetX+rcBounds.x, cgo.TargetY+rcBounds.y, ImagePreviewSize, ImagePreviewSize);
|
|
if (fError || !sFilename)
|
|
{
|
|
C4Facet &fctNoneImg = ::GraphicsResource.fctOKCancel;
|
|
fctNoneImg.Draw(cgoPicture.Surface, cgoPicture.X+(cgoPicture.Wdt-fctNoneImg.Wdt)/2, cgoPicture.Y+(cgoPicture.Hgt-fctNoneImg.Hgt)/2, 1,0);
|
|
}
|
|
else
|
|
{
|
|
if (!fctImage.Surface)
|
|
{
|
|
// not loaded yet
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
fctImage.Draw(cgoPicture);
|
|
}
|
|
}
|
|
// draw filename
|
|
pDraw->TextOut(sFilenameLabelText.getData(), *pUseFont, 1.0f, cgo.Surface, cgoPicture.X+rcBounds.Wdt/2, cgoPicture.Y+cgoPicture.Hgt, C4GUI_MessageFontClr, ACenter, false);
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------
|
|
// C4PortraitSelDlg::LoaderThread
|
|
|
|
void C4PortraitSelDlg::LoaderThread::ClearLoadItems()
|
|
{
|
|
// stop thread so list can be accessed
|
|
Stop();
|
|
// clear list
|
|
LoadItems.clear();
|
|
}
|
|
|
|
void C4PortraitSelDlg::LoaderThread::AddLoadItem(ListItem *pItem)
|
|
{
|
|
// not to be called when thread is running!
|
|
assert(!IsStarted());
|
|
LoadItems.push_back(pItem);
|
|
}
|
|
|
|
void C4PortraitSelDlg::LoaderThread::Execute()
|
|
{
|
|
// list empty?
|
|
if (!LoadItems.size())
|
|
{
|
|
// then we're done!
|
|
SignalStop();
|
|
return;
|
|
}
|
|
// load one item at the time
|
|
ListItem *pLoadItem = LoadItems.front();
|
|
pLoadItem->Load();
|
|
LoadItems.erase(LoadItems.begin());
|
|
}
|
|
|
|
// ---------------------------------------------------
|
|
// C4PortraitSelDlg
|
|
|
|
C4PortraitSelDlg::C4PortraitSelDlg(C4FileSel_BaseCB *pSelCallback)
|
|
: C4FileSelDlg(Config.General.SystemDataPath, FormatString(LoadResStr("IDS_MSG_SELECT"), LoadResStr("IDS_TYPE_PORTRAIT")).getData(), pSelCallback, false)
|
|
{
|
|
char path[_MAX_PATH+1];
|
|
// add common picture locations
|
|
StdStrBuf strLocation;
|
|
SCopy(Config.General.UserDataPath, path, _MAX_PATH); TruncateBackslash(path);
|
|
strLocation.Format("%s %s", C4ENGINECAPTION, LoadResStr("IDS_TEXT_USERPATH"));
|
|
AddLocation(strLocation.getData(), path);
|
|
SCopy(Config.General.SystemDataPath, path, _MAX_PATH); TruncateBackslash(path);
|
|
strLocation.Format("%s %s", C4ENGINECAPTION, LoadResStr("IDS_TEXT_PROGRAMDIRECTORY"));
|
|
AddCheckedLocation(strLocation.getData(), path);
|
|
#ifdef _WIN32
|
|
wchar_t wpath[MAX_PATH+1];
|
|
if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_PERSONAL, false)) AddCheckedLocation(LoadResStr("IDS_TEXT_MYDOCUMENTS"), StdStrBuf(wpath).getData());
|
|
if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_MYPICTURES, false)) AddCheckedLocation(LoadResStr("IDS_TEXT_MYPICTURES"), StdStrBuf(wpath).getData());
|
|
if (SHGetSpecialFolderPathW(nullptr, wpath, CSIDL_DESKTOPDIRECTORY, false)) AddCheckedLocation(LoadResStr("IDS_TEXT_DESKTOP"), StdStrBuf(wpath).getData());
|
|
#endif
|
|
#ifdef __APPLE__
|
|
AddCheckedLocation(LoadResStr("IDS_TEXT_HOME"), getenv("HOME"));
|
|
#else
|
|
AddCheckedLocation(LoadResStr("IDS_TEXT_HOMEFOLDER"), getenv("HOME"));
|
|
#endif
|
|
#ifndef _WIN32
|
|
sprintf(path, "%s%c%s", getenv("HOME"), (char)DirectorySeparator, (const char *)"Desktop");
|
|
AddCheckedLocation(LoadResStr("IDS_TEXT_DESKTOP"), path);
|
|
#endif
|
|
// build dialog
|
|
InitElements();
|
|
// select last location
|
|
SetCurrentLocation(Config.Startup.LastPortraitFolderIdx, false);
|
|
}
|
|
|
|
void C4PortraitSelDlg::OnClosed(bool fOK)
|
|
{
|
|
// remember location
|
|
Config.Startup.LastPortraitFolderIdx = GetCurrentLocationIndex();
|
|
// inherited
|
|
C4FileSelDlg::OnClosed(fOK);
|
|
}
|
|
|
|
C4FileSelDlg::ListItem *C4PortraitSelDlg::CreateListItem(const char *szFilename)
|
|
{
|
|
// use own list item type
|
|
ListItem *pNew = new ListItem(szFilename);;
|
|
// schedule image loading
|
|
ImageLoader.AddLoadItem(pNew);
|
|
return pNew;
|
|
}
|
|
|
|
void C4PortraitSelDlg::BeginFileListUpdate()
|
|
{
|
|
// new file list. Stop loading current
|
|
ImageLoader.ClearLoadItems();
|
|
}
|
|
|
|
void C4PortraitSelDlg::EndFileListUpdate()
|
|
{
|
|
#if USE_BACKGROUND_THREAD_LOAD
|
|
// Begin loading images
|
|
ImageLoader.Start();
|
|
#endif
|
|
}
|
|
|
|
void C4PortraitSelDlg::OnIdle()
|
|
{
|
|
#if !USE_BACKGROUND_THREAD_LOAD
|
|
// no multithreading? Workaround for image loading then...
|
|
static int32_t i = 0;
|
|
if (!(i++%10)) ImageLoader.Execute();
|
|
#endif
|
|
}
|
|
|
|
bool C4PortraitSelDlg::SelectPortrait(C4GUI::Screen *pOnScreen, StdStrBuf *pSelection)
|
|
{
|
|
// let the user select a portrait by showing a modal selection dialog
|
|
C4PortraitSelDlg *pDlg = new C4PortraitSelDlg(nullptr);
|
|
bool fResult;
|
|
if ((fResult = pOnScreen->ShowModalDlg(pDlg, false)))
|
|
{
|
|
pSelection->Take(pDlg->GetSelection(nullptr, false));
|
|
}
|
|
delete pDlg;
|
|
return fResult;
|
|
}
|