fpcalc.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  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. constructor Init(var Bounds: TRect);
  43. constructor Load(var S: TStream);
  44. function CalcKey(Key: string): boolean;
  45. procedure Clear;
  46. procedure Draw; virtual;
  47. function GetPalette: PPalette; virtual;
  48. procedure HandleEvent(var Event: TEvent); virtual;
  49. procedure Store(var S: TStream);
  50. private
  51. procedure GetDisplay(var R: extended);
  52. procedure SetDisplay(R: extended;ShouldKeepZeroes : boolean);
  53. procedure Error;
  54. end;
  55. PCalculator = ^TCalculator;
  56. TCalculator = object(TCenterDialog)
  57. CD : PCalcDisplay;
  58. constructor Init;
  59. procedure HandleEvent(var Event: TEvent); virtual;
  60. procedure Show; {virtual;}
  61. procedure Close; virtual;
  62. constructor Load(var S: TStream);
  63. procedure Store(var S: TStream);
  64. end;
  65. {$ifndef NOOBJREG}
  66. const
  67. RCalcButton: TStreamRec = (
  68. ObjType: 10139;
  69. VmtLink: Ofs(TypeOf(TCalcButton)^);
  70. Load: @TCalcButton.Load;
  71. Store: @TCalcButton.Store
  72. );
  73. RCalcDisplay: TStreamRec = (
  74. ObjType: 10140;
  75. VmtLink: Ofs(TypeOf(TCalcDisplay)^);
  76. Load: @TCalcDisplay.Load;
  77. Store: @TCalcDisplay.Store
  78. );
  79. RCalculator: TStreamRec = (
  80. ObjType: 10141;
  81. VmtLink: Ofs(TypeOf(TCalculator)^);
  82. Load: @TCalculator.Load;
  83. Store: @TCalculator.Store
  84. );
  85. {$endif}
  86. procedure RegisterFPCalc;
  87. implementation
  88. uses
  89. {$ifdef Unix}
  90. {$ifdef VER1_0}
  91. linux,
  92. {$else}
  93. unix,
  94. {$endif}
  95. {$endif}
  96. {$ifdef go32v2}
  97. dpmiexcp,
  98. {$endif}
  99. {$ifdef win32}
  100. signals,
  101. {$endif}
  102. FPString,FPUtils,FPConst,WUtils;
  103. const
  104. cmCalcButton = 100;
  105. cmPressButton = 101;
  106. procedure TCalcButton.HandleEvent(var Event: TEvent);
  107. var
  108. Call : boolean;
  109. i : Sw_Word;
  110. begin
  111. Call:=true;
  112. case Event.What of
  113. evKeyDown :
  114. case Event.KeyCode of
  115. kbEnter : Call:=false;
  116. end;
  117. evBroadcast :
  118. case Event.Command of
  119. cmDefault : Call:=false;
  120. cmPressButton :
  121. begin
  122. if (PString(Event.InfoPtr)^=Title^) or
  123. ((PString(Event.InfoPtr)^='^') and (Title^='x^y')) then
  124. begin
  125. Select;
  126. DrawState(true);
  127. i:=GetDosTicks+2;
  128. repeat
  129. until GetDosTicks>i;
  130. DrawState(false);
  131. ClearEvent(Event);
  132. end;
  133. end;
  134. end;
  135. end;
  136. if Call then
  137. inherited HandleEvent(Event);
  138. end;
  139. constructor TCalcDisplay.Init(var Bounds: TRect);
  140. begin
  141. inherited Init(Bounds);
  142. Options := Options or ofSelectable;
  143. EventMask := evKeyDown + evBroadcast;
  144. Clear;
  145. HelpCtx:={hcCalculatorLine}0;
  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. 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. {$ifdef Unix}
  204. Procedure CalcSigFPE(sig : longint);cdecl;
  205. {$else}
  206. function CalcSigFPE(sig : longint) : longint;
  207. {$endif}
  208. begin
  209. ErrorBox('Error while computing math expression',nil);
  210. {$ifdef go32v2}
  211. Dpmi_LongJmp(CalcSigJmp,1);
  212. {$else : not go32v2}
  213. LongJmp(CalcSigJmp,1);
  214. {$endif go32v2}
  215. {$ifndef Unix}
  216. { Just here to avoid compiler warnings PM }
  217. CalcSigFPE:=0;
  218. {$endif}
  219. end;
  220. {$endif HasSignal}
  221. function TCalcDisplay.CalcKey(Key: string): boolean;
  222. var
  223. R,D: extended;
  224. procedure CheckFirst;
  225. begin
  226. if Status = csFirst then
  227. begin
  228. Status := csValid;
  229. SetDisplay(0,false);
  230. end;
  231. end;
  232. {$ifdef HasSignal}
  233. var
  234. StoreSigFPE : SignalHandler;
  235. {$endif HasSignal}
  236. begin
  237. CalcKey:=true;
  238. Key := UpCaseStr(Key);
  239. {$ifdef HasSignal}
  240. {$ifdef go32v2}
  241. if Dpmi_SetJmp(CalcSigJmp)=0 then
  242. {$else : not go32v2}
  243. if SetJmp(CalcSigJmp)=0 then
  244. {$endif go32v2}
  245. {$endif HasSignal}
  246. begin
  247. {$ifdef HasSignal}
  248. StoreSigFPE:=Signal(SIGFPE,@CalcSigFPE);
  249. {$endif HasSignal}
  250. if (Status = csError) and (Key <> 'C') then Key := ' ';
  251. if Key='X^Y' then Key:='^';
  252. if length(Key)>1 then
  253. begin
  254. { if Status = csFirst then}
  255. begin
  256. { Status := csValid;}
  257. GetDisplay(R);
  258. if Key='1/X' then begin if R=0 then Error else SetDisplay(1/R,false) end else
  259. if Key='SQR' then begin if R<0 then Error else SetDisplay(sqrt(R),false) end else
  260. if Key='LOG' then begin if R<=0 then Error else SetDisplay(ln(R),false) end else
  261. if Key='X^2' then SetDisplay(R*R,false) else
  262. if Key='M+' then Memory:=Memory+R else
  263. if Key='M-' then Memory:=Memory-R else
  264. if Key='M'#26 then SetDisplay(Memory,false) else
  265. if Key='M'#27 then Memory:=R else
  266. if Key='M'#29 then begin D:=Memory; Memory:=R; SetDisplay(D,false); end;
  267. end;
  268. end
  269. else
  270. case Key[1] of
  271. '0'..'9':
  272. if Length(Number)<MaxDigits then
  273. begin
  274. CheckFirst;
  275. if Number = '0' then Number := '';
  276. Number := Number + Key;
  277. SetDisplay(StrToExtended(Number),true);
  278. end;
  279. '.':
  280. begin
  281. CheckFirst;
  282. if Pos('.', Number) = 0 then Number := Number + '.';
  283. end;
  284. #8, #27:
  285. begin
  286. CheckFirst;
  287. if Length(Number) = 1 then Number := '0' else Dec(Number[0]);
  288. SetDisplay(StrToExtended(Number),true); { !!! }
  289. end;
  290. '_', #241:
  291. if Sign = ' ' then Sign := '-' else Sign := ' ';
  292. '+', '-', '*', '/', '=', '%', #13, '^':
  293. begin
  294. if Status = csValid then
  295. begin
  296. Status := csFirst;
  297. GetDisplay(R);
  298. if Key = '%' then
  299. case _Operator of
  300. '+', '-': R := Operand * R / 100;
  301. '*', '/': R := R / 100;
  302. end;
  303. case _Operator of
  304. '^': SetDisplay(Power(Operand,R),false);
  305. '+': SetDisplay(Operand + R,false);
  306. '-': SetDisplay(Operand - R,false);
  307. '*': SetDisplay(Operand * R,false);
  308. '/': if R = 0 then Error else SetDisplay(Operand / R,false);
  309. end;
  310. end;
  311. _Operator := Key[1];
  312. GetDisplay(Operand);
  313. end;
  314. 'C':
  315. Clear;
  316. else CalcKey:=false;
  317. end;
  318. {$ifdef HasSignal}
  319. Signal(SIGFPE,StoreSigFPE);
  320. {$endif HasSignal}
  321. DrawView;
  322. {$ifdef HasSignal}
  323. end
  324. else { LongJmp called }
  325. begin
  326. ErrorBox('Error while computing '+Key,nil);
  327. CalcKey:=true;
  328. {$endif HasSignal}
  329. end;
  330. end;
  331. procedure TCalcDisplay.Clear;
  332. begin
  333. Status := csFirst;
  334. Number := '0';
  335. Sign := ' ';
  336. _Operator := '=';
  337. end;
  338. procedure TCalcDisplay.Draw;
  339. var
  340. Color: Byte;
  341. I: Integer;
  342. B: TDrawBuffer;
  343. begin
  344. Color := GetColor(1);
  345. I := Size.X - Length(Number) - 2;
  346. MoveChar(B, ' ', Color, Size.X);
  347. MoveChar(B[I], Sign, Color, 1);
  348. MoveStr(B[I + 1], Number, Color);
  349. WriteBuf(0, 0, Size.X, 1, B);
  350. end;
  351. function TCalcDisplay.GetPalette: PPalette;
  352. const
  353. P: string[1] = #19;
  354. begin
  355. GetPalette := @P;
  356. end;
  357. procedure TCalcDisplay.HandleEvent(var Event: TEvent);
  358. var S: string[3];
  359. begin
  360. inherited HandleEvent(Event);
  361. case Event.What of
  362. evKeyDown:
  363. if Owner<>nil then
  364. if (Owner^.State and sfSelected)<>0 then
  365. begin
  366. S:=Event.CharCode;
  367. Message(Owner,evBroadcast,cmPressButton,@S);
  368. if CalcKey(Event.CharCode) then
  369. ClearEvent(Event);
  370. end;
  371. evBroadcast:
  372. if Event.Command = cmCalcButton then
  373. begin
  374. CalcKey(PButton(Event.InfoPtr)^.Title^);
  375. ClearEvent(Event);
  376. end;
  377. end;
  378. end;
  379. procedure TCalcDisplay.Store(var S: TStream);
  380. begin
  381. TView.Store(S);
  382. S.Write(Status, SizeOf(Status) + SizeOf(Number) + SizeOf(Sign) +
  383. SizeOf(_Operator) + SizeOf(Operand));
  384. end;
  385. { TCalculator }
  386. constructor TCalculator.Init;
  387. const
  388. Keys: array[0..29] of string[3] =
  389. ('M+', 'x^y','C' ,#27 ,'%' ,#241 ,
  390. 'M-', 'x^2','7' ,'8' ,'9' ,'/' ,
  391. 'M'#26,'1/x','4' ,'5' ,'6' ,'*' ,
  392. 'M'#27,'sqr','1' ,'2' ,'3' ,'-' ,
  393. 'M'#29,'log','0' ,'.' ,'=' ,'+' );
  394. var
  395. I: Integer;
  396. P: PView;
  397. R: TRect;
  398. begin
  399. R.Assign(5, 3, 43, 18);
  400. inherited Init(R, dialog_Calculator);
  401. Options := Options or ofFirstClick or ofTopSelect;
  402. HelpCtx:=hcCalcWindow;
  403. for I := 0 to 29 do
  404. begin
  405. R.A.X := (I mod 6) * 5 + 2;
  406. R.A.Y := (I div 6) * 2 + 4;
  407. R.B.X := R.A.X + 5;
  408. R.B.Y := R.A.Y + 2;
  409. if (I mod 6)=0 then Inc(R.B.X,1) else
  410. if (I mod 6)=1 then begin R.Move(1,0); Inc(R.B.X,2) end else
  411. R.Move(3,0);
  412. P := New(PCalcButton, Init(R, Keys[I], cmCalcButton,
  413. bfNormal + bfBroadcast+bfGrabFocus));
  414. P^.Options := P^.Options {and not ofSelectable};
  415. Insert(P);
  416. end;
  417. R.Assign(3, 2, 35, 3);
  418. New(CD, Init(R));
  419. CD^.Options:=CD^.Options or ofSelectable;
  420. Insert(CD);
  421. end;
  422. procedure TCalculator.HandleEvent(var Event: TEvent);
  423. var R: extended;
  424. { Re: real;}
  425. begin
  426. if (State and sfSelected)<>0 then
  427. case Event.What of
  428. evCommand :
  429. case Event.Command of
  430. cmCalculatorPaste :
  431. Message(@Self,evKeyDown,kbCtrlEnter,nil);
  432. end;
  433. evKeyDown :
  434. case Event.KeyCode of
  435. kbEnter :
  436. begin
  437. Event.KeyCode:=0;
  438. Event.CharCode:='=';
  439. end;
  440. kbCtrlEnter :
  441. begin
  442. ClearEvent(Event);
  443. CD^.GetDisplay(R); {Re:=R;}
  444. Close;
  445. CalcClipboard:=R;
  446. Message(Application,evBroadcast,cmCalculatorPaste,nil);
  447. end;
  448. kbEsc :
  449. begin
  450. CD^.GetDisplay(R);
  451. if R<>0 then begin
  452. CD^.SetDisplay(0,false);
  453. CD^.DrawView;
  454. end
  455. else Close;
  456. ClearEvent(Event);
  457. end;
  458. end;
  459. end;
  460. { lets CD try to handle this }
  461. if Event.What=evKeyDown then
  462. Message(CD,Event.What,Event.KeyCode,Event.InfoPtr);
  463. inherited HandleEvent(Event);
  464. end;
  465. procedure TCalculator.Show;
  466. begin
  467. { if GetState(sfVisible)=false then CD^.Clear;}
  468. inherited Show;
  469. end;
  470. procedure TCalculator.Close;
  471. begin
  472. Hide;
  473. end;
  474. constructor TCalculator.Load(var S: TStream);
  475. begin
  476. inherited Load(S);
  477. GetSubViewPtr(S,CD);
  478. end;
  479. procedure TCalculator.Store(var S: TStream);
  480. begin
  481. inherited Store(S);
  482. PutSubViewPtr(S,CD);
  483. end;
  484. procedure RegisterFPCalc;
  485. begin
  486. {$ifndef NOOBJREG}
  487. RegisterType(RCalcButton);
  488. RegisterType(RCalcDisplay);
  489. RegisterType(RCalculator);
  490. {$endif}
  491. end;
  492. end.
  493. {
  494. $Log$
  495. Revision 1.3 2001-11-14 23:55:38 pierre
  496. * fix bug 1680 for go32v2 and hopefully for linux
  497. Revision 1.2 2001/08/05 02:01:47 peter
  498. * FVISION define to compile with fvision units
  499. Revision 1.1 2001/08/04 11:30:22 peter
  500. * ide works now with both compiler versions
  501. Revision 1.1.2.1 2000/11/13 16:59:08 pierre
  502. * some function in double removed from fputils unit
  503. Revision 1.1 2000/07/13 09:48:34 michael
  504. + Initial import
  505. Revision 1.10 2000/05/02 08:42:26 pierre
  506. * new set of Gabor changes: see fixes.txt
  507. Revision 1.9 2000/04/18 11:42:36 pierre
  508. lot of Gabor changes : see fixes.txt
  509. Revision 1.8 2000/03/21 23:34:10 pierre
  510. adapted to wcedit addition by Gabor
  511. Revision 1.7 1999/09/13 16:24:42 peter
  512. + clock
  513. * backspace unident like tp7
  514. Revision 1.6 1999/09/07 09:20:52 pierre
  515. * traling zero after . could not be inserted
  516. * load/store was missing => CD not set on loading.
  517. * log function was not implemented : ln is used,
  518. should it rather be decimal logarithm ?
  519. Revision 1.5 1999/06/28 19:25:35 peter
  520. * fixes from gabor
  521. Revision 1.4 1999/04/07 21:55:41 peter
  522. + object support for browser
  523. * html help fixes
  524. * more desktop saving things
  525. * NODEBUG directive to exclude debugger
  526. Revision 1.3 1999/03/01 15:41:49 peter
  527. + Added dummy entries for functions not yet implemented
  528. * MenuBar didn't update itself automatically on command-set changes
  529. * Fixed Debugging/Profiling options dialog
  530. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  531. set
  532. * efBackSpaceUnindents works correctly
  533. + 'Messages' window implemented
  534. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  535. + Added TP message-filter support (for ex. you can call GREP thru
  536. GREP2MSG and view the result in the messages window - just like in TP)
  537. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  538. so topic search didn't work...
  539. * In FPHELP.PAS there were still context-variables defined as word instead
  540. of THelpCtx
  541. * StdStatusKeys() was missing from the statusdef for help windows
  542. + Topic-title for index-table can be specified when adding a HTML-files
  543. Revision 1.1 1998/12/22 14:27:54 peter
  544. * moved
  545. Revision 1.2 1998/12/22 10:39:39 peter
  546. + options are now written/read
  547. + find and replace routines
  548. }