OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4ChatDlg.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2007-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // IRC client dialog
17 
18 #include "C4Include.h"
19 #include "gui/C4ChatDlg.h"
20 
21 #include "game/C4Application.h"
23 #include "gui/C4MessageInput.h"
24 #include "lib/C4InputValidation.h"
25 #include "network/C4Network2IRC.h"
26 
27 /* C4ChatControl::ChatSheet::NickItem */
28 
29 C4ChatControl::ChatSheet::NickItem::NickItem(class C4Network2IRCUser *pByUser) : pStatusIcon(nullptr), pNameLabel(nullptr), fFlaggedExisting(false), iStatus(0)
30 {
31  // create elements - will be positioned when resized
32  C4Rect rcDefault(0,0,10,10);
33  AddElement(pStatusIcon = new C4GUI::Icon(rcDefault, C4GUI::Ico_None));
34  AddElement(pNameLabel = new C4GUI::Label("", rcDefault, ALeft, C4GUI_CaptionFontClr, nullptr, false, false, false));
35  // set height (pos and width set when added to the list)
37  rcBounds.Set(0,0, 100,pUseFont->GetLineHeight());
38  // initial update
39  Update(pByUser);
40 }
41 
43 {
44  typedef C4GUI::Window ParentClass;
45  ParentClass::UpdateOwnPos();
46  // reposition elements
47  if (pStatusIcon && pNameLabel)
48  {
50  pStatusIcon->SetBounds(caMain.GetFromLeft(caMain.GetInnerHeight()));
51  pNameLabel->SetBounds(caMain.GetAll());
52  }
53 }
54 
56 {
57  // set status icon
58  const char *szPrefix = pByUser->getPrefix();
59  if (!szPrefix) szPrefix = "";
60  C4GUI::Icons eStatusIcon;
61  switch (*szPrefix)
62  {
63  case '!': eStatusIcon = C4GUI::Ico_Rank1; iStatus = 4; break;
64  case '@': eStatusIcon = C4GUI::Ico_Rank2; iStatus = 3; break;
65  case '%': eStatusIcon = C4GUI::Ico_Rank3; iStatus = 2; break;
66  case '+': eStatusIcon = C4GUI::Ico_AddPlr; iStatus = 1; break;
67  case '\0': eStatusIcon = C4GUI::Ico_Player; iStatus = 0; break;
68  default: eStatusIcon = C4GUI::Ico_Player; iStatus = 0; break;
69  }
70  pStatusIcon->SetIcon(eStatusIcon);
71  // set name
72  pNameLabel->SetText(pByUser->getName());
73  // tooltip is status+name
74  SetToolTip(FormatString("%s%s", szPrefix, pByUser->getName()).getData());
75 }
76 
78 {
79  const auto *pNickItem1 = static_cast<const NickItem *>(pEl1);
80  const auto *pNickItem2 = static_cast<const NickItem *>(pEl2);
81  int32_t s1 = pNickItem1->GetStatus(), s2 = pNickItem2->GetStatus();
82  if (s1 != s2) return s1 - s2;
83  return stricmp(pNickItem2->GetNick(), pNickItem1->GetNick());
84 }
85 
86 
87 /* C4ChatControl::ChatSheet */
88 
89 C4ChatControl::ChatSheet::ChatSheet(C4ChatControl *pChatControl, const char *szTitle, const char *szIdent, SheetType eType)
90  : C4GUI::Tabular::Sheet(szTitle, C4Rect(0,0,10,10), C4GUI::Ico_None, true, false), pChatControl(pChatControl), pNickList(nullptr), pInputLbl(nullptr), iBackBufferIndex(-1), eType(eType), fHasUnread(false)
91 {
92  if (szIdent) sIdent.Copy(szIdent);
93  // create elements - positioned later
94  C4Rect rcDefault(0,0,10,10);
95  pChatBox = new C4GUI::TextWindow(rcDefault,0,0,0,100,4096," ",false,nullptr,0,true);
96  pChatBox->SetDecoration(false, false, nullptr, false);
97  AddElement(pChatBox);
98  if (eType == CS_Channel)
99  {
100  pNickList = new C4GUI::ListBox(rcDefault);
101  pNickList->SetDecoration(false, nullptr, true, false);
102  pNickList->SetSelectionDblClickFn(new C4GUI::CallbackHandler<C4ChatControl::ChatSheet>(this, &C4ChatControl::ChatSheet::OnNickDblClick));
103  AddElement(pNickList);
104  }
105  if (eType != CS_Server)
106  pInputLbl = new C4GUI::WoodenLabel(LoadResStr("IDS_DLG_CHAT"), rcDefault, C4GUI_CaptionFontClr, &::GraphicsResource.TextFont);
107  pInputEdit = new C4GUI::CallbackEdit<C4ChatControl::ChatSheet>(rcDefault, this, &C4ChatControl::ChatSheet::OnChatInput);
108  pInputEdit->SetToolTip(LoadResStr("IDS_DLGTIP_CHAT"));
109  if (pInputLbl)
110  {
111  pInputLbl->SetToolTip(LoadResStr("IDS_DLGTIP_CHAT"));
112  pInputLbl->SetClickFocusControl(pInputEdit);
113  AddElement(pInputLbl);
114  }
115  AddElement(pInputEdit);
116  // key bindings
117  pKeyHistoryUp = new C4KeyBinding(C4KeyCodeEx(K_UP ), "ChatHistoryUp" , KEYSCOPE_Gui, new C4GUI::DlgKeyCBEx<C4ChatControl::ChatSheet, bool>(*this, true , &C4ChatControl::ChatSheet::KeyHistoryUpDown), C4CustomKey::PRIO_CtrlOverride);
118  pKeyHistoryDown= new C4KeyBinding(C4KeyCodeEx(K_DOWN), "ChatHistoryDown", KEYSCOPE_Gui, new C4GUI::DlgKeyCBEx<C4ChatControl::ChatSheet, bool>(*this, false, &C4ChatControl::ChatSheet::KeyHistoryUpDown), C4CustomKey::PRIO_CtrlOverride);
119 }
120 
121 C4ChatControl::ChatSheet::~ChatSheet()
122 {
123  delete pKeyHistoryUp;
124  delete pKeyHistoryDown;
125 }
126 
127 void C4ChatControl::ChatSheet::UpdateSize()
128 {
129  // parent update
130  typedef C4GUI::Window ParentClass;
131  ParentClass::UpdateSize();
132  // position child elements
134  C4GUI::ComponentAligner caChat(caMain.GetFromBottom(C4GUI::Edit::GetDefaultEditHeight()), 0,0);
135  if (pNickList) pNickList->SetBounds(caMain.GetFromRight(std::max<int32_t>(caMain.GetInnerWidth()/5, 100)));
136  pChatBox->SetBounds(caMain.GetAll());
137  if (pInputLbl) pInputLbl->SetBounds(caChat.GetFromLeft(40));
138  pInputEdit->SetBounds(caChat.GetAll());
139 }
140 
141 void C4ChatControl::ChatSheet::OnShown(bool fByUser)
142 {
143  ResetUnread();
144  if (fByUser)
145  {
146  Update(true);
147  pChatControl->UpdateTitle();
148  }
149 }
150 
151 C4GUI::Edit::InputResult C4ChatControl::ChatSheet::OnChatInput(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
152 {
154  // get edit text
155  const char *szInputText = pInputEdit->GetText();
156  // no input?
157  if (!szInputText || !*szInputText)
158  {
159  // do some error sound then
160  DoError(nullptr);
161  }
162  else
163  {
164  // remember in history
165  ::MessageInput.StoreBackBuffer(szInputText);
166  // forward to chat control for processing
167  if (!pChatControl->ProcessInput(szInputText, this)) eResult = C4GUI::Edit::IR_Abort;
168  }
169  // clear edit field after text has been processed
170  pInputEdit->SelectAll(); pInputEdit->DeleteSelection();
171  // reset backbuffer-index of chat history
172  iBackBufferIndex = -1;
173  // OK, on we go
174  return eResult;
175 }
176 
177 bool C4ChatControl::ChatSheet::KeyHistoryUpDown(bool fUp)
178 {
179  // chat input only
180  if (!IsFocused(pInputEdit)) return false;
181  pInputEdit->SelectAll(); pInputEdit->DeleteSelection();
182  const char *szPrevInput = ::MessageInput.GetBackBuffer(fUp ? (++iBackBufferIndex) : (--iBackBufferIndex));
183  if (!szPrevInput || !*szPrevInput)
184  iBackBufferIndex = -1;
185  else
186  {
187  pInputEdit->InsertText(szPrevInput, true);
188  pInputEdit->SelectAll();
189  }
190  return true;
191 }
192 
193 void C4ChatControl::ChatSheet::OnNickDblClick(class C4GUI::Element *pEl)
194 {
195  if (!pEl) return;
196  NickItem *pNickItem = static_cast<NickItem *>(pEl);
197  pChatControl->OpenQuery(pNickItem->GetNick(), true, nullptr);
198 }
199 
200 void C4ChatControl::ChatSheet::AddTextLine(const char *szText, uint32_t dwClr)
201 {
202  // strip stuff that would confuse Clonk
203  StdStrBuf sText; sText.Copy(szText);
204  for (char c='\x01'; c<' '; ++c)
205  sText.ReplaceChar(c, ' ');
206  // convert incoming Windows-1252
207  sText.EnsureUnicode();
208  // add text line to chat box
209  CStdFont *pUseFont = &::GraphicsResource.TextFont;
210  pChatBox->AddTextLine(sText.getData(), pUseFont, dwClr, true, false);
211  pChatBox->ScrollToBottom();
212  // sheet now has unread messages if not selected
213  if (!fHasUnread && !IsActiveSheet())
214  {
215  fHasUnread = true;
217  }
218 }
219 
220 void C4ChatControl::ChatSheet::ResetUnread()
221 {
222  // mark messages as read
223  if (fHasUnread)
224  {
225  fHasUnread = false;
226  SetCaptionColor();
227  }
228 }
229 
230 void C4ChatControl::ChatSheet::DoError(const char *szError)
231 {
232  if (szError)
233  {
234  AddTextLine(szError, C4GUI_ErrorFontClr);
235  }
236  C4GUI::GUISound("UI::Error");
237 }
238 
239 void C4ChatControl::ChatSheet::Update(bool fLock)
240 {
241  // lock IRC client data if desired
242  if (fLock)
243  {
244  CStdLock Lock(pChatControl->getIRCClient()->getCSec());
245  Update(false);
246  return;
247  }
248  // only channels need updates
249  if (eType == CS_Channel)
250  {
251  C4Network2IRCChannel *pIRCChan = pChatControl->getIRCClient()->getChannel(GetIdent());
252  if (pIRCChan)
253  {
254  // update user list (if not locked, becuase it's being received)
255  if (!pIRCChan->isUsersLocked()) UpdateUsers(pIRCChan->getUsers());
256  // update topic
257  const char *szTopic = pIRCChan->getTopic();
258  sChatTitle.Format("%s%s%s", sIdent.getData(), szTopic ? ": " : "", szTopic ? szTopic : "");
259  sChatTitle.EnsureUnicode();
260  }
261  }
262 }
263 
264 void C4ChatControl::ChatSheet::UpdateUsers(C4Network2IRCUser *pUsers)
265 {
266  NickItem *pNickItem, *pNextNickItem;
267  // update existing users
268  for (; pUsers; pUsers = pUsers->getNext())
269  {
270  if ((pNickItem = GetNickItem(pUsers->getName())))
271  {
272  pNickItem->Update(pUsers);
273  }
274  else
275  {
276  // new user!
277  pNickItem = new NickItem(pUsers);
278  pNickList->AddElement(pNickItem);
279  }
280  pNickItem->SetFlaggedExisting(true);
281  }
282  // remove left users
283  pNextNickItem = GetFirstNickItem();
284  while ((pNickItem = pNextNickItem))
285  {
286  pNextNickItem = GetNextNickItem(pNickItem);
287  if (!pNickItem->IsFlaggedExisting())
288  {
289  // this user left
290  delete pNickItem;
291  }
292  else
293  {
294  // user didn't leave; reset flag for next check
295  pNickItem->SetFlaggedExisting(false);
296  }
297  }
298  // sort the rest
299  pNickList->SortElements(&NickItem::SortFunc, nullptr);
300 }
301 
302 void C4ChatControl::ChatSheet::UserClose()
303 {
304  typedef C4GUI::Tabular::Sheet ParentClass;
305  switch (eType)
306  {
307  case CS_Server:
308  // closing server window? Always forward to control
309  pChatControl->UserQueryQuit();
310  break;
311 
312  case CS_Channel:
313  // channel: Send part. Close done by server.
314  pChatControl->getIRCClient()->Part(sIdent.getData());
315  break;
316 
317  case CS_Query:
318  // query: Always allow simple close
319  ParentClass::UserClose();
320  break;
321  }
322 }
323 
324 C4ChatControl::ChatSheet::NickItem *C4ChatControl::ChatSheet::GetNickItem(const char *szByNick)
325 {
326  // find by name
327  for (NickItem *pNickItem = GetFirstNickItem(); pNickItem; pNickItem = GetNextNickItem(pNickItem))
328  if (SEqualNoCase(pNickItem->GetNick(), szByNick))
329  return pNickItem;
330  // not found
331  return nullptr;
332 }
333 
334 
335 /* C4ChatControl */
336 
337 C4ChatControl::C4ChatControl(C4Network2IRCClient *pnIRCClient) : C4GUI::Window(), pTitleChangeBC(nullptr), pIRCClient(pnIRCClient), fInitialMessagesReceived(false)
338 {
339  // create elements - positioned later
340  C4Rect rcDefault(0,0,10,10);
341  // main tabular tabs between chat components (login and channels)
342  pTabMain = new C4GUI::Tabular(rcDefault, C4GUI::Tabular::tbNone);
343  pTabMain->SetDrawDecoration(false);
344  pTabMain->SetSheetMargin(0);
345  AddElement(pTabMain);
346  C4GUI::Tabular::Sheet *pSheetLogin = pTabMain->AddSheet(nullptr);
347  C4GUI::Tabular::Sheet *pSheetChats = pTabMain->AddSheet(nullptr);
348  // login sheet
349  CStdFont *pUseFont = &::GraphicsResource.TextFont;
350  pSheetLogin->AddElement(pLblLoginNick = new C4GUI::Label(LoadResStr("IDS_CTL_NICK"), rcDefault, ALeft, C4GUI_CaptionFontClr, pUseFont, false, true));
351  pSheetLogin->AddElement(pEdtLoginNick = new C4GUI::CallbackEdit<C4ChatControl>(rcDefault, this, &C4ChatControl::OnLoginDataEnter));
352  pSheetLogin->AddElement(pLblLoginPass = new C4GUI::Label(LoadResStr("IDS_CTL_PASSWORDOPTIONAL"), rcDefault, ALeft, C4GUI_CaptionFontClr, pUseFont, false, true));
353  pSheetLogin->AddElement(pEdtLoginPass = new C4GUI::CallbackEdit<C4ChatControl>(rcDefault, this, &C4ChatControl::OnLoginDataEnter));
354  pEdtLoginPass->SetPasswordMask('*');
355  pSheetLogin->AddElement(pLblLoginRealName = new C4GUI::Label(LoadResStr("IDS_CTL_REALNAME"), rcDefault, ALeft, C4GUI_CaptionFontClr, pUseFont, false, true));
356  pSheetLogin->AddElement(pEdtLoginRealName = new C4GUI::CallbackEdit<C4ChatControl>(rcDefault, this, &C4ChatControl::OnLoginDataEnter));
357  pSheetLogin->AddElement(pLblLoginChannel = new C4GUI::Label(LoadResStr("IDS_CTL_CHANNEL"), rcDefault, ALeft, C4GUI_CaptionFontClr, pUseFont, false, true));
358  pSheetLogin->AddElement(pEdtLoginChannel = new C4GUI::CallbackEdit<C4ChatControl>(rcDefault, this, &C4ChatControl::OnLoginDataEnter));
359  pSheetLogin->AddElement(pBtnLogin = new C4GUI::CallbackButtonEx<C4ChatControl>(LoadResStr("IDS_BTN_CONNECT"), rcDefault, this, &C4ChatControl::OnConnectBtn));
360  // channel/query tabular
361  pTabChats = new C4GUI::Tabular(rcDefault, C4GUI::Tabular::tbTop);
362  pTabChats->SetSheetMargin(0);
363  pSheetChats->AddElement(pTabChats);
364  // initial connection values
365  const char *szNick = Config.IRC.Nick, *szRealName = Config.IRC.RealName;
366  StdStrBuf sNick, sRealName;
367  if (!*szNick) szNick = Config.Network.Nick.getData();
368  pEdtLoginNick->SetText(szNick, false);
369  pEdtLoginRealName->SetText(szRealName, false);
370  pEdtLoginChannel->SetText(Config.IRC.Channel, false);
371  // initial sheets
372  ClearChatSheets();
373  // set IRC event callback
375 }
376 
378 {
380  delete pTitleChangeBC;
381 }
382 
384 {
385  delete pTitleChangeBC;
386  pTitleChangeBC = pNewCB;
387  // initial title
388  if (pTitleChangeBC) pTitleChangeBC->OnOK(sTitle);
389 }
390 
392 {
393  // parent update
394  typedef C4GUI::Window ParentClass;
395  ParentClass::UpdateSize();
396  // position child elements
397  pTabMain->SetBounds(GetContainedClientRect());
398  pTabChats->SetBounds(pTabChats->GetParent()->GetContainedClientRect());
399  C4GUI::Tabular::Sheet *pSheetLogin = pTabMain->GetSheet(0);
400  C4GUI::ComponentAligner caLoginSheet(pSheetLogin->GetContainedClientRect(), 0,0, false);
401  CStdFont *pUseFont = &::GraphicsResource.TextFont;
402  int32_t iIndent1 = C4GUI_DefDlgSmallIndent/2, iIndent2 = C4GUI_DefDlgIndent/2;
403  int32_t iLoginHgt = pUseFont->GetLineHeight() * 8 + iIndent1*10 + iIndent2*10 + C4GUI_ButtonHgt + 20;
404  int32_t iLoginWdt = iLoginHgt*2/3;
405  C4GUI::ComponentAligner caLogin(caLoginSheet.GetCentered(std::min<int32_t>(iLoginWdt, caLoginSheet.GetInnerWidth()), std::min<int32_t>(iLoginHgt, caLoginSheet.GetInnerHeight())), iIndent1, iIndent1);
406  pLblLoginNick->SetBounds(caLogin.GetFromTop(pUseFont->GetLineHeight()));
407  pEdtLoginNick->SetBounds(caLogin.GetFromTop(C4GUI::Edit::GetDefaultEditHeight()));
408  caLogin.ExpandTop(2*(iIndent1-iIndent2));
409  pLblLoginPass->SetBounds(caLogin.GetFromTop(pUseFont->GetLineHeight()));
410  pEdtLoginPass->SetBounds(caLogin.GetFromTop(C4GUI::Edit::GetDefaultEditHeight()));
411  caLogin.ExpandTop(2*(iIndent1-iIndent2));
412  pLblLoginRealName->SetBounds(caLogin.GetFromTop(pUseFont->GetLineHeight()));
413  pEdtLoginRealName->SetBounds(caLogin.GetFromTop(C4GUI::Edit::GetDefaultEditHeight()));
414  caLogin.ExpandTop(2*(iIndent1-iIndent2));
415  pLblLoginChannel->SetBounds(caLogin.GetFromTop(pUseFont->GetLineHeight()));
416  pEdtLoginChannel->SetBounds(caLogin.GetFromTop(C4GUI::Edit::GetDefaultEditHeight()));
417  caLogin.ExpandTop(2*(iIndent1-iIndent2));
418  pBtnLogin->SetBounds(caLogin.GetFromTop(C4GUI_ButtonHgt, C4GUI_DefButtonWdt));
419 }
420 
422 {
423  UpdateShownPage();
424 }
425 
427 {
428  // only return a default control if no control is selected to prevent deselection of other controls
429  if (GetDlg()->GetFocus()) return nullptr;
430  ChatSheet *pActiveSheet = GetActiveChatSheet();
431  if (pActiveSheet) return pActiveSheet->GetInputEdit();
432  if (pBtnLogin->IsVisible()) return pBtnLogin;
433  return nullptr;
434 }
435 
436 C4ChatControl::ChatSheet *C4ChatControl::GetActiveChatSheet()
437 {
438  if (pTabChats->IsVisible())
439  {
440  C4GUI::Tabular::Sheet *pSheet = pTabChats->GetActiveSheet();
441  if (pSheet) return static_cast<ChatSheet *>(pSheet);
442  }
443  return nullptr;
444 }
445 
446 C4ChatControl::ChatSheet *C4ChatControl::GetSheetByIdent(const char *szIdent, C4ChatControl::ChatSheet::SheetType eType)
447 {
448  int32_t i=0; C4GUI::Tabular::Sheet *pSheet; const char *szCheckIdent;
449  while ((pSheet = pTabChats->GetSheet(i++)))
450  {
451  ChatSheet *pChatSheet = static_cast<ChatSheet *>(pSheet);
452  if ((szCheckIdent = pChatSheet->GetIdent()))
453  if (SEqualNoCase(szCheckIdent, szIdent))
454  if (eType == pChatSheet->GetSheetType())
455  return pChatSheet;
456  }
457  return nullptr;
458 }
459 
460 C4ChatControl::ChatSheet *C4ChatControl::GetSheetByTitle(const char *szTitle, C4ChatControl::ChatSheet::SheetType eType)
461 {
462  int32_t i=0; C4GUI::Tabular::Sheet *pSheet; const char *szCheckTitle;
463  while ((pSheet = pTabChats->GetSheet(i++)))
464  if ((szCheckTitle = pSheet->GetTitle()))
465  if (SEqualNoCase(szCheckTitle, szTitle))
466  {
467  ChatSheet *pChatSheet = static_cast<ChatSheet *>(pSheet);
468  if (eType == pChatSheet->GetSheetType())
469  return pChatSheet;
470  }
471  return nullptr;
472 }
473 
474 C4ChatControl::ChatSheet *C4ChatControl::GetServerSheet()
475 {
476  // server sheet is always the first
477  return static_cast<ChatSheet *>(pTabChats->GetSheet(0));
478 }
479 
481 {
482  // advance focus when user presses enter in one of the login edits
483  GetDlg()->AdvanceFocus(false);
484  // no more pasting
485  return C4GUI::Edit::IR_Abort;
486 }
487 
489 {
490  // check parameters
491  StdCopyStrBuf sNick(pEdtLoginNick->GetText());
492  StdCopyStrBuf sPass(pEdtLoginPass->GetText());
493  StdCopyStrBuf sRealName(pEdtLoginRealName->GetText());
494  StdCopyStrBuf sChannel (pEdtLoginChannel->GetText());
495  StdCopyStrBuf sServer(Config.IRC.Server);
497  {
498  GetScreen()->ShowErrorMessage(LoadResStr("IDS_ERR_INVALIDNICKNAME"));
499  GetDlg()->SetFocus(pEdtLoginNick, false);
500  return;
501  }
502  if (sPass.getLength() && C4InVal::ValidateString(sPass, C4InVal::VAL_IRCPass))
503  {
504  GetScreen()->ShowErrorMessage(LoadResStr("IDS_ERR_INVALIDPASSWORDMAX31CHARA"));
505  GetDlg()->SetFocus(pEdtLoginPass, false);
506  return;
507  }
508  if (sChannel.getLength() && C4InVal::ValidateString(sChannel, C4InVal::VAL_IRCChannel))
509  {
510  GetScreen()->ShowErrorMessage(LoadResStr("IDS_ERR_INVALIDCHANNELNAME"));
511  pEdtLoginChannel->SetText(sChannel.getData(), false);
512  GetDlg()->SetFocus(pEdtLoginChannel, false);
513  return;
514  }
515  if (!sPass.getLength()) sPass.Clear();
516  if (!sChannel.getLength()) sChannel.Clear();
517  // store back config values
518  SCopy(sNick.getData(), Config.IRC.Nick, CFG_MaxString);
519  SCopy(sRealName.getData(), Config.IRC.RealName, CFG_MaxString);
520  SCopy(sChannel.getData(), Config.IRC.Channel, CFG_MaxString);
521  // show chat warning
522  StdStrBuf sWarnMsg;
523  sWarnMsg.Format(LoadResStr("IDS_MSG_YOUAREABOUTTOCONNECTTOAPU"), sServer.getData());
525  return;
526  // set up IRC callback
528  // initiate connection
529  if (!pIRCClient->Connect(sServer.getData(), sNick.getData(), sRealName.getData(), sPass.getData(), sChannel.getData()))
530  {
531  GetScreen()->ShowErrorMessage(FormatString(LoadResStr("IDS_ERR_IRCCONNECTIONFAILED"), pIRCClient->GetError()).getData());
532  return;
533  }
534  // enable client execution
536  // reset chat sheets (close queries, etc.)
537  ClearChatSheets();
538  // connection message
539  ChatSheet *pServerSheet = GetServerSheet();
540  if (pServerSheet)
541  {
542  pServerSheet->SetChatTitle(sServer.getData());
543  pServerSheet->AddTextLine(FormatString(LoadResStr("IDS_NET_CONNECTING"), sServer.getData(), "").getData(), C4GUI_MessageFontClr);
544  }
545  // switch to server window
546  UpdateShownPage();
547 }
548 
550 {
551  if (pIRCClient->IsActive())
552  {
553  // connected to a server: Show chat window
554  pTabMain->SelectSheet(1, false);
555  Update();
556  }
557  else
558  {
559  // not connected: Login stuff
560  pTabMain->SelectSheet((int)0, false);
561  UpdateTitle();
562  }
563 }
564 
565 bool C4ChatControl::IsServiceName(const char *szName)
566 {
567  // return true for some hardcoded list of service names
568  if (!szName) return false;
569  const char *szServiceNames [] = { "NickServ", "ChanServ", "MemoServ", "HelpServ", "Global", nullptr }, *szServiceName;
570  int32_t i = 0;
571  while ((szServiceName = szServiceNames[i++]))
572  if (SEqualNoCase(szName, szServiceName))
573  return true;
574  return false;
575 }
576 
578 {
579  CStdLock Lock(pIRCClient->getCSec());
580  // update channels
581  for (C4Network2IRCChannel *pChan = pIRCClient->getFirstChannel(); pChan; pChan = pIRCClient->getNextChannel(pChan))
582  {
583  ChatSheet *pChanSheet = GetSheetByIdent(pChan->getName(), ChatSheet::CS_Channel);
584  if (!pChanSheet)
585  {
586  // new channel! Create sheet for it
587  pTabChats->AddCustomSheet(pChanSheet = new ChatSheet(this, pChan->getName(), pChan->getName(), ChatSheet::CS_Channel));
588  // and show immediately
589  pTabChats->SelectSheet(pChanSheet, false);
590  }
591  }
592  // remove parted channels
593  int32_t i=0; C4GUI::Tabular::Sheet *pSheet;
594  while ((pSheet = pTabChats->GetSheet(i++)))
595  {
596  C4Network2IRCChannel *pIRCChan;
597  ChatSheet *pChatSheet = static_cast<ChatSheet *>(pSheet);
598  if (pChatSheet->GetSheetType() == ChatSheet::CS_Channel)
599  if (!(pIRCChan = pIRCClient->getChannel(pChatSheet->GetTitle())))
600  {
601  delete pChatSheet;
602  --i;
603  }
604  }
605  // retrieve messages: All messages in initial update; only unread in subsequent calls
606  C4Network2IRCMessage *pMsg;
607  if (fInitialMessagesReceived)
608  {
609  pMsg = pIRCClient->getUnreadMessageLog();
610  }
611  else
612  {
613  pMsg = pIRCClient->getMessageLog();
614  fInitialMessagesReceived = true;
615  }
616  // update messages
617  for (; pMsg; pMsg = pMsg->getNext())
618  {
619  // get target sheet to put message into
620  ChatSheet *pChatSheet; StdStrBuf sUser, sIdent;
621  bool fMsgToService = false;
622  if (pMsg->getType() == MSG_Server)
623  {
624  // server messages in server sheet
625  pChatSheet = GetServerSheet();
626  }
627  else
628  {
629  if (pMsg->getType() != MSG_Status) sUser.Copy(pMsg->getSource());
630  if (!sUser.SplitAtChar('!', &sIdent)) sIdent.Ref(sUser);
631  // message: Either channel or user message (or channel notify). Get correct sheet.
632  if (pMsg->isChannel())
633  {
634  // if no sheet is found, don't create - assume it's an outdated message with the cahnnel window already closed
635  pChatSheet = GetSheetByIdent(pMsg->getTarget(), ChatSheet::CS_Channel);
636  }
637  else if (IsServiceName(sUser.getData()))
638  {
639  // notices and messages by services always in server sheet
640  pChatSheet = GetServerSheet();
641  }
642  else if (pMsg->getType() == MSG_Notice)
643  {
644  // notifies in current sheet; default to server sheet
645  pChatSheet = GetActiveChatSheet();
646  if (!pChatSheet) pChatSheet = GetServerSheet();
647  }
648  else if (pMsg->getType() == MSG_Status || !sUser.getLength())
649  {
650  // server message
651  pChatSheet = GetServerSheet();
652  }
653  else if (sUser == pIRCClient->getUserName())
654  {
655  // private message by myself
656  // message to a service into service window; otherwise (new) query
657  if (IsServiceName(pMsg->getTarget()))
658  {
659  pChatSheet = GetServerSheet();
660  fMsgToService = true;
661  }
662  else
663  {
664  pChatSheet = OpenQuery(pMsg->getTarget(), true, nullptr);
665  if (pChatSheet) pChatSheet->SetChatTitle(pMsg->getTarget());
666  }
667  }
668  else
669  {
670  // private message
671  pChatSheet = OpenQuery(sUser.getData(), false, sIdent.getData());
672  if (pChatSheet) pChatSheet->SetChatTitle(pMsg->getSource());
673  }
674  }
675  if (pChatSheet)
676  {
677  // get message formatting and color
678  StdStrBuf sMsg; uint32_t dwClr = C4GUI_MessageFontClr;
679  switch (pMsg->getType())
680  {
681  case MSG_Server:
682  sMsg.Format("- %s", pMsg->getData());
683  break;
684 
685  case MSG_Status:
686  sMsg.Format("- %s", pMsg->getData());
688  break;
689 
690  case MSG_Notice:
691  if (sUser.getLength())
692  if (sUser != pIRCClient->getUserName())
693  sMsg.Format("-%s- %s", sUser.getData(), pMsg->getData());
694  else
695  sMsg.Format("-> -%s- %s", pMsg->getTarget(), pMsg->getData());
696  else
697  sMsg.Format("* %s", pMsg->getData());
698  dwClr = C4GUI_NotifyFontClr;
699  break;
700 
701  case MSG_Message:
702  if (fMsgToService)
703  sMsg.Format("-> *%s* %s", pMsg->getTarget(), pMsg->getData());
704  else if (sUser.getLength())
705  sMsg.Format("<%s> %s", sUser.getData(), pMsg->getData());
706  else
707  sMsg.Format("* %s", pMsg->getData());
708  break;
709 
710  case MSG_Action:
711  if (sUser.getLength())
712  sMsg.Format("* %s %s", sUser.getData(), pMsg->getData());
713  else
714  sMsg.Format("* %s", pMsg->getData());
715  break;
716 
717  default:
718  sMsg.Format("??? %s", pMsg->getData());
719  dwClr = C4GUI_ErrorFontClr;
720  break;
721  }
722  pChatSheet->AddTextLine(sMsg.getData(), dwClr);
723  }
724  }
725  // OK; all messages processed. Delete overflow messages.
726  pIRCClient->MarkMessageLogRead();
727  // update selected channel (users, topic)
728  ChatSheet *pActiveSheet = GetActiveChatSheet();
729  if (pActiveSheet) pActiveSheet->Update(false);
730  // update title
731  UpdateTitle();
732 }
733 
734 C4ChatControl::ChatSheet *C4ChatControl::OpenQuery(const char *szForNick, bool fSelect, const char *szIdentFallback)
735 {
736  // search existing query first
737  ChatSheet *pChatSheet = GetSheetByTitle(szForNick, ChatSheet::CS_Query);
738  // not found but ident given? Then search for ident as well
739  if (!pChatSheet && szIdentFallback) pChatSheet = GetSheetByIdent(szIdentFallback, ChatSheet::CS_Query);
740  // auto-open query if not found
741  if (!pChatSheet)
742  {
743  pTabChats->AddCustomSheet(pChatSheet = new ChatSheet(this, szForNick, szIdentFallback, ChatSheet::CS_Query));
744  // initial chat title just user name; changed to user name+ident if a message from the nick arrives
745  pChatSheet->SetChatTitle(szForNick);
746  }
747  else
748  {
749  // query already open: Update user name if necessary
750  pChatSheet->SetTitle(szForNick);
751  if (szIdentFallback) pChatSheet->SetIdent(szIdentFallback);
752  }
753  if (fSelect) pTabChats->SelectSheet(pChatSheet, true);
754  return pChatSheet;
755 }
756 
758 {
759  StdCopyStrBuf sNewTitle;
760  if (pTabMain->GetActiveSheetIndex() == 0)
761  {
762  // login title
763  sNewTitle = LoadResStr("IDS_CHAT_NOTCONNECTED");
764  }
765  else
766  {
767  // login by active sheet
768  ChatSheet *pActiveSheet = GetActiveChatSheet();
769  if (pActiveSheet)
770  {
771  sNewTitle = pActiveSheet->GetChatTitle();
772  }
773  else
774  sNewTitle = "";
775  }
776  // call update proc only if title changed
777  if (sTitle != sNewTitle)
778  {
779  sTitle.Take(std::move(sNewTitle));
780  if (pTitleChangeBC) pTitleChangeBC->OnOK(sTitle);
781  }
782  // reload the channel join string from config to fetch C4Network2IRCClient's changes
783  pEdtLoginChannel->SetText(Config.IRC.Channel, false);
784 }
785 
787 {
788  // enter on connect button connects
789  if (GetDlg()->GetFocus() == pBtnLogin) { OnConnectBtn(pBtnLogin); return true; }
790  return false;
791 }
792 
793 void C4ChatControl::ClearChatSheets()
794 {
795  pTabChats->ClearSheets();
796  // add server sheet
797  pTabChats->AddCustomSheet(new ChatSheet(this, LoadResStr("IDS_CHAT_SERVER"), nullptr, ChatSheet::CS_Server));
798 }
799 
800 bool C4ChatControl::ProcessInput(const char *szInput, ChatSheet *pChatSheet)
801 {
802  CStdLock Lock(pIRCClient->getCSec());
803  // process chat input - return false if no more pasting is to be done (i.e., on /quit or /part in channel)
804  bool fResult = true;
805  bool fIRCSuccess = true;
806  // not connected?
807  if (!pIRCClient->IsConnected())
808  {
809  pChatSheet->DoError(LoadResStr("IDS_ERR_NOTCONNECTEDTOSERVER"));
810  return fResult;
811  }
812  // safety
813  if (!szInput || !*szInput || !pChatSheet) return fResult;
814  // command?
815  if (*szInput == '/' && !SEqual2NoCase(szInput + 1, "me "))
816  {
817  StdStrBuf sCommand, sParam(""); sCommand.Copy(szInput+1);
818  sCommand.SplitAtChar(' ', &sParam);
819  if (SEqualNoCase(sCommand.getData(), "help"))
820  {
821  pChatSheet->DoError(LoadResStr("IDS_ERR_HELPCMD"));
822  fResult = false;
823  }
824  else if (SEqualNoCase(sCommand.getData(), "quit"))
825  {
826  // query disconnect from IRC server
827  fIRCSuccess = pIRCClient->Quit(sParam.getData());
828  fResult = false;
829  }
830  else if (SEqualNoCase(sCommand.getData(), "part"))
831  {
832  // part channel. Default to current channel if typed within a channel
833  if (!sParam.getLength() && pChatSheet->GetSheetType() == ChatSheet::CS_Channel)
834  {
835  sParam.Copy(pChatSheet->GetIdent());
836  fResult = false;
837  }
838  fIRCSuccess = pIRCClient->Part(sParam.getData());
839  }
840  else if (SEqualNoCase(sCommand.getData(), "join") || SEqualNoCase(sCommand.getData(), "j"))
841  {
842  if (!sParam.getLength()) sParam.Copy(Config.IRC.Channel);
843  fIRCSuccess = pIRCClient->Join(sParam.getData());
844  }
845  else if (SEqualNoCase(sCommand.getData(), "notice") || SEqualNoCase(sCommand.getData(), "msg"))
846  {
847  bool fMsg = SEqualNoCase(sCommand.getData(), "msg");
848  StdStrBuf sMsg;
849  if (!sParam.SplitAtChar(' ', &sMsg) || !sMsg.getLength())
850  {
851  pChatSheet->DoError(FormatString(LoadResStr("IDS_ERR_INSUFFICIENTPARAMETERS"), sCommand.getData()).getData());
852  }
853  else
854  {
855  if (fMsg)
856  fIRCSuccess = pIRCClient->Message(sParam.getData(), sMsg.getData());
857  else
858  fIRCSuccess = pIRCClient->Notice(sParam.getData(), sMsg.getData());
859  }
860  }
861  else if (SEqualNoCase(sCommand.getData(), "raw"))
862  {
863  if (!sParam.getLength())
864  pChatSheet->DoError(FormatString(LoadResStr("IDS_ERR_INSUFFICIENTPARAMETERS"), sCommand.getData()).getData());
865  else
866  fIRCSuccess = pIRCClient->Send(sParam.getData());
867  }
868  else if (SEqualNoCase(sCommand.getData(), "ns") || SEqualNoCase(sCommand.getData(), "cs") || SEqualNoCase(sCommand.getData(), "ms"))
869  {
870  if (!sParam.getLength())
871  pChatSheet->DoError(FormatString(LoadResStr("IDS_ERR_INSUFFICIENTPARAMETERS"), sCommand.getData()).getData());
872  else
873  {
874  const char *szMsgTarget;
875  if (SEqualNoCase(sCommand.getData(), "ns")) szMsgTarget = "NickServ";
876  else if (SEqualNoCase(sCommand.getData(), "cs")) szMsgTarget = "ChanServ";
877  else szMsgTarget = "MemoServ";
878  fIRCSuccess = pIRCClient->Message(szMsgTarget, sParam.getData());
879  }
880  }
881  else if (SEqualNoCase(sCommand.getData(), "query") || SEqualNoCase(sCommand.getData(), "q"))
882  {
883  if (!sParam.getLength())
884  pChatSheet->DoError(FormatString(LoadResStr("IDS_ERR_INSUFFICIENTPARAMETERS"), sCommand.getData()).getData());
885  else
886  {
887  StdStrBuf sMsg;
888  sParam.SplitAtChar(' ', &sMsg);
889  OpenQuery(sParam.getData(), true, nullptr);
890  }
891  }
892  else if (SEqualNoCase(sCommand.getData(), "nick"))
893  {
895  pChatSheet->DoError(FormatString(LoadResStr("IDS_ERR_INVALIDNICKNAME2"), sCommand.getData()).getData());
896  else
897  fIRCSuccess = pIRCClient->ChangeNick(sParam.getData());
898  }
899  else
900  {
901  // unknown command
902  pChatSheet->DoError(FormatString(LoadResStr("IDS_ERR_UNKNOWNCMD"), sCommand.getData()).getData());
903  }
904  }
905  else
906  {
907  // regular chat input: Send as message to current channel/user
908  const char *szMsgTarget;
909  ChatSheet::SheetType eSheetType = pChatSheet->GetSheetType();
910  if (eSheetType == ChatSheet::CS_Server)
911  {
912  pChatSheet->DoError(LoadResStr("IDS_ERR_NOTONACHANNEL"));
913  }
914  else
915  {
916  szMsgTarget = pChatSheet->GetTitle();
917  if (*szInput == '/') // action
918  fIRCSuccess = pIRCClient->Action(szMsgTarget, szInput+4);
919  else
920  fIRCSuccess = pIRCClient->Message(szMsgTarget, szInput);
921  }
922  }
923  // IRC sending error? log it
924  if (!fIRCSuccess)
925  {
926  pChatSheet->DoError(pIRCClient->GetError());
927  }
928  return fResult;
929 }
930 
932 {
933  // still connected? Then confirm first
934  if (pIRCClient->IsActive())
935  {
936  if (!GetScreen()->ShowMessageModal(LoadResStr("IDS_MSG_DISCONNECTFROMSERVER"), LoadResStr("IDS_DLG_CHAT"), C4GUI::MessageDialog::btnOKAbort, C4GUI::Ico_Confirm, nullptr))
937  return;
938  }
939  // disconnect from server
940  pIRCClient->Close();
941  // change back to login page
942  UpdateShownPage();
943 }
944 
945 
946 /* C4ChatDlg */
947 
948 C4ChatDlg *C4ChatDlg::pInstance = nullptr;
949 
950 C4ChatDlg::C4ChatDlg() : C4GUI::Dialog(100, 100, "IRC", false)
951 {
952  // child elements - positioned later
953  pChatCtrl = new C4ChatControl(&Application.IRCClient);
955  AddElement(pChatCtrl);
956  // del dlg when closed
957  SetDelOnClose();
958  // set initial element positions
959  UpdateSize();
960  // intial focus
961  SetFocus(GetDefaultControl(), false);
962 }
963 
964 C4ChatDlg::~C4ChatDlg() = default;
965 
967 {
968  if (!pInstance)
969  {
970  pInstance = new C4ChatDlg();
971  pInstance->Show(::pGUI, true);
972  }
973  else
974  {
975  ::pGUI->ActivateDialog(pInstance);
976  }
977  return pInstance;
978 }
979 
981 {
982  if (!pInstance) return;
983  pInstance->Close(false);
985 }
986 
988 {
989  if (pInstance && pInstance->IsShown())
990  pInstance->Close(false);
991  else
992  ShowChat();
993  return true;
994 }
995 
997 {
998  // not if chat is disabled
999  if (!IsChatEnabled()) return false;
1000  // check whether IRC is connected
1001  return Application.IRCClient.IsActive();
1002 }
1003 
1005 {
1006  return true;
1007 }
1008 
1010 {
1011  return pChatCtrl->GetDefaultControl();
1012 }
1013 
1014 bool C4ChatDlg::DoPlacement(C4GUI::Screen *pOnScreen, const C4Rect &rPreferredDlgRect)
1015 {
1016  // ignore preferred rect; place over complete screen
1017  C4Rect rcPos = pOnScreen->GetContainedClientRect();
1018  rcPos.x += rcPos.Wdt/10; rcPos.Wdt -= rcPos.Wdt/5;
1019  rcPos.y += rcPos.Hgt/10; rcPos.Hgt -= rcPos.Hgt/5;
1020  SetBounds(rcPos);
1021  return true;
1022 }
1023 
1024 void C4ChatDlg::OnClosed(bool fOK)
1025 {
1026  // callback when dlg got closed
1027  pInstance = nullptr;
1028  typedef C4GUI::Dialog ParentClass;
1029  ParentClass::OnClosed(fOK);
1030 }
1031 
1033 {
1034  // callback when shown - should not delete the dialog
1035  typedef C4GUI::Dialog ParentClass;
1036  ParentClass::OnShown();
1037  pChatCtrl->OnShown();
1038 }
1039 
1041 {
1042  // parent update
1043  typedef C4GUI::Dialog ParentClass;
1044  ParentClass::UpdateSize();
1045  // position child elements
1047  pChatCtrl->SetBounds(caMain.GetAll());
1048 }
1049 
1051 {
1052  UserClose(false);
1053 }
1054 
1056 {
1057  SetTitle(FormatString("%s - %s", LoadResStr("IDS_DLG_CHAT"), sNewTitle.getData()).getData());
1058 }
const char * getData() const
Definition: StdBuf.h:442
const char * GetTitle()
Definition: C4Gui.h:1624
ChatSheet * OpenQuery(const char *szForNick, bool fSelect, const char *szIdentFallback)
Definition: C4ChatDlg.cpp:734
C4Network2IRCMessageType getType() const
Definition: C4Network2IRC.h:51
bool DoPlacement(C4GUI::Screen *pOnScreen, const C4Rect &rPreferredDlgRect) override
Definition: C4ChatDlg.cpp:1014
#define C4GUI_CaptionFontClr
Definition: C4Gui.h:37
const char * getName() const
Definition: C4Network2IRC.h:75
C4Config Config
Definition: C4Config.cpp:833
int GetLineHeight() const
Definition: C4FontLoader.h:125
void GUISound(const char *szSound)
Definition: C4Gui.cpp:1175
C4ConfigStartup Startup
Definition: C4Config.h:260
#define C4GUI_ButtonHgt
Definition: C4Gui.h:111
bool Send(const char *szCommand, const char *szParameters=nullptr)
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
void GetAll(C4Rect &rcOut)
Definition: C4Gui.cpp:1125
char Server[CFG_MaxString+1]
Definition: C4Config.h:205
const char * GetBackBuffer(int32_t iIndex)
C4Network2IRCUser * getUsers() const
#define C4GUI_MessageFontClr
Definition: C4Gui.h:43
int32_t GetInnerHeight() const
Definition: C4Gui.h:2806
#define C4GUI_InactMessageFontClr
Definition: C4Gui.h:45
bool SplitAtChar(char cSplit, StdStrBuf *psSplit)
Definition: StdBuf.h:619
bool DlgEnter()
Definition: C4ChatDlg.cpp:786
bool IsVisible()
Definition: C4Gui.cpp:201
int stricmp(const char *s1, const char *s2)
C4Network2IRCClient * getIRCClient()
Definition: C4ChatDlg.h:130
void SetToolTip(const char *szNewTooltip, bool is_immediate=false)
Definition: C4Gui.cpp:409
#define C4GUI_DefDlgSmallIndent
Definition: C4Gui.h:133
void SetSheetMargin(int32_t iMargin)
Definition: C4Gui.h:1713
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:207
void Close(bool fOK)
const char * GetText()
Definition: C4Gui.h:1338
int32_t HideMsgIRCDangerous
Definition: C4Config.h:188
void Update(class C4Network2IRCUser *pByUser)
Definition: C4ChatDlg.cpp:55
void SetNotify(class C4InteractiveThread *pnNotify)
Definition: C4Rect.h:27
virtual const char * GetError() const
Definition: C4NetIO.h:284
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
C4MessageInput MessageInput
void AddElement(Element *pChild)
bool isChannel() const
Definition: C4Network2IRC.h:55
class C4GUI::Control * GetDefaultControl() override
Definition: C4ChatDlg.cpp:1009
C4Rect GetContainedClientRect()
Definition: C4Gui.h:448
bool Action(const char *szTarget, const char *szText)
C4GraphicsResource GraphicsResource
void SelectSheet(int32_t iIndex, bool fByUser)
bool IsConnected() const
virtual void OnOK(const StdStrBuf &sText)=0
void SetDelOnClose(bool fToVal=true)
Definition: C4Gui.h:2190
void OnChatTitleChange(const StdStrBuf &sNewTitle)
Definition: C4ChatDlg.cpp:1055
C4Network2IRCChannel * getNextChannel(C4Network2IRCChannel *pPrevChan) const
static int32_t SortFunc(const C4GUI::Element *pEl1, const C4GUI::Element *pEl2, void *)
Definition: C4ChatDlg.cpp:77
#define C4GUI_NotifyFontClr
Definition: C4Gui.h:46
bool Close() override
C4Network2IRCChannel * getChannel(const char *szName) const
void ClearCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
void OnShown() override
Definition: C4ChatDlg.cpp:1032
bool IsShown()
Definition: C4Gui.h:2147
ValidatedStdCopyStrBuf< C4InVal::VAL_NameAllowEmpty > Nick
Definition: C4Config.h:156
bool SetText(const char *szText, bool fUser)
Definition: C4Gui.h:1292
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
int32_t Wdt
Definition: C4Rect.h:30
C4Network2IRCMessage * getMessageLog() const
bool Show(Screen *pOnScreen, bool fCB)
char RealName[CFG_MaxString+1]
Definition: C4Config.h:207
C4GUIScreen * pGUI
Definition: C4Gui.cpp:1191
void StoreBackBuffer(const char *szMessage)
C4Network2IRCChannel * getFirstChannel() const
void UpdateShownPage()
Definition: C4ChatDlg.cpp:549
C4Network2IRCUser * getNext() const
Definition: C4Network2IRC.h:76
bool Connect(const char *szServer, const char *szNick, const char *szRealName, const char *szPassword=nullptr, const char *szChannel=nullptr)
const char * getTopic() const
NickItem(class C4Network2IRCUser *pByUser)
Definition: C4ChatDlg.cpp:29
int32_t y
Definition: C4Rect.h:30
bool ShowMessageModal(const char *szMessage, const char *szCaption, DWORD dwButtons, Icons icoIcon, int32_t *piConfigDontShowAgainSetting=nullptr)
void UpdateTitle()
Definition: C4ChatDlg.cpp:757
bool isUsersLocked() const
bool ShowErrorMessage(const char *szMessage)
bool Part(const char *szChannel)
C4Rect rcBounds
Definition: C4Gui.h:385
void Take(char *pnData)
Definition: StdBuf.h:457
C4ConfigIRC IRC
Definition: C4Config.h:257
bool ChangeNick(const char *szNewNick)
C4ConfigNetwork Network
Definition: C4Config.h:255
void OnExitBtn(C4GUI::Control *btn)
Definition: C4ChatDlg.cpp:1050
Container * GetParent()
Definition: C4Gui.h:429
void AddCustomSheet(Sheet *pAddSheet)
#define C4GUI_DefButtonWdt
Definition: C4Gui.h:114
void UpdateSize() override
Definition: C4ChatDlg.cpp:391
const char * getPrefix() const
Definition: C4Network2IRC.h:74
Sheet * GetSheet(int32_t iIndex)
Definition: C4Gui.h:1706
static int32_t GetDefaultEditHeight()
Definition: C4GuiEdit.cpp:113
void SetCaptionColor(uint32_t dwNewClr=0)
Definition: C4Gui.h:1627
const char * getTarget() const
Definition: C4Network2IRC.h:53
virtual void OnShown()
Definition: C4ChatDlg.cpp:421
C4GUI::Edit::InputResult OnLoginDataEnter(C4GUI::Edit *edt, bool fPasting, bool fPastingMore)
Definition: C4ChatDlg.cpp:480
void UserQueryQuit()
Definition: C4ChatDlg.cpp:931
void UpdateSize() override
Definition: C4ChatDlg.cpp:1040
const char * getData() const
Definition: C4Network2IRC.h:54
bool Message(const char *szTarget, const char *szText)
bool AddProc(StdSchedulerProc *pProc)
int ReplaceChar(char cOld, char cNew)
Definition: StdBuf.cpp:336
virtual class Dialog * GetDlg()
Definition: C4Gui.cpp:288
Icons
Definition: C4Gui.h:637
bool Quit(const char *szReason)
const char * getUserName() const
bool ValidateString(char *szString, ValidationOption eOption, size_t iMaxSize)
int32_t x
Definition: C4Rect.h:30
static void StopChat()
Definition: C4ChatDlg.cpp:980
void Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
Definition: C4Rect.cpp:86
char Nick[CFG_MaxString+1]
Definition: C4Config.h:206
void Ref(const char *pnData)
Definition: StdBuf.h:455
void SetCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
void SetFocus(Control *pCtrl, bool fByMouse)
static bool IsChatEnabled()
Definition: C4ChatDlg.cpp:1004
void SetBounds(const C4Rect &rcNewBound)
Definition: C4Gui.h:446
static C4ChatDlg * ShowChat()
Definition: C4ChatDlg.cpp:966
static bool IsChatActive()
Definition: C4ChatDlg.cpp:996
Sheet * GetActiveSheet()
Definition: C4Gui.h:1707
static bool ToggleChat()
Definition: C4ChatDlg.cpp:987
#define C4GUI_Caption2FontClr
Definition: C4Gui.h:38
bool EnsureUnicode()
Definition: StdBuf.cpp:421
virtual void UserClose(bool fOK)
Definition: C4Gui.h:2206
char Channel[CFG_MaxString+1]
Definition: C4Config.h:208
const int ALeft
Definition: C4Surface.h:41
void OnClosed(bool fOK) override
Definition: C4ChatDlg.cpp:1024
void SetTitle(const char *szToTitle, bool fShowCloseButton=true)
bool Join(const char *szChannel)
void OnConnectBtn(C4GUI::Control *btn)
Definition: C4ChatDlg.cpp:488
#define C4GUI_DefDlgIndent
Definition: C4Gui.h:132
C4ChatControl(C4Network2IRCClient *pIRC)
Definition: C4ChatDlg.cpp:337
bool SEqual2NoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:220
void SetPasswordMask(char cNewPasswordMask)
Definition: C4Gui.h:1293
#define C4GUI_ErrorFontClr
Definition: C4Gui.h:53
Sheet * AddSheet(const char *szTitle, int32_t icoTitle=Ico_None)
size_t getLength() const
Definition: StdBuf.h:445
virtual class C4GUI::Control * GetDefaultControl()
Definition: C4ChatDlg.cpp:426
virtual Screen * GetScreen()
Definition: C4Gui.cpp:289
C4Network2IRCMessage * getUnreadMessageLog() const
int32_t Hgt
Definition: C4Rect.h:30
bool GetFromLeft(int32_t iWdt, int32_t iHgt, C4Rect &rcOut)
Definition: C4Gui.cpp:1076
void SetDrawDecoration(bool fToVal)
Definition: C4Gui.h:1714
bool Notice(const char *szTarget, const char *szText)
~C4ChatDlg() override
bool ProcessInput(const char *szInput, ChatSheet *pChatSheet)
Definition: C4ChatDlg.cpp:800
void Copy()
Definition: StdBuf.h:467
C4Network2IRCMessage * getNext() const
Definition: C4Network2IRC.h:57
Sheet(const char *szTitle, const C4Rect &rcBounds, int32_t icoTitle=Ico_None, bool fHasCloseButton=false, bool fTitleMarkup=true)
C4Application Application
Definition: C4Globals.cpp:44
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
C4Network2IRCClient & IRCClient
Definition: C4Application.h:47
void ActivateDialog(Dialog *pDlg)
Definition: C4Gui.cpp:647
const char * getSource() const
Definition: C4Network2IRC.h:52
int32_t GetActiveSheetIndex()
CStdCSec * getCSec()
void AdvanceFocus(bool fBackwards)
void SetTitleChangeCB(C4GUI::BaseInputCallback *pNewCB)
Definition: C4ChatDlg.cpp:383
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
virtual bool IsFocused(Control *pCtrl)
Definition: C4Gui.h:836
~C4ChatControl() override
Definition: C4ChatDlg.cpp:377
bool IsActive() const