Updated German translation
[dasher.git] / Src / DasherCore / ButtonMode.cpp
blob8716d5a8a6dbcff0a10d4c87aa113b35cc493259
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"
8 #include "ButtonMode.h"
9 #include "DasherScreen.h"
10 #include "DasherInterfaceBase.h"
11 #include <valarray>
12 #include <iostream>
14 // Track memory leaks on Windows to the line that new'd the memory
15 #ifdef _WIN32
16 #ifdef _DEBUG_MEMLEAKS
17 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
18 #define new DEBUG_NEW
19 #undef THIS_FILE
20 static char THIS_FILE[] = __FILE__;
21 #endif
22 #endif
24 using namespace Dasher;
26 static SModuleSettings sSettings[] = {
27 /* TRANSLATORS: The number of time steps over which to perform the zooming motion in button mode. */
28 {LP_ZOOMSTEPS, T_LONG, 1, 63, 1, 1, _("Frames over which to perform zoom")},
29 {LP_BUTTON_SCAN_TIME, T_LONG, 0, 2000, 1, 100, _("Scan time in menu mode (0 to not scan)")},
30 {LP_B, T_LONG, 2, 10, 1, 1, _("Number of forward boxes")},
31 {LP_S, T_LONG, 0, 256, 1, 1, _("Safety margin")},
32 /* TRANSLATORS: The boxes (zoom targets) in button mode can either be the same size, or different sizes - this is the extent to which the sizes are allowed to differ from each other. */
33 /* XXX PRLW: 128 log(2) = 89, where 2 is the ratio of adjacent boxes
34 * however the code seems to use ratio = (129/127)^-r, instead of
35 * ratio = exp(r/128) used in the design document
37 {LP_R, T_LONG, -89, 89, 1, 10, _("Box non-uniformity")},
38 /* TRANSLATORS: Intercept keyboard events for 'special' keys even when the Dasher window doesn't have keyboard focus.*/
39 {BP_GLOBAL_KEYBOARD, T_BOOL, -1, -1, -1, -1, _("Global keyboard grab")}
42 // FIX iStyle == 0
44 CButtonMode::CButtonMode(CSettingsUser *pCreator, CDasherInterfaceBase *pInterface, bool bMenu, int iID, const char *szName)
45 : CDasherButtons(pCreator, pInterface, bMenu, iID, szName), CSettingsObserver(pCreator) {}
47 void CButtonMode::SetupBoxes()
49 int iDasherY(CDasherModel::MAX_Y);
51 int iForwardBoxes(GetLongParameter(LP_B));
52 m_pBoxes = new SBoxInfo[m_iNumBoxes = iForwardBoxes+1];
54 // Calculate the sizes of non-uniform boxes using standard
55 // geometric progression results
57 // FIXME - implement this using DJCM's integer method?
58 // See ~mackay/dasher/buttons/
59 const double dRatio = pow(129/127.0, -static_cast<double>(GetLongParameter(LP_R)));
61 if(m_bMenu) {
63 double dMaxSize;
64 if(dRatio == 1.0)
65 dMaxSize = iDasherY / static_cast<double>(iForwardBoxes);
66 else
67 dMaxSize = ((dRatio - 1)/(pow(dRatio, iForwardBoxes) - 1)) * iDasherY;
69 double dMin(0.0);
70 double dMax;
72 for(int i(0); i < m_iNumBoxes - 1; ++i) { // One button reserved for backoff
73 dMax = dMin + dMaxSize * pow(dRatio, i);
75 // m_pBoxes[i].iDisplayTop = (i * iDasherY) / (m_iNumBoxes - 1);
76 // m_pBoxes[i].iDisplayBottom = ((i+1) * iDasherY) / (m_iNumBoxes - 1);
78 m_pBoxes[i].iDisplayTop = static_cast<int>(dMin);
79 m_pBoxes[i].iDisplayBottom = static_cast<int>(dMax);
81 m_pBoxes[i].iTop = m_pBoxes[i].iDisplayTop - GetLongParameter(LP_S);
82 m_pBoxes[i].iBottom = m_pBoxes[i].iDisplayBottom + GetLongParameter(LP_S);
84 dMin = dMax;
88 else {
89 if(iForwardBoxes == 2) { // Special case for two forwards buttons
90 myint iMid = static_cast<int>(iDasherY / (1.0+dRatio));
92 m_pBoxes[0].iDisplayTop = 0;
93 m_pBoxes[0].iDisplayBottom = iMid;
95 m_pBoxes[1].iDisplayTop = iMid;
96 m_pBoxes[1].iDisplayBottom = iDasherY;
98 else {
99 bool bEven(iForwardBoxes % 2 == 0);
101 const int iGeometricTerms = (iForwardBoxes+1)/2; //int div, round down
103 double dMaxSize;
105 if(dRatio == 1.0) {
106 dMaxSize = iDasherY / iForwardBoxes;
108 else {
109 if(bEven)
110 dMaxSize = iDasherY * (dRatio - 1) / (2 * (pow(dRatio, iGeometricTerms) - 1));
111 else
112 dMaxSize = iDasherY * (dRatio - 1) / (2 * (pow(dRatio, iGeometricTerms) - 1) - (dRatio - 1));
115 double dMin = (bEven) ? iDasherY/2.0 : (iDasherY-dMaxSize)/2.0;
117 const int iUpBase = iForwardBoxes/2; //int div, round down if !bEven
118 const int iDownBase = bEven ? iUpBase-1 : iUpBase;
121 for(int i(0); i < iGeometricTerms; ++i) { // One button reserved for backoff
122 const double dMax = dMin + dMaxSize * pow(dRatio, i);
124 m_pBoxes[iUpBase + i].iDisplayTop = int(dMin);
125 m_pBoxes[iUpBase + i].iDisplayBottom = int(dMax);
127 m_pBoxes[iDownBase - i].iDisplayTop = int(iDasherY - dMax);
128 m_pBoxes[iDownBase - i].iDisplayBottom = int(iDasherY - dMin);
130 dMin = dMax;
135 for(int i(0); i < m_iNumBoxes - 1; ++i) {
136 m_pBoxes[i].iTop = m_pBoxes[i].iDisplayTop - GetLongParameter(LP_S);
137 m_pBoxes[i].iBottom = m_pBoxes[i].iDisplayBottom + GetLongParameter(LP_S);
140 m_pBoxes[m_iNumBoxes-1].iDisplayTop = 0;
141 m_pBoxes[m_iNumBoxes-1].iDisplayBottom = iDasherY;
143 m_pBoxes[m_iNumBoxes-1].iTop = int(- iDasherY / 2);
144 m_pBoxes[m_iNumBoxes-1].iBottom = int(iDasherY * 1.5);
147 bool CButtonMode::DecorateView(CDasherView *pView, CDasherInput *pInput) {
148 for(int i(0); i < m_iNumBoxes; ++i) {
149 if(i != iActiveBox)
150 NewDrawGoTo(pView, m_pBoxes[i].iDisplayTop, m_pBoxes[i].iDisplayBottom, false);
152 NewDrawGoTo(pView, m_pBoxes[iActiveBox].iDisplayTop, m_pBoxes[iActiveBox].iDisplayBottom, m_bMenu || m_bHighlight);
154 bool bRV(m_bDecorationChanged);
155 m_bDecorationChanged = false;
156 return bRV;
159 void CButtonMode::Timer(unsigned long Time, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel, CExpansionPolicy **pol) {
160 bool m_bOldHighlight(m_bHighlight);
161 m_bHighlight = (Time - m_iLastTime < 200);
163 if(m_bOldHighlight != m_bHighlight)
164 m_bDecorationChanged = true;
166 CDasherButtons::Timer(Time, pView, pInput, pModel, pol);
169 void CButtonMode::KeyDown(unsigned long iTime, int iId, CDasherView *pView, CDasherInput *pInput, CDasherModel *pModel) {
170 if (iId == 100) {
171 //Mouse!
172 if (m_bMenu) {
173 bool bScan;
174 if (GetLongParameter(LP_BUTTON_SCAN_TIME))
175 bScan = false; //auto-scan, any click selects
176 else {
177 //top scans, bottom selects
178 screenint iScreenX, iScreenY;
179 pInput->GetScreenCoords(iScreenX, iScreenY, pView);
180 bScan = iScreenY < pView->Screen()->GetHeight()/2;
182 CDasherButtons::KeyDown(iTime, bScan ? 1 : 2, pView, pInput, pModel);
183 return;
184 } else {
185 myint iDasherX, iDasherY;
186 pInput->GetDasherCoords(iDasherX, iDasherY, pView);
187 //look for a click _in_ a box -> activate box
188 for (int i = 0; i < m_iNumBoxes; i++) {
189 if (iDasherY < m_pBoxes[i].iDisplayBottom &&
190 iDasherY > m_pBoxes[i].iDisplayTop &&
191 iDasherX < (m_pBoxes[i].iDisplayBottom - m_pBoxes[i].iDisplayTop)) {
192 //user has clicked in box! Simulate press of appropriate (direct-mode) button...
193 CDasherButtons::KeyDown(iTime, (i==m_iNumBoxes-1) ? 1 : i+2, pView, pInput, pModel);
194 return;
197 //not in any box. Fall through, just to be conservative...
200 CDasherButtons::KeyDown(iTime, iId, pView, pInput, pModel);
203 void CButtonMode::DirectKeyDown(unsigned long iTime, int iId, CDasherView *pView, CDasherModel *pModel) {
204 CDasherButtons::DirectKeyDown(iTime, iId, pView, pModel);
205 if (iId!=100) m_iLastTime = iTime;
208 void CButtonMode::HandleEvent(int iParameter) {
209 switch (iParameter) {
210 case LP_B:
211 case LP_R:
212 // Delibarate fallthrough
213 delete[] m_pBoxes;
214 SetupBoxes();
215 m_pInterface->ScheduleRedraw();
216 break;
220 bool CButtonMode::GetSettings(SModuleSettings **pSettings, int *iCount) {
221 *pSettings = sSettings;
222 *iCount = sizeof(sSettings) / sizeof(SModuleSettings);
224 return true;