fpcalc.pas 14 KB

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