mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
726 lines
16 KiB
C++
726 lines
16 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2003-2008 Martin Freij
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Nestopia is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "NstWindowUser.hpp"
|
|
#include "NstWindowDropFiles.hpp"
|
|
#include "NstResourceString.hpp"
|
|
#include "NstDialogFind.hpp"
|
|
#include "NstManagerPaths.hpp"
|
|
#include "NstDialogLauncher.hpp"
|
|
#include <Shlwapi.h>
|
|
|
|
namespace Nestopia
|
|
{
|
|
namespace Window
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
wcstring Launcher::List::Strings::GetMapper(const uint value)
|
|
{
|
|
if (value-1 < 4095)
|
|
{
|
|
uint count = mappers.Size();
|
|
|
|
if (count <= value)
|
|
{
|
|
const uint size = NST_MAX(256,count+value+1);
|
|
mappers.Resize( size );
|
|
|
|
do
|
|
{
|
|
_itow( count, mappers[count].string, 10 );
|
|
}
|
|
while (++count != size);
|
|
}
|
|
|
|
return mappers[value].string;
|
|
}
|
|
else
|
|
{
|
|
return L"-";
|
|
}
|
|
}
|
|
|
|
wcstring Launcher::List::Strings::GetSize(uint value)
|
|
{
|
|
if (value)
|
|
{
|
|
SizeString& string = sizes[value];
|
|
|
|
if (string.Empty())
|
|
string << value << 'k';
|
|
|
|
return string.Ptr();
|
|
}
|
|
|
|
return L"-";
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
void Launcher::List::Strings::Flush()
|
|
{
|
|
sizes.clear();
|
|
mappers.Destroy();
|
|
}
|
|
|
|
Launcher::List::List
|
|
(
|
|
Dialog& dialog,
|
|
Menu::CmdHandler& cmdHandler,
|
|
const Managers::Paths& p,
|
|
const Configuration& cfg,
|
|
const Nes::Cartridge::Database& database
|
|
)
|
|
:
|
|
imageDatabase ( database ),
|
|
useImageDatabase ( NULL ),
|
|
typeFilter ( 0 ),
|
|
style ( STYLE ),
|
|
finder ( dialog ),
|
|
paths ( cfg ),
|
|
columns ( cfg ),
|
|
pathManager ( p )
|
|
{
|
|
static const Menu::CmdHandler::Entry<Launcher::List> commands[] =
|
|
{
|
|
{ IDM_LAUNCHER_EDIT_FIND, &List::OnCmdEditFind },
|
|
{ IDM_LAUNCHER_EDIT_INSERT, &List::OnCmdEditInsert },
|
|
{ IDM_LAUNCHER_EDIT_REMOVE, &List::OnCmdEditDelete },
|
|
{ IDM_LAUNCHER_EDIT_CLEAR, &List::OnCmdEditClear },
|
|
{ IDM_LAUNCHER_VIEW_ALIGNCOLUMNS, &List::OnCmdViewAlignColumns },
|
|
{ IDM_LAUNCHER_OPTIONS_COLUMNS, &List::OnCmdOptionsColumns }
|
|
};
|
|
|
|
cmdHandler.Add( this, commands );
|
|
|
|
Configuration::ConstSection show( cfg["launcher"]["view"]["show"] );
|
|
|
|
if (!show["grid-lines"].No())
|
|
style |= LVS_EX_GRIDLINES;
|
|
|
|
if (!show["image-database-adjusted"].No())
|
|
useImageDatabase = &imageDatabase;
|
|
}
|
|
|
|
Launcher::List::~List()
|
|
{
|
|
}
|
|
|
|
void Launcher::List::operator = (const Control::ListView& listView)
|
|
{
|
|
files.Load();
|
|
|
|
typeFilter = 0;
|
|
|
|
ctrl = listView;
|
|
ctrl.StyleEx() = style;
|
|
|
|
ReloadListColumns();
|
|
|
|
ctrl.Reserve( files.Count() );
|
|
ctrl.Columns().Align();
|
|
|
|
InvalidateRect( ctrl.GetWindow(), NULL, false );
|
|
}
|
|
|
|
void Launcher::List::Close()
|
|
{
|
|
finder.Close();
|
|
UpdateColumnOrder();
|
|
columns.Update( order );
|
|
strings.Flush();
|
|
}
|
|
|
|
void Launcher::List::Insert(const Param& param)
|
|
{
|
|
DropFiles dropFiles( param );
|
|
|
|
if (dropFiles.Inside( ctrl.GetHandle() ))
|
|
{
|
|
uint anyInserted = false;
|
|
|
|
for (uint i=0, n=dropFiles.Size(); i < n; ++i)
|
|
anyInserted |= uint(files.Insert( imageDatabase, dropFiles[i] ));
|
|
|
|
if (anyInserted && !Optimize())
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
void Launcher::List::Add(wcstring const fileName)
|
|
{
|
|
if (files.Insert( imageDatabase, fileName ) && !Optimize())
|
|
Redraw();
|
|
}
|
|
|
|
void Launcher::List::Save(Configuration& cfg,bool saveFiles)
|
|
{
|
|
paths.Save( cfg );
|
|
columns.Save( cfg );
|
|
|
|
if (saveFiles)
|
|
files.Save();
|
|
|
|
Configuration::Section show( cfg["launcher"]["view"]["show"] );
|
|
|
|
show["grid-lines"].YesNo() = style & LVS_EX_GRIDLINES;
|
|
show["image-database-adjusted"].YesNo() = useImageDatabase;
|
|
}
|
|
|
|
void Launcher::List::SetColors(const uint bg,const uint fg,const Updater redraw) const
|
|
{
|
|
ctrl.SetBkColor( bg );
|
|
ctrl.SetTextBkColor( bg );
|
|
ctrl.SetTextColor( fg );
|
|
|
|
if (redraw)
|
|
ctrl.Redraw();
|
|
}
|
|
|
|
void Launcher::List::Redraw()
|
|
{
|
|
Application::Instance::Waiter wait;
|
|
Generic::LockDraw lock( ctrl.GetHandle() );
|
|
|
|
ctrl.Clear();
|
|
|
|
if (const uint count = files.Count())
|
|
{
|
|
uint size = 0;
|
|
|
|
for (uint i=0; i < count; ++i)
|
|
size += (files[i].GetType() & typeFilter) != 0;
|
|
|
|
if (size)
|
|
{
|
|
ctrl.Reserve( size );
|
|
|
|
for (uint i=0; i < count; ++i)
|
|
{
|
|
if (files[i].GetType() & typeFilter)
|
|
ctrl.Add( GenericString(), &files[i] );
|
|
}
|
|
|
|
Sort();
|
|
ctrl.Columns().Align();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Launcher::List::Optimize()
|
|
{
|
|
if (files.ShouldDefrag())
|
|
{
|
|
files.Defrag();
|
|
Redraw();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Launcher::List::CanRefresh() const
|
|
{
|
|
return
|
|
(
|
|
(paths.GetSettings().folders.size()) &&
|
|
(paths.GetSettings().include.Word() & Paths::Settings::Include::TYPES)
|
|
);
|
|
}
|
|
|
|
void Launcher::List::Refresh()
|
|
{
|
|
if (CanRefresh())
|
|
{
|
|
{
|
|
Application::Instance::Waiter wait;
|
|
ctrl.Clear();
|
|
}
|
|
|
|
files.Refresh( paths.GetSettings(), imageDatabase );
|
|
ctrl.Reserve( files.Count() );
|
|
Redraw();
|
|
}
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("t", on)
|
|
#endif
|
|
|
|
void Launcher::List::OnGetDisplayInfo(LPARAM lParam)
|
|
{
|
|
LVITEM& item = reinterpret_cast<NMLVDISPINFO*>(lParam)->item;
|
|
|
|
if (item.mask & LVIF_TEXT)
|
|
{
|
|
const Files::Entry& entry = *reinterpret_cast<const Files::Entry*>( item.lParam );
|
|
|
|
switch (columns.GetType( item.iSubItem ))
|
|
{
|
|
case Columns::TYPE_FILE:
|
|
|
|
item.pszText = const_cast<wchar_t*>( entry.GetFile(files.GetStrings()) );
|
|
break;
|
|
|
|
case Columns::TYPE_SYSTEM:
|
|
{
|
|
NST_COMPILE_ASSERT
|
|
(
|
|
Files::Entry::SYSTEM_UNKNOWN == 0 &&
|
|
Files::Entry::SYSTEM_PC10 == 1 &&
|
|
Files::Entry::SYSTEM_VS == 2 &&
|
|
Files::Entry::SYSTEM_PAL == 3 &&
|
|
Files::Entry::SYSTEM_NTSC == 4 &&
|
|
Files::Entry::SYSTEM_NTSC_PAL == 5
|
|
);
|
|
|
|
static const wchar_t lut[][9] =
|
|
{
|
|
L"-",
|
|
L"pc10",
|
|
L"vs",
|
|
L"pal",
|
|
L"ntsc",
|
|
L"ntsc/pal"
|
|
};
|
|
|
|
item.pszText = const_cast<wchar_t*>( lut[entry.GetSystem( useImageDatabase )] );
|
|
break;
|
|
}
|
|
|
|
case Columns::TYPE_BATTERY:
|
|
|
|
item.pszText = const_cast<wchar_t*>
|
|
(
|
|
(entry.GetType() & (Files::Entry::NES|Files::Entry::UNF)) ?
|
|
entry.GetBattery( useImageDatabase ) ? L"yes" : L"no" : L"-"
|
|
);
|
|
break;
|
|
|
|
case Columns::TYPE_DUMP:
|
|
{
|
|
NST_COMPILE_ASSERT
|
|
(
|
|
Nes::Cartridge::Profile::Dump::OK == 0 &&
|
|
Nes::Cartridge::Profile::Dump::BAD == 1 &&
|
|
Nes::Cartridge::Profile::Dump::UNKNOWN == 2
|
|
);
|
|
|
|
static const wchar_t lut[][4] =
|
|
{
|
|
L"ok",
|
|
L"bad",
|
|
L"-"
|
|
};
|
|
|
|
item.pszText = const_cast<wchar_t*>( lut[entry.GetDump( useImageDatabase )] );
|
|
break;
|
|
}
|
|
|
|
case Columns::TYPE_NAME:
|
|
|
|
item.pszText = const_cast<wchar_t*>( entry.GetName( files.GetStrings(), useImageDatabase ) );
|
|
break;
|
|
|
|
case Columns::TYPE_FOLDER:
|
|
|
|
item.pszText = const_cast<wchar_t*>( entry.GetPath( files.GetStrings() ) );
|
|
break;
|
|
|
|
case Columns::TYPE_PROM:
|
|
|
|
item.pszText = const_cast<wchar_t*>( strings.GetSize(entry.GetPRom(useImageDatabase)) );
|
|
break;
|
|
|
|
case Columns::TYPE_CROM:
|
|
|
|
if (const uint cRom = entry.GetCRom( useImageDatabase ))
|
|
item.pszText = const_cast<wchar_t*>( strings.GetSize( cRom ) );
|
|
else
|
|
item.pszText = const_cast<wchar_t*>( L"-" );
|
|
break;
|
|
|
|
case Columns::TYPE_MAPPER:
|
|
|
|
item.pszText = const_cast<wchar_t*>( strings.GetMapper(entry.GetMapper( useImageDatabase )) );
|
|
break;
|
|
|
|
case Columns::TYPE_WRAM:
|
|
|
|
if (const uint wRam = entry.GetWRam( useImageDatabase ))
|
|
item.pszText = const_cast<wchar_t*>( strings.GetSize( wRam ) );
|
|
else
|
|
item.pszText = const_cast<wchar_t*>( L"-" );
|
|
break;
|
|
|
|
case Columns::TYPE_VRAM:
|
|
|
|
if (const uint vRam = entry.GetVRam( useImageDatabase ))
|
|
item.pszText = const_cast<wchar_t*>( strings.GetSize( vRam ) );
|
|
else
|
|
item.pszText = const_cast<wchar_t*>( L"-" );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
void Launcher::List::ReloadListColumns() const
|
|
{
|
|
ctrl.Columns().Clear();
|
|
|
|
for (uint i=0; i < columns.Count(); ++i)
|
|
ctrl.Columns().Insert( i, Resource::String(columns.GetStringId(i)).Ptr() );
|
|
}
|
|
|
|
void Launcher::List::UpdateColumnOrder()
|
|
{
|
|
int array[Columns::NUM_TYPES];
|
|
ctrl.Columns().GetOrder( array, columns.Count() );
|
|
|
|
for (uint i=0; i < columns.Count(); ++i)
|
|
order[i] = columns.GetType( array[i] );
|
|
}
|
|
|
|
void Launcher::List::UpdateSortColumnOrder(const uint firstSortColumn)
|
|
{
|
|
int array[Columns::NUM_TYPES];
|
|
ctrl.Columns().GetOrder( array, columns.Count() );
|
|
|
|
order[0] = columns.GetType( firstSortColumn );
|
|
|
|
for (uint i=0, j=1; i < columns.Count(); ++i)
|
|
{
|
|
if (firstSortColumn != array[i])
|
|
order[j++] = columns.GetType( array[i] );
|
|
}
|
|
}
|
|
|
|
void Launcher::List::Sort(const uint firstSortColumn)
|
|
{
|
|
if (ctrl.Size() > 1)
|
|
{
|
|
UpdateSortColumnOrder( firstSortColumn );
|
|
ctrl.Sort( this, &List::Sorter );
|
|
}
|
|
}
|
|
|
|
int Launcher::List::Sorter(const void* obj1,const void* obj2)
|
|
{
|
|
const Files::Entry& a = *static_cast<const Files::Entry*>( obj1 );
|
|
const Files::Entry& b = *static_cast<const Files::Entry*>( obj2 );
|
|
|
|
for (uint i=0, n=columns.Count(); i < n; ++i)
|
|
{
|
|
switch (order[i])
|
|
{
|
|
case Columns::TYPE_FILE:
|
|
|
|
if (const int ret = ::StrCmp( a.GetFile(files.GetStrings()), b.GetFile(files.GetStrings()) ))
|
|
return ret;
|
|
|
|
continue;
|
|
|
|
case Columns::TYPE_SYSTEM:
|
|
{
|
|
const uint system[] =
|
|
{
|
|
a.GetSystem( useImageDatabase ),
|
|
b.GetSystem( useImageDatabase )
|
|
};
|
|
|
|
if (system[0] == system[1])
|
|
continue;
|
|
|
|
return system[0] < system[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_MAPPER:
|
|
{
|
|
const uint mapper[] =
|
|
{
|
|
a.GetMapper( useImageDatabase ),
|
|
b.GetMapper( useImageDatabase )
|
|
};
|
|
|
|
if (mapper[0] == mapper[1])
|
|
continue;
|
|
|
|
return mapper[0] > mapper[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_PROM:
|
|
{
|
|
const uint pRom[] =
|
|
{
|
|
a.GetPRom( useImageDatabase ),
|
|
b.GetPRom( useImageDatabase )
|
|
};
|
|
|
|
if (pRom[0] == pRom[1])
|
|
continue;
|
|
|
|
return pRom[0] > pRom[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_CROM:
|
|
{
|
|
const uint cRom[] =
|
|
{
|
|
a.GetCRom( useImageDatabase ),
|
|
b.GetCRom( useImageDatabase )
|
|
};
|
|
|
|
if (cRom[0] == cRom[1])
|
|
continue;
|
|
|
|
return cRom[0] > cRom[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_WRAM:
|
|
{
|
|
const uint wRam[] =
|
|
{
|
|
a.GetWRam( useImageDatabase ),
|
|
b.GetWRam( useImageDatabase )
|
|
};
|
|
|
|
if (wRam[0] == wRam[1])
|
|
continue;
|
|
|
|
return wRam[0] > wRam[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_VRAM:
|
|
{
|
|
const uint vRam[] =
|
|
{
|
|
a.GetVRam( useImageDatabase ),
|
|
b.GetVRam( useImageDatabase )
|
|
};
|
|
|
|
if (vRam[0] == vRam[1])
|
|
continue;
|
|
|
|
return vRam[0] > vRam[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_BATTERY:
|
|
{
|
|
const uint battery[] =
|
|
{
|
|
a.GetBattery( useImageDatabase ) + bool(a.GetType() & (List::Files::Entry::NES|List::Files::Entry::UNF)),
|
|
b.GetBattery( useImageDatabase ) + bool(b.GetType() & (List::Files::Entry::NES|List::Files::Entry::UNF))
|
|
};
|
|
|
|
if (battery[0] == battery[1])
|
|
continue;
|
|
|
|
return battery[0] < battery[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_DUMP:
|
|
{
|
|
const uint dump[] =
|
|
{
|
|
a.GetDump( useImageDatabase ),
|
|
b.GetDump( useImageDatabase )
|
|
};
|
|
|
|
if (dump[0] == dump[1])
|
|
continue;
|
|
|
|
return dump[0] > dump[1] ? +1 : -1;
|
|
}
|
|
|
|
case Columns::TYPE_NAME:
|
|
{
|
|
wcstring const names[] =
|
|
{
|
|
a.GetName( files.GetStrings(), useImageDatabase ),
|
|
b.GetName( files.GetStrings(), useImageDatabase )
|
|
};
|
|
|
|
if (names[0][0] != '-' && names[1][0] == '-') return -1;
|
|
if (names[0][0] == '-' && names[1][0] != '-') return +1;
|
|
|
|
if (const int ret = ::StrCmp( names[0], names[1] ))
|
|
return ret;
|
|
|
|
continue;
|
|
}
|
|
|
|
case Columns::TYPE_FOLDER:
|
|
|
|
if (const int ret = ::StrCmp( a.GetPath(files.GetStrings()), b.GetPath(files.GetStrings()) ))
|
|
return ret;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Launcher::List::OnFind(GenericString string,const uint flags)
|
|
{
|
|
const uint count = ctrl.Size();
|
|
|
|
if (count > 1 && string.Length())
|
|
{
|
|
const uint column = ctrl.Columns().GetIndex(0);
|
|
const int selection = ctrl.Selection().GetIndex();
|
|
const uint wrap = selection > 0 ? selection : 0;
|
|
uint index = wrap;
|
|
bool found;
|
|
|
|
HeapString item;
|
|
|
|
do
|
|
{
|
|
if (flags & Finder::DOWN)
|
|
{
|
|
if (++index == count)
|
|
index = 0;
|
|
}
|
|
else
|
|
{
|
|
if (--index == ~0U)
|
|
index = count - 1;
|
|
}
|
|
|
|
ctrl[index].Text( column ) >> item;
|
|
|
|
if (flags & Finder::WHOLEWORD)
|
|
{
|
|
found = item.Length() == string.Length() && ::StrIsIntlEqual( (flags & Finder::MATCHCASE), item.Ptr(), string.Ptr(), string.Length() );
|
|
}
|
|
else if (flags & Finder::MATCHCASE)
|
|
{
|
|
found = ::StrStr( item.Ptr(), string.Ptr() );
|
|
}
|
|
else
|
|
{
|
|
found = ::StrStrI( item.Ptr(), string.Ptr() );
|
|
}
|
|
}
|
|
while (!found && index != wrap);
|
|
|
|
if (found)
|
|
{
|
|
if (selection >= 0)
|
|
ctrl[selection].Select( false );
|
|
|
|
ctrl[index].Select();
|
|
ctrl[index].Show();
|
|
}
|
|
else
|
|
{
|
|
User::Inform( IDS_TEXT_SEARCH_NOT_FOUND, IDS_TEXT_FIND );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Launcher::List::OnCmdEditFind(uint)
|
|
{
|
|
finder.Open( this, &List::OnFind );
|
|
}
|
|
|
|
void Launcher::List::OnCmdEditInsert(uint)
|
|
{
|
|
enum
|
|
{
|
|
FILE_TYPES =
|
|
(
|
|
Managers::Paths::File::IMAGE |
|
|
Managers::Paths::File::PATCH |
|
|
Managers::Paths::File::ARCHIVE
|
|
)
|
|
};
|
|
|
|
Add( pathManager.BrowseLoad( FILE_TYPES, GenericString(), Managers::Paths::DONT_CHECK_FILE ).Ptr() );
|
|
}
|
|
|
|
void Launcher::List::OnCmdEditDelete(uint)
|
|
{
|
|
Application::Instance::Waiter wait;
|
|
|
|
int last = -1;
|
|
|
|
for (int index = ctrl.Selection().GetIndex(); index != -1; index = ctrl.Selection().GetIndex())
|
|
{
|
|
last = index;
|
|
void* const entry = ctrl[index].Data();
|
|
ctrl[index].Delete();
|
|
files.Disable( static_cast<Files::Entry*>(entry) );
|
|
}
|
|
|
|
if (ctrl.Size())
|
|
{
|
|
if (last != -1)
|
|
ctrl[last].Select();
|
|
}
|
|
else if (typeFilter == Files::Entry::ALL)
|
|
{
|
|
files.Clear();
|
|
}
|
|
}
|
|
|
|
void Launcher::List::OnCmdEditClear(uint)
|
|
{
|
|
Application::Instance::Waiter wait;
|
|
ctrl.Clear();
|
|
files.Clear();
|
|
}
|
|
|
|
void Launcher::List::OnCmdViewAlignColumns(uint)
|
|
{
|
|
Application::Instance::Waiter wait;
|
|
ctrl.Columns().Align();
|
|
}
|
|
|
|
void Launcher::List::OnCmdOptionsColumns(uint)
|
|
{
|
|
UpdateColumnOrder();
|
|
|
|
columns.Update( order );
|
|
columns.Open();
|
|
|
|
Application::Instance::Waiter wait;
|
|
Generic::LockDraw lock( ctrl.GetHandle() );
|
|
|
|
ReloadListColumns();
|
|
Sort();
|
|
ctrl.Columns().Align();
|
|
}
|
|
}
|
|
}
|