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