mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 21:57:35 +00:00
Applications: Move to Userland/Applications/
This commit is contained in:
parent
aa939c4b4b
commit
dc28c07fa5
287 changed files with 1 additions and 1 deletions
14
Userland/Applications/IRCClient/CMakeLists.txt
Normal file
14
Userland/Applications/IRCClient/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
set(SOURCES
|
||||
IRCAppWindow.cpp
|
||||
IRCChannel.cpp
|
||||
IRCChannelMemberListModel.cpp
|
||||
IRCClient.cpp
|
||||
IRCLogBuffer.cpp
|
||||
IRCQuery.cpp
|
||||
IRCWindow.cpp
|
||||
IRCWindowListModel.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_app(IRCClient ICON app-irc-client)
|
||||
target_link_libraries(IRCClient LibWeb LibGUI)
|
375
Userland/Applications/IRCClient/IRCAppWindow.cpp
Normal file
375
Userland/Applications/IRCClient/IRCAppWindow.cpp
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCAppWindow.h"
|
||||
#include "IRCChannel.h"
|
||||
#include "IRCWindow.h"
|
||||
#include "IRCWindowListModel.h"
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/InputBox.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/MenuBar.h>
|
||||
#include <LibGUI/Splitter.h>
|
||||
#include <LibGUI/StackWidget.h>
|
||||
#include <LibGUI/TableView.h>
|
||||
#include <LibGUI/ToolBar.h>
|
||||
#include <LibGUI/ToolBarContainer.h>
|
||||
|
||||
static IRCAppWindow* s_the;
|
||||
|
||||
IRCAppWindow& IRCAppWindow::the()
|
||||
{
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
IRCAppWindow::IRCAppWindow(String server, int port)
|
||||
: m_client(IRCClient::construct(server, port))
|
||||
{
|
||||
ASSERT(!s_the);
|
||||
s_the = this;
|
||||
|
||||
set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app-irc-client.png"));
|
||||
|
||||
update_title();
|
||||
resize(600, 400);
|
||||
setup_actions();
|
||||
setup_menus();
|
||||
setup_widgets();
|
||||
|
||||
setup_client();
|
||||
}
|
||||
|
||||
IRCAppWindow::~IRCAppWindow()
|
||||
{
|
||||
}
|
||||
|
||||
void IRCAppWindow::update_title()
|
||||
{
|
||||
set_title(String::formatted("{}@{}:{} - IRC Client", m_client->nickname(), m_client->hostname(), m_client->port()));
|
||||
}
|
||||
|
||||
void IRCAppWindow::setup_client()
|
||||
{
|
||||
m_client->aid_create_window = [this](void* owner, IRCWindow::Type type, const String& name) {
|
||||
return create_window(owner, type, name);
|
||||
};
|
||||
m_client->aid_get_active_window = [this] {
|
||||
return static_cast<IRCWindow*>(m_container->active_widget());
|
||||
};
|
||||
m_client->aid_update_window_list = [this] {
|
||||
m_window_list->model()->update();
|
||||
};
|
||||
m_client->on_nickname_changed = [this](const String&) {
|
||||
update_title();
|
||||
};
|
||||
m_client->on_part_from_channel = [this](auto&) {
|
||||
update_gui_actions();
|
||||
};
|
||||
|
||||
if (m_client->hostname().is_empty()) {
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter server:", "Connect to server") == GUI::InputBox::ExecCancel)
|
||||
::exit(0);
|
||||
|
||||
m_client->set_server(value, 6667);
|
||||
}
|
||||
update_title();
|
||||
bool success = m_client->connect();
|
||||
ASSERT(success);
|
||||
}
|
||||
|
||||
void IRCAppWindow::setup_actions()
|
||||
{
|
||||
m_join_action = GUI::Action::create("Join channel", { Mod_Ctrl, Key_J }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-join.png"), [&](auto&) {
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter channel name:", "Join channel") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_join_action(value);
|
||||
});
|
||||
|
||||
m_list_channels_action = GUI::Action::create("List channels", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-list.png"), [&](auto&) {
|
||||
m_client->handle_list_channels_action();
|
||||
});
|
||||
|
||||
m_part_action = GUI::Action::create("Part from channel", { Mod_Ctrl, Key_P }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-part.png"), [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
m_client->handle_part_action(window->channel().name());
|
||||
});
|
||||
|
||||
m_whois_action = GUI::Action::create("Whois user", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-whois.png"), [&](auto&) {
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nickname:", "IRC WHOIS lookup") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_whois_action(value);
|
||||
});
|
||||
|
||||
m_open_query_action = GUI::Action::create("Open query", { Mod_Ctrl, Key_O }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-open-query.png"), [&](auto&) {
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nickname:", "Open IRC query with...") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_open_query_action(value);
|
||||
});
|
||||
|
||||
m_close_query_action = GUI::Action::create("Close query", { Mod_Ctrl, Key_D }, Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-close-query.png"), [](auto&) {
|
||||
outln("FIXME: Implement close-query action");
|
||||
});
|
||||
|
||||
m_change_nick_action = GUI::Action::create("Change nickname", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-nick.png"), [this](auto&) {
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nickname:", "Change nickname") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_change_nick_action(value);
|
||||
});
|
||||
|
||||
m_change_topic_action = GUI::Action::create("Change topic", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-topic.png"), [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter topic:", "Change topic") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_change_topic_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_invite_user_action = GUI::Action::create("Invite user", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-invite.png"), [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "Invite user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_invite_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_banlist_action = GUI::Action::create("Ban list", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
m_client->handle_banlist_action(window->channel().name());
|
||||
});
|
||||
|
||||
m_voice_user_action = GUI::Action::create("Voice user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "Voice user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_voice_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_devoice_user_action = GUI::Action::create("DeVoice user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "DeVoice user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_devoice_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_hop_user_action = GUI::Action::create("Hop user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "Hop user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_hop_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_dehop_user_action = GUI::Action::create("DeHop user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "DeHop user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_dehop_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_op_user_action = GUI::Action::create("Op user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "Op user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_op_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_deop_user_action = GUI::Action::create("DeOp user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, this, "Enter nick:", "DeOp user") == GUI::InputBox::ExecOK && !value.is_empty())
|
||||
m_client->handle_deop_user_action(window->channel().name(), value);
|
||||
});
|
||||
|
||||
m_kick_user_action = GUI::Action::create("Kick user", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
String nick_value;
|
||||
if (GUI::InputBox::show(nick_value, this, "Enter nick:", "Kick user") != GUI::InputBox::ExecOK || nick_value.is_empty())
|
||||
return;
|
||||
String reason_value;
|
||||
if (GUI::InputBox::show(reason_value, this, "Enter reason:", "Reason") == GUI::InputBox::ExecOK)
|
||||
m_client->handle_kick_user_action(window->channel().name(), nick_value, reason_value.characters());
|
||||
});
|
||||
|
||||
m_cycle_channel_action = GUI::Action::create("Cycle channel", [this](auto&) {
|
||||
auto* window = m_client->current_window();
|
||||
if (!window || window->type() != IRCWindow::Type::Channel) {
|
||||
return;
|
||||
}
|
||||
m_client->handle_cycle_channel_action(window->channel().name());
|
||||
});
|
||||
}
|
||||
|
||||
void IRCAppWindow::setup_menus()
|
||||
{
|
||||
auto menubar = GUI::MenuBar::construct();
|
||||
auto& app_menu = menubar->add_menu("IRC Client");
|
||||
app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
|
||||
dbgln("Terminal: Quit menu activated!");
|
||||
GUI::Application::the()->quit();
|
||||
return;
|
||||
}));
|
||||
|
||||
auto& server_menu = menubar->add_menu("Server");
|
||||
server_menu.add_action(*m_change_nick_action);
|
||||
server_menu.add_separator();
|
||||
server_menu.add_action(*m_join_action);
|
||||
server_menu.add_action(*m_list_channels_action);
|
||||
server_menu.add_separator();
|
||||
server_menu.add_action(*m_whois_action);
|
||||
server_menu.add_action(*m_open_query_action);
|
||||
server_menu.add_action(*m_close_query_action);
|
||||
|
||||
auto& channel_menu = menubar->add_menu("Channel");
|
||||
channel_menu.add_action(*m_change_topic_action);
|
||||
channel_menu.add_action(*m_invite_user_action);
|
||||
channel_menu.add_action(*m_banlist_action);
|
||||
|
||||
auto& channel_control_menu = channel_menu.add_submenu("Control");
|
||||
channel_control_menu.add_action(*m_voice_user_action);
|
||||
channel_control_menu.add_action(*m_devoice_user_action);
|
||||
channel_control_menu.add_action(*m_hop_user_action);
|
||||
channel_control_menu.add_action(*m_dehop_user_action);
|
||||
channel_control_menu.add_action(*m_op_user_action);
|
||||
channel_control_menu.add_action(*m_deop_user_action);
|
||||
channel_control_menu.add_separator();
|
||||
channel_control_menu.add_action(*m_kick_user_action);
|
||||
|
||||
channel_menu.add_separator();
|
||||
channel_menu.add_action(*m_cycle_channel_action);
|
||||
channel_menu.add_action(*m_part_action);
|
||||
|
||||
auto& help_menu = menubar->add_menu("Help");
|
||||
help_menu.add_action(GUI::CommonActions::make_about_action("IRC Client", GUI::Icon::default_icon("app-irc-client"), this));
|
||||
|
||||
GUI::Application::the()->set_menubar(move(menubar));
|
||||
}
|
||||
|
||||
void IRCAppWindow::setup_widgets()
|
||||
{
|
||||
auto& widget = set_main_widget<GUI::Widget>();
|
||||
widget.set_fill_with_background_color(true);
|
||||
widget.set_layout<GUI::VerticalBoxLayout>();
|
||||
widget.layout()->set_spacing(0);
|
||||
|
||||
auto& toolbar_container = widget.add<GUI::ToolBarContainer>();
|
||||
auto& toolbar = toolbar_container.add<GUI::ToolBar>();
|
||||
toolbar.set_has_frame(false);
|
||||
toolbar.add_action(*m_change_nick_action);
|
||||
toolbar.add_separator();
|
||||
toolbar.add_action(*m_join_action);
|
||||
toolbar.add_action(*m_part_action);
|
||||
toolbar.add_separator();
|
||||
toolbar.add_action(*m_whois_action);
|
||||
toolbar.add_action(*m_open_query_action);
|
||||
toolbar.add_action(*m_close_query_action);
|
||||
|
||||
auto& outer_container = widget.add<GUI::Widget>();
|
||||
outer_container.set_layout<GUI::VerticalBoxLayout>();
|
||||
outer_container.layout()->set_margins({ 2, 0, 2, 2 });
|
||||
|
||||
auto& horizontal_container = outer_container.add<GUI::HorizontalSplitter>();
|
||||
|
||||
m_window_list = horizontal_container.add<GUI::TableView>();
|
||||
m_window_list->set_column_headers_visible(false);
|
||||
m_window_list->set_alternating_row_colors(false);
|
||||
m_window_list->set_model(m_client->client_window_list_model());
|
||||
m_window_list->set_activates_on_selection(true);
|
||||
m_window_list->set_fixed_width(100);
|
||||
m_window_list->on_activation = [this](auto& index) {
|
||||
set_active_window(m_client->window_at(index.row()));
|
||||
};
|
||||
|
||||
m_container = horizontal_container.add<GUI::StackWidget>();
|
||||
m_container->on_active_widget_change = [this](auto*) {
|
||||
update_gui_actions();
|
||||
};
|
||||
|
||||
create_window(&m_client, IRCWindow::Server, "Server");
|
||||
}
|
||||
|
||||
void IRCAppWindow::set_active_window(IRCWindow& window)
|
||||
{
|
||||
m_container->set_active_widget(&window);
|
||||
window.clear_unread_count();
|
||||
auto index = m_window_list->model()->index(m_client->window_index(window));
|
||||
m_window_list->selection().set(index);
|
||||
}
|
||||
|
||||
void IRCAppWindow::update_gui_actions()
|
||||
{
|
||||
auto* window = static_cast<IRCWindow*>(m_container->active_widget());
|
||||
bool is_open_channel = window && window->type() == IRCWindow::Type::Channel && window->channel().is_open();
|
||||
m_change_topic_action->set_enabled(is_open_channel);
|
||||
m_invite_user_action->set_enabled(is_open_channel);
|
||||
m_banlist_action->set_enabled(is_open_channel);
|
||||
m_voice_user_action->set_enabled(is_open_channel);
|
||||
m_devoice_user_action->set_enabled(is_open_channel);
|
||||
m_hop_user_action->set_enabled(is_open_channel);
|
||||
m_dehop_user_action->set_enabled(is_open_channel);
|
||||
m_op_user_action->set_enabled(is_open_channel);
|
||||
m_deop_user_action->set_enabled(is_open_channel);
|
||||
m_kick_user_action->set_enabled(is_open_channel);
|
||||
m_cycle_channel_action->set_enabled(is_open_channel);
|
||||
m_part_action->set_enabled(is_open_channel);
|
||||
}
|
||||
|
||||
NonnullRefPtr<IRCWindow> IRCAppWindow::create_window(void* owner, IRCWindow::Type type, const String& name)
|
||||
{
|
||||
return m_container->add<IRCWindow>(m_client, owner, type, name);
|
||||
}
|
76
Userland/Applications/IRCClient/IRCAppWindow.h
Normal file
76
Userland/Applications/IRCClient/IRCAppWindow.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IRCClient.h"
|
||||
#include "IRCWindow.h"
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
class IRCAppWindow : public GUI::Window {
|
||||
C_OBJECT(IRCAppWindow);
|
||||
|
||||
public:
|
||||
virtual ~IRCAppWindow() override;
|
||||
|
||||
static IRCAppWindow& the();
|
||||
|
||||
void set_active_window(IRCWindow&);
|
||||
|
||||
private:
|
||||
IRCAppWindow(String server, int port);
|
||||
|
||||
void setup_client();
|
||||
void setup_actions();
|
||||
void setup_menus();
|
||||
void setup_widgets();
|
||||
void update_title();
|
||||
void update_gui_actions();
|
||||
|
||||
NonnullRefPtr<IRCWindow> create_window(void* owner, IRCWindow::Type, const String& name);
|
||||
NonnullRefPtr<IRCClient> m_client;
|
||||
RefPtr<GUI::StackWidget> m_container;
|
||||
RefPtr<GUI::TableView> m_window_list;
|
||||
RefPtr<GUI::Action> m_join_action;
|
||||
RefPtr<GUI::Action> m_list_channels_action;
|
||||
RefPtr<GUI::Action> m_part_action;
|
||||
RefPtr<GUI::Action> m_cycle_channel_action;
|
||||
RefPtr<GUI::Action> m_whois_action;
|
||||
RefPtr<GUI::Action> m_open_query_action;
|
||||
RefPtr<GUI::Action> m_close_query_action;
|
||||
RefPtr<GUI::Action> m_change_nick_action;
|
||||
RefPtr<GUI::Action> m_change_topic_action;
|
||||
RefPtr<GUI::Action> m_invite_user_action;
|
||||
RefPtr<GUI::Action> m_banlist_action;
|
||||
RefPtr<GUI::Action> m_voice_user_action;
|
||||
RefPtr<GUI::Action> m_devoice_user_action;
|
||||
RefPtr<GUI::Action> m_hop_user_action;
|
||||
RefPtr<GUI::Action> m_dehop_user_action;
|
||||
RefPtr<GUI::Action> m_op_user_action;
|
||||
RefPtr<GUI::Action> m_deop_user_action;
|
||||
RefPtr<GUI::Action> m_kick_user_action;
|
||||
};
|
144
Userland/Applications/IRCClient/IRCChannel.cpp
Normal file
144
Userland/Applications/IRCClient/IRCChannel.cpp
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCChannel.h"
|
||||
#include "IRCChannelMemberListModel.h"
|
||||
#include "IRCClient.h"
|
||||
#include <stdio.h>
|
||||
|
||||
IRCChannel::IRCChannel(IRCClient& client, const String& name)
|
||||
: m_client(client)
|
||||
, m_name(name)
|
||||
, m_log(IRCLogBuffer::create())
|
||||
, m_member_model(IRCChannelMemberListModel::create(*this))
|
||||
{
|
||||
m_window = m_client.aid_create_window(this, IRCWindow::Channel, m_name);
|
||||
m_window->set_log_buffer(*m_log);
|
||||
}
|
||||
|
||||
IRCChannel::~IRCChannel()
|
||||
{
|
||||
}
|
||||
|
||||
NonnullRefPtr<IRCChannel> IRCChannel::create(IRCClient& client, const String& name)
|
||||
{
|
||||
return adopt(*new IRCChannel(client, name));
|
||||
}
|
||||
|
||||
void IRCChannel::add_member(const String& name, char prefix)
|
||||
{
|
||||
for (auto& member : m_members) {
|
||||
if (member.name == name) {
|
||||
member.prefix = prefix;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_members.append({ name, prefix });
|
||||
m_member_model->update();
|
||||
}
|
||||
|
||||
void IRCChannel::remove_member(const String& name)
|
||||
{
|
||||
m_members.remove_first_matching([&](auto& member) { return name == member.name; });
|
||||
}
|
||||
|
||||
void IRCChannel::add_message(char prefix, const String& name, const String& text, Color color)
|
||||
{
|
||||
log().add_message(prefix, name, text, color);
|
||||
window().did_add_message(name, text);
|
||||
}
|
||||
|
||||
void IRCChannel::add_message(const String& text, Color color)
|
||||
{
|
||||
log().add_message(text, color);
|
||||
window().did_add_message();
|
||||
}
|
||||
|
||||
void IRCChannel::say(const String& text)
|
||||
{
|
||||
m_client.send_privmsg(m_name, text);
|
||||
add_message(' ', m_client.nickname(), text);
|
||||
}
|
||||
|
||||
void IRCChannel::handle_join(const String& nick, const String& hostmask)
|
||||
{
|
||||
if (nick == m_client.nickname()) {
|
||||
m_open = true;
|
||||
return;
|
||||
}
|
||||
add_member(nick, (char)0);
|
||||
m_member_model->update();
|
||||
if (m_client.show_join_part_messages())
|
||||
add_message(String::formatted("*** {} [{}] has joined {}", nick, hostmask, m_name), Color::MidGreen);
|
||||
}
|
||||
|
||||
void IRCChannel::handle_part(const String& nick, const String& hostmask)
|
||||
{
|
||||
if (nick == m_client.nickname()) {
|
||||
m_open = false;
|
||||
m_members.clear();
|
||||
m_client.did_part_from_channel({}, *this);
|
||||
} else {
|
||||
remove_member(nick);
|
||||
}
|
||||
m_member_model->update();
|
||||
if (m_client.show_join_part_messages())
|
||||
add_message(String::formatted("*** {} [{}] has parted from {}", nick, hostmask, m_name), Color::MidGreen);
|
||||
}
|
||||
|
||||
void IRCChannel::handle_quit(const String& nick, const String& hostmask, const String& message)
|
||||
{
|
||||
if (nick == m_client.nickname()) {
|
||||
m_open = false;
|
||||
m_members.clear();
|
||||
m_client.did_part_from_channel({}, *this);
|
||||
} else {
|
||||
remove_member(nick);
|
||||
}
|
||||
m_member_model->update();
|
||||
add_message(String::formatted("*** {} [{}] has quit ({})", nick, hostmask, message), Color::MidGreen);
|
||||
}
|
||||
|
||||
void IRCChannel::handle_topic(const String& nick, const String& topic)
|
||||
{
|
||||
if (nick.is_null())
|
||||
add_message(String::formatted("*** Topic is \"{}\"", topic), Color::MidBlue);
|
||||
else
|
||||
add_message(String::formatted("*** {} set topic to \"{}\"", nick, topic), Color::MidBlue);
|
||||
}
|
||||
|
||||
void IRCChannel::notify_nick_changed(const String& old_nick, const String& new_nick)
|
||||
{
|
||||
for (auto& member : m_members) {
|
||||
if (member.name == old_nick) {
|
||||
member.name = new_nick;
|
||||
m_member_model->update();
|
||||
if (m_client.show_nick_change_messages())
|
||||
add_message(String::formatted("~ {} changed nickname to {}", old_nick, new_nick), Color::MidMagenta);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
94
Userland/Applications/IRCClient/IRCChannel.h
Normal file
94
Userland/Applications/IRCClient/IRCChannel.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IRCLogBuffer.h"
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
class IRCClient;
|
||||
class IRCChannelMemberListModel;
|
||||
class IRCWindow;
|
||||
|
||||
class IRCChannel : public RefCounted<IRCChannel> {
|
||||
public:
|
||||
static NonnullRefPtr<IRCChannel> create(IRCClient&, const String&);
|
||||
~IRCChannel();
|
||||
|
||||
bool is_open() const { return m_open; }
|
||||
void set_open(bool b) { m_open = b; }
|
||||
|
||||
String name() const { return m_name; }
|
||||
|
||||
void add_member(const String& name, char prefix);
|
||||
void remove_member(const String& name);
|
||||
|
||||
void add_message(char prefix, const String& name, const String& text, Color = Color::Black);
|
||||
void add_message(const String& text, Color = Color::Black);
|
||||
|
||||
void say(const String&);
|
||||
|
||||
const IRCLogBuffer& log() const { return *m_log; }
|
||||
IRCLogBuffer& log() { return *m_log; }
|
||||
|
||||
IRCChannelMemberListModel* member_model() { return m_member_model.ptr(); }
|
||||
const IRCChannelMemberListModel* member_model() const { return m_member_model.ptr(); }
|
||||
|
||||
int member_count() const { return m_members.size(); }
|
||||
String member_at(int i) { return m_members[i].name; }
|
||||
|
||||
void handle_join(const String& nick, const String& hostmask);
|
||||
void handle_part(const String& nick, const String& hostmask);
|
||||
void handle_quit(const String& nick, const String& hostmask, const String& message);
|
||||
void handle_topic(const String& nick, const String& topic);
|
||||
|
||||
IRCWindow& window() { return *m_window; }
|
||||
const IRCWindow& window() const { return *m_window; }
|
||||
|
||||
String topic() const { return m_topic; }
|
||||
|
||||
void notify_nick_changed(const String& old_nick, const String& new_nick);
|
||||
|
||||
private:
|
||||
IRCChannel(IRCClient&, const String&);
|
||||
|
||||
IRCClient& m_client;
|
||||
String m_name;
|
||||
String m_topic;
|
||||
struct Member {
|
||||
String name;
|
||||
char prefix { 0 };
|
||||
};
|
||||
Vector<Member> m_members;
|
||||
bool m_open { false };
|
||||
|
||||
NonnullRefPtr<IRCLogBuffer> m_log;
|
||||
NonnullRefPtr<IRCChannelMemberListModel> m_member_model;
|
||||
IRCWindow* m_window { nullptr };
|
||||
};
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCChannelMemberListModel.h"
|
||||
#include "IRCChannel.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
IRCChannelMemberListModel::IRCChannelMemberListModel(IRCChannel& channel)
|
||||
: m_channel(channel)
|
||||
{
|
||||
}
|
||||
|
||||
IRCChannelMemberListModel::~IRCChannelMemberListModel()
|
||||
{
|
||||
}
|
||||
|
||||
int IRCChannelMemberListModel::row_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return m_channel.member_count();
|
||||
}
|
||||
|
||||
int IRCChannelMemberListModel::column_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
String IRCChannelMemberListModel::column_name(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Name:
|
||||
return "Name";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
GUI::Variant IRCChannelMemberListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||
{
|
||||
if (role == GUI::ModelRole::TextAlignment)
|
||||
return Gfx::TextAlignment::CenterLeft;
|
||||
if (role == GUI::ModelRole::Display) {
|
||||
switch (index.column()) {
|
||||
case Column::Name:
|
||||
return m_channel.member_at(index.row());
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void IRCChannelMemberListModel::update()
|
||||
{
|
||||
did_update();
|
||||
}
|
||||
|
||||
String IRCChannelMemberListModel::nick_at(const GUI::ModelIndex& index) const
|
||||
{
|
||||
return data(index, GUI::ModelRole::Display).to_string();
|
||||
}
|
53
Userland/Applications/IRCClient/IRCChannelMemberListModel.h
Normal file
53
Userland/Applications/IRCClient/IRCChannelMemberListModel.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
class IRCChannel;
|
||||
|
||||
class IRCChannelMemberListModel final : public GUI::Model {
|
||||
public:
|
||||
enum Column {
|
||||
Name
|
||||
};
|
||||
static NonnullRefPtr<IRCChannelMemberListModel> create(IRCChannel& channel) { return adopt(*new IRCChannelMemberListModel(channel)); }
|
||||
virtual ~IRCChannelMemberListModel() override;
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex&) const override;
|
||||
virtual int column_count(const GUI::ModelIndex&) const override;
|
||||
virtual String column_name(int column) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||
virtual void update() override;
|
||||
virtual String nick_at(const GUI::ModelIndex& index) const;
|
||||
|
||||
private:
|
||||
explicit IRCChannelMemberListModel(IRCChannel&);
|
||||
|
||||
IRCChannel& m_channel;
|
||||
};
|
1188
Userland/Applications/IRCClient/IRCClient.cpp
Normal file
1188
Userland/Applications/IRCClient/IRCClient.cpp
Normal file
File diff suppressed because it is too large
Load diff
236
Userland/Applications/IRCClient/IRCClient.h
Normal file
236
Userland/Applications/IRCClient/IRCClient.h
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IRCLogBuffer.h"
|
||||
#include "IRCWindow.h"
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/TCPSocket.h>
|
||||
|
||||
class IRCChannel;
|
||||
class IRCQuery;
|
||||
class IRCWindowListModel;
|
||||
|
||||
class IRCClient final : public Core::Object {
|
||||
C_OBJECT(IRCClient)
|
||||
friend class IRCChannel;
|
||||
friend class IRCQuery;
|
||||
|
||||
public:
|
||||
virtual ~IRCClient() override;
|
||||
|
||||
void set_server(const String& hostname, int port = 6667);
|
||||
|
||||
bool connect();
|
||||
|
||||
String hostname() const { return m_hostname; }
|
||||
int port() const { return m_port; }
|
||||
|
||||
String nickname() const { return m_nickname; }
|
||||
|
||||
String ctcp_version_reply() const { return m_ctcp_version_reply; }
|
||||
String ctcp_userinfo_reply() const { return m_ctcp_userinfo_reply; }
|
||||
String ctcp_finger_reply() const { return m_ctcp_finger_reply; }
|
||||
|
||||
bool show_join_part_messages() const { return m_show_join_part_messages; }
|
||||
bool show_nick_change_messages() const { return m_show_nick_change_messages; }
|
||||
|
||||
bool notify_on_message() const { return m_notify_on_message; }
|
||||
bool notify_on_mention() const { return m_notify_on_mention; }
|
||||
|
||||
void join_channel(const String&);
|
||||
void part_channel(const String&);
|
||||
void change_nick(const String&);
|
||||
|
||||
static bool is_nick_prefix(char);
|
||||
static bool is_channel_prefix(char);
|
||||
String nick_without_prefix(const String& nick);
|
||||
|
||||
IRCWindow* current_window() { return aid_get_active_window(); }
|
||||
const IRCWindow* current_window() const { return aid_get_active_window(); }
|
||||
|
||||
Function<void()> on_disconnect;
|
||||
Function<void()> on_server_message;
|
||||
Function<void(const String&)> on_nickname_changed;
|
||||
Function<void(IRCChannel&)> on_part_from_channel;
|
||||
|
||||
Function<NonnullRefPtr<IRCWindow>(void*, IRCWindow::Type, const String&)> aid_create_window;
|
||||
Function<IRCWindow*()> aid_get_active_window;
|
||||
Function<void()> aid_update_window_list;
|
||||
|
||||
void register_subwindow(IRCWindow&);
|
||||
void unregister_subwindow(IRCWindow&);
|
||||
|
||||
IRCWindowListModel* client_window_list_model() { return m_client_window_list_model.ptr(); }
|
||||
const IRCWindowListModel* client_window_list_model() const { return m_client_window_list_model.ptr(); }
|
||||
|
||||
int window_count() const { return m_windows.size(); }
|
||||
const IRCWindow& window_at(int index) const { return *m_windows.at(index); }
|
||||
IRCWindow& window_at(int index) { return *m_windows.at(index); }
|
||||
|
||||
size_t window_index(const IRCWindow& window) const
|
||||
{
|
||||
for (size_t i = 0; i < m_windows.size(); ++i) {
|
||||
if (m_windows[i] == &window)
|
||||
return i;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
void did_part_from_channel(Badge<IRCChannel>, IRCChannel&);
|
||||
|
||||
void handle_user_input_in_channel(const String& channel_name, const String&);
|
||||
void handle_user_input_in_query(const String& query_name, const String&);
|
||||
void handle_user_input_in_server(const String&);
|
||||
|
||||
void handle_list_channels_action();
|
||||
void handle_whois_action(const String& nick);
|
||||
void handle_ctcp_user_action(const String& nick, const String& message);
|
||||
void handle_open_query_action(const String&);
|
||||
void handle_close_query_action(const String&);
|
||||
void handle_join_action(const String& channel_name);
|
||||
void handle_part_action(const String& channel_name);
|
||||
void handle_cycle_channel_action(const String& channel_name);
|
||||
void handle_change_nick_action(const String& nick);
|
||||
void handle_change_topic_action(const String& channel_name, const String&);
|
||||
void handle_invite_user_action(const String& channel_name, const String& nick);
|
||||
void handle_banlist_action(const String& channel_name);
|
||||
void handle_voice_user_action(const String& channel_name, const String& nick);
|
||||
void handle_devoice_user_action(const String& channel_name, const String& nick);
|
||||
void handle_hop_user_action(const String& channel_name, const String& nick);
|
||||
void handle_dehop_user_action(const String& channel_name, const String& nick);
|
||||
void handle_op_user_action(const String& channel_name, const String& nick);
|
||||
void handle_deop_user_action(const String& channel_name, const String& nick);
|
||||
void handle_kick_user_action(const String& channel_name, const String& nick, const String&);
|
||||
|
||||
IRCQuery* query_with_name(const String&);
|
||||
IRCQuery& ensure_query(const String& name);
|
||||
IRCChannel& ensure_channel(const String& name);
|
||||
|
||||
void add_server_message(const String&, Color = Color::Black);
|
||||
|
||||
private:
|
||||
IRCClient(String server, int port);
|
||||
|
||||
struct Message {
|
||||
String prefix;
|
||||
String command;
|
||||
Vector<String> arguments;
|
||||
};
|
||||
|
||||
enum class PrivmsgOrNotice {
|
||||
Privmsg,
|
||||
Notice,
|
||||
};
|
||||
|
||||
void receive_from_server();
|
||||
void send(const String&);
|
||||
void send_user();
|
||||
void send_nick();
|
||||
void send_pong(const String& server);
|
||||
void send_privmsg(const String& target, const String&);
|
||||
void send_notice(const String& target, const String&);
|
||||
void send_topic(const String& channel_name, const String&);
|
||||
void send_invite(const String& channel_name, const String& nick);
|
||||
void send_banlist(const String& channel_name);
|
||||
void send_voice_user(const String& channel_name, const String& nick);
|
||||
void send_devoice_user(const String& channel_name, const String& nick);
|
||||
void send_hop_user(const String& channel_name, const String& nick);
|
||||
void send_dehop_user(const String& channel_name, const String& nick);
|
||||
void send_op_user(const String& channel_name, const String& nick);
|
||||
void send_deop_user(const String& channel_name, const String& nick);
|
||||
void send_kick(const String& channel_name, const String& nick, const String&);
|
||||
void send_list();
|
||||
void send_whois(const String&);
|
||||
void process_line(const String&);
|
||||
void handle_join(const Message&);
|
||||
void handle_part(const Message&);
|
||||
void handle_quit(const Message&);
|
||||
void handle_ping(const Message&);
|
||||
void handle_topic(const Message&);
|
||||
void handle_rpl_welcome(const Message&);
|
||||
void handle_rpl_topic(const Message&);
|
||||
void handle_rpl_whoisuser(const Message&);
|
||||
void handle_rpl_whoisserver(const Message&);
|
||||
void handle_rpl_whoisoperator(const Message&);
|
||||
void handle_rpl_whoisidle(const Message&);
|
||||
void handle_rpl_endofwho(const Message&);
|
||||
void handle_rpl_endofwhois(const Message&);
|
||||
void handle_rpl_endofwhowas(const Message&);
|
||||
void handle_rpl_endofmotd(const Message&);
|
||||
void handle_rpl_whoischannels(const Message&);
|
||||
void handle_rpl_topicwhotime(const Message&);
|
||||
void handle_rpl_endofnames(const Message&);
|
||||
void handle_rpl_endofbanlist(const Message&);
|
||||
void handle_rpl_namreply(const Message&);
|
||||
void handle_rpl_banlist(const Message&);
|
||||
void handle_err_nosuchnick(const Message&);
|
||||
void handle_err_unknowncommand(const Message&);
|
||||
void handle_err_nicknameinuse(const Message&);
|
||||
void handle_privmsg_or_notice(const Message&, PrivmsgOrNotice);
|
||||
void handle_nick(const Message&);
|
||||
void handle(const Message&);
|
||||
void handle_user_command(const String&);
|
||||
void handle_ctcp_request(const StringView& peer, const StringView& payload);
|
||||
void handle_ctcp_response(const StringView& peer, const StringView& payload);
|
||||
void send_ctcp_request(const StringView& peer, const StringView& payload);
|
||||
void send_ctcp_response(const StringView& peer, const StringView& payload);
|
||||
|
||||
void on_socket_connected();
|
||||
|
||||
String m_hostname;
|
||||
int m_port { 6667 };
|
||||
|
||||
RefPtr<Core::TCPSocket> m_socket;
|
||||
|
||||
String m_nickname;
|
||||
RefPtr<Core::Notifier> m_notifier;
|
||||
HashMap<String, RefPtr<IRCChannel>, CaseInsensitiveStringTraits> m_channels;
|
||||
HashMap<String, RefPtr<IRCQuery>, CaseInsensitiveStringTraits> m_queries;
|
||||
|
||||
bool m_show_join_part_messages { 1 };
|
||||
bool m_show_nick_change_messages { 1 };
|
||||
|
||||
bool m_notify_on_message { 1 };
|
||||
bool m_notify_on_mention { 1 };
|
||||
|
||||
String m_ctcp_version_reply;
|
||||
String m_ctcp_userinfo_reply;
|
||||
String m_ctcp_finger_reply;
|
||||
|
||||
Vector<IRCWindow*> m_windows;
|
||||
|
||||
IRCWindow* m_server_subwindow { nullptr };
|
||||
|
||||
NonnullRefPtr<IRCWindowListModel> m_client_window_list_model;
|
||||
NonnullRefPtr<IRCLogBuffer> m_log;
|
||||
NonnullRefPtr<Core::ConfigFile> m_config;
|
||||
};
|
98
Userland/Applications/IRCClient/IRCLogBuffer.cpp
Normal file
98
Userland/Applications/IRCClient/IRCLogBuffer.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCLogBuffer.h"
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibWeb/DOM/DocumentFragment.h>
|
||||
#include <LibWeb/DOM/DocumentType.h>
|
||||
#include <LibWeb/DOM/ElementFactory.h>
|
||||
#include <LibWeb/DOM/Text.h>
|
||||
#include <LibWeb/HTML/HTMLBodyElement.h>
|
||||
#include <time.h>
|
||||
|
||||
NonnullRefPtr<IRCLogBuffer> IRCLogBuffer::create()
|
||||
{
|
||||
return adopt(*new IRCLogBuffer);
|
||||
}
|
||||
|
||||
IRCLogBuffer::IRCLogBuffer()
|
||||
{
|
||||
m_document = Web::DOM::Document::create();
|
||||
m_document->append_child(adopt(*new Web::DOM::DocumentType(document())));
|
||||
auto html_element = m_document->create_element("html");
|
||||
m_document->append_child(html_element);
|
||||
auto head_element = m_document->create_element("head");
|
||||
html_element->append_child(head_element);
|
||||
auto style_element = m_document->create_element("style");
|
||||
style_element->append_child(adopt(*new Web::DOM::Text(document(), "div { font-family: Csilla; font-weight: lighter; }")));
|
||||
head_element->append_child(style_element);
|
||||
auto body_element = m_document->create_element("body");
|
||||
html_element->append_child(body_element);
|
||||
m_container_element = body_element;
|
||||
}
|
||||
|
||||
IRCLogBuffer::~IRCLogBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
static String timestamp_string()
|
||||
{
|
||||
auto now = time(nullptr);
|
||||
auto* tm = localtime(&now);
|
||||
return String::formatted("{:02}:{:02}:{:02} ", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
|
||||
void IRCLogBuffer::add_message(char prefix, const String& name, const String& text, Color color)
|
||||
{
|
||||
auto nick_string = String::formatted("<{}{}> ", prefix ? prefix : ' ', name.characters());
|
||||
auto html = String::formatted(
|
||||
"<span>{}</span>"
|
||||
"<b>{}</b>"
|
||||
"<span>{}</span>",
|
||||
timestamp_string(),
|
||||
escape_html_entities(nick_string),
|
||||
escape_html_entities(text));
|
||||
|
||||
auto wrapper = m_document->create_element(Web::HTML::TagNames::div);
|
||||
wrapper->set_attribute(Web::HTML::AttributeNames::style, String::formatted("color: {}", color.to_string()));
|
||||
wrapper->set_inner_html(html);
|
||||
m_container_element->append_child(wrapper);
|
||||
m_document->force_layout();
|
||||
}
|
||||
|
||||
void IRCLogBuffer::add_message(const String& text, Color color)
|
||||
{
|
||||
auto html = String::formatted(
|
||||
"<span>{}</span>"
|
||||
"<span>{}</span>",
|
||||
timestamp_string(),
|
||||
escape_html_entities(text));
|
||||
auto wrapper = m_document->create_element(Web::HTML::TagNames::div);
|
||||
wrapper->set_attribute(Web::HTML::AttributeNames::style, String::formatted("color: {}", color.to_string()));
|
||||
wrapper->set_inner_html(html);
|
||||
m_container_element->append_child(wrapper);
|
||||
m_document->force_layout();
|
||||
}
|
58
Userland/Applications/IRCClient/IRCLogBuffer.h
Normal file
58
Userland/Applications/IRCClient/IRCLogBuffer.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
|
||||
class IRCLogBuffer : public RefCounted<IRCLogBuffer> {
|
||||
public:
|
||||
static NonnullRefPtr<IRCLogBuffer> create();
|
||||
~IRCLogBuffer();
|
||||
|
||||
struct Message {
|
||||
time_t timestamp { 0 };
|
||||
char prefix { 0 };
|
||||
String sender;
|
||||
String text;
|
||||
Color color { Color::Black };
|
||||
};
|
||||
|
||||
void add_message(char prefix, const String& name, const String& text, Color = Color::Black);
|
||||
void add_message(const String& text, Color = Color::Black);
|
||||
|
||||
const Web::DOM::Document& document() const { return *m_document; }
|
||||
Web::DOM::Document& document() { return *m_document; }
|
||||
|
||||
private:
|
||||
IRCLogBuffer();
|
||||
RefPtr<Web::DOM::Document> m_document;
|
||||
RefPtr<Web::DOM::Element> m_container_element;
|
||||
};
|
65
Userland/Applications/IRCClient/IRCQuery.cpp
Normal file
65
Userland/Applications/IRCClient/IRCQuery.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCQuery.h"
|
||||
#include "IRCClient.h"
|
||||
#include <stdio.h>
|
||||
|
||||
IRCQuery::IRCQuery(IRCClient& client, const String& name)
|
||||
: m_client(client)
|
||||
, m_name(name)
|
||||
, m_log(IRCLogBuffer::create())
|
||||
{
|
||||
m_window = m_client->aid_create_window(this, IRCWindow::Query, m_name);
|
||||
m_window->set_log_buffer(*m_log);
|
||||
}
|
||||
|
||||
IRCQuery::~IRCQuery()
|
||||
{
|
||||
}
|
||||
|
||||
NonnullRefPtr<IRCQuery> IRCQuery::create(IRCClient& client, const String& name)
|
||||
{
|
||||
return adopt(*new IRCQuery(client, name));
|
||||
}
|
||||
|
||||
void IRCQuery::add_message(char prefix, const String& name, const String& text, Color color)
|
||||
{
|
||||
log().add_message(prefix, name, text, color);
|
||||
window().did_add_message(name, text);
|
||||
}
|
||||
|
||||
void IRCQuery::add_message(const String& text, Color color)
|
||||
{
|
||||
log().add_message(text, color);
|
||||
window().did_add_message();
|
||||
}
|
||||
|
||||
void IRCQuery::say(const String& text)
|
||||
{
|
||||
m_client->send_privmsg(m_name, text);
|
||||
add_message(' ', m_client->nickname(), text);
|
||||
}
|
64
Userland/Applications/IRCClient/IRCQuery.h
Normal file
64
Userland/Applications/IRCClient/IRCQuery.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IRCLogBuffer.h"
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
class IRCClient;
|
||||
class IRCWindow;
|
||||
|
||||
class IRCQuery : public RefCounted<IRCQuery> {
|
||||
public:
|
||||
static NonnullRefPtr<IRCQuery> create(IRCClient&, const String& name);
|
||||
~IRCQuery();
|
||||
|
||||
String name() const { return m_name; }
|
||||
void add_message(char prefix, const String& name, const String& text, Color = Color::Black);
|
||||
void add_message(const String& text, Color = Color::Black);
|
||||
|
||||
const IRCLogBuffer& log() const { return *m_log; }
|
||||
IRCLogBuffer& log() { return *m_log; }
|
||||
|
||||
void say(const String&);
|
||||
|
||||
IRCWindow& window() { return *m_window; }
|
||||
const IRCWindow& window() const { return *m_window; }
|
||||
|
||||
private:
|
||||
IRCQuery(IRCClient&, const String& name);
|
||||
|
||||
NonnullRefPtr<IRCClient> m_client;
|
||||
String m_name;
|
||||
RefPtr<IRCWindow> m_window;
|
||||
|
||||
NonnullRefPtr<IRCLogBuffer> m_log;
|
||||
};
|
278
Userland/Applications/IRCClient/IRCWindow.cpp
Normal file
278
Userland/Applications/IRCClient/IRCWindow.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCWindow.h"
|
||||
#include "IRCChannel.h"
|
||||
#include "IRCChannelMemberListModel.h"
|
||||
#include "IRCClient.h"
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/InputBox.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/Notification.h>
|
||||
#include <LibGUI/Splitter.h>
|
||||
#include <LibGUI/TableView.h>
|
||||
#include <LibGUI/TextBox.h>
|
||||
#include <LibGUI/TextEditor.h>
|
||||
#include <LibGUI/Window.h>
|
||||
#include <LibWeb/InProcessWebView.h>
|
||||
|
||||
IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& name)
|
||||
: m_client(client)
|
||||
, m_owner(owner)
|
||||
, m_type(type)
|
||||
, m_name(name)
|
||||
{
|
||||
set_layout<GUI::VerticalBoxLayout>();
|
||||
|
||||
// Make a container for the log buffer view + (optional) member list.
|
||||
auto& container = add<GUI::HorizontalSplitter>();
|
||||
|
||||
m_page_view = container.add<Web::InProcessWebView>();
|
||||
|
||||
if (m_type == Channel) {
|
||||
auto& member_view = container.add<GUI::TableView>();
|
||||
member_view.set_column_headers_visible(false);
|
||||
member_view.set_fixed_width(100);
|
||||
member_view.set_alternating_row_colors(false);
|
||||
member_view.set_model(channel().member_model());
|
||||
member_view.set_activates_on_selection(true);
|
||||
member_view.on_activation = [&](auto& index) {
|
||||
if (!index.is_valid())
|
||||
return;
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_open_query_action(m_client->nick_without_prefix(nick.characters()));
|
||||
};
|
||||
member_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) {
|
||||
if (!index.is_valid())
|
||||
return;
|
||||
|
||||
m_context_menu = GUI::Menu::construct();
|
||||
|
||||
m_context_menu->add_action(GUI::Action::create("Open query", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-open-query.png"), [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_open_query_action(m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
m_context_menu->add_action(GUI::Action::create("Whois", Gfx::Bitmap::load_from_file("/res/icons/16x16/irc-whois.png"), [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_whois_action(m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
auto& context_control_menu = m_context_menu->add_submenu("Control");
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("Voice", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_voice_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("DeVoice", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_devoice_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("Hop", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_hop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("DeHop", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_dehop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("Op", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_op_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("DeOp", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_deop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()));
|
||||
}));
|
||||
|
||||
context_control_menu.add_separator();
|
||||
|
||||
context_control_menu.add_action(GUI::Action::create("Kick", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
if (IRCClient::is_nick_prefix(nick[0]))
|
||||
nick = nick.substring(1, nick.length() - 1);
|
||||
String value;
|
||||
if (GUI::InputBox::show(value, window(), "Enter reason:", "Reason") == GUI::InputBox::ExecOK)
|
||||
m_client->handle_kick_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()), value);
|
||||
}));
|
||||
|
||||
auto& context_ctcp_menu = m_context_menu->add_submenu("CTCP");
|
||||
|
||||
context_ctcp_menu.add_action(GUI::Action::create("User info", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "USERINFO");
|
||||
}));
|
||||
|
||||
context_ctcp_menu.add_action(GUI::Action::create("Finger", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "FINGER");
|
||||
}));
|
||||
|
||||
context_ctcp_menu.add_action(GUI::Action::create("Time", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "TIME");
|
||||
}));
|
||||
|
||||
context_ctcp_menu.add_action(GUI::Action::create("Version", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "VERSION");
|
||||
}));
|
||||
|
||||
context_ctcp_menu.add_action(GUI::Action::create("Client info", [&](const GUI::Action&) {
|
||||
auto nick = channel().member_model()->nick_at(member_view.selection().first());
|
||||
if (nick.is_empty())
|
||||
return;
|
||||
m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "CLIENTINFO");
|
||||
}));
|
||||
|
||||
m_context_menu->popup(event.screen_position());
|
||||
};
|
||||
}
|
||||
|
||||
m_text_box = add<GUI::TextBox>();
|
||||
m_text_box->set_fixed_height(19);
|
||||
m_text_box->on_return_pressed = [this] {
|
||||
if (m_type == Channel)
|
||||
m_client->handle_user_input_in_channel(m_name, m_text_box->text());
|
||||
else if (m_type == Query)
|
||||
m_client->handle_user_input_in_query(m_name, m_text_box->text());
|
||||
else if (m_type == Server)
|
||||
m_client->handle_user_input_in_server(m_text_box->text());
|
||||
m_text_box->add_current_text_to_history();
|
||||
m_text_box->clear();
|
||||
};
|
||||
m_text_box->set_history_enabled(true);
|
||||
m_text_box->set_placeholder("Message");
|
||||
|
||||
m_client->register_subwindow(*this);
|
||||
}
|
||||
|
||||
IRCWindow::~IRCWindow()
|
||||
{
|
||||
m_client->unregister_subwindow(*this);
|
||||
}
|
||||
|
||||
void IRCWindow::set_log_buffer(const IRCLogBuffer& log_buffer)
|
||||
{
|
||||
m_log_buffer = &log_buffer;
|
||||
m_page_view->set_document(const_cast<Web::DOM::Document*>(&log_buffer.document()));
|
||||
}
|
||||
|
||||
bool IRCWindow::is_active() const
|
||||
{
|
||||
return m_client->current_window() == this;
|
||||
}
|
||||
|
||||
void IRCWindow::post_notification_if_needed(const String& name, const String& message)
|
||||
{
|
||||
if (name.is_null() || message.is_null())
|
||||
return;
|
||||
if (is_active() && window()->is_active())
|
||||
return;
|
||||
|
||||
auto notification = GUI::Notification::construct();
|
||||
|
||||
if (type() == Type::Channel) {
|
||||
if (!m_client->notify_on_mention())
|
||||
return;
|
||||
if (!message.contains(m_client->nickname()))
|
||||
return;
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append(name);
|
||||
builder.append(" in ");
|
||||
builder.append(m_name);
|
||||
notification->set_title(builder.to_string());
|
||||
} else {
|
||||
if (!m_client->notify_on_message())
|
||||
return;
|
||||
notification->set_title(name);
|
||||
}
|
||||
|
||||
notification->set_icon(Gfx::Bitmap::load_from_file("/res/icons/32x32/app-irc-client.png"));
|
||||
notification->set_text(message);
|
||||
notification->show();
|
||||
}
|
||||
|
||||
void IRCWindow::did_add_message(const String& name, const String& message)
|
||||
{
|
||||
post_notification_if_needed(name, message);
|
||||
|
||||
if (!is_active()) {
|
||||
++m_unread_count;
|
||||
m_client->aid_update_window_list();
|
||||
return;
|
||||
}
|
||||
m_page_view->scroll_to_bottom();
|
||||
}
|
||||
|
||||
void IRCWindow::clear_unread_count()
|
||||
{
|
||||
if (!m_unread_count)
|
||||
return;
|
||||
m_unread_count = 0;
|
||||
m_client->aid_update_window_list();
|
||||
}
|
||||
|
||||
int IRCWindow::unread_count() const
|
||||
{
|
||||
return m_unread_count;
|
||||
}
|
82
Userland/Applications/IRCClient/IRCWindow.h
Normal file
82
Userland/Applications/IRCClient/IRCWindow.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
class IRCChannel;
|
||||
class IRCClient;
|
||||
class IRCQuery;
|
||||
class IRCLogBuffer;
|
||||
|
||||
class IRCWindow : public GUI::Widget {
|
||||
C_OBJECT(IRCWindow)
|
||||
public:
|
||||
enum Type {
|
||||
Server,
|
||||
Channel,
|
||||
Query,
|
||||
};
|
||||
|
||||
virtual ~IRCWindow() override;
|
||||
|
||||
String name() const { return m_name; }
|
||||
void set_name(const String& name) { m_name = name; }
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
||||
void set_log_buffer(const IRCLogBuffer&);
|
||||
|
||||
bool is_active() const;
|
||||
|
||||
int unread_count() const;
|
||||
void clear_unread_count();
|
||||
|
||||
void did_add_message(const String& name = {}, const String& message = {});
|
||||
|
||||
IRCChannel& channel() { return *(IRCChannel*)m_owner; }
|
||||
const IRCChannel& channel() const { return *(const IRCChannel*)m_owner; }
|
||||
|
||||
IRCQuery& query() { return *(IRCQuery*)m_owner; }
|
||||
const IRCQuery& query() const { return *(const IRCQuery*)m_owner; }
|
||||
|
||||
private:
|
||||
IRCWindow(IRCClient&, void* owner, Type, const String& name);
|
||||
|
||||
void post_notification_if_needed(const String& name, const String& message);
|
||||
|
||||
NonnullRefPtr<IRCClient> m_client;
|
||||
void* m_owner { nullptr };
|
||||
Type m_type;
|
||||
String m_name;
|
||||
RefPtr<Web::InProcessWebView> m_page_view;
|
||||
RefPtr<GUI::TextBox> m_text_box;
|
||||
RefPtr<IRCLogBuffer> m_log_buffer;
|
||||
RefPtr<GUI::Menu> m_context_menu;
|
||||
int m_unread_count { 0 };
|
||||
};
|
91
Userland/Applications/IRCClient/IRCWindowListModel.cpp
Normal file
91
Userland/Applications/IRCClient/IRCWindowListModel.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCWindowListModel.h"
|
||||
#include "IRCChannel.h"
|
||||
#include "IRCClient.h"
|
||||
|
||||
IRCWindowListModel::IRCWindowListModel(IRCClient& client)
|
||||
: m_client(client)
|
||||
{
|
||||
}
|
||||
|
||||
IRCWindowListModel::~IRCWindowListModel()
|
||||
{
|
||||
}
|
||||
|
||||
int IRCWindowListModel::row_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return m_client->window_count();
|
||||
}
|
||||
|
||||
int IRCWindowListModel::column_count(const GUI::ModelIndex&) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
String IRCWindowListModel::column_name(int column) const
|
||||
{
|
||||
switch (column) {
|
||||
case Column::Name:
|
||||
return "Name";
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
GUI::Variant IRCWindowListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
|
||||
{
|
||||
if (role == GUI::ModelRole::TextAlignment)
|
||||
return Gfx::TextAlignment::CenterLeft;
|
||||
if (role == GUI::ModelRole::Display) {
|
||||
switch (index.column()) {
|
||||
case Column::Name: {
|
||||
auto& window = m_client->window_at(index.row());
|
||||
if (window.unread_count())
|
||||
return String::formatted("{} ({})", window.name(), window.unread_count());
|
||||
return window.name();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (role == GUI::ModelRole::ForegroundColor) {
|
||||
switch (index.column()) {
|
||||
case Column::Name: {
|
||||
auto& window = m_client->window_at(index.row());
|
||||
if (window.unread_count())
|
||||
return Color(Color::Red);
|
||||
if (!window.channel().is_open())
|
||||
return Color(Color::WarmGray);
|
||||
return Color(Color::Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void IRCWindowListModel::update()
|
||||
{
|
||||
did_update();
|
||||
}
|
54
Userland/Applications/IRCClient/IRCWindowListModel.h
Normal file
54
Userland/Applications/IRCClient/IRCWindowListModel.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <LibGUI/Model.h>
|
||||
|
||||
class IRCClient;
|
||||
class IRCWindow;
|
||||
|
||||
class IRCWindowListModel final : public GUI::Model {
|
||||
public:
|
||||
enum Column {
|
||||
Name,
|
||||
};
|
||||
|
||||
static NonnullRefPtr<IRCWindowListModel> create(IRCClient& client) { return adopt(*new IRCWindowListModel(client)); }
|
||||
virtual ~IRCWindowListModel() override;
|
||||
|
||||
virtual int row_count(const GUI::ModelIndex&) const override;
|
||||
virtual int column_count(const GUI::ModelIndex&) const override;
|
||||
virtual String column_name(int column) const override;
|
||||
virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override;
|
||||
virtual void update() override;
|
||||
|
||||
private:
|
||||
explicit IRCWindowListModel(IRCClient&);
|
||||
|
||||
NonnullRefPtr<IRCClient> m_client;
|
||||
};
|
109
Userland/Applications/IRCClient/main.cpp
Normal file
109
Userland/Applications/IRCClient/main.cpp
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "IRCAppWindow.h"
|
||||
#include "IRCClient.h"
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/MessageBox.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (pledge("stdio inet dns unix shared_buffer cpath rpath fattr wpath cpath", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (getuid() == 0) {
|
||||
warnln("Refusing to run as root");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto app = GUI::Application::construct(argc, argv);
|
||||
|
||||
if (pledge("stdio inet dns unix shared_buffer rpath wpath cpath", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/tmp/portal/lookup", "rw") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/tmp/portal/notify", "rw") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/etc/passwd", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil(Core::StandardPaths::home_directory().characters(), "rwc") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/res", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
URL url = "";
|
||||
if (app->args().size() >= 1) {
|
||||
url = URL::create_with_url_or_path(app->args()[0]);
|
||||
|
||||
if (url.protocol().to_lowercase() == "ircs") {
|
||||
warnln("Secure IRC over SSL/TLS (ircs) is not supported");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (url.protocol().to_lowercase() != "irc") {
|
||||
warnln("Unsupported protocol");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (url.host().is_empty()) {
|
||||
warnln("Invalid URL");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!url.port() || url.port() == 80)
|
||||
url.set_port(6667);
|
||||
}
|
||||
|
||||
auto app_window = IRCAppWindow::construct(url.host(), url.port());
|
||||
app_window->show();
|
||||
return app->exec();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue