fpcalc.pas 15 KB

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