2
0

fpcalc.pas 14 KB

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