1 module Connection; 2 3 import core.thread; 4 import gui; 5 import gdk.Threads : te = threadsEnter, tl = threadsLeave; 6 import gtk.Box; 7 import std.stdio; 8 import libdnet.dclient; 9 import std.socket; 10 import gtk.ListBox; 11 import gtk.Label; 12 13 import Channel; 14 import std.string; 15 16 import core.sync.mutex; 17 18 import gtk.Notebook; 19 20 import std.conv; 21 22 public final class Connection : Thread 23 { 24 private GUI gui; 25 private Box box; 26 private ListBox channels; 27 private ListBox users; 28 private ListBox textArea; 29 30 private DClient client; 31 private Address address; 32 private string[] auth; 33 private string statusText; 34 35 /* TODO: Check if we need to protect */ 36 /* TODO: So far usage is in signal handlers (mutex safved) and within te-tl lock for notifications */ 37 private string currentChannel; /* TODO: Used to track what notificaitons come throug */ 38 private Label currentChannelLabel; /* TODO: Using getChild would be nicer, but yeah, this is for the title */ 39 40 /** 41 * All joined Channel-s in this Connection 42 */ 43 private Notebook notebookSwitcher; 44 private Channel[] chans; /*TODO: Technically locking by GTK would make this not needed */ 45 private Mutex chansLock; 46 private Channel focusedChan; 47 48 49 // public void setPrescence(string pres) 50 // { 51 // /* The new status */ 52 // string newStatus = 53 // statusText = ""; 54 // statusText = 55 // } 56 57 this(GUI gui, Address address, string[] auth) 58 { 59 super(&worker); 60 this.gui = gui; 61 this.address = address; 62 this.auth = auth; 63 64 /* Initialize locks */ 65 initializeLocks(); 66 67 statusText = "Hey there, I'm using Dnet!"; 68 69 /* Start the notification atcher */ 70 start(); 71 } 72 73 /** 74 * Initializes all locks (other than GDK) 75 */ 76 private void initializeLocks() 77 { 78 chansLock = new Mutex(); 79 } 80 81 private void worker() 82 { 83 /* Create a new Label */ 84 currentChannelLabel = new Label("CHANNEL NAME GOES HERE"); 85 86 /** 87 * Setup the tab for this connection 88 */ 89 te(); 90 box = getChatPane(); 91 gui.notebook.add(box); 92 //gui.notebook.setTabReorderable(box, true); 93 //gui.notebook setChildPacking(box, true, true, 0, GtkPackType.START); 94 // gui.mainWindow. 95 gui.notebook.setTabLabelText(box, auth[0]~"@"~address.toString()); 96 gui.notebook.showAll(); 97 tl(); 98 99 100 /** 101 * Connects and logs in 102 */ 103 client = new DClient(address); 104 client.auth(auth[0], auth[1]); /* TODO: DO this without auth (the list in the loop, crahses server) */ 105 106 /* Set your prescense */ 107 client.setProperty("pres","available"); 108 109 110 111 // te(); 112 // import ProfileWIndow; 113 // ProfileWindow profile = new ProfileWindow(this, auth[0]); 114 // tl(); 115 116 /** 117 * Notification loop 118 * 119 * Awaits notifications and then displays them 120 */ 121 while(true) 122 { 123 /* Receive a notification */ 124 byte[] notificationData = client.awaitNotification(); 125 writeln(notificationData); 126 127 te(); 128 // import std.conv; 129 // textArea.add(new Label(to!(string)(notificationData))); 130 // textArea.showAll(); 131 132 133 process(notificationData); 134 //gui.mainWindow.showAll(); 135 136 // import gtk.InfoBar; 137 // InfoBar notificationBar = new InfoBar(); 138 // notificationBar.add(new Label("Poes")); 139 // notificationBar.setMessageType(GtkMessageType.INFO); 140 // notificationBar 141 142 // box.add(notificationBar); 143 // notificationBar.showAll(); 144 145 notebookSwitcher.showAll(); 146 147 148 tl(); 149 150 //Thread.sleep(dur!("seconds")(2)); 151 } 152 } 153 154 155 156 /** 157 * Processes an incoming notification 158 * accordingly 159 */ 160 private void process(byte[] data) 161 { 162 /* TODO: Implement me */ 163 164 /* TODO: Check notification type */ 165 ubyte notificationType = data[0]; 166 167 /* For normal message (to channel or user) */ 168 if(notificationType == 0) 169 { 170 /* TODO: Decode using tristanable */ 171 writeln("new message"); 172 writeln(data); 173 174 /* Decode is a test for assuming channel message received */ 175 data = data[1..data.length]; 176 177 /* Decode the [usernameLength, username] */ 178 ubyte usernameLength = data[1]; 179 writeln(usernameLength); 180 string username = cast(string)data[2..2+usernameLength]; 181 writeln(username); 182 183 /* Decode the [channelLength, channel] */ 184 ubyte channelLength = data[2+usernameLength]; 185 writeln(channelLength); 186 string channel = cast(string)data[2+usernameLength+1..2+usernameLength+1+channelLength]; 187 writeln(channel); 188 189 190 findChannel(channel).receiveMessage(username, cast(string)data[2+usernameLength+1+channelLength..data.length]); 191 192 writeln("hdsfhdk"); 193 } 194 /* Channel notification (ntype=1) */ 195 else if(notificationType == 1) 196 { 197 /* Get the sub-type */ 198 ubyte subType = data[1]; 199 200 /* If the notification was leave (stype=0) */ 201 if(subType == 0) 202 { 203 /* LeaveInfo: <channel>,<username> */ 204 string[] leaveInfo = split(cast(string)data[2..data.length],","); 205 writeln("LeaveInfo: ",leaveInfo); 206 207 /* Decode the LeaveInfo */ 208 string channel = leaveInfo[0]; 209 string username = leaveInfo[1]; 210 211 /* Find the channel */ 212 Channel matchedChannel = findChannel(channel); 213 214 /* Channel leave */ 215 matchedChannel.channelLeave(username); 216 } 217 /* If the notification was join (stype=1) */ 218 else if(subType == 1) 219 { 220 /* JoinInfo: <channel>,<username> */ 221 string[] joinInfo = split(cast(string)data[2..data.length],","); 222 writeln("JoinInfo: ",joinInfo); 223 224 /* Decode the JoinInfo */ 225 string channel = joinInfo[0]; 226 string username = joinInfo[1]; 227 228 /* Find the channel */ 229 Channel matchedChannel = findChannel(channel); 230 231 /* Channel join */ 232 matchedChannel.channelJoin(username); 233 } 234 /* TODO: Unknown */ 235 else 236 { 237 238 } 239 } 240 } 241 242 243 public void joinChannel(string channelName) 244 { 245 /* Check if we have joined this channel already */ 246 Channel foundChannel = findChannel(channelName); 247 248 /* If we have joined this channel before */ 249 if(foundChannel) 250 { 251 /* TODO: Switch to */ 252 writeln("nope time: "~channelName); 253 254 255 } 256 /* If we haven't joined this channel before */ 257 else 258 { 259 /* Join the channel */ 260 client.join(channelName); 261 262 /* Create the Channel object */ 263 Channel newChannel = new Channel(client, channelName); 264 265 /* Add the channel */ 266 addChannel(newChannel); 267 268 /* Set as the `foundChannel` */ 269 foundChannel = newChannel; 270 271 /* Get the Widgets container for this channel and add a tab for it */ 272 notebookSwitcher.add(newChannel.getBox()); 273 notebookSwitcher.setTabReorderable(newChannel.getBox(), true); 274 notebookSwitcher.setTabLabelText(newChannel.getBox(), newChannel.getName()); 275 276 writeln("hdsjghjsd"); 277 278 writeln("first time: "~channelName); 279 280 /* Get the user's list */ 281 newChannel.populateUsersList(); 282 } 283 284 /* Render recursively all children of the container and then the container itself */ 285 box.showAll(); 286 } 287 288 289 private void channelList() 290 { 291 te(); 292 channelList_unsafe(); 293 tl(); 294 } 295 296 public DClient getClient() 297 { 298 return client; 299 } 300 301 /** 302 * Lists all channels and displays them 303 * 304 * Only to be aclled when locked (i.e. by the event 305 * loop signal dispatch or when we lock it 306 * i.e. `channelList`) 307 */ 308 private void channelList_unsafe() 309 { 310 string[] channelList = client.list(); 311 312 foreach(string channel; channelList) 313 { 314 channels.add(new Label(channel)); 315 channels.showAll(); 316 } 317 } 318 319 public Channel findChannel(string channelName) 320 { 321 Channel result; 322 323 chansLock.lock(); 324 325 foreach(Channel channel; chans) 326 { 327 if(cmp(channel.getName(), channelName) == 0) 328 { 329 result = channel; 330 break; 331 } 332 } 333 334 import std.stdio; 335 writeln("\""~channelName~"\""); 336 337 338 chansLock.unlock(); 339 340 return result; 341 } 342 343 /** 344 * Adds the given channel to the tarcking list 345 * 346 * This adds the Channel object to the list of 347 * channels joined 348 * 349 * TODO: Migrate the gui.d channel join selectChannel 350 * here 351 * NOTE: You must manually join it though 352 */ 353 public void addChannel(Channel newChannel) 354 { 355 /* Add the channel to the `chans` tracking list */ 356 chansLock.lock(); 357 chans ~= newChannel; 358 chansLock.unlock(); 359 360 /* Add the channel to the channels list (sidebar) */ 361 writeln("Adding channel "~newChannel.getName()); 362 Label babaBooey = new Label(newChannel.getName()); /* TODO: Fuck Pango, fix here but yeah _ */ 363 babaBooey.setUseMarkup(false); 364 babaBooey.setText(newChannel.getName()); 365 channels.add(babaBooey); 366 } 367 368 /** 369 * Called when you select a channel in the sidebar 370 * 371 * This moves you to the correct notebook tab for 372 * that channel 373 */ 374 private void viewChannel(ListBox s) 375 { 376 /* Get the name of the channel selected */ 377 string channelSelected = (cast(Label)(s.getSelectedRow().getChild())).getText(); 378 379 /* Check if we have joined this channel already */ 380 Channel foundChannel = findChannel(channelSelected); 381 writeln(foundChannel is null); 382 383 /* Switch to the channel's pane */ 384 notebookSwitcher.setCurrentPage(foundChannel.getBox()); 385 386 box.showAll(); 387 } 388 389 390 /** 391 * Creates a message box 392 * 393 * A message box consists of two labels 394 * one being the name of the person who sent 395 * the message and the next being the message 396 * itself 397 */ 398 private Box createMessageBox() 399 { 400 return null; 401 } 402 403 private Box getChatPane() 404 { 405 /* The main page of the tab */ 406 Box box = new Box(GtkOrientation.HORIZONTAL, 1); 407 408 /* The channels box */ 409 Box channelBox = new Box(GtkOrientation.VERTICAL, 1); 410 411 /* The channel's list */ 412 channels = new ListBox(); 413 channels.addOnSelectedRowsChanged(&viewChannel); 414 415 channelBox.add(new Label("Channels")); 416 channelBox.add(channels); 417 418 // /* The user's box */ 419 // Box userBox = new Box(GtkOrientation.VERTICAL, 1); 420 421 // /* The user's list */ 422 // users = new ListBox(); 423 424 // userBox.add(new Label("Users")); 425 // userBox.add(users); 426 427 // /* The text box */ 428 // Box textBox = new Box(GtkOrientation.VERTICAL, 1); 429 // textBox.add(currentChannelLabel); 430 // textArea = new ListBox(); 431 // import gtk.ScrolledWindow; 432 433 // ScrolledWindow scrollTextChats = new ScrolledWindow(textArea); 434 // textBox.add(scrollTextChats); 435 // import gtk.TextView; 436 // textBox.add(new TextView()); 437 438 439 // import gtk.TextView; 440 // TextView f = new TextView(); 441 // textBox.add(f); 442 443 notebookSwitcher = new Notebook(); 444 notebookSwitcher.setScrollable(true); 445 //notebookSwitcher.add(newnew Label("test")); 446 447 box.add(channelBox); 448 box.add(notebookSwitcher); 449 // box.add(textBox); 450 //box.packEnd(notebookSwitcher,0,0,0); 451 452 // textBox.setChildPacking(scrollTextChats, true, true, 0, GtkPackType.START); 453 box.setChildPacking(notebookSwitcher, true, true, 0, GtkPackType.START); 454 455 456 457 return box; 458 } 459 460 private int getPageNum() 461 { 462 return gui.notebook.pageNum(box); 463 } 464 465 public void shutdown() 466 { 467 /* This is called from gui.d */ 468 int pageNum = getPageNum(); 469 470 if(pageNum == -1) 471 { 472 /* TODO: Error handling */ 473 } 474 else 475 { 476 gui.notebook.removePage(pageNum); 477 gui.notebook.showAll(); 478 } 479 } 480 }