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 "BuddyList.h"
26 #include <cppconsui/Spacer.h>
30 BuddyList
*BuddyList::my_instance_
= nullptr;
32 BuddyList
*BuddyList::instance()
37 bool BuddyList::processInputText(const TermKeyKey
&key
)
39 if (!filter_
->isVisible())
42 std::size_t input_len
= strlen(key
.utf8
);
43 if (filter_buffer_length_
+ input_len
+ 1 > sizeof(filter_buffer_
))
46 char *pos
= filter_buffer_
+ filter_buffer_length_
;
47 strcpy(pos
, key
.utf8
);
48 filter_buffer_onscreen_width_
+= CppConsUI::Curses::onScreenWidth(pos
);
49 filter_buffer_length_
+= input_len
;
51 updateList(UPDATE_OTHERS
);
57 bool BuddyList::restoreFocus()
59 FOOTER
->setText(_("%s act conv, %s main menu, %s context menu, %s filter"),
60 "centerim|conversation-active", "centerim|generalmenu",
61 "buddylist|contextmenu", "buddylist|filter");
63 return CppConsUI::Window::restoreFocus();
66 void BuddyList::ungrabFocus()
68 FOOTER
->setText(nullptr);
69 CppConsUI::Window::ungrabFocus();
72 void BuddyList::close()
74 // BuddyList cannot be closed, instead close the filter input if it is active.
75 if (!filter_
->isVisible())
79 updateList(UPDATE_OTHERS
);
82 void BuddyList::onScreenResized()
84 moveResizeRect(CENTERIM
->getScreenArea(CenterIM::BUDDY_LIST_AREA
));
87 void BuddyList::updateNode(PurpleBlistNode
*node
)
89 update(buddylist_
, node
);
92 BuddyList::Filter::Filter(BuddyList
*parent_blist
)
93 : Widget(AUTOSIZE
, 1), parent_blist_(parent_blist
)
97 int BuddyList::Filter::draw(
98 CppConsUI::Curses::ViewPort area
, CppConsUI::Error
&error
)
101 DRAW(area
.addString(0, 0, real_width_
, _("Filter: "), error
, &printed
));
102 if (static_cast<int>(parent_blist_
->filter_buffer_onscreen_width_
) <=
103 real_width_
- printed
) {
104 // Optimized simple case.
105 DRAW(area
.addString(printed
, 0, parent_blist_
->filter_buffer_
, error
));
111 parent_blist_
->filter_buffer_
+ parent_blist_
->filter_buffer_length_
;
114 g_utf8_find_prev_char(parent_blist_
->filter_buffer_
, cur
);
117 gunichar uc
= g_utf8_get_char(prev
);
118 int wc
= CppConsUI::Curses::onScreenWidth(uc
);
119 if (w
+ wc
> real_width_
- printed
)
124 DRAW(area
.addString(real_width_
- w
, 0, cur
, error
));
129 BuddyList::AddWindow::AddWindow(const char *title
)
130 : SplitDialog(0, 0, 80, 24, title
)
132 setColorScheme(CenterIM::SCHEME_GENERALWINDOW
);
134 treeview_
= new CppConsUI::TreeView(AUTOSIZE
, AUTOSIZE
);
135 setContainer(*treeview_
);
137 buttons_
->appendItem(_("Add"), sigc::mem_fun(this, &AddWindow::onAddRequest
));
138 buttons_
->appendSeparator();
139 buttons_
->appendItem(
140 _("Cancel"), sigc::hide(sigc::mem_fun(this, &AddWindow::close
)));
145 void BuddyList::AddWindow::onScreenResized()
147 moveResizeRect(CENTERIM
->getScreenArea(CenterIM::CHAT_AREA
));
150 BuddyList::AddWindow::AccountOption::AccountOption(
151 PurpleAccount
*default_account
, bool chat_only
)
152 : ComboBox(_("Account"))
154 for (GList
*l
= purple_accounts_get_all(); l
!= nullptr; l
= l
->next
) {
155 PurpleAccount
*account
= static_cast<PurpleAccount
*>(l
->data
);
156 if (!purple_account_is_connected(account
))
160 PurpleConnection
*gc
= purple_account_get_connection(account
);
161 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
)->join_chat
== nullptr)
166 g_strdup_printf("[%s] %s", purple_account_get_protocol_name(account
),
167 purple_account_get_username(account
));
168 addOptionPtr(label
, account
);
171 setSelectedByDataPtr(default_account
);
174 BuddyList::AddWindow::GroupOption::GroupOption(const char *default_group
)
175 : ComboBox(_("Group"))
177 bool add_default_group
= true;
178 for (PurpleBlistNode
*node
= purple_blist_get_root(); node
!= nullptr;
179 node
= purple_blist_node_get_sibling_next(node
))
180 if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
181 const char *name
= purple_group_get_name(PURPLE_GROUP(node
));
182 int opt
= addOption(name
);
183 if (default_group
!= nullptr && std::strcmp(default_group
, name
) == 0)
185 add_default_group
= false;
187 if (add_default_group
)
188 addOption(_("Default"));
191 BuddyList::AddWindow::StringOption::StringOption(
192 const char *text
, const char *value
, bool masked
)
193 : Button(FLAG_VALUE
, text
, value
, nullptr, nullptr, masked
)
195 signal_activate
.connect(sigc::mem_fun(this, &StringOption::onActivate
));
198 void BuddyList::AddWindow::StringOption::onActivate(
199 CppConsUI::Button
& /*activator*/)
201 auto dialog
= new CppConsUI::InputDialog(getText(), getValue());
202 dialog
->setMasked(isMasked());
203 dialog
->signal_response
.connect(
204 sigc::mem_fun(this, &StringOption::responseHandler
));
208 void BuddyList::AddWindow::StringOption::responseHandler(
209 CppConsUI::InputDialog
&activator
,
210 CppConsUI::AbstractDialog::ResponseType response
)
212 if (response
!= AbstractDialog::RESPONSE_OK
)
215 setValue(activator
.getText());
218 BuddyList::AddWindow::IntegerOption::IntegerOption(
219 const char *text
, const char *value
, bool masked
, int min
, int max
)
220 : Button(FLAG_VALUE
, text
, nullptr, nullptr, nullptr, masked
),
221 min_value_(min
), max_value_(max
)
223 // Make sure that the default value is in the range.
224 long i
= std::strtol(value
, nullptr, 10);
225 i
= CLAMP(i
, min_value_
, max_value_
);
228 signal_activate
.connect(sigc::mem_fun(this, &IntegerOption::onActivate
));
231 void BuddyList::AddWindow::IntegerOption::onActivate(
232 CppConsUI::Button
& /*activator*/)
234 auto dialog
= new CppConsUI::InputDialog(getText(), getValue());
235 dialog
->setFlags(CppConsUI::TextEntry::FLAG_NUMERIC
);
236 dialog
->setMasked(isMasked());
237 dialog
->signal_response
.connect(
238 sigc::mem_fun(this, &IntegerOption::responseHandler
));
242 void BuddyList::AddWindow::IntegerOption::responseHandler(
243 CppConsUI::InputDialog
&activator
,
244 CppConsUI::AbstractDialog::ResponseType response
)
246 if (response
!= AbstractDialog::RESPONSE_OK
)
250 if (!Utils::stringToNumber(activator
.getText(), min_value_
, max_value_
, &num
))
256 BuddyList::AddWindow::BooleanOption::BooleanOption(
257 const char *text
, bool checked
)
258 : CheckBox(text
, checked
)
262 BuddyList::AddBuddyWindow::AddBuddyWindow(PurpleAccount
*account
,
263 const char *username
, const char *group
, const char *alias
)
264 : AddWindow(_("Add buddy"))
266 CppConsUI::TreeView::NodeReference parent
= treeview_
->getRootNode();
268 account_option_
= new AccountOption(account
);
269 treeview_
->appendNode(parent
, *account_option_
);
270 account_option_
->grabFocus();
272 name_option_
= new StringOption(_("Buddy name"), username
);
273 treeview_
->appendNode(parent
, *name_option_
);
275 alias_option_
= new StringOption(_("Alias"), alias
);
276 treeview_
->appendNode(parent
, *alias_option_
);
278 group_option_
= new GroupOption(group
);
279 treeview_
->appendNode(parent
, *group_option_
);
282 void BuddyList::AddBuddyWindow::onAddRequest(CppConsUI::Button
& /*activator*/)
284 PurpleAccount
*account
=
285 static_cast<PurpleAccount
*>(account_option_
->getSelectedDataPtr());
286 const char *name
= name_option_
->getValue();
287 const char *alias
= alias_option_
->getValue();
288 const char *group
= group_option_
->getSelectedTitle();
290 if (!purple_account_is_connected(account
)) {
291 LOG
->message(_("Selected account is not connected."));
294 if (name
== nullptr || name
[0] == '\0') {
295 LOG
->message(_("No buddy name specified."));
298 if (alias
!= nullptr && alias
[0] == '\0')
301 PurpleGroup
*g
= purple_find_group(group
);
303 g
= purple_group_new(group
);
304 purple_blist_add_group(g
, nullptr);
306 PurpleBuddy
*b
= purple_find_buddy_in_group(account
, name
, g
);
308 LOG
->message(_("Specified buddy is already in the list."));
310 // Add the specified buddy.
311 b
= purple_buddy_new(account
, name
, alias
);
312 purple_blist_add_buddy(b
, nullptr, g
, nullptr);
313 purple_account_add_buddy(account
, b
);
319 BuddyList::AddChatWindow::AddChatWindow(PurpleAccount
*account
,
320 const char * /*name*/, const char *alias
, const char *group
)
321 : AddWindow(_("Add chat"))
323 CppConsUI::TreeView::NodeReference parent
= treeview_
->getRootNode();
325 account_option_
= new AccountOption(account
, true);
326 account_option_
->signal_selection_changed
.connect(
327 sigc::mem_fun(this, &AddChatWindow::onAccountChanged
));
328 account_option_ref_
= treeview_
->appendNode(parent
, *account_option_
);
329 account_option_
->grabFocus();
331 alias_option_
= new StringOption(_("Alias"), alias
);
332 treeview_
->appendNode(parent
, *alias_option_
);
334 group_option_
= new GroupOption(group
);
335 treeview_
->appendNode(parent
, *group_option_
);
337 autojoin_option_
= new BooleanOption(_("Auto-join"), false);
338 treeview_
->appendNode(parent
, *autojoin_option_
);
340 // Add protocol-specific fields.
342 static_cast<PurpleAccount
*>(account_option_
->getSelectedDataPtr()));
345 void BuddyList::AddChatWindow::onAddRequest(CppConsUI::Button
& /*activator*/)
347 PurpleAccount
*account
=
348 static_cast<PurpleAccount
*>(account_option_
->getSelectedDataPtr());
349 const char *alias
= alias_option_
->getValue();
350 const char *group
= group_option_
->getSelectedTitle();
351 bool autojoin
= autojoin_option_
->isChecked();
353 if (!purple_account_is_connected(account
)) {
354 LOG
->message(_("Selected account is not connected."));
358 GHashTable
*components
=
359 g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
360 for (ChatInfos::value_type
&chat_info
: chat_infos_
) {
361 CppConsUI::Button
*button
=
362 dynamic_cast<CppConsUI::Button
*>(chat_info
.second
->getWidget());
363 g_assert(button
!= nullptr);
364 const char *value
= button
->getValue();
365 if (value
[0] != '\0')
366 g_hash_table_replace(components
, g_strdup(chat_info
.first
.c_str()),
367 g_strdup(button
->getValue()));
370 PurpleChat
*chat
= purple_chat_new(account
, alias
, components
);
372 if (chat
!= nullptr) {
373 PurpleGroup
*g
= purple_find_group(group
);
375 g
= purple_group_new(group
);
376 purple_blist_add_group(g
, nullptr);
378 purple_blist_add_chat(chat
, g
, nullptr);
379 purple_blist_node_set_bool(
380 PURPLE_BLIST_NODE(chat
), PACKAGE_NAME
"-autojoin", autojoin
);
386 void BuddyList::AddChatWindow::populateChatInfo(PurpleAccount
*account
)
388 // Remove old entries.
389 for (ChatInfos::value_type
&chat_info
: chat_infos_
)
390 treeview_
->deleteNode(chat_info
.second
, false);
393 PurpleConnection
*gc
= purple_account_get_connection(account
);
394 PurplePluginProtocolInfo
*info
=
395 PURPLE_PLUGIN_PROTOCOL_INFO(purple_connection_get_prpl(gc
));
396 if (info
->chat_info
== nullptr)
399 GHashTable
*defaults
= nullptr;
400 if (info
->chat_info_defaults
!= nullptr)
401 defaults
= info
->chat_info_defaults(gc
, "");
403 CppConsUI::TreeView::NodeReference ref
= account_option_ref_
;
404 for (GList
*l
= info
->chat_info(gc
); l
!= nullptr; l
= l
->next
) {
405 struct proto_chat_entry
*entry
=
406 static_cast<struct proto_chat_entry
*>(l
->data
);
408 // Remove any accelerator from the label:
409 char *label
= Utils::stripAccelerator(entry
->label
);
411 // And strip any trailing colon.
412 std::size_t len
= strlen(label
);
413 if (label
[len
- 1] == ':')
414 label
[len
- 1] = '\0';
416 // Get default value.
417 const char *value
= nullptr;
418 if (defaults
!= nullptr)
419 value
= static_cast<const char *>(
420 g_hash_table_lookup(defaults
, entry
->identifier
));
422 CppConsUI::Button
*button
;
425 new IntegerOption(label
, value
, entry
->secret
, entry
->min
, entry
->max
);
427 button
= new StringOption(label
, value
, entry
->secret
);
430 ref
= treeview_
->insertNodeAfter(ref
, *button
);
431 chat_infos_
[entry
->identifier
] = ref
;
434 if (defaults
!= nullptr)
435 g_hash_table_destroy(defaults
);
438 void BuddyList::AddChatWindow::onAccountChanged(
439 CppConsUI::Button
& /*activator*/, std::size_t /*new_entry*/,
440 const char * /*title*/, intptr_t data
)
442 populateChatInfo(reinterpret_cast<PurpleAccount
*>(data
));
445 BuddyList::AddGroupWindow::AddGroupWindow() : AddWindow(_("Add group"))
447 name_option_
= new StringOption(_("Name"), _("New group"));
448 treeview_
->appendNode(treeview_
->getRootNode(), *name_option_
);
449 name_option_
->grabFocus();
452 void BuddyList::AddGroupWindow::onAddRequest(CppConsUI::Button
& /*activator*/)
454 const char *name
= name_option_
->getValue();
455 if (name
== nullptr || name
[0] == '\0') {
456 LOG
->message(_("No group name specified."));
460 PurpleGroup
*group
= purple_group_new(name
);
461 purple_blist_add_group(group
, nullptr);
466 BuddyList::BuddyList() : Window(0, 0, 80, 24)
468 setColorScheme(CenterIM::SCHEME_BUDDYLIST
);
470 auto lbox
= new CppConsUI::ListBox(AUTOSIZE
, AUTOSIZE
);
471 addWidget(*lbox
, 1, 1);
473 auto hbox
= new CppConsUI::HorizontalListBox(AUTOSIZE
, AUTOSIZE
);
474 lbox
->appendWidget(*hbox
);
475 hbox
->appendWidget(*(new CppConsUI::Spacer(1, AUTOSIZE
)));
476 treeview_
= new CppConsUI::TreeView(AUTOSIZE
, AUTOSIZE
);
477 hbox
->appendWidget(*treeview_
);
478 hbox
->appendWidget(*(new CppConsUI::Spacer(1, AUTOSIZE
)));
480 filter_
= new Filter(this);
482 lbox
->appendWidget(*filter_
);
484 // TODO Check if this has been moved to purple_blist_init(). Remove these
485 // lines if it was as this will probably move to purple_init(), the buddylist
486 // object should be available a lot more early and the uiops should be set a
487 // lot more early. (All in all a lot of work.)
488 buddylist_
= purple_blist_new();
489 buddylist_
->ui_data
= this;
490 purple_set_blist(buddylist_
);
493 purple_pounces_load();
496 purple_prefs_add_none(CONF_PREFIX
"/blist");
497 purple_prefs_add_bool(CONF_PREFIX
"/blist/show_empty_groups", false);
498 purple_prefs_add_bool(CONF_PREFIX
"/blist/show_offline_buddies", true);
499 purple_prefs_add_string(CONF_PREFIX
"/blist/list_mode", "normal");
500 purple_prefs_add_string(CONF_PREFIX
"/blist/group_sort_mode", "name");
501 purple_prefs_add_string(CONF_PREFIX
"/blist/buddy_sort_mode", "status");
502 purple_prefs_add_string(CONF_PREFIX
"/blist/colorization_mode", "none");
504 updateCachedPreference(CONF_PREFIX
"/blist/show_empty_groups");
505 updateCachedPreference(CONF_PREFIX
"/blist/show_offline_buddies");
506 updateCachedPreference(CONF_PREFIX
"/blist/list_mode");
507 updateCachedPreference(CONF_PREFIX
"/blist/group_sort_mode");
508 updateCachedPreference(CONF_PREFIX
"/blist/buddy_sort_mode");
509 updateCachedPreference(CONF_PREFIX
"/blist/colorization_mode");
511 // Connect callbacks.
512 purple_prefs_connect_callback(
513 this, CONF_PREFIX
"/blist", blist_pref_change_
, this);
515 // Setup the callbacks for the buddylist.
516 std::memset(¢erim_blist_ui_ops_
, 0, sizeof(centerim_blist_ui_ops_
));
517 centerim_blist_ui_ops_
.new_list
= new_list_
;
518 centerim_blist_ui_ops_
.new_node
= new_node_
;
519 // centerim_blist_ui_ops_.show = show_;
520 centerim_blist_ui_ops_
.update
= update_
;
521 centerim_blist_ui_ops_
.remove
= remove_
;
522 centerim_blist_ui_ops_
.destroy
= destroy_
;
523 // centerim_blist_ui_ops_.set_visible = set_visible_;
524 centerim_blist_ui_ops_
.request_add_buddy
= request_add_buddy_
;
525 centerim_blist_ui_ops_
.request_add_chat
= request_add_chat_
;
526 centerim_blist_ui_ops_
.request_add_group
= request_add_group_
;
527 // Since libpurple 2.6.0.
528 // centerim_blist_ui_ops_.save_node = save_node_;
529 // centerim_blist_ui_ops_.remove_node = remove_node_;
530 // centerim_blist_ui_ops_.save_account = save_account_;
531 purple_blist_set_ui_ops(¢erim_blist_ui_ops_
);
533 CENTERIM
->timeoutOnceConnect(sigc::mem_fun(this, &BuddyList::load
), 0);
539 BuddyList::~BuddyList()
541 purple_blist_set_ui_ops(nullptr);
542 purple_prefs_disconnect_by_handle(this);
545 void BuddyList::init()
547 g_assert(my_instance_
== nullptr);
549 my_instance_
= new BuddyList
;
550 my_instance_
->show();
553 void BuddyList::finalize()
555 g_assert(my_instance_
!= nullptr);
558 my_instance_
= nullptr;
561 void BuddyList::load()
563 // Load the buddy list from ~/.centerim5/blist.xml.
566 delayedGroupNodesInit();
569 void BuddyList::rebuildList()
573 for (PurpleBlistNode
*node
= purple_blist_get_root(); node
!= nullptr;
574 node
= purple_blist_node_next(node
, TRUE
))
578 void BuddyList::updateList(int flags
)
580 for (PurpleBlistNode
*node
= purple_blist_get_root(); node
!= nullptr;
581 node
= purple_blist_node_next(node
, TRUE
))
582 if (PURPLE_BLIST_NODE_IS_GROUP(node
) ? (flags
& UPDATE_GROUPS
)
583 : (flags
& UPDATE_OTHERS
)) {
584 BuddyListNode
*bnode
=
585 reinterpret_cast<BuddyListNode
*>(purple_blist_node_get_ui_data(node
));
586 if (bnode
!= nullptr)
591 void BuddyList::delayedGroupNodesInit()
593 // Delayed group nodes init.
594 for (PurpleBlistNode
*node
= purple_blist_get_root(); node
!= nullptr;
595 node
= purple_blist_node_get_sibling_next(node
))
596 if (PURPLE_BLIST_NODE_IS_GROUP(node
)) {
597 BuddyListGroup
*gnode
=
598 reinterpret_cast<BuddyListGroup
*>(purple_blist_node_get_ui_data(node
));
599 if (gnode
!= nullptr)
600 gnode
->initCollapsedState();
604 void BuddyList::updateCachedPreference(const char *name
)
606 if (std::strcmp(name
, CONF_PREFIX
"/blist/show_empty_groups") == 0)
607 show_empty_groups_
= purple_prefs_get_bool(name
);
608 else if (std::strcmp(name
, CONF_PREFIX
"/blist/show_offline_buddies") == 0)
609 show_offline_buddies_
= purple_prefs_get_bool(name
);
610 else if (std::strcmp(name
, CONF_PREFIX
"/blist/list_mode") == 0) {
611 const char *value
= purple_prefs_get_string(name
);
612 if (std::strcmp(value
, "flat") == 0)
613 list_mode_
= LIST_FLAT
;
615 list_mode_
= LIST_NORMAL
;
617 else if (std::strcmp(name
, CONF_PREFIX
"/blist/group_sort_mode") == 0) {
618 const char *value
= purple_prefs_get_string(name
);
619 if (std::strcmp(value
, "user") == 0)
620 group_sort_mode_
= GROUP_SORT_BY_USER
;
622 group_sort_mode_
= GROUP_SORT_BY_NAME
;
624 else if (std::strcmp(name
, CONF_PREFIX
"/blist/buddy_sort_mode") == 0) {
625 const char *value
= purple_prefs_get_string(name
);
626 if (std::strcmp(value
, "status") == 0)
627 buddy_sort_mode_
= BUDDY_SORT_BY_STATUS
;
628 else if (std::strcmp(value
, "activity") == 0)
629 buddy_sort_mode_
= BUDDY_SORT_BY_ACTIVITY
;
631 buddy_sort_mode_
= BUDDY_SORT_BY_NAME
;
633 else if (std::strcmp(name
, CONF_PREFIX
"/blist/colorization_mode") == 0) {
634 const char *value
= purple_prefs_get_string(name
);
635 if (std::strcmp(value
, "status") == 0)
636 colorization_mode_
= COLOR_BY_STATUS
;
637 else if (std::strcmp(value
, "account") != 0)
638 colorization_mode_
= COLOR_BY_ACCOUNT
;
640 colorization_mode_
= COLOR_NONE
;
644 bool BuddyList::isAnyAccountConnected()
646 for (GList
*list
= purple_accounts_get_all(); list
!= nullptr;
648 PurpleAccount
*account
= reinterpret_cast<PurpleAccount
*>(list
->data
);
649 if (purple_account_is_connected(account
))
653 LOG
->message(_("There are no connected accounts."));
657 void BuddyList::filterHide()
659 filter_
->setVisibility(false);
660 filter_buffer_
[0] = '\0';
661 filter_buffer_length_
= 0;
662 filter_buffer_onscreen_width_
= 0;
665 void BuddyList::actionOpenFilter()
667 if (filter_
->isVisible())
670 filter_
->setVisibility(true);
673 g_assert(filter_buffer_
[0] == '\0');
674 g_assert(filter_buffer_length_
== 0);
675 g_assert(filter_buffer_onscreen_width_
== 0);
678 void BuddyList::actionDeleteChar()
680 if (!filter_
->isVisible())
683 const char *end
= filter_buffer_
+ filter_buffer_length_
;
684 g_assert(*end
== '\0');
685 char *prev
= g_utf8_find_prev_char(filter_buffer_
, end
);
686 if (prev
!= nullptr) {
687 filter_buffer_length_
-= end
- prev
;
688 filter_buffer_onscreen_width_
-= CppConsUI::Curses::onScreenWidth(prev
);
694 updateList(UPDATE_OTHERS
);
698 void BuddyList::declareBindables()
700 declareBindable("buddylist", "filter",
701 sigc::mem_fun(this, &BuddyList::actionOpenFilter
),
702 InputProcessor::BINDABLE_NORMAL
);
703 declareBindable("textentry", "backspace",
704 sigc::mem_fun(this, &BuddyList::actionDeleteChar
),
705 InputProcessor::BINDABLE_NORMAL
);
708 void BuddyList::new_list(PurpleBuddyList
*list
)
710 if (buddylist_
!= list
)
711 LOG
->error(_("Different buddylist detected!"));
714 void BuddyList::new_node(PurpleBlistNode
*node
)
716 g_return_if_fail(!purple_blist_node_get_ui_data(node
));
718 if (PURPLE_BLIST_NODE_IS_GROUP(node
) && list_mode_
== BuddyList::LIST_FLAT
) {
719 // Flat mode = no groups.
723 BuddyListNode
*bnode
= BuddyListNode::createNode(node
);
724 if (bnode
== nullptr)
727 BuddyListNode
*parent
= bnode
->getParentNode();
728 CppConsUI::TreeView::NodeReference nref
= treeview_
->appendNode(
729 parent
? parent
->getRefNode() : treeview_
->getRootNode(), *bnode
);
730 bnode
->setRefNode(nref
);
734 void BuddyList::update(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
736 // Not cool, but necessary because libpurple does not always behave nice. Note
737 // that calling new_node() can modify node's ui_data.
738 if (purple_blist_node_get_ui_data(node
) == nullptr)
741 BuddyListNode
*bnode
=
742 reinterpret_cast<BuddyListNode
*>(purple_blist_node_get_ui_data(node
));
743 if (bnode
== nullptr)
746 // Update the node data.
749 if (node
->parent
!= nullptr)
750 update(list
, node
->parent
);
753 void BuddyList::remove(PurpleBuddyList
*list
, PurpleBlistNode
*node
)
755 BuddyListNode
*bnode
=
756 reinterpret_cast<BuddyListNode
*>(purple_blist_node_get_ui_data(node
));
757 if (bnode
== nullptr)
760 treeview_
->deleteNode(bnode
->getRefNode(), false);
762 if (node
->parent
!= nullptr)
763 update(list
, node
->parent
);
766 void BuddyList::destroy(PurpleBuddyList
* /*list*/)
770 void BuddyList::request_add_buddy(PurpleAccount
*account
, const char *username
,
771 const char *group
, const char *alias
)
773 if (!isAnyAccountConnected())
776 auto window
= new AddBuddyWindow(account
, username
, group
, alias
);
780 void BuddyList::request_add_chat(PurpleAccount
*account
, PurpleGroup
*group
,
781 const char *alias
, const char *name
)
783 if (!isAnyAccountConnected())
786 // Find an account with chat capabilities.
787 bool chat_account_found
= false;
788 for (GList
*l
= purple_connections_get_all(); l
!= nullptr; l
= l
->next
) {
789 PurpleConnection
*gc
= reinterpret_cast<PurpleConnection
*>(l
->data
);
791 if (PURPLE_PLUGIN_PROTOCOL_INFO(gc
->prpl
)->join_chat
) {
792 chat_account_found
= true;
797 if (!chat_account_found
) {
798 LOG
->message(_("You are not currently signed on with any "
799 "protocols that have the ability to chat."));
803 const char *group_name
= nullptr;
804 if (group
!= nullptr)
805 group_name
= purple_group_get_name(group
);
806 auto window
= new AddChatWindow(account
, name
, alias
, group_name
);
810 void BuddyList::request_add_group()
812 if (!isAnyAccountConnected())
815 auto window
= new AddGroupWindow
;
819 void BuddyList::blist_pref_change(
820 const char *name
, PurplePrefType
/*type*/, gconstpointer
/*val*/)
822 // Some blist/* preference changed.
823 updateCachedPreference(name
);
825 if (std::strcmp(name
, CONF_PREFIX
"/blist/list_mode") == 0) {
830 bool groups_only
= false;
831 if (std::strcmp(name
, CONF_PREFIX
"/blist/show_empty_groups") == 0 ||
832 std::strcmp(name
, CONF_PREFIX
"/blist/group_sort_mode") == 0)
835 updateList(UPDATE_GROUPS
| (!groups_only
? UPDATE_OTHERS
: 0));
838 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: