1 // This file is part of Golly.
2 // See docs/License.html for the copyright notice.
3
4 #include "bigint.h"
5 #include "lifealgo.h"
6
7 #include "utils.h" // for Fatal, Beep
8 #include "prefs.h" // for mindelay, maxdelay, etc
9 #include "algos.h" // for algoinfo
10 #include "layer.h" // for currlayer
11 #include "view.h" // for nopattupdate, widescreen, PointInView, etc
12 #include "status.h"
13 #include <math.h> // for fabs
14
15 #ifdef ANDROID_GUI
16 #include "jnicalls.h" // for UpdateStatus, GetRuleName
17 #endif
18
19 #ifdef WEB_GUI
20 #include "webcalls.h" // for UpdateStatus, GetRuleName
21 #endif
22
23 #ifdef IOS_GUI
24 #import "PatternViewController.h" // for UpdateStatus
25 #import "RuleViewController.h" // for GetRuleName
26 #endif
27
28 // -----------------------------------------------------------------------------
29
30 std::string status1; // top line
31 std::string status2; // middle line
32 std::string status3; // bottom line
33
34 // prefixes used when widescreen is true:
35 const char* large_gen_prefix = "Generation=";
36 const char* large_algo_prefix = " Algorithm=";
37 const char* large_rule_prefix = " Rule=";
38 const char* large_pop_prefix = " Population=";
39 const char* large_scale_prefix = " Scale=";
40 const char* large_step_prefix = " ";
41 const char* large_xy_prefix = " XY=";
42
43 // prefixes used when widescreen is false:
44 const char* small_gen_prefix = "Gen=";
45 const char* small_algo_prefix = " Algo=";
46 const char* small_rule_prefix = " Rule=";
47 const char* small_pop_prefix = " Pop=";
48 const char* small_scale_prefix = " Scale=";
49 const char* small_step_prefix = " ";
50 const char* small_xy_prefix = " XY=";
51
52 static bigint currx, curry; // cursor location in cell coords
53 static bool showxy = false; // show cursor's XY location?
54
55 // -----------------------------------------------------------------------------
56
UpdateStatusLines()57 void UpdateStatusLines()
58 {
59 std::string rule = currlayer->algo->getrule();
60
61 status1 = "Pattern=";
62 if (currlayer->dirty) {
63 // display asterisk to indicate pattern has been modified
64 status1 += "*";
65 }
66 status1 += currlayer->currname;
67 status1 += widescreen ? large_algo_prefix : small_algo_prefix;
68 status1 += GetAlgoName(currlayer->algtype);
69 status1 += widescreen ? large_rule_prefix : small_rule_prefix;
70 status1 += rule;
71
72 // show rule name if one exists and is not same as rule
73 // (best NOT to remove any suffix like ":T100,200" in case we allow
74 // users to name "B3/S23:T100,200" as "Life on torus")
75 std::string rulename = GetRuleName(rule);
76 if (!rulename.empty() && rulename != rule) {
77 status1 += " [";
78 status1 += rulename;
79 status1 += "]";
80 }
81
82 char scalestr[32];
83 int mag = currlayer->view->getmag();
84 if (mag < 0) {
85 sprintf(scalestr, "2^%d:1", -mag);
86 } else {
87 sprintf(scalestr, "1:%d", 1 << mag);
88 }
89
90 char stepstr[32];
91 if (currlayer->currexpo < 0) {
92 // show delay in secs
93 sprintf(stepstr, "Delay=%gs", (double)GetCurrentDelay() / 1000.0);
94 } else {
95 sprintf(stepstr, "Step=%d^%d", currlayer->currbase, currlayer->currexpo);
96 }
97
98 status2 = widescreen ? large_gen_prefix : small_gen_prefix;
99 if (nopattupdate) {
100 status2 += "0";
101 } else {
102 status2 += Stringify(currlayer->algo->getGeneration());
103 }
104 status2 += widescreen ? large_pop_prefix : small_pop_prefix;
105 if (nopattupdate) {
106 status2 += "0";
107 } else {
108 bigint popcount = currlayer->algo->getPopulation();
109 if (popcount.sign() < 0) {
110 // getPopulation returns -1 if it can't be calculated
111 status2 += "?";
112 } else {
113 status2 += Stringify(popcount);
114 }
115 }
116 status2 += widescreen ? large_scale_prefix : small_scale_prefix;
117 status2 += scalestr;
118 status2 += widescreen ? large_step_prefix : small_step_prefix;
119 status2 += stepstr;
120 status2 += widescreen ? large_xy_prefix : small_xy_prefix;
121 #ifdef WEB_GUI
122 // in web app we show the cursor's current cell location,
123 // or nothing if the cursor is outside the viewport (ie. showxy is false)
124 if (showxy) {
125 bigint xo, yo;
126 bigint xpos = currx; xpos -= currlayer->originx;
127 bigint ypos = curry; ypos -= currlayer->originy;
128 if (mathcoords) {
129 // Y values increase upwards
130 bigint temp = 0;
131 temp -= ypos;
132 ypos = temp;
133 }
134 status2 += Stringify(xpos);
135 status2 += " ";
136 status2 += Stringify(ypos);
137 }
138 #else
139 // in iOS and Android apps we show location of the cell in middle of viewport
140 status2 += Stringify(currlayer->view->x);
141 status2 += " ";
142 status2 += Stringify(currlayer->view->y);
143 #endif
144 }
145
146 // -----------------------------------------------------------------------------
147
ClearMessage()148 void ClearMessage()
149 {
150 if (status3.length() == 0) return; // no need to clear message
151
152 status3.clear();
153 UpdateStatus();
154 }
155
156 // -----------------------------------------------------------------------------
157
DisplayMessage(const char * s)158 void DisplayMessage(const char* s)
159 {
160 status3 = s;
161 UpdateStatus();
162 }
163
164 // -----------------------------------------------------------------------------
165
ErrorMessage(const char * s)166 void ErrorMessage(const char* s)
167 {
168 Beep();
169 DisplayMessage(s);
170 }
171
172 // -----------------------------------------------------------------------------
173
SetMessage(const char * s)174 void SetMessage(const char* s)
175 {
176 // set message string without displaying it
177 status3 = s;
178 }
179
180 // -----------------------------------------------------------------------------
181
GetCurrentDelay()182 int GetCurrentDelay()
183 {
184 int gendelay = mindelay * (1 << (-currlayer->currexpo - 1));
185 if (gendelay > maxdelay) gendelay = maxdelay;
186 return gendelay;
187 }
188
189 // -----------------------------------------------------------------------------
190
Stringify(const bigint & b)191 char* Stringify(const bigint& b)
192 {
193 static char buf[32];
194 char* p = buf;
195 double d = b.todouble();
196 if ( fabs(d) > 1000000000.0 ) {
197 // use e notation for abs value > 10^9 (agrees with min & max_coord)
198 sprintf(p, "%g", d);
199 } else {
200 // show exact value with commas inserted for readability
201 if ( d < 0 ) {
202 d = - d;
203 *p++ = '-';
204 }
205 sprintf(p, "%.f", d);
206 int len = (int)strlen(p);
207 int commas = ((len + 2) / 3) - 1;
208 int dest = len + commas;
209 int src = len;
210 p[dest] = 0;
211 while (commas > 0) {
212 p[--dest] = p[--src];
213 p[--dest] = p[--src];
214 p[--dest] = p[--src];
215 p[--dest] = ',';
216 commas--;
217 }
218 if ( p[-1] == '-' ) p--;
219 }
220 return p;
221 }
222
223 // -----------------------------------------------------------------------------
224
CheckMouseLocation(int x,int y)225 void CheckMouseLocation(int x, int y)
226 {
227 // check if we need to update XY location in status bar
228 bool mouse_in_grid = false;
229 bigint xpos, ypos;
230 if (PointInView(x, y)) {
231 // get mouse location in cell coords
232 pair<bigint, bigint> cellpos = currlayer->view->at(x, y);
233 xpos = cellpos.first;
234 ypos = cellpos.second;
235 // check if xpos,ypos is inside grid (possibly bounded)
236 mouse_in_grid = CellInGrid(xpos, ypos);
237 }
238
239 if (mouse_in_grid) {
240 if ( xpos != currx || ypos != curry ) {
241 // show new XY location
242 currx = xpos;
243 curry = ypos;
244 showxy = true;
245 UpdateStatus();
246 } else if (!showxy) {
247 // mouse moved from outside grid and back over currx,curry
248 showxy = true;
249 UpdateStatus();
250 }
251 } else {
252 // mouse is outside grid so clear XY location if necessary
253 if (showxy) {
254 showxy = false;
255 UpdateStatus();
256 }
257 }
258 }
259