Update list of wide characters
[centerim5.git] / src / AccountWindow.cpp
blobad5a27441a608473a7ebb5f29c2ed340b043a557
1 // Copyright (C) 2007 Mark Pustjens <pustjens@dds.nl>
2 // Copyright (C) 2010-2015 Petr Pavlu <setup@dagobah.cz>
3 //
4 // This file is part of CenterIM.
5 //
6 // CenterIM is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // CenterIM is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with CenterIM. If not, see <http://www.gnu.org/licenses/>.
19 #include "AccountWindow.h"
21 #include "Log.h"
22 #include "Utils.h"
24 #include "gettext.h"
25 #include <cstring>
27 AccountWindow::AccountWindow() : SplitDialog(0, 0, 80, 24, _("Accounts"))
29 setColorScheme(CenterIM::SCHEME_GENERALWINDOW);
31 treeview_ = new CppConsUI::TreeView(AUTOSIZE, AUTOSIZE);
32 setContainer(*treeview_);
34 // Populate all defined accounts.
35 for (GList *i = purple_accounts_get_all(); i != nullptr; i = i->next)
36 populateAccount(reinterpret_cast<PurpleAccount *>(i->data));
38 buttons_->appendItem(
39 _("Add"), sigc::mem_fun(this, &AccountWindow::addAccount));
40 buttons_->appendSeparator();
41 buttons_->appendItem(
42 _("Done"), sigc::hide(sigc::mem_fun(this, &AccountWindow::close)));
44 onScreenResized();
47 void AccountWindow::onScreenResized()
49 moveResizeRect(CENTERIM->getScreenArea(CenterIM::CHAT_AREA));
52 AccountWindow::BoolOption::BoolOption(
53 PurpleAccount *account, PurpleAccountOption *option)
54 : account_(account), option_(option), type_(TYPE_PURPLE)
56 g_assert(account_ != nullptr);
57 g_assert(option_ != nullptr);
59 setText(purple_account_option_get_text(option_));
60 setChecked(purple_account_get_bool(account_,
61 purple_account_option_get_setting(option_),
62 purple_account_option_get_default_bool(option_)));
64 signal_toggle.connect(sigc::mem_fun(this, &BoolOption::onToggle));
67 AccountWindow::BoolOption::BoolOption(PurpleAccount *account, Type type)
68 : account_(account), option_(nullptr), type_(type)
70 g_assert(account_ != nullptr);
72 if (type == TYPE_REMEMBER_PASSWORD) {
73 setText(_("Remember password"));
74 setChecked(purple_account_get_remember_password(account_));
76 else if (type == TYPE_ENABLE_ACCOUNT) {
77 setText(_("Account enabled"));
78 setChecked(purple_account_get_enabled(account_, PACKAGE_NAME));
81 signal_toggle.connect(sigc::mem_fun(this, &BoolOption::onToggle));
84 void AccountWindow::BoolOption::onToggle(
85 CheckBox & /*activator*/, bool new_state)
87 if (type_ == TYPE_REMEMBER_PASSWORD)
88 purple_account_set_remember_password(account_, new_state);
89 else if (type_ == TYPE_ENABLE_ACCOUNT)
90 purple_account_set_enabled(account_, PACKAGE_NAME, new_state);
91 else
92 purple_account_set_bool(
93 account_, purple_account_option_get_setting(option_), new_state);
96 AccountWindow::StringOption::StringOption(
97 PurpleAccount *account, PurpleAccountOption *option)
98 : Button(FLAG_VALUE), account_(account), option_(option), type_(TYPE_PURPLE)
100 g_assert(account_ != nullptr);
101 g_assert(option_ != nullptr);
103 initialize();
106 AccountWindow::StringOption::StringOption(PurpleAccount *account, Type type)
107 : Button(FLAG_VALUE), account_(account), option_(nullptr), type_(type)
109 g_assert(account_ != nullptr);
111 initialize();
114 void AccountWindow::StringOption::initialize()
116 if (type_ == TYPE_PASSWORD)
117 setText(_("Password"));
118 else if (type_ == TYPE_ALIAS)
119 setText(_("Alias"));
120 else
121 setText(purple_account_option_get_text(option_));
123 updateValue();
124 signal_activate.connect(sigc::mem_fun(this, &StringOption::onActivate));
127 void AccountWindow::StringOption::updateValue()
129 if (type_ == TYPE_PASSWORD) {
130 setMasked(true);
131 setValue(purple_account_get_password(account_));
133 else if (type_ == TYPE_ALIAS)
134 setValue(purple_account_get_alias(account_));
135 else
136 setValue(purple_account_get_string(account_,
137 purple_account_option_get_setting(option_),
138 purple_account_option_get_default_string(option_)));
141 void AccountWindow::StringOption::onActivate(Button & /*activator*/)
143 auto dialog = new CppConsUI::InputDialog(getText(), getValue());
144 dialog->setMasked(isMasked());
145 dialog->signal_response.connect(
146 sigc::mem_fun(this, &StringOption::responseHandler));
147 dialog->show();
150 void AccountWindow::StringOption::responseHandler(
151 CppConsUI::InputDialog &activator, AbstractDialog::ResponseType response)
153 if (response != AbstractDialog::RESPONSE_OK)
154 return;
156 if (type_ == TYPE_PASSWORD)
157 purple_account_set_password(account_, activator.getText());
158 else if (type_ == TYPE_ALIAS)
159 purple_account_set_alias(account_, activator.getText());
160 else
161 purple_account_set_string(account_,
162 purple_account_option_get_setting(option_), activator.getText());
164 updateValue();
167 AccountWindow::IntegerOption::IntegerOption(
168 PurpleAccount *account, PurpleAccountOption *option)
169 : Button(FLAG_VALUE), account_(account), option_(option)
171 g_assert(account_ != nullptr);
172 g_assert(option_ != nullptr);
174 setText(purple_account_option_get_text(option_));
175 updateValue();
176 signal_activate.connect(sigc::mem_fun(this, &IntegerOption::onActivate));
179 void AccountWindow::IntegerOption::updateValue()
181 setValue(
182 purple_account_get_int(account_, purple_account_option_get_setting(option_),
183 purple_account_option_get_default_int(option_)));
186 void AccountWindow::IntegerOption::onActivate(Button & /*activator*/)
188 auto dialog = new CppConsUI::InputDialog(getText(), getValue());
189 dialog->setFlags(CppConsUI::TextEntry::FLAG_NUMERIC);
190 dialog->signal_response.connect(
191 sigc::mem_fun(this, &IntegerOption::responseHandler));
192 dialog->show();
195 void AccountWindow::IntegerOption::responseHandler(
196 CppConsUI::InputDialog &activator,
197 CppConsUI::AbstractDialog::ResponseType response)
199 if (response != AbstractDialog::RESPONSE_OK)
200 return;
202 long num;
203 if (!Utils::stringToNumber(activator.getText(), INT_MIN, INT_MAX, &num))
204 return;
205 purple_account_set_int(
206 account_, purple_account_option_get_setting(option_), num);
208 updateValue();
211 AccountWindow::StringListOption::StringListOption(
212 PurpleAccount *account, PurpleAccountOption *option)
213 : account_(account), option_(option)
215 g_assert(account_ != nullptr);
216 g_assert(option_ != nullptr);
218 setText(purple_account_option_get_text(option_));
220 const char *def = purple_account_get_string(account_,
221 purple_account_option_get_setting(option_),
222 purple_account_option_get_default_list_value(option_));
223 for (GList *l = purple_account_option_get_list(option_); l != nullptr;
224 l = l->next)
225 if (l->data != nullptr) {
226 PurpleKeyValuePair *kvp = reinterpret_cast<PurpleKeyValuePair *>(l->data);
227 addOptionPtr(kvp->key, kvp->value);
228 if (kvp->value != nullptr && def != nullptr &&
229 std::strcmp(def, reinterpret_cast<const char *>(kvp->value)) == 0)
230 setSelectedByDataPtr(kvp->value);
233 signal_selection_changed.connect(
234 sigc::mem_fun(this, &StringListOption::onSelectionChanged));
237 void AccountWindow::StringListOption::onSelectionChanged(
238 ComboBox & /*activator*/, int /*new_entry*/, const char * /*title*/,
239 intptr_t data)
241 purple_account_set_string(account_,
242 purple_account_option_get_setting(option_),
243 reinterpret_cast<const char *>(data));
246 AccountWindow::SplitOption::SplitOption(PurpleAccount *account,
247 PurpleAccountUserSplit *split, AccountEntry *account_entry)
248 : Button(FLAG_VALUE), account_(account), split_(split),
249 account_entry_(account_entry)
251 g_assert(account_ != nullptr);
253 if (split_ != nullptr)
254 setText(purple_account_user_split_get_text(split_));
255 else
256 setText(_("Username"));
258 signal_activate.connect(sigc::mem_fun(this, &SplitOption::onActivate));
261 void AccountWindow::SplitOption::updateSplits()
263 SplitWidgets *split_widgets = &account_entry_->split_widgets;
264 SplitWidgets::iterator split_widget = split_widgets->begin();
265 SplitOption *widget = *split_widget;
266 const char *val = widget->getValue();
267 ++split_widget;
269 GString *username = g_string_new(val);
270 PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(
271 purple_find_prpl(purple_account_get_protocol_id(account_)));
273 for (GList *iter = prplinfo->user_splits;
274 iter != nullptr && split_widget != split_widgets->end();
275 iter = iter->next, ++split_widget) {
276 PurpleAccountUserSplit *user_split =
277 reinterpret_cast<PurpleAccountUserSplit *>(iter->data);
278 widget = *split_widget;
280 val = widget->getValue();
281 if (val == nullptr || *val == '\0')
282 val = purple_account_user_split_get_default_value(user_split);
283 g_string_append_printf(username, "%c%s",
284 purple_account_user_split_get_separator(user_split), val);
287 purple_account_set_username(account_, username->str);
288 g_string_free(username, TRUE);
291 void AccountWindow::SplitOption::onActivate(Button & /*activator*/)
293 auto dialog = new CppConsUI::InputDialog(text_, value_);
294 dialog->signal_response.connect(
295 sigc::mem_fun(this, &SplitOption::responseHandler));
296 dialog->show();
299 void AccountWindow::SplitOption::responseHandler(
300 CppConsUI::InputDialog &activator,
301 CppConsUI::AbstractDialog::ResponseType response)
303 if (response != AbstractDialog::RESPONSE_OK)
304 return;
306 setValue(activator.getText());
307 updateSplits();
310 AccountWindow::ProtocolOption::ProtocolOption(
311 PurpleAccount *account, AccountWindow &account_window)
312 : account_window_(&account_window), account_(account)
314 g_assert(account_ != nullptr);
316 setText(_("Protocol"));
318 for (GList *i = purple_plugins_get_protocols(); i != nullptr; i = i->next)
319 addOptionPtr(
320 purple_plugin_get_name(reinterpret_cast<PurplePlugin *>(i->data)),
321 i->data);
323 const char *proto_id = purple_account_get_protocol_id(account_);
324 PurplePlugin *plugin = purple_plugins_find_with_id(proto_id);
325 setSelectedByDataPtr(plugin);
327 signal_selection_changed.connect(
328 sigc::mem_fun(this, &ProtocolOption::onProtocolChanged));
331 void AccountWindow::ProtocolOption::onProtocolChanged(ComboBox & /*activator*/,
332 std::size_t /*new_entry*/, const char * /*title*/, intptr_t data)
334 purple_account_set_protocol_id(
335 account_, purple_plugin_get_id(reinterpret_cast<PurplePlugin *>(data)));
337 // This deletes us so do not touch any member variable after it.
338 account_window_->populateAccount(account_);
341 AccountWindow::ColorOption::ColorOption(PurpleAccount *account)
342 : ColorPicker(CppConsUI::Curses::Color::DEFAULT,
343 CppConsUI::Curses::Color::DEFAULT, _("Buddylist color:"), true),
344 account_(account)
346 g_assert(account_ != nullptr);
348 int fg = purple_account_get_ui_int(account, "centerim5",
349 "buddylist-foreground-color", CppConsUI::Curses::Color::DEFAULT);
350 int bg = purple_account_get_ui_int(account, "centerim5",
351 "buddylist-background-color", CppConsUI::Curses::Color::DEFAULT);
352 setColorPair(fg, bg);
353 signal_colorpair_selected.connect(
354 sigc::mem_fun(this, &ColorOption::onColorChanged));
357 void AccountWindow::ColorOption::onColorChanged(
358 CppConsUI::ColorPicker & /*activator*/, int new_fg, int new_bg)
360 purple_account_set_ui_int(
361 account_, "centerim5", "buddylist-foreground-color", new_fg);
362 purple_account_set_ui_int(
363 account_, "centerim5", "buddylist-background-color", new_bg);
366 void AccountWindow::clearAccount(PurpleAccount *account, bool full)
368 AccountEntry *account_entry = &account_entries_[account];
370 // Move focus.
371 account_entry->parent->grabFocus();
373 // Remove the nodes from the tree.
374 treeview_->deleteNodeChildren(account_entry->parent_reference, false);
375 if (full) {
376 treeview_->deleteNode(account_entry->parent_reference, false);
377 account_entries_.erase(account);
381 void AccountWindow::populateAccount(PurpleAccount *account)
383 if (account_entries_.find(account) == account_entries_.end()) {
384 // No entry for this account, add one.
385 AccountEntry entry;
386 entry.parent = nullptr;
387 account_entries_[account] = entry;
389 else {
390 // The account exists, clear all data.
391 clearAccount(account, false);
394 AccountEntry *account_entry = &account_entries_[account];
396 if (account_entry->parent == nullptr) {
397 auto button = new CppConsUI::TreeView::ToggleCollapseButton;
398 CppConsUI::TreeView::NodeReference parent_reference =
399 treeview_->appendNode(treeview_->getRootNode(), *button);
400 treeview_->setCollapsed(parent_reference, true);
401 account_entry->parent = button;
402 account_entry->parent_reference = parent_reference;
405 char *label =
406 g_strdup_printf("[%s] %s", purple_account_get_protocol_name(account),
407 purple_account_get_username(account));
408 account_entry->parent->setText(label);
409 g_free(label);
411 const char *protocol_id = purple_account_get_protocol_id(account);
412 PurplePlugin *prpl = purple_find_prpl(protocol_id);
414 if (prpl == nullptr) {
415 // Settings of an unknown account cannot be changed.
416 auto label =
417 new CppConsUI::Label(_("Invalid account or protocol plugin not loaded."));
418 treeview_->appendNode(account_entry->parent_reference, *label);
420 else {
421 PurplePluginProtocolInfo *prplinfo = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
423 // Protocols combobox.
424 auto combobox = new ProtocolOption(account, *this);
425 CppConsUI::TreeView::NodeReference protocol_node =
426 treeview_->appendNode(account_entry->parent_reference, *combobox);
427 combobox->grabFocus();
429 // The username must be treated in a special way because it can contain
430 // multiple values (e.g. user@server:port/resource).
431 char *username = g_strdup(purple_account_get_username(account));
433 for (GList *iter = g_list_last(prplinfo->user_splits); iter != nullptr;
434 iter = iter->prev) {
435 PurpleAccountUserSplit *split =
436 reinterpret_cast<PurpleAccountUserSplit *>(iter->data);
438 char *s;
439 if (purple_account_user_split_get_reverse(split))
440 s = strrchr(username, purple_account_user_split_get_separator(split));
441 else
442 s = strchr(username, purple_account_user_split_get_separator(split));
444 const char *value;
445 if (s != nullptr) {
446 *s = '\0';
447 value = s + 1;
449 else
450 value = purple_account_user_split_get_default_value(split);
452 // Create widget for the username split and remember.
453 auto widget_split = new SplitOption(account, split, account_entry);
454 widget_split->setValue(value);
455 account_entry->split_widgets.push_front(widget_split);
457 treeview_->appendNode(account_entry->parent_reference, *widget_split);
460 auto widget_split = new SplitOption(account, nullptr, account_entry);
461 widget_split->setValue(username);
462 account_entry->split_widgets.push_front(widget_split);
463 treeview_->insertNodeAfter(protocol_node, *widget_split);
464 g_free(username);
466 // Password.
467 Widget *widget = new StringOption(account, StringOption::TYPE_PASSWORD);
468 treeview_->appendNode(account_entry->parent_reference, *widget);
470 // Remember password.
471 widget = new BoolOption(account, BoolOption::TYPE_REMEMBER_PASSWORD);
472 treeview_->appendNode(account_entry->parent_reference, *widget);
474 // Alias.
475 widget = new StringOption(account, StringOption::TYPE_ALIAS);
476 treeview_->appendNode(account_entry->parent_reference, *widget);
478 for (GList *pref = prplinfo->protocol_options; pref != nullptr;
479 pref = pref->next) {
480 PurpleAccountOption *option =
481 reinterpret_cast<PurpleAccountOption *>(pref->data);
482 PurplePrefType type = purple_account_option_get_type(option);
484 switch (type) {
485 case PURPLE_PREF_STRING:
486 widget = new StringOption(account, option);
487 treeview_->appendNode(account_entry->parent_reference, *widget);
488 break;
489 case PURPLE_PREF_INT:
490 widget = new IntegerOption(account, option);
491 treeview_->appendNode(account_entry->parent_reference, *widget);
492 break;
493 case PURPLE_PREF_BOOLEAN:
494 widget = new BoolOption(account, option);
495 treeview_->appendNode(account_entry->parent_reference, *widget);
496 break;
497 case PURPLE_PREF_STRING_LIST:
498 widget = new StringListOption(account, option);
499 treeview_->appendNode(account_entry->parent_reference, *widget);
500 break;
501 default:
502 LOG->error(_("Unhandled account option type '%d'."), type);
503 break;
507 // Enable/disable account.
508 widget = new BoolOption(account, BoolOption::TYPE_ENABLE_ACCOUNT);
509 treeview_->appendNode(account_entry->parent_reference, *widget);
511 // Color.
512 widget = new ColorOption(account);
513 treeview_->appendNode(account_entry->parent_reference, *widget);
516 // Drop account.
517 auto drop_button = new CppConsUI::Button(_("Drop account"));
518 drop_button->signal_activate.connect(
519 sigc::bind(sigc::mem_fun(this, &AccountWindow::dropAccount), account));
520 treeview_->appendNode(account_entry->parent_reference, *drop_button);
523 void AccountWindow::addAccount(CppConsUI::Button & /*activator*/)
525 GList *i = purple_plugins_get_protocols();
526 if (i == nullptr) {
527 // Libpurple returns nullptr if there are no protocols available.
528 LOG->warning(_("No protocol plugins available."));
529 return;
532 PurpleAccount *account = purple_account_new(_("Username"),
533 purple_plugin_get_id(reinterpret_cast<PurplePlugin *>(i->data)));
535 // Stop here if libpurple returned an already created account. This happens
536 // when user pressed Add button twice in a row. In that case there is
537 // already one "empty" account that user can edit.
538 if (account_entries_.find(account) == account_entries_.end()) {
539 purple_accounts_add(account);
541 populateAccount(account);
543 account_entries_[account].parent->grabFocus();
546 void AccountWindow::dropAccount(
547 CppConsUI::Button & /*activator*/, PurpleAccount *account)
549 auto dialog = new CppConsUI::MessageDialog(
550 _("Account deletion"), _("Are you sure you want to delete this account?"));
551 dialog->signal_response.connect(sigc::bind(
552 sigc::mem_fun(this, &AccountWindow::dropAccountResponseHandler), account));
553 dialog->show();
556 void AccountWindow::dropAccountResponseHandler(
557 CppConsUI::MessageDialog & /*activator*/,
558 CppConsUI::AbstractDialog::ResponseType response, PurpleAccount *account)
560 if (response != AbstractDialog::RESPONSE_OK)
561 return;
563 purple_accounts_remove(account);
564 clearAccount(account, true);
567 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: