1 // Copyright (C) 2012-2015 Petr Pavlu <setup@dagobah.cz>
3 // This file is part of CenterIM.
5 // CenterIM 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.
10 // CenterIM 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.
15 // You should have received a copy of the GNU General Public License
16 // along with CenterIM. If not, see <http://www.gnu.org/licenses/>.
18 #include "PluginWindow.h"
24 #include <cppconsui/Label.h>
27 // - Improve handling of PURPLE_PREF_PATH.
28 // - Use these functions and use their results correctly:
29 // purple_plugin_pref_get_max_length(),
30 // purple_plugin_pref_get_masked(),
31 // purple_plugin_pref_get_format_type(),
32 // purple_plugin_pref_get_bounds().
34 PluginWindow::PluginWindow() : SplitDialog(0, 0, 80, 24, _("Plugins"))
36 setColorScheme(CenterIM::SCHEME_GENERALWINDOW
);
38 treeview_
= new CppConsUI::TreeView(AUTOSIZE
, AUTOSIZE
);
39 setContainer(*treeview_
);
41 // Show settings for all loaded plugins.
42 for (GList
*iter
= purple_plugins_get_loaded(); iter
!= nullptr;
44 PurplePlugin
*plugin
= reinterpret_cast<PurplePlugin
*>(iter
->data
);
45 if (plugin
->info
->type
== PURPLE_PLUGIN_STANDARD
&&
46 !(plugin
->info
->flags
& PURPLE_PLUGIN_FLAG_INVISIBLE
))
47 populatePlugin(plugin
);
50 buttons_
->appendItem(_("Add"), sigc::mem_fun(this, &PluginWindow::addPlugin
));
51 buttons_
->appendSeparator();
53 _("Done"), sigc::hide(sigc::mem_fun(this, &PluginWindow::close
)));
58 PluginWindow::~PluginWindow()
60 // Destroy all allocated pref frames.
61 for (PluginEntries::value_type
&entry
: plugin_entries_
)
62 if (entry
.second
.frame
!= nullptr)
63 purple_plugin_pref_frame_destroy(entry
.second
.frame
);
66 void PluginWindow::onScreenResized()
68 moveResizeRect(CENTERIM
->getScreenArea(CenterIM::CHAT_AREA
));
71 PluginWindow::AddPluginWindow::AddPluginWindow()
72 : Window(0, 0, 80, 24, _("Add plugin"), TYPE_TOP
)
74 auto treeview
= new CppConsUI::TreeView(AUTOSIZE
, AUTOSIZE
);
75 addWidget(*treeview
, 1, 1);
77 // Populate available plugins except the ones that are already enabled.
78 for (GList
*iter
= purple_plugins_get_all(); iter
!= nullptr;
80 PurplePlugin
*plugin
= reinterpret_cast<PurplePlugin
*>(iter
->data
);
81 if (purple_plugin_is_loaded(plugin
) ||
82 plugin
->info
->type
!= PURPLE_PLUGIN_STANDARD
||
83 (plugin
->info
->flags
& PURPLE_PLUGIN_FLAG_INVISIBLE
))
86 char *text
= g_strdup_printf("%s, %s\n%s", purple_plugin_get_name(plugin
),
87 purple_plugin_get_version(plugin
), purple_plugin_get_summary(plugin
));
88 auto button
= new CppConsUI::Button(text
);
91 button
->signal_activate
.connect(sigc::bind(
92 sigc::mem_fun(this, &AddPluginWindow::onPluginButtonActivate
), plugin
));
93 treeview
->appendNode(treeview
->getRootNode(), *button
);
99 void PluginWindow::AddPluginWindow::onScreenResized()
101 moveResizeRect(CENTERIM
->getScreenAreaCentered(CenterIM::CHAT_AREA
));
104 void PluginWindow::AddPluginWindow::onPluginButtonActivate(
105 CppConsUI::Button
& /*activator*/, PurplePlugin
*plugin
)
107 signal_selection(*this, plugin
);
111 PluginWindow::BoolOption::BoolOption(const char *name
, const char *pref
)
114 g_assert(name
!= nullptr);
115 g_assert(pref
!= nullptr);
117 pref_
= g_strdup(pref
);
118 setChecked(purple_prefs_get_bool(pref_
));
119 signal_toggle
.connect(sigc::mem_fun(this, &BoolOption::onToggle
));
122 PluginWindow::BoolOption::~BoolOption()
127 void PluginWindow::BoolOption::onToggle(
128 CheckBox
& /*activator*/, bool new_state
)
130 purple_prefs_set_bool(pref_
, new_state
);
133 PluginWindow::StringOption::StringOption(const char *name
, const char *pref
)
134 : Button(FLAG_VALUE
, name
)
136 g_assert(name
!= nullptr);
137 g_assert(pref
!= nullptr);
139 pref_
= g_strdup(pref
);
141 signal_activate
.connect(sigc::mem_fun(this, &StringOption::onActivate
));
144 PluginWindow::StringOption::~StringOption()
149 void PluginWindow::StringOption::updateValue()
151 setValue(purple_prefs_get_string(pref_
));
154 void PluginWindow::StringOption::onActivate(Button
& /*activator*/)
156 auto dialog
= new CppConsUI::InputDialog(getText(), getValue());
157 dialog
->signal_response
.connect(
158 sigc::mem_fun(this, &StringOption::responseHandler
));
162 void PluginWindow::StringOption::responseHandler(
163 CppConsUI::InputDialog
&activator
, AbstractDialog::ResponseType response
)
165 if (response
!= AbstractDialog::RESPONSE_OK
)
168 purple_prefs_set_string(pref_
, activator
.getText());
172 PluginWindow::IntegerOption::IntegerOption(const char *name
, const char *pref
)
173 : Button(FLAG_VALUE
, name
)
175 g_assert(name
!= nullptr);
176 g_assert(pref
!= nullptr);
178 pref_
= g_strdup(pref
);
180 signal_activate
.connect(sigc::mem_fun(this, &IntegerOption::onActivate
));
183 PluginWindow::IntegerOption::~IntegerOption()
188 void PluginWindow::IntegerOption::updateValue()
190 setValue(purple_prefs_get_int(pref_
));
193 void PluginWindow::IntegerOption::onActivate(Button
& /*activator*/)
195 auto dialog
= new CppConsUI::InputDialog(getText(), getValue());
196 dialog
->setFlags(CppConsUI::TextEntry::FLAG_NUMERIC
);
197 dialog
->signal_response
.connect(
198 sigc::mem_fun(this, &IntegerOption::responseHandler
));
202 void PluginWindow::IntegerOption::responseHandler(
203 CppConsUI::InputDialog
&activator
,
204 CppConsUI::AbstractDialog::ResponseType response
)
206 if (response
!= AbstractDialog::RESPONSE_OK
)
210 if (!Utils::stringToNumber(activator
.getText(), INT_MIN
, INT_MAX
, &num
))
212 purple_prefs_set_int(pref_
, num
);
217 PluginWindow::PathOption::PathOption(const char *name
, const char *pref
)
218 : Button(FLAG_VALUE
, name
)
220 g_assert(name
!= nullptr);
221 g_assert(pref
!= nullptr);
223 pref_
= g_strdup(pref
);
225 signal_activate
.connect(sigc::mem_fun(this, &PathOption::onActivate
));
228 PluginWindow::PathOption::~PathOption()
233 void PluginWindow::PathOption::updateValue()
235 setValue(purple_prefs_get_path(pref_
));
238 void PluginWindow::PathOption::onActivate(Button
& /*activator*/)
240 auto dialog
= new CppConsUI::InputDialog(getText(), getValue());
241 dialog
->signal_response
.connect(
242 sigc::mem_fun(this, &PathOption::responseHandler
));
246 void PluginWindow::PathOption::responseHandler(
247 CppConsUI::InputDialog
&activator
, AbstractDialog::ResponseType response
)
249 if (response
!= AbstractDialog::RESPONSE_OK
)
252 purple_prefs_set_path(pref_
, activator
.getText());
256 void PluginWindow::clearPlugin(PurplePlugin
*plugin
)
258 PluginEntry
*plugin_entry
= &plugin_entries_
[plugin
];
259 treeview_
->deleteNode(plugin_entry
->parent_reference
, false);
260 if (plugin_entry
->frame
!= nullptr)
261 purple_plugin_pref_frame_destroy(plugin_entry
->frame
);
262 plugin_entries_
.erase(plugin
);
265 void PluginWindow::populatePlugin(PurplePlugin
*plugin
)
267 // Show settings for a single plugin.
268 CppConsUI::Button
*button
= new CppConsUI::TreeView::ToggleCollapseButton(
269 purple_plugin_get_name(plugin
));
270 CppConsUI::TreeView::NodeReference parent_reference
=
271 treeview_
->appendNode(treeview_
->getRootNode(), *button
);
272 treeview_
->setCollapsed(parent_reference
, true);
274 // Record this plugin in plugin_entries_.
276 entry
.parent
= button
;
277 entry
.parent_reference
= parent_reference
;
278 entry
.frame
= nullptr;
279 plugin_entries_
[plugin
] = entry
;
281 if (PURPLE_PLUGIN_HAS_PREF_FRAME(plugin
)) {
282 PurplePluginUiInfo
*ui_info
= PURPLE_PLUGIN_UI_INFO(plugin
);
283 PurplePluginPrefFrame
*frame
= ui_info
->get_plugin_pref_frame(plugin
);
285 // Note this pref frame.
286 plugin_entries_
[plugin
].frame
= frame
;
288 // Group node (set to an "invalid" value).
289 CppConsUI::TreeView::NodeReference group
= treeview_
->getRootNode();
291 for (GList
*iter
= purple_plugin_pref_frame_get_prefs(frame
);
292 iter
!= nullptr; iter
= iter
->next
) {
293 PurplePluginPref
*pref
= reinterpret_cast<PurplePluginPref
*>(iter
->data
);
294 PurplePluginPrefType type
= purple_plugin_pref_get_type(pref
);
295 const char *label
= purple_plugin_pref_get_label(pref
);
296 const char *name
= purple_plugin_pref_get_name(pref
);
297 if (name
== nullptr) {
298 if (label
== nullptr)
301 if (type
== PURPLE_PLUGIN_PREF_INFO
) {
303 treeview_
->appendNode(
304 parent_reference
, *(new CppConsUI::Label(label
)));
308 group
= treeview_
->appendNode(parent_reference
,
309 *(new CppConsUI::TreeView::ToggleCollapseButton(label
)));
314 PurplePrefType value_type
= purple_prefs_get_type(name
);
315 CppConsUI::Widget
*pref_widget
;
316 if (type
== PURPLE_PLUGIN_PREF_CHOICE
) {
317 auto combo
= new CppConsUI::ComboBox(label
);
320 // Add possible options.
321 for (GList
*pref_iter
= purple_plugin_pref_get_choices(pref
);
322 pref_iter
!= nullptr && pref_iter
->next
!= nullptr;
323 pref_iter
= pref_iter
->next
->next
)
324 combo
->addOptionPtr(reinterpret_cast<const char *>(pref_iter
->data
),
325 pref_iter
->next
->data
);
327 // Set default value.
328 intptr_t current_value
;
329 if (value_type
== PURPLE_PREF_BOOLEAN
)
330 current_value
= purple_prefs_get_bool(name
);
331 else if (value_type
== PURPLE_PREF_INT
)
332 current_value
= purple_prefs_get_int(name
);
333 else if (value_type
== PURPLE_PREF_STRING
)
335 reinterpret_cast<intptr_t>(purple_prefs_get_string(name
));
336 else if (value_type
== PURPLE_PREF_PATH
)
338 reinterpret_cast<intptr_t>(purple_prefs_get_path(name
));
340 LOG
->error(_("Unhandled plugin preference type '%d'."), value_type
);
343 combo
->setSelectedByData(current_value
);
346 if (value_type
== PURPLE_PREF_BOOLEAN
)
347 pref_widget
= new BoolOption(label
, name
);
348 else if (value_type
== PURPLE_PREF_INT
)
349 pref_widget
= new IntegerOption(label
, name
);
350 else if (value_type
== PURPLE_PREF_STRING
)
351 pref_widget
= new StringOption(label
, name
);
352 else if (value_type
== PURPLE_PREF_PATH
)
353 pref_widget
= new PathOption(label
, name
);
355 LOG
->error(_("Unhandled plugin preference type '%d'."), value_type
);
360 if (group
== treeview_
->getRootNode()) {
361 // No group has been created, so create one.
362 group
= treeview_
->appendNode(parent_reference
,
363 *(new CppConsUI::TreeView::ToggleCollapseButton(_("Preferences"))));
365 treeview_
->appendNode(group
, *pref_widget
);
370 auto disable_button
= new CppConsUI::Button(_("Disable plugin"));
371 disable_button
->signal_activate
.connect(
372 sigc::bind(sigc::mem_fun(this, &PluginWindow::disablePlugin
), plugin
));
373 treeview_
->appendNode(parent_reference
, *disable_button
);
376 void PluginWindow::addPlugin(CppConsUI::Button
& /*activator*/)
378 // Show a window for the user to select a plugin that he wants to add.
379 auto win
= new AddPluginWindow
;
380 win
->signal_selection
.connect(
381 sigc::mem_fun(this, &PluginWindow::onAddPluginSelection
));
385 void PluginWindow::onAddPluginSelection(
386 AddPluginWindow
& /*activator*/, PurplePlugin
*plugin
)
388 // The user has selected a plugin that he wants to add, so do it.
389 if (!purple_plugin_load(plugin
)) {
390 LOG
->error(_("Error loading plugin '%s'."), purple_plugin_get_name(plugin
));
394 purple_plugins_save_loaded(CONF_PLUGINS_SAVE_PREF
);
396 // Populate this plugin.
397 populatePlugin(plugin
);
399 // Make the button for this plugin focused.
400 PluginEntries::iterator i
= plugin_entries_
.find(plugin
);
401 g_assert(i
!= plugin_entries_
.end());
402 g_assert(i
->second
.parent
!= nullptr);
403 i
->second
.parent
->grabFocus();
406 void PluginWindow::disablePlugin(
407 CppConsUI::Button
& /*activator*/, PurplePlugin
*plugin
)
409 auto dialog
= new CppConsUI::MessageDialog(
410 _("Disable plugin"), _("Are you sure you want to disable this plugin?"));
411 dialog
->signal_response
.connect(sigc::bind(
412 sigc::mem_fun(this, &PluginWindow::disablePluginResponseHandler
), plugin
));
416 void PluginWindow::disablePluginResponseHandler(
417 CppConsUI::MessageDialog
& /*activator*/,
418 CppConsUI::AbstractDialog::ResponseType response
, PurplePlugin
*plugin
)
420 if (response
!= AbstractDialog::RESPONSE_OK
)
423 if (!purple_plugin_unload(plugin
)) {
424 LOG
->error(_("Error unloading plugin '%s'. The plugin could not be "
425 "unloaded now, but will be disabled at the next startup."),
426 purple_plugin_get_name(plugin
));
428 purple_plugin_disable(plugin
);
432 purple_plugins_save_loaded(CONF_PLUGINS_SAVE_PREF
);
435 // vim: set tabstop=2 shiftwidth=2 textwidth=80 expandtab: