fpcalc.pas 14 KB

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