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