Selaa lähdekoodia

IRCClient: Hacking on IRCClient bringup.

Andreas Kling 6 vuotta sitten
vanhempi
commit
850c7504a2

+ 16 - 5
Applications/IRCClient/IRCAppWindow.cpp

@@ -44,11 +44,22 @@ void IRCAppWindow::setup_widgets()
     auto* subwindow_list = new GListBox(widget);
     subwindow_list->set_size_policy(SizePolicy::Fixed, SizePolicy::Fill);
     subwindow_list->set_preferred_size({ 120, 0 });
-    subwindow_list->add_item("test1");
-    subwindow_list->add_item("test2");
-    subwindow_list->add_item("test3");
+    subwindow_list->add_item("Server");
+    subwindow_list->add_item("#test");
 
-    auto* container = new GWidget(widget);
+    m_subwindow_container = new GWidget(widget);
+    m_subwindow_container->set_layout(make<GBoxLayout>(Orientation::Vertical));
+    m_subwindow_container->set_fill_with_background_color(true);
+    m_subwindow_container->set_background_color(Color::Yellow);
+    m_subwindow_container->set_size_policy(SizePolicy::Fill, SizePolicy::Fill);
 
-    auto* subwindow = new IRCSubWindow("Server", container);
+    create_subwindow(IRCSubWindow::Server, "Server");
+}
+
+void IRCAppWindow::create_subwindow(IRCSubWindow::Type type, const String& name)
+{
+    auto* subwindow = new IRCSubWindow(m_client, type, name, m_subwindow_container);
+    subwindow->set_fill_with_background_color(true);
+    subwindow->set_background_color(Color::Magenta);
+    m_subwindows.append(subwindow);
 }

+ 6 - 0
Applications/IRCClient/IRCAppWindow.h

@@ -3,6 +3,7 @@
 #include <LibGUI/GWindow.h>
 #include <LibGUI/GWidget.h>
 #include "IRCClient.h"
+#include "IRCSubWindow.h"
 
 class IRCAppWindow : public GWindow {
 public:
@@ -13,5 +14,10 @@ private:
     void setup_client();
     void setup_widgets();
 
+    void create_subwindow(IRCSubWindow::Type, const String& name);
+
     IRCClient m_client;
+
+    GWidget* m_subwindow_container { nullptr };
+    Vector<IRCSubWindow*> m_subwindows;
 };

+ 35 - 3
Applications/IRCClient/IRCClient.cpp

@@ -1,6 +1,8 @@
 #include "IRCClient.h"
 #include "IRCChannel.h"
 #include "IRCQuery.h"
+#include "IRCLogBuffer.h"
+#include "IRCSubWindow.h"
 #include <LibGUI/GNotifier.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
@@ -17,6 +19,7 @@ IRCClient::IRCClient(const String& address, int port)
     : m_hostname(address)
     , m_port(port)
     , m_nickname("anon")
+    , m_log(IRCLogBuffer::create())
 {
 }
 
@@ -165,7 +168,7 @@ void IRCClient::process_line()
         msg.arguments.append(String(current_parameter.data(), current_parameter.size()));
     msg.prefix = String(prefix.data(), prefix.size());
     msg.command = String(command.data(), command.size());
-    handle(msg);
+    handle(msg, String(m_line_buffer.data(), m_line_buffer.size()));
 }
 
 void IRCClient::send(const String& text)
@@ -198,7 +201,7 @@ void IRCClient::join_channel(const String& channel_name)
     send(String::format("JOIN %s\r\n", channel_name.characters()));
 }
 
-void IRCClient::handle(const Message& msg)
+void IRCClient::handle(const Message& msg, const String& verbatim)
 {
     printf("IRCClient::execute: prefix='%s', command='%s', arguments=%d\n",
         msg.prefix.characters(),
@@ -231,6 +234,8 @@ void IRCClient::handle(const Message& msg)
 
     if (msg.command == "PRIVMSG")
         return handle_privmsg(msg);
+
+    m_log->add_message(0, "Server", verbatim);
 }
 
 bool IRCClient::is_nick_prefix(char ch) const
@@ -297,7 +302,7 @@ void IRCClient::handle_ping(const Message& msg)
 {
     if (msg.arguments.size() < 0)
         return;
-    m_server_messages.enqueue(String::format("Ping? Pong! %s\n", msg.arguments[0].characters()));
+    m_log->add_message(0, "", String::format("Ping? Pong! %s\n", msg.arguments[0].characters()));
     send_pong(msg.arguments[0]);
 }
 
@@ -339,3 +344,30 @@ void IRCClient::handle_namreply(const Message& msg)
 
     channel.dump();
 }
+
+void IRCClient::register_subwindow(IRCSubWindow& subwindow)
+{
+    if (subwindow.type() == IRCSubWindow::Server) {
+        m_server_subwindow = &subwindow;
+        subwindow.set_log_buffer(*m_log);
+        return;
+    }
+    if (subwindow.type() == IRCSubWindow::Channel) {
+        auto it = m_channels.find(subwindow.name());
+        ASSERT(it != m_channels.end());
+        auto& channel = *(*it).value;
+        subwindow.set_log_buffer(channel.log());
+        return;
+    }
+    if (subwindow.type() == IRCSubWindow::Query) {
+        subwindow.set_log_buffer(ensure_query(subwindow.name()).log());
+    }
+}
+
+void IRCClient::unregister_subwindow(IRCSubWindow& subwindow)
+{
+    if (subwindow.type() == IRCSubWindow::Server) {
+        m_server_subwindow = &subwindow;
+        return;
+    }
+}

+ 9 - 2
Applications/IRCClient/IRCClient.h

@@ -4,9 +4,11 @@
 #include <AK/HashMap.h>
 #include <AK/CircularQueue.h>
 #include <AK/Function.h>
+#include "IRCLogBuffer.h"
 
 class IRCChannel;
 class IRCQuery;
+class IRCSubWindow;
 class GNotifier;
 
 class IRCClient {
@@ -31,6 +33,9 @@ public:
     Function<void(const String& name)> on_query_message;
     Function<void()> on_server_message;
 
+    void register_subwindow(IRCSubWindow&);
+    void unregister_subwindow(IRCSubWindow&);
+
 private:
     struct Message {
         String prefix;
@@ -48,7 +53,7 @@ private:
     void handle_ping(const Message&);
     void handle_namreply(const Message&);
     void handle_privmsg(const Message&);
-    void handle(const Message&);
+    void handle(const Message&, const String& verbatim);
     IRCQuery& ensure_query(const String& name);
 
     String m_hostname;
@@ -61,5 +66,7 @@ private:
     HashMap<String, RetainPtr<IRCChannel>> m_channels;
     HashMap<String, RetainPtr<IRCQuery>> m_queries;
 
-    CircularQueue<String, 1024> m_server_messages;
+    IRCSubWindow* m_server_subwindow { nullptr };
+
+    Retained<IRCLogBuffer> m_log;
 };

+ 3 - 0
Applications/IRCClient/IRCLogBuffer.cpp

@@ -1,4 +1,5 @@
 #include "IRCLogBuffer.h"
+#include "IRCLogBufferModel.h"
 #include <stdio.h>
 #include <time.h>
 
@@ -9,6 +10,7 @@ Retained<IRCLogBuffer> IRCLogBuffer::create()
 
 IRCLogBuffer::IRCLogBuffer()
 {
+    m_model = new IRCLogBufferModel(*this);
 }
 
 IRCLogBuffer::~IRCLogBuffer()
@@ -18,6 +20,7 @@ IRCLogBuffer::~IRCLogBuffer()
 void IRCLogBuffer::add_message(char prefix, const String& name, const String& text)
 {
     m_messages.enqueue({ time(nullptr), prefix, name, text });
+    m_model->update();
 }
 
 void IRCLogBuffer::dump() const

+ 7 - 0
Applications/IRCClient/IRCLogBuffer.h

@@ -5,6 +5,8 @@
 #include <AK/Retainable.h>
 #include <AK/RetainPtr.h>
 
+class IRCLogBufferModel;
+
 class IRCLogBuffer : public Retainable<IRCLogBuffer> {
 public:
     static Retained<IRCLogBuffer> create();
@@ -24,8 +26,13 @@ public:
 
     void dump() const;
 
+    const IRCLogBufferModel* model() const { return m_model; }
+    IRCLogBufferModel* model() { return m_model; }
+
 private:
     IRCLogBuffer();
 
+    IRCLogBufferModel* m_model { nullptr };
+
     CircularQueue<Message, 1000> m_messages;
 };

+ 65 - 0
Applications/IRCClient/IRCLogBufferModel.cpp

@@ -0,0 +1,65 @@
+#include "IRCLogBufferModel.h"
+#include "IRCLogBuffer.h"
+#include <stdio.h>
+
+IRCLogBufferModel::IRCLogBufferModel(Retained<IRCLogBuffer>&& log_buffer)
+    : m_log_buffer(move(log_buffer))
+{
+}
+
+IRCLogBufferModel::~IRCLogBufferModel()
+{
+}
+
+int IRCLogBufferModel::row_count() const
+{
+    return m_log_buffer->count();
+}
+
+int IRCLogBufferModel::column_count() const
+{
+    return 4;
+}
+
+String IRCLogBufferModel::column_name(int column) const
+{
+    switch (column) {
+    case Column::Timestamp: return "Time";
+    case Column::Prefix: return "@";
+    case Column::Name: return "Name";
+    case Column::Text: return "Text";
+    }
+    ASSERT_NOT_REACHED();
+}
+
+GTableModel::ColumnMetadata IRCLogBufferModel::column_metadata(int column) const
+{
+    switch (column) {
+    case Column::Timestamp: return { 90, TextAlignment::CenterLeft };
+    case Column::Prefix: return { 10, TextAlignment::CenterLeft };
+    case Column::Name: return { 70, TextAlignment::CenterRight };
+    case Column::Text: return { 400, TextAlignment::CenterLeft };
+    }
+    ASSERT_NOT_REACHED();
+}
+
+GVariant IRCLogBufferModel::data(const GModelIndex& index, Role role) const
+{
+    auto& entry = m_log_buffer->at(index.row());
+    switch (index.column()) {
+    case Column::Timestamp: return String::format("%u", entry.timestamp);
+    case Column::Prefix: return entry.prefix;
+    case Column::Name: return entry.sender;
+    case Column::Text: return entry.text;
+    }
+    ASSERT_NOT_REACHED();
+}
+
+void IRCLogBufferModel::update()
+{
+    did_update();
+}
+
+void IRCLogBufferModel::activate(const GModelIndex&)
+{
+}

+ 29 - 0
Applications/IRCClient/IRCLogBufferModel.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <LibGUI/GTableModel.h>
+
+class IRCLogBuffer;
+
+class IRCLogBufferModel final : public GTableModel {
+public:
+    enum Column {
+        Timestamp = 0,
+        Prefix,
+        Name,
+        Text,
+    };
+
+    explicit IRCLogBufferModel(Retained<IRCLogBuffer>&&);
+    virtual ~IRCLogBufferModel() override;
+
+    virtual int row_count() const override;
+    virtual int column_count() const override;
+    virtual String column_name(int column) const override;
+    virtual ColumnMetadata column_metadata(int column) const override;
+    virtual GVariant data(const GModelIndex&, Role = Role::Display) const override;
+    virtual void update() override;
+    virtual void activate(const GModelIndex&) override;
+
+private:
+    Retained<IRCLogBuffer> m_log_buffer;
+};

+ 20 - 1
Applications/IRCClient/IRCSubWindow.cpp

@@ -1,11 +1,30 @@
 #include "IRCSubWindow.h"
+#include "IRCClient.h"
+#include "IRCLogBufferModel.h"
+#include <LibGUI/GBoxLayout.h>
+#include <LibGUI/GTableView.h>
+#include <LibGUI/GTextBox.h>
 
-IRCSubWindow::IRCSubWindow(const String& name, GWidget* parent)
+IRCSubWindow::IRCSubWindow(IRCClient& client, Type type, const String& name, GWidget* parent)
     : GWidget(parent)
+    , m_client(client)
+    , m_type(type)
     , m_name(name)
 {
+    set_layout(make<GBoxLayout>(Orientation::Vertical));
+    m_table_view = new GTableView(this);
+    m_table_view->set_font(Font::default_fixed_width_font());
+
+    m_client.register_subwindow(*this);
 }
 
 IRCSubWindow::~IRCSubWindow()
 {
+    m_client.unregister_subwindow(*this);
+}
+
+void IRCSubWindow::set_log_buffer(const IRCLogBuffer& log_buffer)
+{
+    m_log_buffer = &log_buffer;
+    m_table_view->set_model(OwnPtr<IRCLogBufferModel>((IRCLogBufferModel*)log_buffer.model()));
 }

+ 19 - 1
Applications/IRCClient/IRCSubWindow.h

@@ -2,14 +2,32 @@
 
 #include <LibGUI/GWidget.h>
 
+class IRCClient;
+class IRCLogBuffer;
+class GTableView;
+
 class IRCSubWindow : public GWidget {
 public:
-    explicit IRCSubWindow(const String& name, GWidget* parent);
+    enum Type {
+        Server,
+        Channel,
+        Query,
+    };
+
+    explicit IRCSubWindow(IRCClient&, Type, const String& name, GWidget* parent);
     virtual ~IRCSubWindow() 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&);
+
 private:
+    IRCClient& m_client;
+    Type m_type;
     String m_name;
+    GTableView* m_table_view { nullptr };
+    RetainPtr<IRCLogBuffer> m_log_buffer;
 };

+ 1 - 0
Applications/IRCClient/Makefile

@@ -3,6 +3,7 @@ OBJS = \
     IRCChannel.o \
     IRCQuery.o \
     IRCLogBuffer.o \
+    IRCLogBufferModel.o \
     IRCAppWindow.o \
     IRCSubWindow.o \
     main.o