fpcalc.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  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. {$ifdef FVISION}
  18. FVConsts,
  19. {$else}
  20. Commands,
  21. {$endif}
  22. WViews,
  23. FPViews;
  24. const
  25. MaxDecimals = 10;
  26. MaxDigits = 30;
  27. type
  28. TCalcState = (csFirst, csValid, csError);
  29. PCalcButton = ^TCalcButton;
  30. TCalcButton = object(TButton)
  31. procedure HandleEvent(var Event: TEvent); virtual;
  32. end;
  33. PCalcDisplay = ^TCalcDisplay;
  34. TCalcDisplay = object(TView)
  35. Status: TCalcState;
  36. Number: string[MaxDigits];
  37. Sign: Char;
  38. _Operator: Char;
  39. Operand: extended;
  40. Memory: extended;
  41. DispNumber: extended;
  42. HexShown : boolean;
  43. constructor Init(var Bounds: TRect);
  44. constructor Load(var S: TStream);
  45. function CalcKey(Key: string): boolean;
  46. procedure Clear;
  47. procedure Draw; virtual;
  48. function GetPalette: PPalette; virtual;
  49. procedure HandleEvent(var Event: TEvent); virtual;
  50. procedure Store(var S: TStream);
  51. private
  52. procedure GetDisplay(var R: extended);
  53. procedure SetDisplay(R: extended;ShouldKeepZeroes : boolean);
  54. procedure Error;
  55. end;
  56. PCalculator = ^TCalculator;
  57. TCalculator = object(TCenterDialog)
  58. CD : PCalcDisplay;
  59. constructor Init;
  60. procedure HandleEvent(var Event: TEvent); virtual;
  61. procedure Show; {virtual;}
  62. procedure Close; virtual;
  63. constructor Load(var S: TStream);
  64. procedure Store(var S: TStream);
  65. end;
  66. {$ifndef NOOBJREG}
  67. const
  68. RCalcButton: TStreamRec = (
  69. ObjType: 10139;
  70. VmtLink: Ofs(TypeOf(TCalcButton)^);
  71. Load: @TCalcButton.Load;
  72. Store: @TCalcButton.Store
  73. );
  74. RCalcDisplay: TStreamRec = (
  75. ObjType: 10140;
  76. VmtLink: Ofs(TypeOf(TCalcDisplay)^);
  77. Load: @TCalcDisplay.Load;
  78. Store: @TCalcDisplay.Store
  79. );
  80. RCalculator: TStreamRec = (
  81. ObjType: 10141;
  82. VmtLink: Ofs(TypeOf(TCalculator)^);
  83. Load: @TCalculator.Load;
  84. Store: @TCalculator.Store
  85. );
  86. {$endif}
  87. procedure RegisterFPCalc;
  88. implementation
  89. uses
  90. {$ifdef Unix}
  91. {$ifdef VER1_0}
  92. linux,
  93. {$else}
  94. unix,
  95. {$endif}
  96. {$endif}
  97. {$ifdef go32v2}
  98. dpmiexcp,
  99. {$endif}
  100. {$ifdef win32}
  101. signals,
  102. {$endif}
  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;
  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:=Signal(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='SQR' 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(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 Status = csValid then
  332. begin
  333. Status := csFirst;
  334. GetDisplay(R);
  335. if Key = '%' then
  336. case _Operator of
  337. '+', '-': R := Operand * R / 100;
  338. '*', '/': R := R / 100;
  339. end;
  340. case _Operator of
  341. '^': SetDisplay(Power(Operand,R),false);
  342. '+': SetDisplay(Operand + R,false);
  343. '-': SetDisplay(Operand - R,false);
  344. '*': SetDisplay(Operand * R,false);
  345. '/': if R = 0 then Error else SetDisplay(Operand / R,false);
  346. end;
  347. end;
  348. _Operator := Key[1];
  349. GetDisplay(Operand);
  350. end;
  351. 'C':
  352. Clear;
  353. else CalcKey:=false;
  354. end;
  355. {$ifdef HasSignal}
  356. Signal(SIGFPE,StoreSigFPE);
  357. {$endif HasSignal}
  358. DrawView;
  359. {$ifdef HasSignal}
  360. end
  361. else { LongJmp called }
  362. begin
  363. ErrorBox('Error while computing '+Key,nil);
  364. CalcKey:=true;
  365. {$endif HasSignal}
  366. end;
  367. end;
  368. procedure TCalcDisplay.Clear;
  369. begin
  370. Status := csFirst;
  371. Number := '0';
  372. Sign := ' ';
  373. _Operator := '=';
  374. end;
  375. procedure TCalcDisplay.Draw;
  376. var
  377. Color: Byte;
  378. I: Integer;
  379. B: TDrawBuffer;
  380. begin
  381. Color := GetColor(1);
  382. I := Size.X - Length(Number) - 2;
  383. MoveChar(B, ' ', Color, Size.X);
  384. MoveChar(B[I], Sign, Color, 1);
  385. MoveStr(B[I + 1], Number, Color);
  386. WriteBuf(0, 0, Size.X, 1, B);
  387. end;
  388. function TCalcDisplay.GetPalette: PPalette;
  389. const
  390. P: string[1] = #19;
  391. begin
  392. GetPalette := @P;
  393. end;
  394. procedure TCalcDisplay.HandleEvent(var Event: TEvent);
  395. var S: string[3];
  396. begin
  397. inherited HandleEvent(Event);
  398. case Event.What of
  399. evKeyDown:
  400. if Owner<>nil then
  401. if (Owner^.State and sfSelected)<>0 then
  402. begin
  403. S:=Event.CharCode;
  404. Message(Owner,evBroadcast,cmPressButton,@S);
  405. if CalcKey(Event.CharCode) then
  406. ClearEvent(Event);
  407. end;
  408. evBroadcast:
  409. if Event.Command = cmCalcButton then
  410. begin
  411. CalcKey(PButton(Event.InfoPtr)^.Title^);
  412. ClearEvent(Event);
  413. end;
  414. end;
  415. end;
  416. procedure TCalcDisplay.Store(var S: TStream);
  417. begin
  418. TView.Store(S);
  419. S.Write(Status, SizeOf(Status) + SizeOf(Number) + SizeOf(Sign) +
  420. SizeOf(_Operator) + SizeOf(Operand));
  421. end;
  422. { TCalculator }
  423. constructor TCalculator.Init;
  424. const
  425. Keys: array[0..29] of string[3] =
  426. ('M+', 'x^y','C' ,#27 ,'%' ,#241 ,
  427. 'M-', 'x^2','7' ,'8' ,'9' ,'/' ,
  428. 'M'#26,'1/x','4' ,'5' ,'6' ,'*' ,
  429. 'M'#27,'sqr','1' ,'2' ,'3' ,'-' ,
  430. 'M'#29,'log','0' ,'.' ,'=' ,'+' );
  431. var
  432. I: Integer;
  433. P: PView;
  434. R: TRect;
  435. begin
  436. R.Assign(5, 3, 43, 18);
  437. inherited Init(R, dialog_Calculator);
  438. Options := Options or ofFirstClick or ofTopSelect;
  439. HelpCtx:=hcCalcWindow;
  440. for I := 0 to 29 do
  441. begin
  442. R.A.X := (I mod 6) * 5 + 2;
  443. R.A.Y := (I div 6) * 2 + 4;
  444. R.B.X := R.A.X + 5;
  445. R.B.Y := R.A.Y + 2;
  446. if (I mod 6)=0 then Inc(R.B.X,1) else
  447. if (I mod 6)=1 then begin R.Move(1,0); Inc(R.B.X,2) end else
  448. R.Move(3,0);
  449. P := New(PCalcButton, Init(R, Keys[I], cmCalcButton,
  450. bfNormal + bfBroadcast+bfGrabFocus));
  451. P^.Options := P^.Options {and not ofSelectable};
  452. Insert(P);
  453. end;
  454. R.Assign(3, 2, 35, 3);
  455. New(CD, Init(R));
  456. CD^.Options:=CD^.Options or ofSelectable;
  457. Insert(CD);
  458. end;
  459. procedure TCalculator.HandleEvent(var Event: TEvent);
  460. var R: extended;
  461. { Re: real;}
  462. begin
  463. if (State and sfSelected)<>0 then
  464. case Event.What of
  465. evCommand :
  466. case Event.Command of
  467. cmCalculatorPaste :
  468. Message(@Self,evKeyDown,kbCtrlEnter,nil);
  469. end;
  470. evKeyDown :
  471. case Event.KeyCode of
  472. kbEnter :
  473. begin
  474. Event.KeyCode:=0;
  475. Event.CharCode:='=';
  476. end;
  477. kbCtrlEnter :
  478. begin
  479. ClearEvent(Event);
  480. CD^.GetDisplay(R); {Re:=R;}
  481. Close;
  482. CalcClipboard:=R;
  483. Message(Application,evBroadcast,cmCalculatorPaste,nil);
  484. end;
  485. kbEsc :
  486. begin
  487. CD^.GetDisplay(R);
  488. if R<>0 then begin
  489. CD^.SetDisplay(0,false);
  490. CD^.DrawView;
  491. end
  492. else Close;
  493. ClearEvent(Event);
  494. end;
  495. end;
  496. end;
  497. { lets CD try to handle this }
  498. if Event.What=evKeyDown then
  499. Message(CD,Event.What,Event.KeyCode,Event.InfoPtr);
  500. inherited HandleEvent(Event);
  501. end;
  502. procedure TCalculator.Show;
  503. begin
  504. { if GetState(sfVisible)=false then CD^.Clear;}
  505. inherited Show;
  506. end;
  507. procedure TCalculator.Close;
  508. begin
  509. Hide;
  510. end;
  511. constructor TCalculator.Load(var S: TStream);
  512. begin
  513. inherited Load(S);
  514. GetSubViewPtr(S,CD);
  515. end;
  516. procedure TCalculator.Store(var S: TStream);
  517. begin
  518. inherited Store(S);
  519. PutSubViewPtr(S,CD);
  520. end;
  521. procedure RegisterFPCalc;
  522. begin
  523. {$ifndef NOOBJREG}
  524. RegisterType(RCalcButton);
  525. RegisterType(RCalcDisplay);
  526. RegisterType(RCalculator);
  527. {$endif}
  528. end;
  529. end.
  530. {
  531. $Log$
  532. Revision 1.5 2002-01-22 14:56:37 pierre
  533. * fix wrong sign change handling
  534. + add 'H' to view current value as hexadecimal
  535. Revision 1.4 2002/01/22 13:56:04 pierre
  536. * fix multiple FPU excpetion trapping problem for unix
  537. Revision 1.3 2001/11/14 23:55:38 pierre
  538. * fix bug 1680 for go32v2 and hopefully for linux
  539. Revision 1.2 2001/08/05 02:01:47 peter
  540. * FVISION define to compile with fvision units
  541. Revision 1.1 2001/08/04 11:30:22 peter
  542. * ide works now with both compiler versions
  543. Revision 1.1.2.1 2000/11/13 16:59:08 pierre
  544. * some function in double removed from fputils unit
  545. Revision 1.1 2000/07/13 09:48:34 michael
  546. + Initial import
  547. Revision 1.10 2000/05/02 08:42:26 pierre
  548. * new set of Gabor changes: see fixes.txt
  549. Revision 1.9 2000/04/18 11:42:36 pierre
  550. lot of Gabor changes : see fixes.txt
  551. Revision 1.8 2000/03/21 23:34:10 pierre
  552. adapted to wcedit addition by Gabor
  553. Revision 1.7 1999/09/13 16:24:42 peter
  554. + clock
  555. * backspace unident like tp7
  556. Revision 1.6 1999/09/07 09:20:52 pierre
  557. * traling zero after . could not be inserted
  558. * load/store was missing => CD not set on loading.
  559. * log function was not implemented : ln is used,
  560. should it rather be decimal logarithm ?
  561. Revision 1.5 1999/06/28 19:25:35 peter
  562. * fixes from gabor
  563. Revision 1.4 1999/04/07 21:55:41 peter
  564. + object support for browser
  565. * html help fixes
  566. * more desktop saving things
  567. * NODEBUG directive to exclude debugger
  568. Revision 1.3 1999/03/01 15:41:49 peter
  569. + Added dummy entries for functions not yet implemented
  570. * MenuBar didn't update itself automatically on command-set changes
  571. * Fixed Debugging/Profiling options dialog
  572. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  573. set
  574. * efBackSpaceUnindents works correctly
  575. + 'Messages' window implemented
  576. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  577. + Added TP message-filter support (for ex. you can call GREP thru
  578. GREP2MSG and view the result in the messages window - just like in TP)
  579. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  580. so topic search didn't work...
  581. * In FPHELP.PAS there were still context-variables defined as word instead
  582. of THelpCtx
  583. * StdStatusKeys() was missing from the statusdef for help windows
  584. + Topic-title for index-table can be specified when adding a HTML-files
  585. Revision 1.1 1998/12/22 14:27:54 peter
  586. * moved
  587. Revision 1.2 1998/12/22 10:39:39 peter
  588. + options are now written/read
  589. + find and replace routines
  590. }