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 tl(); 116 117 //Thread.sleep(dur!("seconds")(2)); 118 } 119 } 120 121 122 123 /** 124 * Processes an incoming notification 125 * accordingly 126 */ 127 private void process(byte[] data) 128 { 129 /* TODO: Implement me */ 130 131 /* TODO: Check notification type */ 132 ubyte notificationType = data[0]; 133 134 /* For normal message (to channel or user) */ 135 if(notificationType == 0) 136 { 137 /* TODO: Decode using tristanable */ 138 writeln("new message"); 139 } 140 /* Channel notification (ntype=1) */ 141 else if(notificationType == 1) 142 { 143 /* Get the sub-type */ 144 ubyte subType = data[1]; 145 146 /* If the notification was leave (stype=0) */ 147 if(subType == 0) 148 { 149 /* LeaveInfo: <channel>,<username> */ 150 string[] leaveInfo = split(cast(string)data[2..data.length],","); 151 writeln("LeaveInfo: ",leaveInfo); 152 153 /* Decode the LeaveInfo */ 154 string channel = leaveInfo[0]; 155 string username = leaveInfo[1]; 156 157 /* Find the channel */ 158 Channel matchedChannel = findChannel(channel); 159 160 /* Channel leave */ 161 matchedChannel.channelLeave(username); 162 } 163 /* If the notification was join (stype=1) */ 164 else if(subType == 1) 165 { 166 /* JoinInfo: <channel>,<username> */ 167 string[] joinInfo = split(cast(string)data[2..data.length],","); 168 writeln("JoinInfo: ",joinInfo); 169 170 /* Decode the JoinInfo */ 171 string channel = joinInfo[0]; 172 string username = joinInfo[1]; 173 174 /* Find the channel */ 175 Channel matchedChannel = findChannel(channel); 176 177 /* Channel join */ 178 matchedChannel.channelJoin(username); 179 } 180 /* TODO: Unknown */ 181 else 182 { 183 184 } 185 } 186 } 187 188 189 190 191 192 private void channelList() 193 { 194 te(); 195 channelList_unsafe(); 196 tl(); 197 } 198 199 public DClient getClient() 200 { 201 return client; 202 } 203 204 /** 205 * Lists all channels and displays them 206 * 207 * Only to be aclled when locked (i.e. by the event 208 * loop signal dispatch or when we lock it 209 * i.e. `channelList`) 210 */ 211 private void channelList_unsafe() 212 { 213 string[] channelList = client.list(); 214 215 foreach(string channel; channelList) 216 { 217 channels.add(new Label(channel)); 218 channels.showAll(); 219 } 220 } 221 222 private Channel findChannel(string channelName) 223 { 224 Channel result; 225 226 chansLock.lock(); 227 228 foreach(Channel channel; chans) 229 { 230 if(cmp(channel.getName(), channelName) == 0) 231 { 232 result = channel; 233 break; 234 } 235 } 236 237 chansLock.unlock(); 238 239 return result; 240 } 241 242 private void addChannel(Channel newChannel) 243 { 244 chansLock.lock(); 245 246 chans ~= newChannel; 247 248 chansLock.unlock(); 249 } 250 251 private void selectChannel(ListBox s) 252 { 253 /* Get the name of the channel selected */ 254 string channelSelected = (cast(Label)(s.getSelectedRow().getChild())).getText(); 255 256 /* Check if we have joined this channel already */ 257 Channel foundChannel = findChannel(channelSelected); 258 259 /* If we have joined this channel before */ 260 if(foundChannel) 261 { 262 /* TODO: Switch to */ 263 writeln("nope time: "~channelSelected); 264 265 266 } 267 /* If we haven't joined this channel before */ 268 else 269 { 270 /* Join the channel */ 271 client.join(channelSelected); 272 273 /* Create the Channel object */ 274 Channel newChannel = new Channel(client, channelSelected); 275 276 /* Add the channel */ 277 addChannel(newChannel); 278 279 /* Set as the `foundChannel` */ 280 foundChannel = newChannel; 281 282 /* Get the Widgets container for this channel and add a tab for it */ 283 notebookSwitcher.add(newChannel.getBox()); 284 notebookSwitcher.setTabLabelText(newChannel.getBox(), newChannel.getName()); 285 286 writeln("hdsjghjsd"); 287 288 writeln("first time: "~channelSelected); 289 290 /* Get the user's list */ 291 newChannel.populateUsersList(); 292 } 293 294 /* Switch to the channel's pane */ 295 notebookSwitcher.setCurrentPage(foundChannel.getBox()); 296 297 box.showAll(); 298 // notebookSwitcher.showAll(); 299 300 /* TODO: Now add the widget */ 301 302 // /* Set this as the currently selected channel */ 303 // currentChannel = channelSelected; 304 // currentChannelLabel.setText(currentChannel); 305 // // currentChannelLabel.show(); 306 // // box.show(); 307 308 // /* Fetch a list of members */ 309 // string[] members = client.getMembers(channelSelected); 310 311 // /* Display the members */ 312 // users.removeAll(); 313 // foreach(string member; members) 314 // { 315 // users.add(new Label(member)); 316 // users.showAll(); 317 // } 318 319 // /* Clear the text area */ 320 // textArea.removeAll(); 321 // textArea.showAll(); 322 } 323 324 325 /** 326 * Creates a message box 327 * 328 * A message box consists of two labels 329 * one being the name of the person who sent 330 * the message and the next being the message 331 * itself 332 */ 333 private Box createMessageBox() 334 { 335 return null; 336 } 337 338 private Box getChatPane() 339 { 340 /* The main page of the tab */ 341 Box box = new Box(GtkOrientation.HORIZONTAL, 1); 342 343 /* The channels box */ 344 Box channelBox = new Box(GtkOrientation.VERTICAL, 1); 345 346 /* The channel's list */ 347 channels = new ListBox(); 348 channels.addOnSelectedRowsChanged(&selectChannel); 349 350 channelBox.add(new Label("Channels")); 351 channelBox.add(channels); 352 353 // /* The user's box */ 354 // Box userBox = new Box(GtkOrientation.VERTICAL, 1); 355 356 // /* The user's list */ 357 // users = new ListBox(); 358 359 // userBox.add(new Label("Users")); 360 // userBox.add(users); 361 362 // /* The text box */ 363 // Box textBox = new Box(GtkOrientation.VERTICAL, 1); 364 // textBox.add(currentChannelLabel); 365 // textArea = new ListBox(); 366 // import gtk.ScrolledWindow; 367 368 // ScrolledWindow scrollTextChats = new ScrolledWindow(textArea); 369 // textBox.add(scrollTextChats); 370 // import gtk.TextView; 371 // textBox.add(new TextView()); 372 373 374 // import gtk.TextView; 375 // TextView f = new TextView(); 376 // textBox.add(f); 377 378 notebookSwitcher = new Notebook(); 379 //notebookSwitcher.add(newnew Label("test")); 380 381 box.add(channelBox); 382 box.add(notebookSwitcher); 383 // box.add(textBox); 384 //box.packEnd(notebookSwitcher,0,0,0); 385 386 // textBox.setChildPacking(scrollTextChats, true, true, 0, GtkPackType.START); 387 box.setChildPacking(notebookSwitcher, true, true, 0, GtkPackType.START); 388 389 390 391 return box; 392 } 393 394 private int getPageNum() 395 { 396 return gui.notebook.pageNum(box); 397 } 398 399 public void shutdown() 400 { 401 /* This is called from gui.d */ 402 int pageNum = getPageNum(); 403 404 if(pageNum == -1) 405 { 406 /* TODO: Error handling */ 407 } 408 else 409 { 410 gui.notebook.removePage(pageNum); 411 gui.notebook.showAll(); 412 } 413 } 414 }