fpcalc.pas 14 KB

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