calc.c (21689B)
1 /* calc.c - estado y teclas de la calculadora (modo Calcular, menus, ajustes, catalogo). */ 2 #include <string.h> 3 #include "calc.h" 4 #include "dmath.h" 5 #include "str.h" 6 7 static const char *const MODE_NAMES[NMODES] = { "Calcular", "Ecuaciones", "Tabla", "Gráfico", "Programador", "Estadística", "Complejos", "Matrices" }; 8 const char *calc_mode_name(int m) { return m >= 0 && m < NMODES ? MODE_NAMES[m] : "?"; } 9 const char *calc_ang_name(int a) { return a == ANG_DEG ? "D" : a == ANG_RAD ? "R" : "G"; } 10 11 const char *calc_err_text(int err) 12 { 13 switch (err) { 14 case CE_SYNTAX: return "Error de sintaxis"; 15 case CE_MATH: return "Error matemático"; 16 case CE_STACK: return "Demasiado largo"; 17 case CE_ARG: return "Error de argumento"; 18 case CE_NOVAR: return "Falta un valor"; 19 } 20 return ""; 21 } 22 23 /* ------------------------------------------------------------ catalogo */ 24 25 static const CatItem C_TRIG[] = { 26 { "sin(", "sin(" }, { "cos(", "cos(" }, { "tan(", "tan(" }, 27 { "sin⁻¹(", "asin(" }, { "cos⁻¹(", "acos(" }, { "tan⁻¹(", "atan(" }, 28 }; 29 static const CatItem C_HYP[] = { 30 { "sinh(", "sinh(" }, { "cosh(", "cosh(" }, { "tanh(", "tanh(" }, 31 { "sinh⁻¹(", "asinh(" }, { "cosh⁻¹(", "acosh(" }, { "tanh⁻¹(", "atanh(" }, 32 }; 33 static const CatItem C_EXP[] = { 34 { "ln(", "ln(" }, { "log(", "log(" }, { "log□(□)", "\\l{" }, { "e^□", "\\e\\x{" }, 35 { "10^□", "10\\x{" }, { "√□", "\\r{" }, { "∛□", "\\n{3}{" }, { "□√□", "\\n{" }, 36 { "x²", "\\x{2}" }, { "x³", "\\x{3}" }, { "x⁻¹", "\\inv" }, { "x^□", "\\x{" }, 37 }; 38 static const CatItem C_PROB[] = { 39 { "x!", "!" }, { "nPr", "\\P" }, { "nCr", "\\C" }, { "Ran#", "\\ran" }, { "%", "%" }, 40 }; 41 static const CatItem C_NUM[] = { 42 { "Abs |□|", "\\a{" }, { "Int(", "int(" }, { "Intg(", "intg(" }, { "Rnd(", "rnd(" }, 43 { "GCD(", "gcd(" }, { "LCM(", "lcm(" }, { "□/□", "\\f{" }, { "□ □/□", "\\m{" }, 44 { "×10^", "\\E" }, { "Pol(", "pol(" }, { "Rec(", "rec(" }, 45 }; 46 static const CatItem C_CONST[] = { 47 { "π", "\\pi" }, { "e", "\\e" }, 48 { "c velocidad de la luz", "299792458" }, 49 { "g gravedad estándar", "9.80665" }, 50 { "G gravitación", "6.6743\\E-11" }, 51 { "h Planck", "6.62607015\\E-34" }, 52 { "q carga elemental", "1.602176634\\E-19" }, 53 { "me masa del electrón", "9.1093837015\\E-31" }, 54 { "mp masa del protón", "1.67262192369\\E-27" }, 55 { "NA Avogadro", "6.02214076\\E23" }, 56 { "k Boltzmann", "1.380649\\E-23" }, 57 { "R gases", "8.314462618" }, 58 { "atm", "101325" }, 59 }; 60 static const CatItem C_VAR[] = { 61 { "A", "\\vA" }, { "B", "\\vB" }, { "C", "\\vC" }, { "D", "\\vD" }, { "E", "\\vE" }, 62 { "F", "\\vF" }, { "M", "\\vM" }, { "X", "\\vX" }, { "Y", "\\vY" }, { "Ans", "\\ans" }, 63 { "PreAns", "\\pans" }, 64 }; 65 66 static const CatItem C_DIST[] = { 67 { "NormPD(x, σ, μ) densidad", "normpd(" }, { "NormCD(a, b, σ, μ) P(a≤X≤b)", "normcd(" }, 68 { "InvN(área, σ, μ) normal inversa", "invnorm(" }, { "BinomPD(x, n, p) P(X=x)", "binompd(" }, 69 { "BinomCD(x, n, p) P(X≤x)", "binomcd(" }, { "PoissPD(x, λ) P(X=x)", "poisspd(" }, 70 { "PoissCD(x, λ) P(X≤x)", "poisscd(" }, 71 }; 72 static const CatItem C_MAT[] = { 73 { "MatA", "\\MA" }, { "MatB", "\\MB" }, { "MatC", "\\MC" }, { "MatD", "\\MD" }, { "MatAns", "\\Mans" }, 74 { "VctA", "\\VA" }, { "VctB", "\\VB" }, { "VctC", "\\VC" }, { "VctD", "\\VD" }, { "VctAns", "\\Vans" }, 75 { "Det( determinante", "det(" }, { "Trn( traspuesta", "trn(" }, { "Identity(n)", "identity(" }, 76 { "x⁻¹ inversa", "\\inv" }, { "DotP( producto escalar", "dot(" }, { "CrossP( producto vectorial", "cross(" }, 77 { "Angle( ángulo entre vectores", "angle(" }, { "UnitV( vector unitario", "unitv(" }, { "|□| norma", "\\a{" }, 78 }; 79 static const CatItem C_CPLX[] = { 80 { "i unidad imaginaria", "i" }, { "∠ forma polar r∠θ", "\\ang" }, { "arg(", "arg(" }, 81 { "Conjg(", "conjg(" }, { "ReP( parte real", "real(" }, { "ImP( parte imaginaria", "imag(" }, 82 }; 83 static const CatItem C_CALC[] = { 84 { "∫ integral definida", "\\i{" }, { "d/dx derivada en un punto", "\\d{" }, 85 { "Σ sumatoria", "\\s{" }, { "Π productoria", "\\p{" }, 86 }; 87 88 #define G(name, arr) { name, arr, (int)(sizeof arr / sizeof arr[0]) } 89 const CatGroup CATALOG[] = { 90 G("Trigonométricas", C_TRIG), G("Hiperbólicas", C_HYP), G("Exponenciales y raíces", C_EXP), 91 G("Análisis (∫ d/dx Σ Π)", C_CALC), G("Probabilidad", C_PROB), G("Distribuciones", C_DIST), 92 G("Complejos", C_CPLX), G("Matrices y vectores", C_MAT), G("Numéricas", C_NUM), G("Constantes", C_CONST), G("Variables", C_VAR), 93 }; 94 const int CATALOG_N = (int)(sizeof CATALOG / sizeof CATALOG[0]); 95 96 /* ------------------------------------------------------------ estado */ 97 98 void calc_init(Calc *c) 99 { 100 memset(c, 0, sizeof *c); 101 c->ang = ANG_DEG; 102 fmt_default(&c->fmt); 103 for (int i = 0; i < NVARS; i++) c->vars[i] = c->vars_im[i] = num_int(0); 104 c->ans = c->preans = c->ans_im = c->res_im = num_int(0); 105 c->hsel = -1; 106 c->cat = -1; 107 c->pg.base = 10; 108 c->pg.bits = 32; 109 c->tb.start = num_int(1); 110 c->tb.end = num_int(5); 111 c->tb.step = num_int(1); 112 c->gr.xmin = dec_int(-10); c->gr.xmax = dec_int(10); 113 c->gr.ymin = dec_int(-6); c->gr.ymax = dec_int(6); 114 c->gr.autoy = true; 115 } 116 117 EvalCtx calc_ctx(const Calc *c) 118 { 119 EvalCtx cx; 120 memset(&cx, 0, sizeof cx); 121 cx.ang = c->ang; 122 cx.vars = (Num *)c->vars; 123 cx.ans = c->ans; 124 cx.preans = c->preans; 125 cx.rand = c->rand; 126 cx.prog = c->mode == MODE_PROG; 127 cx.base = c->pg.base; 128 cx.bits = c->pg.bits; 129 cx.rvalid = c->st.rvalid; 130 cx.rtype = c->st.rtype; 131 cx.ra = c->st.ra; cx.rb = c->st.rb; cx.rc = c->st.rc; 132 cx.cplx = c->mode == MODE_CMPLX; 133 cx.ans_im = c->ans_im; 134 cx.vars_im = (Num *)c->vars_im; 135 cx.mats = c->mt.m; 136 cx.vcts = c->mt.v; 137 cx.ans_mat = c->mt.ans_mat; 138 return cx; 139 } 140 141 /* re + im·i (o r∠θ) como expresion */ 142 static void cplx_expr(const Calc *c, Num re, Num im, bool dec, bool polar, Expr *out) 143 { 144 static Expr a; 145 bool d = dec || c->dec_default; 146 if (polar) { 147 Num r = num_sqrt(num_add(num_mul(re, re), num_mul(im, im))); 148 fmt_result(&r, &c->fmt, d, out); 149 if (!re.v.m && !im.v.m) return; 150 Num th = cplx_arg(re, im, c->ang); 151 fmt_result(&th, &c->fmt, d, &a); 152 out->cur = out->n; 153 ex_insert_raw(out, T_ANGLE); 154 ex_insert_expr(out, &a); 155 return; 156 } 157 fmt_result(&re, &c->fmt, d, out); 158 if (!im.v.m) return; 159 bool neg = im.v.neg; 160 if (neg) im = num_neg(im); 161 fmt_result(&im, &c->fmt, d, &a); 162 out->cur = out->n; 163 if (!re.v.m) ex_clear(out); 164 if (re.v.m || neg) ex_insert_raw(out, neg ? '-' : '+'); 165 bool one = im.exact && ex_is_int(&im.x) && im.x.a == 1; 166 /* fraccion o suma en la parte imaginaria: i al costado queda claro igual */ 167 if (!one) ex_insert_expr(out, &a); 168 ex_insert_raw(out, 'i'); 169 } 170 171 void calc_res_expr(const Calc *c, Expr *out) 172 { 173 if (c->mode == MODE_CMPLX && !c->dms) { cplx_expr(c, c->res, c->res_im, c->want_dec, c->polar, out); return; } 174 calc_result_expr(c, &c->res, c->want_dec, out); 175 } 176 177 void calc_hist_expr(const Calc *c, const Hist *h, Expr *out) 178 { 179 if (h->mkind) { 180 ex_clear(out); 181 ex_insert_raw(out, h->mkind == MV_MAT ? T_MATANS : T_VCTANS); 182 return; 183 } 184 if (h->res_im.v.m || h->polar) { cplx_expr(c, h->res, h->res_im, h->dec, h->polar, out); return; } 185 fmt_result(&h->res, &c->fmt, h->dec || c->dec_default, out); 186 } 187 188 bool calc_res_has_exact(const Calc *c) 189 { 190 return fmt_has_exact(&c->res, &c->fmt) || (c->mode == MODE_CMPLX && fmt_has_exact(&c->res_im, &c->fmt)); 191 } 192 193 bool calc_result_expr(const Calc *c, const Num *v, bool dec, Expr *out) 194 { 195 if (v == &c->res && c->dms) { fmt_dms(v->v, out); return false; } 196 return fmt_result(v, &c->fmt, dec || c->dec_default, out); 197 } 198 199 static void changed(Calc *c) { c->version++; } 200 201 static void say(Calc *c, const char *m) { str_put(c->msg, sizeof c->msg, 0, m); } 202 203 /* ------------------------------------------------------------ evaluar */ 204 205 static void push_hist(Calc *c) 206 { 207 if (c->nh == HIST_MAX) { memmove(&c->hist[0], &c->hist[1], sizeof c->hist[0] * (HIST_MAX - 1)); c->nh--; } 208 Hist *h = &c->hist[c->nh++]; 209 h->in = c->in; 210 h->res = c->res; 211 h->err = (uint8_t)c->err; 212 h->dec = c->want_dec; 213 h->res_im = c->mode == MODE_CMPLX ? c->res_im : num_int(0); 214 h->polar = c->polar && c->mode == MODE_CMPLX; 215 h->mkind = c->mode == MODE_MATRIX && !c->err ? c->mt.res.kind : 0; 216 } 217 218 static void exe(Calc *c, bool force_dec) 219 { 220 if (ex_empty(&c->in)) return; 221 c->in.cur = c->in.n; 222 ex_tidy(&c->in); 223 EvalCtx cx = calc_ctx(c); 224 Num r, ri = num_int(0); 225 int pos = 0; 226 if (c->mode == MODE_CMPLX) c->err = calc_eval_c(&c->in, &cx, &r, &ri, &pos); 227 else if (c->mode == MODE_MATRIX) { 228 c->err = calc_eval_m(&c->in, &cx, &c->mt.res, &pos); 229 const MVal *mv = &c->mt.res; 230 if (!c->err) { 231 if (mv->kind == MV_SCALAR) { r = mv->a[0]; c->mt.ans_mat = false; } 232 else { 233 r = num_int(0); 234 c->mt.m[4].r = c->mt.v[4].r = 0; 235 if (mv->kind == MV_MAT) c->mt.m[4] = *mv; else c->mt.v[4] = *mv; 236 c->mt.ans_mat = true; 237 } 238 } 239 } 240 else c->err = calc_eval(&c->in, &cx, &r, &pos); 241 c->err_pos = pos; 242 c->shown = true; 243 c->want_dec = force_dec; 244 c->dms = false; 245 if (!c->err) { 246 /* lo que se guarda en Ans va redondeado a 15 cifras, como la Casio */ 247 if (!r.exact) r.v = dec_round_sig(r.v, 15); 248 c->res = r; 249 c->res_im = ri; 250 c->preans = c->ans; 251 c->ans = r; 252 c->ans_im = ri; 253 } 254 if (c->hsel >= 0 && c->hsel < c->nh) { 255 /* se re-ejecuto una del historial: va al final como nueva */ 256 c->hsel = -1; 257 } 258 push_hist(c); 259 c->hsel = -1; 260 c->dirty_save = true; 261 } 262 263 /* guarda el valor actual (o el resultado) en una variable */ 264 static void store(Calc *c, int var) 265 { 266 if (!c->shown && !ex_empty(&c->in)) exe(c, false); 267 if (c->err) return; 268 c->vars[var] = c->shown ? c->res : c->ans; 269 c->vars_im[var] = c->mode == MODE_CMPLX ? (c->shown ? c->res_im : c->ans_im) : num_int(0); 270 char m[32] = "Guardado en "; 271 int p = (int)strlen(m); 272 m[p] = VAR_NAMES[var]; m[p + 1] = 0; 273 say(c, m); 274 c->dirty_save = true; 275 } 276 277 static int var_of_key(int key) 278 { 279 if (key >= 'a' && key <= 'z') key -= 32; 280 const char *v = key ? strchr(VAR_NAMES, key) : 0; 281 return v ? (int)(v - VAR_NAMES) : -1; 282 } 283 284 /* una tecla con el resultado en pantalla: empezar una cuenta nueva */ 285 static void new_entry(Calc *c, int key) 286 { 287 bool op = key == '+' || key == '-' || key == '*' || key == ':' || key == '/' || key == '^' || 288 key == '!' || key == '%'; 289 ex_clear(&c->in); 290 c->shown = false; 291 c->err = 0; 292 c->want_dec = false; 293 c->dms = false; 294 if (op && !c->err) ex_insert_raw(&c->in, T_ANS); 295 } 296 297 static void recall(Calc *c, int idx) 298 { 299 c->hsel = idx; 300 if (idx < 0) { 301 ex_clear(&c->in); 302 c->shown = false; 303 c->err = 0; 304 return; 305 } 306 Hist *h = &c->hist[idx]; 307 c->in = h->in; 308 c->in.cur = c->in.n; 309 c->res = h->res; 310 c->res_im = h->res_im; 311 c->polar = h->polar; 312 c->err = h->err; 313 c->want_dec = h->dec; 314 c->shown = true; 315 } 316 317 /* tecla de edicion sobre una expresion cualquiera (se usa tambien en los modos) */ 318 bool edit_key(Calc *c, Expr *e, int key) 319 { 320 switch (key) { 321 case K_LEFT: ex_left(e); return true; 322 case K_RIGHT: ex_right(e); return true; 323 case K_HOME: ex_home(e); return true; 324 case K_END: ex_end(e); return true; 325 case K_BKSP: ex_backspace(e); return true; 326 case K_DEL: ex_delete(e); return true; 327 case '*': return ex_insert(e, T_MUL); 328 case ':': return ex_insert(e, T_DIV); 329 case '#': return c->shift ? ex_root_n(e, 3) : ex_template(e, T_SQRT); 330 case '$': return ex_template(e, T_ROOT); 331 case '|': { 332 /* la segunda | cierra el valor absoluto, como al escribirlo a mano */ 333 int fld, ct = ex_container(e, e->cur, &fld); 334 if (ct >= 0 && e->t[ct] == T_ABS && e->cur < e->n && e->t[e->cur] == T_END && e->t[e->cur - 1] != T_ABS) { 335 e->cur++; 336 return true; 337 } 338 return ex_template(e, T_ABS); 339 } 340 case ';': return ex_template(e, T_MIXED); 341 case '[': return ex_insert(e, T_E10); 342 case '^': return c->shift ? ex_insert(e, T_INV) : ex_insert(e, '^'); 343 } 344 if (c->mode == MODE_PROG && key >= 'A' && key <= 'F') return ex_insert_raw(e, key); /* digitos hex */ 345 if (c->mode == MODE_MATRIX && key >= 'A' && key <= 'D') /* MatA-D; con SHIFT, VctA-D */ 346 return ex_insert_raw(e, (c->shift ? T_VCT : T_MAT) + key - 'A'); 347 if (key >= 32 && key < 127) return ex_insert(e, key); 348 return false; 349 } 350 351 /* ------------------------------------------------------------ menus */ 352 353 static void setup_form(Calc *c) 354 { 355 static const char *const ANG[] = { "Grados", "Radianes", "Gradianes" }; 356 static const char *const RES[] = { "Exacto (fracción, raíz, pi)", "Decimal" }; 357 static const char *const FMTS[] = { "Norm 1", "Norm 2", "Fix", "Sci", "Eng" }; 358 static const char *const NS[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; 359 static const char *const FR[] = { "a/b", "mixta a b/c" }; 360 static const char *const DG[] = { "10", "12", "15" }; 361 Form *f = &c->form; 362 form_begin(f, "Ajustes"); 363 form_choice(f, 1, "Ángulo", ANG, 3, c->ang); 364 form_choice(f, 2, "Resultado", RES, 2, c->dec_default); 365 form_choice(f, 3, "Formato", FMTS, 5, c->fmt.mode); 366 form_choice(f, 4, "Fix/Sci: cifras", NS, 10, c->fmt.n); 367 form_choice(f, 5, "Fracciones", FR, 2, c->fmt.mixed); 368 form_choice(f, 6, "Cifras en Norm", DG, 3, c->fmt.digits == 15 ? 2 : c->fmt.digits == 12 ? 1 : 0); 369 form_button(f, 10, "Borrar el historial"); 370 form_button(f, 11, "Borrar las variables"); 371 form_button(f, 12, "Probar el teclado"); 372 form_button(f, 13, "Volver"); 373 c->form_kind = FORM_SETUP; 374 c->scr = SCR_FORM; 375 } 376 377 static void setup_apply(Calc *c) 378 { 379 Form *f = &c->form; 380 c->ang = form_get(f, 1)->value; 381 c->dec_default = form_get(f, 2)->value; 382 c->fmt.mode = (uint8_t)form_get(f, 3)->value; 383 c->fmt.n = (uint8_t)form_get(f, 4)->value; 384 c->fmt.mixed = form_get(f, 5)->value; 385 int d = form_get(f, 6)->value; 386 c->fmt.digits = (uint8_t)(d == 2 ? 15 : d == 1 ? 12 : 10); 387 c->dirty_save = true; 388 } 389 390 /* formularios de los modos (ver modes.c) */ 391 bool mode_form_done(Calc *c, int id); 392 393 static bool form_key_calc(Calc *c, int key) 394 { 395 int r = form_key(&c->form, key); 396 if (c->form_kind == FORM_SETUP) { 397 setup_apply(c); 398 if (r == 10) { c->nh = 0; c->hsel = -1; say(c, "Historial borrado"); c->scr = SCR_MAIN; } 399 else if (r == 11) { 400 for (int i = 0; i < NVARS; i++) c->vars[i] = c->vars_im[i] = num_int(0); 401 c->ans = c->preans = c->ans_im = num_int(0); 402 say(c, "Variables en cero"); 403 c->scr = SCR_MAIN; 404 } else if (r == 12) { c->scr = SCR_KEYTEST; c->keytest = -1; } 405 else if (r == 13 || r == FORM_CANCEL) c->scr = SCR_MAIN; 406 return true; 407 } 408 if (r == FORM_NONE) return true; 409 if (r == FORM_CANCEL) { c->scr = SCR_MAIN; return true; } 410 return mode_form_done(c, r); 411 } 412 413 static bool menu_key(Calc *c, int key) 414 { 415 int n = NMODES + 1; /* + Ajustes */ 416 if (key == K_UP) c->menu_sel = (c->menu_sel + n - 1) % n; 417 else if (key == K_DOWN || key == K_TAB) c->menu_sel = (c->menu_sel + 1) % n; 418 else if (key == K_LEFT) c->menu_sel = (c->menu_sel + n - 3) % n; 419 else if (key == K_RIGHT) c->menu_sel = (c->menu_sel + 3) % n; 420 else if (key == K_BACK || key == '@' || key == K_F1 + 1) c->scr = SCR_MAIN; 421 else if (key >= '1' && key <= '0' + n) { c->menu_sel = key - '1'; key = K_OK; } 422 if (key == K_OK) { 423 if (c->menu_sel == NMODES) setup_form(c); 424 else { c->scr = SCR_MAIN; mode_enter(c, c->menu_sel); } 425 } 426 return true; 427 } 428 429 static void catalog_insert(Calc *c, const char *text) 430 { 431 Expr *e = &c->in; 432 extern Expr *mode_target(Calc *c); 433 Expr *t = mode_target(c); 434 if (t) e = t; 435 if (e == &c->in && c->shown) new_entry(c, 0); 436 ex_type_text(e, text); 437 } 438 439 static bool catalog_key(Calc *c, int key) 440 { 441 int n = c->cat < 0 ? CATALOG_N : CATALOG[c->cat].n; 442 if (key == K_UP) c->cat_sel = (c->cat_sel + n - 1) % n; 443 else if (key == K_DOWN || key == K_TAB) c->cat_sel = (c->cat_sel + 1) % n; 444 else if (key == K_BACK || key == K_LEFT) { 445 if (c->cat >= 0) { c->cat_sel = c->cat; c->cat = -1; } 446 else c->scr = SCR_MAIN; 447 } else if (key >= '1' && key <= '9' && key - '1' < n) { c->cat_sel = key - '1'; key = K_OK; } 448 if (key == K_OK || key == K_RIGHT) { 449 if (c->cat < 0) { c->cat = c->cat_sel; c->cat_sel = 0; } 450 else if (key == K_OK) { 451 catalog_insert(c, CATALOG[c->cat].items[c->cat_sel].text); 452 c->scr = SCR_MAIN; 453 } 454 } 455 return true; 456 } 457 458 /* ------------------------------------------------------------ teclas */ 459 460 static bool main_key(Calc *c, int key) 461 { 462 /* STO pendiente: la tecla que sigue es la variable */ 463 if (c->sto) { 464 c->sto = false; 465 int v = var_of_key(key); 466 if (v >= 0) { store(c, v); return true; } 467 say(c, ""); 468 } 469 switch (key) { 470 case K_OK: 471 exe(c, c->shift); 472 return true; 473 case K_BACK: /* AC */ 474 ex_clear(&c->in); 475 c->shown = false; 476 c->err = 0; 477 c->hsel = -1; 478 return true; 479 case K_TAB: case K_F1 + 3: /* S⇔D (en Programador: la base) */ 480 if (c->mode == MODE_PROG) { 481 static const int B[] = { 10, 16, 2, 8 }; 482 int i = 0; 483 while (i < 4 && B[i] != c->pg.base) i++; 484 c->pg.base = B[(i + 1) % 4]; 485 if (c->shown) { c->shown = false; ex_clear(&c->in); } 486 c->dirty_save = true; 487 return true; 488 } 489 if (c->shown && !c->err && calc_res_has_exact(c)) c->want_dec = !c->want_dec; 490 else if (!c->shown && !ex_empty(&c->in)) exe(c, true); 491 return true; 492 case '=': 493 if (c->mode == MODE_MATRIX) { extern void mat_pick_form(Calc *c); mat_pick_form(c); return true; } 494 break; 495 case '\'': /* °′″: en el resultado alterna; si no, escribe ° */ 496 if (c->shown && !c->err && c->mode == MODE_CALC) { c->dms = !c->dms; return true; } 497 if (c->shown) new_entry(c, 0); 498 c->hsel = -1; 499 return ex_insert(&c->in, T_DEGS); 500 case '<': /* complejos: ∠, o alternar a+bi / r∠θ */ 501 if (c->mode != MODE_CMPLX) break; 502 if (c->shown && !c->err) { c->polar = !c->polar; return true; } 503 if (c->shown) new_entry(c, 0); 504 c->hsel = -1; 505 return ex_insert(&c->in, T_ANGLE); 506 case '>': 507 c->sto = true; 508 say(c, "Guardar en: tocá A-F, M, X o Y"); 509 return true; 510 case K_UP: case K_DOWN: { 511 if (!c->shown) { 512 /* moverse dentro de la expresion 2D lo resuelve el frontend con el layout; 513 * si no hay fila, historial */ 514 if (key == K_UP && c->nh > 0) { recall(c, c->hsel < 0 ? c->nh - 1 : c->hsel > 0 ? c->hsel - 1 : 0); return true; } 515 return false; 516 } 517 if (key == K_UP) { if (c->nh) recall(c, c->hsel < 0 ? (c->nh > 1 ? c->nh - 2 : 0) : c->hsel > 0 ? c->hsel - 1 : 0); } 518 else { 519 if (c->hsel < 0) return false; 520 recall(c, c->hsel + 1 < c->nh ? c->hsel + 1 : -1); 521 } 522 return true; 523 } 524 case K_LEFT: case K_RIGHT: 525 if (c->shown) { 526 /* volver a editar la cuenta (con error: el cursor va al lugar del error) */ 527 c->shown = false; 528 if (c->err) c->in.cur = c->err_pos < c->in.n ? c->err_pos : c->in.n; 529 else c->in.cur = key == K_LEFT ? c->in.n : 0; 530 c->err = 0; 531 return true; 532 } 533 break; 534 } 535 if (c->shown) { 536 if (key == K_BKSP || key == K_DEL) { c->shown = false; c->err = 0; return true; } 537 if ((key >= 32 && key < 127) || key == K_F1) new_entry(c, key); 538 } 539 c->hsel = -1; 540 c->err = 0; 541 return edit_key(c, &c->in, key); 542 } 543 544 bool calc_key(Calc *c, int key) 545 { 546 if (key <= 0) return key == 0; 547 bool shift = c->shift; 548 if (key == K_F1 || key == '`') { 549 c->shift = !c->shift; 550 changed(c); 551 return true; 552 } 553 c->msg[0] = 0; 554 bool r = false; 555 if (key == K_F1 + 1 || key == '@') { /* MENU */ 556 if (c->scr == SCR_MENU) c->scr = SCR_MAIN; 557 else { c->scr = SCR_MENU; c->menu_sel = c->mode; } 558 r = true; 559 } else if ((key == K_F1 + 4) && c->scr != SCR_FORM) { setup_form(c); r = true; } 560 else if ((key == K_F1 + 2 || key == '?') && c->scr == SCR_MAIN) { 561 c->scr = SCR_CATALOG; c->cat = -1; c->cat_sel = 0; r = true; 562 } else switch (c->scr) { 563 case SCR_MENU: r = menu_key(c, key); break; 564 case SCR_FORM: r = form_key_calc(c, key); break; 565 case SCR_CATALOG: r = catalog_key(c, key); break; 566 case SCR_KEYTEST: 567 if (key == K_BACK && c->keytest == K_BACK) c->scr = SCR_MAIN; /* Esc dos veces sale */ 568 c->keytest = key; 569 r = true; 570 break; 571 default: 572 r = calc_main_screen(c) ? main_key(c, key) : mode_key(c, key); 573 } 574 if (shift && key != K_F1) c->shift = false; 575 if (r) changed(c); 576 return r; 577 }