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 }