1 /*
2   LICENSE
3   -------
4 Copyright 2005-2013 Nullsoft, Inc.
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without modification,
8 are permitted provided that the following conditions are met:
9 
10   * Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12 
13   * Redistributions in binary form must reproduce the above copyright notice,
14     this list of conditions and the following disclaimer in the documentation
15     and/or other materials provided with the distribution.
16 
17   * Neither the name of Nullsoft nor the names of its contributors may be used to
18     endorse or promote products derived from this software without specific prior written permission.
19 
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
21 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 
30 #include "api.h"
31 #include "state.h"		// for CBlendableFloat - fix this
32 #include "menu.h"
33 #include "plugin.h"
34 #include <stdio.h>
35 #include <math.h>
36 #include <assert.h>
37 #include "resource.h"
38 
39 extern CPlugin g_plugin;		// declared in main.cpp
40 
41 //----------------------------------------
42 
CMilkMenuItem()43 CMilkMenuItem::CMilkMenuItem()
44 {
45 	WASABI_API_LNGSTRINGW_BUF(IDS_UNTITLED_MENU_ITEM,m_szName,64);
46 	m_szToolTip[0] = 0;
47 	m_type = MENUITEMTYPE_BUNK;
48 	m_fMin = 0.0f;
49 	m_fMax = 0.0f;
50 	m_var_offset = NULL;
51 	m_pCallbackFn = NULL;
52 	m_pNext = NULL;
53 	m_original_value = NULL;
54 	m_nLastCursorPos = 0;
55     m_bEnabled = true;
56 }
57 
~CMilkMenuItem()58 CMilkMenuItem::~CMilkMenuItem()
59 {
60 	if (m_pNext)
61 	{
62 		delete m_pNext;
63 		m_pNext = NULL;
64 	}
65 }
66 
67 //----------------------------------------
68 
CMilkMenu()69 CMilkMenu::CMilkMenu()
70 {
71     //Reset();
72 }
73 
~CMilkMenu()74 CMilkMenu::~CMilkMenu()
75 {
76     /*
77 	if (m_pFirstChildItem)
78 	{
79 		delete m_pFirstChildItem;
80 		m_pFirstChildItem = NULL;
81 	}
82     */
83 }
84 
85 //----------------------------------------
86 
ItemIsEnabled(int j)87 bool CMilkMenu::ItemIsEnabled(int j)
88 {
89     if (j < m_nChildMenus)
90         return m_ppChildMenu[j]->IsEnabled();
91 
92     int i = m_nChildMenus;
93     CMilkMenuItem *pChild = m_pFirstChildItem;
94     while (pChild && i<j)
95     {
96         pChild = pChild->m_pNext;
97         i++;
98     }
99     if (pChild)
100         return pChild->m_bEnabled;
101 
102     return false;
103 }
104 
105 //----------------------------------------
106 
EnableItem(wchar_t * szName,bool bEnable)107 void CMilkMenu::EnableItem(wchar_t* szName, bool bEnable)
108 {
109     //search submenus
110     for (int i=0; i<m_nChildMenus; i++) {
111         if (!wcscmp(m_ppChildMenu[i]->GetName(), szName))
112         {
113             m_ppChildMenu[i]->Enable(bEnable);
114             if (!bEnable)
115             {
116                 while (m_nCurSel > 0 && !ItemIsEnabled(m_nCurSel))
117     			    m_nCurSel--;
118                 if (m_nCurSel==0 && !ItemIsEnabled(m_nCurSel))
119                     while (m_nCurSel < m_nChildMenus+m_nChildItems-1 && !ItemIsEnabled(m_nCurSel))
120     			        m_nCurSel++;
121             }
122             return;
123         }
124     }
125 
126     //search child items
127     CMilkMenuItem *pChild = m_pFirstChildItem;
128     while (pChild)
129     {
130         if (!wcscmp(pChild->m_szName, szName))
131         {
132             pChild->m_bEnabled = bEnable;
133             if (!bEnable)
134             {
135                 while (m_nCurSel > 0 && !ItemIsEnabled(m_nCurSel))
136     			    m_nCurSel--;
137                 if (m_nCurSel==0 && !ItemIsEnabled(m_nCurSel))
138                     while (m_nCurSel < m_nChildMenus+m_nChildItems-1 && !ItemIsEnabled(m_nCurSel))
139     			        m_nCurSel++;
140             }
141             return;
142         }
143         pChild = pChild->m_pNext;
144         i++;
145     }
146 }
147 
148 //----------------------------------------
149 
Reset()150 void CMilkMenu::Reset()
151 {
152 	m_pParentMenu = NULL;
153 	for (int i=0; i<MAX_CHILD_MENUS; i++)
154 		m_ppChildMenu[i] = NULL;
155 	m_pFirstChildItem = NULL;
156 	WASABI_API_LNGSTRINGW_BUF(IDS_UNTITLED_MENU,m_szMenuName,64);
157 	m_nChildMenus = 0;
158 	m_nChildItems = 0;
159 	m_nCurSel = 0;
160 	m_bEditingCurSel = false;
161     m_bEnabled = true;
162 }
163 
164 //----------------------------------------
165 
Init(wchar_t * szName)166 void CMilkMenu::Init(wchar_t *szName)
167 {
168     Reset();
169     if (szName && szName[0])
170         wcsncpy(m_szMenuName, szName, 64);
171 }
172 
Finish()173 void CMilkMenu::Finish()
174 {
175 	if (m_pFirstChildItem)
176 	{
177 		delete m_pFirstChildItem;
178 		m_pFirstChildItem = NULL;
179 	}
180 }
181 
182 //----------------------------------------
183 
AddChildMenu(CMilkMenu * pMenu)184 void CMilkMenu::AddChildMenu(CMilkMenu *pMenu)
185 {
186 	if (m_nChildMenus < MAX_CHILD_MENUS)
187 	{
188 		m_ppChildMenu[m_nChildMenus++] = pMenu;
189 		pMenu->SetParentPointer(this);
190 	}
191 }
192 
193 //----------------------------------------
194 
AddItem(wchar_t * szName,void * var,MENUITEMTYPE type,wchar_t * szToolTip,float min,float max,MilkMenuCallbackFnPtr pCallback,unsigned int wParam,unsigned int lParam)195 void CMilkMenu::AddItem(wchar_t *szName, void *var, MENUITEMTYPE type, wchar_t *szToolTip,
196 						float min, float max, MilkMenuCallbackFnPtr pCallback,
197 						unsigned int wParam, unsigned int lParam)
198 {
199 	CMilkMenuItem *pLastItem = NULL;
200 
201 	// find last item in linked list
202 	if (!m_pFirstChildItem)
203 	{
204 		// first item
205 		m_pFirstChildItem = new CMilkMenuItem;
206 		pLastItem = m_pFirstChildItem;
207 	}
208 	else
209 	{
210 		pLastItem = m_pFirstChildItem;
211 		while (pLastItem->m_pNext)
212 			pLastItem = pLastItem->m_pNext;
213 
214 		// allocate a new CMilkMenuItem
215 		pLastItem->m_pNext = new CMilkMenuItem;
216 		pLastItem = pLastItem->m_pNext;
217 	}
218 
219 	// set its attributes
220 	wcsncpy(pLastItem->m_szName, szName, 64);
221 	wcsncpy(pLastItem->m_szToolTip, szToolTip, 1024);
222 	pLastItem->m_var_offset = (size_t)var - (size_t)(g_plugin.m_pState);
223 	pLastItem->m_type = type;
224 	pLastItem->m_fMin = min;
225 	pLastItem->m_fMax = max;
226     pLastItem->m_wParam = wParam;
227     pLastItem->m_lParam = lParam;
228 	if ((type==MENUITEMTYPE_LOGBLENDABLE || type==MENUITEMTYPE_LOGFLOAT) && min==max)
229 	{
230 		// special defaults
231 		pLastItem->m_fMin = 0.01f;
232 		pLastItem->m_fMax = 100.0f;
233 	}
234 	pLastItem->m_pCallbackFn = pCallback;
235 
236 	m_nChildItems++;
237 }
238 
239 //----------------------------------------
240 
MyMenuTextOut(eFontIndex font_index,wchar_t * str,DWORD color,RECT * pRect,int bCalcRect,RECT * pCalcRect)241 void MyMenuTextOut(eFontIndex font_index, wchar_t* str, DWORD color, RECT* pRect, int bCalcRect, RECT* pCalcRect)
242 {
243     if (bCalcRect)
244     {
245         RECT t = *pRect;
246 		pRect->top += g_plugin.m_text.DrawTextW(g_plugin.GetFont(font_index), str, -1, &t, DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT, 0xFFFFFFFF, false);
247         pCalcRect->bottom += t.bottom - t.top;
248         //if (pCalcRect->bottom > pRect->bottom)
249         //    pCalcRect->bottom = pRect->bottom;
250         pCalcRect->right = max(pCalcRect->right, pCalcRect->left + t.right - t.left);
251     }
252     else
253     {
254 		pRect->top += g_plugin.m_text.DrawTextW(g_plugin.GetFont(font_index), str, -1, pRect, DT_SINGLELINE | DT_END_ELLIPSIS, color, false);
255     }
256 }
257 
DrawMenu(RECT rect,int xR,int yB,int bCalcRect,RECT * pCalcRect)258 void CMilkMenu::DrawMenu(RECT rect, int xR, int yB, int bCalcRect, RECT* pCalcRect)
259 {
260     // 'rect' is the bounding rectangle in which we're allowed to draw the menu;
261     //   it's .top member is incremented as we draw downward.
262     // if bCalcRect==1, then we return pCalcRect as the area that the menu actually
263     //   occupies, excluding any tooltips.
264 
265     if (bCalcRect!=0 && pCalcRect==NULL)
266         return;
267 
268     if (bCalcRect)
269     {
270         pCalcRect->left   = rect.left;
271         pCalcRect->right  = rect.left;
272         pCalcRect->top    = rect.top;
273         pCalcRect->bottom = rect.top;
274     }
275 
276 	if (!m_bEditingCurSel)
277 	{
278 		int nLines = (rect.bottom - rect.top - PLAYLIST_INNER_MARGIN*2) / g_plugin.GetFontHeight(SIMPLE_FONT) - 1;	// save 1 line for the tooltip
279         if (nLines<1) return;
280 		int nStart = (m_nCurSel/nLines)*nLines;
281 
282         int nLinesDrawn = 0;
283 
284 		for (int i=0; i < m_nChildMenus; i++)
285 		{
286 			if (i >= nStart && i < nStart+nLines)
287 			{
288                 //rect.top += g_plugin.GetFont(SIMPLE_FONT)->DrawText(m_ppChildMenu[i]->m_szMenuName, -1, pRect, DT_SINGLELINE | DT_END_ELLIPSIS, (i == m_nCurSel) ? MENU_HILITE_COLOR : MENU_COLOR);
289                 if (m_ppChildMenu[i]->IsEnabled()) {
290                     MyMenuTextOut(SIMPLE_FONT, m_ppChildMenu[i]->m_szMenuName, (i == m_nCurSel) ? MENU_HILITE_COLOR : MENU_COLOR, &rect, bCalcRect, pCalcRect);
291                     nLinesDrawn++;
292                 }
293 
294 				if (g_plugin.m_bShowMenuToolTips && i == m_nCurSel && !bCalcRect)
295 				{
296 					// tooltip:
297                     g_plugin.DrawTooltip(WASABI_API_LNGSTRINGW(IDS_SZ_MENU_NAV_TOOLTIP), xR, yB);
298 				}
299 			}
300 		}
301 
302 		CMilkMenuItem *pItem = m_pFirstChildItem;
303 
304 		while (pItem && nLinesDrawn < nStart+nLines)
305 		{
306             if (!pItem->m_bEnabled)
307             {
308                 pItem = pItem->m_pNext;
309                 i++;
310                 continue;
311             }
312 
313 			size_t addr = pItem->m_var_offset + (size_t)g_plugin.m_pState;
314 			if (i >= nStart)
315 			{
316 				wchar_t szItemText[256];
317 				switch(pItem->m_type)
318 				{
319 				case MENUITEMTYPE_STRING:
320 					lstrcpyW(szItemText, pItem->m_szName);
321 					break;
322 				case MENUITEMTYPE_BOOL:
323 					swprintf(szItemText, L"%s [%s]", pItem->m_szName,
324 							 WASABI_API_LNGSTRINGW(*((bool *)(addr)) ? IDS_ON : IDS_OFF));
325 					break;
326 				default:
327 					lstrcpyW(szItemText, pItem->m_szName);
328 					break;
329 				}
330 
331 				if (i == m_nCurSel)
332 				{
333                     MyMenuTextOut(SIMPLE_FONT, szItemText, MENU_HILITE_COLOR, &rect, bCalcRect, pCalcRect);
334 
335 					if (g_plugin.m_bShowMenuToolTips && !bCalcRect)
336 					{
337 						// tooltip:
338                         g_plugin.DrawTooltip(pItem->m_szToolTip, xR, yB);
339 					}
340 				}
341 				else
342 				{
343                     MyMenuTextOut(SIMPLE_FONT, szItemText, MENU_COLOR, &rect, bCalcRect, pCalcRect);
344 				}
345                 nLinesDrawn++;
346 			}
347 
348 			pItem = pItem->m_pNext;
349 			i++;
350 		}
351 	}
352 	else
353 	{
354 		// editing current selection
355 
356 		// find the item
357 		CMilkMenuItem *pItem = m_pFirstChildItem;
358 		for (int i=m_nChildMenus; i < m_nCurSel; i++)
359 			pItem = pItem->m_pNext;
360 		size_t addr = pItem->m_var_offset + (size_t)g_plugin.m_pState;
361 
362 		wchar_t buf[256];
363 
364         MyMenuTextOut(SIMPLE_FONT, WASABI_API_LNGSTRINGW(IDS_USE_UP_DOWN_ARROW_KEYS), MENU_COLOR, &rect, bCalcRect, pCalcRect);
365 		swprintf(buf, WASABI_API_LNGSTRINGW(IDS_CURRENT_VALUE_OF_X), pItem->m_szName);
366         MyMenuTextOut(SIMPLE_FONT, buf, MENU_COLOR, &rect, bCalcRect, pCalcRect);
367 
368 		switch(pItem->m_type)
369 		{
370 		case MENUITEMTYPE_INT:
371 			swprintf(buf, L" %d ", *((int*)(addr)) );
372 			break;
373 		case MENUITEMTYPE_FLOAT:
374 		case MENUITEMTYPE_LOGFLOAT:
375 			swprintf(buf, L" %5.3f ", *((float*)(addr)) );
376 			break;
377 		case MENUITEMTYPE_BLENDABLE:
378 		case MENUITEMTYPE_LOGBLENDABLE:
379 			swprintf(buf, L" %5.3f ", ((CBlendableFloat*)addr)->eval(-1) );
380 			break;
381 		default:
382 			lstrcpyW(buf, L" ? ");
383 			break;
384 		}
385 
386         MyMenuTextOut(SIMPLE_FONT, buf, MENU_HILITE_COLOR, &rect, bCalcRect, pCalcRect);
387 
388 		// tooltip:
389 		if (g_plugin.m_bShowMenuToolTips && !bCalcRect)
390 		{
391             g_plugin.DrawTooltip(pItem->m_szToolTip, xR, yB);
392 		}
393 	}
394 }
395 
OnWaitStringAccept(wchar_t * szNewString)396 void CMilkMenu::OnWaitStringAccept(wchar_t *szNewString)
397 {
398 	m_bEditingCurSel = false;
399 
400 	// find the item
401 	CMilkMenuItem *pItem = m_pFirstChildItem;
402 	for (int i=m_nChildMenus; i < m_nCurSel; i++)
403 		pItem = pItem->m_pNext;
404 	size_t addr = pItem->m_var_offset + (size_t)g_plugin.m_pState;
405 
406 	assert(pItem->m_type == MENUITEMTYPE_STRING);
407 
408 	// apply the edited string
409 	lstrcpyW((wchar_t *)(addr), szNewString);
410 
411 	// if user gave us a callback function pointer, call it now
412 	if (pItem->m_pCallbackFn)
413 	{
414 		pItem->m_pCallbackFn(0, 0);
415 	}
416 
417 	// remember the last cursor position
418 	pItem->m_nLastCursorPos = g_plugin.m_waitstring.nCursorPos;
419 }
420 
421 //----------------------------------------
422 
HandleKeydown(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)423 LRESULT CMilkMenu::HandleKeydown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
424 {
425 	// all WM_KEYDOWNS that your app gets when a menu is up should be handled here,
426 	//  by the menu that is currently active.
427 
428 	// return value: FALSE if it handled the key; TRUE if it didn't
429 
430     int nRepeat = LOWORD(lParam);
431     int rep;
432 
433 	if (!m_bEditingCurSel)
434 	{
435 		switch(wParam)
436 		{
437 		case VK_UP:
438             for (rep=0; rep<nRepeat; rep++)
439             {
440                 if (m_nCurSel==0)
441                     break;
442                 do {
443 			        m_nCurSel--;
444                 } while (m_nCurSel > 0 && !ItemIsEnabled(m_nCurSel));
445             }
446 			if (m_nCurSel < 0) m_nCurSel = 0;//m_nChildMenus + m_nChildItems - 1;
447             while (m_nCurSel < m_nChildMenus + m_nChildItems - 1 && !ItemIsEnabled(m_nCurSel))
448 			    m_nCurSel++;
449 			return 0; // we processed (or absorbed) the key
450 
451 		case VK_DOWN:
452             for (rep=0; rep<nRepeat; rep++)
453             {
454                 if (m_nCurSel == m_nChildMenus + m_nChildItems - 1)
455                     break;
456                 do {
457 			        m_nCurSel++;
458                 } while (m_nCurSel < m_nChildMenus + m_nChildItems - 1 && !ItemIsEnabled(m_nCurSel));
459             }
460             if (m_nCurSel >= m_nChildMenus + m_nChildItems) m_nCurSel = m_nChildMenus + m_nChildItems - 1;//0;
461             while (m_nCurSel > 0 && !ItemIsEnabled(m_nCurSel))
462 			    m_nCurSel--;
463 			return 0; // we processed (or absorbed) the key
464 
465 		case VK_HOME:
466 			m_nCurSel = 0;
467 			return 0; // we processed (or absorbed) the key
468 
469 		case VK_END:
470 			m_nCurSel = m_nChildMenus + m_nChildItems - 1;
471 			return 0; // we processed (or absorbed) the key
472 
473 		case VK_ESCAPE:
474 			g_plugin.m_UI_mode = UI_REGULAR;
475 			return 0; // we processed (or absorbed) the key
476 
477 		case VK_BACK:
478 		case VK_LEFT:
479 			if (m_pParentMenu)
480 				g_plugin.m_pCurMenu = m_pParentMenu;
481 			else
482 				g_plugin.m_UI_mode = UI_REGULAR;		// exit the menu
483 			return 0; // we processed (or absorbed) the key
484 
485 		case VK_RETURN:
486 		case VK_RIGHT:
487 		case VK_SPACE:
488 			if (m_nCurSel < m_nChildMenus)
489 			{
490 				// go to sub-menu
491 				g_plugin.m_pCurMenu = m_ppChildMenu[m_nCurSel];
492 			}
493 			else
494 			{
495 				// find the item
496 				CMilkMenuItem *pItem = GetCurItem();
497 				size_t addr = pItem->m_var_offset + (size_t)g_plugin.m_pState;
498 
499 				float fTemp;
500 
501 				// begin editing the item
502 
503 				switch(pItem->m_type)
504 				{
505                 case MENUITEMTYPE_UIMODE:
506                     g_plugin.m_UI_mode = (ui_mode)pItem->m_wParam;
507 
508                     if (g_plugin.m_UI_mode==UI_IMPORT_WAVE ||
509                         g_plugin.m_UI_mode==UI_EXPORT_WAVE ||
510                         g_plugin.m_UI_mode==UI_IMPORT_SHAPE ||
511                         g_plugin.m_UI_mode==UI_EXPORT_SHAPE)
512                     {
513 			            g_plugin.m_bPresetLockedByCode = true;
514 
515 			            // enter WaitString mode
516 			            g_plugin.m_waitstring.bActive = true;
517 			            g_plugin.m_waitstring.bFilterBadChars = false;
518 			            g_plugin.m_waitstring.bDisplayAsCode = false;
519 			            g_plugin.m_waitstring.nSelAnchorPos = -1;
520 			            g_plugin.m_waitstring.nMaxLen = min(sizeof(g_plugin.m_waitstring.szText)-1, MAX_PATH - wcslen(g_plugin.GetPresetDir()) - 6);	// 6 for the extension + null char.    We set this because win32 LoadFile, MoveFile, etc. barf if the path+filename+ext are > MAX_PATH chars.
521 						swprintf(g_plugin.m_waitstring.szText, L"%sfile.dat", g_plugin.m_szPresetDir);
522                         if (g_plugin.m_UI_mode==UI_IMPORT_WAVE || g_plugin.m_UI_mode==UI_IMPORT_SHAPE)
523 							WASABI_API_LNGSTRINGW_BUF(IDS_LOAD_FROM_FILE,g_plugin.m_waitstring.szPrompt,512);
524                         else
525 							WASABI_API_LNGSTRINGW_BUF(IDS_SAVE_TO_FILE,g_plugin.m_waitstring.szPrompt,512);
526 			            g_plugin.m_waitstring.szToolTip[0] = 0;
527 			            g_plugin.m_waitstring.nCursorPos = wcslen(g_plugin.m_waitstring.szText);	// set the starting edit position
528                     }
529                     break;
530 				case MENUITEMTYPE_BOOL:
531 					*((bool *)addr) = !(*((bool *)addr));
532 					break;
533 				case MENUITEMTYPE_INT:
534 					m_bEditingCurSel = true;
535 					pItem->m_original_value = (LPARAM)(*((int*)(addr)));
536 					break;
537 				case MENUITEMTYPE_FLOAT:
538 				case MENUITEMTYPE_LOGFLOAT:
539 					m_bEditingCurSel = true;
540 					pItem->m_original_value = (LPARAM)(*((float*)(addr))*10000L);
541 					break;
542 				case MENUITEMTYPE_BLENDABLE:
543 				case MENUITEMTYPE_LOGBLENDABLE:
544 					m_bEditingCurSel = true;
545 					{
546 						//CBlendableFloat *p = (CBlendableFloat*)(pItem->m_pVariable);
547 						//*p = 0.99f;
548 						fTemp = ((CBlendableFloat*)addr)->eval(-1);//p->eval(-1);
549 					}
550 					pItem->m_original_value = (LPARAM)(fTemp*10000L);
551 					break;
552 				case MENUITEMTYPE_STRING:
553 					// enter waitstring mode.  ***This function will cease to receive keyboard input
554 					// while the string is being edited***
555 					g_plugin.m_UI_mode = UI_EDIT_MENU_STRING;
556 					g_plugin.m_waitstring.bActive = true;
557 					g_plugin.m_waitstring.bFilterBadChars = false;
558 					g_plugin.m_waitstring.bDisplayAsCode = true;
559 					g_plugin.m_waitstring.nSelAnchorPos = -1;
560                     g_plugin.m_waitstring.nMaxLen = pItem->m_wParam ? pItem->m_wParam : 8190;
561                     g_plugin.m_waitstring.nMaxLen = min(g_plugin.m_waitstring.nMaxLen, sizeof(g_plugin.m_waitstring.szText)-16);
562 					//lstrcpyW(g_plugin.m_waitstring.szText, (wchar_t *)addr);
563 					lstrcpyA((char*)g_plugin.m_waitstring.szText, (char*)addr);
564 					swprintf(g_plugin.m_waitstring.szPrompt, WASABI_API_LNGSTRINGW(IDS_ENTER_THE_NEW_STRING), pItem->m_szName);
565 					lstrcpyW(g_plugin.m_waitstring.szToolTip, pItem->m_szToolTip);
566 					g_plugin.m_waitstring.nCursorPos = strlen/*wcslen*/((char*)g_plugin.m_waitstring.szText);
567 					if (pItem->m_nLastCursorPos < g_plugin.m_waitstring.nCursorPos)
568 						g_plugin.m_waitstring.nCursorPos = pItem->m_nLastCursorPos;
569 					break;
570 				/*
571 				case MENUITEMTYPE_OSC:
572 					m_bEditingCurSel = true;
573 					pItem->m_bEditingSubSel = false;
574 					break;
575 					*/
576 				}
577 			}
578 			return 0; // we processed (or absorbed) the key
579 
580 		default:
581 			// key wasn't handled
582 			return TRUE;
583 			break;
584 		}
585 	}
586 	else  // m_bEditingCurSel
587 	{
588 		float fMult = 1.0f;
589 		bool  bDec;
590 
591 		// find the item
592 		CMilkMenuItem *pItem = m_pFirstChildItem;
593 		for (int i=m_nChildMenus; i < m_nCurSel; i++)
594 			pItem = pItem->m_pNext;
595 		size_t addr = pItem->m_var_offset + (size_t)g_plugin.m_pState;
596 
597 		switch(wParam)
598 		{
599 		case VK_ESCAPE:		// exit Edit mode & restore original value
600 
601 			switch(pItem->m_type)
602 			{
603 			case MENUITEMTYPE_INT:
604 				m_bEditingCurSel = false;
605 				*((int *)addr) = (int)pItem->m_original_value;
606 				break;
607 			case MENUITEMTYPE_FLOAT:
608 				m_bEditingCurSel = false;
609 				*((float *)addr) = ((float)pItem->m_original_value)*0.0001f;
610 				break;
611 			case MENUITEMTYPE_LOGFLOAT:
612 				m_bEditingCurSel = false;
613 				*((float *)addr) = ((float)pItem->m_original_value)*0.0001f;
614 				break;
615 			case MENUITEMTYPE_BLENDABLE:
616 				m_bEditingCurSel = false;
617 				*((CBlendableFloat *)(addr)) = ((float)(pItem->m_original_value))*0.0001f;
618 				break;
619 			case MENUITEMTYPE_LOGBLENDABLE:
620 				m_bEditingCurSel = false;
621 				*((CBlendableFloat *)(addr)) = ((float)(pItem->m_original_value))*0.0001f;
622 				break;
623 			//case MENUITEMTYPE_STRING:
624 				// won't ever happen - see OnWaitStringCancel()
625 			}
626 			return 0;
627 
628 		case VK_RETURN:
629 
630 			//if (pItem->m_type == MENUITEMTYPE_STRING)
631 			//	... won't ever happen - see OnWaitStringAccept()
632 
633 			m_bEditingCurSel = false;
634 			return 0;
635 
636 
637 		case VK_NEXT:
638 		case VK_PRIOR:
639 			fMult *= 10.0f;
640             // break intentionally left out here...
641 		case VK_UP:
642 		case VK_DOWN:
643 
644 			{
645 				USHORT mask = 1 << (sizeof(USHORT)*8 - 1);	// we want the highest-order bit
646 				bool bShiftHeldDown = (GetKeyState(VK_SHIFT) & mask) != 0;
647 				//bool bCtrlHeldDown = (GetKeyState(VK_CONTROL) & mask) != 0;
648 				if (bShiftHeldDown && (wParam==VK_UP || wParam==VK_DOWN))
649 					fMult *= 0.1f;
650 			}
651 
652 			bDec = (wParam == VK_DOWN || wParam == VK_NEXT);
653 
654 			switch(pItem->m_type)
655 			{
656 			case MENUITEMTYPE_INT:
657 				{
658 					int *pInt = ((int *)addr);
659                     if (fMult<1) fMult=1;
660 					(*pInt) += (int)((bDec) ? -fMult : fMult);
661 					if (*pInt < pItem->m_fMin) *pInt = (int)pItem->m_fMin;
662 					if (*pInt > pItem->m_fMax) *pInt = (int)pItem->m_fMax;
663 				}
664 				break;
665 			case MENUITEMTYPE_FLOAT:
666 				{
667 					float *pFloat = ((float *)addr);
668 					float fInc = (pItem->m_fMax - pItem->m_fMin)*0.01f*fMult;
669 					(*pFloat) += (bDec) ? -fInc : fInc;
670 					if (*pFloat < pItem->m_fMin) *pFloat = pItem->m_fMin;
671 					if (*pFloat > pItem->m_fMax) *pFloat = pItem->m_fMax;
672 				}
673 				break;
674 			case MENUITEMTYPE_LOGFLOAT:
675 				{
676 					float *pFloat = ((float *)addr);
677 					(*pFloat) *= (bDec) ? powf(1.0f/1.01f, fMult) : powf(1.01f, fMult);
678 					if (*pFloat < pItem->m_fMin) *pFloat = pItem->m_fMin;
679 					if (*pFloat > pItem->m_fMax) *pFloat = pItem->m_fMax;
680 				}
681 				break;
682 			case MENUITEMTYPE_BLENDABLE:
683 				{
684 					CBlendableFloat *pBlend = ((CBlendableFloat *)addr);
685 					float fInc = (pItem->m_fMax - pItem->m_fMin)*0.01f*fMult;
686 					(*pBlend) += (bDec) ? -fInc : fInc;
687 					if (pBlend->eval(-1) < pItem->m_fMin) *pBlend = pItem->m_fMin;
688 					if (pBlend->eval(-1) > pItem->m_fMax) *pBlend = pItem->m_fMax;
689 				}
690 				break;
691 			case MENUITEMTYPE_LOGBLENDABLE:
692 				{
693 					CBlendableFloat *pBlend = ((CBlendableFloat *)addr);
694 					(*pBlend) *= (bDec) ? powf(1.0f/1.01f, fMult) : powf(1.01f, fMult);
695 					if (pBlend->eval(-1) < pItem->m_fMin) *pBlend = pItem->m_fMin;
696 					if (pBlend->eval(-1) > pItem->m_fMax) *pBlend = pItem->m_fMax;
697 				}
698 				break;
699 			/*
700 			case MENUITEMTYPE_OSC:
701 				if (pItem->m_bEditingSubSel)
702 				{
703 					if (wParam == VK_UP)
704 					{
705 						pItem->m_nSubSel--;
706 						if (pItem->m_nSubSel < 0) pItem->m_nSubSel = 4;
707 					}
708 					else if (wParam == VK_DOWN)
709 					{
710 						pItem->m_nSubSel++;
711 						if (pItem->m_nSubSel > 4) pItem->m_nSubSel = 0;
712 					}
713 				}
714 				else
715 				{
716 					switch(pItem->m_nSubSel)
717 					{
718 						also to do: make 'drawtext' draw it properly
719 
720 					case 0:
721 						fixme	- what are the bounds for each type?  and are incs constant or log?
722 						break;
723 					case 1:
724 						fixme
725 						break;
726 					case 2:
727 						fixme
728 						break;
729 					case 3:
730 						fixme
731 						break;
732 					case 4:
733 						fixme
734 						break;
735 					}
736 				}
737 				break;
738 				*/
739 			}
740 			return 0;
741 
742 		default:
743 			// key wasn't handled
744 			return TRUE;
745 			break;
746 		}
747 	}
748 
749 	return TRUE;
750 }