Sage.UDPChatClient.Mod 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. MODULE UDPChatClient; (** AUTHOR "SAGE"; PURPOSE "UDP Chat Client" *)
  2. IMPORT
  3. Base := UDPChatBase, UDP, IP, DNS,
  4. Dates, Strings,
  5. WMStandardComponents, WMComponents, WM := WMWindowManager,
  6. WMDialogs, WMEditors, WMRectangles,
  7. Modules, Texts, UTF8Strings, Inputs, Kernel, Events;
  8. CONST
  9. serverStr = "127.0.0.1";
  10. branchInit = 0;
  11. branchPacketReceive = 1;
  12. branchVersionCheck = 2;
  13. branchPacketHandle = 3;
  14. branchEnd = 4;
  15. branchTerminated = 5;
  16. moduleName = "UDPChatClient";
  17. (* Event classification as in Events.XML *)
  18. EventClass = 3; (* UDP Chat *)
  19. EventSubclass = 3; (* UDP Chat Client *)
  20. (* Window size at application startup *)
  21. WindowWidth = 40 * 12;
  22. WindowHeight = 30 * 12;
  23. TYPE
  24. msg = ARRAY 1500 OF CHAR; (* Maximum allowed message length caused by Network MTU limit *)
  25. String = Strings.String;
  26. Instance = OBJECT
  27. VAR
  28. next: Instance;
  29. chat: ChatWindow;
  30. server: ARRAY 256 OF CHAR;
  31. CRLF: ARRAY 3 OF CHAR;
  32. login: ARRAY 9 OF CHAR;
  33. password, passwordConfirm: ARRAY 33 OF CHAR;
  34. shortName, fullName, eMail: ARRAY 65 OF CHAR;
  35. uin: LONGINT; res: WORD;
  36. dt: Dates.DateTime;
  37. keepAliveTimer: Kernel.MilliTimer;
  38. s: UDP.Socket;
  39. serverIP, ip: IP.Adr;
  40. running, terminated, onLine: BOOLEAN;
  41. str1, str2: ARRAY 256 OF CHAR;
  42. branch, command, seqNum, messageType, inSeqNum, outSeqNum: INTEGER;
  43. senderUin, receiverUin, port, len, receiveBufOffset: LONGINT;
  44. sendBuf-: Base.Buffer;
  45. receiveBuf, message, string: String;
  46. userInfos: Base.List;
  47. userInfo: Base.UserInfo;
  48. ACKReqList: Base.List;
  49. ACKReq: Base.ACKRec;
  50. csa: Texts.CharacterStyleArray;
  51. psa: Texts.ParagraphStyleArray;
  52. PROCEDURE &New*;
  53. BEGIN
  54. (* Chain the previous instance(s) to this new one, for guaranteed cleanup. *)
  55. next := instances;
  56. instances := SELF
  57. END New;
  58. PROCEDURE Finalize;
  59. BEGIN
  60. IF chat # NIL THEN chat.Close END;
  61. running := FALSE;
  62. BEGIN {EXCLUSIVE}
  63. AWAIT (terminated)
  64. END;
  65. FreeInstance (SELF);
  66. END Finalize;
  67. PROCEDURE Client_ACK (seqNum: INTEGER; uin: LONGINT; sendBuf: Base.Buffer;
  68. s: UDP.Socket; ip: IP.Adr);
  69. VAR
  70. res: WORD;
  71. string: String;
  72. BEGIN {EXCLUSIVE}
  73. Base.ClientPacketInit (Base.ACK, seqNum, uin, sendBuf);
  74. string := sendBuf.GetString ();
  75. s.Send (ip, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  76. END Client_ACK;
  77. PROCEDURE Client_NewUserReg (password, shortName, fullName, eMail: ARRAY OF CHAR;
  78. VAR seqNum: INTEGER; sendBuf: Base.Buffer;
  79. s: UDP.Socket; ip: IP.Adr);
  80. VAR
  81. len: LONGINT; res: WORD;
  82. string: String;
  83. BEGIN {EXCLUSIVE}
  84. Base.ClientPacketInit (Base.NEW_USER_REG, seqNum, 0, sendBuf);
  85. NEW (ACKReq);
  86. ACKReq.seqNum := seqNum;
  87. ACKReqList.Add (ACKReq);
  88. INC (seqNum);
  89. len := Strings.Length (password) + 1;
  90. sendBuf.AddInt (len, 2);
  91. sendBuf.Add (password, 0, len, TRUE, res);
  92. len := Strings.Length (shortName) + 1;
  93. sendBuf.AddInt (len, 2);
  94. sendBuf.Add (shortName, 0, len, TRUE, res);
  95. len := Strings.Length (fullName) + 1;
  96. sendBuf.AddInt (len, 2);
  97. sendBuf.Add (fullName, 0, len, TRUE, res);
  98. len := Strings.Length (eMail) + 1;
  99. sendBuf.AddInt (len, 2);
  100. sendBuf.Add (eMail, 0, len, TRUE, res);
  101. string := sendBuf.GetString ();
  102. s.Send (ip, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  103. END Client_NewUserReg;
  104. PROCEDURE Client_Login (password: ARRAY OF CHAR;
  105. VAR seqNum: INTEGER; uin: LONGINT; sendBuf: Base.Buffer;
  106. s: UDP.Socket; ip: IP.Adr);
  107. VAR
  108. len: LONGINT; res: WORD;
  109. string: String;
  110. BEGIN {EXCLUSIVE}
  111. Base.ClientPacketInit (Base.LOGIN, seqNum, uin, sendBuf);
  112. NEW (ACKReq);
  113. ACKReq.seqNum := seqNum;
  114. ACKReqList.Add (ACKReq);
  115. INC (seqNum);
  116. len := Strings.Length (password) + 1;
  117. sendBuf.AddInt (len, 2);
  118. sendBuf.Add (password, 0, len, TRUE, res);
  119. string := sendBuf.GetString ();
  120. s.Send (ip, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  121. END Client_Login;
  122. PROCEDURE Client_InfoReq (userUIN: LONGINT; VAR seqNum: INTEGER;
  123. uin: LONGINT; sendBuf: Base.Buffer;
  124. s: UDP.Socket; ip: IP.Adr);
  125. VAR
  126. res: WORD;
  127. string: String;
  128. BEGIN {EXCLUSIVE}
  129. Base.ClientPacketInit (Base.INFO_REQ, seqNum, uin, sendBuf);
  130. NEW (ACKReq);
  131. ACKReq.seqNum := seqNum;
  132. ACKReqList.Add (ACKReq);
  133. INC (seqNum);
  134. sendBuf.AddInt (userUIN, 4);
  135. string := sendBuf.GetString ();
  136. s.Send (ip, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  137. END Client_InfoReq;
  138. PROCEDURE Client_SendMessage (
  139. userUIN: LONGINT; messageType: INTEGER; message: String;
  140. VAR seqNum: INTEGER; uin: LONGINT; sendBuf: Base.Buffer;
  141. s: UDP.Socket; ip: IP.Adr);
  142. VAR
  143. string: String;
  144. len: LONGINT; res: WORD;
  145. BEGIN {EXCLUSIVE}
  146. Base.ClientPacketInit (Base.SEND_MESSAGE, seqNum, uin, sendBuf);
  147. NEW (ACKReq);
  148. ACKReq.seqNum := seqNum;
  149. ACKReqList.Add (ACKReq);
  150. INC (seqNum);
  151. sendBuf.AddInt (userUIN, 4);
  152. sendBuf.AddInt (messageType, 2);
  153. (*
  154. len := Strings.Length (message^) + 1;
  155. *)
  156. len := LEN (message^);
  157. sendBuf.AddInt (len, 2);
  158. sendBuf.Add (message^, 0, len, TRUE, res);
  159. string := sendBuf.GetString ();
  160. s.Send (serverIP, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  161. END Client_SendMessage;
  162. PROCEDURE Client_SendTextCode (code: String;
  163. VAR seqNum: INTEGER; uin: LONGINT; sendBuf: Base.Buffer;
  164. s: UDP.Socket; ip: IP.Adr);
  165. VAR
  166. string: String;
  167. len: LONGINT; res: WORD;
  168. BEGIN {EXCLUSIVE}
  169. Base.ClientPacketInit (Base.SEND_TEXT_CODE, seqNum, uin, sendBuf);
  170. NEW (ACKReq);
  171. ACKReq.seqNum := seqNum;
  172. ACKReqList.Add (ACKReq);
  173. INC (seqNum);
  174. len := Strings.Length (code^) + 1;
  175. sendBuf.AddInt (len, 2);
  176. sendBuf.Add (code^, 0, len, TRUE, res);
  177. string := sendBuf.GetString ();
  178. s.Send (serverIP, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  179. END Client_SendTextCode;
  180. PROCEDURE Client_KeepAlive (VAR seqNum: INTEGER; uin: LONGINT; sendBuf: Base.Buffer;
  181. s: UDP.Socket; ip: IP.Adr);
  182. VAR
  183. res: WORD;
  184. string: String;
  185. BEGIN {EXCLUSIVE}
  186. Base.ClientPacketInit (Base.KEEP_ALIVE, seqNum, uin, sendBuf);
  187. NEW (ACKReq);
  188. ACKReq.seqNum := seqNum;
  189. ACKReqList.Add (ACKReq);
  190. INC (seqNum);
  191. string := sendBuf.GetString ();
  192. s.Send (ip, Base.serverPort, string^, 0, sendBuf.GetLength (), res);
  193. END Client_KeepAlive;
  194. PROCEDURE FindUserInfo (list: Base.List; uin: LONGINT): Base.UserInfo;
  195. VAR
  196. i: LONGINT;
  197. u: Base.UserInfo;
  198. ptr: ANY;
  199. BEGIN
  200. i := 0;
  201. WHILE i < list.GetCount () DO
  202. ptr := list.GetItem (i);
  203. u := ptr (Base.UserInfo);
  204. IF uin = u.uin THEN
  205. RETURN u;
  206. END;
  207. INC (i);
  208. END;
  209. RETURN NIL;
  210. END FindUserInfo;
  211. PROCEDURE Log (type, code : SHORTINT; msg: ARRAY OF CHAR; showOnKernelLog : BOOLEAN);
  212. VAR message : Events.Message;
  213. BEGIN
  214. COPY(msg, message);
  215. Events.AddEvent(moduleName, type, EventClass, EventSubclass, code, message, showOnKernelLog);
  216. END Log;
  217. BEGIN {ACTIVE}
  218. branch := branchInit;
  219. REPEAT
  220. CASE branch OF
  221. | branchInit:
  222. server := serverStr;
  223. running := FALSE;
  224. terminated := TRUE;
  225. onLine := FALSE;
  226. branch := branchEnd;
  227. csa := Texts.GetCharacterStyleArray ();
  228. psa := Texts.GetParagraphStyleArray ();
  229. res := WMDialogs.QueryString ("Server", server);
  230. IF res = WMDialogs.ResOk THEN
  231. DNS.HostByName (server, serverIP, res);
  232. IF res # DNS.Ok THEN
  233. Log (Events.Error, 0, "host name not found!", TRUE);
  234. serverIP := IP.StrToAdr (server);
  235. IF IP.IsNilAdr (serverIP) THEN
  236. Log (Events.Error, 0, "IP address not valid!", TRUE);
  237. END;
  238. END;
  239. IF ~IP.IsNilAdr (serverIP) THEN
  240. CRLF[0] := 0DX;
  241. CRLF[1] := 0AX;
  242. CRLF[2] := 0X;
  243. NEW (s, UDP.NilPort, res);
  244. NEW (receiveBuf, Base.MaxUDPDataLen);
  245. NEW (sendBuf, 0);
  246. NEW (ACKReqList);
  247. running := TRUE;
  248. terminated := FALSE;
  249. onLine := FALSE;
  250. inSeqNum := -1;
  251. outSeqNum := 1;
  252. res := WMDialogs.Message (WMDialogs.TQuestion, "Chat Client", "Get new User ID?",
  253. {WMDialogs.ResYes, WMDialogs.ResNo});
  254. CASE res OF
  255. | WMDialogs.ResYes:
  256. res := WMDialogs.QueryUserInfo ("Register new user",
  257. shortName, fullName, eMail, password, passwordConfirm);
  258. IF res = WMDialogs.ResOk THEN
  259. IF (shortName # "") &
  260. (password # "") &
  261. (password = passwordConfirm) THEN
  262. Client_NewUserReg (password, shortName, fullName,
  263. eMail, outSeqNum, sendBuf, s, serverIP);
  264. branch := branchPacketReceive;
  265. END;
  266. END;
  267. | WMDialogs.ResNo:
  268. res := WMDialogs.QueryLogin ("Login", login, password);
  269. IF res = WMDialogs.ResOk THEN
  270. Strings.StrToInt (login, uin);
  271. IF uin # 0 THEN
  272. NEW (chat, SELF);
  273. Client_Login (password, outSeqNum, uin,
  274. sendBuf, s, serverIP);
  275. branch := branchPacketReceive;
  276. END;
  277. END;
  278. ELSE
  279. END;
  280. END;
  281. END;
  282. | branchPacketReceive:
  283. IF running THEN
  284. s.Receive (receiveBuf^, 0, Base.MaxUDPDataLen, 1, ip, port, len, res);
  285. IF (res = UDP.Ok) & (len > 0) THEN
  286. receiveBufOffset := 0;
  287. branch := branchVersionCheck;
  288. ELSE
  289. branch := branchPacketReceive;
  290. END;
  291. IF onLine THEN
  292. IF Kernel.Expired (keepAliveTimer) THEN
  293. Client_KeepAlive (outSeqNum, uin, sendBuf, s, serverIP);
  294. Kernel.SetTimer (keepAliveTimer, Base.clientKeepAliveInterval);
  295. END;
  296. END;
  297. ELSE
  298. branch := branchEnd;
  299. END;
  300. | branchVersionCheck:
  301. IF Base.BufGetInt (receiveBuf, receiveBufOffset) = Base.VERSION THEN
  302. branch := branchPacketHandle;
  303. ELSE
  304. branch := branchPacketReceive;
  305. END;
  306. | branchPacketHandle:
  307. command := Base.BufGetInt (receiveBuf, receiveBufOffset);
  308. seqNum := Base.BufGetInt (receiveBuf, receiveBufOffset);
  309. Strings.IntToStr (seqNum, str1);
  310. Strings.Concat (" SeqNum: ", str1, str1);
  311. Strings.Concat (str1, " Command: ", str1);
  312. Strings.IntToStr (uin, str2);
  313. Strings.Concat ("User ID: ", str2, str2);
  314. Strings.Concat (str2, str1, str1);
  315. Base.CommandDecode (command, str2);
  316. Strings.Concat (str1, str2, str1);
  317. Log (Events.Information, 0, str1, FALSE);
  318. IF onLine THEN
  319. CASE command OF
  320. | Base.ACK:
  321. IF Base.SeqNumInACKList (ACKReqList, seqNum, ACKReq) THEN
  322. ACKReqList.Remove (ACKReq);
  323. END;
  324. | Base.INFO_REPLY:
  325. IF Base.SeqNumInACKList (ACKReqList, seqNum, ACKReq) THEN
  326. ACKReqList.Remove (ACKReq);
  327. receiverUin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
  328. userInfo := FindUserInfo (userInfos, receiverUin);
  329. IF userInfo = NIL THEN
  330. NEW (userInfo);
  331. userInfos.Add (userInfo);
  332. userInfo.uin := receiverUin;
  333. END;
  334. string := Base.BufGetString (receiveBuf, receiveBufOffset);
  335. COPY (string^, userInfo.shortName);
  336. Strings.IntToStr (receiverUin, str1);
  337. Strings.Concat ("User with User ID: #", str1, str1);
  338. Strings.Concat (str1, " now known as '", str1);
  339. Strings.Concat (str1, userInfo.shortName, str1);
  340. Strings.Concat (str1, "'", str1);
  341. Strings.Concat (CRLF, str1, str1);
  342. chat.Append (Strings.NewString (str1), csa[8], psa[1]);
  343. END;
  344. ELSE (* CASE *)
  345. IF Base.isNextSeqNum (seqNum, inSeqNum) THEN
  346. inSeqNum := seqNum;
  347. Client_ACK (inSeqNum, uin, sendBuf, s, serverIP);
  348. CASE command OF
  349. | Base.USER_ONLINE:
  350. receiverUin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
  351. Strings.IntToStr (receiverUin, str1);
  352. Strings.Concat ("User with User ID: #", str1, str1);
  353. userInfo := FindUserInfo (userInfos, receiverUin);
  354. IF userInfo = NIL THEN
  355. Client_InfoReq (receiverUin, outSeqNum, uin, sendBuf, s, serverIP);
  356. ELSE
  357. Strings.Concat (str1, " known as '", str1);
  358. Strings.Concat (str1, userInfo.shortName, str1);
  359. Strings.Concat (str1, "'", str1);
  360. END;
  361. Strings.Concat (str1, " is ON-LINE!", str1);
  362. Strings.Concat (CRLF, str1, str1);
  363. chat.Append (Strings.NewString (str1), csa[8], psa[1]);
  364. | Base.USER_OFFLINE:
  365. receiverUin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
  366. Strings.IntToStr (receiverUin, str1);
  367. Strings.Concat ("User with User ID: #", str1, str1);
  368. userInfo := FindUserInfo (userInfos, receiverUin);
  369. IF userInfo # NIL THEN
  370. Strings.Concat (str1, " known as '", str1);
  371. Strings.Concat (str1, userInfo.shortName, str1);
  372. Strings.Concat (str1, "'", str1);
  373. END;
  374. Strings.Concat (str1, " is OFF-LINE!", str1);
  375. Strings.Concat (CRLF, str1, str1);
  376. chat.Append (Strings.NewString (str1), csa[8], psa[1]);
  377. | Base.RECEIVE_MESSAGE:
  378. senderUin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
  379. dt.year := Base.BufGetInt (receiveBuf, receiveBufOffset);
  380. dt.month := Base.BufGetSInt (receiveBuf, receiveBufOffset);
  381. dt.day := Base.BufGetSInt (receiveBuf, receiveBufOffset);
  382. dt.hour := Base.BufGetSInt (receiveBuf, receiveBufOffset);
  383. dt.minute := Base.BufGetSInt (receiveBuf, receiveBufOffset);
  384. dt.second := 0;
  385. messageType := Base.BufGetInt (receiveBuf, receiveBufOffset);
  386. message := Base.BufGetString (receiveBuf, receiveBufOffset);
  387. CASE messageType OF
  388. | Base.MESSAGE_TYPE_NORMAL:
  389. userInfo := FindUserInfo (userInfos, senderUin);
  390. IF userInfo = NIL THEN
  391. Strings.IntToStr (senderUin, str1);
  392. Strings.Concat ("#", str1, str1);
  393. ELSE
  394. COPY (userInfo.shortName, str1);
  395. END;
  396. Strings.Concat (CRLF, str1, str1);
  397. chat.Append (Strings.NewString (str1), csa[1], psa[0]);
  398. Strings.FormatDateTime ("yyyy.mm.dd hh:nn:ss", dt, str1);
  399. Strings.Concat (" (", str1, str1);
  400. Strings.Concat (str1, ")", str1);
  401. chat.Append (Strings.NewString (str1), csa[3], psa[0]);
  402. message := Strings.ConcatToNew (CRLF, message^);
  403. chat.Append (message, csa[0], psa[0]);
  404. | Base.MESSAGE_TYPE_URL:
  405. | Base.MESSAGE_TYPE_DATA:
  406. chat.Append (Strings.NewString ("data"), csa[0], psa[0]);
  407. ELSE
  408. END;
  409. ELSE
  410. END;
  411. END;
  412. END;
  413. branch := branchPacketReceive;
  414. ELSE
  415. IF Base.SeqNumInACKList (ACKReqList, seqNum, ACKReq) THEN
  416. ACKReqList.Remove (ACKReq);
  417. CASE command OF
  418. | Base.LOGIN_REPLY:
  419. NEW (userInfos);
  420. onLine := TRUE;
  421. Kernel.SetTimer (keepAliveTimer, Base.clientKeepAliveInterval);
  422. Client_InfoReq (uin, outSeqNum, uin, sendBuf, s, serverIP);
  423. | Base.NEW_USER_REPLY:
  424. uin := Base.BufGetLInt (receiveBuf, receiveBufOffset);
  425. Strings.IntToStr (uin, login);
  426. Strings.Concat ("Remember your User ID: ", login, str1);
  427. WMDialogs.Information ("New user registered", str1);
  428. res := WMDialogs.QueryLogin ("Login", login, password);
  429. IF res = WMDialogs.ResOk THEN
  430. Strings.StrToInt (login, uin);
  431. IF uin # 0 THEN
  432. NEW (chat, SELF);
  433. Client_Login (password, outSeqNum, uin, sendBuf, s, serverIP);
  434. END;
  435. END;
  436. ELSE
  437. END;
  438. END;
  439. branch := branchPacketReceive;
  440. END;
  441. | branchEnd:
  442. BEGIN {EXCLUSIVE}
  443. terminated := TRUE
  444. END;
  445. branch := branchTerminated;
  446. ELSE
  447. END;
  448. UNTIL branch = branchTerminated;
  449. END Instance;
  450. ChatWindow = OBJECT (WMComponents.FormWindow)
  451. VAR
  452. instance: Instance;
  453. editSend*, editChat*: WMEditors.Editor;
  454. buttonSend: WMStandardComponents.Button;
  455. PROCEDURE Close*;
  456. BEGIN
  457. Close^;
  458. IF instance.onLine THEN
  459. instance.Client_SendTextCode (Strings.NewString("USER_DISCONNECTED"),
  460. instance.outSeqNum, instance.uin, instance.sendBuf, instance.s, instance.serverIP);
  461. END;
  462. END Close;
  463. PROCEDURE KeyEvent*(ucs: LONGINT; flags: SET; keysym: LONGINT);
  464. BEGIN
  465. IF Inputs.Release IN flags THEN RETURN END;
  466. IF (keysym = 0FF0DH) & (flags * Inputs.Ctrl # {}) THEN (* Ctrl + Enter *)
  467. SendClick (SELF, NIL);
  468. END;
  469. END KeyEvent;
  470. PROCEDURE Append (message: String; cs: Texts.CharacterStyle; ps: Texts.ParagraphStyle);
  471. VAR
  472. len, idx: LONGINT;
  473. ucs32: Texts.PUCS32String;
  474. BEGIN
  475. NEW (ucs32, Strings.Length (message^) + 1);
  476. idx := 0;
  477. UTF8Strings.UTF8toUnicode (message^, ucs32^, idx);
  478. editChat.text.AcquireRead;
  479. len := editChat.text.GetLength ();
  480. editChat.text.ReleaseRead;
  481. editChat.text.AcquireWrite;
  482. editChat.text.InsertUCS32 (len, ucs32^);
  483. editChat.text.SetCharacterStyle (len, idx-1, cs);
  484. editChat.text.SetParagraphStyle (len+2, idx-3, ps);
  485. editChat.text.ReleaseWrite;
  486. editChat.tv.End (TRUE, FALSE);
  487. END Append;
  488. PROCEDURE SendClick (sender, data:ANY);
  489. VAR
  490. message: msg;
  491. string: String;
  492. BEGIN
  493. editSend.text.AcquireRead;
  494. (*
  495. NEW (string, editSend.text.GetLength () * 2 + 1); (* GetLength () returns nuber of characters, not bytes!!! *)
  496. editSend.GetAsString (string^); (* text that appears in string are in UTF8 encoding *)
  497. *)
  498. editSend.GetAsString (message);
  499. NEW (string, Strings.Length (message) + 1);
  500. COPY (message, string^);
  501. editSend.text.ReleaseRead;
  502. editSend.SetAsString ("");
  503. IF instance.onLine THEN
  504. instance.Client_SendMessage (
  505. 0, Base.MESSAGE_TYPE_NORMAL, string, instance.outSeqNum, instance.uin,
  506. instance.sendBuf, instance.s, instance.serverIP);
  507. END;
  508. END SendClick;
  509. PROCEDURE CreateForm (): WMComponents.VisualComponent;
  510. VAR
  511. panel, sendPanel, buttonPanel: WMStandardComponents.Panel;
  512. resizerV : WMStandardComponents.Resizer;
  513. manager: WM.WindowManager;
  514. windowStyle: WM.WindowStyle;
  515. BEGIN
  516. manager := WM.GetDefaultManager ();
  517. windowStyle := manager.GetStyle ();
  518. NEW (panel);
  519. panel.bounds.SetExtents (WindowWidth, WindowHeight);
  520. panel.fillColor.Set (windowStyle.bgColor);
  521. panel.takesFocus.Set (FALSE);
  522. NEW(buttonPanel);
  523. buttonPanel.alignment.Set(WMComponents.AlignBottom); buttonPanel.bounds.SetHeight(20);
  524. buttonPanel.bearing.Set(WMRectangles.MakeRect(12, 0, 12, 12));
  525. panel.AddContent(buttonPanel);
  526. NEW (buttonSend); buttonSend.caption.SetAOC ("Send");
  527. buttonSend.alignment.Set(WMComponents.AlignRight);
  528. buttonSend.onClick.Add (SendClick);
  529. buttonPanel.AddContent (buttonSend);
  530. NEW(sendPanel);
  531. sendPanel.alignment.Set(WMComponents.AlignBottom); sendPanel.bounds.SetHeight(5 * 12 + 20);
  532. sendPanel.fillColor.Set(windowStyle.bgColor);
  533. panel.AddContent(sendPanel);
  534. NEW(resizerV);
  535. resizerV.alignment.Set(WMComponents.AlignTop);
  536. resizerV.bounds.SetHeight(4);
  537. sendPanel.AddContent(resizerV);
  538. NEW (editSend);
  539. editSend.tv.defaultTextColor.Set (windowStyle.fgColor);
  540. editSend.tv.defaultTextBgColor.Set (windowStyle.bgColor);
  541. editSend.bearing.Set(WMRectangles.MakeRect(12, 12, 12, 12));
  542. editSend.alignment.Set(WMComponents.AlignClient);
  543. editSend.multiLine.Set (TRUE); editSend.tv.borders.Set (WMRectangles.MakeRect(5, 2, 3, 2));
  544. editSend.tv.showBorder.Set (TRUE);
  545. sendPanel.AddContent (editSend);
  546. NEW (editChat);
  547. editChat.tv.defaultTextColor.Set (windowStyle.fgColor);
  548. editChat.tv.defaultTextBgColor.Set (windowStyle.bgColor);
  549. editChat.bearing.Set(WMRectangles.MakeRect(12, 12, 12,12));
  550. editChat.alignment.Set(WMComponents.AlignClient);
  551. editChat.readOnly.Set (TRUE);
  552. editChat.multiLine.Set (TRUE); editChat.tv.borders.Set (WMRectangles.MakeRect (5, 2, 3, 2));
  553. editChat.tv.showBorder.Set (TRUE);
  554. panel.AddContent(editChat);
  555. RETURN panel
  556. END CreateForm;
  557. PROCEDURE &New *(inst: Instance);
  558. VAR
  559. vc: WMComponents.VisualComponent;
  560. vp: WM.ViewPort;
  561. i, j: LONGINT;
  562. str: ARRAY 128 OF CHAR;
  563. BEGIN
  564. instance := inst;
  565. vc := CreateForm ();
  566. i := vc.bounds.GetWidth ();
  567. j := vc.bounds.GetHeight ();
  568. Init (i, j, FALSE);
  569. SetContent (vc);
  570. vp := WM.GetDefaultView ();
  571. WM.AddWindow (SELF,
  572. (ENTIER (vp.range.r - vp.range.l) - i) DIV 2,
  573. (ENTIER (vp.range.b - vp.range.t) - j) DIV 2);
  574. COPY ("Chat - ", str);
  575. Strings.Append (str, instance.login);
  576. SetTitle (WM.NewString (str));
  577. END New;
  578. END ChatWindow;
  579. VAR
  580. instances: Instance;
  581. (* Remove the instance from the linked list *)
  582. PROCEDURE FreeInstance (free: Instance);
  583. VAR
  584. instance: Instance;
  585. BEGIN
  586. IF free = instances THEN (* the element to free is the first in list *)
  587. instances := instances.next
  588. ELSE
  589. instance := instances;
  590. WHILE (instance # NIL) & (instance.next # free) DO
  591. instance := instance.next
  592. END;
  593. IF instance # NIL THEN (* not yet at the end of the chain: unchain it*)
  594. instance.next := free.next
  595. END
  596. END
  597. END FreeInstance;
  598. PROCEDURE Open*;
  599. VAR
  600. instance: Instance;
  601. BEGIN
  602. NEW (instance);
  603. END Open;
  604. PROCEDURE Cleanup;
  605. BEGIN
  606. WHILE instances # NIL DO
  607. instances.Finalize ();
  608. END
  609. END Cleanup;
  610. BEGIN
  611. Modules.InstallTermHandler (Cleanup);
  612. END UDPChatClient.
  613. System.Free UDPChatClient ~ UDPChatClient.Open ~