fpcalc.pas 14 KB

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