fpcalc.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. {
  2. This file is part of the Free Pascal Integrated Development Environment
  3. Copyright (c) 1998 by Berczi Gabor
  4. Calculator object for the IDE
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$I globdir.inc}
  12. unit FPCalc;
  13. interface
  14. uses
  15. Drivers,Objects,Views,Dialogs,App,
  16. FVConsts,
  17. WViews,
  18. FPViews;
  19. const
  20. MaxDecimals = 10;
  21. MaxDigits = 30;
  22. type
  23. TCalcState = (csFirst, csValid, csError);
  24. PCalcButton = ^TCalcButton;
  25. TCalcButton = object(TButton)
  26. procedure HandleEvent(var Event: TEvent); virtual;
  27. end;
  28. PCalcDisplay = ^TCalcDisplay;
  29. TCalcDisplay = object(TView)
  30. Status: TCalcState;
  31. Number: string[MaxDigits];
  32. Sign: Char;
  33. LastOperator,
  34. _Operator: Char;
  35. LastR,
  36. Operand: extended;
  37. Memory: extended;
  38. DispNumber: extended;
  39. HexShown : boolean;
  40. constructor Init(var Bounds: TRect);
  41. constructor Load(var S: TStream);
  42. function CalcKey(Key: string): boolean;
  43. procedure Clear;
  44. procedure Draw; virtual;
  45. function GetPalette: PPalette; virtual;
  46. procedure HandleEvent(var Event: TEvent); virtual;
  47. procedure Store(var S: TStream);
  48. private
  49. procedure GetDisplay(var R: extended);
  50. procedure SetDisplay(R: extended;ShouldKeepZeroes : boolean);
  51. procedure Error;
  52. end;
  53. PCalculator = ^TCalculator;
  54. TCalculator = object(TCenterDialog)
  55. CD : PCalcDisplay;
  56. constructor Init;
  57. procedure HandleEvent(var Event: TEvent); virtual;
  58. procedure Show; {virtual;}
  59. procedure Close; virtual;
  60. constructor Load(var S: TStream);
  61. procedure Store(var S: TStream);
  62. end;
  63. {$ifndef NOOBJREG}
  64. const
  65. RCalcButton: TStreamRec = (
  66. ObjType: 10139;
  67. VmtLink: Ofs(TypeOf(TCalcButton)^);
  68. Load: @TCalcButton.Load;
  69. Store: @TCalcButton.Store
  70. );
  71. RCalcDisplay: TStreamRec = (
  72. ObjType: 10140;
  73. VmtLink: Ofs(TypeOf(TCalcDisplay)^);
  74. Load: @TCalcDisplay.Load;
  75. Store: @TCalcDisplay.Store
  76. );
  77. RCalculator: TStreamRec = (
  78. ObjType: 10141;
  79. VmtLink: Ofs(TypeOf(TCalculator)^);
  80. Load: @TCalculator.Load;
  81. Store: @TCalculator.Store
  82. );
  83. {$endif}
  84. procedure RegisterFPCalc;
  85. implementation
  86. uses
  87. {$ifdef Unix}
  88. baseunix,
  89. unix,
  90. {$endif}
  91. {$ifdef go32v2}
  92. dpmiexcp,
  93. {$endif}
  94. {$ifdef windows}
  95. {$ifdef HasSignal}
  96. signals,
  97. {$endif}
  98. {$endif windows}
  99. FPUtils,FPConst,WUtils;
  100. const
  101. cmCalcButton = 100;
  102. cmPressButton = 101;
  103. {$ifdef useresstrings}
  104. resourcestring
  105. {$else}
  106. const
  107. {$endif}
  108. dialog_calculator = 'Calculator';
  109. procedure TCalcButton.HandleEvent(var Event: TEvent);
  110. var
  111. Call : boolean;
  112. i : Sw_Word;
  113. begin
  114. Call:=true;
  115. case Event.What of
  116. evKeyDown :
  117. case Event.KeyCode of
  118. kbEnter : Call:=false;
  119. end;
  120. evBroadcast :
  121. case Event.Command of
  122. cmDefault : Call:=false;
  123. cmPressButton :
  124. begin
  125. if (PString(Event.InfoPtr)^=Title^) or
  126. ((PString(Event.InfoPtr)^='^') and (Title^='x^y')) then
  127. begin
  128. Select;
  129. DrawState(true);
  130. i:=GetDosTicks+2;
  131. repeat
  132. until GetDosTicks>i;
  133. DrawState(false);
  134. ClearEvent(Event);
  135. end;
  136. end;
  137. end;
  138. end;
  139. if Call then
  140. inherited HandleEvent(Event);
  141. end;
  142. constructor TCalcDisplay.Init(var Bounds: TRect);
  143. begin
  144. inherited Init(Bounds);
  145. Options := Options or ofSelectable;
  146. EventMask := evKeyDown + evBroadcast;
  147. Clear;
  148. HelpCtx:={hcCalculatorLine}0;
  149. HexShown:=false;
  150. end;
  151. constructor TCalcDisplay.Load(var S: TStream);
  152. begin
  153. inherited Load(S);
  154. S.Read(Status, SizeOf(Status) + SizeOf(Number) + SizeOf(Sign) +
  155. SizeOf(_Operator) + SizeOf(Operand));
  156. HexShown:=false;
  157. end;
  158. procedure TCalcDisplay.GetDisplay(var R: extended);
  159. begin
  160. { Val(Sign + Number, R, E);}
  161. R:=DispNumber;
  162. end;
  163. procedure TCalcDisplay.SetDisplay(R: extended;ShouldKeepZeroes : boolean);
  164. var
  165. S: string[MaxDigits];
  166. i,KeepZeroes : byte;
  167. begin
  168. DispNumber:=R;
  169. KeepZeroes:=0;
  170. if ShouldKeepZeroes and (pos('.',Number)>0) then
  171. for i:=length(Number) downto pos('.',Number)+1 do
  172. if Number[i]='0' then
  173. inc(KeepZeroes)
  174. else
  175. break;
  176. Str(R: 0: MaxDecimals, S);
  177. if Pos('.',S)<>0 then
  178. while (length(S)>1) and (S[length(S)]='0') do Dec(S[0]);
  179. if KeepZeroes>0 then
  180. for i:=1 to KeepZeroes do
  181. S:=S+'0';
  182. if S[1] <> '-' then Sign := ' ' else
  183. begin
  184. Delete(S, 1, 1);
  185. Sign := '-';
  186. end;
  187. if Length(S) > MaxDigits + 1 + MaxDecimals then Error
  188. else
  189. begin
  190. if S[Length(S)] = '.' then Dec(S[0]);
  191. Number := S;
  192. end;
  193. end;
  194. procedure TCalcDisplay.Error;
  195. begin
  196. Status := csError;
  197. Number := 'Error';
  198. Sign := ' ';
  199. DrawView;
  200. end;
  201. {$ifdef HasSignal}
  202. var
  203. {$ifndef go32v2}
  204. CalcSigJmp : Jmp_Buf;
  205. {$else : go32v2}
  206. CalcSigJmp : dpmi_jmp_buf;
  207. {$endif go32v2}
  208. const
  209. fpucw : word = $1332;
  210. {$ifdef Unix}
  211. Procedure CalcSigFPE(sig : longint);cdecl;
  212. {$else}
  213. function CalcSigFPE(sig : longint) : longint;cdecl;
  214. {$endif}
  215. begin
  216. {$ifdef CPUI386}
  217. asm
  218. fninit
  219. fldcw fpucw
  220. end;
  221. {$endif}
  222. { ErrorBox('Error while computing math expression',nil);
  223. was only there for debugging PM }
  224. {$ifdef go32v2}
  225. Dpmi_LongJmp(CalcSigJmp,1);
  226. {$else : not go32v2}
  227. LongJmp(CalcSigJmp,1);
  228. {$endif go32v2}
  229. {$ifndef Unix}
  230. { Just here to avoid compiler warnings PM }
  231. CalcSigFPE:=0;
  232. {$endif}
  233. end;
  234. {$endif HasSignal}
  235. function TCalcDisplay.CalcKey(Key: string): boolean;
  236. var
  237. R,D: extended;
  238. X : cardinal;
  239. procedure CheckFirst;
  240. begin
  241. if Status = csFirst then
  242. begin
  243. Status := csValid;
  244. SetDisplay(0,false);
  245. end;
  246. end;
  247. {$ifdef HasSignal}
  248. var
  249. StoreSigFPE : SignalHandler;
  250. {$endif HasSignal}
  251. begin
  252. CalcKey:=true;
  253. Key := UpCaseStr(Key);
  254. {$ifdef HasSignal}
  255. {$ifdef CPUI386}
  256. asm
  257. fstcw fpucw
  258. end;
  259. {$endif}
  260. {$ifdef go32v2}
  261. if Dpmi_SetJmp(CalcSigJmp)=0 then
  262. {$else : not go32v2}
  263. if SetJmp(CalcSigJmp)=0 then
  264. {$endif go32v2}
  265. {$endif HasSignal}
  266. begin
  267. {$ifdef HasSignal}
  268. StoreSigFPE:={$ifdef unix}fpSignal{$else}Signal{$endif}(SIGFPE,@CalcSigFPE);
  269. {$endif HasSignal}
  270. if (Status = csError) and (Key <> 'C') then Key := ' ';
  271. if HexShown then
  272. begin
  273. GetDisplay(R);
  274. SetDisplay(R,false);
  275. HexShown := false;
  276. if Key = 'H' then
  277. Key := ' ';
  278. end;
  279. if Key='X^Y' then Key:='^';
  280. if length(Key)>1 then
  281. begin
  282. { if Status = csFirst then}
  283. begin
  284. { Status := csValid;}
  285. GetDisplay(R);
  286. if Key='1/X' then begin if R=0 then Error else SetDisplay(1/R,false) end else
  287. if Key='SQRT' then begin if R<0 then Error else SetDisplay(sqrt(R),false) end else
  288. if Key='LOG' then begin if R<=0 then Error else SetDisplay(ln(R),false) end else
  289. if Key='X^2' then SetDisplay(R*R,false) else
  290. if Key='M+' then Memory:=Memory+R else
  291. if Key='M-' then Memory:=Memory-R else
  292. if Key='M'#26 then SetDisplay(Memory,false) else
  293. if Key='M'#27 then Memory:=R else
  294. if Key='M'#29 then begin D:=Memory; Memory:=R; SetDisplay(D,false); end;
  295. end;
  296. end
  297. else
  298. case Key[1] of
  299. '0'..'9':
  300. if Length(Number)<MaxDigits then
  301. begin
  302. CheckFirst;
  303. if Number = '0' then Number := '';
  304. Number := Number + Key;
  305. SetDisplay(StrToExtended(Number),true);
  306. end;
  307. '.':
  308. begin
  309. CheckFirst;
  310. if Pos('.', Number) = 0 then Number := Number + '.';
  311. end;
  312. #8, #27:
  313. begin
  314. CheckFirst;
  315. if Length(Number) = 1 then Number := '0' else Dec(Number[0]);
  316. SetDisplay(StrToExtended(Number),true); { !!! }
  317. end;
  318. 'H':
  319. begin
  320. GetDisplay(R);
  321. X:=trunc(abs(R));
  322. Number:=HexStr(longint(X),8);
  323. HexShown:=true;
  324. end;
  325. '_', #241:
  326. begin
  327. if Sign = ' ' then Sign := '-' else Sign := ' ';
  328. GetDisplay(R);
  329. SetDisplay(-R,true);
  330. end;
  331. '+', '-', '*', '/', '=', '%', #13, '^':
  332. begin
  333. if (Key[1]='=') and (Status=csFirst) then
  334. begin
  335. Status:=csValid;
  336. R:=LastR;
  337. _Operator:=LastOperator;
  338. end
  339. else
  340. GetDisplay(R);
  341. if (Status = csValid) then
  342. begin
  343. Status := csFirst;
  344. LastR:=R;
  345. LastOperator:=_Operator;
  346. if Key = '%' then
  347. case _Operator of
  348. '+', '-': R := Operand * R / 100;
  349. '*', '/': R := R / 100;
  350. end;
  351. case _Operator of
  352. '^': if (Operand = 0)and(R <= 0) then Error else SetDisplay(Power(Operand,R),false);
  353. '+': SetDisplay(Operand + R,false);
  354. '-': SetDisplay(Operand - R,false);
  355. '*': SetDisplay(Operand * R,false);
  356. '/': if R = 0 then Error else SetDisplay(Operand / R,false);
  357. end;
  358. end;
  359. _Operator := Key[1];
  360. GetDisplay(Operand);
  361. end;
  362. 'C':
  363. Clear;
  364. else CalcKey:=false;
  365. end;
  366. {$ifdef HasSignal}
  367. {$ifdef unix}fpSignal{$else}Signal{$endif}(SIGFPE,StoreSigFPE);
  368. {$endif HasSignal}
  369. DrawView;
  370. {$ifdef HasSignal}
  371. end
  372. else { LongJmp called }
  373. begin
  374. ErrorBox('Error while computing '+Key,nil);
  375. CalcKey:=true;
  376. {$endif HasSignal}
  377. end;
  378. end;
  379. procedure TCalcDisplay.Clear;
  380. begin
  381. Status := csFirst;
  382. Number := '0';
  383. Sign := ' ';
  384. _Operator := '=';
  385. end;
  386. procedure TCalcDisplay.Draw;
  387. var
  388. Color: Byte;
  389. I: Integer;
  390. B: TDrawBuffer;
  391. begin
  392. Color := GetColor(1);
  393. I := Size.X - Length(Number) - 2;
  394. MoveChar(B, ' ', Color, Size.X);
  395. MoveChar(B[I], Sign, Color, 1);
  396. MoveStr(B[I + 1], Number, Color);
  397. WriteBuf(0, 0, Size.X, 1, B);
  398. end;
  399. function TCalcDisplay.GetPalette: PPalette;
  400. const
  401. P: string[1] = #19;
  402. begin
  403. GetPalette := @P;
  404. end;
  405. procedure TCalcDisplay.HandleEvent(var Event: TEvent);
  406. var S: string[3];
  407. begin
  408. inherited HandleEvent(Event);
  409. case Event.What of
  410. evKeyDown:
  411. if Owner<>nil then
  412. if (Owner^.State and sfSelected)<>0 then
  413. begin
  414. S:=Event.CharCode;
  415. Message(Owner,evBroadcast,cmPressButton,@S);
  416. if CalcKey(Event.CharCode) then
  417. ClearEvent(Event);
  418. end;
  419. evBroadcast:
  420. if Event.Command = cmCalcButton then
  421. begin
  422. CalcKey(PButton(Event.InfoPtr)^.Title^);
  423. ClearEvent(Event);
  424. end;
  425. end;
  426. end;
  427. procedure TCalcDisplay.Store(var S: TStream);
  428. begin
  429. TView.Store(S);
  430. S.Write(Status, SizeOf(Status) + SizeOf(Number) + SizeOf(Sign) +
  431. SizeOf(_Operator) + SizeOf(Operand));
  432. end;
  433. { TCalculator }
  434. constructor TCalculator.Init;
  435. const
  436. Keys: array[0..29] of string[4] =
  437. ('M+', 'x^y','C' ,#27 ,'%' ,#241 ,
  438. 'M-', 'x^2','7' ,'8' ,'9' ,'/' ,
  439. 'M'#26,'1/x','4' ,'5' ,'6' ,'*' ,
  440. 'M'#27,'sqrt','1' ,'2' ,'3' ,'-' ,
  441. 'M'#29,'log','0' ,'.' ,'=' ,'+' );
  442. var
  443. I: Integer;
  444. P: PView;
  445. R: TRect;
  446. begin
  447. R.Assign(5, 3, 43, 18);
  448. inherited Init(R, dialog_Calculator);
  449. Options := Options or ofFirstClick or ofTopSelect;
  450. HelpCtx:=hcCalcWindow;
  451. for I := 0 to 29 do
  452. begin
  453. R.A.X := (I mod 6) * 5 + 2;
  454. R.A.Y := (I div 6) * 2 + 4;
  455. R.B.X := R.A.X + 5;
  456. R.B.Y := R.A.Y + 2;
  457. if (I mod 6)=0 then Inc(R.B.X,1) else
  458. if (I mod 6)=1 then begin R.Move(1,0); Inc(R.B.X,2) end else
  459. R.Move(3,0);
  460. P := New(PCalcButton, Init(R, Keys[I], cmCalcButton,
  461. bfNormal + bfBroadcast+bfGrabFocus));
  462. P^.Options := P^.Options {and not ofSelectable};
  463. Insert(P);
  464. end;
  465. R.Assign(3, 2, 35, 3);
  466. New(CD, Init(R));
  467. CD^.Options:=CD^.Options or ofSelectable;
  468. Insert(CD);
  469. end;
  470. procedure TCalculator.HandleEvent(var Event: TEvent);
  471. var R: extended;
  472. { Re: real;}
  473. begin
  474. if (State and sfSelected)<>0 then
  475. case Event.What of
  476. evCommand :
  477. case Event.Command of
  478. cmCalculatorPaste :
  479. Message(@Self,evKeyDown,kbCtrlEnter,nil);
  480. end;
  481. evKeyDown :
  482. case Event.KeyCode of
  483. kbEnter :
  484. begin
  485. Event.KeyCode:=0;
  486. Event.CharCode:='=';
  487. end;
  488. kbCtrlEnter :
  489. begin
  490. ClearEvent(Event);
  491. CD^.GetDisplay(R); {Re:=R;}
  492. Close;
  493. CalcClipboard:=R;
  494. Message(Application,evBroadcast,cmCalculatorPaste,nil);
  495. end;
  496. kbEsc :
  497. begin
  498. CD^.GetDisplay(R);
  499. if R<>0 then begin
  500. CD^.SetDisplay(0,false);
  501. CD^.DrawView;
  502. end
  503. else Close;
  504. ClearEvent(Event);
  505. end;
  506. end;
  507. end;
  508. { lets CD try to handle this }
  509. if Event.What=evKeyDown then
  510. Message(CD,Event.What,Event.KeyCode,Event.InfoPtr);
  511. inherited HandleEvent(Event);
  512. end;
  513. procedure TCalculator.Show;
  514. begin
  515. { if GetState(sfVisible)=false then CD^.Clear;}
  516. inherited Show;
  517. end;
  518. procedure TCalculator.Close;
  519. begin
  520. Hide;
  521. end;
  522. constructor TCalculator.Load(var S: TStream);
  523. begin
  524. inherited Load(S);
  525. GetSubViewPtr(S,CD);
  526. end;
  527. procedure TCalculator.Store(var S: TStream);
  528. begin
  529. inherited Store(S);
  530. PutSubViewPtr(S,CD);
  531. end;
  532. procedure RegisterFPCalc;
  533. begin
  534. {$ifndef NOOBJREG}
  535. RegisterType(RCalcButton);
  536. RegisterType(RCalcDisplay);
  537. RegisterType(RCalculator);
  538. {$endif}
  539. end;
  540. end.