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