1 // Copyright (C) 2007 Mark Pustjens <pustjens@dds.nl>
2 // Copyright (C) 2010-2015 Petr Pavlu <setup@dagobah.cz>
4 // This file is part of CenterIM.
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"
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
));
39 _("Add"), sigc::mem_fun(this, &AccountWindow::addAccount
));
40 buttons_
->appendSeparator();
42 _("Done"), sigc::hide(sigc::mem_fun(this, &AccountWindow::close
)));
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
);
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);
106 AccountWindow::StringOption::StringOption(PurpleAccount
*account
, Type type
)
107 : Button(FLAG_VALUE
), account_(account
), option_(nullptr), type_(type
)
109 g_assert(account_
!= nullptr);
114 void AccountWindow::StringOption::initialize()
116 if (type_
== TYPE_PASSWORD
)
117 setText(_("Password"));
118 else if (type_
== TYPE_ALIAS
)
121 setText(purple_account_option_get_text(option_
));
124 signal_activate
.connect(sigc::mem_fun(this, &StringOption::onActivate
));
127 void AccountWindow::StringOption::updateValue()
129 if (type_
== TYPE_PASSWORD
) {
131 setValue(purple_account_get_password(account_
));
133 else if (type_
== TYPE_ALIAS
)
134 setValue(purple_account_get_alias(account_
));
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
));
150 void AccountWindow::StringOption::responseHandler(
151 CppConsUI::InputDialog
&activator
, AbstractDialog::ResponseType response
)
153 if (response
!= AbstractDialog::RESPONSE_OK
)
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());
161 purple_account_set_string(account_
,
162 purple_account_option_get_setting(option_
), activator
.getText());
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_
));
176 signal_activate
.connect(sigc::mem_fun(this, &IntegerOption::onActivate
));
179 void AccountWindow::IntegerOption::updateValue()
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
));
195 void AccountWindow::IntegerOption::responseHandler(
196 CppConsUI::InputDialog
&activator
,
197 CppConsUI::AbstractDialog::ResponseType response
)
199 if (response
!= AbstractDialog::RESPONSE_OK
)
203 if (!Utils::stringToNumber(activator
.getText(), INT_MIN
, INT_MAX
, &num
))
205 purple_account_set_int(
206 account_
, purple_account_option_get_setting(option_
), num
);
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;
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*/,
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_
));
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();
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
));
299 void AccountWindow::SplitOption::responseHandler(
300 CppConsUI::InputDialog
&activator
,
301 CppConsUI::AbstractDialog::ResponseType response
)
303 if (response
!= AbstractDialog::RESPONSE_OK
)
306 setValue(activator
.getText());
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
)
320 purple_plugin_get_name(reinterpret_cast<PurplePlugin
*>(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),
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
];
371 account_entry
->parent
->grabFocus();
373 // Remove the nodes from the tree.
374 treeview_
->deleteNodeChildren(account_entry
->parent_reference
, false);
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.
386 entry
.parent
= nullptr;
387 account_entries_
[account
] = entry
;
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
;
406 g_strdup_printf("[%s] %s", purple_account_get_protocol_name(account
),
407 purple_account_get_username(account
));
408 account_entry
->parent
->setText(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.
417 new CppConsUI::Label(_("Invalid account or protocol plugin not loaded."));
418 treeview_
->appendNode(account_entry
->parent_reference
, *label
);
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;
435 PurpleAccountUserSplit
*split
=
436 reinterpret_cast<PurpleAccountUserSplit
*>(iter
->data
);
439 if (purple_account_user_split_get_reverse(split
))
440 s
= strrchr(username
, purple_account_user_split_get_separator(split
));
442 s
= strchr(username
, purple_account_user_split_get_separator(split
));
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
);
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
);
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;
480 PurpleAccountOption
*option
=
481 reinterpret_cast<PurpleAccountOption
*>(pref
->data
);
482 PurplePrefType type
= purple_account_option_get_type(option
);
485 case PURPLE_PREF_STRING
:
486 widget
= new StringOption(account
, option
);
487 treeview_
->appendNode(account_entry
->parent_reference
, *widget
);
489 case PURPLE_PREF_INT
:
490 widget
= new IntegerOption(account
, option
);
491 treeview_
->appendNode(account_entry
->parent_reference
, *widget
);
493 case PURPLE_PREF_BOOLEAN
:
494 widget
= new BoolOption(account
, option
);
495 treeview_
->appendNode(account_entry
->parent_reference
, *widget
);
497 case PURPLE_PREF_STRING_LIST
:
498 widget
= new StringListOption(account
, option
);
499 treeview_
->appendNode(account_entry
->parent_reference
, *widget
);
502 LOG
->error(_("Unhandled account option type '%d'."), type
);
507 // Enable/disable account.
508 widget
= new BoolOption(account
, BoolOption::TYPE_ENABLE_ACCOUNT
);
509 treeview_
->appendNode(account_entry
->parent_reference
, *widget
);
512 widget
= new ColorOption(account
);
513 treeview_
->appendNode(account_entry
->parent_reference
, *widget
);
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();
527 // Libpurple returns nullptr if there are no protocols available.
528 LOG
->warning(_("No protocol plugins available."));
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
));
556 void AccountWindow::dropAccountResponseHandler(
557 CppConsUI::MessageDialog
& /*activator*/,
558 CppConsUI::AbstractDialog::ResponseType response
, PurpleAccount
*account
)
560 if (response
!= AbstractDialog::RESPONSE_OK
)
563 purple_accounts_remove(account
);
564 clearAccount(account
, true);
567 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: