From f82b07e37e8dccbfa748418e6bdb8da8accab7fb Mon Sep 17 00:00:00 2001 From: Vadim Lopatin Date: Tue, 15 Mar 2016 16:44:54 +0300 Subject: [PATCH] IRC Client example, continue development --- examples/ircclient/src/ircclient/net/client.d | 3 + examples/ircclient/src/ircclient/ui/frame.d | 359 ++++++++++++++++++ .../ircclient/src/ircclient/ui/settingsdlg.d | 93 +++++ examples/ircclient/src/main.d | 330 +--------------- examples/ircclient/views/res/i18n/en.ini | 2 + 5 files changed, 459 insertions(+), 328 deletions(-) create mode 100644 examples/ircclient/src/ircclient/ui/frame.d create mode 100644 examples/ircclient/src/ircclient/ui/settingsdlg.d diff --git a/examples/ircclient/src/ircclient/net/client.d b/examples/ircclient/src/ircclient/net/client.d index b9ec9d5c..d48b33e6 100644 --- a/examples/ircclient/src/ircclient/net/client.d +++ b/examples/ircclient/src/ircclient/net/client.d @@ -304,6 +304,9 @@ public: if (_socket) destroy(_socket); } + @property SocketState state() { + return _socket.state; + } IRCChannel removeChannel(string name) { if (auto p = name in _channels) { _channels.remove(name); diff --git a/examples/ircclient/src/ircclient/ui/frame.d b/examples/ircclient/src/ircclient/ui/frame.d new file mode 100644 index 00000000..615bc5c0 --- /dev/null +++ b/examples/ircclient/src/ircclient/ui/frame.d @@ -0,0 +1,359 @@ +module ircclient.ui.frame; + +import dlangui; +import dlangui.dialogs.filedlg; +import dlangui.dialogs.dialog; +import dlangui.dml.dmlhighlight; +import std.array : replaceFirst; +import ircclient.net.client; +import ircclient.ui.settingsdlg; +import std.string : startsWith, indexOf; + +// action codes +enum IDEActions : int { + //ProjectOpen = 1010000, + FileNew = 1010000, + FileOpen, + FileSave, + FileSaveAs, + FileSaveAll, + FileClose, + FileExit, + EditPreferences, + Connect, + Disconnect, + HelpAbout, +} + +// actions +const Action ACTION_FILE_NEW = new Action(IDEActions.FileNew, "MENU_FILE_NEW"c, "document-new", KeyCode.KEY_N, KeyFlag.Control); +const Action ACTION_FILE_SAVE = (new Action(IDEActions.FileSave, "MENU_FILE_SAVE"c, "document-save", KeyCode.KEY_S, KeyFlag.Control)).disableByDefault(); +const Action ACTION_FILE_SAVE_AS = (new Action(IDEActions.FileSaveAs, "MENU_FILE_SAVE_AS"c)).disableByDefault(); +const Action ACTION_FILE_OPEN = new Action(IDEActions.FileOpen, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control); +const Action ACTION_FILE_EXIT = new Action(IDEActions.FileExit, "MENU_FILE_EXIT"c, "document-close"c, KeyCode.KEY_X, KeyFlag.Alt); +const Action ACTION_EDIT_COPY = (new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy"c, KeyCode.KEY_C, KeyFlag.Control)).addAccelerator(KeyCode.INS, KeyFlag.Control).disableByDefault(); +const Action ACTION_EDIT_PASTE = (new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste"c, KeyCode.KEY_V, KeyFlag.Control)).addAccelerator(KeyCode.INS, KeyFlag.Shift).disableByDefault(); +const Action ACTION_EDIT_CUT = (new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut"c, KeyCode.KEY_X, KeyFlag.Control)).addAccelerator(KeyCode.DEL, KeyFlag.Shift).disableByDefault(); +const Action ACTION_EDIT_UNDO = (new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo"c, KeyCode.KEY_Z, KeyFlag.Control)).disableByDefault(); +const Action ACTION_EDIT_REDO = (new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo"c, KeyCode.KEY_Y, KeyFlag.Control)).addAccelerator(KeyCode.KEY_Z, KeyFlag.Control|KeyFlag.Shift).disableByDefault(); +const Action ACTION_EDIT_INDENT = (new Action(EditorActions.Indent, "MENU_EDIT_INDENT"c, "edit-indent"c, KeyCode.TAB, 0)).addAccelerator(KeyCode.KEY_BRACKETCLOSE, KeyFlag.Control).disableByDefault(); +const Action ACTION_EDIT_UNINDENT = (new Action(EditorActions.Unindent, "MENU_EDIT_UNINDENT"c, "edit-unindent", KeyCode.TAB, KeyFlag.Shift)).addAccelerator(KeyCode.KEY_BRACKETOPEN, KeyFlag.Control).disableByDefault(); +const Action ACTION_EDIT_TOGGLE_LINE_COMMENT = (new Action(EditorActions.ToggleLineComment, "MENU_EDIT_TOGGLE_LINE_COMMENT"c, null, KeyCode.KEY_DIVIDE, KeyFlag.Control)).disableByDefault(); +const Action ACTION_EDIT_TOGGLE_BLOCK_COMMENT = (new Action(EditorActions.ToggleBlockComment, "MENU_EDIT_TOGGLE_BLOCK_COMMENT"c, null, KeyCode.KEY_DIVIDE, KeyFlag.Control|KeyFlag.Shift)).disableByDefault(); + +const Action ACTION_EDIT_PREFERENCES = (new Action(IDEActions.EditPreferences, "MENU_EDIT_PREFERENCES"c, "document-properties"c, KeyCode.F9, 0)); +const Action ACTION_CONNECT = (new Action(IDEActions.Connect, "MENU_CONNECT"c, "debug-run"c, KeyCode.F5, 0)).disableByDefault(); +const Action ACTION_DISCONNECT = (new Action(IDEActions.Disconnect, "MENU_DISCONNECT"c, "debug-run"c, KeyCode.F5, 0)).disableByDefault(); +const Action ACTION_HELP_ABOUT = new Action(IDEActions.HelpAbout, "MENU_HELP_ABOUT"c, "document-open"c, KeyCode.F1, 0); + +class IRCFrame : AppFrame, IRCClientCallback { + + MenuItem mainMenuItems; + IRCClient _client; + + ~this() { + if (_client) + destroy(_client); + } + + override protected void initialize() { + _appName = "DlangUI_IRCClient"; + super.initialize(); + } + + /// create main menu + override protected MainMenu createMainMenu() { + mainMenuItems = new MenuItem(); + MenuItem fileItem = new MenuItem(new Action(1, "MENU_FILE")); + fileItem.add(//ACTION_FILE_NEW, ACTION_FILE_OPEN, + ACTION_HELP_ABOUT, ACTION_EDIT_PREFERENCES, ACTION_FILE_EXIT); + mainMenuItems.add(fileItem); + //MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT")); + //editItem.add(ACTION_EDIT_COPY, ACTION_EDIT_PASTE, + // ACTION_EDIT_CUT, ACTION_EDIT_UNDO, ACTION_EDIT_REDO, + // ACTION_EDIT_INDENT, ACTION_EDIT_UNINDENT, ACTION_EDIT_TOGGLE_LINE_COMMENT, ACTION_EDIT_TOGGLE_BLOCK_COMMENT, ACTION_DEBUG_START); + // + //editItem.add(ACTION_EDIT_PREFERENCES); + //mainMenuItems.add(editItem); + MainMenu mainMenu = new MainMenu(mainMenuItems); + return mainMenu; + } + + + /// create app toolbars + override protected ToolBarHost createToolbars() { + ToolBarHost res = new ToolBarHost(); + ToolBar tb; + tb = res.getOrAddToolbar("Standard"); + tb.addButtons(//ACTION_FILE_NEW, ACTION_FILE_OPEN, ACTION_FILE_SAVE, ACTION_SEPARATOR, + ACTION_CONNECT, + ACTION_DISCONNECT, + ACTION_EDIT_PREFERENCES, + ACTION_HELP_ABOUT); + + //tb = res.getOrAddToolbar("Edit"); + //tb.addButtons(ACTION_EDIT_COPY, ACTION_EDIT_PASTE, ACTION_EDIT_CUT, ACTION_SEPARATOR, + // ACTION_EDIT_UNDO, ACTION_EDIT_REDO, ACTION_EDIT_INDENT, ACTION_EDIT_UNINDENT); + return res; + } + + bool onCanClose() { + // todo + return true; + } + + /// override to handle specific actions + override bool handleAction(const Action a) { + if (a) { + switch (a.id) { + case IDEActions.FileExit: + if (onCanClose()) + window.close(); + return true; + case IDEActions.HelpAbout: + window.showMessageBox(UIString("About DlangUI IRC Client"d), + UIString("DLangUI IRC Client\n(C) Vadim Lopatin, 2015\nhttp://github.com/buggins/dlangui\nSimple IRC client"d)); + return true; + case IDEActions.EditPreferences: + showPreferences(); + return true; + case IDEActions.Connect: + case IDEActions.Disconnect: + if (!_client || _client.state == SocketState.Disconnected) + connect(); + else + _client.disconnect(); + return true; + default: + return super.handleAction(a); + } + } + return false; + } + + void showPreferences() { + SettingsDialog dlg = new SettingsDialog(this); + dlg.dialogResult = delegate(Dialog dlg, const Action result) { + if (result.id == ACTION_APPLY.id) { + //Log.d("settings after edit:\n", s.toJSON(true)); + //_settings.applySettings(s); + //applySettings(_settings); + //_settings.save(); + } + }; + dlg.show(); + } + + /// override to handle specific actions state (e.g. change enabled state for supported actions) + override bool handleActionStateRequest(const Action a) { + switch (a.id) { + case IDEActions.HelpAbout: + case IDEActions.FileNew: + case IDEActions.FileSave: + case IDEActions.FileOpen: + case IDEActions.EditPreferences: + a.state = ACTION_STATE_ENABLED; + return true; + case IDEActions.Connect: + a.state = !_client || _client.state == SocketState.Disconnected ? ACTION_STATE_ENABLED : ACTION_STATE_DISABLE; + return true; + case IDEActions.Disconnect: + a.state = !_client || _client.state == SocketState.Disconnected ? ACTION_STATE_DISABLE : ACTION_STATE_ENABLED; + return true; + default: + return super.handleActionStateRequest(a); + } + } + + TabWidget _tabs; + /// create app body widget + override protected Widget createBody() { + _tabs = new TabWidget("TABS"); + _tabs.layoutWidth = FILL_PARENT; + _tabs.layoutHeight = FILL_PARENT; + //tabs.addTab(new IRCWindow("sample"), "Sample"d); + statusLine.setStatusText(toUTF32("Not Connected")); + return _tabs; + } + + void connect() { + if (!_client) { + _client = new IRCClient(); + AsyncSocket connection = window.createAsyncSocket(_client); + _client.socket = connection; + _client.callback = this; + } + _client.connect("irc.freenode.net", 6667); + } + + IRCWindow getOrCreateWindowFor(string party) { + string winId = party; + IRCWindow w = cast(IRCWindow)_tabs.tabBody(winId); + if (!w) { + w = new IRCWindow(winId, _client); + _tabs.addTab(w, toUTF32(winId)); + } + return w; + } + + void onIRCConnect(IRCClient client) { + IRCWindow w = getOrCreateWindowFor(client.hostPort); + w.addLine("connected to " ~ client.hostPort); + client.sendMessage("USER username 0 * :Real name"); + client.nick("dlangui_irc"); + client.join("#clienttest"); + statusLine.setStatusText(toUTF32("Connected to " ~ client.hostPort)); + } + + void onIRCDisconnect(IRCClient client) { + IRCWindow w = getOrCreateWindowFor(client.hostPort); + w.addLine("disconnected from " ~ client.hostPort); + statusLine.setStatusText(toUTF32("Disconnected")); + } + + void onIRCPing(IRCClient client, string message) { + IRCWindow w = getOrCreateWindowFor(client.hostPort); + w.addLine("PING " ~ message); + client.pong(message); + } + + void onIRCPrivmsg(IRCClient client, IRCAddress source, string target, string message) { + string wid = target.startsWith("#") ? target : client.hostPort; + if (target == client.nick) + wid = source.nick; + else if (source.nick == client.nick) + wid = target; + IRCWindow w = getOrCreateWindowFor(wid); + w.addLine("<" ~ (!source.nick.empty ? source.nick : source.full) ~ "> " ~ message); + } + + void onIRCNotice(IRCClient client, IRCAddress source, string target, string message) { + IRCWindow w = getOrCreateWindowFor(target.startsWith("#") ? target : client.hostPort); + w.addLine("-" ~ source.full ~ "- " ~ message); + } + + void onIRCMessage(IRCClient client, IRCMessage message) { + IRCWindow w = getOrCreateWindowFor(client.hostPort); + switch (message.commandId) with (IRCCommand) { + case JOIN: + case PART: + if (message.sourceAddress && !message.sourceAddress.nick.empty && message.target.startsWith("#")) { + w = getOrCreateWindowFor(message.target); + if (message.commandId == JOIN) { + w.addLine("* " ~ message.sourceAddress.longName ~ " has joined " ~ message.target); + } else { + w.addLine("* " ~ message.sourceAddress.longName ~ " has left " ~ message.target ~ (message.message.empty ? "" : ("(Reason: " ~ message.message ~ ")"))); + } + } + return; + case CHANNEL_NAMES_LIST_END: + if (message.target.startsWith("#")) { + w = getOrCreateWindowFor(message.target); + IRCChannel channel = _client.channelByName(message.target); + w.updateUserList(channel); + } + return; + default: + if (message.commandId < 1000) { + // custom server messages + w.addLine(message.message); + return; + } + break; + } + w.addLine(message.msg); + } +} + +enum IRCWindowKind { + Server, + Channel, + Private +} + +class IRCWindow : VerticalLayout, EditorActionHandler { + LogWidget _editBox; + StringListWidget _listBox; + EditLine _editLine; + IRCClient _client; + IRCWindowKind _kind; + this(string ID, IRCClient client) { + super(ID); + _client = client; + layoutWidth = FILL_PARENT; + layoutHeight = FILL_PARENT; + HorizontalLayout hlayout = new HorizontalLayout(); + hlayout.layoutWidth = FILL_PARENT; + hlayout.layoutHeight = FILL_PARENT; + _editBox = new LogWidget(); + _editBox.layoutWidth = FILL_PARENT; + _editBox.layoutHeight = FILL_PARENT; + hlayout.addChild(_editBox); + if (ID.startsWith("#")) { + _listBox = new StringListWidget(); + _listBox.layoutHeight = FILL_PARENT; + _listBox.layoutWidth = WRAP_CONTENT; + _listBox.minWidth = 100; + _listBox.maxWidth = 200; + _listBox.orientation = Orientation.Vertical; + //_listBox.items = ["Nick1"d, "Nick2"d]; + hlayout.addChild(new ResizerWidget(null, Orientation.Horizontal)); + hlayout.addChild(_listBox); + _kind = IRCWindowKind.Channel; + } else { + if (id.indexOf(':') >= 0) + _kind = IRCWindowKind.Server; + else + _kind = IRCWindowKind.Private; + } + addChild(hlayout); + _editLine = new EditLine(); + addChild(_editLine); + _editLine.editorAction = this; + } + void addLine(string s) { + _editBox.appendText(toUTF32(s ~ "\n")); + if (visible) + window.update(); + } + void updateUserList(IRCChannel channel) { + _listBox.items = channel.userNames; + window.update(); + } + bool onEditorAction(const Action action) { + if (!_editLine.text.empty) { + string s = toUTF8(_editLine.text); + _editLine.text = ""d; + if (s.startsWith("/")) { + Log.d("Custom command: " ~ s); + // command + string cmd = parseDelimitedParameter(s); + + if (cmd == "/quit") { + _client.quit(s); + return true; + } + + string param = parseDelimitedParameter(s); + if (cmd == "/nick" && !param.empty) { + _client.nick(param); + } else if (cmd == "/join" && param.startsWith("#")) { + _client.join(param); + } else if (cmd == "/part" && param.startsWith("#")) { + _client.part(param, s); + } else if (cmd == "/msg" && !param.empty && !s.empty) { + _client.privMsg(param, s); + } else { + Log.d("Unknown command: " ~ cmd); + addLine("Supported commands: /nick /join /part /msg /quit"); + } + } else { + // message + if (_kind != IRCWindowKind.Server) { + _client.privMsg(id, s); + } + } + } + return true; + } +} diff --git a/examples/ircclient/src/ircclient/ui/settingsdlg.d b/examples/ircclient/src/ircclient/ui/settingsdlg.d new file mode 100644 index 00000000..60e5ecc6 --- /dev/null +++ b/examples/ircclient/src/ircclient/ui/settingsdlg.d @@ -0,0 +1,93 @@ +module ircclient.ui.settingsdlg; + +import dlangui.core.stdaction; +import dlangui.dialogs.dialog; +import dlangui.widgets.widget; +import dlangui.dml.parser; +import ircclient.ui.frame; + +class SettingsDialog : Dialog { + IRCFrame _frame; + this(IRCFrame parent) { + super(UIString("IRC Client Settings"d), parent.window, + DialogFlag.Modal | DialogFlag.Resizable | DialogFlag.Popup, 500, 400); + _icon = "dlangui-logo1"; + _frame = parent; + } + + /// override to implement creation of dialog controls + override void initialize() { + super.initialize(); + Widget content; + try { + content = parseML(q{ + VerticalLayout { + id: vlayout + padding: Rect { 5, 5, 5, 5 } + layoutWidth: fill; layoutHeight: fill + HorizontalLayout { + layoutWidth: fill; layoutHeight: fill + VerticalLayout { + margins: 5 + layoutWidth: 25%; layoutHeight: fill + TextWidget { text: "Project template" } + StringListWidget { + id: projectTemplateList + layoutWidth: wrap; layoutHeight: fill + } + } + VerticalLayout { + margins: 5 + layoutWidth: 40%; layoutHeight: fill + TextWidget { text: "Template description" } + EditBox { + id: templateDescription; readOnly: true + layoutWidth: fill; layoutHeight: fill + } + } + VerticalLayout { + layoutWidth: 35%; layoutHeight: fill + margins: 5 + TextWidget { text: "Directory layout" } + EditBox { + id: directoryLayout; readOnly: true + layoutWidth: fill; layoutHeight: fill + } + } + } + TableLayout { + margins: 5 + colCount: 2 + layoutWidth: fill; layoutHeight: wrap + TextWidget { text: "" } + CheckBox { id: cbCreateWorkspace; text: "Create new solution"; checked: true } + TextWidget { text: "Workspace name" } + EditLine { id: edWorkspaceName; text: "newworkspace"; layoutWidth: fill } + TextWidget { text: "" } + CheckBox { id: cbCreateWorkspaceSubdir; text: "Create subdirectory for workspace"; checked: true } + TextWidget { text: "Project name" } + EditLine { id: edProjectName; text: "newproject"; layoutWidth: fill } + TextWidget { text: "" } + CheckBox { id: cbCreateSubdir; text: "Create subdirectory for project"; checked: true } + TextWidget { text: "Location" } + DirEditLine { id: edLocation; layoutWidth: fill } + } + TextWidget { id: statusText; text: ""; layoutWidth: fill } + } + }); + } catch (Exception e) { + Log.e("Exceptin while parsing DML", e); + throw e; + } + addChild(content); + addChild(createButtonsPanel([ACTION_APPLY, ACTION_CANCEL], 0, 0)); + } + + override void close(const Action action) { + Action newaction = action.clone(); + //if (action.id == IDEActions.FileNewWorkspace || action.id == IDEActions.FileNewProject) { + // newaction.objectParam = _result; + //} + super.close(newaction); + } +} diff --git a/examples/ircclient/src/main.d b/examples/ircclient/src/main.d index aaa8694b..8a1f00f8 100644 --- a/examples/ircclient/src/main.d +++ b/examples/ircclient/src/main.d @@ -1,336 +1,10 @@ -module dmledit; +module main; import dlangui; -import dlangui.dialogs.filedlg; -import dlangui.dialogs.dialog; -import dlangui.dml.dmlhighlight; -import std.array : replaceFirst; -import ircclient.net.client; -import std.string : startsWith, indexOf; +import ircclient.ui.frame; mixin APP_ENTRY_POINT; -// action codes -enum IDEActions : int { - //ProjectOpen = 1010000, - FileNew = 1010000, - FileOpen, - FileSave, - FileSaveAs, - FileSaveAll, - FileClose, - FileExit, - EditPreferences, - DebugStart, - HelpAbout, -} - -// actions -const Action ACTION_FILE_NEW = new Action(IDEActions.FileNew, "MENU_FILE_NEW"c, "document-new", KeyCode.KEY_N, KeyFlag.Control); -const Action ACTION_FILE_SAVE = (new Action(IDEActions.FileSave, "MENU_FILE_SAVE"c, "document-save", KeyCode.KEY_S, KeyFlag.Control)).disableByDefault(); -const Action ACTION_FILE_SAVE_AS = (new Action(IDEActions.FileSaveAs, "MENU_FILE_SAVE_AS"c)).disableByDefault(); -const Action ACTION_FILE_OPEN = new Action(IDEActions.FileOpen, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control); -const Action ACTION_FILE_EXIT = new Action(IDEActions.FileExit, "MENU_FILE_EXIT"c, "document-close"c, KeyCode.KEY_X, KeyFlag.Alt); -const Action ACTION_EDIT_COPY = (new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy"c, KeyCode.KEY_C, KeyFlag.Control)).addAccelerator(KeyCode.INS, KeyFlag.Control).disableByDefault(); -const Action ACTION_EDIT_PASTE = (new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste"c, KeyCode.KEY_V, KeyFlag.Control)).addAccelerator(KeyCode.INS, KeyFlag.Shift).disableByDefault(); -const Action ACTION_EDIT_CUT = (new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut"c, KeyCode.KEY_X, KeyFlag.Control)).addAccelerator(KeyCode.DEL, KeyFlag.Shift).disableByDefault(); -const Action ACTION_EDIT_UNDO = (new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo"c, KeyCode.KEY_Z, KeyFlag.Control)).disableByDefault(); -const Action ACTION_EDIT_REDO = (new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo"c, KeyCode.KEY_Y, KeyFlag.Control)).addAccelerator(KeyCode.KEY_Z, KeyFlag.Control|KeyFlag.Shift).disableByDefault(); -const Action ACTION_EDIT_INDENT = (new Action(EditorActions.Indent, "MENU_EDIT_INDENT"c, "edit-indent"c, KeyCode.TAB, 0)).addAccelerator(KeyCode.KEY_BRACKETCLOSE, KeyFlag.Control).disableByDefault(); -const Action ACTION_EDIT_UNINDENT = (new Action(EditorActions.Unindent, "MENU_EDIT_UNINDENT"c, "edit-unindent", KeyCode.TAB, KeyFlag.Shift)).addAccelerator(KeyCode.KEY_BRACKETOPEN, KeyFlag.Control).disableByDefault(); -const Action ACTION_EDIT_TOGGLE_LINE_COMMENT = (new Action(EditorActions.ToggleLineComment, "MENU_EDIT_TOGGLE_LINE_COMMENT"c, null, KeyCode.KEY_DIVIDE, KeyFlag.Control)).disableByDefault(); -const Action ACTION_EDIT_TOGGLE_BLOCK_COMMENT = (new Action(EditorActions.ToggleBlockComment, "MENU_EDIT_TOGGLE_BLOCK_COMMENT"c, null, KeyCode.KEY_DIVIDE, KeyFlag.Control|KeyFlag.Shift)).disableByDefault(); -const Action ACTION_EDIT_PREFERENCES = (new Action(IDEActions.EditPreferences, "MENU_EDIT_PREFERENCES"c, null)).disableByDefault(); -const Action ACTION_DEBUG_START = new Action(IDEActions.DebugStart, "MENU_DEBUG_UPDATE_PREVIEW"c, "debug-run"c, KeyCode.F5, 0); -const Action ACTION_HELP_ABOUT = new Action(IDEActions.HelpAbout, "MENU_HELP_ABOUT"c); - -class IRCFrame : AppFrame, IRCClientCallback { - - MenuItem mainMenuItems; - IRCClient _client; - - ~this() { - if (_client) - destroy(_client); - } - - override protected void initialize() { - _appName = "DlangUI_IRCClient"; - super.initialize(); - } - - /// create main menu - override protected MainMenu createMainMenu() { - mainMenuItems = new MenuItem(); - MenuItem fileItem = new MenuItem(new Action(1, "MENU_FILE")); - fileItem.add(//ACTION_FILE_NEW, ACTION_FILE_OPEN, - ACTION_FILE_EXIT); - mainMenuItems.add(fileItem); - //MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT")); - //editItem.add(ACTION_EDIT_COPY, ACTION_EDIT_PASTE, - // ACTION_EDIT_CUT, ACTION_EDIT_UNDO, ACTION_EDIT_REDO, - // ACTION_EDIT_INDENT, ACTION_EDIT_UNINDENT, ACTION_EDIT_TOGGLE_LINE_COMMENT, ACTION_EDIT_TOGGLE_BLOCK_COMMENT, ACTION_DEBUG_START); - // - //editItem.add(ACTION_EDIT_PREFERENCES); - //mainMenuItems.add(editItem); - MainMenu mainMenu = new MainMenu(mainMenuItems); - return mainMenu; - } - - - /// create app toolbars - override protected ToolBarHost createToolbars() { - ToolBarHost res = new ToolBarHost(); - ToolBar tb; - tb = res.getOrAddToolbar("Standard"); - tb.addButtons(//ACTION_FILE_NEW, ACTION_FILE_OPEN, ACTION_FILE_SAVE, ACTION_SEPARATOR, - ACTION_DEBUG_START); - - //tb = res.getOrAddToolbar("Edit"); - //tb.addButtons(ACTION_EDIT_COPY, ACTION_EDIT_PASTE, ACTION_EDIT_CUT, ACTION_SEPARATOR, - // ACTION_EDIT_UNDO, ACTION_EDIT_REDO, ACTION_EDIT_INDENT, ACTION_EDIT_UNINDENT); - return res; - } - - bool onCanClose() { - // todo - return true; - } - - /// override to handle specific actions - override bool handleAction(const Action a) { - if (a) { - switch (a.id) { - case IDEActions.FileExit: - if (onCanClose()) - window.close(); - return true; - case IDEActions.HelpAbout: - window.showMessageBox(UIString("About DlangUI IRC Client"d), - UIString("DLangUI IRC Client\n(C) Vadim Lopatin, 2015\nhttp://github.com/buggins/dlangui\nSimple IRC client"d)); - return true; - case IDEActions.EditPreferences: - //showPreferences(); - return true; - case IDEActions.DebugStart: - connect(); - return true; - default: - return super.handleAction(a); - } - } - return false; - } - - /// override to handle specific actions state (e.g. change enabled state for supported actions) - override bool handleActionStateRequest(const Action a) { - switch (a.id) { - case IDEActions.HelpAbout: - case IDEActions.FileNew: - case IDEActions.FileSave: - case IDEActions.FileOpen: - case IDEActions.DebugStart: - case IDEActions.EditPreferences: - a.state = ACTION_STATE_ENABLED; - return true; - default: - return super.handleActionStateRequest(a); - } - } - - TabWidget _tabs; - /// create app body widget - override protected Widget createBody() { - _tabs = new TabWidget("TABS"); - _tabs.layoutWidth = FILL_PARENT; - _tabs.layoutHeight = FILL_PARENT; - //tabs.addTab(new IRCWindow("sample"), "Sample"d); - statusLine.setStatusText(toUTF32("Not Connected")); - return _tabs; - } - - void connect() { - if (!_client) { - _client = new IRCClient(); - AsyncSocket connection = window.createAsyncSocket(_client); - _client.socket = connection; - _client.callback = this; - } - _client.connect("irc.freenode.net", 6667); - } - - IRCWindow getOrCreateWindowFor(string party) { - string winId = party; - IRCWindow w = cast(IRCWindow)_tabs.tabBody(winId); - if (!w) { - w = new IRCWindow(winId, _client); - _tabs.addTab(w, toUTF32(winId)); - } - return w; - } - - void onIRCConnect(IRCClient client) { - IRCWindow w = getOrCreateWindowFor(client.hostPort); - w.addLine("connected to " ~ client.hostPort); - client.sendMessage("USER username 0 * :Real name"); - client.nick("dlangui_irc"); - client.join("#clienttest"); - statusLine.setStatusText(toUTF32("Connected to " ~ client.hostPort)); - } - - void onIRCDisconnect(IRCClient client) { - IRCWindow w = getOrCreateWindowFor(client.hostPort); - w.addLine("disconnected from " ~ client.hostPort); - statusLine.setStatusText(toUTF32("Disconnected")); - } - - void onIRCPing(IRCClient client, string message) { - IRCWindow w = getOrCreateWindowFor(client.hostPort); - w.addLine("PING " ~ message); - client.pong(message); - } - - void onIRCPrivmsg(IRCClient client, IRCAddress source, string target, string message) { - string wid = target.startsWith("#") ? target : client.hostPort; - if (target == client.nick) - wid = source.nick; - else if (source.nick == client.nick) - wid = target; - IRCWindow w = getOrCreateWindowFor(wid); - w.addLine("<" ~ (!source.nick.empty ? source.nick : source.full) ~ "> " ~ message); - } - - void onIRCNotice(IRCClient client, IRCAddress source, string target, string message) { - IRCWindow w = getOrCreateWindowFor(target.startsWith("#") ? target : client.hostPort); - w.addLine("-" ~ source.full ~ "- " ~ message); - } - - void onIRCMessage(IRCClient client, IRCMessage message) { - IRCWindow w = getOrCreateWindowFor(client.hostPort); - switch (message.commandId) with (IRCCommand) { - case JOIN: - case PART: - if (message.sourceAddress && !message.sourceAddress.nick.empty && message.target.startsWith("#")) { - w = getOrCreateWindowFor(message.target); - if (message.commandId == JOIN) { - w.addLine("* " ~ message.sourceAddress.longName ~ " has joined " ~ message.target); - } else { - w.addLine("* " ~ message.sourceAddress.longName ~ " has left " ~ message.target ~ (message.message.empty ? "" : ("(Reason: " ~ message.message ~ ")"))); - } - } - return; - case CHANNEL_NAMES_LIST_END: - if (message.target.startsWith("#")) { - w = getOrCreateWindowFor(message.target); - IRCChannel channel = _client.channelByName(message.target); - w.updateUserList(channel); - } - return; - default: - if (message.commandId < 1000) { - // custom server messages - w.addLine(message.message); - return; - } - break; - } - w.addLine(message.msg); - } -} - -enum IRCWindowKind { - Server, - Channel, - Private -} - -class IRCWindow : VerticalLayout, EditorActionHandler { - LogWidget _editBox; - StringListWidget _listBox; - EditLine _editLine; - IRCClient _client; - IRCWindowKind _kind; - this(string ID, IRCClient client) { - super(ID); - _client = client; - layoutWidth = FILL_PARENT; - layoutHeight = FILL_PARENT; - HorizontalLayout hlayout = new HorizontalLayout(); - hlayout.layoutWidth = FILL_PARENT; - hlayout.layoutHeight = FILL_PARENT; - _editBox = new LogWidget(); - _editBox.layoutWidth = FILL_PARENT; - _editBox.layoutHeight = FILL_PARENT; - hlayout.addChild(_editBox); - if (ID.startsWith("#")) { - _listBox = new StringListWidget(); - _listBox.layoutHeight = FILL_PARENT; - _listBox.layoutWidth = WRAP_CONTENT; - _listBox.minWidth = 100; - _listBox.maxWidth = 200; - _listBox.orientation = Orientation.Vertical; - //_listBox.items = ["Nick1"d, "Nick2"d]; - hlayout.addChild(new ResizerWidget(null, Orientation.Horizontal)); - hlayout.addChild(_listBox); - _kind = IRCWindowKind.Channel; - } else { - if (id.indexOf(':') >= 0) - _kind = IRCWindowKind.Server; - else - _kind = IRCWindowKind.Private; - } - addChild(hlayout); - _editLine = new EditLine(); - addChild(_editLine); - _editLine.editorAction = this; - } - void addLine(string s) { - _editBox.appendText(toUTF32(s ~ "\n")); - if (visible) - window.update(); - } - void updateUserList(IRCChannel channel) { - _listBox.items = channel.userNames; - window.update(); - } - bool onEditorAction(const Action action) { - if (!_editLine.text.empty) { - string s = toUTF8(_editLine.text); - _editLine.text = ""d; - if (s.startsWith("/")) { - Log.d("Custom command: " ~ s); - // command - string cmd = parseDelimitedParameter(s); - - if (cmd == "/quit") { - _client.quit(s); - return true; - } - - string param = parseDelimitedParameter(s); - if (cmd == "/nick" && !param.empty) { - _client.nick(param); - } else if (cmd == "/join" && param.startsWith("#")) { - _client.join(param); - } else if (cmd == "/part" && param.startsWith("#")) { - _client.part(param, s); - } else if (cmd == "/msg" && !param.empty && !s.empty) { - _client.privMsg(param, s); - } else { - Log.d("Unknown command: " ~ cmd); - addLine("Supported commands: /nick /join /part /msg /quit"); - } - } else { - // message - if (_kind != IRCWindowKind.Server) { - _client.privMsg(id, s); - } - } - } - return true; - } -} - /// entry point for dlangui based application extern (C) int UIAppMain(string[] args) { diff --git a/examples/ircclient/views/res/i18n/en.ini b/examples/ircclient/views/res/i18n/en.ini index 576671ef..d0b8e479 100644 --- a/examples/ircclient/views/res/i18n/en.ini +++ b/examples/ircclient/views/res/i18n/en.ini @@ -38,3 +38,5 @@ TAB_TABLE_LAYOUT=Table layout TAB_EDITORS=Editors TAB_CANVAS=Canvas +MENU_CONNECT=Connect +MENU_DISCONNECT=Connect