1 //////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // Nestopia - NES/Famicom emulator written in C++ 4 // 5 // Copyright (C) 2003-2008 Martin Freij 6 // 7 // This file is part of Nestopia. 8 // 9 // Nestopia is free software; you can redistribute it and/or modify 10 // it under the terms of the GNU General Public License as published by 11 // the Free Software Foundation; either version 2 of the License, or 12 // (at your option) any later version. 13 // 14 // Nestopia is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with Nestopia; if not, write to the Free Software 21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 // 23 //////////////////////////////////////////////////////////////////////////////////////// 24 25 #include "resource/resource.h" 26 #include "NstResourceString.hpp" 27 #include "NstResourceIcon.hpp" 28 #include "NstWindowParam.hpp" 29 #include "NstSystemKeyboard.hpp" 30 #include "NstDialogLauncher.hpp" 31 #include "NstDialogInesHeader.hpp" 32 33 namespace Nestopia 34 { 35 namespace Window 36 { 37 struct Launcher::Handlers 38 { 39 static const MsgHandler::Entry<Launcher> messages[]; 40 static const Menu::CmdHandler::Entry<Launcher> commands[]; 41 static const Control::NotificationHandler::Entry<Launcher> listNotifications[]; 42 static const Control::NotificationHandler::Entry<Launcher> treeNotifications[]; 43 }; 44 45 const MsgHandler::Entry<Launcher> Launcher::Handlers::messages[] = 46 { 47 { WM_INITDIALOG, &Launcher::OnInitDialog }, 48 { WM_CLOSE, &Launcher::OnClose }, 49 { WM_DESTROY, &Launcher::OnDestroy }, 50 { WM_SIZE, &Launcher::OnSize }, 51 { WM_DROPFILES, &Launcher::OnDropFiles } 52 }; 53 54 const Menu::CmdHandler::Entry<Launcher> Launcher::Handlers::commands[] = 55 { 56 { IDM_LAUNCHER_FILE_RUN, &Launcher::OnCmdFileRun }, 57 { IDM_LAUNCHER_FILE_EDITHEADER, &Launcher::OnCmdEditHeader }, 58 { IDM_LAUNCHER_FILE_REFRESH, &Launcher::OnCmdFileRefresh }, 59 { IDM_LAUNCHER_VIEW_SHOWGRIDS, &Launcher::OnCmdViewShowGrids }, 60 { IDM_LAUNCHER_VIEW_SHOWDATABASECORRECTED, &Launcher::OnCmdViewShowDatabaseCorrected }, 61 { IDM_LAUNCHER_VIEW_ONTOP, &Launcher::OnCmdViewShowOnTop }, 62 { IDM_LAUNCHER_OPTIONS_COLORS, &Launcher::OnCmdOptionsColors }, 63 { IDM_LAUNCHER_OPTIONS_PATHS, &Launcher::OnCmdOptionsPaths } 64 }; 65 66 const Control::NotificationHandler::Entry<Launcher> Launcher::Handlers::listNotifications[] = 67 { 68 { LVN_GETDISPINFO, &Launcher::OnListGetDisplayInfo }, 69 { LVN_KEYDOWN, &Launcher::OnListKeyDown }, 70 { LVN_COLUMNCLICK, &Launcher::OnListColumnClick }, 71 { LVN_ITEMACTIVATE, &Launcher::OnListItemActivate }, 72 { LVN_ITEMCHANGED, &Launcher::OnListItemChanged }, 73 { LVN_INSERTITEM, &Launcher::OnListInsertItem }, 74 { LVN_DELETEALLITEMS, &Launcher::OnListDeleteAllItems }, 75 { LVN_DELETEITEM, &Launcher::OnListDeleteItem } 76 }; 77 78 const Control::NotificationHandler::Entry<Launcher> Launcher::Handlers::treeNotifications[] = 79 { 80 { TVN_SELCHANGING, &Launcher::OnTreeSelectionChanging } 81 }; 82 Launcher(const Nes::Cartridge::Database & database,const Managers::Paths & p,const Configuration & cfg)83 Launcher::Launcher(const Nes::Cartridge::Database& database,const Managers::Paths& p,const Configuration& cfg) 84 : 85 dialog ( IDD_LAUNCHER, this, Handlers::messages ), 86 menu ( IDR_MENU_LAUNCHER ), 87 listNotifications ( IDC_LAUNCHER_LIST, dialog.Messages() ), 88 treeNotifications ( IDC_LAUNCHER_TREE, dialog.Messages() ), 89 statusBar ( dialog, STATUSBAR_SECOND_FIELD_WIDTH ), 90 list ( dialog, menu.Commands(), p, cfg, database ), 91 colors ( cfg ) 92 { 93 menu.Commands().Add( this, Handlers::commands ); 94 dialog.Commands().Add( CMD_ENTER, this, &Launcher::OnCmdEnter ); 95 listNotifications.Add( this, Handlers::listNotifications ); 96 treeNotifications.Add( this, Handlers::treeNotifications ); 97 98 { 99 Configuration::ConstSection launcher( cfg["launcher"] ); 100 101 menu[IDM_LAUNCHER_VIEW_ONTOP].Check( launcher["view"]["show"]["on-top"].Yes() ); 102 103 Configuration::ConstSection size( launcher["window-size"] ); 104 105 initialSize.x = size["x"].Int(); 106 initialSize.y = size["y"].Int(); 107 108 if (!initialSize.x || !initialSize.y) 109 { 110 initialSize.x = 0; 111 initialSize.y = 0; 112 } 113 } 114 115 HeapString name; 116 117 for (uint i=0; i < 6; ++i) 118 { 119 static const ushort keys[6][2] = 120 { 121 { IDM_LAUNCHER_FILE_RUN, VK_RETURN }, 122 { IDM_LAUNCHER_FILE_EDITHEADER, VK_F4 }, 123 { IDM_LAUNCHER_FILE_REFRESH, VK_F5 }, 124 { IDM_LAUNCHER_EDIT_FIND, VK_F3 }, 125 { IDM_LAUNCHER_EDIT_INSERT, VK_INSERT }, 126 { IDM_LAUNCHER_EDIT_REMOVE, VK_DELETE } 127 }; 128 129 menu[keys[i][0]].Text() >> name; 130 menu[keys[i][0]].Text() << (name << '\t' << System::Keyboard::GetName( keys[i][1] )); 131 } 132 } 133 ~Launcher()134 Launcher::~Launcher() 135 { 136 Close(); 137 } 138 Save(Configuration & cfg,bool saveSize,bool saveFiles)139 void Launcher::Save(Configuration& cfg,bool saveSize,bool saveFiles) 140 { 141 Configuration::Section launcher( cfg["launcher"] ); 142 143 if (saveSize) 144 { 145 Configuration::Section size( cfg["window-size"] ); 146 147 size["x"].Int() = initialSize.x; 148 size["y"].Int() = initialSize.y; 149 } 150 151 launcher["view"]["show"]["on-top"].YesNo() = menu[IDM_LAUNCHER_VIEW_ONTOP].Checked(); 152 153 list.Save( cfg, saveFiles ); 154 colors.Save( cfg ); 155 } 156 Open(bool child)157 void Launcher::Open(bool child) 158 { 159 menu[IDM_LAUNCHER_VIEW_ONTOP].Enable( !child ); 160 dialog.Open( child ? Dialog::MODELESS_CHILD : Dialog::MODELESS_FREE ); 161 } 162 Close()163 void Launcher::Close() 164 { 165 dialog.Close(); 166 } 167 Synchronize(HWND hWnd) const168 void Launcher::Synchronize(HWND hWnd) const 169 { 170 if (dialog.IsOpen() && menu[IDM_LAUNCHER_VIEW_ONTOP].Enabled() && menu[IDM_LAUNCHER_VIEW_ONTOP].Unchecked()) 171 dialog.Position().BringBehind( hWnd ); 172 } 173 OnInitDialog(Param &)174 ibool Launcher::OnInitDialog(Param&) 175 { 176 menu.Hook( dialog ); 177 menu.Show(); 178 179 statusBar.Enable(); 180 181 if (menu[IDM_LAUNCHER_VIEW_ONTOP].Enabled()) 182 dialog.MakeTopMost( menu[IDM_LAUNCHER_VIEW_ONTOP].Checked() ); 183 184 list = dialog.ListView( IDC_LAUNCHER_LIST ); 185 tree = dialog.TreeView( IDC_LAUNCHER_TREE ); 186 187 dialog.Send 188 ( 189 WM_SETICON, 190 ICON_SMALL, 191 static_cast<HICON>(Resource::Icon(Application::Instance::GetIconStyle() == Application::Instance::ICONSTYLE_NES ? IDI_PAD : IDI_PAD_J)) 192 ); 193 194 margin = dialog.Coordinates().Corner() - list.GetWindow().Coordinates().Corner(); 195 196 list.SetColors( colors.GetBackgroundColor(), colors.GetForegroundColor() ); 197 tree.SetColors( colors.GetBackgroundColor(), colors.GetForegroundColor() ); 198 199 menu[ IDM_LAUNCHER_VIEW_SHOWGRIDS ].Check( list.GetStyle() & LVS_EX_GRIDLINES ); 200 menu[ IDM_LAUNCHER_VIEW_SHOWDATABASECORRECTED ].Check( list.DatabaseCorrectionEnabled() ); 201 menu[ IDM_LAUNCHER_FILE_REFRESH ].Enable( list.CanRefresh() ); 202 203 if (initialSize.x && initialSize.y) 204 dialog.Size() = initialSize; 205 else 206 initialSize = dialog.GetPlacement().Size(); 207 208 return true; 209 } 210 OnClose(Param &)211 ibool Launcher::OnClose(Param&) 212 { 213 Close(); 214 return true; 215 } 216 OnDestroy(Param &)217 ibool Launcher::OnDestroy(Param&) 218 { 219 tree.Close(); 220 list.Close(); 221 222 initialSize = dialog.GetPlacement().Size(); 223 224 return true; 225 } 226 OnSize(Param & param)227 ibool Launcher::OnSize(Param& param) 228 { 229 if (param.wParam != SIZE_MINIMIZED) 230 { 231 const Point corner( dialog.Coordinates().Corner() - margin - list.GetWindow().Position() ); 232 233 list.GetWindow().Size() = corner; 234 tree.GetWindow().Size() = Point( tree.GetWindow().Coordinates().Width(), corner.y ); 235 } 236 237 return true; 238 } 239 OnCmdEnter(Param &)240 ibool Launcher::OnCmdEnter(Param&) 241 { 242 OnCmdFileRun(); 243 return true; 244 } 245 OnDropFiles(Param & param)246 ibool Launcher::OnDropFiles(Param& param) 247 { 248 list.Insert( param ); 249 return true; 250 } 251 UpdateItemCount(const uint count) const252 void Launcher::UpdateItemCount(const uint count) const 253 { 254 if (count == 0 || count == 1) 255 { 256 menu[IDM_LAUNCHER_EDIT_FIND].Enable( count ); 257 menu[IDM_LAUNCHER_EDIT_CLEAR].Enable( count ); 258 } 259 260 static HeapString form( HeapString() << ' ' << Resource::String(IDS_TEXT_FILES) << ": " ); 261 262 const uint length = form.Length(); 263 statusBar.Text(StatusBar::SECOND_FIELD) << (form << count).Ptr(); 264 form.ShrinkTo( length ); 265 } 266 OnListGetDisplayInfo(const NMHDR & nmhdr)267 void Launcher::OnListGetDisplayInfo(const NMHDR& nmhdr) 268 { 269 list.OnGetDisplayInfo( reinterpret_cast<LPARAM>(&nmhdr) ); 270 } 271 OnListKeyDown(const NMHDR & nmhdr)272 void Launcher::OnListKeyDown(const NMHDR& nmhdr) 273 { 274 switch (reinterpret_cast<const NMLVKEYDOWN&>(nmhdr).wVKey) 275 { 276 case VK_INSERT: if (menu[ IDM_LAUNCHER_EDIT_INSERT ].Enabled()) dialog.PostCommand( IDM_LAUNCHER_EDIT_INSERT ); break; 277 case VK_DELETE: if (menu[ IDM_LAUNCHER_EDIT_REMOVE ].Enabled()) dialog.PostCommand( IDM_LAUNCHER_EDIT_REMOVE ); break; 278 case VK_F3: if (menu[ IDM_LAUNCHER_EDIT_FIND ].Enabled()) dialog.PostCommand( IDM_LAUNCHER_EDIT_FIND ); break; 279 case VK_F4: if (menu[ IDM_LAUNCHER_FILE_EDITHEADER ].Enabled()) dialog.PostCommand( IDM_LAUNCHER_FILE_EDITHEADER ); break; 280 case VK_F5: if (menu[ IDM_LAUNCHER_FILE_REFRESH ].Enabled()) dialog.PostCommand( IDM_LAUNCHER_FILE_REFRESH ); break; 281 } 282 } 283 OnListColumnClick(const NMHDR & nmhdr)284 void Launcher::OnListColumnClick(const NMHDR& nmhdr) 285 { 286 Application::Instance::Waiter wait; 287 list.Sort( reinterpret_cast<const NMLISTVIEW&>(nmhdr).iSubItem ); 288 } 289 OnListItemActivate(const NMHDR &)290 void Launcher::OnListItemActivate(const NMHDR&) 291 { 292 OnCmdFileRun(); 293 } 294 OnListItemChanged(const NMHDR & nmhdr)295 void Launcher::OnListItemChanged(const NMHDR& nmhdr) 296 { 297 const NMLISTVIEW& nmlv = reinterpret_cast<const NMLISTVIEW&>(nmhdr); 298 299 if ((nmlv.uOldState ^ nmlv.uNewState) & LVIS_SELECTED) 300 { 301 if (nmlv.uNewState & LVIS_SELECTED) 302 { 303 if (const List::Files::Entry* const entry = list[nmlv.iItem]) 304 { 305 { 306 Path path( entry->GetPath(list.GetStrings()), entry->GetFile(list.GetStrings()) ); 307 308 if (path.Length() > MAX_PATH) 309 { 310 path.ShrinkTo( MAX_PATH-3 ); 311 path << "..."; 312 } 313 314 statusBar.Text(StatusBar::FIRST_FIELD) << path.Ptr(); 315 } 316 317 menu[IDM_LAUNCHER_FILE_RUN].Enable(); 318 menu[IDM_LAUNCHER_FILE_EDITHEADER].Enable( entry->GetType() == List::Files::Entry::NES ); 319 menu[IDM_LAUNCHER_EDIT_REMOVE].Enable(); 320 } 321 } 322 else 323 { 324 OnNoSelection(); 325 } 326 } 327 } 328 OnListInsertItem(const NMHDR &)329 void Launcher::OnListInsertItem(const NMHDR&) 330 { 331 UpdateItemCount( list.Size() ); 332 } 333 OnListDeleteItem(const NMHDR &)334 void Launcher::OnListDeleteItem(const NMHDR&) 335 { 336 const uint size = list.Size(); 337 UpdateItemCount( size - (size != 0) ); 338 } 339 OnListDeleteAllItems(const NMHDR &)340 void Launcher::OnListDeleteAllItems(const NMHDR&) 341 { 342 OnNoSelection(); 343 const uint size = list.Size(); 344 UpdateItemCount( size - (size != 0) ); 345 } 346 OnTreeSelectionChanging(const NMHDR & nmhdr)347 void Launcher::OnTreeSelectionChanging(const NMHDR& nmhdr) 348 { 349 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 350 list.Draw( tree.GetType(reinterpret_cast<const NMTREEVIEW&>(nmhdr).itemNew.hItem) ); 351 } 352 OnNoSelection() const353 void Launcher::OnNoSelection() const 354 { 355 statusBar.Text(StatusBar::FIRST_FIELD).Clear(); 356 357 menu[IDM_LAUNCHER_FILE_RUN].Disable(); 358 menu[IDM_LAUNCHER_FILE_EDITHEADER].Disable(); 359 menu[IDM_LAUNCHER_EDIT_REMOVE].Disable(); 360 } 361 OnCmdFileRun(uint)362 void Launcher::OnCmdFileRun(uint) 363 { 364 if (const List::Files::Entry* const entry = list.GetSelection()) 365 Application::Instance::GetMainWindow().Send( Application::Instance::WM_NST_LAUNCH, 0, Path(entry->GetPath(list.GetStrings()),entry->GetFile(list.GetStrings())).Ptr() ); 366 } 367 OnCmdFileRefresh(uint)368 void Launcher::OnCmdFileRefresh(uint) 369 { 370 list.Refresh(); 371 } 372 OnCmdEditHeader(uint)373 void Launcher::OnCmdEditHeader(uint) 374 { 375 if (const List::Files::Entry* const entry = list.GetSelection()) 376 Window::InesHeader( list.GetPaths() ).Open( Path(entry->GetPath(list.GetStrings()),entry->GetFile(list.GetStrings())) ); 377 } 378 OnCmdViewShowGrids(uint)379 void Launcher::OnCmdViewShowGrids(uint) 380 { 381 menu[IDM_LAUNCHER_VIEW_SHOWGRIDS].Check( list.ToggleGrids() ); 382 } 383 OnCmdViewShowDatabaseCorrected(uint)384 void Launcher::OnCmdViewShowDatabaseCorrected(uint) 385 { 386 menu[IDM_LAUNCHER_VIEW_SHOWDATABASECORRECTED].Check( list.ToggleDatabase() ); 387 } 388 OnCmdViewShowOnTop(uint)389 void Launcher::OnCmdViewShowOnTop(uint) 390 { 391 dialog.MakeTopMost( menu[IDM_LAUNCHER_VIEW_ONTOP].ToggleCheck() ); 392 } 393 OnCmdOptionsPaths(uint)394 void Launcher::OnCmdOptionsPaths(uint) 395 { 396 list.OpenPathDialog(); 397 menu[IDM_LAUNCHER_FILE_REFRESH].Enable( list.CanRefresh() ); 398 } 399 OnCmdOptionsColors(uint)400 void Launcher::OnCmdOptionsColors(uint) 401 { 402 colors.Open(); 403 404 list.SetColors( colors.GetBackgroundColor(), colors.GetForegroundColor(), List::REPAINT ); 405 tree.SetColors( colors.GetBackgroundColor(), colors.GetForegroundColor(), Tree::REPAINT ); 406 } 407 } 408 } 409