WMV24Component.Mod 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529
  1. MODULE WMV24Component; (** AUTHOR "TF/staubesv"; PURPOSE "Terminal"; *)
  2. (**
  3. * History:
  4. * 19.01.2006 Adapted to Serials, added clear button, synchronize operations to V24Panel.open (staubesv)
  5. * 10.06.2006 Added YSend functionality, use XYModem.Mod instead of XModem.Mod, command button
  6. * 14.06.2006 Busy loop removed, made window restorable (staubesv)
  7. * 15.06.2006 Support to paste to terminal window from clipboard, cursor always at the end of the text, keyboard focus indication, added scrollbar (staubesv)
  8. * 16.06.2006 Added SendCommand functionality (staubesv)
  9. * 21.06.2006 Improved error reporting (staubesv)
  10. * 22.06.2006 Added LineFeed and UseBackspace options, added status bar (staubesv)
  11. * 05.09.2006 Relay all keys to serial port, added copy&paste buttons since shortcuts are relayed to text view anymore,
  12. * don't send command if port is not open, configuration can be specified in Configuration.XML (staubesv)
  13. * 25.09.2006 Added XY modem receive & echo capability (staubesv)
  14. * 20.02.2007 Better usability for selection, can send multiple files via XY-Modem protocol (staubesv)
  15. * 25.06.2007 Open progress windows on current view (staubesv)
  16. *
  17. * TODO:
  18. *
  19. * Sometimes, not all data that is received is displayed until the user sends a character to the device (The data is really receive, w.Char and w.Update
  20. * are both called but the content of the writer w are not displayed). FIX!
  21. *)
  22. IMPORT
  23. KernelLog, Objects, Streams, Configuration, Texts, TextUtilities, Strings,
  24. Modules, Kernel, Serials, XYModem, Files, Inputs,
  25. WMWindowManager, WMMessages, WMRestorable, WMGraphics, WMRectangles,
  26. WMComponents, WMStandardComponents, WMProgressComponents, WMTextView, WMEditors, WMPopups, WMDialogs,
  27. XML, XMLObjects, WMSearchComponents, Commands, V24, T := Trace;
  28. CONST
  29. (* Terminal Configuration - The default configuration is overriden by the configuration in Configuration.XML if available *)
  30. (* Default size of window at start up *)
  31. DefaultWidth = 800; DefaultHeight = 400;
  32. (* Default serial port settings *)
  33. DefaultPort = 1;
  34. DefaultBps = 115200;
  35. DefaultDataBits = 8;
  36. DefaultParity = Serials.ParNo;
  37. DefaultStopBits = Serials.Stop1;
  38. (* If TRUE, the terminal panel is grey when it has no keyboard focus *)
  39. DefaultIndicateKeyboardFocus = TRUE;
  40. (* If TRUE, some CTRL-key combinations are intercepted by the terminal window but not sent to the stream *)
  41. DefaultShortcutsEnabled = FALSE;
  42. (* Display status bar with error & port status indication? *)
  43. DefaultShowStatusBar = TRUE;
  44. (* Send <CR><LF> when the user presses <CR>. Also send <CR><LF> instead of <CR> when sending commands *)
  45. DefaultLineFeed = FALSE;
  46. (* The window manager reports a DEL key pressed when pressing backspace. If TRUE, the terminal sends backspaces instead of deletes *)
  47. DefaultUseBackspace = TRUE;
  48. (* Should received characters be sent back? *)
  49. DefaultEcho = FALSE;
  50. (* Is UTF support is disabled, all non-ascii characters are replaced by the character "." *)
  51. DefaultUTF8Support = FALSE;
  52. (* Internal terminal configuration *)
  53. (* How often should the port status be polled? *)
  54. UpdateInterval = 200; (* ms *)
  55. ReceiveBufferSize = 1024;
  56. (* Trace Configuration *)
  57. TraceCharactersSent = {0};
  58. TraceCharactersReceived = {1};
  59. Trace = {};
  60. (* Internal Constants *)
  61. Backspace = 08X;
  62. CR = 0DX;
  63. LF = 0AX;
  64. ESC = 1BX;
  65. DEL = 7FX;
  66. (* Lock *)
  67. Free = 0;
  68. Terminal = 1;
  69. DataTransfer = 2;
  70. ModuleName = "WMV24Component";
  71. TYPE
  72. Settings = OBJECT
  73. VAR
  74. portSettings : ARRAY 64 OF CHAR;
  75. indicateKeyboardFocus : BOOLEAN;
  76. showStatusBar : BOOLEAN;
  77. shortcutsEnabled : BOOLEAN;
  78. linefeed : BOOLEAN;
  79. echo : BOOLEAN;
  80. utf8Support : BOOLEAN;
  81. useBackspace : BOOLEAN;
  82. xReceiveCommand, yReceiveCommand : Strings.String;
  83. xSendCommand, ySendCommand : Strings.String;
  84. (* Load settings from Configuration.XML. For settings that are not available, default settings are used *)
  85. PROCEDURE Load;
  86. VAR
  87. value, temp : ARRAY 256 OF CHAR;
  88. res : LONGINT;
  89. BEGIN
  90. Configuration.Get("Applications.WMV24Component.PortSettings", value, res);
  91. IF (res = Configuration.Ok) THEN COPY(value, portSettings); END;
  92. Configuration.GetBoolean("Applications.WMV24Component.IndicateKeyboardFocus", indicateKeyboardFocus, res);
  93. Configuration.GetBoolean("Applications.WMV24Component.LineFeed", linefeed, res);
  94. Configuration.GetBoolean("Applications.WMV24Component.UseBackspace", useBackspace, res);
  95. Configuration.GetBoolean("Applications.WMV24Component.ShowStatusBar", showStatusBar, res);
  96. Configuration.GetBoolean("Applications.WMV24Component.ShortcutsEnabled", shortcutsEnabled, res);
  97. Configuration.GetBoolean("Applications.WMV24Component.Echo", echo, res);
  98. Configuration.GetBoolean("Applications.WMV24Component.UTF8Support", utf8Support, res);
  99. Configuration.Get("Applications.WMV24Component.XReceiveCommand", value, res);
  100. COPY(value, temp); Strings.TrimWS(temp);
  101. IF (res = Configuration.Ok) & (temp # "") THEN xReceiveCommand := Strings.NewString(value); END;
  102. Configuration.Get("Applications.WMV24Component.YReceiveCommand", value, res);
  103. COPY(value, temp); Strings.TrimWS(temp);
  104. IF (res = Configuration.Ok) & (temp # "") THEN yReceiveCommand := Strings.NewString(value); END;
  105. Configuration.Get("Applications.WMV24Component.XSendCommand", value, res);
  106. COPY(value, temp); Strings.TrimWS(temp);
  107. IF (res = Configuration.Ok) & (temp # "") THEN xSendCommand := Strings.NewString(value); END;
  108. Configuration.Get("Applications.WMV24Component.YSendCommand", value, res);
  109. COPY(value, temp); Strings.TrimWS(temp);
  110. IF (res = Configuration.Ok) & (temp # "") THEN ySendCommand := Strings.NewString(value); END;
  111. END Load;
  112. PROCEDURE GetDefaultPortSettings(VAR portSettings : ARRAY OF CHAR);
  113. VAR w : Streams.StringWriter;
  114. BEGIN
  115. NEW(w, 64);
  116. w.Int(DefaultPort, 0); w.Char(" ");
  117. w.Int(DefaultBps, 0); w.Char(" ");
  118. w.Int(DefaultDataBits, 0); w.Char(" ");
  119. w.Int(DefaultStopBits, 0); w.Char(" ");
  120. CASE DefaultParity OF
  121. |Serials.ParNo: w.String("none");
  122. |Serials.ParOdd: w.String("odd");
  123. |Serials.ParEven: w.String("even");
  124. |Serials.ParMark: w.String("mark");
  125. |Serials.ParSpace: w.String("space");
  126. ELSE
  127. w.String("unknown");
  128. END;
  129. w.Get(portSettings);
  130. END GetDefaultPortSettings;
  131. PROCEDURE &Init*;
  132. BEGIN
  133. GetDefaultPortSettings(portSettings);
  134. indicateKeyboardFocus := DefaultIndicateKeyboardFocus;
  135. linefeed := DefaultLineFeed;
  136. useBackspace := DefaultUseBackspace;
  137. showStatusBar := DefaultShowStatusBar;
  138. shortcutsEnabled := DefaultShortcutsEnabled;
  139. echo := DefaultEcho;
  140. utf8Support := DefaultUTF8Support;
  141. xReceiveCommand := NIL;
  142. yReceiveCommand := NIL;
  143. xSendCommand := NIL;
  144. ySendCommand := NIL;
  145. END Init;
  146. END Settings;
  147. TYPE
  148. (* Recursive lock. This lock is used to provide exclusive access to the currently opened serial port to either
  149. * the Terminal or data transfer operation *)
  150. Lock = OBJECT
  151. VAR
  152. lock : LONGINT;
  153. locklevel : LONGINT;
  154. PROCEDURE TryAcquire(lock : LONGINT) : BOOLEAN;
  155. BEGIN {EXCLUSIVE}
  156. IF (SELF.lock # Free) & (SELF.lock # lock) THEN
  157. RETURN FALSE;
  158. ELSE
  159. TakeLock(lock);
  160. RETURN TRUE;
  161. END;
  162. END TryAcquire;
  163. PROCEDURE Acquire(lock : LONGINT);
  164. BEGIN {EXCLUSIVE}
  165. IF (SELF.lock # Free) & (SELF.lock # lock) THEN
  166. AWAIT(SELF.lock=Free);
  167. END;
  168. TakeLock(lock);
  169. END Acquire;
  170. PROCEDURE Release;
  171. BEGIN {EXCLUSIVE}
  172. ASSERT(locklevel > 0);
  173. DEC(locklevel);
  174. IF locklevel = 0 THEN lock := Free; END;
  175. END Release;
  176. PROCEDURE TakeLock(lock : LONGINT);
  177. BEGIN (* only call from critical sections !*)
  178. IF SELF.lock = lock THEN
  179. INC(locklevel);
  180. ELSE
  181. SELF.lock := lock; locklevel := 1;
  182. END;
  183. END TakeLock;
  184. PROCEDURE &Init*;
  185. BEGIN
  186. lock := Free; locklevel := 0;
  187. END Init;
  188. END Lock;
  189. TYPE
  190. Command = POINTER TO RECORD
  191. name : ARRAY 64 OF CHAR;
  192. commandString : ARRAY 256 OF CHAR;
  193. next : Command;
  194. END;
  195. TYPE
  196. ProgressInfo = OBJECT(WMComponents.VisualComponent)
  197. VAR
  198. progressBar : WMProgressComponents.ProgressBar;
  199. filenameLabel : WMStandardComponents.Label;
  200. progressLabel : WMStandardComponents.Label;
  201. currentBytes, maxBytes : LONGINT;
  202. w : Streams.StringWriter;
  203. string : ARRAY 128 OF CHAR;
  204. PROCEDURE SetProgress(progress : LONGINT);
  205. BEGIN
  206. w.Reset;
  207. w.String("Received "); w.Int(progress, 0);
  208. IF maxBytes > 0 THEN
  209. w.String(" of "); w.Int(maxBytes, 0); w.String(" Bytes");
  210. progressBar.SetCurrent(progress);
  211. ELSE
  212. w.String(" Bytes");
  213. END;
  214. w.Get(string);
  215. progressLabel.caption.SetAOC(string);
  216. END SetProgress;
  217. PROCEDURE &New*(CONST filename : ARRAY OF CHAR; length : LONGINT);
  218. VAR panel : WMStandardComponents.Panel;
  219. BEGIN
  220. Init;
  221. NEW(w, 128);
  222. currentBytes := 0; maxBytes := length;
  223. NEW(panel); panel.fillColor.Set(0FFFFFFFFH); panel.bounds.SetExtents(300, 60); panel.alignment.Set(WMComponents.AlignClient);
  224. AddContent(panel);
  225. NEW(filenameLabel); filenameLabel.bounds.SetHeight(20); filenameLabel.alignment.Set(WMComponents.AlignTop);
  226. filenameLabel.caption.SetAOC(filename);
  227. panel.AddContent(filenameLabel);
  228. NEW(progressLabel); progressLabel.bounds.SetHeight(20); progressLabel.alignment.Set(WMComponents.AlignTop);
  229. panel.AddContent(progressLabel);
  230. IF maxBytes > 0 THEN
  231. NEW(progressBar); progressBar.bounds.SetHeight(20); progressBar.alignment.Set(WMComponents.AlignTop);
  232. progressBar.SetRange(0, maxBytes);
  233. panel.AddContent(progressBar);
  234. END;
  235. SetProgress(0);
  236. SetNameAsString(StrProgressInfo);
  237. END New;
  238. END ProgressInfo;
  239. TYPE
  240. (* The cursor position of this textview cannot be changed using the mouse pointer *)
  241. CustomTextView = OBJECT(WMTextView.TextView)
  242. VAR
  243. selecting, selectWords, dragPossible : BOOLEAN;
  244. lastPos : LONGINT;
  245. downX, downY : LONGINT;
  246. utilreader : Texts.TextReader;
  247. text : Texts.Text;
  248. PROCEDURE SetText*(text : Texts.Text);
  249. BEGIN
  250. SetText^(text);
  251. SELF.text := text;
  252. NEW(utilreader, text);
  253. END SetText;
  254. PROCEDURE PointerDown*(x, y : LONGINT; keys : SET);
  255. VAR pos : LONGINT;
  256. BEGIN
  257. IF keys * {0, 1, 2} = {2} THEN
  258. ShowContextMenu(x, y) END;
  259. IF 0 IN keys THEN
  260. text.AcquireRead;
  261. ViewToTextPos(x, y, pos);
  262. dragPossible := FALSE; selectWords := FALSE;
  263. IF pos >= 0 THEN
  264. selection.Sort;
  265. IF (pos >= selection.a) & (pos < selection.b) THEN
  266. dragPossible := TRUE; downX := x; downY := y
  267. ELSE
  268. (* clicking the same position twice --> Word Selection Mode *)
  269. IF pos = lastPos THEN
  270. selectWords := TRUE;
  271. selection.SetFromTo(TextUtilities.FindPosWordLeft(utilreader, pos - 1),
  272. TextUtilities.FindPosWordRight(utilreader, pos + 1))
  273. ELSE
  274. selection.SetFromTo(pos, pos) (* reset selection *)
  275. END;
  276. selecting := TRUE
  277. END
  278. END;
  279. lastPos := pos;
  280. text.ReleaseRead;
  281. END;
  282. END PointerDown;
  283. PROCEDURE PointerMove*(x, y : LONGINT; keys : SET);
  284. CONST DragDist = 5;
  285. VAR pos : LONGINT;
  286. BEGIN
  287. IF dragPossible THEN
  288. IF (ABS(x - downX) > DragDist) OR (ABS(y - downY) > DragDist) THEN dragPossible := FALSE; AutoStartDrag END
  289. ELSE
  290. IF selecting THEN
  291. text.AcquireRead;
  292. ViewToTextPos(x, y, pos);
  293. IF selecting THEN
  294. IF selectWords THEN
  295. IF pos < selection.from.GetPosition() THEN pos := TextUtilities.FindPosWordLeft(utilreader, pos - 1);
  296. ELSE pos := TextUtilities.FindPosWordRight(utilreader, pos + 1)
  297. END;
  298. selection.SetTo(pos)
  299. ELSE
  300. selection.SetTo(pos);
  301. END;
  302. Texts.SetLastSelection(text, selection.from, selection.to);
  303. END;
  304. text.ReleaseRead;
  305. END;
  306. END
  307. END PointerMove;
  308. PROCEDURE PointerUp*(x, y : LONGINT; keys : SET);
  309. BEGIN
  310. selecting := FALSE;
  311. IF dragPossible THEN selection.SetFromTo(0, 0); Texts.ClearLastSelection (* reset selection *) END;
  312. dragPossible := FALSE
  313. END PointerUp;
  314. PROCEDURE &Init*;
  315. BEGIN
  316. Init^;
  317. SetNameAsString(StrCustomTextView);
  318. END Init;
  319. END CustomTextView;
  320. TYPE
  321. TerminalComponent = OBJECT(WMComponents.VisualComponent)
  322. VAR
  323. settings : Settings;
  324. (* Access to serial port *)
  325. in : Streams.Reader;
  326. out : Streams.Writer;
  327. port : Serials.Port;
  328. portNr, bps, databits, parity, stop : LONGINT;
  329. open : BOOLEAN;
  330. lock : Lock;
  331. (* Terminal window text writer *)
  332. w : TextUtilities.TextWriter;
  333. textView : CustomTextView;
  334. text : Texts.Text;
  335. searchPanel : WMSearchComponents.SearchPanel;
  336. (* Upper Toolbar *)
  337. opencloseBtn : WMStandardComponents.Button;
  338. settingsEdit : WMEditors.Editor;
  339. sendXBtn, sendYBtn : WMStandardComponents.Button;
  340. receiveXBtn, receiveYBtn : WMStandardComponents.Button;
  341. (* Lower Toolbar *)
  342. lowerToolBar : WMStandardComponents.Panel;
  343. sendCommandBtn : WMStandardComponents.Button;
  344. sendCommandEditor : WMEditors.Editor;
  345. commandPopup : WMPopups.Popup; (* can be NIL *)
  346. commandMenuBtn : WMStandardComponents.Button;
  347. (* Status Bar *)
  348. status : WMStandardComponents.Label;
  349. dsr : WMStandardComponents.Label;
  350. clearStatusBtn : WMStandardComponents.Button;
  351. (* Error Counters *)
  352. overrunErrors, framingErrors, parityErrors, breakInterrupts, transportErrors, otherErrors : LONGINT;
  353. statusUpdater : StatusUpdater;
  354. running : BOOLEAN;
  355. timer : Kernel.Timer;
  356. PROCEDURE Handle*(VAR m: WMMessages.Message);
  357. BEGIN
  358. IF m.msgType = WMMessages.MsgKey THEN
  359. IF ~settings.shortcutsEnabled OR ~HandleShortcut(m.x, m.flags, m.y) THEN
  360. Handle^(m);
  361. END;
  362. ELSE Handle^(m)
  363. END
  364. END Handle;
  365. PROCEDURE HandleCommandMenuButton(sender, data : ANY);
  366. VAR buttonBounds, panelBounds: WMRectangles.Rectangle; gx, gy : LONGINT;
  367. BEGIN
  368. buttonBounds := commandMenuBtn.bounds.Get();
  369. panelBounds := bounds.Get();
  370. ToWMCoordinates(panelBounds.l + buttonBounds.l, panelBounds.t + buttonBounds.b, gx, gy);
  371. commandPopup.Popup(gx,gy);
  372. END HandleCommandMenuButton;
  373. PROCEDURE HandleCommandPopup(sender, data : ANY);
  374. VAR command : Command;
  375. BEGIN
  376. IF (data # NIL) & (data IS Command) & open THEN
  377. command := data (Command);
  378. lock.Acquire(Terminal);
  379. out.String(command.commandString); out.Char(CR);
  380. IF settings.linefeed THEN out.Char(LF); END;
  381. out.Update;
  382. lock.Release;
  383. END;
  384. END HandleCommandPopup;
  385. PROCEDURE HandleSendCommandButton(sender, data : ANY);
  386. VAR commandString : ARRAY 1024 OF CHAR;
  387. BEGIN
  388. sendCommandEditor.GetAsString(commandString);
  389. IF open & (commandString # "") THEN
  390. lock.Acquire(Terminal);
  391. out.String(commandString); out.Char(CR);
  392. IF settings.linefeed THEN out.Char(LF); END;
  393. out.Update;
  394. lock.Release;
  395. sendCommandEditor.SetAsString("");
  396. END;
  397. END HandleSendCommandButton;
  398. PROCEDURE HandleClearStatusButton(sender, data : ANY);
  399. BEGIN
  400. ResetStatus;
  401. END HandleClearStatusButton;
  402. PROCEDURE HandleSearchButton(sender, data : ANY);
  403. VAR searchString : WMSearchComponents.SearchString;
  404. BEGIN
  405. searchPanel.visible.Set(TRUE);
  406. searchPanel.SetToLastSelection;
  407. searchPanel.searchEdit.GetAsString(searchString);
  408. IF (searchString # "") THEN
  409. searchPanel.SearchHandler(NIL, NIL);
  410. ELSE
  411. searchPanel.searchEdit.SetFocus;
  412. END;
  413. END HandleSearchButton;
  414. PROCEDURE HandleClearButton(sender, data : ANY);
  415. BEGIN
  416. text.AcquireWrite;
  417. text.Delete(0, text.GetLength());
  418. textView.firstLine.Set(0); textView.cursor.SetPosition(0);
  419. text.ReleaseWrite
  420. END HandleClearButton;
  421. PROCEDURE HandleCopyButton(sender, data : ANY);
  422. BEGIN
  423. textView.CopySelection;
  424. END HandleCopyButton;
  425. PROCEDURE HandlePasteButton(sender, data : ANY);
  426. BEGIN
  427. IF open THEN
  428. CopyFromClipboard;
  429. ELSE
  430. WMDialogs.Error("Terminal", "Port is not open");
  431. RETURN;
  432. END;
  433. END HandlePasteButton;
  434. PROCEDURE HandleXYButtons(sender, data : ANY);
  435. VAR
  436. button : WMStandardComponents.Button;
  437. command : Strings.String;
  438. filename, msg : ARRAY 512 OF CHAR;
  439. filenames : Strings.StringArray;
  440. mode, i : LONGINT;
  441. send : BOOLEAN;
  442. BEGIN
  443. IF sender IS WMStandardComponents.Button THEN
  444. button := sender (WMStandardComponents.Button);
  445. IF button = sendXBtn THEN
  446. mode := XYModem.XModem; send := TRUE;
  447. ELSIF button = receiveXBtn THEN
  448. mode := XYModem.XModem; send := FALSE;
  449. ELSIF button = sendYBtn THEN
  450. mode := XYModem.YModem; send := TRUE;
  451. ELSIF button = receiveYBtn THEN
  452. mode := XYModem.YModem; send := FALSE;
  453. ELSE
  454. HALT(99);
  455. END;
  456. ELSE
  457. HALT(99);
  458. END;
  459. IF ~open THEN
  460. WMDialogs.Error("Terminal", "Port is not open");
  461. RETURN;
  462. END;
  463. IF send THEN msg := "File to send:"; ELSE msg := "File to receive:"; END;
  464. IF WMDialogs.QueryString(msg, filename) = WMDialogs.ResOk THEN
  465. filenames := Strings.Split(filename, ";");
  466. command := GetXYCommand(send, mode);
  467. IF (LEN(filenames) > 1) & (command = NIL) THEN
  468. WMDialogs.Error("Terminal", "Multiple files can only be sent if send command is specified");
  469. ELSE
  470. FOR i := 0 TO LEN(filenames)-1 DO
  471. Strings.TrimWS(filenames[i]^);
  472. IF command # NIL THEN SendXYCommand(send, command^, filenames[i]^); END;
  473. IF send THEN
  474. SendXYModem(filenames[i]^, mode);
  475. ELSE
  476. ReceiveXYModem(filenames[i]^, mode);
  477. END;
  478. END;
  479. END;
  480. END;
  481. END HandleXYButtons;
  482. PROCEDURE HandleShortcut(ucs : LONGINT; flags : SET; keySym : LONGINT) : BOOLEAN;
  483. VAR handled : BOOLEAN;
  484. BEGIN
  485. IF ControlKeyDown(flags) THEN
  486. handled := TRUE;
  487. IF keySym = 01H THEN (* Ctrl-A *)
  488. textView.SelectAll;
  489. ELSIF keySym = 03H THEN (* Ctrl-C *)
  490. textView.CopySelection;
  491. ELSIF keySym = 04H THEN (* Ctrl-D *)
  492. HandleXYButtons(sendYBtn, NIL);
  493. ELSIF (keySym = 06H) THEN (* CTRL-F *)
  494. searchPanel.ToggleVisibility;
  495. ELSIF (keySym= 0EH) THEN (* CTRL-N *)
  496. searchPanel.HandlePreviousNext(TRUE);
  497. ELSIF (keySym = 10H) THEN (* CTRL-P *)
  498. searchPanel.HandlePreviousNext(FALSE);
  499. ELSE
  500. handled := FALSE;
  501. END;
  502. ELSIF (keySym = Inputs.KsTab) & (flags = {}) THEN (* TAB *)
  503. handled := searchPanel.HandleTab();
  504. ELSE
  505. handled := FALSE;
  506. END;
  507. RETURN handled;
  508. END HandleShortcut;
  509. PROCEDURE ExtKeyPressed(ucs : LONGINT; flags : SET; VAR keySym : LONGINT; VAR handled : BOOLEAN);
  510. BEGIN
  511. textView.SetFlags(flags);
  512. handled := TRUE;
  513. IF (ucs > 0) & (ucs < 256) THEN
  514. IF open & ~(Inputs.Release IN flags) THEN
  515. IF lock.TryAcquire(Terminal) THEN
  516. IF Trace * TraceCharactersSent # {} THEN Show("Sending character: "); KernelLog.Int(ucs, 0); KernelLog.Ln; END;
  517. IF ucs > 127 THEN (* Escape non-ascii characters *)
  518. out.Char(ESC); out.Char("["); ucs := ucs - 128;
  519. END;
  520. IF settings.linefeed & (ucs = ORD(CR)) THEN
  521. out.Char(CR); out.Char(LF); out.Update;
  522. ELSIF settings.useBackspace & (ucs = ORD(DEL)) THEN
  523. out.Char(Backspace); out.Update;
  524. ELSE
  525. out.Char(CHR(ucs)); out.Update;
  526. END;
  527. lock.Release;
  528. ELSE
  529. (* ignore characters *)
  530. END;
  531. END;
  532. END;
  533. END ExtKeyPressed;
  534. PROCEDURE ExtFocus(hasFocus : BOOLEAN);
  535. BEGIN
  536. IF hasFocus THEN
  537. FocusReceived;
  538. IF settings.indicateKeyboardFocus THEN textView.fillColor.Set(00H); END;
  539. ELSE
  540. FocusLost;
  541. IF settings.indicateKeyboardFocus THEN textView.fillColor.Set(0CCCCCCCCH); END;
  542. END;
  543. END ExtFocus;
  544. PROCEDURE CreateUpperToolBar() : WMComponents.VisualComponent;
  545. VAR toolbar : WMStandardComponents.Panel; label : WMStandardComponents.Label; button : WMStandardComponents.Button;
  546. BEGIN
  547. NEW(toolbar);
  548. toolbar.alignment.Set(WMComponents.AlignTop); toolbar.bounds.SetHeight(20);
  549. toolbar.fillColor.Set(0E0E0E0FFH);
  550. NEW(label);
  551. label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(50);
  552. label.caption.SetAOC(" Settings:");
  553. toolbar.AddContent(label);
  554. NEW(settingsEdit);
  555. settingsEdit.alignment.Set(WMComponents.AlignLeft);
  556. settingsEdit.bounds.SetWidth(110);
  557. settingsEdit.multiLine.Set(FALSE);
  558. settingsEdit.tv.textAlignV.Set(WMGraphics.AlignCenter);
  559. settingsEdit.fillColor.Set(WMGraphics.White);
  560. settingsEdit.tv.borders.Set(WMRectangles.MakeRect(4, 3, 2, 2));
  561. settingsEdit.tv.showBorder.Set(TRUE);
  562. settingsEdit.tv.textAlignV.Set(WMGraphics.AlignCenter);
  563. settingsEdit.SetAsString(settings.portSettings);
  564. toolbar.AddContent(settingsEdit);
  565. (* open/close *)
  566. NEW(opencloseBtn);
  567. opencloseBtn.alignment.Set(WMComponents.AlignLeft);
  568. opencloseBtn.takesFocus.Set(FALSE);
  569. opencloseBtn.caption.SetAOC("Open");
  570. opencloseBtn.onClick.Add(ToggleOpen);
  571. toolbar.AddContent(opencloseBtn);
  572. NEW(label);
  573. label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(65);
  574. label.caption.SetAOC(" XModem: ");
  575. toolbar.AddContent(label);
  576. (* send XModem *)
  577. NEW(sendXBtn);
  578. sendXBtn.alignment.Set(WMComponents.AlignLeft);
  579. sendXBtn.bounds.SetWidth(40);
  580. sendXBtn.caption.SetAOC("Send");
  581. sendXBtn.onClick.Add(HandleXYButtons);
  582. toolbar.AddContent(sendXBtn);
  583. (* receive XModem *)
  584. NEW(receiveXBtn);
  585. receiveXBtn.alignment.Set(WMComponents.AlignLeft);
  586. receiveXBtn.bounds.SetWidth(40);
  587. receiveXBtn.caption.SetAOC("Receive");
  588. receiveXBtn.onClick.Add(HandleXYButtons);
  589. toolbar.AddContent(receiveXBtn);
  590. NEW(label);
  591. label.alignment.Set(WMComponents.AlignLeft); label.bounds.SetWidth(65);
  592. label.caption.SetAOC(" YModem:");
  593. toolbar.AddContent(label);
  594. (* send YModem *)
  595. NEW(sendYBtn);
  596. sendYBtn.alignment.Set(WMComponents.AlignLeft);
  597. sendYBtn.bounds.SetWidth(40);
  598. sendYBtn.caption.SetAOC("Send");
  599. sendYBtn.onClick.Add(HandleXYButtons);
  600. toolbar.AddContent(sendYBtn);
  601. (* receive YModem *)
  602. NEW(receiveYBtn);
  603. receiveYBtn.alignment.Set(WMComponents.AlignLeft);
  604. receiveYBtn.bounds.SetWidth(40);
  605. receiveYBtn.caption.SetAOC("Receive");
  606. receiveYBtn.onClick.Add(HandleXYButtons);
  607. toolbar.AddContent(receiveYBtn);
  608. (* Clear *)
  609. NEW(button); button.alignment.Set(WMComponents.AlignRight);
  610. button.caption.SetAOC("Clear");
  611. button.onClick.Add(HandleClearButton);
  612. toolbar.AddContent(button);
  613. (* Paste *)
  614. NEW(button); button.alignment.Set(WMComponents.AlignRight);
  615. button.caption.SetAOC("Paste");
  616. button.onClick.Add(HandlePasteButton);
  617. toolbar.AddContent(button);
  618. (* Copy *)
  619. NEW(button); button.alignment.Set(WMComponents.AlignRight);
  620. button.caption.SetAOC("Copy");
  621. button.onClick.Add(HandleCopyButton);
  622. toolbar.AddContent(button);
  623. (* Search *)
  624. NEW(button); button.alignment.Set(WMComponents.AlignRight);
  625. button.caption.SetAOC("Search");
  626. button.onClick.Add(HandleSearchButton);
  627. toolbar.AddContent(button);
  628. RETURN toolbar;
  629. END CreateUpperToolBar;
  630. PROCEDURE CreateCommandMenu() : WMStandardComponents.Button;
  631. VAR command : Command; button : WMStandardComponents.Button;
  632. BEGIN
  633. command := LoadCommandMenu();
  634. IF command # NIL THEN
  635. NEW(commandPopup);
  636. WHILE command # NIL DO
  637. commandPopup.AddParButton(command.name, HandleCommandPopup, command);
  638. command := command.next;
  639. END;
  640. NEW(button);
  641. button.bounds.SetWidth(150); button.alignment.Set(WMComponents.AlignRight);
  642. button.takesFocus.Set(FALSE);
  643. button.caption.SetAOC("Commands");
  644. button.onClick.Add(HandleCommandMenuButton);
  645. END;
  646. RETURN button;
  647. END CreateCommandMenu;
  648. PROCEDURE CreateLowerToolBar() : WMStandardComponents.Panel;
  649. VAR toolbar : WMStandardComponents.Panel;
  650. BEGIN
  651. NEW(toolbar);
  652. toolbar.alignment.Set(WMComponents.AlignBottom); toolbar.bounds.SetHeight(20);
  653. toolbar.fillColor.Set(0E0E0E0FFH);
  654. NEW(sendCommandBtn);
  655. sendCommandBtn.alignment.Set(WMComponents.AlignLeft); sendCommandBtn.bounds.SetWidth(100);
  656. sendCommandBtn.takesFocus.Set(FALSE);
  657. sendCommandBtn.caption.SetAOC("Send Command:");
  658. sendCommandBtn.onClick.Add(HandleSendCommandButton);
  659. toolbar.AddContent(sendCommandBtn);
  660. commandMenuBtn := CreateCommandMenu();
  661. IF commandMenuBtn # NIL THEN
  662. toolbar.AddContent(commandMenuBtn);
  663. END;
  664. NEW(sendCommandEditor);
  665. sendCommandEditor.alignment.Set(WMComponents.AlignClient);
  666. sendCommandEditor.multiLine.Set(FALSE);
  667. sendCommandEditor.tv.textAlignV.Set(WMGraphics.AlignCenter);
  668. sendCommandEditor.fillColor.Set(WMGraphics.White);
  669. sendCommandEditor.tv.borders.Set(WMRectangles.MakeRect(4, 3, 2, 2));
  670. sendCommandEditor.tv.showBorder.Set(TRUE);
  671. sendCommandEditor.SetAsString("");
  672. sendCommandEditor.onEnter.Add(HandleSendCommandButton);
  673. toolbar.AddContent(sendCommandEditor);
  674. RETURN toolbar;
  675. END CreateLowerToolBar;
  676. PROCEDURE CreateStatusBar() : WMStandardComponents.Panel;
  677. VAR statusBar : WMStandardComponents.Panel;
  678. BEGIN
  679. NEW(statusBar);
  680. statusBar.alignment.Set(WMComponents.AlignBottom); statusBar.bounds.SetHeight(20);
  681. statusBar.fillColor.Set(0E0E0E0FFH);
  682. NEW(clearStatusBtn);
  683. clearStatusBtn.bounds.SetWidth(80); clearStatusBtn.alignment.Set(WMComponents.AlignRight);
  684. clearStatusBtn.caption.SetAOC("Clear Status");
  685. clearStatusBtn.onClick.Add(HandleClearStatusButton);
  686. statusBar.AddContent(clearStatusBtn);
  687. NEW(dsr);
  688. dsr.bounds.SetWidth(30); dsr.alignment.Set(WMComponents.AlignRight);
  689. dsr.bearing.Set(WMRectangles.MakeRect(1,1,1,1));
  690. dsr.caption.SetAOC("DSR"); dsr.fillColor.Set(WMGraphics.White); dsr.alignH.Set(WMGraphics.AlignCenter);
  691. statusBar.AddContent(dsr);
  692. NEW(status);
  693. status.alignment.Set(WMComponents.AlignClient);
  694. statusBar.AddContent(status);
  695. RETURN statusBar;
  696. END CreateStatusBar;
  697. PROCEDURE CreateContent;
  698. VAR scrollbarY : WMStandardComponents.Scrollbar;
  699. BEGIN
  700. AddContent(CreateUpperToolBar()); (* AlignTop *)
  701. IF settings.showStatusBar THEN
  702. AddContent(CreateStatusBar()); (* AlignBottom *)
  703. END;
  704. lowerToolBar := CreateLowerToolBar();
  705. AddContent(lowerToolBar); (* AlignBottom *)
  706. NEW(scrollbarY); scrollbarY.alignment.Set(WMComponents.AlignRight); scrollbarY.vertical.Set(TRUE);
  707. NEW(text);
  708. NEW(textView); textView.alignment.Set(WMComponents.AlignClient);
  709. textView.SetText(text);
  710. textView.showBorder.Set(TRUE);
  711. textView.SetScrollbars(NIL, scrollbarY);
  712. textView.SetExtKeyEventHandler(ExtKeyPressed);
  713. textView.SetExtFocusHandler(ExtFocus);
  714. IF settings.indicateKeyboardFocus THEN textView.fillColor.Set(0CCCCCCCCH); END;
  715. NEW(searchPanel);
  716. searchPanel.alignment.Set(WMComponents.AlignBottom);
  717. searchPanel.bounds.SetHeight(40);
  718. searchPanel.SetText(text);
  719. searchPanel.SetTextView(textView);
  720. searchPanel.visible.Set(FALSE);
  721. AddContent(searchPanel);
  722. AddContent(scrollbarY);
  723. AddContent(textView);
  724. END CreateContent;
  725. PROCEDURE Wait(ms : LONGINT);
  726. BEGIN
  727. timer.Sleep(ms);
  728. END Wait;
  729. PROCEDURE &Init*;
  730. BEGIN
  731. Init^;
  732. SetGenerator("WMV24Component.NewTerminalComponent");
  733. NEW(timer); NEW(lock);
  734. NEW(settings); settings.Load;
  735. CreateContent;
  736. NEW(w, text); w.SetFontName("Courier");
  737. IF settings.showStatusBar THEN NEW(statusUpdater, SELF); END;
  738. SetNameAsString(StrTerminalComponent);
  739. END Init;
  740. (* Get global coordinates of the terminal panel *)
  741. PROCEDURE GetPanelCoordinates(VAR gx, gy : LONGINT);
  742. VAR rect : WMRectangles.Rectangle;
  743. BEGIN
  744. rect := bounds.Get();
  745. ToWMCoordinates(rect.l, rect.t, gx, gy);
  746. END GetPanelCoordinates;
  747. PROCEDURE CopyFromClipboard;
  748. VAR string : POINTER TO ARRAY OF CHAR;
  749. BEGIN
  750. Texts.clipboard.AcquireRead;
  751. IF Texts.clipboard.GetLength() > 0 THEN
  752. NEW(string, Texts.clipboard.GetLength()+1);
  753. TextUtilities.TextToStr(Texts.clipboard, string^);
  754. END;
  755. Texts.clipboard.ReleaseRead;
  756. lock.Acquire(Terminal); out.String(string^); out.Update; lock.Release;
  757. END CopyFromClipboard;
  758. PROCEDURE GetXYCommand(send : BOOLEAN; mode : LONGINT) : Strings.String;
  759. VAR command : Strings.String;
  760. BEGIN
  761. IF (mode = XYModem.XModem) THEN
  762. IF send THEN command := settings.xReceiveCommand;
  763. ELSE command := settings.xSendCommand;
  764. END;
  765. ELSE
  766. IF send THEN command := settings.yReceiveCommand;
  767. ELSE command := settings.ySendCommand;
  768. END;
  769. END;
  770. RETURN command;
  771. END GetXYCommand;
  772. PROCEDURE SendXYCommand(send : BOOLEAN; CONST command, filename : ARRAY OF CHAR);
  773. BEGIN
  774. lock.Acquire(Terminal);
  775. out.String(command);
  776. IF ~send THEN out.Char(" "); out.String(filename); END;
  777. out.Char(CR);
  778. IF settings.linefeed THEN out.Char(LF); END;
  779. out.Update;
  780. lock.Release;
  781. Wait(500);
  782. END SendXYCommand;
  783. PROCEDURE SendXYModem(CONST filename : ARRAY OF CHAR; mode : LONGINT);
  784. VAR
  785. f : Files.File;
  786. progressWindow : ProgressWindow;
  787. progressInfo : ProgressInfo;
  788. xysender : XYModem.Sender;
  789. msg : ARRAY 32 OF CHAR;
  790. x, y, res : LONGINT;
  791. BEGIN
  792. f := Files.Old(filename);
  793. IF f # NIL THEN
  794. IF open THEN
  795. NEW(timer);
  796. open := FALSE;
  797. port.Close; (* Force ReceiveCharacters to release the lock *)
  798. lock.Acquire(DataTransfer);
  799. (* Now we have the port for us alone *)
  800. port.Open(bps, databits, parity, stop, res);
  801. IF res = Serials.Ok THEN
  802. in.Reset; out.Reset;
  803. NEW(xysender, out, in, f, mode);
  804. NEW(progressInfo, filename, f.Length()); progressInfo.bounds.SetExtents(300, 60);
  805. GetPanelCoordinates(x, y);
  806. NEW(progressWindow, progressInfo, x + 150, y + 50);
  807. WHILE ~xysender.IsDone() DO
  808. progressInfo.SetProgress(xysender.bytesProcessed);
  809. Wait(500);
  810. END;
  811. progressInfo.SetProgress(xysender.bytesProcessed);
  812. xysender.Await(msg);
  813. ELSE
  814. Show("FATAL ERROR, could not re-open the port"); KernelLog.Ln;
  815. END;
  816. lock.Release;
  817. IF msg # "" THEN
  818. WMDialogs.Error("Transmission failed", msg)
  819. END;
  820. Wait(1000);
  821. progressWindow.Close;
  822. BEGIN {EXCLUSIVE} open := TRUE; END;
  823. END;
  824. ELSE
  825. WMDialogs.Error("File not found", filename);
  826. END;
  827. END SendXYModem;
  828. PROCEDURE ReceiveXYModem(filename : ARRAY OF CHAR; mode : LONGINT);
  829. VAR
  830. f : Files.File;
  831. progressWindow : ProgressWindow;
  832. label : WMStandardComponents.Label;
  833. caption : ARRAY 128 OF CHAR;
  834. xyreceiver : XYModem.Receiver;
  835. msg : ARRAY 32 OF CHAR;
  836. x, y, res : LONGINT;
  837. awaitF : BOOLEAN;
  838. BEGIN
  839. IF filename # "" THEN
  840. f := Files.New(filename); awaitF := FALSE;
  841. IF f = NIL THEN
  842. WMDialogs.Error("Couldn't create file ", filename);
  843. RETURN;
  844. END;
  845. ELSE
  846. f := NIL; awaitF := TRUE
  847. END;
  848. IF open THEN
  849. NEW(timer);
  850. open := FALSE;
  851. port.Close; (* Force ReceiveCharacters to release the lock *)
  852. lock.Acquire(DataTransfer);
  853. (* Now we have the port for us alone *)
  854. port.Open(bps, databits, parity, stop, res);
  855. IF res = Serials.Ok THEN
  856. in.Reset; out.Reset;
  857. NEW(xyreceiver, out, in, f, mode);
  858. NEW(label); label.alignment.Set(WMComponents.AlignLeft);
  859. label.bounds.SetExtents(300, 100); label.fillColor.Set(WMGraphics.White);
  860. label.alignH.Set(WMGraphics.AlignCenter); label.alignV.Set(WMGraphics.AlignCenter);
  861. label.caption.SetAOC("Receiving data...");
  862. GetPanelCoordinates(x, y);
  863. NEW(progressWindow, label, x + 150, y + 50);
  864. WHILE ~xyreceiver.IsDone() DO
  865. Strings.IntToStr(xyreceiver.bytesProcessed, caption);
  866. Strings.Append(caption, " bytes received");
  867. label.caption.SetAOC(caption);
  868. Wait(500);
  869. END;
  870. Strings.IntToStr(xyreceiver.bytesProcessed, caption);
  871. Strings.Append(caption, " bytes received");
  872. label.caption.SetAOC(caption);
  873. IF ~awaitF THEN
  874. xyreceiver.Await(msg)
  875. ELSE
  876. xyreceiver.AwaitF(f, msg)
  877. END;
  878. ELSE
  879. Show("FATAL ERROR, could not re-open the port"); KernelLog.Ln;
  880. END;
  881. lock.Release;
  882. Wait(500); (* Give the port open time so we see the output below *)
  883. IF msg # "" THEN
  884. WMDialogs.Error("Reception failed", msg)
  885. ELSIF f = NIL THEN
  886. WMDialogs.Error("Error: File is NIL", msg);
  887. ELSE
  888. Files.Register(f);
  889. IF awaitF THEN
  890. f.GetName(filename);
  891. END;
  892. caption := "File "; Strings.Append(caption, filename); Strings.Append(caption, " received (");
  893. Strings.IntToStr(xyreceiver.bytesProcessed, msg); Strings.Append(caption, msg); Strings.Append(caption, "Bytes)");
  894. label.caption.SetAOC(caption);
  895. END;
  896. Wait(500);
  897. progressWindow.Close;
  898. BEGIN {EXCLUSIVE} open := TRUE; END;
  899. END;
  900. END ReceiveXYModem;
  901. PROCEDURE ResetStatus;
  902. BEGIN
  903. overrunErrors := 0; parityErrors := 0; framingErrors := 0; transportErrors := 0; breakInterrupts := 0;
  904. END ResetStatus;
  905. PROCEDURE ToggleOpen(sender, data : ANY);
  906. VAR msg, s, t : ARRAY 64 OF CHAR; parityChar : CHAR;
  907. r : Streams.StringReader;
  908. res : LONGINT;
  909. BEGIN
  910. ResetStatus;
  911. IF open THEN
  912. open := FALSE;
  913. port.Close;
  914. opencloseBtn.caption.SetAOC("Open");
  915. ELSE
  916. settingsEdit.GetAsString(s);
  917. NEW(r, 64); r.Set(s); r.SkipWhitespace;
  918. r.Int(portNr, FALSE); r.SkipWhitespace;
  919. r.Int(bps, FALSE); r.SkipWhitespace;
  920. r.Int(databits, FALSE); r.SkipWhitespace;
  921. r.Int(stop, FALSE); r.SkipWhitespace;
  922. r.Char(parityChar);
  923. port := Serials.GetPort(portNr);
  924. IF port # NIL THEN
  925. CASE CAP(parityChar) OF
  926. | "N" : parity := Serials.ParNo;
  927. | "O" : parity := Serials.ParOdd;
  928. | "E" : parity := Serials.ParEven;
  929. | "M" : parity := Serials.ParMark;
  930. | "S" : parity := Serials.ParSpace;
  931. ELSE parity := Serials.ParNo
  932. END;
  933. port.Open(bps, databits, parity, stop, res);
  934. IF res = Serials.Ok THEN
  935. opencloseBtn.caption.SetAOC("Close");
  936. NEW(in, port.Receive, 64); NEW(out, port.Send, 64);
  937. BEGIN {EXCLUSIVE}
  938. open := TRUE
  939. END
  940. ELSE
  941. ReportError("Configuration Error", res);
  942. END
  943. ELSE
  944. msg := "Port number not available: "; Strings.IntToStr(portNr, t); Strings.Append(msg, t);
  945. WMDialogs.Error("Port not found", msg)
  946. END;
  947. END;
  948. END ToggleOpen;
  949. PROCEDURE Finalize*;
  950. BEGIN
  951. Finalize^;
  952. IF settings.showStatusBar THEN statusUpdater.Terminate; END;
  953. BEGIN {EXCLUSIVE}
  954. running := FALSE;
  955. IF port # NIL THEN port.Close; open := FALSE; END;
  956. END;
  957. END Finalize;
  958. PROCEDURE DeleteNCharacters(nbrOfCharacters : LONGINT);
  959. VAR pos : LONGINT;
  960. BEGIN
  961. text.AcquireWrite;
  962. pos := textView.cursor.GetPosition();
  963. text.Delete(pos - nbrOfCharacters, nbrOfCharacters);
  964. text.ReleaseWrite;
  965. END DeleteNCharacters;
  966. PROCEDURE ReportError(CONST title : ARRAY OF CHAR; res : LONGINT);
  967. VAR msg : ARRAY 128 OF CHAR;
  968. BEGIN
  969. CASE res OF
  970. | Serials.PortInUse : msg := "Port already in use"
  971. | Serials.WrongBPS : msg := "Unsupported BPS"
  972. | Serials.WrongData : msg := "Unsupported data or stop bits"
  973. | Serials.WrongParity : msg := "Unsupported parity";
  974. | Serials.OverrunError : msg := "Overrun Error";
  975. | Serials.ParityError : msg := "Parity Error";
  976. | Serials.FramingError : msg := "Framing Error (Wrong bitrate?)";
  977. | Serials.BreakInterrupt : msg := "Break Interrupt received";
  978. | Serials.Closed : msg := "Port is closed";
  979. | Serials.TransportError : msg := "Transport Layer Error";
  980. ELSE msg := "Unspecified error"
  981. END;
  982. WMDialogs.Error(title, msg)
  983. END ReportError;
  984. PROCEDURE EvaluateError(res : LONGINT);
  985. BEGIN
  986. CASE res OF
  987. |Serials.OverrunError: INC(overrunErrors);
  988. |Serials.ParityError: INC(parityErrors);
  989. |Serials.FramingError: INC(framingErrors);
  990. |Serials.BreakInterrupt: INC(breakInterrupts);
  991. |Serials.TransportError: INC(transportErrors);
  992. ELSE
  993. INC(otherErrors);
  994. END;
  995. END EvaluateError;
  996. PROCEDURE ReceiveCharacters;
  997. VAR ch : CHAR; buffer : ARRAY ReceiveBufferSize OF CHAR; backspaces, i, len, res : LONGINT;
  998. BEGIN
  999. len := 0;
  1000. res := Streams.Ok;
  1001. WHILE running & (res = Streams.Ok) & (len = 0) DO
  1002. lock.Acquire(Terminal);
  1003. len := port.Available();
  1004. IF len # 0 THEN
  1005. port.Receive(buffer, 0, ReceiveBufferSize, MIN(len,ReceiveBufferSize), len, res);
  1006. END;
  1007. lock.Release;
  1008. IF running & (res = Streams.Ok) & (len = 0) THEN
  1009. Objects.Yield;
  1010. END;
  1011. END;
  1012. IF res = Serials.Ok THEN
  1013. FOR i := 0 TO len-1 DO
  1014. ch := buffer[i];
  1015. IF Trace * TraceCharactersReceived # {} THEN Show("Received character: "); KernelLog.Int(ORD(ch), 0); KernelLog.Ln; END;
  1016. IF settings.echo THEN out.Char(ch); out.Update; END;
  1017. IF ~settings.utf8Support & (ORD(ch) > 127) THEN
  1018. ch := ".";
  1019. END;
  1020. IF (ch = DEL) OR (ch = Backspace) THEN
  1021. INC(backspaces);
  1022. ELSE
  1023. IF (backspaces > 0) THEN
  1024. w.Update;
  1025. DeleteNCharacters(backspaces);
  1026. backspaces := 0;
  1027. END;
  1028. w.Char(ch);
  1029. END;
  1030. END;
  1031. w.Update;
  1032. ELSE
  1033. EvaluateError(res);
  1034. END;
  1035. DeleteNCharacters(backspaces);
  1036. END ReceiveCharacters;
  1037. BEGIN {ACTIVE}
  1038. running := TRUE;
  1039. WHILE running DO
  1040. BEGIN {EXCLUSIVE} AWAIT(open OR ~running); END;
  1041. IF running THEN ReceiveCharacters; END;
  1042. END;
  1043. END TerminalComponent;
  1044. TYPE
  1045. StatusUpdater = OBJECT
  1046. VAR
  1047. terminal : TerminalComponent;
  1048. writer : Streams.StringWriter;
  1049. alive, dead : BOOLEAN;
  1050. timer : Kernel.Timer;
  1051. PROCEDURE UpdateStatusLabel;
  1052. VAR string : ARRAY 1024 OF CHAR; port : Serials.Port; mc : SET;
  1053. BEGIN
  1054. writer.Reset;
  1055. writer.String(" Errors: ");
  1056. writer.String("Overruns: "); writer.Int(terminal.overrunErrors, 5); writer.String(" ");
  1057. writer.String("Parity: "); writer.Int(terminal.parityErrors, 5); writer.String(" ");
  1058. writer.String("Framing: "); writer.Int(terminal.framingErrors, 5); writer.String(" ");
  1059. writer.String("Transport: "); writer.Int(terminal.transportErrors, 5);
  1060. port := terminal.port;
  1061. IF (port # NIL) & terminal.open THEN
  1062. writer.String(" ");
  1063. writer.String("Sent: "); writer.Int(port.charactersSent, 8); writer.String(" ");
  1064. writer.String("Received: "); writer.Int(port.charactersReceived, 8);
  1065. port.GetMC(mc);
  1066. IF mc * {Serials.DSR} # {} THEN
  1067. terminal.dsr.fillColor.Set(WMGraphics.Green);
  1068. ELSE
  1069. terminal.dsr.fillColor.Set(WMGraphics.Red);
  1070. END;
  1071. ELSE
  1072. terminal.dsr.fillColor.Set(WMGraphics.White);
  1073. END;
  1074. writer.Get(string);
  1075. terminal.status.caption.SetAOC(string);
  1076. IF (terminal.overrunErrors > 0) OR (terminal.parityErrors > 0) OR (terminal.framingErrors > 0) OR
  1077. (terminal.transportErrors > 0) THEN
  1078. terminal.status.fillColor.Set(WMGraphics.Red);
  1079. ELSE
  1080. terminal.status.fillColor.Set(0E0E0E0FFH);
  1081. END;
  1082. END UpdateStatusLabel;
  1083. PROCEDURE Terminate;
  1084. BEGIN {EXCLUSIVE}
  1085. alive := FALSE; timer.Wakeup;
  1086. AWAIT(dead);
  1087. END Terminate;
  1088. PROCEDURE &Init*(terminal : TerminalComponent);
  1089. BEGIN
  1090. ASSERT(terminal # NIL);
  1091. SELF.terminal := terminal;
  1092. alive := TRUE; dead := FALSE;
  1093. NEW(timer);
  1094. NEW(writer, 1024);
  1095. END Init;
  1096. BEGIN {ACTIVE}
  1097. WHILE alive DO
  1098. UpdateStatusLabel;
  1099. timer.Sleep(UpdateInterval);
  1100. END;
  1101. BEGIN {EXCLUSIVE} dead := TRUE; END;
  1102. END StatusUpdater;
  1103. TYPE
  1104. KillerMsg = OBJECT
  1105. END KillerMsg;
  1106. ProgressWindow = OBJECT(WMComponents.FormWindow);
  1107. PROCEDURE Close*;
  1108. BEGIN
  1109. Close^;
  1110. DecCount
  1111. END Close;
  1112. PROCEDURE Handle*(VAR x : WMMessages.Message);
  1113. BEGIN
  1114. IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) & (x.ext IS KillerMsg) THEN Close
  1115. ELSE Handle^(x)
  1116. END
  1117. END Handle;
  1118. PROCEDURE &New*(vc : WMComponents.VisualComponent; x, y : LONGINT);
  1119. BEGIN
  1120. IncCount;
  1121. Init(vc.bounds.GetWidth(), vc.bounds.GetHeight(), FALSE);
  1122. SetContent(vc);
  1123. SetTitle(Strings.NewString("Progress"));
  1124. WMWindowManager.DefaultAddWindow(SELF);
  1125. END New;
  1126. END ProgressWindow;
  1127. TYPE
  1128. Window* = OBJECT (WMComponents.FormWindow)
  1129. VAR
  1130. terminal : TerminalComponent;
  1131. PROCEDURE GetStartupSize(VAR width, height : LONGINT);
  1132. VAR strings : Strings.StringArray; value : ARRAY 64 OF CHAR; res : LONGINT;
  1133. BEGIN
  1134. width := DefaultWidth; height := DefaultHeight;
  1135. Configuration.Get("Applications.WMV24Component.WindowStartupSize", value, res);
  1136. IF (res = Configuration.Ok) THEN
  1137. Strings.UpperCase(value);
  1138. Strings.TrimWS(value);
  1139. strings := Strings.Split(value, "X");
  1140. IF LEN(strings) = 2 THEN
  1141. Strings.StrToInt(strings[0]^, width);
  1142. Strings.StrToInt(strings[1]^, height);
  1143. END;
  1144. END;
  1145. END GetStartupSize;
  1146. PROCEDURE CreateForm(): WMComponents.VisualComponent;
  1147. VAR panel : WMStandardComponents.Panel; width, height : LONGINT;
  1148. BEGIN
  1149. GetStartupSize(width, height);
  1150. NEW(panel); panel.bounds.SetExtents(width, height); panel.fillColor.Set(0FFFFFFFFH); panel.takesFocus.Set(TRUE);
  1151. NEW(terminal); terminal.alignment.Set(WMComponents.AlignClient);
  1152. panel.AddContent(terminal);
  1153. RETURN panel
  1154. END CreateForm;
  1155. PROCEDURE &New*(c : WMRestorable.Context; context: Commands.Context);
  1156. VAR
  1157. vc : WMComponents.VisualComponent;
  1158. configuration : WMRestorable.XmlElement; string : ARRAY 64 OF CHAR;
  1159. s: POINTER TO ARRAY OF CHAR;
  1160. len: LONGINT;
  1161. BEGIN
  1162. IncCount;
  1163. vc := CreateForm();
  1164. Init(vc.bounds.GetWidth(), vc.bounds.GetHeight(), FALSE);
  1165. SetContent(vc);
  1166. SetTitle(Strings.NewString("BlueTerminal"));
  1167. SetIcon(WMGraphics.LoadImage("WMIcons.tar://WMV24Component.png", TRUE));
  1168. IF c # NIL THEN
  1169. configuration := WMRestorable.GetElement(c, "Configuration");
  1170. IF configuration # NIL THEN
  1171. WMRestorable.LoadString(configuration, "PortSettings", string);
  1172. terminal.settingsEdit.SetAsString(string);
  1173. END;
  1174. WMRestorable.AddByContext(SELF, c);
  1175. Resized(GetWidth(), GetHeight());
  1176. ELSE
  1177. WMWindowManager.DefaultAddWindow(SELF);
  1178. IF context # NIL THEN
  1179. NEW(s, (*context.arg.Pos()+*)context.arg.Available());
  1180. context.arg.SkipWhitespace();
  1181. context.arg.Bytes(s^, 0 (*context.arg.Pos()*), context.arg.Available(), len);
  1182. (* Only automatically open the Com Port if a paramater was passed *)
  1183. IF (len > 0) & (s[0] # 0X) THEN
  1184. terminal.settingsEdit.SetAsString(s^);
  1185. terminal.ToggleOpen(NIL, NIL);
  1186. END;
  1187. END;
  1188. END;
  1189. END New;
  1190. PROCEDURE Close*;
  1191. BEGIN
  1192. T.String("closing window"); T.Ln;
  1193. Close^;
  1194. DecCount
  1195. END Close;
  1196. PROCEDURE Handle*(VAR x : WMMessages.Message);
  1197. VAR configuration : WMRestorable.XmlElement; string : ARRAY 64 OF CHAR;
  1198. BEGIN
  1199. IF (x.msgType = WMMessages.MsgExt) & (x.ext # NIL) THEN
  1200. IF (x.ext IS KillerMsg) THEN
  1201. Close;
  1202. ELSIF (x.ext IS WMRestorable.Storage) THEN
  1203. NEW(configuration); configuration.SetName("Configuration");
  1204. terminal.settingsEdit.GetAsString(string);
  1205. WMRestorable.StoreString(configuration, "PortSettings", string);
  1206. x.ext(WMRestorable.Storage).Add("BlueTerminal", "WMV24Component.Restore", SELF, configuration);
  1207. ELSE
  1208. Handle^(x);
  1209. END;
  1210. ELSE Handle^(x)
  1211. END
  1212. END Handle;
  1213. END Window;
  1214. VAR
  1215. nofWindows : LONGINT;
  1216. timeout: BOOLEAN;
  1217. StrProgressInfo, StrCustomTextView, StrTerminalComponent : Strings.String;
  1218. PROCEDURE ControlKeyDown(flags : SET) : BOOLEAN;
  1219. BEGIN
  1220. RETURN (flags * Inputs.Ctrl # {}) & (flags - Inputs.Ctrl = {});
  1221. END ControlKeyDown;
  1222. PROCEDURE LoadCommandMenu() : Command;
  1223. VAR
  1224. commandList : Command;
  1225. enum: XMLObjects.Enumerator; p: ANY; e: XML.Element;
  1226. PROCEDURE AddCommand(name, value : XML.String);
  1227. VAR c, newCmd : Command;
  1228. BEGIN
  1229. IF (name # NIL) & (value # NIL) THEN
  1230. NEW(newCmd);
  1231. COPY(name^, newCmd.name);
  1232. COPY(value^, newCmd.commandString);
  1233. (* append to command list *)
  1234. c := commandList;
  1235. WHILE (c.next # NIL) DO c := c.next; END;
  1236. c.next := newCmd;
  1237. ELSE
  1238. Show("Command menu definition has errors."); KernelLog.Ln;
  1239. END;
  1240. END AddCommand;
  1241. BEGIN
  1242. NEW(commandList); commandList.next := NIL;
  1243. e := Configuration.GetSection("Applications.WMV24Component.CommandMenu");
  1244. IF (e # NIL) THEN
  1245. enum := e.GetContents();
  1246. WHILE enum.HasMoreElements() DO
  1247. p := enum.GetNext();
  1248. IF p IS XML.Element THEN
  1249. e := p (XML.Element);
  1250. AddCommand(e.GetAttributeValue("name"), e.GetAttributeValue("value"));
  1251. END;
  1252. END;
  1253. END;
  1254. RETURN commandList.next;
  1255. END LoadCommandMenu;
  1256. PROCEDURE InitStrings;
  1257. BEGIN
  1258. StrProgressInfo := Strings.NewString("ProgressInfo");
  1259. StrCustomTextView := Strings.NewString("CustomTextView");
  1260. StrTerminalComponent := Strings.NewString("TerminalComponent");
  1261. END InitStrings;
  1262. PROCEDURE Show(CONST string : ARRAY OF CHAR);
  1263. BEGIN
  1264. KernelLog.String(ModuleName); KernelLog.String(": "); KernelLog.String(string);
  1265. END Show;
  1266. PROCEDURE Restore*(context : WMRestorable.Context);
  1267. VAR window : Window;
  1268. BEGIN
  1269. NEW(window, context, NIL);
  1270. END Restore;
  1271. PROCEDURE Open*(context: Commands.Context);
  1272. VAR window : Window;
  1273. BEGIN
  1274. NEW(window, NIL, context);
  1275. END Open;
  1276. PROCEDURE NewTerminalComponent*(): XML.Element;
  1277. VAR component: TerminalComponent;
  1278. BEGIN NEW(component); RETURN component;
  1279. END NewTerminalComponent;
  1280. PROCEDURE IncCount;
  1281. BEGIN {EXCLUSIVE}
  1282. INC(nofWindows)
  1283. END IncCount;
  1284. PROCEDURE DecCount;
  1285. BEGIN {EXCLUSIVE}
  1286. DEC(nofWindows)
  1287. END DecCount;
  1288. PROCEDURE Timeout;
  1289. BEGIN{EXCLUSIVE}
  1290. timeout := TRUE
  1291. END Timeout;
  1292. PROCEDURE Cleanup;
  1293. VAR die : KillerMsg;
  1294. msg : WMMessages.Message;
  1295. m : WMWindowManager.WindowManager;
  1296. timer: OBJECT VAR timer: Kernel.Timer; BEGIN{ACTIVE} NEW(timer); timer.Sleep(100); Timeout END;
  1297. BEGIN {EXCLUSIVE}
  1298. NEW(die);
  1299. msg.ext := die;
  1300. msg.msgType := WMMessages.MsgExt;
  1301. m := WMWindowManager.GetDefaultManager();
  1302. WHILE nofWindows >0 DO
  1303. m.Broadcast(msg);
  1304. timeout := FALSE; NEW(timer);
  1305. AWAIT (nofWindows = 0) OR timeout;
  1306. END;
  1307. END Cleanup;
  1308. PROCEDURE InitV24;
  1309. VAR res: LONGINT; msg: ARRAY 32 OF CHAR;
  1310. BEGIN
  1311. Commands.Call("V24.Install",{},res,msg); (* auto-initialize V24 in Windows *)
  1312. END InitV24;
  1313. BEGIN
  1314. Modules.InstallTermHandler(Cleanup);
  1315. InitStrings;
  1316. END WMV24Component.
  1317. V24.Install ~
  1318. Serials.Show ~
  1319. SystemTools.Free WMV24Component WMProgressComponents XYModem ~
  1320. WMV24Component.Open ~
  1321. Serials.CloseAllPorts ~