1 module gui; 2 3 import core.thread; 4 import gtk.MainWindow; 5 import gtk.ListBox; 6 import gtk.Label; 7 import gtk.Notebook; 8 import gdk.Threads : te = threadsEnter, tl = threadsLeave; 9 import gtk.MenuBar; 10 import gtk.Box; 11 import gtk.Menu; 12 import gtk.MenuItem; 13 import std.stdio; 14 import gtk.Statusbar; 15 import gtk.Toolbar; 16 import gtk.ToolButton; 17 import gtk.ScrolledWindow; 18 import gtk.SeparatorToolItem; 19 import gtk.ToolItem; 20 import gtk.SearchEntry; 21 import gtk.Image; 22 23 import Connection; 24 import Channel; 25 import std.socket; 26 27 import std.conv; 28 29 public class GUI : Thread 30 { 31 /* Main window (GUI homepage) */ 32 public MainWindow mainWindow; 33 private MenuBar menuBar; 34 private Toolbar toolbar; 35 36 private Box box; 37 private Box welcomeBox; 38 39 public Notebook notebook; 40 41 42 43 private Statusbar statusBar; 44 45 46 47 48 private Connection[] connections; 49 50 private ListBox list; 51 52 53 this() 54 { 55 super(&worker); 56 } 57 58 private void worker() 59 { 60 initializeGUI(); 61 62 63 te(); 64 65 tl(); 66 writeln("brg"); 67 while(true) 68 { 69 70 } 71 } 72 73 private void initializeGUI() 74 { 75 initializeMainWindow(); 76 } 77 78 79 /** 80 * The welcome box is shown before 81 * you have added any connections 82 * (it takes place of the Notebook) 83 * and shows information about the 84 * application 85 * 86 * Once you make your first conneciton 87 * it is removed and its space is taken 88 * up by the Notebook 89 */ 90 private Box getWelcomeBox() 91 { 92 /* Create a vertically stacking Box */ 93 Box welcomeBox = new Box(GtkOrientation.VERTICAL, 1); 94 95 /* Add the logo */ 96 Image logo = new Image("user-available", GtkIconSize.DIALOG); 97 logo.setPixelSize(250); 98 welcomeBox.add(logo); 99 100 /* Create the welcome text */ 101 Label title = new Label("<span size=\"100\">Gustav</span>"); 102 title.setMarkup("<span font_desc=\"Open Sans Extrabold\" size=\"50000\">Gustav</span>"); 103 welcomeBox.add(title); 104 105 /* Create the welcome tagline */ 106 Label tagline = new Label("<span size=\"100\">Gustav</span>"); 107 tagline.setMarkup("<span size=\"30000\">GTK+ graphical DNET client</span>"); 108 welcomeBox.add(tagline); 109 110 Label findServersLabel = new Label("<a href=\"\">fok</a>"); 111 findServersLabel.setMarkup("<a href=\"\">Find some servers</a>"); 112 welcomeBox.add(findServersLabel); 113 114 Label configureConnectionsLabel = new Label("<a href=\"\">Configure connections</a>"); 115 configureConnectionsLabel.setMarkup("<a href=\"\">Configure connections</a>"); 116 configureConnectionsLabel.addOnActivateLink(&conifgureConnectionsAssistant); 117 welcomeBox.add(configureConnectionsLabel); 118 119 Label connectGenesisLabel = new Label("<a href=\"\">Connect to the genesis server</a>"); 120 connectGenesisLabel.setMarkup("<span size=\"12000\"> <a href=\"\">Connect to the genesis server</a></span>"); 121 connectGenesisLabel.addOnActivateLink(&welcomeGenesisLabelClick); 122 welcomeBox.add(connectGenesisLabel); 123 124 125 126 return welcomeBox; 127 } 128 129 private bool welcomeGenesisLabelClick(string, Label) 130 { 131 connectServer("0.0.0.0", 7777, ["testGustav"~to!(string)(connections.length), "bruh"]); 132 133 return 1; 134 } 135 136 /** 137 * Initializes the main home screen window 138 */ 139 private void initializeMainWindow() 140 { 141 /* Get GTK lock */ 142 te(); 143 144 /* Create a window */ 145 mainWindow = new MainWindow("unamed"); 146 147 /** 148 * Create a Box in vertical layout mode 149 * and adds it to the window 150 * 151 * This lays out components like so: 152 * 153 * |component 1| 154 * |component 2| 155 */ 156 box = new Box(GtkOrientation.VERTICAL, 1); 157 158 /** 159 * Add needed components 160 * 161 * Menubar, tabbed pane switcher, statusbar 162 */ 163 menuBar = initializeMenuBar(); 164 box.add(menuBar); 165 166 toolbar = getToolbar(); 167 box.add(toolbar); 168 169 /* Create the welcome box and set it */ 170 welcomeBox = getWelcomeBox(); 171 box.add(welcomeBox); 172 173 174 175 statusBar = new Statusbar(); 176 statusBar.add(new Label("Gustav: Bruh")); 177 // import gtk.IconView; 178 // IconView j = new IconView(); 179 // j.set 180 // statusBar.add(d); 181 182 183 184 185 box.packEnd(statusBar, 0, 0, 0); 186 //notebook.add(createServerTab()); 187 188 189 190 191 /* Add the Box to main window */ 192 mainWindow.add(box); 193 194 mainWindow.showAll(); 195 196 /* Unlock GTK lock */ 197 tl(); 198 199 writeln("unlock gui setup"); 200 } 201 202 private Toolbar getToolbar() 203 { 204 /* Create a new Toolbar */ 205 Toolbar toolbar = new Toolbar(); 206 207 /* Status selector dropdown */ 208 /* TODO */ 209 210 211 /* Set available button */ 212 ToolButton setAvail = new ToolButton(""); 213 setAvail.setLabel("available"); 214 setAvail.setIconName("user-available"); 215 toolbar.add(setAvail); 216 217 /* Set away button */ 218 ToolButton setAway = new ToolButton(""); 219 setAway.setLabel("away"); 220 setAway.setIconName("user-away"); 221 toolbar.add(setAway); 222 223 /* Set busy button */ 224 ToolButton setBusy = new ToolButton(""); 225 setBusy.setLabel("busy"); 226 setBusy.setIconName("user-busy"); 227 toolbar.add(setBusy); 228 229 230 /* Assign actions */ 231 setAvail.addOnClicked(&setStatus); 232 setAway.addOnClicked(&setStatus); 233 setBusy.addOnClicked(&setStatus); 234 235 236 /* The status box */ 237 Entry statusBox = new Entry(); 238 statusBox.addOnActivate(&setStatusMessage); 239 statusBox.setPlaceholderText("I'm currently..."); 240 ToolItem statusBoxItem = new ToolItem(); 241 statusBoxItem.add(statusBox); 242 toolbar.add(statusBoxItem); 243 244 245 /* Add a seperator */ 246 toolbar.add(new SeparatorToolItem()); 247 248 249 /* List channels button */ 250 ToolButton channelListButton = new ToolButton(""); 251 channelListButton.setIconName("emblem-documents"); 252 channelListButton.setTooltipText("List channels"); 253 channelListButton.addOnClicked(&listChannels); 254 toolbar.add(channelListButton); 255 256 257 258 259 260 SearchEntry dd = new SearchEntry(); 261 ToolItem j = new ToolItem(); 262 j.add(dd); 263 toolbar.add(j); 264 265 266 267 268 269 270 271 272 273 274 return toolbar; 275 } 276 277 import gtk.Entry; 278 import std.string; 279 private void setStatusMessage(Entry f) 280 { 281 /* Get the current connection */ 282 Connection currentConnection = connections[notebook.getCurrentPage()]; 283 284 /* Get the input text (removing leading and trailing whitespace) */ 285 string statusTextInput = f.getBuffer().getText(); 286 statusTextInput = strip(statusTextInput); 287 288 /* Set the text box to the stripped version */ 289 //f.getBuffer().setText(statusTextInput, cast(int)statusTextInput.length); 290 291 /* If the status text is empty */ 292 if(cmp(statusTextInput, "") == 0) 293 { 294 /* Delete the status property */ 295 currentConnection.getClient().deleteProperty("status"); 296 } 297 /* If the status text is non empty */ 298 else 299 { 300 /* Set the status */ 301 currentConnection.getClient().setProperty("status", statusTextInput); 302 } 303 304 //f.setInputHints(GtkInputHints.) 305 306 /* Defocus the currently focused widget which would always be me if you are hitting enter */ 307 mainWindow.setFocus(null); 308 } 309 310 private void about(MenuItem) 311 { 312 import gtk.AboutDialog; 313 AboutDialog about = new AboutDialog(); 314 315 about.setVersion("21893"); 316 317 /* TODO: License */ 318 /* TODO: Icon */ 319 /* TODO: Buttons or close */ 320 /* TODO: Set version based on compiler flag */ 321 322 about.setLogoIconName("user-available"); 323 about.setArtists(["i wonder if I could commision an artwork from her"]); 324 325 /* Set all the information */ 326 about.setLicense("LICENSE GOES HERE"); 327 about.setComments("A clean GTK+ graphical DNET client"); 328 about.setWebsite("http://deavmi.assigned.network/docs/dnet/site"); 329 about.setDocumenters(["ss","fdsfsd"]); 330 about.setAuthors(["Tristan B. Kildaire (Deavmi) - deavmi@disroot.org"]); 331 332 /* Show the about dialog */ 333 about.showAll(); 334 } 335 336 import gtk.Button; 337 338 /** 339 * Returns a Box which contains channel list item 340 */ 341 342 private class JoinButton : Button 343 { 344 private string channelName; 345 this(string channelName) 346 { 347 this.channelName = channelName; 348 } 349 public string getChannelName() 350 { 351 return channelName; 352 } 353 } 354 private Box channelItemList(Connection currentConnection, string channelName) 355 { 356 /* Create the main container */ 357 Box containerMain = new Box(GtkOrientation.HORIZONTAL, 1); 358 359 360 361 /* Add the channel label */ 362 Label channelLabel = new Label(""); 363 channelLabel.setHalign(GtkAlign.START); 364 channelLabel.setMarkup("<b>"~channelName~"</b>"); 365 366 /* Add the member count */ 367 ulong memberCount = currentConnection.getClient().getMemberCount(channelName); 368 Label memberCountLabel = new Label(""); 369 memberCountLabel.setHalign(GtkAlign.START); 370 memberCountLabel.setText(to!(string)(memberCount)~" members"); 371 372 /* Create the channel box */ 373 Box channelBox = new Box(GtkOrientation.VERTICAL, 1); 374 channelBox.add(channelLabel); 375 channelBox.add(memberCountLabel); 376 377 /* Join button */ 378 JoinButton joinButton = new JoinButton(channelName); 379 joinButton.setLabel("Join"); 380 381 382 383 /* Add this then a button */ 384 containerMain.add(channelBox); 385 containerMain.packEnd(joinButton,0,0,0); 386 387 388 389 joinButton.addOnClicked(&selectChannel); 390 391 392 393 /* TODO: COnsider adding member list */ 394 /* TODO: Seperate queue for dynamic updates to this list */ 395 containerMain.setTooltipMarkup("<b>"~channelName~"</b>\n"~to!(string)(memberCount)~" members\n\n"~to!(string)(currentConnection.getClient().getMembers(channelName))); 396 397 return containerMain; 398 } 399 400 401 /** 402 * List channels 403 * 404 * Brings up a window listing channels of the current server 405 */ 406 private void listChannels(ToolButton) 407 { 408 import gtk.Window; 409 410 /* Create the window */ 411 Window win = new Window(GtkWindowType.TOPLEVEL); 412 413 /* Create the list of channels */ 414 ListBox channelsList = new ListBox(); 415 win.add(new ScrolledWindow(channelsList)); 416 417 /* Get the current connection */ 418 Connection currentConnection = connections[notebook.getCurrentPage()]; 419 420 /* Fetch the channels */ 421 string[] channels = currentConnection.getClient().list(); 422 423 /* Add each channel */ 424 foreach(string channel; channels) 425 { 426 // channelsList.add(new Label(channel)); 427 channelsList.add(channelItemList(currentConnection, channel)); 428 writeln("bruh: "~channel); 429 channelsList.showAll(); 430 } 431 432 /* TODO: Add handler for clicking label that lets you join the channel */ 433 // channelsList.addOnSelectedRowsChanged(&selectChannel); 434 //channelsList.add 435 436 437 win.showAll(); 438 } 439 440 /** 441 * Opens a new window for connecting to a server 442 */ 443 private void connect(MenuItem) 444 { 445 import gtk.Window; 446 447 /* Create the window */ 448 Window win = new Window(GtkWindowType.TOPLEVEL); 449 450 451 //import gtk.Text 452 453 454 455 win.showAll(); 456 } 457 458 private void selectChannel(Button s) 459 { 460 /* Get the current connection */ 461 Connection currentConnection = connections[notebook.getCurrentPage()]; 462 463 /* Get the name of the channel selected */ 464 string channelSelected = (cast(JoinButton)s).getChannelName(); //(cast(Label)(s.getSelectedRow().getChild())).getText(); 465 466 /* Join the channel on this connection */ 467 currentConnection.joinChannel(channelSelected); 468 } 469 470 private bool conifgureConnectionsAssistant(string, Label) 471 { 472 import ConnectionAssistant; 473 ConnectionAssistant ass = new ConnectionAssistant(this); 474 return 0; 475 } 476 477 private void setStatus(ToolButton x) 478 { 479 /* If there are any available connections */ 480 if(connections.length) 481 { 482 /* Get the current connection */ 483 Connection currentConnection = connections[notebook.getCurrentPage()]; 484 485 /* Set the status */ 486 currentConnection.getClient().setProperty("pres", x.getLabel()); 487 } 488 /* If there are no connections */ 489 else 490 { 491 import gtk.MessageDialog; 492 MessageDialog errorDialog = new MessageDialog(mainWindow, GtkDialogFlags.MODAL, GtkMessageType.ERROR, GtkButtonsType.CLOSE, false, "Cannot list channels\n\nYou are not connected to a server"); 493 errorDialog.setIconName("user-available"); 494 // errorDialog.set 495 errorDialog.run(); 496 } 497 } 498 499 private MenuBar initializeMenuBar() 500 { 501 MenuBar menuBar = new MenuBar(); 502 503 /* Gustav menu */ 504 MenuItem gustavMenuItem = new MenuItem(); 505 gustavMenuItem.setLabel("Gustav"); 506 Menu gustavMenu = new Menu(); 507 gustavMenuItem.setSubmenu(gustavMenu); 508 509 /* Connect option */ 510 MenuItem connectItem = new MenuItem(); 511 connectItem.setLabel("Connect"); 512 connectItem.addOnActivate(&connectButton); 513 gustavMenu.add(connectItem); 514 515 /* Connect v2 option */ 516 MenuItem connectItem2 = new MenuItem(); 517 connectItem2.setLabel("Connect"); 518 connectItem2.addOnActivate(&connect); 519 gustavMenu.add(connectItem2); 520 521 522 523 /* Exit option */ 524 MenuItem exitItem = new MenuItem(); 525 exitItem.setLabel("Exit"); 526 exitItem.addOnActivate(&exitButton); 527 gustavMenu.add(exitItem); 528 529 530 /* Help menu */ 531 MenuItem helpMenuItem = new MenuItem(); 532 helpMenuItem.setLabel("Help"); 533 Menu helpMenu = new Menu(); 534 helpMenuItem.setSubmenu(helpMenu); 535 536 /* About option */ 537 MenuItem aboutItem = new MenuItem(); 538 aboutItem.setLabel("About"); 539 aboutItem.addOnActivate(&about); 540 helpMenu.add(aboutItem); 541 542 543 544 545 546 /* Add all menues */ 547 menuBar.add(gustavMenuItem); 548 menuBar.add(helpMenuItem); 549 550 return menuBar; 551 } 552 553 private void exitButton(MenuItem) 554 { 555 writeln("bruh"); 556 557 /* TODO: Implement exit */ 558 559 560 // tl(); 561 //te(); 562 563 564 shutdownConnections(); 565 566 567 // mainWindow.showAll(); 568 569 // tl(); 570 } 571 572 private void connectButton(MenuItem) 573 { 574 connectServer("0.0.0.0", 7777, ["testGustav"~to!(string)(connections.length), "bruh"]); 575 } 576 577 /** 578 * Connects to the provided server, 579 * add the tab as well 580 * 581 * NOTE: To be called only by a GTK signal 582 * handler 583 */ 584 public void connectServer(string address, ushort port, string[] authDetails) 585 { 586 /** 587 * If this is our first connection then 588 * create a new Notebook which will 589 * hold the connection/session tabs 590 * and remove the welcome page 591 */ 592 if(!notebook) 593 { 594 notebook = new Notebook(); 595 notebook.setScrollable(true); 596 box.add(notebook); 597 box.setChildPacking(notebook, true, true, 0, GtkPackType.START); 598 box.remove(welcomeBox); 599 box.showAll(); 600 } 601 602 /* Create the new Connection */ 603 Connection newConnection = new Connection(this, parseAddress(address, port), authDetails); 604 connections ~= newConnection; 605 606 // import UserDirectory; 607 // UserDirectory d = new UserDirectory(newConnection); 608 609 } 610 611 private void shutdownConnections() 612 { 613 foreach(Connection connection; connections) 614 { 615 /** 616 * TODO: This is called by signal handler, we need no mutexes for signal handler 617 * hence it means that connection 618 */ 619 connection.shutdown(); 620 Thread.sleep(dur!("seconds")(2)); 621 } 622 } 623 624 private void newServer() 625 { 626 627 } 628 629 private Box createServerTab() 630 { 631 Box serverTab = new Box(GtkOrientation.HORIZONTAL, 1); 632 633 serverTab.add(new Label("hello")); 634 635 // serverTab.add(); 636 637 return serverTab; 638 } 639 }