/*
* vim: softtabstop=4 shiftwidth=4 cindent foldmethod=marker expandtab
*
* $LastChangedDate$
* $Revision$
* $LastChangedBy$
* $URL$
*
* Copyright 2009-2011 Eric Connell
*
* This file is part of Mangler.
*
* Mangler is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Mangler is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Mangler. If not, see .
*/
#include
#include
#include
#include
#include
#include "mangler.h"
#include "manglerui.h"
#include "mangler-icons.h"
#include "channeltree.h"
#include "manglernetwork.h"
#include "mangleraudio.h"
#include "manglersettings.h"
#include "manglerserverlist.h"
#include "manglerchat.h"
#include "manglerprivchat.h"
#include "manglercharset.h"
#include "manglerintegration.h"
#include "mangleradmin.h"
#include "manglerrecorder.h"
#ifdef HAVE_XOSD
# include "manglerosd.h"
#endif
#ifdef HAVE_G15
# include "manglerg15.h"
#endif
#include "locale.h"
using namespace std;
Mangler *mangler;
ManglerConfig Mangler::config;
Mangler::Mangler(struct _cli_options *options) {/*{{{*/
this->options = options;
// load all of our icons
icons.insert(std::make_pair("black_circle", Gdk::Pixbuf::create_from_inline(-1, black_circle )));
icons.insert(std::make_pair("blue_circle", Gdk::Pixbuf::create_from_inline(-1, blue_circle )));
icons.insert(std::make_pair("cyan_circle", Gdk::Pixbuf::create_from_inline(-1, cyan_circle )));
icons.insert(std::make_pair("green_circle", Gdk::Pixbuf::create_from_inline(-1, green_circle )));
icons.insert(std::make_pair("purple_circle", Gdk::Pixbuf::create_from_inline(-1, purple_circle )));
icons.insert(std::make_pair("red_circle", Gdk::Pixbuf::create_from_inline(-1, red_circle )));
icons.insert(std::make_pair("yellow_circle", Gdk::Pixbuf::create_from_inline(-1, yellow_circle )));
icons.insert(std::make_pair("mangler_logo", Gdk::Pixbuf::create_from_inline(-1, mangler_logo )));
icons.insert(std::make_pair("tray_icon", Gdk::Pixbuf::create_from_inline(-1, tray_icon_purple )));
icons.insert(std::make_pair("tray_icon_blue", Gdk::Pixbuf::create_from_inline(-1, tray_icon_blue )));
icons.insert(std::make_pair("tray_icon_red", Gdk::Pixbuf::create_from_inline(-1, tray_icon_red )));
icons.insert(std::make_pair("tray_icon_green", Gdk::Pixbuf::create_from_inline(-1, tray_icon_green )));
icons.insert(std::make_pair("tray_icon_yellow", Gdk::Pixbuf::create_from_inline(-1, tray_icon_yellow )));
icons.insert(std::make_pair("tray_icon_grey", Gdk::Pixbuf::create_from_inline(-1, tray_icon_grey )));
icons.insert(std::make_pair("tray_icon_purple", Gdk::Pixbuf::create_from_inline(-1, tray_icon_purple )));
icons.insert(std::make_pair("user_icon_red", Gdk::Pixbuf::create_from_inline(-1, user_icon_red )));
icons.insert(std::make_pair("user_icon_yellow", Gdk::Pixbuf::create_from_inline(-1, user_icon_yellow )));
icons.insert(std::make_pair("user_icon_green", Gdk::Pixbuf::create_from_inline(-1, user_icon_green )));
icons.insert(std::make_pair("user_icon_orange", Gdk::Pixbuf::create_from_inline(-1, user_icon_orange )));
try {
if (options->uifromfile) {
builder = Gtk::Builder::create_from_file(options->uifilename);
} else {
builder = Gtk::Builder::create_from_string(ManglerUI);
}
builder->get_widget("manglerWindow", manglerWindow);
manglerWindow->set_icon(icons["tray_icon"]);
} catch (const Glib::Error& e) {
std::cerr << e.what() << std::endl;
exit(EXIT_FAILURE);
}
manglerWindow->signal_show().connect(sigc::mem_fun(this, &Mangler::mangler_show_cb));
//manglerWindow->signal_hide().connect(sigc::mem_fun(this, &Mangler::manglerWindow_hide_cb));
Gtk::Main::signal_quit().connect(sigc::mem_fun(this, &Mangler::mangler_quit_cb));
/*
* Retreive all buttons from builder and set their singal handler callbacks
*/
// Quick Connect Button
builder->get_widget("quickConnectButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::quickConnectButton_clicked_cb));
// Server List Button
builder->get_widget("serverListButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::serverListButton_clicked_cb));
// Connect Button
builder->get_widget("connectButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::connectButton_clicked_cb));
// Comment Button
builder->get_widget("commentButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::commentButton_clicked_cb));
// Chat Button
builder->get_widget("chatButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::chatButton_clicked_cb));
// Bindings Button
builder->get_widget("bindingsButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::bindingsButton_clicked_cb));
// Admin Button
builder->get_widget("adminButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::adminButton_clicked_cb));
isAdmin = false;
isChanAdmin = false;
// Settings Button
builder->get_widget("settingsButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::settingsButton_clicked_cb));
// About Button
builder->get_widget("aboutButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::aboutButton_clicked_cb));
// Settings Button
builder->get_widget("xmitButton", togglebutton);
togglebutton->signal_toggled().connect(sigc::mem_fun(this, &Mangler::xmitButton_toggled_cb));
// Quick Connect Dialog Buttons
builder->get_widget("qcConnectButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::qcConnectButton_clicked_cb));
builder->get_widget("qcCancelButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::qcCancelButton_clicked_cb));
// Input VU Meter
builder->get_widget("inputVUMeterProgressBar", inputvumeter);
// Quick Mute Options
muteMic = false;
muteSound = false;
builder->get_widget("muteMicCheckButton", checkbutton);
checkbutton->signal_toggled().connect(sigc::mem_fun(this, &Mangler::muteMicCheckButton_toggled_cb));
builder->get_widget("muteSoundCheckButton", checkbutton);
checkbutton->signal_toggled().connect(sigc::mem_fun(this, &Mangler::muteSoundCheckButton_toggled_cb));
// Autoreconnect feature implementation
wantDisconnect = false;
// Feature implementation
motdAlways = false;
/*
* Retreive all menu bar items from builder and set their singal handler
* callbacks. Most of these can use the same callback as their
* corresponding button
*/
builder->get_widget("buttonMenuItem", checkmenuitem);
checkmenuitem->signal_toggled().connect(sigc::mem_fun(this, &Mangler::buttonMenuItem_toggled_cb));
builder->get_widget("hideServerInfoMenuItem", checkmenuitem);
checkmenuitem->signal_toggled().connect(sigc::mem_fun(this, &Mangler::hideServerInfoMenuItem_toggled_cb));
builder->get_widget("hideGuestFlagMenuItem", checkmenuitem);
checkmenuitem->signal_toggled().connect(sigc::mem_fun(this, &Mangler::hideGuestFlagMenuItem_toggled_cb));
builder->get_widget("quickConnectMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::quickConnectButton_clicked_cb));
builder->get_widget("serverListMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::serverListButton_clicked_cb));
builder->get_widget("adminLoginMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::adminLoginMenuItem_activate_cb));
builder->get_widget("adminWindowMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::adminWindowMenuItem_activate_cb));
builder->get_widget("settingsMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::settingsButton_clicked_cb));
builder->get_widget("statusIconSettingsMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::settingsButton_clicked_cb));
builder->get_widget("commentMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::commentButton_clicked_cb));
builder->get_widget("motdMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::motdMenuItem_activate_cb));
builder->get_widget("chatMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::chatButton_clicked_cb));
builder->get_widget("recorderMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::recorderMenuItem_activate_cb));
builder->get_widget("quitMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::quitMenuItem_activate_cb));
builder->get_widget("statusIconQuitMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::quitMenuItem_activate_cb));
builder->get_widget("aboutMenuItem", menuitem);
menuitem->signal_activate().connect(sigc::mem_fun(this, &Mangler::aboutButton_clicked_cb));
// connect the signal for our error dialog button
builder->get_widget("errorOKButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::errorOKButton_clicked_cb));
// Set up our message of the day window
builder->get_widget("motdWindow", motdWindow);
builder->get_widget("motdNotebook", motdNotebook);
motdNotebook->set_current_page(1);
motdNotebook->set_show_tabs(false);
builder->get_widget("motdUsers", motdUsers);
builder->get_widget("motdGuests", motdGuests);
builder->get_widget("motdIgnore", motdIgnore);
motdIgnore->signal_toggled().connect(sigc::mem_fun(this, &Mangler::motdIgnore_toggled_cb));
builder->get_widget("motdOkButton", motdOkButton);
motdOkButton->signal_clicked().connect(sigc::mem_fun(this, &Mangler::motdOkButton_clicked_cb));
// Set up our generic password dialog box
builder->get_widget("passwordDialog", passwordDialog);
builder->get_widget("passwordEntry", passwordEntry);
builder->get_widget("passwordOkButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::passwordDialogOkButton_clicked_cb));
builder->get_widget("passwordCancelButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::passwordDialogCancelButton_clicked_cb));
// Set up our kick/ban reason entry dialog box
builder->get_widget("reasonDialog", reasonDialog);
builder->get_widget("reasonEntry", reasonEntry);
builder->get_widget("reasonOkButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::reasonDialogOkButton_clicked_cb));
builder->get_widget("reasonCancelButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::reasonDialogCancelButton_clicked_cb));
// Set up the text string change dialog box
builder->get_widget("textStringChangeDialog", textStringChangeDialog);
builder->get_widget("textStringChangeCommentEntry", textStringChangeCommentEntry);
builder->get_widget("textStringChangeURLEntry", textStringChangeURLEntry);
builder->get_widget("textStringChangeIntegrationEntry", textStringChangeIntegrationEntry);
builder->get_widget("textStringSilenceCommentCheckButton", textStringSilenceCommentCheckButton);
builder->get_widget("textStringOkButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::textStringChangeDialogOkButton_clicked_cb));
builder->get_widget("textStringCancelButton", button);
button->signal_clicked().connect(sigc::mem_fun(this, &Mangler::textStringChangeDialogCancelButton_clicked_cb));
// Create Channel Tree
channelTree = new ManglerChannelTree(builder);
// Create Network Communication Object
network = new ManglerNetwork(builder);
// Create settings object, load the configuration file, and apply. If the
// user has PTT key/mouse enabled, start a timer here
settings = new ManglerSettings(builder);
isTransmittingButton = 0;
isTransmittingVA = 0;
isTransmittingMouse = 0;
isTransmittingKey = 0;
isTransmitting = 0;
Glib::signal_timeout().connect(sigc::mem_fun(this, &Mangler::checkPushToTalkKeys), 100);
Glib::signal_timeout().connect(sigc::mem_fun(this, &Mangler::checkVoiceActivation), 100);
Glib::signal_timeout().connect(sigc::mem_fun(this, &Mangler::checkPushToTalkMouse), 100);
// Create our audio control object for managing devices
audioControl = new ManglerAudio(AUDIO_CONTROL);
audioControl->getDeviceList(config["AudioSubsystem"].toUString());
// If we have eSpeak, go ahead and initialize it
#ifdef HAVE_ESPEAK
if ((espeakRate = espeak_Initialize(AUDIO_OUTPUT_RETRIEVAL, 0, NULL, espeakEVENT_LIST_TERMINATED)) < 0) {
fprintf(stderr, "espeak: initialize error\n");
return;
}
#endif
// set saved window size from settings
unsigned windowWidth( config["WindowWidth"].toUInt() );
unsigned windowHeight( config["WindowHeight"].toUInt() );
if (windowWidth > 0 && windowHeight > 0) manglerWindow->set_default_size(windowWidth, windowHeight);
// set the master volume
v3_set_volume_master(config["MasterVolumeLevel"].toUInt());
v3_set_volume_xmit(config["InputGainLevel"].toUInt());
builder->get_widget("buttonMenuItem", checkmenuitem);
checkmenuitem->set_active(config["ButtonsHidden"].toBool());
builder->get_widget("hideServerInfoMenuItem", checkmenuitem);
checkmenuitem->set_active(config["ServerInfoHidden"].toBool());
builder->get_widget("hideGuestFlagMenuItem", checkmenuitem);
checkmenuitem->set_active(config["GuestFlagHidden"].toBool());
// Create Server List Window
serverList = new ManglerServerList(builder);
// Create Chat Window
chat = new ManglerChat(builder);
// Create Admin Window
admin = new ManglerAdmin(builder);
wantAdminWindow = false;
// Create Recording Window
recorder = new ManglerRecorder(builder);
// Add our servers to the main window drop down
builder->get_widget("serverSelectComboBox", combobox);
combobox->set_model(serverList->serverListTreeModel);
combobox->pack_start(serverList->serverListColumns.name);
Gtk::CellRendererText *renderer = (Gtk::CellRendererText*)(*(combobox->get_cells().begin()));
renderer->property_ellipsize() = Pango::ELLIPSIZE_END;
int serverSelection = 0, ctr = 0;
iniFile::iterator server = config.servers.begin();
while (server != config.servers.end()) {
Gtk::TreeRow row = *(serverList->serverListTreeModel->append());
// is this id useful at all?
row[serverList->serverListColumns.id] = ctr;
row[serverList->serverListColumns.name] = Glib::locale_to_utf8(server->first);
row[serverList->serverListColumns.hostname] = server->second["Hostname"].toUString();
row[serverList->serverListColumns.port] = server->second["Port"].toUString();
row[serverList->serverListColumns.username] = server->second["Username"].toUString();
if (config["LastConnectedServerName"] == server->first) {
serverSelection = ctr;
}
server++; ++ctr;
}
// Select the last one used (or the first if unknown)
combobox->set_active(serverSelection);
// Status Tray Icon
statusIcon = Gtk::StatusIcon::create(icons["tray_icon_grey"]);
statusIcon->signal_activate().connect(sigc::mem_fun(this, &Mangler::statusIcon_activate_cb));
statusIcon->signal_scroll_event().connect_notify(sigc::mem_fun(this, &Mangler::statusIcon_scroll_event_cb));
statusIcon->signal_button_press_event().connect_notify(sigc::mem_fun(this, &Mangler::statusIcon_buttonpress_event_cb));
builder->get_widget("statusIconMenu", statusIconMenu);
builder->get_widget("muteMicCheckMenuItem", checkmenuitem);
checkmenuitem->signal_toggled().connect(sigc::mem_fun(this, &Mangler::muteMicCheckMenuItem_toggled_cb));
checkmenuitem->set_active(config["MuteMic"].toBool());
builder->get_widget("muteSoundCheckMenuItem", checkmenuitem);
checkmenuitem->signal_toggled().connect(sigc::mem_fun(this, &Mangler::muteSoundCheckMenuItem_toggled_cb));
checkmenuitem->set_active(config["MuteSound"].toBool());
iconified = false;
setTooltip();
// Music (Now playing)
integration = new ManglerIntegration();
//integration->setClient((MusicClient)settings->config.AudioIntegrationPlayer);
// TODO: MusicClient? wants a const char *??
integration->setClient((MusicClient)config["AudioIntegrationPlayer"].toUInt());
integration->update(true);
#ifdef HAVE_XOSD
// Create XOSD Overlay
osd = new ManglerOsd();
#endif
#ifdef HAVE_G15
// Create G15 Keyboard LCD Handler
g15 = new ManglerG15();
#endif
Glib::signal_timeout().connect(sigc::mem_fun(this, &Mangler::updateIntegration), 1000);
Glib::signal_timeout().connect(sigc::mem_fun(*this, &Mangler::updateXferAmounts), 500);
Glib::signal_timeout().connect(sigc::mem_fun(*this, &Mangler::getNetworkEvent), 10);
}/*}}}*/
Mangler::~Mangler() {/*{{{*/
delete channelTree;
delete network;
delete settings;
delete audioControl;
delete serverList;
delete chat;
delete admin;
delete recorder;
delete integration;
#ifdef HAVE_XOSD
delete osd;
#endif
#ifdef HAVE_G15
delete g15;
#endif
}/*}}}*/
/*
* Main Window Callbacks
*/
void Mangler::mangler_show_cb(void) {/*{{{*/
if (options) {
// Command Line Quick Connect
if (!options->qc_server.empty()) {
Glib::ustring::size_type separator = options->qc_server.find_last_of(":");
if (separator > 0 && options->qc_server.length() != separator + 1 && options->qc_username.length()) {
onConnectHandler(
options->qc_server.substr(0, separator),
options->qc_server.substr(separator + 1),
options->qc_username,
options->qc_password);
}
}
options = NULL;
}
}/*}}}*/
bool Mangler::mangler_quit_cb(void) {/*{{{*/
int w, h;
manglerWindow->get_size(w, h);
config["WindowWidth"] = w;
config["WindowHeight"] = h;
config.save();
return true;
}/*}}}*/
/*
* Connection Handling
*/
void Mangler::onConnectHandler(
Glib::ustring hostname,
Glib::ustring port,
Glib::ustring username,
Glib::ustring password,
Glib::ustring phonetic,
Glib::ustring charset,
bool acceptPages,
bool acceptU2U,
bool acceptPrivateChat,
bool allowRecording) {/*{{{*/
set_charset(charset);
isAdmin = false;
isChanAdmin = false;
v3_set_server_opts(V3_USER_ACCEPT_PAGES, acceptPages);
v3_set_server_opts(V3_USER_ACCEPT_U2U, acceptU2U);
v3_set_server_opts(V3_USER_ACCEPT_CHAT, acceptPrivateChat);
v3_set_server_opts(V3_USER_ALLOW_RECORD, allowRecording);
channelTree->updateLobby("Connecting...");
Glib::Thread::create(sigc::bind(sigc::mem_fun(this->network, &ManglerNetwork::connect), hostname, port, username, password, phonetic), FALSE);
}/*}}}*/
void Mangler::onDisconnectHandler(void) {/*{{{*/
Gtk::Button *connectbutton;
builder->get_widget("connectButton", connectbutton);
if (connectbutton->get_label() == "gtk-disconnect") {
builder->get_widget("adminButton", button);
button->set_sensitive(false);
builder->get_widget("adminLoginMenuItem", menuitem);
menuitem->set_label("_Admin Login");
menuitem->set_sensitive(false);
builder->get_widget("adminWindowMenuItem", menuitem);
menuitem->set_sensitive(false);
builder->get_widget("chatButton", button);
button->set_sensitive(false);
builder->get_widget("motdMenuItem", menuitem);
menuitem->set_sensitive(false);
builder->get_widget("chatMenuItem", menuitem);
menuitem->set_sensitive(false);
builder->get_widget("commentButton", button);
button->set_sensitive(false);
builder->get_widget("commentMenuItem", menuitem);
menuitem->set_sensitive(false);
isTransmittingMouse = false;
isTransmittingKey = false;
isTransmittingVA = false;
isTransmittingButton = false;
stopTransmit();
connectbutton->set_sensitive(true);
#ifdef HAVE_XOSD
osd->destroyOsd();
#endif
outputAudio.clear();
channelTree->clear();
admin->hide();
admin->clear();
builder->get_widget("xmitButton", togglebutton);
togglebutton->set_active(false);
builder->get_widget("progressbar", progressbar);
progressbar->set_text("");
progressbar->set_fraction(0);
progressbar->hide();
builder->get_widget("statusbar", statusbar);
statusbar->pop();
statusbar->push("Disconnected");
//builder->get_widget("serverTabLabel", label);
//label->set_label("Not Connected");
builder->get_widget("pingLabel", label);
label->set_label("N/A");
builder->get_widget("userCountLabel", label);
label->set_label("N/A");
builder->get_widget("codecLabel", label);
label->set_label("N/A");
mangler->statusIcon->set(icons["tray_icon_grey"]);
isAdmin = false;
isChanAdmin = false;
wantAdminWindow = false;
motdWindow->hide();
motdNotebook->set_current_page(1);
motdNotebook->set_show_tabs(false);
motdUsers->get_buffer()->set_text("");
motdGuests->get_buffer()->set_text("");
chat->clear();
recorder->can_record(false);
if (! connectedServerName.empty()) {
iniSection &server(config.servers[connectedServerName]);
connectedServerName = "";
if (!wantDisconnect && server["PersistentConnection"].toBool()) {
connectbutton->set_label("gtk-cancel");
lastAttempt = time(NULL);
Glib::signal_timeout().connect_seconds(sigc::mem_fun(*this, &Mangler::reconnectStatusHandler), 1);
return;
}
}
}
connectbutton->set_label("gtk-connect");
builder->get_widget("serverSelectComboBox", combobox);
combobox->set_sensitive(true);
}/*}}}*/
bool Mangler::reconnectStatusHandler(void) {/*{{{*/
Gtk::Button *connectbutton;
char buf[64] = "";
int reconnectTimer = (15 - (time(NULL) - lastAttempt));
builder->get_widget("connectButton", connectbutton);
if (connectbutton->get_label() != "gtk-cancel" || wantDisconnect) {
return false;
}
builder->get_widget("statusbar", statusbar);
snprintf(buf, 63, "Attempting reconnect in %d seconds...", (reconnectTimer < 0) ? 0 : reconnectTimer);
statusbar->pop();
statusbar->push(buf);
if (reconnectTimer <= 0) {
lastAttempt = time(NULL);
connectbutton->set_label("gtk-connect");
Mangler::connectButton_clicked_cb();
return false;
}
return true;
}/*}}}*/
/*
* Button signal handler callbacks
*/
void Mangler::quickConnectButton_clicked_cb(void) {/*{{{*/
builder->get_widget("quickConnectDialog", dialog);
dialog->set_icon(icons["tray_icon"]);
builder->get_widget("qcServerName", entry);
entry->set_text(config["qc_lastserver.hostname"].toCString());
builder->get_widget("qcPort", entry);
entry->set_text(config["qc_lastserver.port"].toCString());
builder->get_widget("qcUsername", entry);
entry->set_text(config["qc_lastserver.username"].toCString());
builder->get_widget("qcPassword", entry);
entry->set_text(config["qc_lastserver.password"].toCString());
builder->get_widget("qcConnectButton", button);
button->set_sensitive(!v3_is_loggedin());
dialog->set_keep_above(true);
dialog->run();
dialog->hide();
}/*}}}*/
void Mangler::serverListButton_clicked_cb(void) {/*{{{*/
builder->get_widget("serverListWindow", window);
window->set_icon(icons["tray_icon"]);
window->show();
}/*}}}*/
void Mangler::connectButton_clicked_cb(void) {/*{{{*/
Gtk::Button *connectbutton;
Gtk::TreeModel::iterator iter;
builder->get_widget("connectButton", connectbutton);
if (connectbutton->get_label() == "gtk-cancel") {
wantDisconnect = true;
onDisconnectHandler();
builder->get_widget("statusbar", statusbar);
statusbar->pop();
statusbar->push("Disconnected");
} else if (connectbutton->get_label() == "gtk-connect") {
builder->get_widget("serverSelectComboBox", combobox);
iter = combobox->get_active();
if (iter) {
Gtk::TreeModel::Row row = *iter;
connectedServerName = Glib::ustring( row[serverList->serverListColumns.name] );
iniSection &server(config.servers[connectedServerName]);
Glib::ustring hostname = server["hostname"].toCString();
Glib::ustring port = server["port"].toCString();
Glib::ustring username = server["username"].toCString();
Glib::ustring password = server["password"].toCString();
Glib::ustring phonetic = server["phonetic"].toCString();
if (!server.size() || hostname.empty() || port.empty() || username.empty()) {
builder->get_widget("statusbar", statusbar);
statusbar->pop();
statusbar->push("Not connected.");
if (hostname.empty()) {
errorDialog("You have not specified a hostname for this server.");
return;
}
if (port.empty()) {
errorDialog("You have not specified a port for this server.");
return;
}
if (username.empty()) {
errorDialog("You have not specified a username for this server.");
return;
}
return;
}
config["LastConnectedServerName"] = connectedServerName;
config.config.save();
wantDisconnect = false;
onConnectHandler(
hostname,
port,
username,
password,
phonetic,
server["Charset"].toUString(),
server["acceptPages"].length() ? server["acceptPages"].toBool() : true,
server["acceptU2U"].length() ? server["acceptU2U"].toBool() : true,
server["acceptPrivateChat"].length() ? server["acceptPrivateChat"].toBool() : true,
server["allowRecording"].length() ? server["allowRecording"].toBool() : true);
}
} else {
wantDisconnect = true;
v3_logout();
}
return;
}/*}}}*/
void Mangler::commentButton_clicked_cb(void) {/*{{{*/
if (v3_is_loggedin()) {
textStringChangeCommentEntry->set_text(comment);
textStringChangeURLEntry->set_text(url);
textStringChangeIntegrationEntry->set_text(integration_text);
textStringChangeDialog->run();
textStringChangeDialog->hide();
}
}/*}}}*/
void Mangler::chatButton_clicked_cb(void) {/*{{{*/
if (v3_is_loggedin()) {
if (!chat->isOpen) {
chat->chatWindow->set_icon(icons["tray_icon"]);
chat->chatWindow->show();
} else {
chat->chatWindow->present();
}
}
}/*}}}*/
void Mangler::bindingsButton_clicked_cb(void) {/*{{{*/
//fprintf(stderr, "bindings button clicked\n");
static Glib::ustring color = "red";
if (color == "red") {
color = "green";
statusIcon->set(icons["tray_icon_green"]);
} else if (color == "green") {
color = "blue";
statusIcon->set(icons["tray_icon_blue"]);
} else if (color == "blue") {
color = "yellow";
statusIcon->set(icons["tray_icon_yellow"]);
} else if (color == "yellow") {
color = "red";
statusIcon->set(icons["tray_icon_red"]);
}
}/*}}}*/
void Mangler::adminButton_clicked_cb(void) {/*{{{*/
Glib::ustring password;
if (! isAdmin) {
password = mangler->getPasswordEntry("Admin Password");
if (password.length()) {
v3_admin_login((char *)password.c_str());
wantAdminWindow = true;
// if we tried sending a password, the only options are either
// success or get booted from the server.
}
} else {
admin->show();
}
}/*}}}*/
void Mangler::adminLoginMenuItem_activate_cb(void) {/*{{{*/
builder->get_widget("adminLoginMenuItem", menuitem);
if (menuitem->get_label() == "_Admin Logout") {
v3_admin_logout();
} else {
adminButton_clicked_cb();
}
}/*}}}*/
void Mangler::adminWindowMenuItem_activate_cb(void) {/*{{{*/
admin->show();
}/*}}}*/
void Mangler::settingsButton_clicked_cb(void) {/*{{{*/
settings->settingsWindow->show();
}/*}}}*/
void Mangler::aboutButton_clicked_cb(void) {/*{{{*/
builder->get_widget("aboutWindow", aboutdialog);
aboutdialog->set_keep_above(true);
aboutdialog->set_logo(icons["mangler_logo"]);
aboutdialog->run();
aboutdialog->hide();
}/*}}}*/
void Mangler::xmitButton_toggled_cb(void) {/*{{{*/
builder->get_widget("xmitButton", togglebutton);
if (togglebutton->get_active()) {
isTransmittingButton = true;
startTransmit();
} else {
isTransmittingButton = false;
if (! isTransmittingKey && ! isTransmittingMouse && ! isTransmittingVA) {
stopTransmit();
}
}
}/*}}}*/
/*
* Menu bar signal handler callbacks
*/
void Mangler::buttonMenuItem_toggled_cb(void) {/*{{{*/
builder->get_widget("buttonMenuItem", checkmenuitem);
builder->get_widget("mainWindowButtonVBox", vbox);
if (checkmenuitem->get_active()) {
vbox->hide();
config["ButtonsHidden"] = true;
} else {
config["ButtonsHidden"] = false;
vbox->show();
}
config.config.save();
}/*}}}*/
void Mangler::hideServerInfoMenuItem_toggled_cb(void) {/*{{{*/
builder->get_widget("hideServerInfoMenuItem", checkmenuitem);
builder->get_widget("serverTable", table);
if (checkmenuitem->get_active()) {
table->hide();
config["ServerInfoHidden"] = true;
} else {
config["ServerInfoHidden"] = false;
table->show();
}
config.config.save();
}/*}}}*/
void Mangler::hideGuestFlagMenuItem_toggled_cb(void) {/*{{{*/
builder->get_widget("hideGuestFlagMenuItem", checkmenuitem);
if (checkmenuitem->get_active()) {
config["GuestFlagHidden"] = true;
} else {
config["GuestFlagHidden"] = false;
}
channelTree->refreshAllUsers();
config.config.save();
}/*}}}*/
void Mangler::motdMenuItem_activate_cb(void) {/*{{{*/
motdIgnore->set_sensitive(!connectedServerName.empty());
motdIgnore->set_active(connectedServerName.length() && config.servers[connectedServerName]["MotdIgnore"].toBool());
motdOkButton->grab_focus();
motdWindow->present();
}/*}}}*/
void Mangler::recorderMenuItem_activate_cb(void) {/*{{{*/
recorder->recWindow->set_icon(icons["tray_icon"]);
recorder->show();
}/*}}}*/
void Mangler::quitMenuItem_activate_cb(void) {/*{{{*/
Gtk::Main::quit();
}/*}}}*/
/*
* Other signal handler callbacks
*/
void Mangler::statusIcon_activate_cb(void) {/*{{{*/
if (iconified == true) {
manglerWindow->deiconify();
manglerWindow->present();
manglerWindow->set_skip_pager_hint(false);
manglerWindow->set_skip_taskbar_hint(false);
iconified = false;
} else {
manglerWindow->iconify();
manglerWindow->set_skip_pager_hint(true);
manglerWindow->set_skip_taskbar_hint(true);
iconified = true;
}
}/*}}}*/
void Mangler::statusIcon_buttonpress_event_cb(GdkEventButton* event) {/*{{{*/
if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) {
builder->get_widget("statusIconMenu", statusIconMenu);
builder->get_widget("muteMicCheckButton", checkbutton);
builder->get_widget("muteMicCheckMenuItem", checkmenuitem);
checkmenuitem->set_active(checkbutton->get_active());
builder->get_widget("muteSoundCheckButton", checkbutton);
builder->get_widget("muteSoundCheckMenuItem", checkmenuitem);
checkmenuitem->set_active(checkbutton->get_active());
statusIconMenu->popup(event->button, event->time);
}
}
/*}}}*/
void Mangler::statusIcon_scroll_event_cb(GdkEventScroll* event) {/*{{{*/
if (event->type != GDK_SCROLL) {
return;
}
int volume = config["MasterVolumeLevel"].toInt();
switch (event->direction) {
case GDK_SCROLL_UP:
volume = (volume + 5 > 148) ? 148 : volume + 5;
break;
case GDK_SCROLL_DOWN:
volume = (volume - 5 < 0) ? 0 : volume - 5;
break;
default:
return;
}
if (volume + 5 > 79 && volume - 5 < 79) {
volume = 79;
}
config["MasterVolumeLevel"] = volume;
settings->volumeAdjustment->set_value(volume);
v3_set_volume_master(volume);
setTooltip();
}/*}}}*/
void Mangler::setTooltip(void) {/*{{{*/
Glib::ustring tooltip = "Mangler: volume: ";
float value = config["MasterVolumeLevel"].toInt();
value = (value > 79) ? ((value-79)/69)*100+100 : (value/79)*100;
tooltip += Glib::ustring::format((int)value) + "%" + ((config["MuteSound"].toBool()) ? " (muted)" : "");
statusIcon->set_tooltip_text(tooltip);
}/*}}}*/
void Mangler::startTransmit(void) {/*{{{*/
const v3_codec *codec;
v3_user *user;
if (! v3_is_loggedin()) {
return;
}
if (muteMic) {
return;
}
user = v3_get_user(v3_get_user_id());
if (! user) {
return;
}
if (isTransmitting) {
v3_free_user(user);
return;
}
isTransmitting = true;
if ((codec = v3_get_channel_codec(user->channel))) {
//fprintf(stderr, "channel %d codec rate: %d at sample size %d\n", user->channel, codec->rate, codec->pcmframesize);
v3_free_user(user);
channelTree->setUserIcon(v3_get_user_id(), "orange");
statusIcon->set(icons["tray_icon_yellow"]);
inputAudio = new ManglerAudio(AUDIO_INPUT, codec->rate, 1, codec->pcmframesize);
}
}/*}}}*/
void Mangler::stopTransmit(void) {/*{{{*/
if (!isTransmitting) {
return;
}
isTransmitting = false;
if (v3_is_loggedin()) {
channelTree->setUserIcon(v3_get_user_id(), "red");
statusIcon->set(icons["tray_icon_red"]);
}
if (inputAudio) {
inputAudio->finish();
inputAudio = NULL;
}
}/*}}}*/
// Quick Sound Mute
void Mangler::muteSoundCheckButton_toggled_cb(void) {/*{{{*/
builder->get_widget("muteSoundCheckButton", checkbutton);
muteSound = checkbutton->get_active();
config["MuteSound"] = muteSound;
setTooltip();
}/*}}}*/
// Quick Mic Mute
void Mangler::muteMicCheckButton_toggled_cb(void) {/*{{{*/
builder->get_widget("muteMicCheckButton", checkbutton);
muteMic = checkbutton->get_active();
config["MuteMic"] = muteMic;
if (muteMic && isTransmitting) {
stopTransmit();
} else if (!muteMic && (isTransmittingMouse || isTransmittingKey || isTransmittingButton || isTransmittingVA)) {
startTransmit();
}
}/*}}}*/
// Status Icon Sound Mute
void Mangler::muteSoundCheckMenuItem_toggled_cb(void) {/*{{{*/
builder->get_widget("muteSoundCheckButton", checkbutton);
builder->get_widget("muteSoundCheckMenuItem", checkmenuitem);
checkbutton->set_active(checkmenuitem->get_active());
}/*}}}*/
// Status Icon Mic Mute
void Mangler::muteMicCheckMenuItem_toggled_cb(void) {/*{{{*/
builder->get_widget("muteMicCheckButton", checkbutton);
builder->get_widget("muteMicCheckMenuItem", checkmenuitem);
checkbutton->set_active(checkmenuitem->get_active());
}/*}}}*/
// Quick Connect Callbacks
void Mangler::qcConnectButton_clicked_cb(void) {/*{{{*/
builder->get_widget("qcServerName", entry);
Glib::ustring hostname = entry->get_text();
builder->get_widget("qcPort", entry);
Glib::ustring port = entry->get_text();
builder->get_widget("qcUsername", entry);
Glib::ustring username = entry->get_text();
builder->get_widget("qcPassword", entry);
Glib::ustring password = entry->get_text();
//fprintf(stderr, "connecting to: %s:%s\n", server.c_str(), port.c_str());
config["qc_lastserver.hostname"] = hostname;
config["qc_lastserver.port"] = port;
config["qc_lastserver.username"] = username;
config["qc_lastserver.password"] = password;
config.config.save();
connectedServerName = "";
onConnectHandler(hostname, port, username, password);
}/*}}}*/
void Mangler::qcCancelButton_clicked_cb(void) {/*{{{*/
}/*}}}*/
// MOTD Window Callbacks
void Mangler::motdIgnore_toggled_cb(void) {/*{{{*/
if (connectedServerName.length()) {
config.servers[connectedServerName]["MotdIgnore"] = motdIgnore->get_active();
}
}/*}}}*/
void Mangler::motdOkButton_clicked_cb(void) {/*{{{*/
motdWindow->hide();
}/*}}}*/
/*
* Timeout Callbacks
*
* Inbound event processing happens here.
*/
bool Mangler::getNetworkEvent() {/*{{{*/
v3_event *ev;
while ((ev = v3_get_event(V3_NONBLOCK)) != NULL) {
v3_user *u;
v3_channel *c;
Glib::ustring rank = "";
gdk_threads_enter();
// if we're not logged in, just ignore whatever messages we receive
// *unless* it's a disconnect message. This prevents old messages in
// the queue from attempting to interact with the GUI after a
// disconnection
switch (ev->type) {
case V3_EVENT_PING:/*{{{*/
if (v3_is_loggedin()) {
char buf[64];
builder->get_widget("pingLabel", label);
builder->get_widget("statusbar", statusbar);
if (ev->ping != 65535) {
snprintf(buf, 16, "%dms", ev->ping);
#ifdef HAVE_G15
g15->update("", "", "", "", buf);
#endif
label->set_text(c_to_ustring(buf));
snprintf(buf, 63, "Ping: %dms - Users: %d/%d", ev->ping, v3_user_count(), v3_get_max_clients());
statusbar->pop();
statusbar->push(c_to_ustring(buf));
} else {
#ifdef HAVE_G15
g15->update("", "", "", "", "checking...");
#endif
label->set_text("checking...");
snprintf(buf, 63, "Ping: checking... - Users: %d/%d", v3_user_count(), v3_get_max_clients());
statusbar->pop();
statusbar->push(c_to_ustring(buf));
}
builder->get_widget("userCountLabel", label);
snprintf(buf, 16, "%d/%d", v3_user_count(), v3_get_max_clients());
label->set_text(c_to_ustring(buf));
}
break;/*}}}*/
case V3_EVENT_STATUS:/*{{{*/
if (v3_is_loggedin()) {
builder->get_widget("progressbar", progressbar);
builder->get_widget("statusbar", statusbar);
if (ev->status.percent == 100) {
progressbar->hide();
} else {
progressbar->show();
progressbar->set_fraction(ev->status.percent/(float)100);
}
statusbar->pop();
statusbar->push(ev->status.message);
//fprintf(stderr, "got event type %d: %d %s\n", ev->type, ev->status.percent, ev->status.message);
}
break;/*}}}*/
case V3_EVENT_USER_LOGIN:/*{{{*/
u = v3_get_user(ev->user.id);
if (!u) {
fprintf(stderr, "couldn't retreive user id %d\n", ev->user.id);
break;
}
if (!(ev->flags & V3_LOGIN_FLAGS_EXISTING) && (v3_get_user_channel(v3_get_user_id()) == ev->channel.id)) {
audioControl->playNotification("channelenter");
}
#ifdef HAVE_G15
if (!(ev->flags & V3_LOGIN_FLAGS_EXISTING)) {
g15->update("", "", u->name, "", "");
}
#endif
//fprintf(stderr, "adding user id %d: %s to channel %d\n", ev->user.id, u->name, ev->channel.id);
if (u->rank_id) {
v3_rank *r;
if ((r = v3_get_rank(u->rank_id))) {
rank = c_to_ustring(r->name);
v3_free_rank(r);
}
}
channelTree->addUser(
(uint32_t)u->id,
(uint32_t)ev->channel.id,
c_to_ustring(u->name),
c_to_ustring(u->comment),
u->phonetic,
u->url,
c_to_ustring(u->integration_text),
(bool)u->guest,
(bool)u->real_user_id,
rank);
if (connectedServerName.length() && u->id != v3_get_user_id()) {
// If we have a per user volume set for this user name, set it now.
if (config.hasUserVolume(connectedServerName, u->name)) {
v3_set_volume_user(u->id, config.UserVolume(connectedServerName, u->name).toInt());
}
// If the user was muted, mute them again.
if (config.UserMuted(connectedServerName, u->name).toBool()) {
channelTree->muteUserToggle(u->id);
}
}
v3_free_user(u);
break;/*}}}*/
case V3_EVENT_USER_MODIFY:/*{{{*/
if (v3_is_loggedin()) {
u = v3_get_user(ev->user.id);
if (!u) {
fprintf(stderr, "couldn't retreive user id %d\n", ev->user.id);
break;
}
if (u->id == 0) {
channelTree->updateLobby(c_to_ustring(u->name), c_to_ustring(u->comment), c_to_ustring(u->phonetic));
} else {
if (u->rank_id) {
v3_rank *r;
if ((r = v3_get_rank(u->rank_id))) {
rank = c_to_ustring(r->name);
v3_free_rank(r);
}
}
channelTree->updateUser(
(uint32_t)u->id,
(uint32_t)ev->channel.id,
c_to_ustring(u->name),
c_to_ustring(u->comment),
u->phonetic,
u->url,
c_to_ustring(u->integration_text),
(bool)u->guest,
(bool)u->real_user_id,
rank);
}
v3_free_user(u);
}
break;/*}}}*/
case V3_EVENT_CHAN_MODIFY:/*{{{*/
if (v3_is_loggedin()) {
const v3_codec *codec_info;
c = v3_get_channel(ev->channel.id);
if (!c) {
fprintf(stderr, "couldn't retreive channel id %d\n", ev->user.id);
break;
}
channelTree->updateChannel(
(uint8_t)c->protect_mode,
(uint32_t)c->id,
(uint32_t)c->parent,
c_to_ustring(c->name),
c_to_ustring(c->comment),
c->phonetic);
if (! isAdmin && ! isChanAdmin && v3_is_channel_admin(c->id)) {
isChanAdmin = true;
}
admin->channelUpdated(c);
if (ev->channel.id == v3_get_user_channel(v3_get_user_id())) {
builder->get_widget("codecLabel", label);
if ((codec_info = v3_get_channel_codec(ev->channel.id))) {
label->set_text(codec_info->name);
} else {
label->set_text("Unsupported Codec");
}
}
v3_free_channel(c);
}
break;/*}}}*/
case V3_EVENT_USER_LOGOUT:/*{{{*/
if (v3_is_loggedin()) {
if (outputAudio[ev->user.id]) {
outputAudio[ev->user.id]->finish();
outputAudio.erase(ev->user.id);
}
if (v3_get_user_channel(v3_get_user_id()) == ev->channel.id) {
audioControl->playNotification("channelleave");
}
// can't get any user info... it's already gone by this point
//fprintf(stderr, "removing user id %d\n", ev->user.id);
channelTree->removeUser(ev->user.id);
chat->removeUser(ev->user.id);
#ifdef HAVE_XOSD
osd->removeUser(ev->user.id);
#endif
}
break;/*}}}*/
case V3_EVENT_CHAN_REMOVE:/*{{{*/
if (v3_is_loggedin()) {
// can't get any channel info... it's already gone by this point
//fprintf(stderr, "removing channel id %d\n", ev->channel.id);
channelTree->removeChannel(ev->channel.id);
admin->channelRemoved(ev->channel.id);
}
break;/*}}}*/
case V3_EVENT_LOGIN_COMPLETE:/*{{{*/
if (v3_is_loggedin()) {
const v3_codec *codec_info;
builder->get_widget("adminButton", button);
button->set_sensitive(true);
builder->get_widget("adminLoginMenuItem", menuitem);
menuitem->set_sensitive(true);
builder->get_widget("adminWindowMenuItem", menuitem);
menuitem->set_sensitive(true);
builder->get_widget("chatButton", button);
button->set_sensitive(true);
builder->get_widget("motdMenuItem", menuitem);
menuitem->set_sensitive(true);
builder->get_widget("chatMenuItem", menuitem);
menuitem->set_sensitive(true);
builder->get_widget("commentButton", button);
button->set_sensitive(true);
builder->get_widget("commentMenuItem", menuitem);
menuitem->set_sensitive(true);
builder->get_widget("codecLabel", label);
if ((codec_info = v3_get_channel_codec(0))) {
label->set_text(codec_info->name);
} else {
label->set_text("Unsupported Codec");
}
channelTree->expand_all();
audioControl->playNotification("login");
if (connectedServerName.length()) {
iniSection &server(config.servers[connectedServerName]);
if (server["PersistentComments"].toBool()) {
comment = server["Comment"].toUString();
url = server["URL"].toUString();
v3_set_text(
(char *)ustring_to_c(comment).c_str(),
(char *)ustring_to_c(url).c_str(),
(char *)ustring_to_c(integration_text).c_str(),
true);
}
uint32_t channel_id = v3_get_channel_id(ustring_to_c(server["DefaultChannel"].toUString()).c_str());
if (channel_id && (c = v3_get_channel(channel_id))) {
Glib::ustring password = channelTree->getChannelSavedPassword(channel_id);
uint16_t pw_channel = 0;
if ((pw_channel = v3_channel_requires_password(channel_id)) &&
(password = channelTree->getChannelSavedPassword(pw_channel)).empty() &&
(password = getPasswordEntry("Channel Password")).length()) {
channelTree->setChannelSavedPassword(pw_channel, password);
}
if (!pw_channel || (pw_channel && password.length())) {
v3_change_channel(channel_id, (char *)((pw_channel) ? password.c_str() : ""));
}
v3_free_channel(c);
}
}
if (chat->isOpen) {
v3_join_chat();
}
recorder->can_record(true);
#ifdef HAVE_G15
g15->addevent("connected to server");
g15->update(connectedServerName, "", "", "", "");
#endif
}
break;/*}}}*/
case V3_EVENT_USER_CHAN_MOVE:/*{{{*/
{
u = v3_get_user(ev->user.id);
if (! u) {
fprintf(stderr, "failed to retreive user information for user id %d\n", ev->user.id);
break;
}
if (ev->user.id == v3_get_user_id()) {
// we're moving channels... update the codec label
const v3_codec *codec_info;
builder->get_widget("codecLabel", label);
if ((codec_info = v3_get_channel_codec(ev->channel.id))) {
label->set_text(codec_info->name);
} else {
label->set_text("Unsupported Codec");
}
#ifdef HAVE_G15
if ((c = v3_get_channel(ev->channel.id))) {
string event = "switched to: ";
event.append(c->name);
g15->addevent(event);
free(c);
}
#endif
#ifdef HAVE_XOSD
osd->destroyOsd();
#endif
} else {
if (ev->channel.id == v3_get_user_channel(v3_get_user_id())) {
// they're joining our channel
audioControl->playNotification("channelenter");
#ifdef HAVE_G15
string event = "joined channel: ";
event.append(u->name);
g15->addevent(event);
g15->update("", "", "", u->name, "");
#endif
} else if (channelTree->getUserChannelId(ev->user.id) == v3_get_user_channel(v3_get_user_id())) {
// they're leaving our channel
audioControl->playNotification("channelleave");
#ifdef HAVE_G15
string event = "left channel: ";
event.append(u->name);
g15->addevent(event);
#endif
}
#ifdef HAVE_XOSD
osd->removeUser(ev->user.id);
#endif
}
if (u->rank_id) {
v3_rank *r;
if ((r = v3_get_rank(u->rank_id))) {
rank = c_to_ustring(r->name);
v3_free_rank(r);
}
}
//fprintf(stderr, "moving user id %d to channel id %d\n", ev->user.id, ev->channel.id);
Glib::ustring last_transmit = channelTree->getLastTransmit((uint32_t)ev->user.id);
channelTree->removeUser((uint32_t)ev->user.id);
channelTree->addUser(
(uint32_t)u->id,
(uint32_t)ev->channel.id,
c_to_ustring(u->name),
c_to_ustring(u->comment),
u->phonetic,
u->url,
c_to_ustring(u->integration_text),
(bool)u->guest,
(bool)u->real_user_id,
rank);
channelTree->setLastTransmit(ev->user.id, last_transmit);
if (connectedServerName.length() && u->id != v3_get_user_id()) {
// If we have a per user volume set for this user name, set it now.
if (config.hasUserVolume(connectedServerName, u->name)) {
v3_set_volume_user(u->id, config.UserVolume(connectedServerName, u->name).toInt());
}
// If the user was muted, mute them again.
if (config.UserMuted(connectedServerName, u->name).toBool()) {
channelTree->muteUserToggle(u->id);
}
}
// if there was an audio stream open for this user, close it
if (outputAudio[ev->user.id]) {
outputAudio[ev->user.id]->finish();
outputAudio.erase(ev->user.id);
}
channelTree->refreshAllUsers();
chat->chatUserTreeModelFilter->refilter();
v3_free_user(u);
}
break;/*}}}*/
case V3_EVENT_CHAN_MOVE:/*{{{*/
channelTree->refreshAllChannels();
admin->channelResort();
break;/*}}}*/
case V3_EVENT_CHAN_ADD:/*{{{*/
c = v3_get_channel(ev->channel.id);
if (! c) {
fprintf(stderr, "failed to retreive channel information for channel id %d\n", ev->channel.id);
break;
}
channelTree->addChannel(
(uint8_t)c->protect_mode,
(uint32_t)c->id,
(uint32_t)c->parent,
c_to_ustring(c->name),
c_to_ustring(c->comment),
c->phonetic);
if (! isAdmin && ! isChanAdmin && v3_is_channel_admin(c->id)) {
isChanAdmin = true;
}
admin->channelAdded(c);
v3_free_channel(c);
break;/*}}}*/
case V3_EVENT_CHAN_BADPASS:/*{{{*/
channelTree->forgetChannelSavedPassword(ev->channel.id);
errorDialog(c_to_ustring(ev->error.message));
break;/*}}}*/
case V3_EVENT_ERROR_MSG:/*{{{*/
errorDialog(c_to_ustring(ev->error.message));
break;/*}}}*/
case V3_EVENT_USER_TALK_START:/*{{{*/
if (v3_is_loggedin()) {
channelTree->refreshUser(ev->user.id);
}
#ifdef HAVE_G15
if (v3_get_user_channel(ev->user.id) == v3_get_user_channel(v3_get_user_id())) {
if ((u = v3_get_user(ev->user.id))) {
string event = "talking: ";
event.append(u->name);
g15->addevent(event);
g15->update("", u->name, "", "", "");
free(u);
}
}
#endif
break;/*}}}*/
case V3_EVENT_USER_TALK_END:
case V3_EVENT_USER_TALK_MUTE:/*{{{*/
if (v3_is_loggedin()) {
channelTree->refreshUser(ev->user.id);
#ifdef HAVE_XOSD
osd->removeUser(ev->user.id);
#endif
if (outputAudio[ev->user.id]) {
outputAudio[ev->user.id]->finish();
outputAudio.erase(ev->user.id);
}
}
break;/*}}}*/
case V3_EVENT_PLAY_AUDIO:/*{{{*/
if (v3_is_loggedin()) {
channelTree->setUserIcon(ev->user.id, "green", true);
#ifdef HAVE_XOSD
osd->addUser(ev->user.id);
#endif
if (!channelTree->isMuted(ev->user.id) && !muteSound) {
// Open a stream if we don't have one for this user
if (!outputAudio[ev->user.id]) {
outputAudio[ev->user.id] = new ManglerAudio(AUDIO_OUTPUT, ev->pcm.rate, ev->pcm.channels);
}
// And queue the audio
if (outputAudio[ev->user.id]) {
outputAudio[ev->user.id]->queue(ev->pcm.length, (uint8_t *)ev->data->sample);
}
} else if (outputAudio[ev->user.id]) {
outputAudio[ev->user.id]->finish();
outputAudio.erase(ev->user.id);
}
}
break;/*}}}*/
case V3_EVENT_RECORD_UPDATE:/*{{{*/
recorder->record(
c_to_ustring(ev->text.name),
c_to_ustring(ev->status.message),
ev->record.index,
ev->record.time,
ev->record.stopped,
ev->record.flushed);
break;/*}}}*/
case V3_EVENT_DISPLAY_MOTD:/*{{{*/
{
Glib::ustring motdKey;
uint32_t motdhash = 0;
if (!ev->flags) {
motdKey = "MotdHashUser";
motdNotebook->set_show_tabs(true);
motdNotebook->set_current_page(0);
motdUsers->get_buffer()->set_text(c_to_ustring(stripMotdRtf(ev->data->motd).c_str()));
} else {
motdKey = "MotdHash";
motdGuests->get_buffer()->set_text(c_to_ustring(stripMotdRtf(ev->data->motd).c_str()));
}
motdIgnore->set_sensitive(!connectedServerName.empty());
motdIgnore->set_active(connectedServerName.length() && config.servers[connectedServerName]["MotdIgnore"].toBool());
if (motdIgnore->get_active() || !strlen(ev->data->motd)) {
break;
}
if (connectedServerName.length()) {
// we're not launching a space shuttle here, no need for
// anything super complex
for (uint32_t ctr = 0; ctr < strlen(ev->data->motd); ctr++) {
motdhash += ev->data->motd[ctr] + ctr;
}
}
if (motdAlways || connectedServerName.empty() || config.servers[connectedServerName][motdKey].toULong() != motdhash) {
if (connectedServerName.length()) {
config.servers[connectedServerName][motdKey] = motdhash;
config.servers.save();
}
motdOkButton->grab_focus();
motdWindow->show();
}
}
break;/*}}}*/
case V3_EVENT_DISCONNECT:/*{{{*/
onDisconnectHandler();
audioControl->playNotification("logout");
break;/*}}}*/
case V3_EVENT_CHAT_JOIN:/*{{{*/
{
chat->addUser(ev->user.id);
u = v3_get_user(ev->user.id);
if (!u) {
fprintf(stderr, "couldn't retreive user id %d\n", ev->user.id);
break;
}
if (u->id != 0) {
if (u->rank_id) {
v3_rank *r;
if ((r = v3_get_rank(u->rank_id))) {
rank = c_to_ustring(r->name);
v3_free_rank(r);
}
}
channelTree->updateUser(
(uint32_t)u->id,
(uint32_t)u->channel,
c_to_ustring(u->name),
c_to_ustring(u->comment),
u->phonetic,
u->url,
c_to_ustring(u->integration_text),
(bool)u->guest,
(bool)u->real_user_id,
rank);
}
v3_free_user(u);
}
break;/*}}}*/
case V3_EVENT_CHAT_LEAVE:/*{{{*/
{
chat->removeUser(ev->user.id);
u = v3_get_user(ev->user.id);
if (!u) {
fprintf(stderr, "couldn't retreive user id %d\n", ev->user.id);
break;
}
if (u->id != 0) {
if (u->rank_id) {
v3_rank *r;
if ((r = v3_get_rank(u->rank_id))) {
rank = c_to_ustring(r->name);
v3_free_rank(r);
}
}
channelTree->updateUser(
(uint32_t)u->id,
(uint32_t)u->channel,
c_to_ustring(u->name),
c_to_ustring(u->comment),
u->phonetic,
u->url,
c_to_ustring(u->integration_text),
(bool)u->guest,
(bool)u->real_user_id,
rank);
}
v3_free_user(u);
}
break;/*}}}*/
case V3_EVENT_CHAT_MESSAGE:/*{{{*/
if (v3_is_loggedin()) {
if (ev->user.id == 0) {
chat->addRconMessage(c_to_ustring(ev->data->chatmessage));
} else {
chat->addChatMessage(ev->user.id, c_to_ustring(ev->data->chatmessage));
}
}
break;/*}}}*/
case V3_EVENT_PRIVATE_CHAT_START:/*{{{*/
{
uint16_t remote;
if (ev->user.privchat_user1 == v3_get_user_id()) {
remote = ev->user.privchat_user2;
} else {
remote = ev->user.privchat_user1;
}
if (privateChatWindows[remote]) {
privateChatWindows[remote]->remoteReopened();
} else {
v3_user *u;
Glib::ustring name = "unknown";
if ((u = v3_get_user(remote)) != NULL) {
name = c_to_ustring(u->name);
v3_free_user(u);
}
privateChatWindows[remote] = new ManglerPrivChat(remote);
privateChatWindows[remote]->addMessage("*** opened private chat with " + name);
}
}
break;/*}}}*/
case V3_EVENT_PRIVATE_CHAT_END:/*{{{*/
{
if (privateChatWindows[ev->user.privchat_user2]) {
privateChatWindows[ev->user.privchat_user2]->remoteClosed();
}
}
break;/*}}}*/
case V3_EVENT_PRIVATE_CHAT_AWAY:/*{{{*/
{
if (privateChatWindows[ev->user.privchat_user2]) {
privateChatWindows[ev->user.privchat_user2]->remoteAway();
}
}
break;/*}}}*/
case V3_EVENT_PRIVATE_CHAT_BACK:/*{{{*/
{
if (privateChatWindows[ev->user.privchat_user2]) {
privateChatWindows[ev->user.privchat_user2]->remoteBack();
}
}
break;/*}}}*/
case V3_EVENT_PRIVATE_CHAT_MESSAGE:/*{{{*/
{
uint16_t remote;
if (ev->user.privchat_user1 == v3_get_user_id()) {
remote = ev->user.privchat_user2;
} else {
remote = ev->user.privchat_user1;
}
if (privateChatWindows[remote]) {
if (!ev->flags) { // set to true on error
privateChatWindows[remote]->addChatMessage(ev->user.privchat_user2, c_to_ustring(ev->data->chatmessage));
} else {
privateChatWindows[remote]->addMessage("*** error sending message to remote user");
}
}
}
break;/*}}}*/
case V3_EVENT_TEXT_TO_SPEECH_MESSAGE:/*{{{*/
{
if ((u = v3_get_user(ev->user.id))) {
//fprintf(stderr, "TTS: %s: %s\n", u->name, ev->data->chatmessage);
audioControl->playText(c_to_ustring((strlen(u->phonetic)) ? u->phonetic : u->name) + ": " + c_to_ustring(ev->data->chatmessage));
v3_free_user(u);
}
}
break;/*}}}*/
case V3_EVENT_PLAY_WAVE_FILE_MESSAGE:/*{{{*/
{
/*
if ((u = v3_get_user(ev->user.id))) {
fprintf(stderr, "WAV: %s: %s\n", u->name, ev->data->chatmessage);
v3_free_user(u);
}
*/
}
break;/*}}}*/
case V3_EVENT_USER_PAGE:/*{{{*/
{
if ((u = v3_get_user(ev->user.id))) {
//fprintf(stderr, "Page from: %s\n", u->name);
audioControl->playText("Page from: " + c_to_ustring((strlen(u->phonetic)) ? u->phonetic : u->name));
v3_free_user(u);
}
}
break;/*}}}*/
case V3_EVENT_ADMIN_AUTH:/*{{{*/
{
const v3_permissions *perms = v3_get_permissions();
if (perms->srv_admin && !isAdmin) {
isAdmin = true;
builder->get_widget("adminLoginMenuItem", menuitem);
menuitem->set_label("_Admin Logout");
if (wantAdminWindow) {
wantAdminWindow = false;
admin->show();
}
} else {
isAdmin = false;
builder->get_widget("adminLoginMenuItem", menuitem);
menuitem->set_label("_Admin Login");
}
v3_user *lobby;
if ((lobby = v3_get_user(0))) {
channelTree->updateLobby(c_to_ustring(lobby->name), c_to_ustring(lobby->comment), c_to_ustring(lobby->phonetic));
v3_free_user(lobby);
}
}
break;/*}}}*/
case V3_EVENT_CHAN_ADMIN_UPDATED:/*{{{*/
channelTree->refreshAllChannels();
break;/*}}}*/
case V3_EVENT_USER_GLOBAL_MUTE_CHANGED:
case V3_EVENT_USER_CHANNEL_MUTE_CHANGED:/*{{{*/
channelTree->refreshUser(ev->user.id);
if (outputAudio[ev->user.id]) {
outputAudio[ev->user.id]->finish();
outputAudio.erase(ev->user.id);
}
break;/*}}}*/
case V3_EVENT_SERVER_PROPERTY_UPDATED:/*{{{*/
switch (ev->serverproperty.property) {
case V3_SRV_PROP_CHAT_FILTER:
chat->isPerChannel = ev->serverproperty.value;
chat->chatUserTreeModelFilter->refilter();
break;
case V3_SRV_PROP_CHAN_ORDER:
channelTree->sortManual = ev->serverproperty.value;
channelTree->refreshAllChannels();
admin->channelSort(ev->serverproperty.value);
break;
case V3_SRV_PROP_MOTD_ALWAYS:
motdAlways = ev->serverproperty.value;
break;
}
break;/*}}}*/
case V3_EVENT_USERLIST_ADD:/*{{{*/
{
v3_account *account = v3_get_account(ev->account.id);
if (account) {
admin->accountAdded(account);
v3_free_account(account);
}
}
break;/*}}}*/
case V3_EVENT_USERLIST_REMOVE:/*{{{*/
admin->accountRemoved(ev->account.id);
break;/*}}}*/
case V3_EVENT_USERLIST_MODIFY:/*{{{*/
{
v3_account *account = v3_get_account(ev->account.id);
if (account) {
admin->accountUpdated(account);
v3_free_account(account);
}
}
break;/*}}}*/
case V3_EVENT_RANK_ADD:/*{{{*/
{
v3_rank *rank = v3_get_rank(ev->data->rank.id);
admin->rankAdded(rank);
}
break;/*}}}*/
case V3_EVENT_RANK_REMOVE:/*{{{*/
{
admin->rankRemoved(ev->data->rank.id);
}
break;/*}}}*/
case V3_EVENT_RANK_MODIFY:/*{{{*/
{
v3_rank *rank = v3_get_rank(ev->data->rank.id);
admin->rankUpdated(rank);
}
break;/*}}}*/
case V3_EVENT_PERMS_UPDATED:/*{{{*/
admin->permsUpdated();
break;/*}}}*/
case V3_EVENT_USER_RANK_CHANGE:/*{{{*/
{
if (v3_is_loggedin()) {
u = v3_get_user(ev->user.id);
if (!u) {
fprintf(stderr, "couldn't retreive user id %d\n", ev->user.id);
break;
}
if (u->id == 0) {
channelTree->updateLobby(c_to_ustring(u->name), c_to_ustring(u->comment), c_to_ustring(u->phonetic));
} else {
if (u->rank_id) {
v3_rank *r;
if ((r = v3_get_rank(u->rank_id))) {
rank = c_to_ustring(r->name);
v3_free_rank(r);
}
}
channelTree->updateUser(
(uint32_t)u->id,
(uint32_t)u->channel,
c_to_ustring(u->name),
c_to_ustring(u->comment),
u->phonetic,
u->url,
c_to_ustring(u->integration_text),
(bool)u->guest,
(bool)u->real_user_id,
rank);
}
v3_free_user(u);
}
}
break;/*}}}*/
case V3_EVENT_SRV_PROP_RECV:/*{{{*/
admin->serverSettingsUpdated(ev->data->srvprop);
break;/*}}}*/
case V3_EVENT_SRV_PROP_SENT:/*{{{*/
admin->serverSettingsSendDone();
break;/*}}}*/
case V3_EVENT_ADMIN_BAN_LIST:/*{{{*/
admin->banList(
ev->data->ban.id,
ev->data->ban.count,
ev->data->ban.bitmask_id,
ev->data->ban.ip_address,
ev->data->ban.user,
ev->data->ban.by,
ev->data->ban.reason);
break;/*}}}*/
default:
fprintf(stderr, "******************************************************** got unknown event type %d\n", ev->type);
}
channelTree->expand_all();
v3_free_event(ev);
gdk_threads_leave();
}
return true;
}/*}}}*/
bool Mangler::checkPushToTalkKeys(void) {/*{{{*/
char pressed_keys[32];
GdkWindow *rootwin = gdk_get_default_root_window();
vector::iterator i;
bool ptt_on = true;
if (! config["PushToTalkKeyEnabled"].toBool()) {
isTransmittingKey = false;
return true;
}
vector ptt_keycodes = config.PushToTalkXKeyCodes();
XQueryKeymap(GDK_WINDOW_XDISPLAY(rootwin), pressed_keys);
for ( i = ptt_keycodes.begin();
i < ptt_keycodes.end();
i++) {
if (!((pressed_keys[*i >> 3] >> (*i & 0x07)) & 0x01)) {
ptt_on = false;
break;
}
}
if (ptt_on) {
isTransmittingKey = true;
startTransmit();
} else {
isTransmittingKey = false;
if (! isTransmittingButton && ! isTransmittingMouse && ! isTransmittingVA) {
stopTransmit();
}
}
return(true);
}/*}}}*/
bool Mangler::checkVoiceActivation(void) {/*{{{*/
if (Mangler::config["VoiceActivationEnabled"].toBool()) {
isTransmittingVA = true;
startTransmit();
} else {
isTransmittingVA = false;
if (! isTransmittingButton && ! isTransmittingMouse && ! isTransmittingKey) {
stopTransmit();
}
}
return true;
}/*}}}*/
bool Mangler::checkPushToTalkMouse(void) {/*{{{*/
GdkWindow *rootwin = gdk_get_default_root_window();
XDeviceInfo *xdev;
XDeviceState *xds;
XButtonState *xbs = NULL;
XInputClass *xic = NULL;
int ctr;
int ndevices_return;
int state = 1;
int byte = config["PushToTalkMouseValue"].toInt() / 8;
int bit = config["PushToTalkMouseValue"].toInt() % 8;
bool ptt_on = false;
if (! config["PushToTalkMouseEnabled"].toBool()) {
isTransmittingMouse = false;
return true;
}
if (config["MouseDeviceName"].empty()) {
return true;
}
if (CurrentOpenMouse != config["MouseDeviceName"].toString()) {
if (dev) {
XCloseDevice(GDK_WINDOW_XDISPLAY(rootwin), dev);
}
xdev = XListInputDevices(GDK_WINDOW_XDISPLAY(rootwin), &ndevices_return);
for (ctr = 0; ctr < ndevices_return; ctr++) {
Glib::ustring name = xdev[ctr].name;
if (config["MouseDeviceName"] == name && xdev[ctr].use == IsXExtensionPointer) {
break;
}
}
if (ctr == ndevices_return) {
XFreeDeviceList(xdev);
return true;
}
dev = XOpenDevice(GDK_WINDOW_XDISPLAY(rootwin), xdev[ctr].id);
XFreeDeviceList(xdev);
if (! dev) {
return true;
}
CurrentOpenMouse = config["MouseDeviceName"].toString();
}
xds = (XDeviceState *)XQueryDeviceState(GDK_WINDOW_XDISPLAY(rootwin), dev);
for (ctr = 0, xic = xds->data; ctr < xds->num_classes; ctr++, xic += xic->length/2) {
if (xic->c_class == ButtonClass) {
xbs = (XButtonState*) xic;
}
}
if (!xbs) {
return true;
}
state = state << bit;
/* debug mouse state buttons
for (int ctr = 1; ctr < 10; ctr++) {
int byte = ctr / 8;
int bit = ctr % 8;
state = 1 << bit;
fprintf(stderr, "(%d/%d)", byte, bit);
fprintf(stderr, "b%d: %d -- ", ctr, (xbs->buttons[byte] & state) >> bit);
}
fprintf(stderr, "\n");
*/
if ((xbs->buttons[byte] & state) >> bit) {
ptt_on = true;
}
XFreeDeviceState(xds);
if (ptt_on) {
isTransmittingMouse = true;
startTransmit();
} else {
isTransmittingMouse = false;
if (! isTransmittingButton && ! isTransmittingKey && ! isTransmittingVA) {
stopTransmit();
}
}
return(true);
}/*}}}*/
bool Mangler::updateXferAmounts(void) {/*{{{*/
double bytes;
uint32_t packets;
char buf[1024];
builder->get_widget("sentLabel", label);
bytes = v3_get_bytes_sent();
packets = v3_get_packets_sent();
if (bytes > 1024*1024) {
snprintf(buf, 1023, "%2.2f megabytes / %u packets", bytes/1024/1024, packets);
} else if (bytes > 1024) {
snprintf(buf, 1023, "%2.2f kilobytes / %u packets", bytes/1024, packets);
} else if (bytes) {
snprintf(buf, 1023, "%.0f bytes / %u packets", bytes, packets);
} else {
snprintf(buf, 1023, "N/A");
}
label->set_text(buf);
builder->get_widget("recvLabel", label);
bytes = v3_get_bytes_recv();
packets = v3_get_packets_recv();
if (bytes > 1024*1024) {
snprintf(buf, 1023, "%2.2f megabytes / %u packets", bytes/1024/1024, packets);
} else if (bytes > 1024) {
snprintf(buf, 1023, "%2.2f kilobytes / %u packets", bytes/1024, packets);
} else if (bytes) {
snprintf(buf, 1023, "%.0f bytes / %u packets", bytes, packets);
} else {
snprintf(buf, 1023, "N/A");
}
label->set_text(buf);
return(true);
}/*}}}*/
/* {{{ GdkFilterReturn ptt_filter(GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) {
GdkWindow *rootwin = gdk_get_default_root_window();
XEvent *xevent = (XEvent *)gdk_xevent;
if (! mangler) {
return GDK_FILTER_CONTINUE;
}
if (! mangler->settings->config.PushToTalkMouseEnabled) {
mangler->isTransmittingKey = false;
return GDK_FILTER_CONTINUE;
}
if (xevent->type == ButtonPress && !mangler->isTransmittingMouse && xevent->xbutton.button == mangler->settings->config.PushToTalkMouseValueInt) {
fprintf(stderr, "press\n");
mangler->startTransmit();
mangler->isTransmittingMouse = true;
fprintf(stderr, "allow\n");
XAllowEvents(GDK_WINDOW_XDISPLAY(rootwin), AsyncPointer, CurrentTime);
fprintf(stderr, "ungrab pointer\n");
XUngrabPointer(GDK_WINDOW_XDISPLAY(rootwin), CurrentTime);
fprintf(stderr, "ungrab button\n");
XUngrabButton(GDK_WINDOW_XDISPLAY(rootwin), mangler->settings->config.PushToTalkMouseValueInt, AnyModifier, GDK_ROOT_WINDOW());
fprintf(stderr, "grab button\n");
XGrabButton(GDK_WINDOW_XDISPLAY(rootwin), mangler->settings->config.PushToTalkMouseValueInt, AnyModifier, GDK_ROOT_WINDOW(), True, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None);
return GDK_FILTER_CONTINUE;
} else if ((xevent->type == ButtonRelease) && (xevent->xbutton.button == mangler->settings->config.PushToTalkMouseValueInt)) {
fprintf(stderr, "release\n");
mangler->stopTransmit();
mangler->isTransmittingMouse = false;
fprintf(stderr, "allow\n");
XAllowEvents(GDK_WINDOW_XDISPLAY(rootwin), AsyncPointer, CurrentTime);
fprintf(stderr, "ungrab pointer\n");
XUngrabPointer(GDK_WINDOW_XDISPLAY(rootwin), CurrentTime);
fprintf(stderr, "ungrab button\n");
XUngrabButton(GDK_WINDOW_XDISPLAY(rootwin), mangler->settings->config.PushToTalkMouseValueInt, AnyModifier, GDK_ROOT_WINDOW());
fprintf(stderr, "grab button\n");
XGrabButton(GDK_WINDOW_XDISPLAY(rootwin), mangler->settings->config.PushToTalkMouseValueInt, AnyModifier, GDK_ROOT_WINDOW(), True, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None);
return GDK_FILTER_CONTINUE;
}
return GDK_FILTER_CONTINUE;
} }}} */
bool Mangler::updateIntegration(void) {/*{{{*/
if (v3_is_loggedin()) {
if (! config["AudioIntegrationEnabled"].toBool() || integration->client == MusicClient_None) {
if (integration_text != "") {
integration_text = "";
v3_set_text(
(char *)ustring_to_c(comment).c_str(),
(char *)ustring_to_c(url).c_str(),
(char *)"",
true);
}
return true;
}
Glib::ustring formatted_text = integration->format();
switch (integration->get_mode()) {
// Polling (mpd)
case 0:
if ( ((integration->update(false) || !integration->first()) ) || integration_text != formatted_text ) {
integration_text = integration->format();
v3_set_text(
(char *)ustring_to_c(comment).c_str(),
(char *)ustring_to_c(url).c_str(),
(char *)ustring_to_c(integration_text).c_str(),
true);
}
break;
// Listening / callbacks (dbus ones)
case 1:
if (integration_text != formatted_text) {
integration_text = formatted_text;
v3_set_text(
(char *)ustring_to_c(comment).c_str(),
(char *)ustring_to_c(url).c_str(),
(char *)ustring_to_c(integration_text).c_str(),
true);
}
break;
}
}
return true;
}/*}}}*/
Glib::ustring Mangler::getPasswordEntry(Glib::ustring title, Glib::ustring prompt) {/*{{{*/
password = "";
passwordEntry->set_text("");
passwordDialog->set_keep_above(true);
passwordDialog->set_title(title);
passwordDialog->run();
passwordDialog->hide();
return(password);
}/*}}}*/
void Mangler::passwordDialogOkButton_clicked_cb(void) {/*{{{*/
password = passwordEntry->get_text();
}/*}}}*/
void Mangler::passwordDialogCancelButton_clicked_cb(void) {/*{{{*/
password = "";
}/*}}}*/
bool Mangler::getReasonEntry(Glib::ustring title, Glib::ustring prompt) {/*{{{*/
reason = "";
reasonValid = false;
reasonEntry->set_text("");
reasonDialog->set_keep_above(true);
reasonDialog->set_title(title);
reasonDialog->run();
reasonDialog->hide();
return reasonValid;
}/*}}}*/
void Mangler::reasonDialogOkButton_clicked_cb(void) {/*{{{*/
reason = reasonEntry->get_text();
reasonValid = true;
}/*}}}*/
void Mangler::reasonDialogCancelButton_clicked_cb(void) {/*{{{*/
reasonValid = false;
}/*}}}*/
void Mangler::textStringChangeDialogOkButton_clicked_cb(void) {/*{{{*/
comment = textStringChangeCommentEntry->get_text();
url = textStringChangeURLEntry->get_text();
integration_text = textStringChangeIntegrationEntry->get_text();
if (! connectedServerName.empty()) {
if (config.servers[connectedServerName]["PersistentComments"].toBool()) {
config.servers[connectedServerName]["Comment"] = comment;
config.servers.save();
}
}
v3_set_text(
(char *)ustring_to_c(comment).c_str(),
(char *)ustring_to_c(url).c_str(),
(char *)ustring_to_c(integration_text).c_str(),
textStringSilenceCommentCheckButton->get_active());
}/*}}}*/
void Mangler::textStringChangeDialogCancelButton_clicked_cb(void) {/*{{{*/
textStringChangeCommentEntry->set_text(comment);
textStringChangeURLEntry->set_text(url);
textStringChangeIntegrationEntry->set_text(integration_text);
}/*}}}*/
// Misc Functions
uint32_t Mangler::getActiveServer(void) {/*{{{*/
builder->get_widget("serverSelectComboBox", combobox);
return combobox->get_active_row_number();
}/*}}}*/
void Mangler::setActiveServer(uint32_t row_number) {/*{{{*/
builder->get_widget("serverSelectComboBox", combobox);
combobox->set_active(row_number);
}/*}}}*/
void Mangler::errorDialog(Glib::ustring message) {/*{{{*/
builder->get_widget("errorWindow", window);
window->set_keep_above(true);
window->set_icon(icons["tray_icon"]);
builder->get_widget("errorMessageLabel", label);
label->set_text(message);
window->show();
/*
builder->get_widget("errorDialog", msgdialog);
msgdialog->set_icon(icons["tray_icon"]);
msgdialog->set_keep_above(true);
msgdialog->set_message(message);
msgdialog->run();
msgdialog->hide();
*/
}/*}}}*/
void Mangler::errorOKButton_clicked_cb(void) {/*{{{*/
builder->get_widget("errorWindow", window);
window->hide();
}/*}}}*/
ManglerError::ManglerError(uint32_t code, Glib::ustring message, Glib::ustring module) {/*{{{*/
this->code = code;
this->message = message;
this->module = module;
}/*}}}*/
std::string Mangler::stripMotdRtf(const char *input) {/*{{{*/
// commentary by x87bliss (Russ Kubes) for your reading pleasure
std::string motd; // I use std::string since the RTF in the MOTDs is not unicode
if (!input || strlen(input) < 5 || memcmp("{\\rtf", input, 5) != 0) { // if the motd is not RTF
motd = input;
return motd;
}
// Note this function only strips RTF markup to make RTF MOTDs human readable. It doesn't support any advanced interpretation of RTF markup.
size_t inputlen = strlen(input);
motd.reserve(inputlen); // allocate enough space, the resulting string will be smaller than input length
// rtf_status is what is the loop looking at, i.e. do we need to interpret controls
enum {RTF_TEXT, RTF_CONTROL, RTF_CONTROL_END, RTF_IGNORE, RTF_BIN} rtf_status = RTF_TEXT;
size_t ignorecount; // number of closing braces to ignore for "\*"
size_t controlpos; // stores the position of the first character of a control word
for (size_t pos = 0; pos < inputlen; ++pos) {
if (rtf_status == RTF_IGNORE) {
if (inputlen - pos > 4 && memcmp("\\bin", input + pos, 4) == 0 && input[pos + 4] >= '0' && input[pos + 4] <= '9') {
// Encountered some binary data that we have to ignore. We have to ignore this a special way since the data may contain special characters
pos += 4;
size_t binskip = 0; // how much data to skip
for (; input[pos] >= '0' && input[pos] <= '9'; ++pos) {
binskip = (binskip * 10) + (input[pos] - '0');
}
pos += binskip;
continue; // every \binN has a space between it and its binary data, continue skips that too.
}
if (input[pos] == '{') {
++ignorecount;
}
if (input[pos] == '}' && --ignorecount == 0) {
rtf_status = RTF_TEXT; // we can stop ignoring now
}
continue; // still ignoring, or ignore the ending brace;
}
if (rtf_status == RTF_CONTROL) {
if ((input[pos] >= 'a' && input[pos] <= 'z') || // valid characters in a control word are a-z, A-Z, 0-9, and - (for negative numbers)
(input[pos] >= 'A' && input[pos] <= 'Z') ||
(input[pos] >= '0' && input[pos] <= '9') ||
(input[pos] == '\'' && pos == controlpos) || // apostrophe is valid, but only as the first character of a control word
input[pos] == '-') {
continue;
} else {
rtf_status = RTF_CONTROL_END; // Any control word has ended
}
}
if (rtf_status == RTF_CONTROL_END) {
// note all RTF control words are case-sensitive. So memcmp can be used instead of stricmp
size_t controllen = pos - controlpos;
// check all exact matches first
if (controllen == 3 && input[controlpos] == '\'') { // 8-bit hex character
char hh = -1, h; // hh is the full interpreted hex character, h is a working value for each hex digit
h = input[controlpos + 1];
if (h >= '0' && h <= '9') {
hh = h - '0'; // temporarily store the value and check for errors in the second digit
} else if (h >= 'a' && h <= 'f') {
hh = h - 'a' + 10;
} else if (h >= 'A' && h <= 'F') {
hh = h - 'A' + 10; // technically only lowercase letters are supported by RTF
}
h = input[controlpos + 2];
if (h >= '0' && h <= '9') {
h = h - '0'; // temporarily store the value
} else if (h >= 'a' && h <= 'f') {
h = h - 'a' + 10;
} else if (h >= 'A' && h <= 'F') {
h = h - 'A' + 10; // technically only lowercase letters are supported by RTF
} else {
h = -1;
}
if (hh >= 0 && h >= 0) { // both hex digits were valid
hh = (hh * 16) + h; // convert each value to the whole character
motd.append(1, hh);
}
} else if (controllen == 3 && memcmp("tab", input + controlpos, 3) == 0) {
motd.append(1, '\t'); // in case Mangler's motd window doesn't properly handle tabs, this can be changed to (4, ' ') for 4 spaces
} else if (controllen == 3 && memcmp("par", input + controlpos, 3) == 0) {
motd.append("\r\n");
} else if (controllen == 4 && (memcmp("line", input + controlpos, 4) == 0 ||
memcmp("page", input + controlpos, 4) == 0 || memcmp("sect", input + controlpos, 4) == 0)) {
motd.append("\r\n");
} else if (controllen == 7 && (memcmp("fonttbl", input + controlpos, 7) == 0 || // nonvisible formatting table
memcmp("filetbl", input + controlpos, 4) == 0)) { // sub-documents table
ignorecount = 1;
rtf_status = RTF_IGNORE; // we're going to ignore a table
} else if (controllen == 8 && memcmp("colortbl", input + controlpos, 8) == 0) { // nonvisible formatting table
ignorecount = 1;
rtf_status = RTF_IGNORE; // we're going to ignore a table
} else if (controllen == 10 && (memcmp("stylesheet", input + controlpos, 10) == 0 || // nonvisible formatting table
memcmp("listtables", input + controlpos, 10) == 0)) { // stores formatting information for lists
ignorecount = 1;
rtf_status = RTF_IGNORE; // we're going to ignore a table
} else if (controllen == 6 && memcmp("revtbl", input + controlpos, 6) == 0) { // This table contains nonvisible information about revisions
ignorecount = 1;
rtf_status = RTF_IGNORE; // we're going to ignore a table
} else if (controllen == 4 && memcmp("info", input + controlpos, 4) == 0) { // The info table contains nonvisible metadata, like author etc..
ignorecount = 1;
rtf_status = RTF_IGNORE; // we're going to ignore a table
} else if (controllen > 3 && memcmp("bin", input + controlpos, 3) == 0 && input[controlpos + 3] >= '0' && input[controlpos + 3] <= '9') {
// Now start to look for partial matches
// We encountered some binary data that we need to skip
size_t binskip = 0; // how much data to skip
for (size_t bpos = controlpos + 3; input[bpos] >= '0' && input [bpos] <= '9'; ++bpos) {
binskip = (binskip * 10) + (input[bpos] - '0');
}
pos += binskip;
rtf_status = RTF_TEXT;
continue;
}
if (input[pos] != ' ') {
--pos; // we need to reevaluate the delimiter if it wasn't a space
}
if (rtf_status != RTF_IGNORE) {
rtf_status = RTF_TEXT;
}
continue;
}
if (rtf_status == RTF_TEXT) {
if (input[pos] == '\\') {
// First check for "\{", "\}", "\\" and "\*", which each would break this function if not explicitly looked for right away
if (inputlen - pos < 2) {
break; // we need at least 2 characters, probably a malformed RTF message.
}
++pos; // increment it here once, so we don't have to add one for each check
if (input[pos] == '{' || input[pos] == '}' || input[pos] == '\\') {
motd.append(1, input[pos]);
continue;
} else if (input[pos] == '*') {
ignorecount = 1;
rtf_status = RTF_IGNORE;
continue;
} else {
controlpos = pos--; // the control word starts at the incremented pos, but we need to decrement it again to examine it
rtf_status = RTF_CONTROL;
continue;
}
} else if (input[pos] != '{' && input[pos] != '}' && // we're just ignoring groups since we're not formatting
input[pos] != '\r' && input[pos] != '\n') { // ignore CRs and LFs in the RTF text
motd.append(1, input[pos]);
}
}
}
return motd;
}/*}}}*/
int
main(int argc, char **argv) {
Gtk::Main kit(argc, argv);
struct _cli_options options = { 0 };
char *locale;
extern char *optarg;
int opt;
while ((opt = getopt(argc, argv, "hd:s:u:p:")) != -1) {
switch (opt) {
case 'h':
fprintf(stderr, "%s: optional arguments: -d .ui -s hostname:port -u username -p password\n", argv[0]);
exit(EXIT_FAILURE);
case 'd':
options.uifilename = c_to_ustring(optarg);
options.uifromfile = true;
fprintf(stderr, "using ui definition file: %s\n", optarg);
break;
case 's':
options.qc_server = c_to_ustring(optarg);
break;
case 'u':
options.qc_username = c_to_ustring(optarg);
break;
case 'p':
options.qc_password = c_to_ustring(optarg);
break;
}
}
if (!(locale = setlocale(LC_ALL, ""))) {
fprintf(stderr, "Can't set the specified locale! " "Check LANG, LC_CTYPE, LC_ALL.\n");
exit(EXIT_FAILURE);
}
//fprintf(stderr, "initialized locale: %s\n", locale);
if (!Glib::thread_supported()) {
Glib::thread_init();
}
gdk_threads_init();
mangler = new Mangler(&options);
gdk_threads_enter();
Gtk::Main::run(*mangler->manglerWindow);
gdk_threads_leave();
delete mangler;
exit(EXIT_SUCCESS);
return 0;
}