1 // DasherButtons.cpp, build a set of boxes for Button Dasher.
2 // Copyright 2005, Chris Ball and David MacKay. GPL.
4 // Idea - should back off button always just undo the previous 'forwards' button?
6 #include "../Common/Common.h"
9 #include "DasherButtons.h"
13 // Track memory leaks on Windows to the line that new'd the memory
16 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
19 static char THIS_FILE
[] = __FILE__
;
23 static SModuleSettings sSettings
[] = {
24 {LP_ZOOMSTEPS
, T_LONG
, 1, 63, 1, 1, "Zoom steps"},
25 {LP_RIGHTZOOM
, T_LONG
, 1024, 10240, 1024, 1024, "Right zoom"},
26 {LP_B
, T_LONG
, 2, 10, 1, 1, "Number of boxes"},
27 {LP_S
, T_LONG
, 0, 256, 1, 1, "Safety margin"},
28 {LP_R
, T_LONG
, -400, 400, 1, 10, "Box non-uniformity"},
29 {BP_GLOBAL_KEYBOARD
, T_BOOL
, -1, -1, -1, -1, "Global keyboard grab"}
32 // FIXME - should compass mode be made a separate class?
34 CDasherButtons::CDasherButtons(Dasher::CEventHandler
* pEventHandler
, CSettingsStore
*pSettingsStore
, CDasherInterfaceBase
*pInterface
, int iNumBoxes
, int iStyle
, bool bMenu
, long long int iID
, const char *szName
)
35 : CInputFilter(pEventHandler
, pSettingsStore
, pInterface
, iID
, 1, szName
) {
39 m_iNumBoxes
= iNumBoxes
;
43 m_pSettingsStore
= pSettingsStore
;
45 m_bDecorationChanged
= true;
50 CDasherButtons::~CDasherButtons()
55 void CDasherButtons::SetupBoxes()
58 int iDasherY(GetLongParameter(LP_MAX_Y
));
62 // Alternating direct mode
64 m_pBoxes
= new SBoxInfo
[5];
69 m_pBoxes
[0].iBottom
= 1000;
70 m_pBoxes
[1].iTop
= 3096;
71 m_pBoxes
[1].iBottom
= 4096;
76 m_pBoxes
[2].iBottom
= 3096;
77 m_pBoxes
[3].iTop
= 1000;
78 m_pBoxes
[3].iBottom
= 4096;
80 m_pBoxes
[0].iDisplayTop
= m_pBoxes
[0].iTop
;
81 m_pBoxes
[0].iDisplayBottom
= m_pBoxes
[0].iBottom
;
82 m_pBoxes
[1].iDisplayTop
= m_pBoxes
[1].iTop
;
83 m_pBoxes
[1].iDisplayBottom
= m_pBoxes
[1].iBottom
;
84 m_pBoxes
[2].iDisplayTop
= m_pBoxes
[2].iTop
;
85 m_pBoxes
[2].iDisplayBottom
= m_pBoxes
[2].iBottom
;
86 m_pBoxes
[3].iDisplayTop
= m_pBoxes
[3].iTop
;
87 m_pBoxes
[3].iDisplayBottom
= m_pBoxes
[3].iBottom
;
90 m_pBoxes
[m_iNumBoxes
-1].iDisplayTop
= 0;
91 m_pBoxes
[m_iNumBoxes
-1].iDisplayBottom
= iDasherY
;
93 m_pBoxes
[m_iNumBoxes
-1].iTop
= int(- iDasherY
/ 2);
94 m_pBoxes
[m_iNumBoxes
-1].iBottom
= int(iDasherY
* 1.5);
99 else if(m_iStyle
== 2) { // Compass mode
100 m_pBoxes
= new SBoxInfo
[4];
102 iTargetWidth
= iDasherY
* 1024 / GetLongParameter(LP_RIGHTZOOM
);
104 // FIXME - need to relate these to cross-hair position as stored in the parameters
106 // Not sure whether this is at all the right algorithm here - need to check
108 m_pBoxes
[1].iTop
= (2048 - iTargetWidth
/ 2);
109 m_pBoxes
[1].iBottom
= 4096 - m_pBoxes
[1].iTop
;
111 // Make this the inverse of the right zoom option
113 m_pBoxes
[3].iTop
= -2048 * m_pBoxes
[1].iTop
/ (2048 - m_pBoxes
[1].iTop
);
114 m_pBoxes
[3].iBottom
= 4096 - m_pBoxes
[3].iTop
;
116 m_pBoxes
[0].iTop
= -iTargetWidth
;
117 m_pBoxes
[0].iBottom
= iDasherY
- iTargetWidth
;
118 m_pBoxes
[2].iTop
= iTargetWidth
;
119 m_pBoxes
[2].iBottom
= iDasherY
+ iTargetWidth
;
121 m_pBoxes
[0].iDisplayTop
= m_pBoxes
[0].iTop
;
122 m_pBoxes
[0].iDisplayBottom
= m_pBoxes
[0].iBottom
;
123 m_pBoxes
[1].iDisplayTop
= m_pBoxes
[1].iTop
;
124 m_pBoxes
[1].iDisplayBottom
= m_pBoxes
[1].iBottom
;
125 m_pBoxes
[2].iDisplayTop
= m_pBoxes
[2].iTop
;
126 m_pBoxes
[2].iDisplayBottom
= m_pBoxes
[2].iBottom
;
127 m_pBoxes
[3].iDisplayTop
= m_pBoxes
[3].iTop
;
128 m_pBoxes
[3].iDisplayBottom
= m_pBoxes
[3].iBottom
;
133 if((m_iStyle
== 1) || (m_iStyle
== 0))
134 m_iNumBoxes
= GetLongParameter(LP_B
) + 1; // One extra box for backoff
141 m_pBoxes
= new SBoxInfo
[m_iNumBoxes
];
142 int iForwardBoxes(m_iNumBoxes
- 1);
144 // Calculate the sizes of non-uniform boxes using standard
145 // geometric progression results
150 // FIXME - implement this using DJCM's integer method?
151 // See ~mackay/dasher/buttons/
152 dRatio
= pow(129/127.0, -static_cast<double>(GetLongParameter(LP_R
)));
158 dMaxSize
= iDasherY
/ static_cast<double>(iForwardBoxes
);
160 dMaxSize
= ((dRatio
- 1)/(pow(dRatio
, iForwardBoxes
) - 1)) * iDasherY
;
165 for(int i(0); i
< m_iNumBoxes
- 1; ++i
) { // One button reserved for backoff
166 dMax
= dMin
+ dMaxSize
* pow(dRatio
, i
);
168 // m_pBoxes[i].iDisplayTop = (i * iDasherY) / (m_iNumBoxes - 1);
169 // m_pBoxes[i].iDisplayBottom = ((i+1) * iDasherY) / (m_iNumBoxes - 1);
171 m_pBoxes
[i
].iDisplayTop
= static_cast<int>(dMin
);
172 m_pBoxes
[i
].iDisplayBottom
= static_cast<int>(dMax
);
174 m_pBoxes
[i
].iTop
= m_pBoxes
[i
].iDisplayTop
- GetLongParameter(LP_S
);
175 m_pBoxes
[i
].iBottom
= m_pBoxes
[i
].iDisplayBottom
+ GetLongParameter(LP_S
);
182 if(m_iNumBoxes
== 2+1) { // Special case for two forwards buttons
185 m_pBoxes
[0].iDisplayTop
= 0;
186 m_pBoxes
[0].iDisplayBottom
= int( (1 / dNorm
) * iDasherY
);
188 m_pBoxes
[1].iDisplayTop
= int( (1 / dNorm
) * iDasherY
);
189 m_pBoxes
[1].iDisplayBottom
= iDasherY
;
192 int iForwardsButtons(m_iNumBoxes
- 1);
193 bool bEven(iForwardsButtons
% 2 == 0);
198 iGeometricTerms
= iForwardsButtons
/ 2;
200 iGeometricTerms
= (1+iForwardsButtons
) / 2;
205 dMaxSize
= iDasherY
/ iForwardsButtons
;
209 dMaxSize
= iDasherY
* (dRatio
- 1) / (2 * (pow(dRatio
, iGeometricTerms
) - 1));
211 dMaxSize
= iDasherY
* (dRatio
- 1) / (2 * (pow(dRatio
, iGeometricTerms
) - 1) - (dRatio
- 1));
220 dMin
= (iDasherY
- dMaxSize
)/2;
226 iUpBase
= iForwardsButtons
/ 2;
227 iDownBase
= iUpBase
- 1;
230 iUpBase
= (iForwardsButtons
- 1)/ 2;
234 for(int i(0); i
< iGeometricTerms
; ++i
) { // One button reserved for backoff
235 dMax
= dMin
+ dMaxSize
* pow(dRatio
, i
);
237 m_pBoxes
[iUpBase
+ i
].iDisplayTop
= int(dMin
);
238 m_pBoxes
[iUpBase
+ i
].iDisplayBottom
= int(dMax
);
240 m_pBoxes
[iDownBase
- i
].iDisplayTop
= int(iDasherY
- dMax
);
241 m_pBoxes
[iDownBase
- i
].iDisplayBottom
= int(iDasherY
- dMin
);
248 for(int i(0); i
< m_iNumBoxes
- 1; ++i
) {
249 m_pBoxes
[i
].iTop
= m_pBoxes
[i
].iDisplayTop
- GetLongParameter(LP_S
);
250 m_pBoxes
[i
].iBottom
= m_pBoxes
[i
].iDisplayBottom
+ GetLongParameter(LP_S
);
253 m_pBoxes
[m_iNumBoxes
-1].iDisplayTop
= 0;
254 m_pBoxes
[m_iNumBoxes
-1].iDisplayBottom
= iDasherY
;
256 m_pBoxes
[m_iNumBoxes
-1].iTop
= int(- iDasherY
/ 2);
257 m_pBoxes
[m_iNumBoxes
-1].iBottom
= int(iDasherY
* 1.5);
261 m_bDecorationChanged
= true;
264 bool CDasherButtons::DecorateView(CDasherView
*pView
) {
266 CDasherScreen
*pScreen(pView
->Screen());
268 int iPos(2048 - iTargetWidth
/ 2);
273 CDasherScreen::point p
[2];
281 pView
->Dasher2Screen(iDasherX
, iDasherY
, p
[0].x
, p
[0].y
);
286 pView
->Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
289 pScreen
->Polyline(p
, 2, 1, 1);
291 pScreen
->Polyline(p
, 2, 1, 2);
294 iDasherY
= 4096 - iPos
;
296 pView
->Dasher2Screen(iDasherX
, iDasherY
, p
[0].x
, p
[0].y
);
299 iDasherY
= 4096 - iPos
;
301 pView
->Dasher2Screen(iDasherX
, iDasherY
, p
[1].x
, p
[1].y
);
304 pScreen
->Polyline(p
, 2, 1, 1);
306 pScreen
->Polyline(p
, 2, 1, 2);
308 iPos
-= iTargetWidth
;
312 else if(m_iStyle
== 3) {
313 if(m_iLastBox
== 1) {
314 pView
->NewDrawGoTo(m_pBoxes
[2].iDisplayTop
, m_pBoxes
[2].iDisplayBottom
, false);
315 pView
->NewDrawGoTo(m_pBoxes
[1].iDisplayTop
, m_pBoxes
[3].iDisplayBottom
, false);
316 pView
->NewDrawGoTo(m_pBoxes
[4].iDisplayTop
, m_pBoxes
[4].iDisplayBottom
, false);
319 pView
->NewDrawGoTo(m_pBoxes
[0].iDisplayTop
, m_pBoxes
[0].iDisplayBottom
, false);
320 pView
->NewDrawGoTo(m_pBoxes
[3].iDisplayTop
, m_pBoxes
[1].iDisplayBottom
, false);
321 pView
->NewDrawGoTo(m_pBoxes
[4].iDisplayTop
, m_pBoxes
[4].iDisplayBottom
, false);
325 for(int i(0); i
< m_iNumBoxes
; ++i
) {
327 pView
->NewDrawGoTo(m_pBoxes
[i
].iDisplayTop
, m_pBoxes
[i
].iDisplayBottom
, false);
329 pView
->NewDrawGoTo(m_pBoxes
[iActiveBox
].iDisplayTop
, m_pBoxes
[iActiveBox
].iDisplayBottom
, m_bMenu
|| m_bHighlight
);
332 bool bRV(m_bDecorationChanged
);
333 m_bDecorationChanged
= false;
338 void CDasherButtons::KeyDown(int iTime
, int iId
, CDasherModel
*pModel
, CUserLog
*pUserLog
) {
344 m_bDecorationChanged
= true;
346 if(iActiveBox
== m_iNumBoxes
)
351 m_bDecorationChanged
= true;
352 pModel
->ScheduleZoom((m_pBoxes
[iActiveBox
].iBottom
- m_pBoxes
[iActiveBox
].iTop
)/2, (m_pBoxes
[iActiveBox
].iBottom
+ m_pBoxes
[iActiveBox
].iTop
)/2);
353 if(iActiveBox
!= m_iNumBoxes
-1)
363 pModel
->ScheduleZoom((m_pBoxes
[2].iBottom
- m_pBoxes
[2].iTop
)/2, (m_pBoxes
[2].iBottom
+ m_pBoxes
[2].iTop
)/2);
365 pModel
->ScheduleZoom((m_pBoxes
[0].iBottom
- m_pBoxes
[0].iTop
)/2, (m_pBoxes
[0].iBottom
+ m_pBoxes
[0].iTop
)/2);
371 pModel
->ScheduleZoom((m_pBoxes
[3].iBottom
- m_pBoxes
[3].iTop
)/2, (m_pBoxes
[3].iBottom
+ m_pBoxes
[3].iTop
)/2);
373 pModel
->ScheduleZoom((m_pBoxes
[1].iBottom
- m_pBoxes
[1].iTop
)/2, (m_pBoxes
[1].iBottom
+ m_pBoxes
[1].iTop
)/2);
377 pModel
->ScheduleZoom((m_pBoxes
[4].iBottom
- m_pBoxes
[4].iTop
)/2, (m_pBoxes
[4].iBottom
+ m_pBoxes
[4].iTop
)/2);
382 if(iId
== 100) // Ignore mouse events
385 iActiveBox
= m_iNumBoxes
- 1;
386 else if(iId
<= m_iNumBoxes
)
389 iActiveBox
= m_iNumBoxes
-2;
393 pModel
->ScheduleZoom((m_pBoxes
[iActiveBox
].iBottom
- m_pBoxes
[iActiveBox
].iTop
)/2, (m_pBoxes
[iActiveBox
].iBottom
+ m_pBoxes
[iActiveBox
].iTop
)/2);
399 bool CDasherButtons::Timer(int Time
, CDasherView
*m_pDasherView
, CDasherModel
*m_pDasherModel
, Dasher::VECTOR_SYMBOL_PROB
*pAdded
, int *pNumDeleted
) {
400 bool m_bOldHighlight(m_bHighlight
);
401 m_bHighlight
= (Time
- m_iLastTime
< 200);
403 if(m_bOldHighlight
!= m_bHighlight
)
404 m_bDecorationChanged
= true;
406 return m_pDasherModel
->Tap_on_display(0, 0, Time
, pAdded
, pNumDeleted
);
409 void CDasherButtons::HandleEvent(Dasher::CEvent
* pEvent
) {
410 if(pEvent
->m_iEventType
== 1) {
411 Dasher::CParameterNotificationEvent
* pEvt(static_cast < Dasher::CParameterNotificationEvent
* >(pEvent
));
413 switch (pEvt
->m_iParameter
) {
417 // Delibarate fallthrough
424 bool CDasherButtons::GetSettings(SModuleSettings
**pSettings
, int *iCount
) {
425 *pSettings
= sSettings
;
426 *iCount
= sizeof(sSettings
) / sizeof(SModuleSettings
);