fpcalc.pas 14 KB

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