fpcalc.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  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. 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;{ $ifndef go32v2}cdecl;{$endif}
  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:={$ifdef unix}{$ifdef ver1_0}Signal{$else}fpSignal{$endif}{$else}Signal{$endif}(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='SQRT' 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 (Key[1]='=') and (Status=csFirst) then
  332. begin
  333. Status:=csValid;
  334. R:=LastR;
  335. _Operator:=LastOperator;
  336. end
  337. else
  338. GetDisplay(R);
  339. if (Status = csValid) then
  340. begin
  341. Status := csFirst;
  342. LastR:=R;
  343. LastOperator:=_Operator;
  344. if Key = '%' then
  345. case _Operator of
  346. '+', '-': R := Operand * R / 100;
  347. '*', '/': R := R / 100;
  348. end;
  349. case _Operator of
  350. '^': if (Operand = 0)and(R <= 0) then Error else SetDisplay(Power(Operand,R),false);
  351. '+': SetDisplay(Operand + R,false);
  352. '-': SetDisplay(Operand - R,false);
  353. '*': SetDisplay(Operand * R,false);
  354. '/': if R = 0 then Error else SetDisplay(Operand / R,false);
  355. end;
  356. end;
  357. _Operator := Key[1];
  358. GetDisplay(Operand);
  359. end;
  360. 'C':
  361. Clear;
  362. else CalcKey:=false;
  363. end;
  364. {$ifdef HasSignal}
  365. {$ifdef unix}{$ifdef ver1_0}Signal{$else}fpSignal{$endif}{$else}Signal{$endif}(SIGFPE,StoreSigFPE);
  366. {$endif HasSignal}
  367. DrawView;
  368. {$ifdef HasSignal}
  369. end
  370. else { LongJmp called }
  371. begin
  372. ErrorBox('Error while computing '+Key,nil);
  373. CalcKey:=true;
  374. {$endif HasSignal}
  375. end;
  376. end;
  377. procedure TCalcDisplay.Clear;
  378. begin
  379. Status := csFirst;
  380. Number := '0';
  381. Sign := ' ';
  382. _Operator := '=';
  383. end;
  384. procedure TCalcDisplay.Draw;
  385. var
  386. Color: Byte;
  387. I: Integer;
  388. B: TDrawBuffer;
  389. begin
  390. Color := GetColor(1);
  391. I := Size.X - Length(Number) - 2;
  392. MoveChar(B, ' ', Color, Size.X);
  393. MoveChar(B[I], Sign, Color, 1);
  394. MoveStr(B[I + 1], Number, Color);
  395. WriteBuf(0, 0, Size.X, 1, B);
  396. end;
  397. function TCalcDisplay.GetPalette: PPalette;
  398. const
  399. P: string[1] = #19;
  400. begin
  401. GetPalette := @P;
  402. end;
  403. procedure TCalcDisplay.HandleEvent(var Event: TEvent);
  404. var S: string[3];
  405. begin
  406. inherited HandleEvent(Event);
  407. case Event.What of
  408. evKeyDown:
  409. if Owner<>nil then
  410. if (Owner^.State and sfSelected)<>0 then
  411. begin
  412. S:=Event.CharCode;
  413. Message(Owner,evBroadcast,cmPressButton,@S);
  414. if CalcKey(Event.CharCode) then
  415. ClearEvent(Event);
  416. end;
  417. evBroadcast:
  418. if Event.Command = cmCalcButton then
  419. begin
  420. CalcKey(PButton(Event.InfoPtr)^.Title^);
  421. ClearEvent(Event);
  422. end;
  423. end;
  424. end;
  425. procedure TCalcDisplay.Store(var S: TStream);
  426. begin
  427. TView.Store(S);
  428. S.Write(Status, SizeOf(Status) + SizeOf(Number) + SizeOf(Sign) +
  429. SizeOf(_Operator) + SizeOf(Operand));
  430. end;
  431. { TCalculator }
  432. constructor TCalculator.Init;
  433. const
  434. Keys: array[0..29] of string[4] =
  435. ('M+', 'x^y','C' ,#27 ,'%' ,#241 ,
  436. 'M-', 'x^2','7' ,'8' ,'9' ,'/' ,
  437. 'M'#26,'1/x','4' ,'5' ,'6' ,'*' ,
  438. 'M'#27,'sqrt','1' ,'2' ,'3' ,'-' ,
  439. 'M'#29,'log','0' ,'.' ,'=' ,'+' );
  440. var
  441. I: Integer;
  442. P: PView;
  443. R: TRect;
  444. begin
  445. R.Assign(5, 3, 43, 18);
  446. inherited Init(R, dialog_Calculator);
  447. Options := Options or ofFirstClick or ofTopSelect;
  448. HelpCtx:=hcCalcWindow;
  449. for I := 0 to 29 do
  450. begin
  451. R.A.X := (I mod 6) * 5 + 2;
  452. R.A.Y := (I div 6) * 2 + 4;
  453. R.B.X := R.A.X + 5;
  454. R.B.Y := R.A.Y + 2;
  455. if (I mod 6)=0 then Inc(R.B.X,1) else
  456. if (I mod 6)=1 then begin R.Move(1,0); Inc(R.B.X,2) end else
  457. R.Move(3,0);
  458. P := New(PCalcButton, Init(R, Keys[I], cmCalcButton,
  459. bfNormal + bfBroadcast+bfGrabFocus));
  460. P^.Options := P^.Options {and not ofSelectable};
  461. Insert(P);
  462. end;
  463. R.Assign(3, 2, 35, 3);
  464. New(CD, Init(R));
  465. CD^.Options:=CD^.Options or ofSelectable;
  466. Insert(CD);
  467. end;
  468. procedure TCalculator.HandleEvent(var Event: TEvent);
  469. var R: extended;
  470. { Re: real;}
  471. begin
  472. if (State and sfSelected)<>0 then
  473. case Event.What of
  474. evCommand :
  475. case Event.Command of
  476. cmCalculatorPaste :
  477. Message(@Self,evKeyDown,kbCtrlEnter,nil);
  478. end;
  479. evKeyDown :
  480. case Event.KeyCode of
  481. kbEnter :
  482. begin
  483. Event.KeyCode:=0;
  484. Event.CharCode:='=';
  485. end;
  486. kbCtrlEnter :
  487. begin
  488. ClearEvent(Event);
  489. CD^.GetDisplay(R); {Re:=R;}
  490. Close;
  491. CalcClipboard:=R;
  492. Message(Application,evBroadcast,cmCalculatorPaste,nil);
  493. end;
  494. kbEsc :
  495. begin
  496. CD^.GetDisplay(R);
  497. if R<>0 then begin
  498. CD^.SetDisplay(0,false);
  499. CD^.DrawView;
  500. end
  501. else Close;
  502. ClearEvent(Event);
  503. end;
  504. end;
  505. end;
  506. { lets CD try to handle this }
  507. if Event.What=evKeyDown then
  508. Message(CD,Event.What,Event.KeyCode,Event.InfoPtr);
  509. inherited HandleEvent(Event);
  510. end;
  511. procedure TCalculator.Show;
  512. begin
  513. { if GetState(sfVisible)=false then CD^.Clear;}
  514. inherited Show;
  515. end;
  516. procedure TCalculator.Close;
  517. begin
  518. Hide;
  519. end;
  520. constructor TCalculator.Load(var S: TStream);
  521. begin
  522. inherited Load(S);
  523. GetSubViewPtr(S,CD);
  524. end;
  525. procedure TCalculator.Store(var S: TStream);
  526. begin
  527. inherited Store(S);
  528. PutSubViewPtr(S,CD);
  529. end;
  530. procedure RegisterFPCalc;
  531. begin
  532. {$ifndef NOOBJREG}
  533. RegisterType(RCalcButton);
  534. RegisterType(RCalcDisplay);
  535. RegisterType(RCalculator);
  536. {$endif}
  537. end;
  538. end.