mirror of https://github.com/buggins/dlangui.git
IRC Client example, continue development
This commit is contained in:
parent
6d0c8d83b3
commit
f82b07e37e
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -38,3 +38,5 @@ TAB_TABLE_LAYOUT=Table layout
|
|||
TAB_EDITORS=Editors
|
||||
TAB_CANVAS=Canvas
|
||||
|
||||
MENU_CONNECT=Connect
|
||||
MENU_DISCONNECT=Connect
|
||||
|
|
Loading…
Reference in New Issue