ClientAgent.cpp 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. /*
  2. * The contents of this file are subject to the Mozilla Public
  3. * License Version 1.1 (the "License"); you may not use this file
  4. * except in compliance with the License. You may obtain a copy of
  5. * the License at http://www.mozilla.org/MPL/
  6. *
  7. * Software distributed under the License is distributed on an "AS
  8. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9. * implied. See the License for the specific language governing
  10. * rights and limitations under the License.
  11. *
  12. * The Original Code is Vision.
  13. *
  14. * The Initial Developer of the Original Code is The Vision Team.
  15. * Portions created by The Vision Team are
  16. * Copyright (C) 1999, 2000, 2001 The Vision Team. All Rights
  17. * Reserved.
  18. *
  19. * Contributor(s): Wade Majors <wade@ezri.org>
  20. * Rene Gollent
  21. * Todd Lair
  22. * Andrew Bazan
  23. * Jamie Wilkinson
  24. */
  25. #include <Beep.h>
  26. #include <Clipboard.h>
  27. #include <File.h>
  28. #include <MenuItem.h>
  29. #include <Path.h>
  30. #include <PopUpMenu.h>
  31. #include <ScrollView.h>
  32. #include <ctype.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include "ClientWindow.h"
  36. #include "ClientAgent.h"
  37. #include "ClientAgentInputFilter.h"
  38. #include "ClientAgentLogger.h"
  39. #include "HistoryList.h"
  40. #include "ResizeView.h"
  41. #include "RunView.h"
  42. #include "StatusView.h"
  43. #include "Utilities.h"
  44. #include "Theme.h"
  45. #include "Vision.h"
  46. #include "VTextControl.h"
  47. #include "WindowList.h"
  48. const char *ClientAgent::endl ("\1\1\1\1\1");
  49. ClientAgent::ClientAgent (
  50. const char *id_,
  51. const char *serverName_,
  52. const char *myNick_,
  53. BRect frame_)
  54. : BView (
  55. frame_,
  56. id_,
  57. B_FOLLOW_ALL_SIDES,
  58. B_WILL_DRAW | B_FRAME_EVENTS),
  59. fCancelMLPaste(false),
  60. fActiveTheme (vision_app->ActiveTheme()),
  61. fId (id_),
  62. fServerName (serverName_),
  63. fMyNick (myNick_),
  64. fTimeStampState (vision_app->GetBool ("timestamp")),
  65. fIsLogging (vision_app->GetBool ("log_enabled")),
  66. fFrame (frame_)
  67. {
  68. Init();
  69. }
  70. ClientAgent::ClientAgent (
  71. const char *id_,
  72. const char *serverName_,
  73. const char *myNick_,
  74. const BMessenger &sMsgr_,
  75. BRect frame_)
  76. : BView (
  77. frame_,
  78. id_,
  79. B_FOLLOW_ALL_SIDES,
  80. B_WILL_DRAW),
  81. fSMsgr (sMsgr_),
  82. fActiveTheme (vision_app->ActiveTheme()),
  83. fId (id_),
  84. fServerName (serverName_),
  85. fMyNick (myNick_),
  86. fTimeStampState (vision_app->GetBool ("timestamp")),
  87. fIsLogging (vision_app->GetBool ("log_enabled")),
  88. fFrame (frame_)
  89. {
  90. fMyLag = "0.000";
  91. Init();
  92. // force server agent to post a lag meter update immediately
  93. fSMsgr.SendMessage (M_LAG_CHANGED);
  94. }
  95. ClientAgent::~ClientAgent (void)
  96. {
  97. delete fAgentWinItem;
  98. delete fHistory;
  99. }
  100. void
  101. ClientAgent::AttachedToWindow (void)
  102. {
  103. BView::AttachedToWindow();
  104. fActiveTheme->WriteLock();
  105. fActiveTheme->AddView (this);
  106. fActiveTheme->WriteUnlock();
  107. }
  108. void
  109. ClientAgent::DetachedFromWindow (void)
  110. {
  111. BView::DetachedFromWindow ();
  112. fActiveTheme->WriteLock();
  113. fActiveTheme->RemoveView (this);
  114. fActiveTheme->WriteUnlock();
  115. }
  116. void
  117. ClientAgent::AllAttached (void)
  118. {
  119. fMsgr = BMessenger (this);
  120. // we initialize the color constants for the fInput control here
  121. // because BTextControl ignores them prior to being attached for some reason
  122. fActiveTheme->ReadLock();
  123. rgb_color fInputColor (fActiveTheme->ForegroundAt (C_INPUT));
  124. fInput->TextView()->SetFontAndColor (&fActiveTheme->FontAt (F_INPUT), B_FONT_ALL,
  125. &fInputColor);
  126. fInput->TextView()->SetViewColor (fActiveTheme->ForegroundAt (C_INPUT_BACKGROUND));
  127. fInput->TextView()->SetColorSpace (B_RGB32);
  128. fActiveTheme->ReadUnlock();
  129. if (fIsLogging)
  130. {
  131. BMessage logMessage (M_REGISTER_LOGGER);
  132. logMessage.AddString ("name", fId.String());
  133. fSMsgr.SendMessage (&logMessage);
  134. }
  135. }
  136. void
  137. ClientAgent::Show (void)
  138. {
  139. Window()->PostMessage (M_STATUS_CLEAR);
  140. this->fMsgr.SendMessage (M_STATUS_ADDITEMS);
  141. BMessage statusMsg (M_CW_UPDATE_STATUS);
  142. statusMsg.AddPointer ("item", fAgentWinItem);
  143. statusMsg.AddInt32 ("status", WIN_NORMAL_BIT);
  144. statusMsg.AddBool ("hidden", false);
  145. Window()->PostMessage (&statusMsg);
  146. const BRect *agentRect (dynamic_cast<ClientWindow *>(Window())->AgentRect());
  147. if (*agentRect != Frame())
  148. {
  149. ResizeTo (agentRect->Width(), agentRect->Height());
  150. MoveTo (agentRect->left, agentRect->top);
  151. }
  152. // make RunView recalculate itself
  153. fText->Show();
  154. BView::Show();
  155. }
  156. void
  157. ClientAgent::Init (void)
  158. {
  159. SetViewColor (ui_color(B_PANEL_BACKGROUND_COLOR));
  160. fInput = new VTextControl (
  161. BRect (
  162. 0,
  163. fFrame.top, // tmp. will be moved
  164. fFrame.right - fFrame.left - 4,
  165. fFrame.bottom),
  166. "Input", 0, 0,
  167. 0,
  168. B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
  169. fInput->SetDivider (0);
  170. fInput->ResizeToPreferred();
  171. fInput->MoveTo (
  172. 0,
  173. fFrame.bottom - fInput->Frame().Height() - 3);
  174. AddChild (fInput);
  175. fInput->TextView()->AddFilter (new ClientAgentInputFilter (this));
  176. fInput->Invalidate();
  177. fHistory = new HistoryList ();
  178. BRect textrect (
  179. 2,
  180. fFrame.top,
  181. fFrame.right - fFrame.left - 1 - B_V_SCROLL_BAR_WIDTH,
  182. fFrame.bottom - fInput->Frame().Height() - 8);
  183. fText = new RunView (
  184. textrect,
  185. fId.String(),
  186. fActiveTheme,
  187. B_FOLLOW_ALL);
  188. fText->SetClippingName (fId.String());
  189. if (vision_app->GetBool ("timestamp"))
  190. fText->SetTimeStampFormat (vision_app->GetString ("timestamp_format"));
  191. fTextScroll = new BScrollView (
  192. "textscroll",
  193. fText,
  194. B_FOLLOW_ALL,
  195. 0,
  196. false,
  197. true,
  198. B_PLAIN_BORDER);
  199. AddChild (fTextScroll);
  200. }
  201. void
  202. ClientAgent::ScrollRange (float *scrollMin, float *scrollMax) const
  203. {
  204. fTextScroll->ScrollBar(B_VERTICAL)->GetRange (scrollMin, scrollMax);
  205. }
  206. float
  207. ClientAgent::ScrollPos (void) const
  208. {
  209. return fTextScroll->ScrollBar (B_VERTICAL)->Value();
  210. }
  211. void
  212. ClientAgent::SetScrollPos (float value)
  213. {
  214. fTextScroll->ScrollBar (B_VERTICAL)->SetValue(value);
  215. }
  216. void
  217. ClientAgent::SetServerName (const char *name)
  218. {
  219. fServerName = name;
  220. }
  221. void
  222. ClientAgent::SetEditStates (BMenu *menu, bool targetonly)
  223. {
  224. if (menu != NULL)
  225. {
  226. if (targetonly)
  227. {
  228. menu->SetTargetForItems(fInput->TextView());
  229. return;
  230. }
  231. BMenuItem *menuItem (menu->FindItem(S_CW_EDIT_CUT));
  232. int32 start (0), finish (0);
  233. fInput->TextView()->GetSelection(&start, &finish);
  234. if (start == finish)
  235. {
  236. menuItem->SetEnabled (false);
  237. }
  238. else
  239. {
  240. menuItem->SetEnabled (true);
  241. menuItem->SetTarget (fInput->TextView());
  242. }
  243. menuItem = menu->FindItem(S_CW_EDIT_COPY);
  244. if (start == finish)
  245. {
  246. BString string;
  247. // check text display
  248. fText->GetSelectionText(string);
  249. if (string.Length() > 0)
  250. {
  251. menuItem->SetTarget (fInput->TextView());
  252. menuItem->SetEnabled (true);
  253. }
  254. else
  255. {
  256. menuItem->SetEnabled (false);
  257. }
  258. }
  259. else
  260. {
  261. menuItem->SetTarget (fInput->TextView());
  262. menuItem->SetEnabled (true);
  263. }
  264. menuItem = menu->FindItem(S_CW_EDIT_PASTE);
  265. menuItem->SetTarget (fInput->TextView());
  266. BClipboard clipboard("system");
  267. BMessage *clip ((BMessage *)NULL);
  268. if (clipboard.Lock()) {
  269. if ((clip = clipboard.Data()))
  270. if (clip->HasData ("text/plain", B_MIME_TYPE))
  271. menuItem->SetEnabled(true);
  272. else
  273. menuItem->SetEnabled(false);
  274. clipboard.Unlock();
  275. }
  276. menuItem = menu->FindItem(S_CW_EDIT_SELECT_ALL);
  277. if (fInput->TextView()->TextLength() == 0)
  278. menuItem->SetTarget (fText);
  279. else
  280. menuItem->SetTarget (fInput->TextView());
  281. }
  282. }
  283. BString
  284. ClientAgent::FilterCrap (const char *data, bool force)
  285. {
  286. BString outData ("", 440);
  287. int32 theChars (strlen (data));
  288. bool ViewCodes (false);
  289. int32 i (0), j(0);
  290. for (i = 0; i < theChars; ++i)
  291. {
  292. if (data[i] == 3 && !force && !vision_app->GetBool ("stripcolors"))
  293. outData << data[i];
  294. else if (data[i] > 1 && data[i] < 32)
  295. {
  296. if (data[i] == 3)
  297. {
  298. if (ViewCodes)
  299. outData << "[0x03]{";
  300. ++i;
  301. // filter foreground
  302. for (j = 0; j < 2; j++)
  303. if (data[i] >= '0' && data[i] <= '9')
  304. {
  305. if (ViewCodes)
  306. outData << data[i];
  307. ++i;
  308. }
  309. else break;
  310. if (data[i] == ',')
  311. {
  312. if (ViewCodes)
  313. outData << data[i];
  314. ++i;
  315. for (j = 0; j < 2; j++)
  316. if (data[i] >= '0' && data[i] <= '9')
  317. {
  318. if (ViewCodes)
  319. outData << data[i];
  320. ++i;
  321. }
  322. else break;
  323. }
  324. --i;
  325. if (ViewCodes)
  326. outData << "}";
  327. }
  328. else if (ViewCodes)
  329. {
  330. char buffer[16];
  331. sprintf (buffer, "[0x%02x]", data[i]);
  332. outData << buffer;
  333. }
  334. }
  335. else
  336. outData << data[i];
  337. }
  338. return outData;
  339. }
  340. void
  341. ClientAgent::Submit (
  342. const char *buffer,
  343. bool clear,
  344. bool fHistoryAdd)
  345. {
  346. BString cmd;
  347. if (fHistoryAdd)
  348. cmd = fHistory->Submit (buffer);
  349. else
  350. cmd = buffer;
  351. if (clear) fInput->SetText ("");
  352. if (cmd.Length()
  353. && !SlashParser (cmd.String())
  354. && cmd[0] != '/')
  355. {
  356. BString tmp;
  357. // break strings up by 440 lens to ensure user doesn't lose data
  358. while (cmd.Length() > 0)
  359. {
  360. cmd.MoveInto (tmp, 0, Get440Len(cmd.String()));
  361. Parser (tmp.String());
  362. tmp = "";
  363. }
  364. }
  365. }
  366. int32
  367. ClientAgent::TimedSubmit (void *arg)
  368. {
  369. BMessage *msg (reinterpret_cast<BMessage *>(arg));
  370. ClientAgent *agent;
  371. ClientWindow *window;
  372. BString buffer;
  373. int32 i;
  374. bool addtofHistory (true);
  375. if (msg->FindPointer ("agent", reinterpret_cast<void **>(&agent)) != B_OK)
  376. {
  377. printf (":ERROR: no valid agent pointer found in TimedSubmit, bailing...\n");
  378. return -1;
  379. }
  380. if (msg->FindPointer ("window", reinterpret_cast<void **>(&window)) != B_OK)
  381. {
  382. printf (":ERROR: no valid window pointer found in TimedSubmit, bailing...\n");
  383. return -1;
  384. }
  385. bool delay (!msg->HasBool ("delay"));
  386. bool autoexec (msg->FindBool ("autoexec"));
  387. if (autoexec)
  388. addtofHistory = false;
  389. BMessenger agentMsgr (agent);
  390. BMessage submitMsg (M_SUBMIT);
  391. submitMsg.AddBool ("history", addtofHistory);
  392. submitMsg.AddBool ("clear", false);
  393. for (i = 0; (msg->HasString ("data", i)) && (agentMsgr.IsValid()) && (false == agent->CancelMultilineTextPaste()); ++i)
  394. {
  395. BString data;
  396. msg->FindString ("data", i, &data);
  397. // add a space so /'s don't get triggered as commands
  398. if (!autoexec)
  399. data.Prepend (" ");
  400. if (!submitMsg.HasString ("input"))
  401. submitMsg.AddString ("input", data);
  402. else
  403. submitMsg.ReplaceString ("input", data);
  404. // :TODO: wade 020101 move locks to ParseCmd?
  405. if (agentMsgr.IsValid())
  406. {
  407. agentMsgr.SendMessage (&submitMsg);
  408. // A small attempt to appease the
  409. // kicker gods
  410. if (delay)
  411. snooze (600000);
  412. }
  413. }
  414. delete msg;
  415. return 0;
  416. }
  417. void
  418. ClientAgent::PackDisplay (
  419. BMessage *msg,
  420. const char *buffer,
  421. uint32 fore,
  422. uint32 back,
  423. uint32 font)
  424. {
  425. BMessage packed;
  426. packed.AddString ("msgz", buffer);
  427. packed.AddInt32 ("fore", fore);
  428. packed.AddInt32 ("back", back);
  429. packed.AddInt32 ("font", font);
  430. if (msg->HasMessage ("packed"))
  431. msg->ReplaceMessage ("packed", &packed);
  432. else
  433. msg->AddMessage ("packed", &packed);
  434. }
  435. void
  436. ClientAgent::Display (
  437. const char *buffer,
  438. uint32 fore,
  439. uint32 back,
  440. uint32 font)
  441. {
  442. // displays normal text if no color codes are present
  443. // (i.e. if the text has already been filtered by ServerAgent::FilterCrap
  444. ParsemIRCColors (buffer, fore, back, font);
  445. if (IsHidden())
  446. {
  447. BMessage statusMsg (M_CW_UPDATE_STATUS);
  448. statusMsg.AddPointer ("item", fAgentWinItem);
  449. statusMsg.AddInt32 ("status", WIN_PAGESIX_BIT);
  450. Window()->PostMessage (&statusMsg);
  451. }
  452. if (fIsLogging)
  453. {
  454. BMessage logMessage (M_CLIENT_LOG);
  455. logMessage.AddString ("name", fId.String());
  456. logMessage.AddString ("data", buffer);
  457. fSMsgr.SendMessage (&logMessage);
  458. }
  459. }
  460. void
  461. ClientAgent::ParsemIRCColors (
  462. const char *buffer,
  463. uint32 fore,
  464. uint32 back,
  465. uint32 font)
  466. {
  467. int mircFore (fore),
  468. mircBack (back),
  469. mircFont (font),
  470. i (0);
  471. const char *start (NULL);
  472. while (buffer && *buffer)
  473. {
  474. start = buffer;
  475. while (*buffer)
  476. {
  477. if (*buffer != 3)
  478. {
  479. ++buffer;
  480. continue;
  481. }
  482. if (*buffer == 3 && start != buffer)
  483. break;
  484. ++buffer;
  485. if (!isdigit (*buffer))
  486. {
  487. // reset
  488. mircFore = fore;
  489. mircBack = back;
  490. mircFont = font;
  491. break;
  492. }
  493. else
  494. {
  495. // parse colors
  496. mircFore = 0;
  497. for (i = 0; i < 2; i++)
  498. {
  499. if (!isdigit (*buffer))
  500. break;
  501. mircFore = mircFore * 10 + *buffer++ - '0';
  502. }
  503. mircFore = (mircFore % 16) + C_MIRC_WHITE;
  504. if (*buffer == ',')
  505. {
  506. ++buffer;
  507. mircBack = 0;
  508. for (i = 0; i < 2; i++)
  509. {
  510. if (!isdigit (*buffer))
  511. break;
  512. mircBack = mircBack * 10 + *buffer++ - '0';
  513. }
  514. mircBack = (mircFore % 16) + C_MIRC_WHITE;
  515. }
  516. }
  517. // set start to text portion (we have recorded the mirc stuff)
  518. start = buffer;
  519. }
  520. if (buffer > start)
  521. fText->Append (start, buffer - start, mircFore, mircBack, mircFont);
  522. }
  523. }
  524. void
  525. ClientAgent::Parser (const char *)
  526. {
  527. // do nothing
  528. }
  529. void
  530. ClientAgent::TabExpansion (void)
  531. {
  532. // do nothing
  533. }
  534. void
  535. ClientAgent::DroppedFile (BMessage *)
  536. {
  537. // do nothing
  538. }
  539. bool
  540. ClientAgent::SlashParser (const char *data)
  541. {
  542. BString first (GetWord (data, 1).ToUpper());
  543. if (ParseCmd (data))
  544. return true;
  545. return false;
  546. }
  547. void
  548. ClientAgent::UpdateStatus (int32 status)
  549. {
  550. BMessage statusMsg (M_CW_UPDATE_STATUS);
  551. statusMsg.AddPointer ("item", fAgentWinItem);
  552. statusMsg.AddInt32 ("status", status);
  553. Window()->PostMessage (&statusMsg);
  554. if (status == WIN_NICK_BIT)
  555. system_beep(kSoundEventNames[(uint32)seNickMentioned]);
  556. }
  557. void
  558. ClientAgent::MessageReceived (BMessage *msg)
  559. {
  560. switch (msg->what)
  561. {
  562. // 22/8/99: this will now look for "text" to add to the
  563. // fInput view. -jamie
  564. case M_INPUT_FOCUS:
  565. {
  566. if (msg->HasString ("text"))
  567. {
  568. BString newtext;
  569. newtext = fInput->Text();
  570. newtext.Append (msg->FindString("text"));
  571. fInput->SetText (newtext.String());
  572. }
  573. fInput->MakeFocus (true);
  574. // We don't like your silly selecting-on-focus.
  575. fInput->TextView()->Select (fInput->TextView()->TextLength(),
  576. fInput->TextView()->TextLength());
  577. }
  578. break;
  579. case M_CLIENT_QUIT:
  580. {
  581. if (fIsLogging &&
  582. !(msg->HasBool("vision:shutdown") && msg->FindBool("vision:shutdown")))
  583. {
  584. BMessage logMessage (M_UNREGISTER_LOGGER);
  585. logMessage.AddString ("name", fId.String());
  586. fSMsgr.SendMessage(&logMessage);
  587. }
  588. BMessage deathchant (M_CLIENT_SHUTDOWN);
  589. deathchant.AddPointer("agent", this);
  590. fSMsgr.SendMessage (&deathchant);
  591. }
  592. break;
  593. case M_THEME_FOREGROUND_CHANGE:
  594. {
  595. int16 which (msg->FindInt16 ("which"));
  596. if (which == C_INPUT || which == C_INPUT_BACKGROUND)
  597. {
  598. fActiveTheme->ReadLock();
  599. rgb_color fInputColor (fActiveTheme->ForegroundAt (C_INPUT));
  600. fInput->TextView()->SetFontAndColor (&fActiveTheme->FontAt(F_INPUT), B_FONT_ALL,
  601. &fInputColor);
  602. fInput->TextView()->SetViewColor (fActiveTheme->ForegroundAt (C_INPUT_BACKGROUND));
  603. fActiveTheme->ReadUnlock();
  604. fInput->TextView()->Invalidate();
  605. }
  606. }
  607. break;
  608. case M_THEME_FONT_CHANGE:
  609. {
  610. int16 which (msg->FindInt16 ("which"));
  611. if (which == F_INPUT)
  612. {
  613. fActiveTheme->ReadLock();
  614. rgb_color fInputColor (fActiveTheme->ForegroundAt (C_INPUT));
  615. fInput->TextView()->SetFontAndColor (&fActiveTheme->FontAt (F_INPUT), B_FONT_ALL,
  616. &fInputColor);
  617. fActiveTheme->ReadUnlock();
  618. Invalidate();
  619. }
  620. }
  621. break;
  622. case M_STATE_CHANGE:
  623. {
  624. if (msg->HasBool ("bool"))
  625. {
  626. bool shouldStamp (vision_app->GetBool ("timestamp"));
  627. if (fTimeStampState != shouldStamp)
  628. {
  629. if ((fTimeStampState = shouldStamp))
  630. fText->SetTimeStampFormat (vision_app->GetString ("timestamp_format"));
  631. else
  632. fText->SetTimeStampFormat (NULL);
  633. }
  634. bool shouldLog = vision_app->GetBool ("log_enabled");
  635. if (fIsLogging != shouldLog)
  636. {
  637. if ((fIsLogging = shouldLog))
  638. {
  639. BMessage logMessage (M_REGISTER_LOGGER);
  640. logMessage.AddString ("name", fId.String());
  641. fSMsgr.SendMessage (&logMessage);
  642. }
  643. else
  644. {
  645. BMessage logMessage (M_UNREGISTER_LOGGER);
  646. logMessage.AddString ("name", fId.String());
  647. fSMsgr.SendMessage (&logMessage);
  648. }
  649. }
  650. }
  651. else if (msg->HasBool ("string"))
  652. {
  653. BString which (msg->FindString ("which"));
  654. if (which == "timestamp_format")
  655. fText->SetTimeStampFormat (vision_app->GetString ("timestamp_format"));
  656. }
  657. }
  658. break;
  659. case M_SUBMIT_INPUT:
  660. {
  661. fCancelMLPaste = false;
  662. int32 which (0);
  663. msg->FindInt32 ("which", &which);
  664. if (msg->HasPointer ("invoker"))
  665. {
  666. BInvoker *invoker (NULL);
  667. msg->FindPointer ("invoker", reinterpret_cast<void **>(&invoker));
  668. delete invoker;
  669. }
  670. switch (which)
  671. {
  672. case PASTE_CANCEL:
  673. break;
  674. case PASTE_MULTI:
  675. case PASTE_MULTI_NODELAY:
  676. {
  677. BMessage *buffer (new BMessage (*msg));
  678. thread_id tid;
  679. // if there is some text in the input control already, submit it before
  680. // starting the timed paste
  681. if (fInput->TextView()->TextLength() != 0)
  682. {
  683. BString inputData (fInput->TextView()->Text());
  684. Submit(inputData.String(), true, true);
  685. }
  686. buffer->AddPointer ("agent", this);
  687. buffer->AddPointer ("window", Window());
  688. if (which == PASTE_MULTI_NODELAY)
  689. buffer->AddBool ("delay", false);
  690. tid = spawn_thread (
  691. TimedSubmit,
  692. "Timed Submit",
  693. B_LOW_PRIORITY,
  694. buffer);
  695. resume_thread (tid);
  696. }
  697. break;
  698. case PASTE_SINGLE:
  699. {
  700. BString buffer;
  701. for (int32 i = 0; msg->HasString ("data", i); ++i)
  702. {
  703. const char *data;
  704. msg->FindString ("data", i, &data);
  705. buffer += (i ? " " : "");
  706. buffer += data;
  707. }
  708. int32 start, finish;
  709. if (msg->FindInt32 ("selstart", &start) == B_OK)
  710. {
  711. msg->FindInt32 ("selend", &finish);
  712. if (start != finish)
  713. fInput->TextView()->Delete (start, finish);
  714. if ((start == 0) && (finish == 0))
  715. {
  716. fInput->TextView()->Insert (fInput->TextView()->TextLength(),
  717. buffer.String(), buffer.Length());
  718. fInput->TextView()->Select (fInput->TextView()->TextLength(),
  719. fInput->TextView()->TextLength());
  720. }
  721. else
  722. {
  723. fInput->TextView()->Insert (start, buffer.String(), buffer.Length());
  724. fInput->TextView()->Select (start + buffer.Length(),
  725. start + buffer.Length());
  726. }
  727. }
  728. else
  729. {
  730. fInput->TextView()->Insert (buffer.String());
  731. fInput->TextView()->Select (fInput->TextView()->TextLength(),
  732. fInput->TextView()->TextLength());
  733. }
  734. fInput->TextView()->ScrollToSelection();
  735. }
  736. break;
  737. default:
  738. break;
  739. }
  740. }
  741. break;
  742. case M_PREVIOUS_INPUT:
  743. {
  744. fHistory->PreviousBuffer (fInput);
  745. }
  746. break;
  747. case M_NEXT_INPUT:
  748. {
  749. fHistory->NextBuffer (fInput);
  750. }
  751. break;
  752. case M_SUBMIT:
  753. {
  754. const char *buffer (NULL);
  755. bool clear (true),
  756. add2history (true);
  757. msg->FindString ("input", &buffer);
  758. if (msg->HasBool ("clear"))
  759. msg->FindBool ("clear", &clear);
  760. if (msg->HasBool ("history"))
  761. msg->FindBool ("history", &add2history);
  762. Submit (buffer, clear, add2history);
  763. }
  764. break;
  765. case M_LAG_CHANGED:
  766. {
  767. msg->FindString ("lag", &fMyLag);
  768. if (!IsHidden())
  769. vision_app->pClientWin()->pStatusView()->SetItemValue (STATUS_LAG, fMyLag.String());
  770. }
  771. break;
  772. case M_DISPLAY:
  773. {
  774. const char *buffer;
  775. for (int32 i = 0; msg->HasMessage ("packed", i); ++i)
  776. {
  777. BMessage packed;
  778. msg->FindMessage ("packed", i, &packed);
  779. packed.FindString ("msgz", &buffer);
  780. Display (buffer, packed.FindInt32 ("fore"), packed.FindInt32 ("back"), packed.FindInt32 ("font"));
  781. }
  782. }
  783. break;
  784. case M_CHANNEL_MSG:
  785. {
  786. BString theNick;
  787. const char *theMessage (NULL);
  788. bool hasNick (false);
  789. bool isAction (false);
  790. BString knownAs;
  791. msg->FindString("nick", &theNick);
  792. msg->FindString("msgz", &theMessage);
  793. BString tempString;
  794. BString nickString;
  795. if (theMessage[0] == '\1')
  796. {
  797. BString aMessage (theMessage);
  798. aMessage.RemoveFirst ("\1ACTION ");
  799. aMessage.RemoveLast ("\1");
  800. tempString = " ";
  801. tempString += aMessage;
  802. tempString += "\n";
  803. nickString = "* ";
  804. nickString += theNick;
  805. isAction = true;
  806. }
  807. else
  808. {
  809. Display ("<", theNick == fMyNick ? C_MYNICK : C_NICK);
  810. Display (theNick.String(), C_NICKDISPLAY);
  811. Display (">", theNick == fMyNick ? C_MYNICK : C_NICK);
  812. tempString += " ";
  813. tempString += theMessage;
  814. tempString += '\n';
  815. }
  816. // scan for presence of nickname, highlight if present
  817. if (theNick != fMyNick)
  818. FirstKnownAs (tempString, knownAs, &hasNick);
  819. tempString.Prepend (nickString);
  820. int32 dispColor = C_TEXT;
  821. if (hasNick)
  822. {
  823. BWindow *window (NULL);
  824. dispColor = C_MYNICK;
  825. if ((window = Window()) != NULL && !window->IsActive())
  826. system_beep(kSoundEventNames[(uint32)seNickMentioned]);
  827. }
  828. else if (isAction)
  829. dispColor = C_ACTION;
  830. Display (tempString.String(), dispColor);
  831. }
  832. break;
  833. case M_CHANGE_NICK:
  834. {
  835. const char *oldNick (NULL);
  836. msg->FindString ("oldnick", &oldNick);
  837. if (fMyNick.ICompare (oldNick) == 0)
  838. fMyNick = msg->FindString ("newnick");
  839. BMessage display;
  840. if (msg->FindMessage ("display", &display) == B_NO_ERROR)
  841. ClientAgent::MessageReceived (&display);
  842. }
  843. break;
  844. case B_ESCAPE:
  845. fCancelMLPaste = true;
  846. break;
  847. case M_DCC_COMPLETE:
  848. {
  849. /// set up ///
  850. BString nick,
  851. file,
  852. size,
  853. type,
  854. completionMsg ("[@] "),
  855. fAck;
  856. int32 rate,
  857. xfersize;
  858. bool completed (true);
  859. msg->FindString ("nick", &nick);
  860. msg->FindString ("file", &file);
  861. msg->FindString ("size", &size);
  862. msg->FindString ("type", &type);
  863. msg->FindInt32 ("transferred", &xfersize);
  864. msg->FindInt32 ("transferRate", &rate);
  865. BPath pFile (file.String());
  866. fAck << xfersize;
  867. if (size.ICompare (fAck))
  868. completed = false;
  869. /// send mesage ///
  870. if (completed)
  871. completionMsg << S_CLIENT_DCC_SUCCESS;
  872. else completionMsg << S_CLIENT_DCC_FAILED;
  873. if (type == "SEND")
  874. completionMsg << S_CLIENT_DCC_SENDTYPE << pFile.Leaf() << S_CLIENT_DCC_TO;
  875. else completionMsg << S_CLIENT_DCC_RECVTYPE << pFile.Leaf() << S_CLIENT_DCC_FROM;
  876. completionMsg << nick << " (";
  877. if (!completed)
  878. completionMsg << fAck << "/";
  879. completionMsg << size << S_CLIENT_DCC_SIZE_UNITS "), ";
  880. completionMsg << rate << S_CLIENT_DCC_SPEED_UNITS "\n";
  881. Display (completionMsg.String(), C_CTCP_RPY);
  882. }
  883. break;
  884. default:
  885. BView::MessageReceived (msg);
  886. }
  887. }
  888. const BString &
  889. ClientAgent::Id (void) const
  890. {
  891. return fId;
  892. }
  893. int32
  894. ClientAgent::FirstKnownAs (
  895. const BString &data,
  896. BString &result,
  897. bool *me) const
  898. {
  899. BString myAKA (vision_app->GetString ("alsoKnownAs"));
  900. int32 hit (data.Length()),
  901. i,
  902. place;
  903. BString target;
  904. if ((place = FirstSingleKnownAs (data, fMyNick)) != B_ERROR)
  905. {
  906. result = fMyNick;
  907. hit = place;
  908. *me = true;
  909. }
  910. for (i = 1; (target = GetWord (myAKA.String(), i)) != "-9z99"; ++i)
  911. {
  912. if ((place = FirstSingleKnownAs (data, target)) != B_ERROR
  913. && place < hit)
  914. {
  915. result = target;
  916. hit = place;
  917. *me = true;
  918. }
  919. }
  920. return hit < data.Length() ? hit : B_ERROR;
  921. }
  922. int32
  923. ClientAgent::FirstSingleKnownAs (const BString &data, const BString &target) const
  924. {
  925. int32 place;
  926. if ((place = data.IFindFirst (target)) != B_ERROR
  927. && (place == 0
  928. || isspace (data[place - 1])
  929. || ispunct (data[place - 1]))
  930. && (place + target.Length() == data.Length()
  931. || isspace (data[place + target.Length()])
  932. || ispunct (data[place + target.Length()])
  933. || (int)data[place + target.Length()] <= 0xa)) // null or newline
  934. return place;
  935. return B_ERROR;
  936. }
  937. void
  938. ClientAgent::AddSend (BMessage *msg, const char *buffer) const
  939. {
  940. if (strcmp (buffer, endl) == 0)
  941. {
  942. if (fSMsgr.IsValid())
  943. fSMsgr.SendMessage (msg);
  944. msg->MakeEmpty();
  945. }
  946. else
  947. msg->AddString ("data", buffer);
  948. }
  949. void
  950. ClientAgent::AddSend (BMessage *msg, const BString &buffer) const
  951. {
  952. AddSend (msg, buffer.String());
  953. }
  954. void
  955. ClientAgent::AddSend (BMessage *msg, int32 value) const
  956. {
  957. BString buffer;
  958. buffer << value;
  959. AddSend (msg, buffer.String());
  960. }
  961. void
  962. ClientAgent::ChannelMessage (
  963. const char *msgz,
  964. const char *nick,
  965. const char *ident,
  966. const char *address)
  967. {
  968. BMessage msg (M_CHANNEL_MSG);
  969. msg.AddString ("msgz", msgz);
  970. if (nick)
  971. msg.AddString ("nick", nick);
  972. if (ident)
  973. msg.AddString ("ident", ident);
  974. if (address)
  975. msg.AddString ("address", address);
  976. fMsgr.SendMessage (&msg);
  977. }
  978. void
  979. ClientAgent::ActionMessage (
  980. const char *msgz,
  981. const char *nick)
  982. {
  983. BMessage actionSend (M_SERVER_SEND);
  984. AddSend (&actionSend, "PRIVMSG ");
  985. AddSend (&actionSend, fId);
  986. AddSend (&actionSend, " :\1ACTION ");
  987. AddSend (&actionSend, msgz);
  988. AddSend (&actionSend, "\1");
  989. AddSend (&actionSend, endl);
  990. BString theAction ("\1ACTION ");
  991. theAction += msgz;
  992. theAction += "\1";
  993. ChannelMessage (theAction.String(), nick);
  994. }
  995. void
  996. ClientAgent::CTCPAction (BString theTarget, BString theMsg)
  997. {
  998. BString theCTCP (GetWord (theMsg.String(), 1).ToUpper()),
  999. theRest (RestOfString (theMsg.String(), 2)),
  1000. tempString ("[CTCP->");
  1001. tempString += theTarget;
  1002. tempString += "] ";
  1003. tempString += theCTCP;
  1004. if (theRest != "-9z99")
  1005. {
  1006. tempString += " ";
  1007. tempString += theRest;
  1008. tempString += '\n';
  1009. }
  1010. else
  1011. tempString += '\n';
  1012. Display (tempString.String(), C_CTCP_REQ, C_BACKGROUND, F_SERVER);
  1013. }