charset: add class Utf8ToLocale
[ncmpc-debian.git] / src / screen_chat.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2018 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #include "screen_chat.hxx"
21 #include "screen_interface.hxx"
22 #include "screen_utils.hxx"
23 #include "screen_status.hxx"
24 #include "TextPage.hxx"
25 #include "mpdclient.hxx"
26 #include "i18n.h"
27 #include "charset.hxx"
28 #include "options.hxx"
29
30 #include <glib.h>
31 #include <mpd/idle.h>
32
33 static const char chat_channel[] = "chat";
34
35 class ChatPage final : public TextPage {
36         unsigned last_connection_id = 0;
37         bool was_supported = false;
38
39 public:
40         ChatPage(ScreenManager &_screen, WINDOW *w, Size size)
41                 :TextPage(_screen, w, size) {}
42
43 private:
44         bool CheckChatSupport(struct mpdclient &c);
45
46         void ProcessMessage(const struct mpd_message &message);
47
48 public:
49         /* virtual methods from class Page */
50         void Update(struct mpdclient &c, unsigned events) override;
51         bool OnCommand(struct mpdclient &c, command_t cmd) override;
52
53         const char *GetTitle(char *, size_t) const override {
54                 return _("Chat");
55         }
56 };
57
58 bool
59 ChatPage::CheckChatSupport(struct mpdclient &c)
60 {
61         /* if we're using the same connection as the last time
62            check_chat_support was called, we can use the cached information
63            about whether chat is supported */
64         if (c.connection_id == last_connection_id)
65                 return was_supported;
66
67         last_connection_id = c.connection_id;
68
69         if (c.connection == nullptr)
70                 return (was_supported = false);
71
72         if (mpd_connection_cmp_server_version(c.connection, 0, 17, 0) == -1) {
73                 const unsigned *version = mpd_connection_get_server_version(c.connection);
74                 char *str;
75
76                 str = g_strdup_printf(
77                         _("connected to MPD %u.%u.%u (you need at least \n"
78                           "version 0.17.0 to use the chat feature)"),
79                         version[0], version[1], version[2]);
80                 Append(str);
81                 g_free(str);
82
83                 return (was_supported = false);
84         }
85
86         /* mpdclient_get_connection? */
87         if (!mpdclient_cmd_subscribe(&c, chat_channel))
88                 return (was_supported = false);
89         /* mpdclient_put_connection? */
90
91         return (was_supported = true);
92 }
93
94 static Page *
95 screen_chat_init(ScreenManager &screen, WINDOW *w, Size size)
96 {
97         return new ChatPage(screen, w, size);
98 }
99
100 void
101 ChatPage::ProcessMessage(const struct mpd_message &message)
102 {
103         /* You'll have to move this out of screen_chat, if you want to use
104            client-to-client messages anywhere else */
105         assert(g_strcmp0(mpd_message_get_channel(&message), chat_channel) == 0);
106
107         Append(Utf8ToLocale(mpd_message_get_text(&message)).c_str());
108
109         SetDirty();
110 }
111
112 void
113 ChatPage::Update(struct mpdclient &c, unsigned events)
114 {
115         if (CheckChatSupport(c) && (events & MPD_IDLE_MESSAGE)) {
116                 if (!mpdclient_send_read_messages(&c))
117                         return;
118
119                 struct mpd_message *message;
120                 while ((message = mpdclient_recv_message(&c)) != nullptr) {
121                         ProcessMessage(*message);
122                         mpd_message_free(message);
123                 }
124
125                 mpdclient_finish_command(&c);
126         }
127 }
128
129 static char *
130 screen_chat_get_prefix()
131 {
132         static char *prefix = nullptr;
133
134         if (prefix)
135                 return prefix;
136
137         if (options.chat_prefix) {
138                 /* Options are encoded in the "locale" charset */
139                 prefix = locale_to_utf8(options.chat_prefix);
140                 return prefix;
141         }
142
143         prefix = g_strconcat("<", g_get_user_name(), "> ", nullptr);
144         return prefix;
145 }
146
147 static void
148 screen_chat_send_message(struct mpdclient *c, char *msg)
149 {
150         char *utf8 = locale_to_utf8(msg);
151         char *prefix = screen_chat_get_prefix();
152         char *full_msg = g_strconcat(prefix, utf8, nullptr);
153         g_free(utf8);
154
155         (void) mpdclient_cmd_send_message(c, chat_channel, full_msg);
156         g_free(full_msg);
157 }
158
159 bool
160 ChatPage::OnCommand(struct mpdclient &c, command_t cmd)
161 {
162         if (TextPage::OnCommand(c, cmd))
163                 return true;
164
165         if (cmd == CMD_PLAY) {
166                 char *message = screen_readln(_("Your message"), nullptr, nullptr, nullptr);
167
168                 /* the user entered an empty line */
169                 if (message == nullptr)
170                         return true;
171
172                 if (CheckChatSupport(c))
173                         screen_chat_send_message(&c, message);
174                 else
175                         screen_status_message(_("Message could not be sent"));
176
177                 g_free(message);
178
179                 return true;
180         }
181
182         return false;
183 }
184
185 const struct screen_functions screen_chat = {
186         "chat",
187         screen_chat_init,
188 };