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 "NstApplicationException.hpp" 26 #include "NstApplicationLanguage.hpp" 27 #include "NstResourceIcon.hpp" 28 #include "NstWindowParam.hpp" 29 #include "NstWindowDialog.hpp" 30 #include "NstWindowDynamic.hpp" 31 32 namespace Nestopia 33 { 34 namespace Window 35 { 36 class Dialog::NoTaskbarWindow : Dynamic 37 { 38 public: 39 NoTaskbarWindow(Window::Generic fakeParent)40 NoTaskbarWindow(Window::Generic fakeParent) 41 { 42 Context context; 43 44 const Point pos( fakeParent.Coordinates().Position() ); 45 46 context.className = L"Helper"; 47 context.x = pos.x; 48 context.y = pos.y + 30; 49 context.width = 8; 50 context.height = 8; 51 context.exStyle = WS_EX_TOOLWINDOW; 52 53 Create( context ); 54 } 55 GetHandle() const56 Generic GetHandle() const 57 { 58 return *this; 59 } 60 }; 61 62 Dialog::Instances Dialog::instances; 63 Dialog::ModelessDialogs::Instances Dialog::ModelessDialogs::instances; 64 Dialog::ModelessDialogs::Processor Dialog::ModelessDialogs::processor = Dialog::ModelessDialogs::ProcessNone; 65 Update()66 void Dialog::ModelessDialogs::Update() 67 { 68 switch (instances.Size()) 69 { 70 case 0: processor = ProcessNone; break; 71 case 1: processor = ProcessSingle; break; 72 default: processor = ProcessMultiple; break; 73 } 74 } 75 Add(HWND const hWnd)76 void Dialog::ModelessDialogs::Add(HWND const hWnd) 77 { 78 instances.PushBack( hWnd ); 79 Update(); 80 } 81 Remove(HWND const hWnd)82 bool Dialog::ModelessDialogs::Remove(HWND const hWnd) 83 { 84 if (Instances::Iterator const instance = instances.Find( hWnd )) 85 { 86 instances.Erase( instance ); 87 Update(); 88 return true; 89 } 90 91 return false; 92 } 93 94 #ifdef NST_MSVC_OPTIMIZE 95 #pragma optimize("t", on) 96 #endif 97 ProcessNone(MSG &)98 bool Dialog::ModelessDialogs::ProcessNone(MSG&) 99 { 100 NST_ASSERT( instances.Size() == 0 ); 101 102 return false; 103 } 104 ProcessSingle(MSG & msg)105 bool Dialog::ModelessDialogs::ProcessSingle(MSG& msg) 106 { 107 NST_ASSERT( instances.Size() == 1 ); 108 109 return ::IsDialogMessage( instances.Front(), &msg ); 110 } 111 ProcessMultiple(MSG & msg)112 bool Dialog::ModelessDialogs::ProcessMultiple(MSG& msg) 113 { 114 NST_ASSERT( instances.Size() >= 2 ); 115 116 Instances::ConstIterator dialog = instances.Begin(); 117 Instances::ConstIterator const end = instances.End(); 118 119 do 120 { 121 if (::IsDialogMessage( *dialog, &msg )) 122 return true; 123 } 124 while (++dialog != end); 125 126 return false; 127 } 128 129 #ifdef NST_MSVC_OPTIMIZE 130 #pragma optimize("", on) 131 #endif 132 Dialog(uint i)133 Dialog::Dialog(uint i) 134 : id(i) 135 { 136 Init(); 137 } 138 Init()139 void Dialog::Init() 140 { 141 noTaskbarWindow = NULL; 142 143 static const MsgHandler::HookEntry<Dialog> hooks[] = 144 { 145 { WM_ENTERMENULOOP, &Dialog::OnIdle }, 146 { WM_ENTERSIZEMOVE, &Dialog::OnIdle }, 147 { WM_SYSCOMMAND, &Dialog::OnSysCommand }, 148 { WM_NCLBUTTONDOWN, &Dialog::OnNclButton }, 149 { WM_NCRBUTTONDOWN, &Dialog::OnNcrButton }, 150 { WM_MOUSEACTIVATE, &Dialog::OnIdle } 151 }; 152 153 msgHandler.Hooks().Add( this, hooks ); 154 } 155 Open(const Type type)156 INT_PTR Dialog::Open(const Type type) 157 { 158 if (hWnd) 159 { 160 if (Visible()) 161 Activate(); 162 163 return 0; 164 } 165 166 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 167 168 NST_ASSERT( !noTaskbarWindow ); 169 170 if (type == MODAL) 171 { 172 for (uint i=0; i < 3; ++i) 173 { 174 static const uchar idc[3] = {IDOK,IDCANCEL,IDABORT}; 175 176 if (!cmdHandler( idc[i] )) 177 cmdHandler.Add( idc[i], this, &Dialog::OnClose ); 178 } 179 } 180 181 if (cmdHandler.Size() && !msgHandler( WM_COMMAND )) 182 msgHandler.Add( WM_COMMAND, this, &Dialog::OnCommand ); 183 184 if (!msgHandler( WM_CLOSE )) 185 msgHandler.Add( WM_CLOSE, this, &Dialog::OnClose ); 186 187 if (type == MODAL) 188 { 189 return DialogBoxParam 190 ( 191 Application::Instance::GetLanguage().GetResourceHandle(), 192 MAKEINTRESOURCE(id), 193 Application::Instance::GetActiveWindow(), 194 DlgProc, 195 reinterpret_cast<LPARAM>(this) 196 ); 197 } 198 199 Application::Instance::Waiter wait; 200 201 if (type == MODELESS_FREE) 202 noTaskbarWindow = new NoTaskbarWindow( Application::Instance::GetActiveWindow() ); 203 204 CreateDialogParam 205 ( 206 Application::Instance::GetLanguage().GetResourceHandle(), 207 MAKEINTRESOURCE(id), 208 noTaskbarWindow ? noTaskbarWindow->GetHandle() : Application::Instance::GetActiveWindow(), 209 DlgProc, 210 reinterpret_cast<LPARAM>(this) 211 ); 212 213 if (hWnd == NULL) 214 throw Application::Exception( IDS_ERR_FAILED, L"CreateDialogParam()" ); 215 216 ModelessDialogs::Add( hWnd ); 217 218 return 0; 219 } 220 Close(const int ret)221 void Dialog::Close(const int ret) 222 { 223 if (hWnd) 224 { 225 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 226 227 if (ModelessDialogs::Remove( hWnd )) 228 { 229 ::DestroyWindow( hWnd ); 230 NST_VERIFY( hWnd == NULL ); 231 } 232 else 233 { 234 ::EndDialog( hWnd, ret ); 235 } 236 } 237 238 delete noTaskbarWindow; 239 noTaskbarWindow = NULL; 240 } 241 ~Dialog()242 Dialog::~Dialog() 243 { 244 Close(); 245 } 246 SetItemIcon(uint item,uint id) const247 void Dialog::SetItemIcon(uint item,uint id) const 248 { 249 ::SendDlgItemMessage( hWnd, item, STM_SETIMAGE, IMAGE_ICON, reinterpret_cast<LPARAM>(static_cast<HICON>(Resource::Icon(id))) ); 250 } 251 OnClose(Param &)252 ibool Dialog::OnClose(Param&) 253 { 254 Close(); 255 return true; 256 } 257 OnIdle(Param &)258 void Dialog::OnIdle(Param&) 259 { 260 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 261 } 262 OnSysCommand(Param & param)263 void Dialog::OnSysCommand(Param& param) 264 { 265 switch (param.wParam & 0xFFF0) 266 { 267 case SC_MINIMIZE: 268 case SC_RESTORE: 269 270 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 271 break; 272 } 273 } 274 OnNclButton(Param & param)275 void Dialog::OnNclButton(Param& param) 276 { 277 switch (param.wParam) 278 { 279 case HTCAPTION: 280 case HTMINBUTTON: 281 case HTMAXBUTTON: 282 case HTCLOSE: 283 284 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 285 break; 286 } 287 } 288 OnNcrButton(Param & param)289 void Dialog::OnNcrButton(Param& param) 290 { 291 switch (param.wParam) 292 { 293 case HTCAPTION: 294 case HTSYSMENU: 295 case HTMINBUTTON: 296 297 Application::Instance::Events::Signal( Application::Instance::EVENT_SYSTEM_BUSY ); 298 break; 299 } 300 } 301 Fetch(HWND const handle)302 void Dialog::Fetch(HWND const handle) 303 { 304 NST_ASSERT( handle && !hWnd ); 305 306 hWnd = handle; 307 instances.PushBack( this ); 308 } 309 Ditch(Instances::Iterator const instance)310 void Dialog::Ditch(Instances::Iterator const instance) 311 { 312 NST_ASSERT( hWnd ); 313 314 ModelessDialogs::Remove( hWnd ); 315 instances.Erase( instance ); 316 hWnd = NULL; 317 } 318 319 #ifdef NST_MSVC_OPTIMIZE 320 #pragma optimize("t", on) 321 #endif 322 OnCommand(Param & param)323 ibool Dialog::OnCommand(Param& param) 324 { 325 if (const MsgHandler::Item* const item = cmdHandler( LOWORD(param.wParam) )) 326 return item->callback( param ); 327 328 return false; 329 } 330 DlgProc(HWND const hWnd,const uint uMsg,WPARAM const wParam,LPARAM const lParam)331 BOOL CALLBACK Dialog::DlgProc(HWND const hWnd,const uint uMsg,WPARAM const wParam,LPARAM const lParam) 332 { 333 if (uMsg == WM_INITDIALOG && lParam) 334 reinterpret_cast<Dialog*>(lParam)->Fetch( hWnd ); 335 336 BOOL ret = false; 337 338 for (Instances::Iterator it(instances.Begin()), end(instances.End()); it != end; ++it) 339 { 340 Dialog& dialog = **it; 341 342 if (dialog.hWnd == hWnd) 343 { 344 if (const MsgHandler::Item* const item = dialog.msgHandler( uMsg )) 345 { 346 Param param = {wParam,lParam,0,hWnd}; 347 ret = item->callback( param ); 348 } 349 350 if (uMsg == WM_DESTROY) 351 dialog.Ditch( it ); 352 353 break; 354 } 355 } 356 357 return ret; 358 } 359 360 #ifdef NST_MSVC_OPTIMIZE 361 #pragma optimize("", on) 362 #endif 363 } 364 } 365