diff --git a/Makefile b/Makefile index a035b8fb96f137968d7f72f5bf3a1fc17e3ca5a3..65e18052cc53f4056cc593ddd33fbf35936b58e4 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # Author: Laurielle Lea (laurielle.lea@savoirfairelinux.com) # all: - cd gsm ; make + cd src/audio/gsm ; make cd src; make install: @@ -15,5 +15,5 @@ uninstall: clean: cd src; make clean - cd gsm ; make clean + cd src/audio/gsm ; make clean diff --git a/src/Makefile b/src/Makefile index 0e2064f80c92c721c75c3a8cc45c5685b7deee57..42d4337ededdf5d20946357dcf4efe777b9940cd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,59 +17,73 @@ SKINS = skins RINGS = rings METAL_SKINS = metal -ifdef ALSA -endif - -INCPATH = -I. -I$(QTDIR)/include -I$(CCPPDIR)/include/cc++2 -I$(CCRTPDIR)/include -I$(OSIPDIR)/include -I$(EXOPSIP)/include -I/usr/include -I../gsm +INCPATH = -I. -I$(QTDIR)/include -I$(CCPPDIR)/include/cc++2 -I$(CCRTPDIR)/include -I$(OSIPDIR)/include -I$(EXOPSIP)/include -I/usr/include -Iaudio/gsm CXXFLAGS = -pipe -Wall -W -g -pipe -march=i386 -mcpu=i686 -DQT_NO_DEBUG -DQT_SHARED -DQT_THREAD_SUPPORT -fpermissive -Wno-deprecated $(INCPATH) LIBS=-L$(QTDIR)/lib -L/usr/X11R6/lib -lqt-mt -lXext -lX11 -lm -L/opt/lib -losip2 -leXosip -lccrtp1 -lasound `$(CCPPDIR)/bin/ccgnu2-config --flags --stdlibs` -EXTRALIBS=../gsm/libgsm.a +EXTRALIBS=audio/gsm/libgsm.a CONFIGURE_CONF=$(shell ls ../configure.conf) OBJS = \ - audiobuffer.o \ - audiocodec.o \ - audiodrivers.o \ - audiodriversoss.o \ - audiodriversalsa.o \ - audiortp.o \ - configuration.o \ + audio/audiobuffer.o \ + audio/audiocodec.o \ + audio/codecDescriptor.o \ + audio/audiodrivers.o \ + audio/audiodriversalsa.o \ + audio/audiodriversoss.o \ + audio/audiortp.o \ + audio/dtmf.o \ + audio/dtmfgenerator.o \ + audio/tonegenerator.o \ + audio/g711.o \ + audio/gsmcodec.o \ + audio/alaw.o \ + audio/ulaw.o \ + call.o \ configitem.o \ + configuration.o \ configurationtree.o \ - dtmf.o \ - dtmfgenerator.o \ error.o \ - g711.o \ + eventthread.o \ + gui/guiframework.o \ + gui/qt/mydisplay.o \ + gui/qt/numerickeypadtools.o \ + gui/qt/phoneline.o \ + gui/qt/point.o \ + gui/qt/transqwidget.o \ + gui/qt/trayicon.o \ + gui/qt/trayicon.moc.o \ + gui/qt/trayicon_x11.o \ + gui/qt/vector.o \ + gui/qt/volumecontrol.o \ + gui/qt/volumecontrol.moc.o \ + gui/qt/jpushbutton.o \ + gui/qt/jpushbutton.moc.o \ + gui/qt/numerickeypad.o \ + gui/qt/numerickeypad.moc.o \ + gui/qt/qjlistboxpixmap.o \ + gui/qt/qtGUImainwindow.o \ + gui/qt/qtGUImainwindow.moc.o \ + gui/qt/configurationpanelui.o \ + gui/qt/configurationpanelui.moc.o \ + gui/qt/url_inputui.o \ + gui/qt/url_inputui.moc.o \ main.o \ manager.o \ - mydisplay.o \ - numerickeypadtools.o \ - phoneline.o \ - point.o \ - qjlistboxpixmap.o \ - sip.o \ sipcall.o \ + sipvoiplink.o \ skin.o \ - url_inputui.o url_inputui.moc.o \ - phonebookui.o phonebookui.moc.o \ - configurationpanelui.o configurationpanelui.moc.o \ - tonegenerator.o \ - transqwidget.o \ - trayicon.o trayicon.moc.o trayicon_x11.o \ - jpushbutton.o jpushbutton.moc.o \ - numerickeypad.o numerickeypad.moc.o \ - qtGUImainwindow.o qtGUImainwindow.moc.o \ - vector.o \ - volumecontrol.o volumecontrol.moc.o \ - stun.o udp.o + voIPLink.o \ + stun.o \ + udp.o + start: check prereq all -#start: check all +#start:check all check: ifeq ($(CONFIGURE_CONF),../configure.conf) @@ -81,14 +95,14 @@ endif .cpp.o: $(CXX) $(DEFVARS) $(CXXFLAGS) -c -o $@ $< -prereq: phonebook.ui url_input.ui configurationpanel.ui +prereq: gui/qt/phonebook.ui gui/qt/url_input.ui gui/qt/configurationpanel.ui @echo "Making User Interface files..." - $(UIC) -o phonebookui.h phonebook.ui - $(UIC) -o phonebookui.cpp -impl phonebookui.h phonebook.ui - $(UIC) -o url_inputui.h url_input.ui - $(UIC) -o url_inputui.cpp -impl url_inputui.h url_input.ui - $(UIC) -o configurationpanelui.h configurationpanel.ui - $(UIC) -o configurationpanelui.cpp -impl configurationpanelui.h configurationpanel.ui + $(UIC) -o gui/qt/phonebookui.h gui/qt/phonebook.ui + $(UIC) -o gui/qt/phonebookui.cpp -impl gui/qt/phonebookui.h gui/qt/phonebook.ui + $(UIC) -o gui/qt/url_inputui.h gui/qt/url_input.ui + $(UIC) -o gui/qt/url_inputui.cpp -impl gui/qt/url_inputui.h gui/qt/url_input.ui + $(UIC) -o gui/qt/configurationpanelui.h gui/qt/configurationpanel.ui + $(UIC) -o gui/qt/configurationpanelui.cpp -impl gui/qt/configurationpanelui.h gui/qt/configurationpanel.ui %.moc.cpp: %.h $(MOC) -o $@ $< @@ -106,21 +120,21 @@ all: $(PROGNAME) $(PROGNAME): $(OBJS) $(EXTRALIBS) $(CXX) -o $@ $(OBJS) $(LIBS) $(EXTRALIBS) -install: all - mkdir -p $(BIN_DIR) - mkdir -p $(SHARE_DIR)/$(PROGNAME) - install --mode=0755 $(PROGNAME) $(BIN_DIR) - cd ..; cp -R $(PIXMAPS) $(SHARE_DIR)/$(PROGNAME)/ ; \ - chmod -R a+rX $(SHARE_DIR)/$(PROGNAME)/$(PIXMAPS) - cd ..; cp -R $(SKINS) $(SHARE_DIR)/$(PROGNAME)/ ; \ - chmod -R a+rX $(SHARE_DIR)/$(PROGNAME)/$(SKINS) - cd ..; cp -R $(RINGS) $(SHARE_DIR)/$(PROGNAME)/ ; \ - chmod -R a+rX $(SHARE_DIR)/$(PROGNAME)/$(RINGS) - -uninstall: - rm -f $(BIN_DIR)/$(PROGNAME) - rm -rf $(SHARE_DIR)/$(PROGNAME) +#install: all +# mkdir -p $(BIN_DIR) +# mkdir -p $(SHARE_DIR)/$(PROGNAME) +# install --mode=0755 $(PROGNAME) $(BIN_DIR) +# cd ..; cp -R $(PIXMAPS) $(SHARE_DIR)/$(PROGNAME)/ ; \ +# chmod -R a+rX $(SHARE_DIR)/$(PROGNAME)/$(PIXMAPS) +# cd ..; cp -R $(SKINS) $(SHARE_DIR)/$(PROGNAME)/ ; \ +# chmod -R a+rX $(SHARE_DIR)/$(PROGNAME)/$(SKINS) +# cd ..; cp -R $(RINGS) $(SHARE_DIR)/$(PROGNAME)/ ; \ +# chmod -R a+rX $(SHARE_DIR)/$(PROGNAME)/$(RINGS) +# +#uninstall: +# rm -f $(BIN_DIR)/$(PROGNAME) +# rm -rf $(SHARE_DIR)/$(PROGNAME) clean: - rm -f $(PROGNAME) *.o *.a *~ *.moc.cpp *.bak core.* + rm -f $(PROGNAME) *.o audio/*.o audio/gsm/*.o gui/*.o gui/qt/*.o audio/gsm/*.a *~ *.moc.cpp *.bak core.* diff --git a/src/audio/alaw.cpp b/src/audio/alaw.cpp new file mode 100644 index 0000000000000000000000000000000000000000..041ee3337f86f22056eb7ebb5ef3911495de306a --- /dev/null +++ b/src/audio/alaw.cpp @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "g711.h" +#include "alaw.h" + +Alaw::Alaw(int payload, const string& codec) : AudioCodec(payload, codec) +{ + _codecName = codec; + _payload = payload; +} + +Alaw::~Alaw (void) +{ +} + +int +Alaw::codecDecode (short *dst, unsigned char *src, unsigned int size) +{ + return G711::ALawDecode (dst, src, size); +} + +int +Alaw::codecEncode (unsigned char *dst, short *src, unsigned int size) +{ + return G711::ALawEncode (dst, src, size); +} + diff --git a/src/audio/alaw.h b/src/audio/alaw.h new file mode 100644 index 0000000000000000000000000000000000000000..27a6038670cac02314fd47b321c8035aec352949 --- /dev/null +++ b/src/audio/alaw.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ALAW_H__ +#define __ALAW_H__ + +#include "audiocodec.h" + +class Alaw : public AudioCodec { +public: + Alaw (int payload, const string& codec); + ~Alaw (void); + + int codecDecode (short *, unsigned char *, unsigned int); + int codecEncode (unsigned char *, short *, unsigned int); + +private: + string _codecName; + int _payload; +}; + +#endif // __ULAW_H__ diff --git a/src/audiobuffer.cpp b/src/audio/audiobuffer.cpp similarity index 95% rename from src/audiobuffer.cpp rename to src/audio/audiobuffer.cpp index c85a0d59dac28dab6c7be4a4605d85440d501fc9..54ed0003db92be1874059b9f12bb601adfaba632 100644 --- a/src/audiobuffer.cpp +++ b/src/audio/audiobuffer.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (c) 2000 Billy Biggs <bbiggs@div8.net> @@ -21,7 +21,7 @@ */ #include "audiobuffer.h" -#include "global.h" +#include "../global.h" #include <string.h> diff --git a/src/audiobuffer.h b/src/audio/audiobuffer.h similarity index 97% rename from src/audiobuffer.h rename to src/audio/audiobuffer.h index c6607a2eed548a032b6496de6f5a5d1a602d7593..386c774403aa1aba44c9277e3ce1ce78cdc9c64b 100644 --- a/src/audiobuffer.h +++ b/src/audio/audiobuffer.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (c) 2000 Billy Biggs <bbiggs@div8.net> diff --git a/src/audio/audiocodec.cpp b/src/audio/audiocodec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..492c492307931e8dde3f5a571791cb481ab71062 --- /dev/null +++ b/src/audio/audiocodec.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This 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 2, + * or (at your option) any later version. + * + * This 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 dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <endian.h> +#include <string.h> +#include <iostream> +#include <string> + +#include "audiocodec.h" +#include "../configuration.h" + +using namespace std; + + +AudioCodec::AudioCodec (int payload, const string& codec) { + _codecName = codec; + _payload = payload; +} + +AudioCodec::~AudioCodec (void) { +} + +void +AudioCodec::setCodecName (const string& codec) +{ + _codecName = codec; +} + +string +AudioCodec::getCodecName (void) +{ + return _codecName; +} + + + + diff --git a/src/audio/audiocodec.h b/src/audio/audiocodec.h new file mode 100644 index 0000000000000000000000000000000000000000..023878b5641dd66080713d7422011ae37e4debf6 --- /dev/null +++ b/src/audio/audiocodec.h @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CODEC_AUDIO_H__ +#define __CODEC_AUDIO_H__ + + +#include <string> + +using namespace std; + + + +class AudioCodec { +public: + AudioCodec(int payload, const string& codec); + virtual ~AudioCodec (void); + + virtual int codecDecode (short *, unsigned char *, unsigned int) = 0; + virtual int codecEncode (unsigned char *, short *, unsigned int) = 0; + + void setCodecName (const string& codec); + string getCodecName (void); + +private: + string _codecName; + int _payload; +}; + +#endif // __CODEC_AUDIO_H__ diff --git a/src/audiodrivers.cpp b/src/audio/audiodrivers.cpp similarity index 96% rename from src/audiodrivers.cpp rename to src/audio/audiodrivers.cpp index c104564e8577cbe101cc3bec294cc8b3df48faf7..30509e3f8601b11797d861115cf38275d15ced18 100644 --- a/src/audiodrivers.cpp +++ b/src/audio/audiodrivers.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (c) 2000 Billy Biggs <bbiggs@div8.net> diff --git a/src/audiodrivers.h b/src/audio/audiodrivers.h similarity index 97% rename from src/audiodrivers.h rename to src/audio/audiodrivers.h index 878ed5c5a32ac80b800cbcd242f04ae139200dcf..bee6e7eb52125b42b0802ebb5913a24490dbaa2e 100644 --- a/src/audiodrivers.h +++ b/src/audio/audiodrivers.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (c) 2000 Billy Biggs <bbiggs@div8.net> diff --git a/src/audiodriversalsa.cpp b/src/audio/audiodriversalsa.cpp similarity index 98% rename from src/audiodriversalsa.cpp rename to src/audio/audiodriversalsa.cpp index 812df6cc26f022c98da83bf4c8a79d1a80de38d4..fffe5cf9aabb8c6f3c1420edc0f9363f6e29ac2d 100644 --- a/src/audiodriversalsa.cpp +++ b/src/audio/audiodriversalsa.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Jerome Oufella <jerome.oufella@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -31,7 +31,7 @@ #include <errno.h> #include "audiodriversalsa.h" -#include "global.h" +#include "../global.h" #define ALSA_DEVICE "plughw:0,0" diff --git a/src/audiodriversalsa.h b/src/audio/audiodriversalsa.h similarity index 96% rename from src/audiodriversalsa.h rename to src/audio/audiodriversalsa.h index f2b857297d815e01b56f9eba54a5d89e455650d5..c337b3a4b3e87686ed1a01b9a8f10d9367ee9b82 100644 --- a/src/audiodriversalsa.h +++ b/src/audio/audiodriversalsa.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Jerome Oufella <jerome.oufella@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -23,7 +23,7 @@ #include <alsa/asoundlib.h> #include "audiodrivers.h" -#include "error.h" +#include "../error.h" /** diff --git a/src/audiodriversoss.cpp b/src/audio/audiodriversoss.cpp similarity index 99% rename from src/audiodriversoss.cpp rename to src/audio/audiodriversoss.cpp index 27b156104d1f36aa48bb36e4dd64c6264f57f219..c00b85cf6a2f0190028c40855509802f952d78d6 100644 --- a/src/audiodriversoss.cpp +++ b/src/audio/audiodriversoss.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (c) 2000 Billy Biggs <bbiggs@div8.net> @@ -32,7 +32,7 @@ #include <stdio.h> #include "audiodriversoss.h" -#include "global.h" +#include "../global.h" AudioDriversOSS::AudioDriversOSS (DeviceMode mode, Error *error) : diff --git a/src/audiodriversoss.h b/src/audio/audiodriversoss.h similarity index 95% rename from src/audiodriversoss.h rename to src/audio/audiodriversoss.h index 622ee6961e0ce863b5d6163e5d1921e954e1e5e8..7956793c41d24369268c49b942ae23e20db43329 100644 --- a/src/audiodriversoss.h +++ b/src/audio/audiodriversoss.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (c) 2000 Billy Biggs <bbiggs@div8.net> @@ -25,7 +25,7 @@ #include "audiodrivers.h" -#include "error.h" +#include "../error.h" // TODO : a mettre dans config #define AUDIO_DEVICE "/dev/dsp" diff --git a/src/audio/audiortp.cpp b/src/audio/audiortp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..896535a90fa8ecfdd6a3a88aaebdc9c9c7508f27 --- /dev/null +++ b/src/audio/audiortp.cpp @@ -0,0 +1,338 @@ +/** + * Copyright (C) 2004 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <cstdio> +#include <cstdlib> +#include <ccrtp/rtp.h> +#include <assert.h> +#include <iostream> +#include <string> + +#include "audiortp.h" +#include "../configuration.h" +#include "../manager.h" +#include "../user_cfg.h" +#include "../sipcall.h" +#include "../stund/stun.h" + +using namespace ost; +using namespace std; + + +//////////////////////////////////////////////////////////////////////////////// +// AudioRtp +//////////////////////////////////////////////////////////////////////////////// +AudioRtp::AudioRtp (Manager *manager) { + string svr; + + _manager = manager; + _RTXThread = NULL; + + if (!manager->useStun()) { + if (get_config_fields_str(SIGNALISATION, PROXY).empty()) { + svr = get_config_fields_str(SIGNALISATION, PROXY); + } + } else { + svr = get_config_fields_str(SIGNALISATION, HOST_PART); + } +} + +AudioRtp::~AudioRtp (void) { +} + +int +AudioRtp::createNewSession (SipCall *ca) { + // Start RTP Send/Receive threads + ca->enable_audio = 1; + if (!_manager->useStun()) { + _symetric = false; + } else { + _symetric = true; + } + +#ifdef ALSA + if (_manager->useAlsa()) { + _RTXThread = new AudioRtpRTX (ca, _manager->audiodriver, + _manager->audiodriverReadAlsa, _manager, _symetric); + } +#endif + if (!_manager->useAlsa()) { + _RTXThread = new AudioRtpRTX (ca, _manager->audiodriver, NULL,_manager, + _symetric); + } + + if (_RTXThread->start() != 0) { + return -1; + } + + return 0; +} + + +void +AudioRtp::closeRtpSession (SipCall *ca) { + // This will make RTP threads finish. + ca->enable_audio = -1; + + if (_RTXThread != NULL) { + delete _RTXThread; + _RTXThread = NULL; + } + + // Flush audio read buffer + _manager->audiodriver->resetDevice(); +} + +//////////////////////////////////////////////////////////////////////////////// +// AudioRtpRTX Class // +//////////////////////////////////////////////////////////////////////////////// +AudioRtpRTX::AudioRtpRTX (SipCall *sipcall, AudioDrivers *driver, + AudioDrivers *read_driver, Manager *mngr, bool sym) { + time = new Time(); + _manager = mngr; + _ca = sipcall; + _sym =sym; + _audioDevice = driver; +#ifdef ALSA + if (_manager->useAlsa()) { + _audioDeviceRead = read_driver; + } +#endif + + // TODO: Change bind address according to user settings. + InetHostAddress local_ip("0.0.0.0"); + + if (!_sym) { + _debug("Audiortp localport : %d\n", _ca->getLocalAudioPort()); + _sessionRecv = new RTPSession (local_ip, _ca->getLocalAudioPort()); + _sessionSend = new RTPSession (local_ip); + } else { + int forcedPort = _manager->getFirewallPort(); + _session = new SymmetricRTPSession (local_ip, forcedPort); + } +} + +AudioRtpRTX::~AudioRtpRTX () { + terminate(); + + if (!_sym) { + if (_sessionRecv != NULL) { + delete _sessionRecv; + _sessionRecv = NULL; + } + if (_sessionSend != NULL) { + delete _sessionSend; + _sessionSend = NULL; + } + } else { + if (_session != NULL) { + delete _session; + _session = NULL; + } + } +} + +void +AudioRtpRTX::run (void) { + unsigned char *data_to_send; + short *data_mute; + short *data_from_mic; + short *data_from_mic_tmp; + int i, + compSize, + timestamp; + int expandedSize; + short *data_for_speakers = NULL; + int countTime = 0; + data_for_speakers = new short[2048]; + data_from_mic = new short[1024]; + data_from_mic_tmp = new short[1024]; + data_to_send = new unsigned char[1024]; + data_mute = new short[1024]; + + InetHostAddress remote_ip(_ca->getRemoteSdpAudioIp()); + + if (!remote_ip) { + _debug("RTX: IP address is not correct!\n"); + exit(); + } else { + _debug("RTX: Connected to %s : %d\n", _ca->getRemoteSdpAudioIp(), + _ca->getRemoteSdpAudioPort()); + } + + // Initialization + if (!_sym) { + _sessionRecv->setSchedulingTimeout (100000); + _sessionRecv->setExpireTimeout(1000000); + + _sessionSend->setSchedulingTimeout(10000); + _sessionSend->setExpireTimeout(1000000); + } else { + _session->setSchedulingTimeout(10000); + _session->setExpireTimeout(1000000); + } + + if (!_sym) { + if (!_sessionSend->addDestination (remote_ip, + (unsigned short) _ca->getRemoteSdpAudioPort())) { + _debug("RTX send: could not connect to port %d\n", + _ca->getRemoteSdpAudioPort()); + exit(); + } else { + _debug("RTP(Send): Added destination %s : %d\n", + remote_ip.getHostname(), + (unsigned short) _ca->getRemoteSdpAudioPort()); + } + + _sessionRecv->setPayloadFormat(StaticPayloadFormat( + (StaticPayloadType) _ca->payload)); + _sessionSend->setPayloadFormat(StaticPayloadFormat( + (StaticPayloadType) _ca->payload)); + setCancel(cancelImmediate); + _sessionSend->setMark(true); + + } else { + if (!_session->addDestination (remote_ip, + (unsigned short) _ca->getRemoteSdpAudioPort())) { + exit(); + } else { + _session->setPayloadFormat(StaticPayloadFormat( + (StaticPayloadType) _ca->payload)); + setCancel(cancelImmediate); + } + } + + timestamp = 0; + + // TODO: get frameSize from user config + int frameSize = 20; // 20ms frames + TimerPort::setTimer(frameSize); + + // start running the packet queue scheduler. + if (!_sym) { + _sessionRecv->startRunning(); + _sessionSend->startRunning(); + } else { + _session->startRunning(); + } + + while (_ca->enable_audio != -1) { + //////////////////////////// + // Send session + //////////////////////////// + int size = 320; + if (!_manager->getCall(_ca->getId())->isOnMute()) { +#ifdef ALSA + if (_manager->useAlsa()) { + i = _audioDeviceRead->readBuffer (data_from_mic, size); + } +#endif + if (!_manager->useAlsa()) { + i = _audioDevice->readBuffer (data_from_mic, size); + } + } + else { + // When IP-phone user click on mute button, we read buffer of a + // temp buffer to avoid delay in sound. +#ifdef ALSA + if (_manager->useAlsa()) + i = _audioDeviceRead->readBuffer (data_mute, size); +#endif + if (!_manager->useAlsa()) + i = _audioDevice->readBuffer (data_mute, size); + } + // TODO : return an error because no sound + if (i < 0) { + break; + } + for (int j = 0; j < i; j++) { + data_from_mic_tmp[j] = data_from_mic[j] * + _manager->getMicroVolume()/100; + } + + // Encode acquired audio sample + compSize = _ca->getAudioCodec()->codecEncode (data_to_send, + data_from_mic_tmp, + i); + // Send encoded audio sample + if (!_sym) { + _sessionSend->putData(timestamp, data_to_send, compSize); + } else { + _session->putData(timestamp, data_to_send, compSize); + } + timestamp += 160; + + //////////////////////////// + // Recv session + //////////////////////////// + const AppDataUnit* adu = NULL; + + do { + Thread::sleep(5); // in msec. + if (!_sym) { + adu = _sessionRecv->getData(_sessionRecv->getFirstTimestamp()); + } else { + adu = _session->getData(_session->getFirstTimestamp()); + } + } while (adu == NULL); + + // Decode data with relevant codec + CodecDescriptor* cd = new CodecDescriptor (adu->getType()); + + AudioCodec* ac = cd->alloc(adu->getType(), ""); + + expandedSize = ac->codecDecode (data_for_speakers, + (unsigned char*) adu->getData(), + adu->getSize()); + + // Set decoded data to sound device + _audioDevice->audio_buf.resize(expandedSize); + _audioDevice->audio_buf.setData (data_for_speakers, + _manager->getSpkrVolume()); + + // Notify (with a bip) an incoming call when there is already a call + countTime += time->getSecond(); + if (_manager->getNumberOfCalls() > 0 and _manager->getbRingtone()) { + countTime = countTime % 2000; + if (countTime < 10 and countTime > 0) { + _manager->notificationIncomingCall(); + } + } + + // Write data or notification + i = _audioDevice->writeBuffer (); + delete cd; + delete adu; + + // Let's wait for the next transmit cycle + Thread::sleep(TimerPort::getTimer()); + TimerPort::incTimer(frameSize); // 'frameSize' ms + } + +// AudioCodec::destroy(_ca->payload); + delete[] data_for_speakers; + delete[] data_from_mic; + delete[] data_from_mic_tmp; + delete[] data_mute; + delete[] data_to_send; + exit(); +} + + +// EOF diff --git a/src/audiortp.h b/src/audio/audiortp.h similarity index 82% rename from src/audiortp.h rename to src/audio/audiortp.h index 2117669d1a9662dc906e2ae1e5d4a1a0ae84065d..d4b66c46fb4425d633afe00d54e16d504e76f36d 100644 --- a/src/audiortp.h +++ b/src/audio/audiortp.h @@ -27,12 +27,12 @@ using namespace ost; -#include "sipcall.h" #define LEN_BUFFER 160 -class SIP; +class AudioDrivers; class Manager; +class SipCall; /////////////////////////////////////////////////////////////////////////////// // Two pair of sockets @@ -41,20 +41,21 @@ class AudioRtpRTX : public Thread, public TimerPort { public: AudioRtpRTX (SipCall *, AudioDrivers *, AudioDrivers *, Manager *, bool); ~AudioRtpRTX(); + Time *time; // For incoming call notification virtual void run (); private: - SipCall *ca; - AudioDrivers *audioDevice; + SipCall* _ca; + AudioDrivers* _audioDevice; #ifdef ALSA - AudioDrivers *audioDeviceRead; + AudioDrivers* _audioDeviceRead; #endif - RTPSession *sessionSend; - RTPSession *sessionRecv; - SymmetricRTPSession *session; - Manager *manager; - bool sym; + RTPSession* _sessionSend; + RTPSession* _sessionRecv; + SymmetricRTPSession* _session; + Manager* _manager; + bool _sym; }; /////////////////////////////////////////////////////////////////////////////// @@ -65,14 +66,13 @@ public: AudioRtp (Manager *); ~AudioRtp (void); - int createNewSession (SipCall *); - void closeRtpSession (SipCall *); + int createNewSession (SipCall *); + void closeRtpSession (SipCall *); private: - AudioRtpRTX *RTXThread; - SIP *sip; - Manager *manager; - bool symetric; + AudioRtpRTX* _RTXThread; + Manager* _manager; + bool _symetric; }; #endif // __AUDIO_RTP_H__ diff --git a/src/audio/codecDescriptor.cpp b/src/audio/codecDescriptor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7cc255b79279dbb38ce7cd66d40268141ed887fb --- /dev/null +++ b/src/audio/codecDescriptor.cpp @@ -0,0 +1,131 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <iostream> + +#include "audio/audiocodec.h" +#include "audio/gsmcodec.h" +#include "audio/alaw.h" +#include "audio/ulaw.h" +#include "codecDescriptor.h" + +CodecDescriptor::CodecDescriptor (int payload) +{ + _payload = payload; + _codecName = ""; +} + +CodecDescriptor::CodecDescriptor (int payload, const string& name) +{ + _payload = payload; + _codecName = name; +} + +CodecDescriptor::~CodecDescriptor (void) +{ +} + +AudioCodec* +CodecDescriptor::alloc (int payload, const string& name) +{ + switch(payload) { + case PAYLOAD_CODEC_ULAW: + return new Ulaw(payload, name); + break; + case PAYLOAD_CODEC_ALAW: + return new Alaw(payload, name); + break; + case PAYLOAD_CODEC_GSM: + return new Gsm(payload, name); + break; + default: + return NULL; + break; + } +} + +void +CodecDescriptor::setPayload (int payload) +{ + _payload =payload; +} + +int +CodecDescriptor::getPayload (void) +{ + return _payload; +} + +void +CodecDescriptor::setNameCodec (const string& name) +{ + _codecName = name; +} + +string +CodecDescriptor::getNameCodec (void) +{ + return _codecName; +} + +int +CodecDescriptor::matchPayloadCodec (const string& codecname) { + if (codecname == CODEC_ALAW) { + return PAYLOAD_CODEC_ALAW; + } else if (codecname == CODEC_ULAW) { + return PAYLOAD_CODEC_ULAW; + } else if (codecname == CODEC_GSM) { + return PAYLOAD_CODEC_GSM; + } else if (codecname == CODEC_ILBC) { + return PAYLOAD_CODEC_ILBC; + } else if (codecname == CODEC_SPEEX) { + return PAYLOAD_CODEC_SPEEX; + } else + return -1; +} + +string +CodecDescriptor::rtpmapPayload (int payload) { + switch (payload) { + case PAYLOAD_CODEC_ALAW: + return string("PCMA"); + break; + + case PAYLOAD_CODEC_ULAW: + return string("PCMU"); + break; + + case PAYLOAD_CODEC_GSM: + return string("GSM"); + break; + + case PAYLOAD_CODEC_ILBC: + return string("iLBC"); + break; + + case PAYLOAD_CODEC_SPEEX: + return string("speex"); + break; + + default: + break; + } + return ""; +} + diff --git a/src/audiocodec.h b/src/audio/codecDescriptor.h similarity index 60% rename from src/audiocodec.h rename to src/audio/codecDescriptor.h index dabae0928ecc14410ffd0e2277af1421d4dd88c2..e651f3727ccbe59addc4b2e9c01157f8f1f8fe39 100644 --- a/src/audiocodec.h +++ b/src/audio/codecDescriptor.h @@ -1,12 +1,12 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program 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 2 of the License, or * (at your option) any later version. - * + * * This program 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 @@ -17,12 +17,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef __CODEC_AUDIO_H__ -#define __CODEC_AUDIO_H__ -#include "../gsm/gsm.h" +#ifndef __CODEC_DESCRIPTOR_H__ +#define __CODEC_DESCRIPTOR_H__ #include <string> + using namespace std; typedef enum { @@ -39,27 +39,34 @@ typedef enum { #define CODEC_ILBC string("iLBC") #define CODEC_SPEEX string("SPEEX") -#define NB_CODECS 5 - -class AudioCodec { +class AudioCodec; +class CodecDescriptor +{ public: - AudioCodec (void); - ~AudioCodec (void); + CodecDescriptor (int payload); + CodecDescriptor (int payload, const string& name); + ~CodecDescriptor (void); + + AudioCodec* alloc (int payload, const string& name); - int handleCodecs[NB_CODECS]; + void setPayload (int payload); + int getPayload (void); + void setNameCodec (const string& name); + string getNameCodec (void); - int matchPayloadCodec (string); - char * rtpmapPayload (int); - void noSupportedCodec (void); - static int codecDecode (int, short *, unsigned char *, unsigned int); - static int codecEncode (int, unsigned char *, short *, unsigned int); - static void create (int); - static void destroy (int); + /* + * Match codec name to the payload + */ + int matchPayloadCodec (const string&); + /* + * Match a payload to the codec name + */ + string rtpmapPayload (int); private: - static void gsmCreate (void); - static void gsmDestroy (void); + int _payload; + string _codecName; }; -#endif // __CODEC_AUDIO_H__ +#endif // __CODEC_DESCRIPTOR_H__ diff --git a/src/audio/common.h b/src/audio/common.h new file mode 100644 index 0000000000000000000000000000000000000000..039dd9132499dc030da6fec132eca3e7ef668d95 --- /dev/null +++ b/src/audio/common.h @@ -0,0 +1,56 @@ +/* + Type definitions and helper macros which aren't part of Standard C++ + This will need to be edited on systems where 'char', 'short' and 'int' + have sizes different from 8, 16 and 32 bits. +*/ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + + +// #define DEBUG /**< Defined when compiling code for debugging */ + +/* +Basic integer types. +Note 'int' is assumed to be in 2s complement format and at least 32 bits in size +*/ +typedef unsigned char uint8; /**< An 8 bit unsigned integer */ +typedef unsigned short uint16; /**< An 16 bit unsigned integer */ +typedef unsigned int uint32; /**< An 32 bit unsigned integer */ +typedef signed char int8; /**< An 8 bit signed integer (2s complement) */ +typedef signed short int16; /**< An 16 bit signed integer (2s complement) */ +typedef signed int int32; /**< An 32 bit signed integer (2s complement) */ +typedef unsigned int uint; /**< An unsigned integer or at least 32 bits */ + + +#ifndef NULL +#define NULL 0 /**< Used to represent a null pointer type */ +#endif + + +#ifdef _MSC_VER // Compiling for Microsoft Visual C++ + +#define DEBUGGER { _asm int 3 } /**< Invoke debugger */ +#define IMPORT __declspec(dllexport) /**< Mark a function which is to be imported from a DLL */ +#define EXPORT __declspec(dllexport) /**< Mark a function to be exported from a DLL */ +#define ASSERT(c) { if(!(c)) DEBUGGER; } /**< Assert that expression 'c' is true */ + +#else // Not compiling for Microsoft Visual C++ ... + +#define DEBUGGER /**< Invoke debugger */ +#define IMPORT /**< Mark a function which is to be imported from a DLL */ +#define EXPORT /**< Mark a function to be exported from a DLL */ +//#define ASSERT(c) /**< Assert that expression 'c' is true */ + +#endif + + +#ifdef DEBUG +#define ASSERT_DEBUG(c) ASSERT(c) /**< Assert that expression 'c' is true (when compiled for debugging)*/ +#else +#define ASSERT_DEBUG(c) +#endif + + +#endif + diff --git a/src/dtmf.cpp b/src/audio/dtmf.cpp similarity index 98% rename from src/dtmf.cpp rename to src/audio/dtmf.cpp index e44f5c1b9dda19a593a271f4a78ac7f9dc54f0f5..b9859627ce28bdf0589602635833a19d7eab0fa5 100644 --- a/src/dtmf.cpp +++ b/src/audio/dtmf.cpp @@ -20,7 +20,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <qapplication.h> #include "dtmf.h" DTMF::DTMF (void) { diff --git a/src/dtmf.h b/src/audio/dtmf.h similarity index 100% rename from src/dtmf.h rename to src/audio/dtmf.h diff --git a/src/dtmfgenerator.cpp b/src/audio/dtmfgenerator.cpp similarity index 98% rename from src/dtmfgenerator.cpp rename to src/audio/dtmfgenerator.cpp index e115bdcbf68e2cb0483597d25e9dfae9642170e2..b0e095fdf2c5f0f64da4ecfcb0b9828217005353 100644 --- a/src/dtmfgenerator.cpp +++ b/src/audio/dtmfgenerator.cpp @@ -23,10 +23,8 @@ #include <math.h> -#include <qapplication.h> - #include "dtmfgenerator.h" -#include "global.h" +#include "../global.h" /* diff --git a/src/dtmfgenerator.h b/src/audio/dtmfgenerator.h similarity index 100% rename from src/dtmfgenerator.h rename to src/audio/dtmfgenerator.h diff --git a/src/g711.cpp b/src/audio/g711.cpp similarity index 100% rename from src/g711.cpp rename to src/audio/g711.cpp diff --git a/src/g711.h b/src/audio/g711.h similarity index 100% rename from src/g711.h rename to src/audio/g711.h diff --git a/gsm/COPYRIGHT b/src/audio/gsm/COPYRIGHT similarity index 100% rename from gsm/COPYRIGHT rename to src/audio/gsm/COPYRIGHT diff --git a/gsm/ChangeLog b/src/audio/gsm/ChangeLog similarity index 100% rename from gsm/ChangeLog rename to src/audio/gsm/ChangeLog diff --git a/gsm/INSTALL b/src/audio/gsm/INSTALL similarity index 100% rename from gsm/INSTALL rename to src/audio/gsm/INSTALL diff --git a/gsm/MACHINES b/src/audio/gsm/MACHINES similarity index 100% rename from gsm/MACHINES rename to src/audio/gsm/MACHINES diff --git a/gsm/MANIFEST b/src/audio/gsm/MANIFEST similarity index 100% rename from gsm/MANIFEST rename to src/audio/gsm/MANIFEST diff --git a/gsm/Makefile b/src/audio/gsm/Makefile similarity index 100% rename from gsm/Makefile rename to src/audio/gsm/Makefile diff --git a/gsm/README b/src/audio/gsm/README similarity index 100% rename from gsm/README rename to src/audio/gsm/README diff --git a/gsm/add.c b/src/audio/gsm/add.c similarity index 100% rename from gsm/add.c rename to src/audio/gsm/add.c diff --git a/gsm/code.c b/src/audio/gsm/code.c similarity index 100% rename from gsm/code.c rename to src/audio/gsm/code.c diff --git a/gsm/config.h b/src/audio/gsm/config.h similarity index 100% rename from gsm/config.h rename to src/audio/gsm/config.h diff --git a/gsm/debug.c b/src/audio/gsm/debug.c similarity index 100% rename from gsm/debug.c rename to src/audio/gsm/debug.c diff --git a/gsm/decode.c b/src/audio/gsm/decode.c similarity index 100% rename from gsm/decode.c rename to src/audio/gsm/decode.c diff --git a/gsm/gsm.h b/src/audio/gsm/gsm.h similarity index 100% rename from gsm/gsm.h rename to src/audio/gsm/gsm.h diff --git a/gsm/gsm_create.c b/src/audio/gsm/gsm_create.c similarity index 100% rename from gsm/gsm_create.c rename to src/audio/gsm/gsm_create.c diff --git a/gsm/gsm_decode.c b/src/audio/gsm/gsm_decode.c similarity index 100% rename from gsm/gsm_decode.c rename to src/audio/gsm/gsm_decode.c diff --git a/gsm/gsm_destroy.c b/src/audio/gsm/gsm_destroy.c similarity index 100% rename from gsm/gsm_destroy.c rename to src/audio/gsm/gsm_destroy.c diff --git a/gsm/gsm_encode.c b/src/audio/gsm/gsm_encode.c similarity index 100% rename from gsm/gsm_encode.c rename to src/audio/gsm/gsm_encode.c diff --git a/gsm/gsm_explode.c b/src/audio/gsm/gsm_explode.c similarity index 100% rename from gsm/gsm_explode.c rename to src/audio/gsm/gsm_explode.c diff --git a/gsm/gsm_implode.c b/src/audio/gsm/gsm_implode.c similarity index 100% rename from gsm/gsm_implode.c rename to src/audio/gsm/gsm_implode.c diff --git a/gsm/gsm_option.c b/src/audio/gsm/gsm_option.c similarity index 100% rename from gsm/gsm_option.c rename to src/audio/gsm/gsm_option.c diff --git a/gsm/gsm_print.c b/src/audio/gsm/gsm_print.c similarity index 100% rename from gsm/gsm_print.c rename to src/audio/gsm/gsm_print.c diff --git a/gsm/long_term.c b/src/audio/gsm/long_term.c similarity index 100% rename from gsm/long_term.c rename to src/audio/gsm/long_term.c diff --git a/gsm/lpc.c b/src/audio/gsm/lpc.c similarity index 100% rename from gsm/lpc.c rename to src/audio/gsm/lpc.c diff --git a/gsm/preprocess.c b/src/audio/gsm/preprocess.c similarity index 100% rename from gsm/preprocess.c rename to src/audio/gsm/preprocess.c diff --git a/gsm/private.h b/src/audio/gsm/private.h similarity index 100% rename from gsm/private.h rename to src/audio/gsm/private.h diff --git a/gsm/proto.h b/src/audio/gsm/proto.h similarity index 100% rename from gsm/proto.h rename to src/audio/gsm/proto.h diff --git a/gsm/rpe.c b/src/audio/gsm/rpe.c similarity index 100% rename from gsm/rpe.c rename to src/audio/gsm/rpe.c diff --git a/gsm/short_term.c b/src/audio/gsm/short_term.c similarity index 100% rename from gsm/short_term.c rename to src/audio/gsm/short_term.c diff --git a/gsm/table.c b/src/audio/gsm/table.c similarity index 100% rename from gsm/table.c rename to src/audio/gsm/table.c diff --git a/gsm/toast.c b/src/audio/gsm/toast.c similarity index 100% rename from gsm/toast.c rename to src/audio/gsm/toast.c diff --git a/gsm/toast.h b/src/audio/gsm/toast.h similarity index 100% rename from gsm/toast.h rename to src/audio/gsm/toast.h diff --git a/gsm/toast_alaw.c b/src/audio/gsm/toast_alaw.c similarity index 100% rename from gsm/toast_alaw.c rename to src/audio/gsm/toast_alaw.c diff --git a/gsm/toast_audio.c b/src/audio/gsm/toast_audio.c similarity index 100% rename from gsm/toast_audio.c rename to src/audio/gsm/toast_audio.c diff --git a/gsm/toast_lin.c b/src/audio/gsm/toast_lin.c similarity index 100% rename from gsm/toast_lin.c rename to src/audio/gsm/toast_lin.c diff --git a/gsm/toast_ulaw.c b/src/audio/gsm/toast_ulaw.c similarity index 100% rename from gsm/toast_ulaw.c rename to src/audio/gsm/toast_ulaw.c diff --git a/gsm/unproto.h b/src/audio/gsm/unproto.h similarity index 100% rename from gsm/unproto.h rename to src/audio/gsm/unproto.h diff --git a/src/audio/gsmcodec.cpp b/src/audio/gsmcodec.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64116b0f494d9751ea4a5cfb35c36f63de2d3037 --- /dev/null +++ b/src/audio/gsmcodec.cpp @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <iostream> +#include "gsm/gsm.h" +#include "gsmcodec.h" +#include "../global.h" + +Gsm::Gsm(int payload, const string& codec) : AudioCodec(payload, codec) +{ + _codecName = codec; + _payload = payload; + + if (!(_decode_gsmhandle = gsm_create() )) + _debug("ERROR: decode_gsm_create\n"); + if (!(_encode_gsmhandle = gsm_create() )) + _debug("AudioCodec: ERROR: encode_gsm_create\n"); +} + +Gsm::~Gsm (void) +{ + gsm_destroy(_decode_gsmhandle); + gsm_destroy(_encode_gsmhandle); + +} + +int +Gsm::codecDecode (short *dst, unsigned char *src, unsigned int size) +{ + if (gsm_decode(_decode_gsmhandle, (gsm_byte*)src, (gsm_signal*)dst) < 0) { + _debug("ERROR: gsm_decode\n"); + } + return 320; +} + +int +Gsm::codecEncode (unsigned char *dst, short *src, unsigned int size) +{ + gsm_encode(_encode_gsmhandle, (gsm_signal*)src, (gsm_byte*)dst); + return 33; +} + + diff --git a/src/audio/gsmcodec.h b/src/audio/gsmcodec.h new file mode 100644 index 0000000000000000000000000000000000000000..28fce39c3acb6895340bf001a321b7b174835c26 --- /dev/null +++ b/src/audio/gsmcodec.h @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __GSM_H__ +#define __GSM_H__ + +#include "audiocodec.h" +#include "gsm/gsm.h" + +class Gsm : public AudioCodec { +public: + Gsm (int payload, const string& codec); + ~Gsm (void); + + int codecDecode (short *, unsigned char *, unsigned int); + int codecEncode (unsigned char *, short *, unsigned int); + +private: + string _codecName; + int _payload; + gsm _decode_gsmhandle; + gsm _encode_gsmhandle; + +}; + +#endif // __ULAW_H__ diff --git a/src/tonegenerator.cpp b/src/audio/tonegenerator.cpp similarity index 82% rename from src/tonegenerator.cpp rename to src/audio/tonegenerator.cpp index 47c9063008d26a8c5d50f270df9857f457734168..7cb987d4d7966f1dbbe42de9271eb0c2e8aea079 100644 --- a/src/tonegenerator.cpp +++ b/src/audio/tonegenerator.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -20,15 +20,15 @@ #include <math.h> #include <iostream> #include <fstream> +#include <stdlib.h> -#include <qapplication.h> -#include <qstring.h> - -#include "audiocodec.h" -#include "configuration.h" -#include "global.h" -#include "manager.h" +#include "codecDescriptor.h" +#include "ulaw.h" #include "tonegenerator.h" +#include "../configuration.h" +#include "../global.h" +#include "../manager.h" +#include "../user_cfg.h" using namespace std; @@ -47,7 +47,7 @@ ToneThread::~ToneThread (void) { void ToneThread::run (void) { - while (mngr->tonezone) { + while (mngr->getTonezone()) { mngr->audiodriver->audio_buf.setData(buffer, mngr->getSpkrVolume()); mngr->audiodriver->writeBuffer(); } @@ -155,20 +155,27 @@ ToneGenerator::generateSin (int lowerfreq, int higherfreq, int amplitude, void ToneGenerator::buildTone (int idCountry, int idTones, int samplingRate, int amplitude, short* temp) { - QString s; + string s; int count = 0; int byte = 0, - byte_temp = 0, - nbcomma; + byte_temp = 0; + static int nbcomma = 0; short *buffer = new short[1024*1024]; + int pos; + + string str(toneZone[idCountry][idTones]); + nbcomma = contains(toneZone[idCountry][idTones], ','); - nbcomma = (toneZone[idCountry][idTones]).contains(','); // Number of format sections - for (int i = 0; i < nbcomma+1; i++) { - s = (toneZone[idCountry][idTones]).section(',', i, i); - freq1 = ((s.section('/', 0, 0)).section('+', 0, 0)).toInt(); - freq2 = ((s.section('/', 0, 0)).section('+', 1, 1)).toInt(); - time = (s.section('/', 1, 1)).toInt(); + for (int i = 0; i < nbcomma + 1; i++) { + pos = str.find(','); + s = str.substr(0, pos); + pos = s.find('+'); + freq1 = atoi((s.substr(0, pos)).data()); + freq2 = atoi((s.substr(pos + 1, s.find('/'))).data()); + pos = s.find('/'); + time = atoi((s.substr(pos + 1, s.length())).data()); + // Generate sinus, buffer is the result generateSin(freq1, freq2, amplitude, samplingRate, buffer); @@ -185,10 +192,11 @@ ToneGenerator::buildTone (int idCountry, int idTones, int samplingRate, } byte_temp = byte; count = 0; + + str = str.substr(str.find(',') + 1, str.length()); } // Total number in final buffer totalbytes = byte + (byte_temp * (nbcomma+1)); - delete[] buffer; } @@ -199,23 +207,23 @@ ToneGenerator::buildTone (int idCountry, int idTones, int samplingRate, * @return id of the zone */ int -ToneGenerator::idZoneName (const QString &name) { - if (name == "North America") { +ToneGenerator::idZoneName (const string& name) { + if (name.compare("North America")) { return ID_NORTH_AMERICA; - } else if (name == "France") { + } else if (name.compare("France")) { return ID_FRANCE; - } else if (name == "Australia") { + } else if (name.compare("Australia")) { return ID_AUSTRALIA; - } else if (name == "United Kingdom") { + } else if (name.compare("United Kingdom")) { return ID_UNITED_KINGDOM; - } else if (name == "Spain") { + } else if (name.compare("Spain")) { return ID_SPAIN; - } else if (name == "Italy") { + } else if (name.compare("Italy")) { return ID_ITALY; - } else if (name == "Japan") { + } else if (name.compare("Japan")) { return ID_JAPAN; } else { - qWarning("Zone no supported"); + _debug("Zone no supported\n"); return -1; } } @@ -228,7 +236,7 @@ ToneGenerator::idZoneName (const QString &name) { */ void ToneGenerator::toneHandle (int idr) { - int idz = idZoneName(Config::gets("Preferences", "Options.zoneToneChoice")); + int idz = idZoneName(get_config_fields_str(PREFERENCES, ZONE_TONE)); if (idz != -1) { buildTone (idz, idr, SAMPLING_RATE, AMPLITUDE, buf); @@ -240,7 +248,7 @@ ToneGenerator::toneHandle (int idr) { tonethread->start(); } - if (!manager->tonezone) { + if (!manager->getTonezone()) { manager->audiodriver->resetDevice(); if (tonethread != NULL) { delete tonethread; @@ -256,6 +264,7 @@ ToneGenerator::playRingtone (const char *fileName) { short* dst = NULL; char* src = NULL; int expandedsize, length; + Ulaw* ulaw = new Ulaw (PAYLOAD_CODEC_ULAW, CODEC_ULAW); if (fileName == NULL) { return 0; @@ -280,11 +289,7 @@ ToneGenerator::playRingtone (const char *fileName) { file.read (src,length); // Decode file.ul - expandedsize = AudioCodec::codecDecode ( - PAYLOAD_CODEC_ULAW, - dst, - (unsigned char *)src, - length); + expandedsize = ulaw->codecDecode (dst, (unsigned char *)src, length); // Start tone thread if (tonethread == NULL) { @@ -292,7 +297,7 @@ ToneGenerator::playRingtone (const char *fileName) { manager->audiodriver->audio_buf.resize(expandedsize); tonethread->start(); } - if (!manager->tonezone) { + if (!manager->getTonezone()) { manager->audiodriver->resetDevice(); if (tonethread != NULL) { delete tonethread; @@ -305,4 +310,17 @@ ToneGenerator::playRingtone (const char *fileName) { return 1; } +int +ToneGenerator::contains (const string& str, char c) +{ + static int nb = 0; + + unsigned int pos = str.find(c); + if (pos != string::npos) { + nb = nb + 1; + return contains(str.substr(pos + 1, str.length()), c); + } else { + return nb; + } +} diff --git a/src/tonegenerator.h b/src/audio/tonegenerator.h similarity index 81% rename from src/tonegenerator.h rename to src/audio/tonegenerator.h index 6a1a787e4bb4593c7acd56c8faaafcd6ee072483..381b122854332bec54f6cbac06b589671d4c0cee 100644 --- a/src/tonegenerator.h +++ b/src/audio/tonegenerator.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -21,10 +21,13 @@ #define __TONE_GENRATOR_H__ #include <cc++/thread.h> +#include <string> +#include "../manager.h" + +using namespace std; using namespace ost; -#include "manager.h" #define ZT_TONE_DIALTONE 0 #define ZT_TONE_BUSY 1 @@ -65,9 +68,9 @@ public: ToneGenerator (); ~ToneGenerator (void); - QString toneZone[NB_ZONES_MAX][NB_TONES_MAX]; + string toneZone[NB_ZONES_MAX][NB_TONES_MAX]; - int idZoneName (const QString &); + int idZoneName (const string &); void generateSin (int, int, int, int, short *); void buildTone (int, int, int, int, short*); @@ -82,7 +85,19 @@ public: short *buf; private: - void initTone (void); + /* + * Initialisation of the supported tones according to the countries. + */ + void initTone (void); + + /* + * Count all the occurences of a character in a string + * + * @param c character to locate + * @param str string to work on + * @return return the number of time 'c' is found in 'str' + */ + int contains(const string& str, char c); Manager* manager; ToneThread* tonethread; diff --git a/src/audio/ulaw.cpp b/src/audio/ulaw.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd3cddd0c0e0031e65146e6172ec9d1b3dfca612 --- /dev/null +++ b/src/audio/ulaw.cpp @@ -0,0 +1,44 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "g711.h" +#include "ulaw.h" + +Ulaw::Ulaw(int payload, const string& codec) : AudioCodec(payload, codec) +{ + _codecName = codec; + _payload = payload; +} + +Ulaw::~Ulaw (void) +{ +} + +int +Ulaw::codecDecode (short *dst, unsigned char *src, unsigned int size) +{ + return G711::ULawDecode (dst, src, size); +} + +int +Ulaw::codecEncode (unsigned char *dst, short *src, unsigned int size) +{ + return G711::ULawEncode (dst, src, size); +} + diff --git a/src/audio/ulaw.h b/src/audio/ulaw.h new file mode 100644 index 0000000000000000000000000000000000000000..418f224804b2feac25d853fa21232d919cf6be66 --- /dev/null +++ b/src/audio/ulaw.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ULAW_H__ +#define __ULAW_H__ + +#include "audiocodec.h" + +class Ulaw : public AudioCodec { +public: + Ulaw (int payload, const string& codec); + ~Ulaw (void); + + int codecDecode (short *, unsigned char *, unsigned int); + int codecEncode (unsigned char *, short *, unsigned int); + +private: + string _codecName; + int _payload; +}; + +#endif // __ULAW_H__ diff --git a/src/audiocodec.cpp b/src/audiocodec.cpp deleted file mode 100644 index 208bacbba59231ced1519bb547810e5cd6bdc867..0000000000000000000000000000000000000000 --- a/src/audiocodec.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * - * This 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 2, - * or (at your option) any later version. - * - * This 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 dpkg; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#include "audiocodec.h" -#include "configuration.h" -#include "g711.h" -#include "../gsm/gsm.h" - -#include <endian.h> -#include <string.h> - -#include <string> - - -using namespace std; - -static gsm decode_gsmhandle; -static gsm encode_gsmhandle; - -AudioCodec::AudioCodec (void) { - // Init array handleCodecs - handleCodecs[0] = matchPayloadCodec(Config::gets("Audio", "Codecs.codec1")); - handleCodecs[1] = matchPayloadCodec(Config::gets("Audio", "Codecs.codec2")); - handleCodecs[2] = matchPayloadCodec(Config::gets("Audio", "Codecs.codec3")); - handleCodecs[3] = matchPayloadCodec(Config::gets("Audio", "Codecs.codec4")); - handleCodecs[4] = matchPayloadCodec(Config::gets("Audio", "Codecs.codec5")); -} - -AudioCodec::~AudioCodec (void) { -} - -int -AudioCodec::matchPayloadCodec (string codecname) { - if (codecname == CODEC_ALAW) { - return PAYLOAD_CODEC_ALAW; - } else if (codecname == CODEC_ULAW) { - return PAYLOAD_CODEC_ULAW; - } else if (codecname == CODEC_GSM) { - return PAYLOAD_CODEC_GSM; - } else if (codecname == CODEC_ILBC) { - return PAYLOAD_CODEC_ILBC; - } else if (codecname == CODEC_SPEEX) { - return PAYLOAD_CODEC_SPEEX; - } else - return -1; -} - -char * -AudioCodec::rtpmapPayload (int payload) { - switch (payload) { - case PAYLOAD_CODEC_ALAW: - return "PCMA"; - break; - - case PAYLOAD_CODEC_ULAW: - return "PCMU"; - break; - - case PAYLOAD_CODEC_GSM: - return "GSM"; - break; - - case PAYLOAD_CODEC_ILBC: - return "iLBC"; - break; - - case PAYLOAD_CODEC_SPEEX: - return "speex"; - break; - - default: - break; - } - return NULL; -} - -int -AudioCodec::codecDecode (int pt, short *dst, unsigned char *src, unsigned int size) { - switch (pt) { - case PAYLOAD_CODEC_ULAW: - return G711::ULawDecode (dst, src, size); - break; - - case PAYLOAD_CODEC_ALAW: - return G711::ALawDecode (dst, src, size); - break; - - case PAYLOAD_CODEC_GSM: - if (gsm_decode(decode_gsmhandle, (gsm_byte*)src, (gsm_signal*)dst) < 0) - printf("AudioCodec: ERROR: gsm_decode\n"); - return 320; - break; - - case PAYLOAD_CODEC_ILBC: - // TODO - break; - - case PAYLOAD_CODEC_SPEEX: - // TODO - break; - - default: - break; - } - return 0; -} - -int -AudioCodec::codecEncode (int pt, unsigned char *dst, short *src, unsigned int size) { - switch (pt) { - case PAYLOAD_CODEC_ULAW: - return G711::ULawEncode (dst, src, size); - break; - - case PAYLOAD_CODEC_ALAW: - return G711::ALawEncode (dst, src, size); - break; - - case PAYLOAD_CODEC_GSM: - gsm_encode(encode_gsmhandle, (gsm_signal*)src, (gsm_byte*)dst); - return 33; - break; - - case PAYLOAD_CODEC_ILBC: - // TODO - break; - - case PAYLOAD_CODEC_SPEEX: - // TODO - break; - - default: - break; - } - return 0; -} - -void -AudioCodec::noSupportedCodec (void) { - printf("Codec no supported\n"); -} - -void -AudioCodec::gsmCreate (void) { - if (!(decode_gsmhandle = gsm_create() )) - printf("AudioCodec: ERROR: decode_gsm_create\n"); - if (!(encode_gsmhandle = gsm_create() )) - printf("AudioCodec: ERROR: encode_gsm_create\n"); -} - -void -AudioCodec::gsmDestroy (void) { - gsm_destroy(decode_gsmhandle); - gsm_destroy(encode_gsmhandle); -} - -void -AudioCodec::create (int pt) { - switch (pt) { - case PAYLOAD_CODEC_GSM: - AudioCodec::gsmCreate(); - break; - default: - break; - } -} - -void -AudioCodec::destroy (int pt) { - switch (pt) { - case PAYLOAD_CODEC_GSM: - AudioCodec::gsmDestroy(); - break; - default: - break; - } -} diff --git a/src/audiortp.cpp b/src/audiortp.cpp deleted file mode 100644 index c2914c6067feb4b094839449c34c5cde7e43bccb..0000000000000000000000000000000000000000 --- a/src/audiortp.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <cstdio> -#include <cstdlib> -#include <ccrtp/rtp.h> - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <qhostaddress.h> -#include <qstring.h> - -#include "audiocodec.h" -#include "configuration.h" -#include "manager.h" -#include "audiortp.h" -#include "sip.h" -#include "../stund/stun.h" - -#include <string> -using namespace ost; -using namespace std; - - -//////////////////////////////////////////////////////////////////////////////// -// AudioRtp -//////////////////////////////////////////////////////////////////////////////// -AudioRtp::AudioRtp (Manager *manager) { - string svr; - this->manager = manager; - RTXThread = NULL; - if (!manager->useStun()) { - if (Config::gets("Signalisations", "SIP.sipproxy") == "") { - svr = Config::gets("Signalisations", "SIP.sipproxy"); - } - } else { - svr = Config::gets("Signalisations", "SIP.hostPart"); - } -} - -AudioRtp::~AudioRtp (void) { -} - -int -AudioRtp::createNewSession (SipCall *ca) { - // Start RTP Send/Receive threads - ca->enable_audio = 1; - if (!manager->useStun()) { - symetric = false; - } else { - symetric = true; - } - -#ifdef ALSA - if (manager->useAlsa) { - RTXThread = new AudioRtpRTX (ca, manager->audiodriver, - manager->audiodriverReadAlsa, manager, symetric); - } -#endif - if (!manager->useAlsa) { - RTXThread = new AudioRtpRTX (ca, manager->audiodriver, NULL, manager, - symetric); - } - - if (RTXThread->start() != 0) { - return -1; - } - - return 0; -} - - -void -AudioRtp::closeRtpSession (SipCall *ca) { - // This will make RTP threads finish. - ca->enable_audio = -1; - - if (RTXThread != NULL) { - delete RTXThread; - RTXThread = NULL; - } - - // Flush audio read buffer - manager->audiodriver->resetDevice(); -} - -//////////////////////////////////////////////////////////////////////////////// -// AudioRtpRTX Class // -//////////////////////////////////////////////////////////////////////////////// -AudioRtpRTX::AudioRtpRTX (SipCall *sipcall, AudioDrivers *driver, - AudioDrivers *read_driver, Manager *mngr, bool sym) { - this->time = new Time(); - this->manager = mngr; - this->ca = sipcall; - this->sym =sym; - this->audioDevice = driver; -#ifdef ALSA - if (manager->useAlsa) { - this->audioDeviceRead = read_driver; - } -#endif - - // TODO: Change bind address according to user settings. - InetHostAddress local_ip("0.0.0.0"); - - if (!sym) { - sessionRecv = new RTPSession (local_ip, ca->getLocalAudioPort()); - sessionSend = new RTPSession (local_ip); - } else { - int forcedPort = manager->getFirewallPort(); - qDebug("Forced port %d", forcedPort); - session = new SymmetricRTPSession (local_ip, forcedPort); - } -} - -AudioRtpRTX::~AudioRtpRTX () { - this->terminate(); - - if (!sym) { - if (sessionRecv != NULL) { - delete sessionRecv; - sessionRecv = NULL; - } - if (sessionSend != NULL) { - delete sessionSend; - sessionSend = NULL; - } - } else { - if (session != NULL) { - delete session; - session = NULL; - } - } -} - -void -AudioRtpRTX::run (void) { - unsigned char *data_to_send; - short *data_mute; - short *data_from_mic; - short *data_from_mic_tmp; - int i, - compSize, - timestamp; - int expandedSize; - short *data_for_speakers = NULL; - int countTime = 0; - data_for_speakers = new short[2048]; - data_from_mic = new short[1024]; - data_from_mic_tmp = new short[1024]; - data_to_send = new unsigned char[1024]; - data_mute = new short[1024]; - - InetHostAddress remote_ip(ca->remote_sdp_audio_ip); - - if (!remote_ip) { - qDebug("RTX: IP address is not correct!"); - exit(); - } else { - qDebug("RTX: Connected to %s:%d", - ca->remote_sdp_audio_ip, ca->remote_sdp_audio_port); - } - - // Initialization - if (!sym) { - sessionRecv->setSchedulingTimeout (100000); - sessionRecv->setExpireTimeout(1000000); - - sessionSend->setSchedulingTimeout(10000); - sessionSend->setExpireTimeout(1000000); - } else { - session->setSchedulingTimeout(10000); - session->setExpireTimeout(1000000); - } - -#if 0 // Necessaire ? - if (!sessionRecv->addDestination(remote_ip, - (unsigned short) ca->remote_sdp_audio_port)) { - qDebug("RTX recv: could not connect to port %d", - ca->remote_sdp_audio_port); - this->exit(); - } else { - qDebug("RTP(Recv): Added destination %s:%d", - remote_ip.getHostname(), - (unsigned short) ca->remote_sdp_audio_port); - } -#endif - - if (!sym) { - if (!sessionSend->addDestination (remote_ip, - (unsigned short) ca->remote_sdp_audio_port)) { - qDebug("RTX send: could not connect to port %d", - ca->remote_sdp_audio_port); - this->exit(); - } else { - qDebug("RTP(Send): Added destination %s:%d", - remote_ip.getHostname(), - (unsigned short) ca->remote_sdp_audio_port); - } - - sessionRecv->setPayloadFormat(StaticPayloadFormat( - (StaticPayloadType) ca->payload)); - sessionSend->setPayloadFormat(StaticPayloadFormat( - (StaticPayloadType) ca->payload)); - setCancel(cancelImmediate); - sessionSend->setMark(true); - - } else { - if (!session->addDestination (remote_ip, - (unsigned short) ca->remote_sdp_audio_port)) { - qDebug("Symmetric: could not connect to port %d", - ca->remote_sdp_audio_port); - this->exit(); - } else { - qDebug("Symmetric: Connected to %s:%d", - remote_ip.getHostname(), - (unsigned short) ca->remote_sdp_audio_port); - - session->setPayloadFormat(StaticPayloadFormat( - (StaticPayloadType) ca->payload)); - setCancel(cancelImmediate); - } - } - - timestamp = 0; - - // TODO: get frameSize from user config - int frameSize = 20; // 20ms frames - TimerPort::setTimer(frameSize); - - // start running the packet queue scheduler. - if (!sym) { - sessionRecv->startRunning(); - sessionSend->startRunning(); - } else { - session->startRunning(); - } - - AudioCodec::create(ca->payload); - - while (ca->enable_audio != -1) { - //////////////////////////// - // Send session - //////////////////////////// - int size = 320; - if (!manager->mute) { -#ifdef ALSA - if (manager->useAlsa) { - i = audioDeviceRead->readBuffer (data_from_mic, size); - } -#endif - if (!manager->useAlsa) { - i = audioDevice->readBuffer (data_from_mic, size); - } - } else { - // When IP-phone user click on mute button, we read buffer of a - // temp buffer to avoid delay in sound. -#ifdef ALSA - if (manager->useAlsa) - i = audioDeviceRead->readBuffer (data_mute, size); -#endif - if (!manager->useAlsa) - i = audioDevice->readBuffer (data_mute, size); - } - // TODO : return an error because no sound - if (i < 0) { - qDebug("audiortp.cpp: No sound"); - break; - } - for (int j = 0; j < i; j++) - data_from_mic_tmp[j] = data_from_mic[j]*manager->getMicVolume()/100; - - // Encode acquired audio sample - compSize = AudioCodec::codecEncode ( - ca->payload, - data_to_send, - data_from_mic_tmp, i); - - // Send encoded audio sample - if (!sym) { - sessionSend->putData(timestamp, data_to_send, compSize); - } else { - session->putData(timestamp, data_to_send, compSize); - } - timestamp += 160; - - //////////////////////////// - // Recv session - //////////////////////////// - const AppDataUnit* adu = NULL; - - do { - Thread::sleep(5); // in msec. - if (!sym) { - adu = sessionRecv->getData(sessionRecv->getFirstTimestamp()); - } else { - adu = session->getData(session->getFirstTimestamp()); - } - } while (adu == NULL); - - // Decode data with relevant codec - expandedSize = AudioCodec::codecDecode ( - adu->getType(), - data_for_speakers, - (unsigned char*) adu->getData(), - adu->getSize()); - - // Set decoded data to sound device - audioDevice->audio_buf.resize(expandedSize); - audioDevice->audio_buf.setData (data_for_speakers, - manager->getSpkrVolume()); - - // Notify (with a bip) an incoming call when there is already a call - countTime += time->getSecond(); - if (manager->getNumberPendingCalls() != 1 and manager->ringing()) { - countTime = countTime % 2000; - if (countTime < 10 and countTime > 0) { - manager->notificationIncomingCall(); - } - } - - // Write data or notification - i = audioDevice->writeBuffer (); - delete adu; - - // Let's wait for the next transmit cycle - Thread::sleep(TimerPort::getTimer()); - TimerPort::incTimer(frameSize); // 'frameSize' ms - } - - AudioCodec::destroy(ca->payload); - delete[] data_for_speakers; - delete[] data_from_mic; - delete[] data_from_mic_tmp; - delete[] data_mute; - delete[] data_to_send; - this->exit(); -} - - -// EOF diff --git a/src/call.cpp b/src/call.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7da704f1839a61ce7bfca9cd7051ac6c4c8cb00 --- /dev/null +++ b/src/call.cpp @@ -0,0 +1,347 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <iostream> +#include "audio/audiocodec.h" +#include "call.h" +#include "manager.h" +#include "sipvoiplink.h" +#include "voIPLink.h" + +Call::Call (void) +{ + initConstructor(); + _id = 0; + _manager = NULL; + _voIPLink = NULL; +} + +Call::Call (Manager* manager, short id, CallType type, VoIPLink* voiplink) +{ + initConstructor(); + _id = id; + _type = type; + _manager = manager; + _voIPLink = voiplink; + + switch (_type) { + case Outgoing: + _voIPLink->newOutgoingCall(_id); + break; + case Incoming: + _voIPLink->newIncomingCall(_id); + break; + default: + break; + } +} + +Call::~Call (void) +{ +} + +short +Call::getId (void) +{ + return _id; +} + +void +Call::setId (short id) +{ + _id = id; +} + +unsigned int +Call::getTimestamp(void) +{ + return _timestamp; +} + +void +Call::setTimestamp (unsigned int timestamp) +{ + _timestamp = timestamp; +} + +short +Call::getVoIPLinkId (void) +{ + return _voIPLinkId; +} + +void +Call::setVoIPLinkId (short voIPLinkId) +{ + _voIPLinkId = voIPLinkId; +} + +void +Call::setVoIPLink (VoIPLink* voIPLink) +{ + _voIPLink = voIPLink; +} + +VoIPLink* +Call::getVoIPLink (void) +{ + return _voIPLink; +} + +void +Call::setAudioCodec (void) +{ + _audiocodec = _voIPLink->getAudioCodec(getId()); +} + +string +Call::getStatus (void) +{ + return _status; +} + +void +Call::setStatus (const string& status) +{ + _status = status; +} + +string +Call::getTo (void) +{ + return _to; +} + +void +Call::setTo (const string& to) +{ + _to = to; +} + +string +Call::getCallerIdName (void) +{ + return _callerIdName; +} + +void +Call::setCallerIdName (const string& callerId_name) +{ + _callerIdName = callerId_name; +} + +string +Call::getCallerIdNumber (void) +{ + return _callerIdNumber; +} + +void +Call::setCallerIdNumber (const string& callerId_number) +{ + _callerIdNumber = callerId_number; +} + +enum CallState +Call::getState (void) +{ + return _state; +} + +void +Call::setState (enum CallState state) +{ + _state = state; +} + +enum CallType +Call::getType (void) +{ + return _type; +} + +void +Call::setType (enum CallType type) +{ + _type = type; +} + +bool +Call::isBusy (void) +{ + if (isAnswered() or isOffHold() or isOnMute()) { + return true; + } else { + return false; + } +} +bool +Call::isOnHold (void) +{ + return (_state == OnHold) ? true : false; +} + +bool +Call::isOffHold (void) +{ + return (_state == OffHold) ? true : false; +} + +bool +Call::isOnMute (void) +{ + return (_state == MuteOn) ? true : false; +} + +bool +Call::isTransfered (void) +{ + return (_state == Transfered) ? true : false; +} + +bool +Call::isHungup (void) +{ + return (_state == Hungup) ? true : false; +} + +bool +Call::isRinging (void) +{ + return (_state == Ringing) ? true : false; +} + +bool +Call::isRefused (void) +{ + return (_state == Refused) ? true : false; +} + +bool +Call::isCancelled (void) +{ + return (_state == Cancelled) ? true : false; +} + +bool +Call::isAnswered (void) +{ + return (_state == Answered) ? true : false; +} + +bool +Call::isProgressing (void) +{ + return (_state == Progressing) ? true : false; +} + +bool +Call::isOutgoingType (void) +{ + return (_type == Outgoing) ? true : false; +} + +bool +Call::isIncomingType (void) +{ + return (_type == Incoming) ? true : false; +} + +int +Call::outgoingCall (const string& to) +{ + return _voIPLink->outgoingInvite(to); +} + +int +Call::hangup (void) +{ + int i = _voIPLink->hangup(_id); + _voIPLink->deleteSipCall(_id); + return 1; +} + +int +Call::answer (void) +{ + int i = _voIPLink->answer(_id); + return 1; +} + +int +Call::onHold (void) +{ + int i = _voIPLink->onhold(_id); + return 1; +} + +int +Call::offHold (void) +{ + int i = _voIPLink->offhold(_id); + return 1; +} + +int +Call::transfer (const string& to) +{ + int i = _voIPLink->transfer(_id, to); + return 1; +} + +int +Call::muteOn (void) +{ + return 1; +} + +int +Call::muteOff (void) +{ + return 1; +} + +int +Call::refuse (void) +{ + int i = _voIPLink->refuse(_id); + _voIPLink->deleteSipCall(_id); + return 1; +} + +int +Call::cancel (void) +{ + int i = _voIPLink->cancel(_id); + _voIPLink->deleteSipCall(_id); + return 1; +} + +/////////////////////////////////////////////////////////////////////////////// +// Private functions +/////////////////////////////////////////////////////////////////////////////// +void +Call::initConstructor(void) +{ + _timestamp = 0; + _state = NotExist; + _type = Null; + _voIPLinkId = 1; + _audiocodec = NULL; +} diff --git a/src/call.h b/src/call.h new file mode 100644 index 0000000000000000000000000000000000000000..b0c6259870793c7544743f0747e6069d232e2fdd --- /dev/null +++ b/src/call.h @@ -0,0 +1,136 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CALL_H__ +#define __CALL_H__ + +#include <string> +#include <vector> +using namespace std; + +#include "sipvoiplink.h" +#include "voIPLink.h" + + +enum CallState { + NotExist = 0, + Busy, + OnHold, + OffHold, + MuteOn, + MuteOff, + Transfered, + Hungup, + Answered, + Ringing, + Progressing, + Cancelled, // for cancel outgoing ringing call + Refused // for refuse incoming ringing call +}; + +enum CallType { + Null = 0, + Incoming, + Outgoing +}; + +#include "manager.h" + +class AudioCodec; + +class Call { +public: + Call(void); + Call(Manager* manager, short id, CallType type, VoIPLink* voiplink); + + ~Call(void); + + short getId (void); + void setId (short id); + + unsigned int getTimestamp(void); + void setTimestamp (unsigned int timestamp); + + short getVoIPLinkId(void); + void setVoIPLinkId (short voIPLinkId); + void setVoIPLink (VoIPLink* voIPLink); + VoIPLink* getVoIPLink(void); + void setAudioCodec(void); + + string getStatus (void); + void setStatus (const string& status); + + string getTo (void); + void setTo (const string& to); + + string getCallerIdName (void); + void setCallerIdName (const string& callerId_name); + string getCallerIdNumber (void); + void setCallerIdNumber (const string& callerId_number); + + enum CallState getState (void); + void setState (enum CallState state); + enum CallType getType (void); + void setType (enum CallType type); + + bool isBusy (void); + bool isOnHold (void); + bool isOffHold (void); + bool isOnMute (void); + bool isTransfered (void); + bool isHungup (void); + bool isRinging (void); + bool isRefused (void); + bool isCancelled (void); + bool isAnswered (void); + bool isProgressing (void); + bool isOutgoingType (void); + bool isIncomingType (void); + + int outgoingCall (const string& to); + int hangup (void); + int answer (void); + int onHold (void); + int offHold (void); + int transfer (const string& to); + int muteOn (void); + int muteOff (void); + int refuse (void); + int cancel (void); + + +private: + void initConstructor (void); + + Manager* _manager; + VoIPLink* _voIPLink; + AudioCodec* _audiocodec; + + short _id; + short _voIPLinkId; + unsigned int _timestamp; + enum CallState _state; + enum CallType _type; + string _to; + string _callerIdName; + string _callerIdNumber; + string _status; +}; + +#endif // __CALL_H__ diff --git a/src/configitem.cpp b/src/configitem.cpp index d86ab9c2c28cb42c513d9de02cf3f47d6ef8ffd6..d9a16ef1a7ee34ae0e23e965ad5927ea2bf63fd6 100644 --- a/src/configitem.cpp +++ b/src/configitem.cpp @@ -3,14 +3,12 @@ // (c) 2004 Savoir-faire Linux inc. // // -#include <cc++/string.h> #include <assert.h> #include <stdio.h> #include <string.h> #include "configitem.h" -using namespace ost; using namespace std; ConfigItem::ConfigItem (void) { @@ -18,16 +16,16 @@ ConfigItem::ConfigItem (void) { } // Create a new ConfigItem, with key name keyName -ConfigItem::ConfigItem (const char *keyName) { +ConfigItem::ConfigItem (const string& keyName) { init(); - this->_key = new String(keyName); + this->_key = new string(keyName); } // Create a new ConfigItem, with key name keyName and value keyVal. -ConfigItem::ConfigItem (const char *keyName, const char *keyVal) { +ConfigItem::ConfigItem (const string& keyName, const string& keyVal) { init(); - this->_key = new String(keyName); - this->_value = new String(keyVal); + this->_key = new string(keyName); + this->_value = new string(keyVal); } ConfigItem::~ConfigItem (void) { @@ -37,8 +35,8 @@ ConfigItem::~ConfigItem (void) { // Get the value of the passed key -String* -ConfigItem::getValueByKey (const char *keyName) { +string* +ConfigItem::getValueByKey (const string& keyName) { assert (_key != NULL); if (*_key == keyName) { @@ -53,7 +51,7 @@ ConfigItem::getValueByKey (const char *keyName) { // Get item pointer using a key value. // If key value not found, new item is appended to list. ConfigItem* -ConfigItem::getItemByKey (const char *keyName) { +ConfigItem::getItemByKey (const string& keyName) { assert (_key != NULL); if (*_key == keyName) { @@ -94,17 +92,17 @@ ConfigItem::saveToFile (fstream *fd) { // Set the current objects value void -ConfigItem::setValue (const char *newValue) { +ConfigItem::setValue (const string& newValue) { if (_value != NULL) { delete _value; } - _value = new String(newValue); + _value = new string(newValue); } // Set a value given its key name void -ConfigItem::setValueByKey (const char *key, const char *value) { +ConfigItem::setValueByKey (const string& key, const string& value) { getItemByKey(key)->setValue(value); } diff --git a/src/configitem.h b/src/configitem.h index 0a7fb8ba0e169edbf8d39081f865ffd73d5c7af8..9a236beab12a8518e64c53e7d2c8c5c354e651dc 100644 --- a/src/configitem.h +++ b/src/configitem.h @@ -9,31 +9,30 @@ #include <iostream> #include <fstream> -#include <cc++/string.h> +#include <string> #include <stdio.h> -using namespace ost; using namespace std; class ConfigItem { public: ConfigItem (void); - ConfigItem (const char *); - ConfigItem (const char *, const char *); + ConfigItem (const string& ); + ConfigItem (const string& , const string& ); ~ConfigItem (void); - String* key (void) { return _key; } - String* value (void) { return _value; } + string* key (void) { return _key; } + string* value (void) { return _value; } ConfigItem* head (void) { return _head; } void setHead (ConfigItem *h) { _head = h; } - String* getValueByKey (const char *); - ConfigItem* getItemByKey (const char *); - void setValue (const char *); - void setValueByKey (const char *, const char *); + string* getValueByKey (const string& ); + ConfigItem* getItemByKey (const string& ); + void setValue (const string& ); + void setValueByKey (const string& , const string& ); void saveToFile (fstream*); private: - String* _key; - String* _value; + string* _key; + string* _value; ConfigItem* _next; ConfigItem* _head; void init (void); diff --git a/src/configuration.cpp b/src/configuration.cpp index fc989cc24c08be517696698730fdd823cdf6be97..17c5c5491d9022a9fc16d8a9ba9e0592e7626f3a 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -17,87 +17,91 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <qstring.h> +#include <string> #include "configuration.h" #include "configurationtree.h" +using namespace std; + static ConfigurationTree *globalConfigTree = NULL; + string -Config::gets (const char *section, const char *key) { - return Config::get (section, key, ""); +Config::gets (const string& section, const string& key) { + return Config::get (section, key, string("")); } + string -Config::get (const char *section, const char *key, const char *defval) { - char *value = tree()->getValue (section, key); - if (value == NULL) { +Config::get (const string& section, const string& key, const string& defval) { + string value = tree()->getValue (section, key); + if (value.empty()) { tree()->setValue(section, key, defval); - return string(defval); + return defval; } else { - return string(value); + return value; } } - -char * -Config::getschar (const char *section, const char *key) { +/* +string +Config::getschar (const string& section, const string& key) { return Config::getchar (section, key, ""); -} +}*/ -char * -Config::getchar (const char *section, const char *key, const char *defval) { - char *value = tree()->getValue (section, key); - if (value == NULL) { +string +Config::getchar (const string& section, const string& key, const string& defval){ + string value = tree()->getValue (section, key); + if (value == string("")) { tree()->setValue(section, key, defval); - return (char*)defval; + return defval; } else { return value; } } - +/* bool -Config::getb (const char *section, const char *key) { +Config::getb (const string& section, const string& key) { return (bool)Config::get (section, key, 0); } - +*/ int -Config::geti (const char *section, const char *key) { +Config::geti (const string& section, const string& key) { return Config::get (section, key, 0); } int -Config::get (const char *section, const char * key, int defval) { - char *value = tree()->getValue(section, key); - if (value == NULL) { +Config::get (const string& section, const string& key, int defval) { + string value = tree()->getValue(section, key); + if (value == string("")) { tree()->setValue(section, key, defval); return defval; } else { - return atoi(value); + return atoi(value.data()); } } string -Config::set (const char *section, const char *key, const char *val) { +Config::set (const string& section, const string& key, const string& val) { tree()->setValue(section, key, val); - return string(val); + return val; } -char * -Config::setchar (const char *section, const char *key, const char *val) { +string +Config::setchar (const string& section, const string& key, const string& val) { tree()->setValue(section, key, val); - return (char*)val; + return val; } int -Config::set (const char *section, const char *key, int val) { +Config::set (const string& section, const string& key, int val) { tree()->setValue(section, key, val); return val; } bool -Config::set (const char *section, const char *key, bool val) { +Config::set (const string& section, const string& key, bool val) { tree()->setValue(section, key, (int)val); return val; } @@ -108,6 +112,11 @@ Config::setTree (ConfigurationTree *t) { globalConfigTree = t; } +void +Config::deleteTree (void) { + delete globalConfigTree; +} + ConfigurationTree* Config::tree(void) { return globalConfigTree; diff --git a/src/configuration.h b/src/configuration.h index d6f147d0b077f01466eb5a810639c9001d1e19b6..885e1107a31eb05f8a756662e6bf1c8003e1259c 100644 --- a/src/configuration.h +++ b/src/configuration.h @@ -20,12 +20,10 @@ #ifndef __CONFIG_H__ #define __CONFIG_H__ -#include <qsettings.h> -#include <qstring.h> +#include <string> #include "global.h" -#include <string> using namespace std; class ConfigurationTree; @@ -34,22 +32,23 @@ public: Config (void) {}; ~Config (void) {}; - static std::string gets (const char*, const char*); - static char* getschar (const char*, const char*); - static int geti (const char*, const char*); - static bool getb (const char*, const char*); + static string gets (const string&, const string&); +// static string getschar(const string&, const string&); + static int geti (const string&, const string&); +// static bool getb (const string&, const string&); - static std::string get (const char*, const char*, const char*); - static char* getchar (const char*, const char*, const char*); - static int get (const char*, const char*, int); - static bool get (const char*, const char*, bool); + static string get (const string&, const string&, const string&); + static string getchar (const string&, const string&, const string&); + static int get (const string&, const string&, int); + static bool get (const string&, const string&, bool); - static int set (const char*, const char*, int); - static bool set (const char*, const char*, bool); - static std::string set (const char*, const char*, const char*); - static char* setchar (const char*, const char*, const char*); + static int set (const string&, const string&, int); + static bool set (const string&, const string&, bool); + static string set (const string&, const string&, const string&); + static string setchar (const string&, const string&, const string&); - static void setTree (ConfigurationTree *); + static void setTree (ConfigurationTree *); + static void deleteTree (void); static ConfigurationTree* tree (void); }; diff --git a/src/configurationtree.cpp b/src/configurationtree.cpp index 4ceb4bc48bfb5c24c0235569804de6f6d3d6b715..43b630665fddac1f7cc15f3ecb5b52c2d9b9583b 100644 --- a/src/configurationtree.cpp +++ b/src/configurationtree.cpp @@ -9,6 +9,7 @@ #include "configitem.h" #include "configurationtree.h" +#include "global.h" using namespace std; @@ -18,7 +19,7 @@ ConfigurationTree::ConfigurationTree (void) { } // Construct with file name, and load from this file -ConfigurationTree::ConfigurationTree (const char *fileName) { +ConfigurationTree::ConfigurationTree (const string& fileName) { populateFromFile (fileName); } @@ -31,67 +32,70 @@ ConfigurationTree::~ConfigurationTree (void) { // Create the tree from an existing ini file int -ConfigurationTree::populateFromFile (const char *fileName) { - if (fileName == NULL) { - printf("Filename is NULL\n"); +ConfigurationTree::populateFromFile (const string& fileName) { + bool out = false; + if (fileName.empty()) { return 0; } fstream file; - file.open(fileName, fstream::in); + file.open(fileName.data(), fstream::in); if (!file.is_open()) { - printf("\nConfig-file is creating ...\n"); - return 0; + file.open(fileName.data(), fstream::out); + out = true; + if (!file.is_open()) { + _debug("Error opening file (%s:%d)\n", __FILE__, __LINE__); + return 0; + } + return 2; } char line[128]; bzero (line, 128); - const char* section = NULL; - const char* key; - const char* val; - String s; + const string section(""); + const string key(""); + const string val(""); + string s; + int pos; while (!file.eof()) { // Read the file line by line file.getline (line, 128 - 1); - String* str = new String(line); - if (*str[0] == '[') { + string str(line); + if (str[0] == '[') { // If the line is a section - s = str->token("[",1); - s.trim("]"); - section = s.data(); - - } else if (*str != NULL and *str[0] != '#') { + pos = str.find(']'); + section = str.substr(1, pos - 1); + } else if (!str.empty() and str[0] != '#') { // If the line is "key=value" and doesn't begin with '#'(comments) - String k = str->token("=", 0); - key = k.data(); - String v = str->token("=", 0); - val = v.data(); - if (String(key).length() > 0 and String(val).length() > 0) { + pos = str.find('='); + key = str.substr(0, pos); + val = str.substr(pos + 1, str.length() - pos); + + if (key.length() > 0 and val.length() > 0) { setValue(section, key, val); } } - delete str; } - + file.close(); return 1; } // Save tree's contents to a file int -ConfigurationTree::saveToFile (const char *fileName) { - if (fileName == NULL || _head == NULL) { +ConfigurationTree::saveToFile (const string& fileName) { + if (fileName.empty() || _head == NULL) { return 0; } fstream file; - file.open(fileName, fstream::out); + file.open(fileName.data(), fstream::out); if (!file.is_open()) { - printf("\nError opening file\n"); + _debug("Error opening file\n"); return 0; } @@ -104,7 +108,7 @@ ConfigurationTree::saveToFile (const char *fileName) { // set [section]/key to int value #define TMPBUFSIZE 32 int -ConfigurationTree::setValue (const char *section, const char *key, int value) { +ConfigurationTree::setValue (const string& section, const string& key, int value) { char tmpBuf[TMPBUFSIZE]; // Make string from int @@ -116,8 +120,9 @@ ConfigurationTree::setValue (const char *section, const char *key, int value) { // set [section]/key to "value" int -ConfigurationTree::setValue (const char *section, const char *key, - const char *value) { +ConfigurationTree::setValue (const string& section, const string& key, + const string& value) { + if (_head != NULL) { ConfigSection *list; ConfigItem *item; @@ -145,11 +150,10 @@ ConfigurationTree::setValue (const char *section, const char *key, } // get [section]/key's value -char * -ConfigurationTree::getValue (const char *section, const char *key) { -// printf ("getValue(%s,%s)\n", section, key); +string +ConfigurationTree::getValue (const string& section, const string& key) { if (_head != NULL) { - String *valuePtr; + string *valuePtr; if (_head->getItemByKey(section)->head() != 0) { // If config file exist valuePtr = _head->getItemByKey(section)->head()->getValueByKey(key); @@ -161,10 +165,10 @@ ConfigurationTree::getValue (const char *section, const char *key) { if (valuePtr != NULL) { return valuePtr->data(); } else { - return NULL; + return ""; } } else { - return (char *) NULL; + return ""; } } diff --git a/src/configurationtree.h b/src/configurationtree.h index 694227e5b647bc4955ed01615651e4e2ad8a4478..a4c3dc35447580b2f273265ea59aed36cd009c36 100644 --- a/src/configurationtree.h +++ b/src/configurationtree.h @@ -28,14 +28,14 @@ class ConfigurationTree { public: ConfigurationTree (void); - ConfigurationTree (const char *); + ConfigurationTree (const string&); ~ConfigurationTree (void); ConfigSection* head (void) { return this->_head; } - int populateFromFile(const char*); - int saveToFile (const char*); - int setValue (const char*, const char*, int); - int setValue (const char*, const char*, const char*); - char* getValue (const char*, const char*); + int populateFromFile(const string& ); + int saveToFile (const string& ); + int setValue (const string& , const string& , int); + int setValue(const string& , const string& , const string& ); + string getValue (const string& , const string& ); private: ConfigSection *_head; diff --git a/src/error.cpp b/src/error.cpp index 19f6234c30446e5efe31d41a13ada002d5e27e77..a46b5643221c5ebdc9c7491412882a08d8db5a7c 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -1,130 +1,133 @@ #include "error.h" +#include "global.h" +#include "manager.h" #include <string> using namespace std; -Error::Error (Manager *_mngr){ - this->mngr = _mngr; +Error::Error (Manager *mngr){ + _mngr = mngr; issetError = 0; } int Error::errorName (Error_enum num_name, char* err) { +#if 1 string str; switch (num_name){ // Handle opening device errors case DEVICE_NOT_OPEN: - printf ("ERROR: Device Not Open\n"); - mngr->errorDisplay("Device not open "); + _debug("ERROR: Device Not Open\n"); + _mngr->displayError("Device not open "); issetError = 2; break; case DEVICE_ALREADY_OPEN: - printf ("ERROR: Device Already Open !\n"); - mngr->errorDisplay("Device already open "); + _debug ("ERROR: Device Already Open !\n"); + _mngr->displayError("Device already open "); issetError = 2; break; case OPEN_FAILED_DEVICE: - printf ("ERROR: Open Failed\n"); - mngr->errorDisplay("Open device failed "); + _debug ("ERROR: Open Failed\n"); + _mngr->displayError("Open device failed "); issetError = 2; break; // Handle ALSA errors case PARAMETER_STRUCT_ERROR_ALSA: str = str.append("Error with hardware parameter structure: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case ACCESS_TYPE_ERROR_ALSA: str = str.append("Cannot set access type: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case SAMPLE_FORMAT_ERROR_ALSA: str = str.append("Cannot set sample format: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case SAMPLE_RATE_ERROR_ALSA: str = str.append("Cannot set sample rate: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case CHANNEL_ERROR_ALSA: str = str.append("Cannot set channel: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case PARAM_SETUP_ALSA: str = str.append("Cannot set parameters: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case DROP_ERROR_ALSA: str = str.append("Error: drop(): ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case PREPARE_ERROR_ALSA: str = str.append("Error: prepare(): ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; // Handle OSS errors case FRAGMENT_ERROR_OSS: str = str.append("Error: SNDCTL_DSP_SETFRAGMENT: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case SAMPLE_FORMAT_ERROR_OSS: str = str.append("Error: SNDCTL_DSP_SETFMT: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case CHANNEL_ERROR_OSS: str = str.append("Error: SNDCTL_DSP_CHANNELS: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case SAMPLE_RATE_ERROR_OSS: str = str.append("Error: SNDCTL_DSP_SPEED: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case GETISPACE_ERROR_OSS: str = str.append("Error: SNDCTL_DSP_GETISPACE: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; case GETOSPACE_ERROR_OSS: str = str.append("Error: SNDCTL_DSP_GETOSPACE: ") + err; - mngr->errorDisplay((char*)str.data()); + _mngr->displayError((char*)str.data()); issetError = 1; break; // Handle setup errors case HOST_PART_FIELD_EMPTY: - mngr->errorDisplay("Fill host part field"); + _mngr->displayError("Fill host part field"); issetError = 2; break; case USER_PART_FIELD_EMPTY: - mngr->errorDisplay("Fill user part field"); + _mngr->displayError("Fill user part field"); issetError = 2; break; case PASSWD_FIELD_EMPTY: - mngr->errorDisplay("Fill password field"); + _mngr->displayError("Fill password field"); issetError = 2; break; // Handle sip uri case FROM_ERROR: - mngr->errorDisplay("Error for 'From' header"); + _mngr->displayError("Error for 'From' header"); issetError = 1; break; case TO_ERROR: - mngr->errorDisplay("Error for 'To' header"); + _mngr->displayError("Error for 'To' header"); issetError = 1; break; @@ -133,5 +136,7 @@ Error::errorName (Error_enum num_name, char* err) { break; } return issetError; +#endif + return 1; } diff --git a/src/error.h b/src/error.h index b58c0896158f3cfd4af3bf40fb320fd7e37b07e0..983acaee0ae56966f720904ff84c2b39b687ab47 100644 --- a/src/error.h +++ b/src/error.h @@ -3,7 +3,6 @@ #include <stdio.h> -#include "manager.h" typedef enum { DEVICE_NOT_OPEN = 0, @@ -35,10 +34,10 @@ typedef enum { } Error_enum; - +class Manager; class Error { public: - Error (Manager *); + Error (Manager *mngr); ~Error (void) {}; int errorName (Error_enum, char *); @@ -46,7 +45,7 @@ public: inline void setError(int err) { issetError = err; } private: - Manager *mngr; + Manager *_mngr; int issetError; }; diff --git a/src/eventthread.cpp b/src/eventthread.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17decd3ef51f2f2bf561d7e274ca5afa5df20cf3 --- /dev/null +++ b/src/eventthread.cpp @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +//#include <stdio.h> +#include <iostream> + +#include "eventthread.h" +#include "sipvoiplink.h" + +using namespace std; + +EventThread::EventThread (SipVoIPLink* sip) : Thread () +{ + _sipthread = sip; +} + +EventThread::~EventThread (void) +{ + terminate(); +} + +/** + * Reimplementation of run() + */ +void +EventThread::run (void) +{ + while(1) { + _sipthread->getEvent(); + } +} + diff --git a/src/eventthread.h b/src/eventthread.h new file mode 100644 index 0000000000000000000000000000000000000000..55c3b31e98c64d7249965f1166deb065f7cffe81 --- /dev/null +++ b/src/eventthread.h @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __EVENT_THREAD_H__ +#define __EVENT_THREAD_H__ + +#include <cc++/thread.h> + +using namespace ost; + +class SipVoIPLink; +class EventThread : public Thread { +public: + EventThread (SipVoIPLink*); + ~EventThread (void); + + virtual void run (); +private: + SipVoIPLink* _sipthread; +}; + +#endif // __EVENT_THREAD_H__ diff --git a/src/global.h b/src/global.h index 1b050dd4dd7dd6a0b40418236182fcceb9d245e3..4f6fb90fa5e0f9177ee9104c123ef9fd7ba3b4c1 100644 --- a/src/global.h +++ b/src/global.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -20,15 +20,24 @@ #ifndef __GLOBAL_H__ #define __GLOBAL_H__ +#define DEBUG + +#ifdef DEBUG + #define _debug(...) fprintf(stderr, "[debug] " __VA_ARGS__) +#else + #define _debug(...) +#endif + #ifndef PREFIX #define PREFIX "/usr/local" #endif -#define VERSION "0.3" +#define VERSION "0.4" #define PROGNAME "sflphone" #define SKINDIR "skins" #define PIXDIR "pixmaps" #define RINGDIR "rings" +#define CODECDIR "codecs" #define SFLPHONE_LOGO "logo_ico.png" #define TRAY_ICON "tray-icon.png" @@ -46,8 +55,6 @@ #define FORMAT 2 // for 16 bits format #define OCTETS SAMPLING_RATE * FORMAT // Number of writen // bytes in buffer -#define YES 0 -#define NO 1 #define OSS_DRIVER 0 #define ALSA_DRIVER 1 #endif // __GLOBAL_H__ diff --git a/src/gui/guiframework.cpp b/src/gui/guiframework.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9db3fa8d2bc43c36fff7ecc7b56cddd23cfd70b2 --- /dev/null +++ b/src/gui/guiframework.cpp @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <string> +using namespace std; + +#include "guiframework.h" +#include "../manager.h" + +GuiFramework::GuiFramework (Manager* manager) +{ + _manager = manager; +} + +GuiFramework::~GuiFramework (void) {} + +int +GuiFramework::outgoingCall (const string& to) +{ + return _manager->outgoingCall(to); +} + +int +GuiFramework::hangupCall (short id) +{ + if (_manager->hangupCall(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::answerCall (short id) +{ + if (_manager->answerCall(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::onHoldCall (short id) +{ + if (_manager->onHoldCall(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::offHoldCall (short id) +{ + if (_manager->offHoldCall(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::transferCall (short id, const string& to) +{ + if (_manager->transferCall(id, to)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::muteOn (short id) +{ + if (_manager->muteOn(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::muteOff (short id) +{ + if (_manager->muteOff(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::refuseCall (short id) +{ + if (_manager->refuseCall(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::cancelCall (short id) +{ + if (_manager->cancelCall(id)) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::saveConfig (void) +{ + if (_manager->saveConfig()) { + return 1; + } else { + return 0; + } +} + +int +GuiFramework::registerVoIPLink (void) +{ + if (_manager->registerVoIPLink()) { + return 1; + } else { + return 0; + } +} + +void +GuiFramework::sendDtmf (short id, char code) +{ + _manager->sendDtmf(id, code); +} + +int +GuiFramework::quitApplication (void) +{ + return (_manager->quitApplication() ? 1 : 0); +} + +int +GuiFramework::sendTextMessage (short id, const string& message) +{ + _manager->sendTextMessage(id, message); + return 1; +} + +int +GuiFramework::accessToDirectory (void) +{ + _manager->accessToDirectory(); + return 1; +} + diff --git a/src/gui/guiframework.h b/src/gui/guiframework.h new file mode 100644 index 0000000000000000000000000000000000000000..affdc23457463a801008b9fe4ad40ff8047008ed --- /dev/null +++ b/src/gui/guiframework.h @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __GUI_FRAMEWORK_H__ +#define __GUI_FRAMEWORK_H__ + +/* Inherited class by GUI classes */ +/* The GuiFramework class is the base of all user interface */ + +#include <string> +using namespace std; + +#include "../manager.h" + +class GuiFramework { +public: + GuiFramework (Manager* manager); + virtual ~GuiFramework (void); + + /* Parent class to child class */ + virtual int incomingCall (short id) = 0; + virtual int peerAnsweredCall (short id) = 0; + virtual int peerRingingCall (short id) = 0; + virtual int peerHungupCall (short id) = 0; + virtual void displayTextMessage (short id, const string& message) = 0; + virtual void displayError (const string& error) = 0; + virtual void displayStatus (const string& status) = 0; + virtual void displayContext (short id) = 0; + virtual string getRingtoneFile (void) = 0; + virtual void setup (void) = 0; + + /* Child class to parent class */ + int outgoingCall (const string& to); + int hangupCall (short id); + int answerCall (short id); + int onHoldCall (short id); + int offHoldCall (short id); + int transferCall (short id, const string& to); + int muteOn (short id); + int muteOff (short id); + int refuseCall (short id); + int cancelCall (short id); + + int saveConfig (void); + int registerVoIPLink (void); + int quitApplication (void); + int sendTextMessage (short id, const string& message); + int accessToDirectory (void); + void sendDtmf (short id, char code); + +protected: + string _message; + +private: + Manager* _manager; +}; + +#endif // __GUI_FRAMEWORK_H__ diff --git a/src/configurationpanel.ui b/src/gui/qt/configurationpanel.ui similarity index 87% rename from src/configurationpanel.ui rename to src/gui/qt/configurationpanel.ui index b9c1ebc504b55262c5f9f9e6dad5f59e05dc8753..72a52c2999473931c46ce33f405b055b10d18d90 100644 --- a/src/configurationpanel.ui +++ b/src/gui/qt/configurationpanel.ui @@ -445,21 +445,24 @@ </property> <widget class="QRadioButton"> <property name="name"> - <cstring>useStunYes</cstring> + <cstring>useStunNo</cstring> </property> <property name="text"> - <string>Yes</string> + <string>No</string> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> <widget class="QRadioButton"> <property name="name"> - <cstring>useStunNo</cstring> + <cstring>useStunYes</cstring> </property> <property name="text"> - <string>No</string> + <string>Yes</string> </property> <property name="checked"> - <bool>true</bool> + <bool>false</bool> </property> </widget> </vbox> @@ -1177,27 +1180,6 @@ <attribute name="title"> <string>About SFLPhone</string> </attribute> - <widget class="QLabel"> - <property name="name"> - <cstring>textLabel2_2</cstring> - </property> - <property name="geometry"> - <rect> - <x>20</x> - <y>170</y> - <width>371</width> - <height>121</height> - </rect> - </property> - <property name="text"> - <string><p align="center"> -Copyright (C) 2004 Savoir-faire Linux inc.<br><br> -Laurielle LEA &lt;laurielle.lea@savoirfairelinux.com&gt;<br><br> -SFLPhone-0.3 is released under the General Public License.<br> -For more information, see http://www.sflphone.org<br> -</p></string> - </property> - </widget> <widget class="QLabel"> <property name="name"> <cstring>pixmapLabel1</cstring> @@ -1217,6 +1199,27 @@ For more information, see http://www.sflphone.org<br> <bool>true</bool> </property> </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="geometry"> + <rect> + <x>20</x> + <y>170</y> + <width>371</width> + <height>121</height> + </rect> + </property> + <property name="text"> + <string><p align="center"> +Copyright (C) 2004-2005 Savoir-faire Linux inc.<br><br> +Laurielle LEA &lt;laurielle.lea@savoirfairelinux.com&gt;<br><br> +SFLPhone-0.4 is released under the General Public License.<br> +For more information, see http://www.sflphone.org<br> +</p></string> + </property> + </widget> </widget> <widget class="QWidget"> <property name="name"> @@ -1273,7 +1276,7 @@ Montreal, Quebec H2T 1S6</p></string> <data format="XPM.GZ" length="58862">789ced5d596fe3b8b27e9f5f114cbd0d0eea6475625cdc878eb3b4b34ca72799e9745f9c07c74b76c776363b07e7bfdf629194488ad49249e4f40151109292488a923e7dac85a2fff9dbc2e9d1e1c26ffffce5fea1f370d95de85e74260bbff51e6f6f67fff7affffdf72fbfae2c2d2f34971696165717967ffdc72fbfe21f0bdd05682c37d61a52ffcc7a87f40eebbb4a6f2a7d47e93da57f17fafa4aa3b7be2274e8487d7d49eab8a4f43575fc96f533d2cf58df547a53e953a5f794fe22f48d95f5de866caf2df58d25a9c34ce96beaf8b9d4cf96cf56f9f890f5333a2edb1b2bbd29757c547a4f1d1f495dd7c78b44efb13e507a43e9db426fae6ef49af27c7da93797943e50fa9ad4f145ea49fd65a577947ea8f5ee1ad7df557a5f1e872dadcbe3f8c47a97daefb2fea0f4a6d2ef95de53faa5d4757de849bdbbacf496d03babcd5e47f6ff52ea9d25a583d2d7a48e7da927f59f125d5ecf27a537947ec47a97eacbfe4c94de51fab1d2fb528767a927f5af94de913a74b5de93e7df577a5f955f14fad96aa7af9ee758eac9f36d28bda1f0b22775dd1e9e28bdaff403a9f79695bece7a2f799e23a5ebe7f9ac74f5fc7045eaba3e7c4af43e1f5f557a43eac0f8eaae25cffb4eeafa7ee354e90da5f7a4aeebe3b5d23baa3d7e3fba3d7d3fe14ee9ea7ee250e9eafec195d4757dfc5debfd06eb6b4aefabf62fb42e8fc367a9f79795ceef5b6f2db99f33a927f7734fe90da57f937a52ff20d1657fba4a6fa8fefc99e8f238bf2fd43b7d3f51e9fa7a6e95aefa8f9b52d7f5e14ce91dd5de86d0fb0d7d7df855eaba7fd854baea0fec285dd587b6d607f2f8b5d2fbaafd2f5a57c7f97dee27fdc153a5ebf6264ad7f5ffd2baac8f37521f2c2bfd4ce90da5ff10fa20391fee4b3d29df52ba2edf49f4019fff46e91da533df0c06fa3882d2d5f1af0f4a1f683d4a94fa24622e4add123117a56e89988b52b744cc45a95b22e6a2d42d117351ea9688b928754bc45c94ba25622e4add123117a56e89988b52b744cc45a95b22e6a2d42d7f177308d821399bf77544f979e4b598c32ef6b08f033cc70bb55de2155ee3cdbcafe8e30bdee210ef7084639ce03d3ee0233ed1df09e9cf38c519decebb87ef2bd53147ac768d2f8cb601612dfbf7136ecefbaa3ea2600bb70855dbb883bbf8b960dbc5366171847bb83fef7ebfbd54c51c1e58dc16da0e3f0eeef81df91dbfbc4bdbfb848b23622d282cf99570f4c7abb6e379dfc1b7962a98c31b1a3ffddce6fb7b52aac53f091107344a5ff2d87c42e8f8eb2daf0fbf25fd7973d4e1293392e0a5eff8a3a0ec6221b785b6a5b7eef7bca53ce6e8e915f39bbd2de7f916c43f57c17a2b7ebb903c966f6447ae166c6b29be08cdbaddde5bdf3b6c187c342a28bbe4e1b0255c278edcc0267e05c42d00e8f8b80ef0ad7b3e5f298b39465c96cb06849b2b42c8359ce1267433c797b1e36d0d88db2e0ab8f2c41db108a56539f602bfa9f324fbe1cd31077d838f724740ea4786bf60e02b07e7597b0fcedfbae7f3957298f372dc00d7ac327f911597e5ac818fb108a7699915dc14c8c40e9cc105f48cba874ead9ea7fdd0c67da3676df8d56f7defc8df4cb928b7751a855deeea602b507694e1bac5b7eef97ca50ce6f08b974baecd32c47321cec93c0db2e0521f37c38364e3ad32838ae3dfac73749df69709bb3d204e931bc915fd774dffabbec14d5e3ffeae900f9a72513ee6006e1d9edb0a9786a1c373776fddf3f94a29cc2dfbb8c4ba4b6730cae19c15abb52fc9fee073a267744163ab331a1242cd7657a13016581fcf6151d99151760727b9656fd9fa53e561f8d6fd9eb714638ec63eafdd649559ceb7afe0dadb5ec5f8b1652f96b2ce98e7743fde97e7c685a587e42734b3f13698900771ef29dfa211f9166790d766618c46956bc2234ee9fca76f7d0f5e234598a3912ec45f3725caa80d9e8c7bfca4f62f57ed2b3c57e5ac6a3c87fb70477e64533c49dcc7198ef101a6f81d66788477d9676ff1dc1fd2d2a7e7fa0c973084195c12b70523bad4b3676a7da6f90c3a30a61acd40e91f7847e567844260940e71421ecc928e1e877009f7e40bbfa49c293816c80dae7ae7df568a30079fcac4df380b96ef4b265137b6d5c4fe4f55fb6ad87307256b54b0e70861a94fd9f1c6ca1c5bdee439e6ba4e12af4bda8147efb9a6b0e9cf3f402bcb4584c5cf057ddbf58dd6701eb80eb204ca32e47b4821cf0d82fc3548fb4dcfd66bf319db956a2fe144c8b1a2037d49ec39b82859a302cf116b14e604888b8c676bf35cb0ceb6739e0d62a6dc3a84310311706ff154f83cb6853d8171ee39a64551ecf7937ccc4149eee2d859418e425bfc894ffa37ecb9b29833edb97c8cc343b9bc00f99f498cc3e539ff465c6322085d2ef46e064e7156a65fcca98955483ed8b0b04e7b5e5c5780b96e0e772dbb1126f20ef2b84e656089add27c43a5ab4eed3928392e97e7395f3e1436c9f619e13d8f50e9fe84e9caf11c6d897f41c8dec91cff4ee36cb64edab359e9dc6ce2e11253da1cb8497bc66401dad7f72131c7fe68c817ddf194ff421e4288eb5655995563ff7295b900863d57d287b0ecb9c3dc92599e3347d16d63ffae1e95b23c0703c2e83159edf6fe84b518a5d639a4f5c6734edaceb199aa93e1397811ef029dcbe52e957f33f2c09f79144d7c06f2904d3bf27b282efdbe92873908fba397e18c395c046cc0849bac5cc33969d7e5f2ef567ceeba548dd7f39ce32fc038cb412ecfe9d82db6c0e226d00c3fb4b8a7a351a5fbca2832b849ed77788ed84af11379d98e1da86a3c18fb3fdb769b7d9daead598fe4610ebf0438ab2036a66c3bb75e12d123241c7a8ef7b1879b79715e279fbb92df0bae61c6e772633336cfd93618f779cb3caef6d93c67645c9969b2e56d2ef5cc0ae051362dc3fce4f09cc54d60e571a1aff6764c4ecc9c636a5c67e6681d928bb96e96af60b7242765e78c185164c2c20a86e6e1898cd6375f9b467c4ee57269cf09b1a42d9be96c1693e7f27d0887e71a9eb34f8de3724434796ed78ec5391cc8c74c5b0ebc36059c5b9cc628b679ce9e1940bd36cb735c86fc629b17ad5929ecc398c7e71025ce1d5bcf325c5421a606570e9739f32838bf15f6750fb3a36726df1afeab50f76a7bce1335b5388547c5bc3c04581c28222c840593af02790b93a3a41de8f09c937b25de73cb8f1c3b8fd80cef69ef049f4c8e53dbccdf8ff7945c9edb7478a56214173edbf5bde7001ac1d788ad2e3dbce7ccb374f2ad79db4dd27aba2f17738e9de3cb45ad1bfcf0cc7bcc7925ce1be5d85a6453c1a3c55781f94966ec0ea6bcc7b6e79cecabe5f34a9e9b94f673c5f6fc1628aa26b93cd7b6b8a354ae8afcd6d574ae0878edb990e00d95bfb238cb9a7f97999f774863ab98a3796d6e84f4c4267c9d3d07de397f8c22cd5b6ced59f69c930720567379cea8af6d35cf3dc8f4c3e1b90da757e30ccfdd67b82c67f3e749de577279cef65bd3b9b7c44df464fd3697f4493fe9a33c37d3f15bf385ce7a62d88f8615e6d873d725be427815cff96d2df23a537e38e23da6cde662ceb60f7f64e6c505ec28b8346d446ec9e63907ab561f183fe4d796e5b85d7a6be610a32b88cfa5398313b56713b59de68d3d305e64fcaec7333121c9af96f0339356560dae4bfc34d39e2b9789b0ecb9f27eab27f2e8f014fb9c16cfb9b195877c9e0bcd37b7da6cf31e8be7dc6cbe555ef2dc83cd6564cd1d1147cf5c216fe263c68493f9bc623612f9142f066f9c786b98b9089e73492894ba971773ceece42fac7c6bc95cadc573b963bbc34b9ef920786c1c67abaa22cfd97c159887697d3721f754e4393cb2ec46af9d305f29c0dcb5b68792586f9abff4320d5c383ee441e29b56fad69ff854b7b39ab49dda732539d3b2e7cef3de6b87973c2c0426dfb0556571cc716e7b82e79a165f79470998581cb5ce67a9ca730dab8dc2797df54b01e63a094f64e36dde6f02c907f5fb92157d5e6c2575137e34f2adaf9957720139b90bc76fcde484a83fd9785b259e7338cc6bd1d9795299d7a8cc73b7561b7e0b08e6397bb370fedc5e300ee6650d62466f79a8b8a289f18d57ea8526f65cd9d62c7bee22ef8b5b273eb7eb8eae3831b84667a52ad9733cea99e7c85eb39527d57da8ca734e1ee2366b27e0a2884e136fcf213627a4107367c118981f7327deb2b93ea3b79d6b6d131afbd2f85cc9595036cfe5f1a39b6fd579247517d0e21a9d4bafca734de71c1607e186fd0dace6daaa3ca7a2d7693b632b5b36219426b6dec7cbf1ababfa1488f5fba3584f5e4eec5a2d7e29c20c8d6487b23e18350d7baee437f98e3d27da7bc26bfe4aec0c76f026b5313df34a1e93f9235bc416296725b9d86af61cd77062678487a9980bcf2b99d8f3eabeeb79ead5790ef7ed6f64a9f777c4a12d7a73d6ad3cc72eeebd359eca4809cc75d03f4fe44f4fd91b1f278215eb861ecab973413f965a39d4fc68bda3697cae24e65c9ef36df024d8cfbf9e08bc40dfcab38a2dc9c556e539f12e79e7cae5e607aaf31cc7654a9c43e639ea9752df1afee9e52e8f756ac5d5923cab3d57c498537268e62cd4d18ecafecbfa16b68cf85ce9b547f025688f5a9c5d7a9ef0d8988d6bd671fc43e215b3debeb17fa9c479ccb97b95798ef717ae8f42e89fd3ecf492dff11f7839c2f10369b4cae44c61e4ceedc41bd8b2ca5c72066b8dc6bc7dec9bf1347b44b66276a5a39999b928be6d10e2b90cefb5cc33e3c4389659338e9ea93e66dd01f12d56ee7976ecd89df33d843b37fbd8386665897196c7a9e4417cd0ef218c2b58f172841127232bed30cb21bed9c41cc53bcce59e174260c6e6c3dfd5f10a1e49609d15fbef75a9ef2176325fd3fc20ab4b1e6b7bce7cac8e7dcfe6f3f1ab9527357dd5a32c16f029e1a6ec9a08a9afbbebe662bf8a6fd9be67cf43e79e8b1da7a5c2ba4c2b7e8e202611ab21f9befbbac89b794e185dc1cb6c7be4b3acf96b243c7a55e50af10b5c65e6d8a5f24d62db89cf6d8ad506f116ce0939c46770e7fff29438eb0e4710f2e167b01d5ef781fcd4291e93bdb8091db21bc70070e79bcfc2e74142cfc8df07f2b7ee704cfdf0f7615f7c718b47d4fe0c0506a7f3febab5e2fa73df8cfc6be15ff2154a45d1e89e7dc12e1c10a20af334342e1f54cba1951587e7d6dfe31c51a4545b67932cfcb273d856e77d65d5c4b1e7fecb57f49daf545f4f18ce3cebcc59f3e4b057cc581f4d2c9efb3ecfafdcfffbe575eba613dfadaa58b1c96dcb7812b2c53eba58f3e7e6f265cacf254994fb156b6cffbddf87c01659619bbcc666096bec230b790c29cfcdbd37957b7f1b9a1bf54ee79b605bcc3026ff7b884fc15253ff4c82f83b385ae025b1e77e8a95cac9537dd4cf148f0803019ff75dcedda7bbb52efa407f3d7122eed30f98fa675245cc6949e69ded64e35c1f51a08f6d9d8703a0ff6bf47b08ed7c6e62b2761073b774ccfbf646cca5c22b03ce7e8e5f01c12631db4b92d53f224ea9317b8adb74be06af36360dadb543e3ee4be4b9aa8220a2baf82c249d394c96df3eaf8ad8247c9a6b81006ee008c5bccb75f5053e582b324d6cfe34d6779af1d7a723dc4a66ac00b5b108e7380cc589517c952884bf06123c074311b9a636c4b70fd64a50740d47b84eef53c1db04f738c6276a690cad74bd46c2ce3936685ba7f6f5fcaa323c774ac7bcbf60103117161abd8e047a080153fdcee20f7a26e24edf132a1e04d7c84c83989706537af6e2c91fd3336b094b3bcd55c13d3dc9699a3d23dc3e0a1c93d5d3a2273862643cc240cdeedc127692380fb5ef99274f3d7a54fd78e49537c56a9a6dfebda60798d179fa12a9544eac43774c489ca2f82e312707c1f938d9c63db5a1381487b84d75c53b764af7635baf6020ed39be2f219e8310f746cc85c5e405badb6da103f38bcea0121bb465665d7872aebf4b8c933c0f40ae97cc04854769f513e2da3ac34a3ca34ac09d6431f20a037379457d3ece7599e7668a5d9b023bb28764a3b6d3160889399683b8428d4a14efceb6ea53bab2a5e895fc46a304cf718b5e8c47cc95131ecbf6388ed74e5732a7777906721d89470fe6c8e6d2775da09339418f4e13815562b7a9594bd86522064123aae0b87e78ee07f1a4e04048eb19ebe22d4aeb9d38746622826c84b66f25163ec69c9a9ffbc73d7d8d65ec3931c3c0bf3f62ae8c306371448a79cef8f681ac37f915ea3a3353c35979eb51e7f84539168dc16dc144b2969e1387fb0223e249499ecbff5d0818a4f612fbad49691a0fd9cee335eddae96a13c2560bc51e39da92fb8d188fad8a87cbf15c287a1331972fbc7afabde4288101c173e69a21642bb5d53aeb6c0d11f7b4d2df39c4b1b4e8041f12beee68ec92df729dca919af889ed3042e494acf47b14489ae9b857becd0f2de2448d6830ed3ee6e24bf9a6703bf41fa1f888da7e0afd2e19db0ea17b704a0c3911f6a8b01f794f097b2e2c117321c1167b09fcf536bde3438939e639e38b6ec154fa7fb27deea43f49a5e52a3a5bd25aa367ced8a3b148f2c4a2fcc2465873f42cd14597e4b982fedda74cc83c675a9fcc73b0adbcd912d11fb6e63c5fe4d01508bbe007db78e2ad929e45299e0b49c45c48c4334d474acef69c2b9e33ec7ab6d2adf71c26621c250e427e4ec2b34cd0c156d8808f4b5d708767652469cf15f58fb84dfb32c29e4b7a21ceca3c37947fcb5e6d766c657b33b100c5fb26cb94b3e7421231e717810ecbba9f04786e94c58688a7249ee391b2e3b46fda521ea9f47685e5e5fb6587923c67d973e6ac7969cf9d9a9e72416b23ee97b326077be3497e83af3df2dcbb094cf819682ff394b5a987e746ca9e33c625b6a75ea4b720d0435c90c6e59e9903d3b89df00492f9bf34864ba40ec379cca4ec84da51dfae727c2ec3733c86bfa4ab7d91b5300dfa98fb6caf6d2719dc0deaa988eebda416204764c6b2d7ca9e1be6d97310f855b388b990f00828be09ddc031fb93722685e00e234a4b969a8c9534e8dd5f17bf8e04d2a6532312790f633342c1f113834f84374825d6a9ddafb00d331561c6222f52fbd26a55b2a30ccfc9d8728bfde545dc00b16eec76deec059cb1cd3aa41a138ef08937e996e37e77e25de0b743c70f5b9263a5671c686f3fe621aa0bee894c02bdef7b34aaece9cc8fbd8616dd5995d1a2e313fe052f61bf19712ef1bcac1a5fed118cd8846a127300e156b10c79230d7745cdac48bb51b02e21d62a0d8f464e6e28fa457d1a17fdde35e1f252bc5d847cd01904eafdb6f88e82aeee2b217028475a7a0ff95706443fc3bf688c81bc47c4dccf2c345abef91783ef3fc721622e4add123117a56e89988b52b744cc45a95b22e6a2d42d117351ea9688b928754bc45c94ba25622e4add123117a56e89988b52b744cc45a95b22e6a2d42d117351ea9688b928754bc45c94ba25622e4add123117a56e89988b52b744cc45a95b7efdcffffcf2ff6d14cd7e</data> </image> <image name="image1"> - <data format="PNG" length="6033">89504e470d0a1a0a0000000d49484452000000ad0000003008060000006357fade0000175849444154789ced5d6d6c1bc7997ed6708d95a018bb86132c85d4208dd8c8d24910321f88a8ab11325723a6ec0026e31ffa68028b768a546e0f81ec1e1a5b2d12592d5a8b31ae95ce6863cac89d2501b14503b12d030a4c164e4505f6891b38b13688032d210422e106e44211a4851a60eec76a975fcb4fc917f7a2f9f348dc9d997766df79e69d77de9da52e00900130ab842d4b8448090034202b004303560618d94051ab59cf1a7e7f913ab382027c84902b77e210120a82a2042101002900ac213acd16d84d80e7511ee2965aea7ee88035fce743aa1aa6e5ef1232f089848010850c165052005d19320c0b8fc58213cf5b30ba915a53e0352c1b2b625af71c21ad174308c780628c5a293acd160ceee311d958bb6642ac61495c57ce8dde2542e2d7a749fda9118435138066570dc33109f5a727205ebf4dbeeb0e59c3fb1f4b322d7f3749da2e4a101212568b598ba18d03c60fb830b461cd645843632ccab48ebb84340e0baac2ae22b316434106ea4f8dc03d47c8fdd0416b78ff6141a6e5ef2649e35901b25221636e5e04d69bf492dc0f029d4ff24829324419e8fa305256390c9dc2d4ebde152dd25a920b64e0fc10a2ff13416ee21fe5e178dc01cb631644eaebff296ce996e40209fd2d04eb235684ac5b57652672cf2d90fe77fb217e2e42bc2381df66816fbf0fe2aec68acbbf17f219a1a1f7c0719710eb7008b25ca63700299cf9b10b2d8fb0a879204f3ff4149180c6f3a1b2bd0c0caa535cef1c21bda7fb31703e00060c64c8ba0c46ffbbf6ba70e257c711b8cf4d12e1ad6e12ba14820c1957fe6b64551443fc7927199d0865f587cd6987eba4bfe2817c2fe433c23ca6752f11527f2a5411c35efb891b2e4b0d8a25dfd528066eca659597cbb8a95f7a11a0ca57a8c8a1763221087add1c678277af070db60688770428090583e1201289b87ecfb9bf9c81647feabe66dc96e402095e0d82df6683f0ec8e152b84edc66dd2f6b3b6ace7d460b321d01fa86a00afb67c85308f6907df9f2461512adfeffab009a4dd8162c93e1481f09552953f17340b270774bcfa42590ae599499246cf0b3aa37a767b60e9ee32ec40d7174912f92c02ac079497f6142cdff54592b06676454cdc925c20439b0a6fa8b8a6a649b5cce49e5d2009395111b3796692a4e9152fe47919407a06ea3cd001741c2cd85fa1ed9b2a1ed895cae79e5d20a08142fd95c5b4f4ad2469bb1842254cd8fe0c83c06ebb81aaaaa95a86cdc573fb2ca09fd85ab2c32cd149d2f6da41bd7ea7d355f15467bb719bf49cea817847346c93d3e9c291431d886edf4af99f7712795e0653c7a0ef0f7d790cc35e1f27c77f7d1cf2bc0c7e1b0ff7f010a5c919181e857033a42b8e96f86d3c1a9eb6a1e34047d68383ff0cf10ff70300fadee9436a672395f8f319d2ff6ebf9e97e34cfa0c72f28d6e245af7183e784d6ea374e6b77d10773552fcd83839fec79eac19494b4c1d038fd38bd6431e7d4d508e7c4c1da3b75793cfb74448ffd900462e05f3eae238137c7bdbe169766374d98fbf5e6b888f10c2fe61a4622664698b61c301d5861df8745961ab60d84c3c1c06524b84948a61e0cd7c960c42380a718f9bb4eef6807d9c877db3a9e488d7a64ca68e41ef9b27c09b79c8dfcae83ddb0f211c45381c8270338ac81c21032f792143863c2fe3c8db47d19153ded01ffaf58794fa46060340f29f245dcb038b01038e33c16ee191525248dc9121de1121de1131303c843363e3445b14750df7eb8c1897e25000489f650f2c25b1a8ff3df1a5005b81761efb45278efeb60bb936be67b7078ee71c880338f8e6615dbe13bf38067a330d9666d17bb61fe1700803970298b819817bb9dc72e4c37cfacf892f0574ce248963974bef239ee3c13fc3c3b2d984d1bf8510bf1347cfbb3de81feec79565a65faf35c477290a59c96038a53cf4df91d0eb3456dc404c2abb9c5228cb299cf8240e530145d37068532d75666c9c1c7cf33000a80f2421c37f363ddaf1b48d30750cf86d16b89e77a165b7278bd1c683d7207d2d41b23f45897f6d447cf9771780f0d3360200f2bc0ce92b09ee663734264924e270ccce128d793c3349e2f7bca0577be217c7907aed6764e0ed2e00eaa0e838e403ddda46c918d5dbc10d5e26474fa9f71c7cf330ceddb84d846777507a7b00c88a0c0e404a4965f5b90c95d1db9b5ba0bcb4875206cf91e8271214590200d89f76a0e3800f810d147532a31e00f0ecf5c0f29b2e6aa4bb2bbf1f2eabf209007a930be4a9b0ba781313227a974d9b4ae56b7ab109be0e9faeb04ea70bf6937e8ab9bc6c52415ddc052f0555523879040e2cfb69bd4b8404a50c46ac046560eaef304c271eb3acaa1fd71f8ec0474afb6fc55d8dd4e4d838da9b5bd0f07883aa2060b2859b072604013da7fc786a5723e8f72ee8e506b76ca232176596e824e1829749ff1e37d1b233600005e838e00353c7e8e5873e8ca4a7e08c81c2733cc45d8d94ff2f437afeb4c266cb9f68dd43b5eff7e97903e70772a50743339001b0349bd5be633f3f06f7f010a5d9e83da7fc088783100509138280fe77fb31787e100c002b6f4d970706bebdad5972e4f6833d3a4db8e065d2f4cacb59fd99525255c9978aa5a02d981930487c1587edc66d62894e12d7d434b15cbf4de2b1b87e7d4210d0925c20eb6500839fa754f756954cd8f6e114a22d56e426d383c0c91f5970f443ac0ae3ca6031f8a954966d3ab4a996b2a839e106e0989d258aac40fa3a8eb81847f0af41c877d2d362cf9f7a303e9324c12d9b287e6c9c0c7e18847033aab240868dac2519324003810d14e5f19f2403c3aa32f69fed476489102926c1ff5a5af1da5f6b817279088984a8e76fdddf8a406b9bb16dde60c3c07935af70338adc55432e93690ca7bcfa7296e9e3d9eb41fc2b49cf47332c3cbb3d1802003abf3d59a6565e3f78f3fa01501553c8f9ad1cf9e46fd3fd2f43867c4746ae3723f33a53c700eb976dda9e890abc0506287c2902c8575a0038d26001c0e0e847c28a6c5a0d0302835614575823ccdc4050a04e3df4079749cfdb7ebd4387ae06b1f8fb3eddb460ea189c7cb31baee75ca8d95c83c1f383e83f15d0efd718a6a5b9039ad2621eb03ad22ac68001cdd5647827d23664f4b36841794d4a46feba1a9dc9b4fcb94c96ae2f6751f99b2eca92f3fbd0493f185d7e26af3d3254ffad7fd9a6a5b91a9c79f3046c8fd9f47ee839e5d7e58182aae4cbaddfb3d703dffed6a26b8ed18db558e7985b2042425e31138e88f92b4c2d1d696071612f9f56c015d4138e4968592a6e22786692c4129d24be12f7591fb1662d4216e75318381f503bb78e41642c8484670f355a5f4b05365014ddda46d15cda1fcdd2acda91f5b59467af0700b2cad3fef7ed6dd7ebe56d69fb3f303c5470a069a6850c19ee67dc7aa99936a3f6e0b3eb2b7f20abf2a7e5d5da63bb713bbde1500784825720ee6ad4fba1757f6b56fb3486ae543efb63f6bc4560218575ccceeac154eb4231052bb639372fc2cb9b502c797913165e77a1f347f615d717fc3c5554611b3d2fe0f06b476075d82175751365f01c714d4d13ef1c219e992461af8f93d4effb48d32be9e98ee778b89e71a7059e07a4cfd2a6484b7281848e7612cd2593c9303280633fefcc6aaf66c331750c5a9bbdfa7dc7df38ae5f0f874390baba89eb8ba4fe406c376e93a1e61612bf13d7f3771c6a2fdb66ac6406829223ef727be20929cb66cd9c115a920bc4fb6a6bd5366d66fd810d14d579a043bf7ef89787c18f8deb44e39d23847eef02f13fef24de975aa1f5d3fa506c397aab5a06ac5bc4c2a12694936a1e007a9d16b43f6ac18e4b2120515dbd822c832ff020688606bf8dd77dacc1ab41e02a00f80bcac56fe3d1fbd60958cc163daf0c194daf79c1ed7113657e11fe5d8d5979726dc0a14db554fb5b7d64e052207d1d406b732b4636a6dd7421eb56eadc8ddbe4c8db4781848ce0d5a02ae3b2570219361dc7991078a70fa3f5cb71c6193e4e239b91e34c902194adb896c7d2ac2f4306b3595534df8b4de879c70fccab5e92b6d70e16ec07206dd366fa888de463ea987c393a0e52edda9a605ef596687dd1e54a9b59fc361eac599d0928cffb932458c90e5806bab731b8b0c78e9a1f14d487a2e948780afe9b892a76c81874bc5a7ccbd576e336097d348a442c81dcbd7519321a1e6f80c96c82bbc195151ce25b2264f0fc20848f05a49414589ac5220db81b9c30994d103f95202b32cc0f7268fad7a62c85d4621ec42fa36069169c9983a5f3684139e90f2e13518ce9f76bf571660eae1fb9f3362ae80f2e93f087612cd2407f4fafba089c499223dd47c0d22cdcfb3d48ed6cac782365429880f9410e09cf9eac7ee83f1b80f499a8cb456f66d0f0b80d56b315914fa37a3f68f932e5ebfd55378636d566c9e7fcb1b3e0cea316dc94595f4a49817fc40e578313d2ce1d7a3e0a6f5d2015ef50d5997061b7a5a449504e8a4829345e1581afe365d7cfd02c7afffd85fb3ab8650def1daeabd4a66c79c68e85438e555158007058582cfcc401e7e37cd972c84a619b9681ea77a66f25c9c4d834197c7f924c8c4d12fa56927897c8da9b11ff0f707dd9b66499d15cd5a49a0780d03e2b4e706c3adeb6d40e599186594e471093d3cc1d060b20043f6741c70a3bccbd44882403b2928283610dc3265b9608111229f0cb030c5b2a0f322956ffa8c156b6692649120905e2b209c53100b37de571c2891bd3242a03525c8297e7c19b69880fa5db437f314b0627e259395d3c0ff7a34c55effcf1779324185330288afa1596e5d0f7bc452f6f7d59b664dd22667fe285a948acec6aa4e30d269868170e5e0a95b46d8b353c26c78dfdc989f236268a61d37014e1587a0d303b4748a6e2f2330ba4f67739311c6f5d20dd4e3bb0b3faf8d2962542ea4f8d403e1582edcf93e4d84fd336bd7b8e90dad39971ca2240b3189f21045baa3f6f227123490e87d3fef5702c028661d19b715fe4f354467f48fa7d5d61169d6393a4615779e19e3e4288f77c04c1b399fefce5726322ba9fb3eaf7af2b87616fef6bbae70aab259f8dc599bdae15316db5f9cac1708eb725f0c954d675ffc78261bd5de12806df9b24ee2a4d94814f24353644494148c859d7250586ed0d25e22b6a6f0a8a610c48e6ab507d2f5ae1345b0cebf74fc8e8f9f33552caafee9e23c4f2c72b088af9f54149a1db69c7c44369622869d35edbef867575cc573d4dfd1d705d9cc288b868785d57dc227215ebf06af39583b9e5690ef9f483a60bd61f8e49681c8e82bf5bb9e222a7dcaceb4abe5c2a53ad6c80a240b9929cbe6f740345b5befa14756e9f0b0c9ddf6eed9d3fee8ba4a1e2d2b792c47a7a0431a526af1e33b388dbaf7b61ca99a1d69be945c4146366b23dc2df131bd6fa2010fe5444f8d304b07911271b1c687d8486e981745d3e1b8b60c28ed19bd17cb938cb77c6b4b9e5a59414f8cceb8b72d1fa859884c6b332aecc244925b6ae58a41d3c9d2f17941458bafa76ca00d802e5323420e6deffc4266a6a8e90a6e110841cffbb0c167b864330ffc738f1d918806621ca718c880a62b9f1dbcbf93a6d0c7a7fec327c6365bddd64424c36f0d3322cc6f7e6c7138c88290c7c2e63f49b14a0bd56ce7120af18c71e144c0f9b80afe2c03c8ba39742384ab3004763769f4337452efc8b05b5525c758765c86761e9f298b6425bb82cc6cb298fa5f9ec075dc30028eef79695141a87059cbb9524cc13e529aebab3241ab64367c49c7a720754a5281690bf10838f6ea4a86300c4ebb3a46b42cccb1793e3e89a50749bdbf0f9302cc6f7b9206ea1a84001b9d6d9381a468ce0fe2193f592e291f014a8772278f97c08a39f4a404c029619ba7b1b5b81b62ea76f95fc7a630aea4f8f2024a96643cd0380db942f9fcbfcddd9b4464c9b793d55826933b1eda280d10f26cb32155245da81028c087a65ed2d182b5282c1f99df5d478b30d4ca1fc05d0c65930fbbaaab0c5ca5fe7325b6064839d7c4edd425bfc0740fde71575e76a3e6e68e3f00c63a8974513cd18d60bb078e1bf471110520080f647f3e5b371a6fbd6a6454d76bb6c660b2e373780fcda8b33bf79819a7dc38b6e27078651af0f883286de1f27a5e284d962edf83fb669e532ca8d6fd9444d1d70e9ed2cb576327326440fd861e4cecbc575e2965aca48f3ad2655616bdfbd027c5d53748478f92aec5ea538231dbc14827d680a2f7f2464fdce2085788991f85d326dae4d3bde6c87b2bd96d26cb3d18d14c5efdc41cdbeee82c7c6034a0a415181fd2fa1a20794489adfd9a81ddf814d5bde00476571da3f28afdc750c000fcfe569fe91b084da3f5d01e6f3577559f87095ae85424c9b81c297e272ebd3bffb6cf6d28af51d322d9bc1b44e330a32c7e8068a72bfb483ea76aa516fda2a3b7e7d9ab8e7d2de051f2124712349fc825cb01d8518317740558a29a5403f96c1b40d7709b19e1e29c9b01ac6949a920357c3753280eee7ad799aefff28aa2a6ca911b2be8052964a2598b6107a1e654a2b5691fc8eb985156de55666d31a4435e5a069e756ea72b30bccb269d4158ea2f654082fbf75811c7ceb1aa1de1ec1e1ab42d119e35ed9b4320a3cff224ceb5e3eac70c7e911ddaf5ceef3151280f5f408e85bc6ee310dd73100261ea2283367422531081a76fef0de316d2e3acd0ce265b8898a95138a29f79469b36ddaf2ca4d6cdf444dbdee35f473963363c0402ed02c42627c454c3b224c19969be9e8cf447e8610ebd908bac251c37c368e41df6e07ba9d1cba9d769899c5bcfb64b068bb18c2e8fb9359334e26aed3fe38d1604235cc27ca29439d2c99aa88e3eddb5dda349081a2e5b45d0c2132364d4c334952cd217795d9b4e5973bba91a252bff4c2c61497df8869c58728ca4c2fe6dd178ec9551f9f3afac12489c9f94cdbcee7cf743e42c8c4d824d971368458229e27878db360bcd986633f7d8aa29fada7f89d3b28d3cead54efbfeda1fa9c16432f435094507f3a84c48de93cf9f5730f9827b652cef7ae91b0e6182ee267ccc4d16ff4b15e769a925076f91a3a794bc1119ea75845fcb4a0590c4c4431202cfffffb6ba458bd1ede82f6277930dbd5608d8afcb4902b529400a5fa39473f982603228af6476efec88126584f8f20f764f6ae09c0f9de24e96cb0a0c9cc16fdf6857789902bb1140e5f8d2026ca79fd686380c05e3b32fda70d77d52dd8ac1dad8cfabb9d7698766ea5e205dacd3dbb959a9d23a4f16214422cdbbf2d2bea7917cef726c99566bb7efcebfacc0206f7b9507f6a246fa414c59884c57f541608eeaff03c04866631f8228f48990a50cd8c510883a284a028433b89c78869331df80c9d597ff19dbb42e87e692be5ba354dda2ee6d767c4b41a534fcd11e2387b256f87331c03c23175e7c9f9de35c2b29ccae8348bb892424852202b325efe5d08face54ce0e95930342afa477a8dc4b84747d2ce1e0e911c3fb19a470aed985c4f64d2589667423457500888c4d938189fc1dd0704c5da49efb224912db3751796779e9472355c0849dcf70e87596bf2346bd7305f8b6a6ecf2cfedb6812e73e78801303136a9aeb62b60f262d8ddc0815f8e9ccf3debecf6ebdeac19809f59208d6747d57eb159d0b0abf4514e85909f21a42d9ccf40360638f6d3c2679b29b79224204859d168d562bb8d87ef496b9e9b31f2c16d322018ef6c39399500ab39a6959f59204dc3a379338686979b5dc6e7d366697ceec833429ac5f87e1b1c16b6a4c2567ab657bb8d81fba5ca4f33cc8c7b6596a79a6ad0c82469b84b88aca460e358c313cb7d8490f8378babf60d092d3e5793ab9cc5a8962ff8b90449a1115a8e4f95e43862724d5e3fdb3880a119b0350a6c660b6c0cd0b4cd54f0b44ae5d634090829080935facc6906589643fba32c9415c6f1fa0821fd37258c88290032c231c0cc2cc2c29830b88f373e9fd6bb44887d387f8417c5874d18773ae0287cb4177a2752159d7f50c969896bf8fdc1822781bb9708693c1badf85b0beec7199c7cce9e15ce381507dac211085f2a6597e3e1698cec7754742eed1a7e3fb0e477c4c4b169e29f88ae8a6d582eb6f3d599046bf8fdc0b2be2346df4a92c35743d9a72ade036468167dbbcb3b87760dbfbf58d677c494273651b36f78e134a3a21dac4ad0c9ab61696b0abb86a5b0e26fe39a6692e478584abf2bb54286759a81beddaeb2370ed6700dabfa36ae0cf56534ffc71246a4b8ba755781cd6aa617e1b359d1f9a4fa5ddcfba123d6f09f0757f415720d357fa0acd008c524a416e9ac3bb41d181b67829da3d7be81bb862bc2ff050be4dfad23e1fbe00000000049454e44ae426082</data> + <data format="PNG" length="5777">89504e470d0a1a0a0000000d49484452000000ad0000003008060000006357fade0000165849444154789ced5d7f6c53d7bdff1cc4d00dcaaaeb6a45d7515be52240dcb0a2da2dd5ecbc216cb46a18a85e625689846e22e6873aa23d75097dda0aad28a04a90803a25456b7150579c4805870948786a875df15eeca9bcf84ef4812b40b911ab622b9be22b86122bafd2f7fde1f8c63faeed6b27b4ec35e79f0fe1def33ddf73cef77ccef77ccfb9c7ec2a000580384fe821a2d85f01f080a202220f983920b084b1f92c6701bfbbc84ecf4180878806eec430aa72f0ca32e4385f3487a3d60aab39819667ad082f63ec51688005fce7435609d30ae344fd5f26d0190e40e544405500be3ce405118d66115d3f35a177c982012fa0712c8b699ba68936f705101c2dcea8e5a2a3d60a5f8380e8634b175c88052c898b8cbce89c264a7e3e414bdff123a8ce182c2fce1b064723a8391345e8da4dfab61b64011f7d2cc9b4c2f804edb8a0428e47be11952c828ac81e27bc6cc16558407d2ccab4b671a2fa0b4aca60e791598ba19ce4613ae647d334d1a3d0400bf8e86141a615c627a8fe8c023559a6c81fc480c575dadfae271238fc232b143586789247eba5414372784ec1d86bee392dd23c9344dd1f7911f9ef107293b45a826b9d0ba61526446b6afe297c69cf24d1c09f0660ffa11dfdcb1f9f9799c8767f92ba3fe846f4cb28a27714482b45b4bdd286f0fa3565cb7f18fae9a16ef4c0364e5473210235ae188b0624159cdfea86fb4900dfcfb30f2d8514a0fe52c47094814f5666b8cefb44de333e747ed409112214289a0e7a7f5bb65a70e4cd038fbc4b12387498e44b32142818faf8eabc18466cff61ea0df667b547ad4384fb7867d903f961e86788696dd3443527236531ecf06e37ac66144d9e2b11f45c47d92af29c82c4ebeeb20c2ab4bb85c2b2ac952d0866b8b736c2657321f445005c9c4377b007f1784c7be7ecfba7a15a9f7ba419d73349e4bbe083fd1927026b6be66c10ce1b63b4b96573563fd92c1678dff7563480e75b3fc34cebbb38424139623ceeba420235d5a158b2f68620ff9dab289e0b5e84835771e0171b0d1994679ca8ce65d518b5a5c103d31badba0de8bc4d14ba1b42e2eb04aa5eda5250bef3361156624e4cec9924f22e2d9cbf7164822a6526e718512299288bd93ce344f6979d501fa800a0b5d7e157df80ead956b0bd02abcadfd92c573fe7181178a0507b6531ad709b684b9fbf2c955ad601de4d561d534da54a193617cf3698605ebbbc6483f19161dab1679756bec3e12c7baa73de18a3f677da11bd13d5ad93c3e144fbee7d50562d67073638487da082afe6e1fb9d2f8f61706d880ebc7900ea0315d24a09aebe5e96d6d3db17867cddaf194e3a492b25d89eb7a0fdd5f6ac8ee33bcfd3c1bea30080ae135dc0fa7a16fdfd69eafea05bcb2b08666d0639fd5a17a2cdf5ba1d9fd65b2f9d3f7116e1f56b98edda4d6a3db63f6b464a27be9a87c7e1816bb7535b1318d18fafe6b5faa6f5f31051f7fb5ef82ff5e795250866b46d6d8373bb0de19938fe626de41191e998bf6c261479a76ec581940fdb7307a9122a60d84c6c0d038969a25267183cabb307d06850c1912d2edad7d002ac34a3f149a9e4884f4f997c358fd36f77a14ea843627102074e1dc56850413018807c3d82d07d22f1250f64c8501fa8f0fc76170ee4c80b1df3699d94f8870a1140a4f338f5cc0c2c112238a10a4ec906455590bc03c8776444ef44d1d3d78bf3d76e527a51d4da775463c4a97812710089bbd99d5c15e7b47f0fde0dc059a09e1daf1fc1ae375b91ebe3b7347850f76c1de200b6fd7a074488100433ba5e3f8e647512222fe2c0a9a3080603e8bcd489d0f510ec33728de8677a60820a55d3efc83891dd31cbf836c106f33a332c4f4ae8fd533f927792d8ff413bf83e1ea119a6d798d67771987a6414e9ca0228a8a03dfa86eb0927d0f369a03c7945f070830be6b54b4b4e31b66b3769dbaf77e8ea944e7c350f69a508e706279a1b9ab318cd334e14f92aa2ebe3b63e6fa1b48ca18fafa2f7d37e6432c9d58b031af3a45d95743a7fe22c1455c1feb70f6a3aecdbed81b979475639926f88769d6cd5f20df40c20b0b686756694bd6ff73e487b77315f8eff0ea498ba657b13aa5edac262beb314fa8b8aa41a0100589fb763df1e0fbc8cb1dc721ab736427ceb60c976f04c12d5ad9fad577ad155ae7e9bb76e86678f07e9e70e8713d6e39d59fd1b387498fa2fa5168a368b05f60f7ad82211a91daffe18a031623998e471eb6fb926914a5d2b4cf31ac7edbc32080f958edf86d7af61b7ae45d0b2bd09ae752e002946cb4ca60726846519474f76a26ebd15c2c75735b9de658c6576141f19266970888e6c71699d2242045460df1e0ff86a5e931ff9afe8ec147cea88569e4db021bc7e0deb7c7f50cbaf67b0228068733d6b7ba54dcbdbf151678ef640ad494cbdcf8b59f53bfe7a075c7dbd2ceda31f3dd98960d08bb8ac222ccbe8fea01bbe5e1f4400922469f2448868dbba2f4b8fdc76b04426481a1c22e7cb9bb3da5351958af4c31d68062b4244fcab189c37c6888f0c53e3c80489d7c628194f6acfc3b20ccf24d16205c0c02852e12dcc4cc965e28e4fcd8834e5870faa9e008eaf3361ffa7a8486e2eaa10e1fb4231c4cbdea58c5967fe760190c606884b7290bf8a622a9a44cf675ee0ceacaefb8fb5e3d63891771963b66b37a9fb520fe4eb91d4b495e1236b1d0505e0538bb3c6cee3d4d3d70b0038fade11848848b9a320b0c7a3bddfb8c785aacbef211e0f6bf99b9b9ae16ddea1ab3f2c22f0512aaf7c3d82dc55c36842810420a42ab3fa0088bfbc318ba91ab73622f655ea99000b38de949a59b003163ebf3eb93356763b6ccc6b07206598fd39ff6744bfc4e24476f977665d33bdf6e6ab794c7d3d95f269db8373f339e5bb21006eddc2da6d220011fbaf634e3e6d1afbbf34c185e206ab87c8d8408803d80760eae2657aefed1ead41bb2f78c1bd7b56732dd23eadfd593b60067cbd3ef49eecd7de4f338c677b3bd2466b7a6042ddba59131321624a48664427667dc8881c29ece2809fcd5f9dd4982c9d3f93c9c29033cacb5954be75308fc9bdc753cc9dd25fccab8f8254fc36edd3724215cebfdd85ba15755a3b1c3dd9a9e9031595e997537ee3d646b4bdb2afe89a23fcd8522c92ee4f921c4fcc9909fdd1fc15663ab5db80f33f31035ce5f2d3d81f8d9474113ce3447c64984abd67ffa13d6b1132f52081ce8f529dc157f308050388baea59a086312f63ccdcbc834d09c9d90ee0531d13a861ac716b63aae333e4a5ff6edbdaa6952b64d09bb7afb7e0403b78eaa896bf795d9326352d7f34a1681d9f5d5e19039acfd96899a98ff3c698b6e190a84e20706900e1f56bb476686e6aceaa5f9aa1cbd5cf6ab16695ef69f0143458696c4c3b4cb52812afc29c7dce1fc4e0968aef2eb82533e8552b0effc439e7f27c5f1437d83a971507f71c45dd3a2b1247bb28e63b4b8d2313e4bc4fe41927c2b521c2bb67a9fee5d9e9ce26d8e05e37db19a6072624e4c4acdc4922fffe364a8764321946017064ff81acfaa67d38be9ac7e6ed4eedbd8edf7468cf83c1001247bbc8799bb40e71de18a3eeed4d94bc93d4f27b76371bf619cb99819011f1caaccfadd108327dd6cc19c13349e46e6a46a53e6d16e333c60ebffa86f6bcf957cdb05dbba9118df33e91f0f1553ab0c141bb5e6a45ba9d584ba5518334fe2006faa5be1f5228c51240cdb90050e24b8742d8b6c909e905fd1169bb3f493bf6ee2a1863d54bd24a091d878e405c29c2ddd49c955710cc483e98ca8ba502c8dbaac4a1b3d479a933eb9df42a3a6bcabe31469edfeed28d7f66264130c37ba20bcaaa547cda9b115bd55b9d0b8219cd97078d6f301051a62b736b3002ef32c63c941d8632da0ebe2d2e6d50ebe9c757f3f07c16ccd32f91b1262894a49512fcbd3e781963ace5e208f594b3039681aea7808186c21b0ba5527bf0163abf4856b04366c2815f14df6870de1823ffa73e24e349e4eead2b50e05ae7022770687234661d0ef11091afd707e52f4a8a41781131a8687634821338c4eeaa184d28b03c25c1fe2f76041e9b8d1ba7cf3c84fe27009117c1091cac6dfb0bea3975f132c515557b3f5d1e277070ffa4396fa362eae26592ff53460c2aba8f75c0cb18f38c1379def040e445d8b73a81f5f5656fa40c860761794a42d4559fd50eddef7b91b81bd3f44a5603ae754e488284d05d596b8774be2cfd0e75c0bb345b3fcb8f2d05771ed3879b32cb535405f61f3ae1b438a1ac9f5d93301c3a4f65336cb584f39b4c255d022329a424507f2506fc3d6ab87c9e13d1f1efcf3dd2875b16f0e1e1a2727dca5ffed809da5d372f060b0076d1047aa50e8debec86f55093c5c35ece6922e136d1e02713e4bb3842839f0c93709bc8394d0b5f46fc3fc0c58657ef4963a7b92a4adf07fc9bcce8165cb3e76d4be853b462a76e61549d65ee20447486fdb00856ec9b6383d9a689a424a0a8093409fa1f657a8828f4d7045cbc098a9a80f8f4e3f3767acc364d049dad6ceede04712a978a8bf222044e85b86aeee784a39f8f5024c9c3a7c8f0586c108424b06cb63e53b7c7c8174e66e5745a6c70d502a8e09b3f617c8242710e3e39ac3d31992574fd88d7e431bc3b4c257dc9c5314ceede8caaef19b1c0b9257f14d876aef41988ab6f6d2bd8201b0f9da74af219c1c18f47a83f3abb0698fc4df6795fdb38d19a53f9fa776d72822bb0783482e9b3212a27c2c299d0b17779962fcdcee49f531e6ab022f974e5f74d443f276a0d67cbe505111d7b675db3e82763d4190ee9f6539b04b85e3476dcd34344ee7321f4c7f44f038ebde6c6e063a9765e648461c7b67f33060b006e0938ff33f79c98b662863680fdd14896bcce3fdfca7a7ef0b3906eb9ad5702f07d3842b60a5d94eeeb0ad4644a9e1c4f643d8f01baf50da85373aa6f829bca93abc695ac4fa10e6f30c3516bd52dbf330cb4fffe6ac97879d33491f8bb01f447395d395d9b9c9ac12a30e0d30ebfe286d954dad8ca49b7fe06b8afc4e02f1095d20cb7885ec51abcd27c46304f1e2fe674b4b960f9c1d108eaffa840182fdf70794e285c0f355faf1443cd6d80a2805c393efb5e780963cdbf58ce2e6f7783e7f2eb9dfee64fbaa76fb8c26da29a937e8c22bfdd6af918c65e73e7cd508b6a11432166b2acb03f141fb6ee09a0ff7a08dbce45c0de1b40471488fd632aeb1db704b87eecd4d74bb07e6b4c9b274fcd362024f4cb4da31c8da0fe4c04dcbd89f20cb7583df87cbda02a10f9caeb59aedcf82ac6c65e73c3c2a979efab4911f567fc38f2ee4d0a5dbb49b11b9334f8c9301d79f7266de9f36b334866be3609507eb5398b6135a6758a331f21e68e284144646bbec5061460f38504d83905ec981fec7711b03f140f92eba61552aa9cafcdd87fce8f9a3351b03f8430f5bfb3af0c3c6f029e94f2f4134ddc23cbb4a249bfdcdce847fd0505b11b65306e917a1462c4dc015536aa05ea5180c17b9730d6b177233bec9074f38daa511c9493387861109d51a416cb39eff18288b19d56b85e7c8e15fa526491c84f418f115c4f20eb23c5f6e02db013b7b0f1233f06bf508068049819215dcf641c17329a1ec4f3cb1de5b0f4a41f91f418f83ee03225f3de6b5ccd3fb24cab9460da4cdc712102dfc56163865ba41e28c088c81d5065a2b618ca935b3c9f7dfd5236d420822ff3ac8945b0626ca715d1128bc7458dabf599f6f486d99d2ef6de406ae7ea41fec8488557aa50762ac4489c88e73ef06bfe6edbb3d6bcf744beea9165dadc7a59242b2e6fb782de74e3f45bcfb1c9dfb871d8c1811752cf7b14a0fbe3a1928b95624cfb4dfbb48a01b9c9a71f67633bad5a3d4bad9d6a6b2544768a081bb85d73517819637a969f5e7cb1f70680bf9b8b8e10f7ec5962e3a904236d3be787b537868de92f1f66fe9f4f2a25c338df26d3e6d66be85f4554ad62da54d7bb8431fbfa356c6ca7158d363ba02ae88f72b0be1f287a41891c8f16aec723e0d3ea0f7094774efb7bc6e42e1201344ae63ccb3f124e809d1800be2ebc1a069419dfb48264c0f793ef868064f6dd616d366769c3fa169936d3a775d4aa059923bc8431d78b35ac6b53ead49b9ce4b1f41d3f929fa74ea3a5dff31051f473a2ce6876bd8c31e237ebd366a2eb7e2a2a508a61d3380a73c981ab31ad02a0eba7f94c7af0d300f0a038c30222f0b5fe179d255319be5f263a6b0d185691fcd2fdc9396de596ebd39692c7bdf0381bdae9063f73cf44eb9500d8c908361e3a4fbb0e0d137bdb8fd62b91a233c6c3f26955ae40ff17615adbcc65853527f5a302c5508ef3a839e98770bbb8e12e12010c3ec6586d6dfe2add081e5ef9f09836171db526240d6c8916931389573d54a6cdae9731b9d1a753e122bd38a791192391ccd70bbc88801c9f13d3fad33b5d3972f5c2502200e91e515ddf2db45e09e8e6b308269c6d70e1b08343d726276af958de7b2a27624b9f1fa18b2359334e1ed38a008e58aa5009f385e34a41bb2c9a7276968ca0afc118731493b3a5cf8fd0b509e2ee4d502597dc95e7d31a97dbbb84b1c4ebfa714e3dcccc1f5fc6985ebc3d389aa8f8fa54dfc5611a55f399b645cc7fdf4344839f0c53cd99084647a3797a58042b861a4474ec5dce926b9732fbfa358c7be171e6fdb72daccb66d28d32f4c811984e4510fd7c244f7fedde0371ed72e6f8f02a0555519b5a8ce0e0d7e59fa78d29302c3f8d0e8bb5e0082fc8b405e4f50403e8e145400da0d4d98b168b15c26a11e2cccd2ab9cf45de955dbe29b5f355890fed658c7500f05d9ca01e0545db2337ffad5737a3e6a43fef66f683b208c7872374c421c22e14ffed0be73451280e345f18c0a892df8e164e8577ab13de8c7caefba92dd8ac1dad8cf2d3672e9205ea2dbdb09c25ee1359ffc304399a7dae5b5553f75d383e1ca1c0cf45ed869fc5990206b63bb1f41d7fde48298a69c62c231d28f39b349e13e1db20206ad0002a99310a618f1c418f9c40fa7e083da6b567941f4262f6395f7ce7ae1036bff43873de18a11d17f2cb2be42bf72e616c6c9aa8eed4004673195705eacfa4fac9f1e15532992558b804c08b88a90a024a15d4a402f99d48be4633721cbc8ac0cf67ef0db64d130dfe25815d27fdbaeff34905033bdd883e5d9a68028f31b60f40e8da04f5040379f50daa80e9580403f788a24f3396779797763552194cd8f60c870e47f1fbbc32133b31002c361b967fd6618579adf1d34a839f0ca756db653079313c6ce1605fbf8629c8bfeb2cf3f49182ec535e6d9208d78b951f4b94ee116dfeb392c740164e45c7dec2779bc56e10f57fa9a03f5ad9172999d866b3a371b5392fcc58ec7497835731b0dd59d135adb671a2fa33f933461a8776baf5efa7cdb27823457122867e26c22e963e5953eedd5e2d16a0f9a5f26f33b44d13a933e75ec5f4b9d60a50cf2571dd2752d404ec4f99742fa5f31051ec1f53f3f61b12e9f3b969bd8c2c46d3f97c5f2850202030733e55516f615435e7e5b0082a784e84a92a06cb6a0b2c9c8acd2bcd052fdd8bdd1821af0cc8f108d4642abc67324b68a905aae6788ed74344ddd795990d2605c1511eb57c0c225f075f83a07f3fad739ac8fac7fc115e14574818b2d5c12e1636d88e30b0ffbaf1fb69cbb92d7101bf3b58f02670db3451fd19a5ecdf5a703d039cde60cd3ace184b009baf8420dfe50ccb699492f0ffccfec85f74bc80df3c96fc1db1d8b5093a180ccc8b6f68145bc4ca5c8205fc6ea0a1df11136e13edb830b3c3f11055e239115d9b8cdd43bb80df5d34f43b62f155a9c0b7a3562d7bc7cc283a2c56245eb52e18ec0296c4b27f1b97bb374107826a2a803e0f2a386a55f81a9c86370e1670012bfa6d5c05a98fd10e7e96803f164b6ddd95e1b3d62286769b1dfb569bb26e6859c005348273fa15f234a6e3818080fe2f6524a6b2e380e91d18676d1d447efee2970bf8ddc4ff03848780e5eb0231170000000049454e44ae426082</data> </image> </images> <connections> @@ -1335,7 +1338,7 @@ Montreal, Quebec H2T 1S6</p></string> <tabstop>confirmationToQuit</tabstop> <tabstop>checkedTray</tabstop> <tabstop>voicemailNumber</tabstop> - <tabstop>useStunNo</tabstop> + <tabstop>useStunYes</tabstop> <tabstop>STUNserver</tabstop> <tabstop>playTones</tabstop> <tabstop>pulseLength</tabstop> diff --git a/src/configurationpanel.ui.h b/src/gui/qt/configurationpanel.ui.h similarity index 57% rename from src/configurationpanel.ui.h rename to src/gui/qt/configurationpanel.ui.h index 7c92a2103b6199f0672560a31ec9c286544c54de..37c37c6cdb2c6d935441ce2f76a337d4bd7edb8e 100644 --- a/src/configurationpanel.ui.h +++ b/src/gui/qt/configurationpanel.ui.h @@ -12,10 +12,12 @@ #include <qdir.h> #include <qstringlist.h> -#include "configuration.h" +#include "../../configuration.h" +#include "../../global.h" +#include "../../skin.h" +#include "../../user_cfg.h" #include "qjlistboxpixmap.h" #include "qmessagebox.h" -#include "skin.h" #include "qtGUImainwindow.h" @@ -25,7 +27,7 @@ void ConfigurationPanel::init() // List skin choice from "skins" directory QDir dir(Skin::getPath(QString(SKINDIR))); if ( !dir.exists() ) { - printf ("\nCannot find 'skins' directory\n"); + _debug("\nCannot find 'skins' directory\n"); } else { dir.setFilter( QDir::Dirs | QDir::NoSymLinks); dir.setSorting( QDir::Name ); @@ -56,22 +58,21 @@ void ConfigurationPanel::init() } } // For signalisations tab - fullName->setText(QString(Config::getchar("Signalisations", "SIP.fullName", ""))); - userPart->setText(QString(Config::getchar("Signalisations", "SIP.userPart", ""))); - username->setText(QString(Config::getchar("Signalisations", "SIP.username", ""))); - password->setText(QString(Config::getchar("Signalisations", "SIP.password", ""))); - hostPart->setText(QString(Config::getchar("Signalisations", "SIP.hostPart", ""))); - sipproxy->setText(QString(Config::getchar("Signalisations", "SIP.sipproxy", ""))); - autoregister->setChecked(Config::get("Signalisations", "SIP.autoregister", (int)true)); - playTones->setChecked(Config::get("Signalisations", "DTMF.playTones", (int)true)); - pulseLength->setValue(Config::get("Signalisations", "DTMF.pulseLength", 250)); - sendDTMFas->setCurrentItem(Config::get("Signalisations", "DTMF.sendDTMFas",0)); - STUNserver->setText(Config::get("Signalisations", "STUN.STUNserver", - "stun.fwdnet.net:3478")); -((QRadioButton*)stunButtonGroup->find(Config::get("Signalisations", "STUN.useStun", 1)))->setChecked(true); + fullName->setText(QString(get_config_fields_str(SIGNALISATION, FULL_NAME))); + userPart->setText(QString(get_config_fields_str(SIGNALISATION, USER_PART))); + username->setText(QString(get_config_fields_str(SIGNALISATION, AUTH_USER_NAME))); + password->setText(QString(get_config_fields_str(SIGNALISATION, PASSWORD))); + hostPart->setText(QString(get_config_fields_str(SIGNALISATION, HOST_PART))); + sipproxy->setText(QString(get_config_fields_str(SIGNALISATION, PROXY))); + autoregister->setChecked(get_config_fields_int(SIGNALISATION, AUTO_REGISTER)); + playTones->setChecked(get_config_fields_int(SIGNALISATION, PLAY_TONES)); + pulseLength->setValue(get_config_fields_int(SIGNALISATION, PULSE_LENGTH)); + sendDTMFas->setCurrentItem(get_config_fields_int(SIGNALISATION, SEND_DTMF_AS)); + STUNserver->setText(QString(get_config_fields_str(SIGNALISATION, STUN_SERVER))); +((QRadioButton*)stunButtonGroup->find(get_config_fields_int(SIGNALISATION, USE_STUN)))->setChecked(true); // For audio tab -((QRadioButton*)DriverChoice->find(Config::get("Audio", "Drivers.driverName", 0)))->setChecked(true); +((QRadioButton*)DriverChoice->find(get_config_fields_int(AUDIO, DRIVER_NAME)))->setChecked(true); #ifdef ALSA alsaButton->setEnabled(true); @@ -79,25 +80,24 @@ void ConfigurationPanel::init() alsaButton->setEnabled(false); #endif - codec1->setCurrentText(QString(Config::getchar("Audio", "Codecs.codec1", "G711u"))); - codec2->setCurrentText(QString(Config::getchar("Audio", "Codecs.codec2", "G711u"))); - codec3->setCurrentText(QString(Config::getchar("Audio", "Codecs.codec3", "G711u"))); - codec4->setCurrentText(QString(Config::getchar("Audio", "Codecs.codec4", "G711u"))); - codec5->setCurrentText(QString(Config::getchar("Audio", "Codecs.codec5", "G711u"))); - ringsChoice->setCurrentText(QString(Config::getchar( - "Audio", "Rings.ringChoice", "konga.ul"))); + codec1->setCurrentText(QString(get_config_fields_str(AUDIO, CODEC1))); + codec2->setCurrentText(QString(get_config_fields_str(AUDIO, CODEC2))); + codec3->setCurrentText(QString(get_config_fields_str(AUDIO, CODEC3))); + codec4->setCurrentText(QString(get_config_fields_str(AUDIO, CODEC4))); + codec5->setCurrentText(QString(get_config_fields_str(AUDIO, CODEC5))); + ringsChoice->setCurrentText(QString(get_config_fields_str(AUDIO, RING_CHOICE))); // For preferences tab - SkinChoice->setCurrentText(QString(Config::getchar( - "Preferences", "Themes.skinChoice", "metal"))); - confirmationToQuit->setChecked(Config::get( - "Preferences", "Options.confirmQuit", (int)true)); - zoneToneChoice->setCurrentText(QString(Config::getchar( - "Preferences", "Options.zoneToneChoice", "North America"))); - checkedTray->setChecked(Config::get( - "Preferences", "Options.checkedTray", (int)false)); - - voicemailNumber->setText(Config::get("Preferences", "Themes.voicemailNumber", "888")); + SkinChoice->setCurrentText(QString(get_config_fields_str( + PREFERENCES, SKIN_CHOICE))); + confirmationToQuit->setChecked(get_config_fields_int( + PREFERENCES, CONFIRM_QUIT)); + zoneToneChoice->setCurrentText(QString(get_config_fields_str( + PREFERENCES, ZONE_TONE))); + checkedTray->setChecked(get_config_fields_int( + PREFERENCES, CHECKED_TRAY)); + voicemailNumber->setText(QString(get_config_fields_str( + PREFERENCES, VOICEMAIL_NUM))); // Init tab view order Tab_Signalisations->show(); @@ -130,41 +130,43 @@ void ConfigurationPanel::init() // For saving settings at application 'save' void ConfigurationPanel::saveSlot() { - Config::set("Signalisations", "SIP.fullName", fullName->text()); - Config::set("Signalisations", "SIP.userPart", userPart->text()); - Config::set("Signalisations", "SIP.username", username->text()); - Config::set("Signalisations", "SIP.password", password->text()); - Config::set("Signalisations", "SIP.hostPart", hostPart->text()); - Config::set("Signalisations", "SIP.sipproxy", sipproxy->text()); - Config::set("Signalisations", "SIP.autoregister",autoregister->isChecked()); - Config::set("Signalisations", "DTMF.pulseLength", pulseLength->value()); - Config::set("Signalisations", "DTMF.playTones", playTones->isChecked()); - Config::set("Signalisations", "DTMF.sendDTMFas" , sendDTMFas->currentItem()); - Config::set("Signalisations", "STUN.STUNserver", STUNserver->text()); + Config::set("VoIPLink", "SIP.fullName", string(fullName->text().ascii())); + Config::set("VoIPLink", "SIP.userPart", string(userPart->text().ascii())); + Config::set("VoIPLink", "SIP.username", string(username->text().ascii())); + Config::set("VoIPLink", "SIP.password", string(password->text().ascii())); + Config::set("VoIPLink", "SIP.hostPart", string(hostPart->text().ascii())); + Config::set("VoIPLink", "SIP.proxy", string(sipproxy->text().ascii())); + Config::set("VoIPLink", "SIP.autoregister",autoregister->isChecked()); + Config::set("VoIPLink", "DTMF.pulseLength", pulseLength->value()); + Config::set("VoIPLink", "DTMF.playTones", playTones->isChecked()); + Config::set("VoIPLink", "DTMF.sendDTMFas" , sendDTMFas->currentItem()); + Config::set("VoIPLink", "STUN.STUNserver", string(STUNserver->text().ascii())); - Config::set("Audio", "Codecs.codec1", codec1->currentText()); - Config::set("Audio", "Codecs.codec2", codec2->currentText()); - Config::set("Audio", "Codecs.codec3", codec3->currentText()); - Config::set("Audio", "Codecs.codec4", codec4->currentText()); - Config::set("Audio", "Codecs.codec5", codec5->currentText()); + Config::set("Audio", "Codecs.codec1", string(codec1->currentText().ascii())); + Config::set("Audio", "Codecs.codec2", string(codec2->currentText().ascii())); + Config::set("Audio", "Codecs.codec3", string(codec3->currentText().ascii())); + Config::set("Audio", "Codecs.codec4", string(codec4->currentText().ascii())); + Config::set("Audio", "Codecs.codec5", string(codec5->currentText().ascii())); if (ringsChoice->currentText() != NULL) - Config::set("Audio", "Rings.ringChoice", ringsChoice->currentText()); + Config::set("Audio", "Rings.ringChoice", + string(ringsChoice->currentText().ascii())); - Config::set("Preferences", "Themes.skinChoice", SkinChoice->currentText()); + Config::set("Preferences", "Themes.skinChoice", + string(SkinChoice->currentText().ascii())); Config::set("Preferences", "Options.zoneToneChoice", - zoneToneChoice->currentText()); + string(zoneToneChoice->currentText().ascii())); Config::set("Preferences", "Options.confirmQuit", confirmationToQuit->isChecked()); Config::set("Preferences", "Options.checkedTray", checkedTray->isChecked()); - Config::set("Preferences", "Options.voicemailNumber", voicemailNumber->text()); + Config::set("Preferences", "Options.voicemailNumber", + string(voicemailNumber->text().ascii())); #if 0 QMessageBox::information(this, "Save settings", "You must restart SFLPhone", QMessageBox::Yes); #endif accept(); - } // Handle tab view according to current item of listbox @@ -231,13 +233,13 @@ void ConfigurationPanel::changeTabSlot() void ConfigurationPanel::useStunSlot(int id) { - Config::set("Signalisations", "STUN.useStun", id); + Config::set("VoIPLink", "STUN.useStun", id); } void ConfigurationPanel::applySkinSlot() { - Config::set("Preferences", "Themes.skinChoice", SkinChoice->currentText()); + Config::set("Preferences", "Themes.skinChoice", string(SkinChoice->currentText().ascii())); } diff --git a/src/configurationpanelui.cpp b/src/gui/qt/configurationpanelui.cpp similarity index 68% rename from src/configurationpanelui.cpp rename to src/gui/qt/configurationpanelui.cpp index 8612f9f466424a0221e696a5a56c6149e948cfe9..c18dffdadf53773dcf8284b1570adbb454d34e73 100644 --- a/src/configurationpanelui.cpp +++ b/src/gui/qt/configurationpanelui.cpp @@ -1,13 +1,13 @@ /**************************************************************************** -** Form implementation generated from reading ui file 'configurationpanel.ui' +** Form implementation generated from reading ui file 'gui/qt/configurationpanel.ui' ** -** Created: Wed Apr 27 10:37:34 2005 +** Created: Fri May 20 14:27:25 2005 ** by: The User Interface Compiler ($Id$) ** ** WARNING! All changes made in this file will be lost! ****************************************************************************/ -#include "configurationpanelui.h" +#include "gui/qt/configurationpanelui.h" #include <qvariant.h> #include <qpushbutton.h> @@ -322,507 +322,486 @@ static const char* const image0_data[] = { static const unsigned char image1_data[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xad, 0x00, 0x00, 0x00, 0x30, - 0x08, 0x06, 0x00, 0x00, 0x00, 0x63, 0x57, 0xfa, 0xde, 0x00, 0x00, 0x17, - 0x58, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0x5d, 0x6d, 0x6c, 0x1b, - 0xc7, 0x99, 0x7e, 0xd6, 0x70, 0x8d, 0x95, 0xa0, 0x18, 0xbb, 0x86, 0x13, - 0x2c, 0x85, 0xd4, 0x20, 0x8d, 0xd8, 0xc8, 0xd2, 0x49, 0x10, 0x32, 0x1f, - 0x88, 0xa8, 0xab, 0x11, 0x32, 0x57, 0x23, 0xa6, 0xec, 0x00, 0x26, 0xe3, - 0x1f, 0xfa, 0x68, 0x02, 0x8b, 0x76, 0x8a, 0x54, 0x6e, 0x0f, 0x81, 0xec, - 0x1e, 0x1a, 0x5b, 0x2d, 0x12, 0x59, 0x2d, 0x5a, 0x8b, 0x31, 0xae, 0x95, - 0xce, 0x68, 0x63, 0xca, 0xc8, 0x9d, 0x25, 0x01, 0xb1, 0x45, 0x03, 0xb1, - 0x2d, 0x03, 0x0a, 0x4c, 0x16, 0x4e, 0x45, 0x05, 0xf6, 0x89, 0x1b, 0x38, - 0xb1, 0x36, 0x88, 0x03, 0x2d, 0x21, 0x04, 0x22, 0xe1, 0x06, 0xe4, 0x42, - 0x11, 0xa4, 0x85, 0x1a, 0x60, 0xee, 0xc7, 0x6a, 0x97, 0x5f, 0xcb, 0x4f, - 0xc9, 0x17, 0xf7, 0xa2, 0xf9, 0xf3, 0x48, 0xdc, 0x9d, 0x99, 0x77, 0x66, - 0xdf, 0x79, 0xe6, 0x9d, 0x77, 0xde, 0x9d, 0xa5, 0x2e, 0x00, 0x90, 0x01, - 0x30, 0xab, 0x84, 0x2d, 0x4b, 0x84, 0x48, 0x09, 0x00, 0x34, 0x20, 0x2b, - 0x00, 0x43, 0x03, 0x56, 0x06, 0x18, 0xd9, 0x40, 0x51, 0xab, 0x59, 0xcf, - 0x1a, 0x7e, 0x7f, 0x91, 0x3a, 0xb3, 0x82, 0x02, 0x7c, 0x84, 0x90, 0x2b, - 0x77, 0xe2, 0x10, 0x12, 0x0a, 0x82, 0xa2, 0x04, 0x21, 0x01, 0x00, 0x29, - 0x00, 0xac, 0x21, 0x3a, 0xcd, 0x16, 0xd8, 0x4d, 0x80, 0xe7, 0x51, 0x1e, - 0xe2, 0x96, 0x5a, 0xea, 0x7e, 0xe8, 0x80, 0x35, 0xfc, 0xe7, 0x43, 0xaa, - 0x1a, 0xa6, 0xe5, 0xef, 0x12, 0x32, 0xf0, 0x89, 0x84, 0x80, 0x10, 0x85, - 0x0c, 0x16, 0x50, 0x52, 0x00, 0x5d, 0x19, 0x32, 0x0c, 0x0b, 0x8f, 0xc5, - 0x82, 0x13, 0xcf, 0x5b, 0x30, 0xba, 0x91, 0x5a, 0x53, 0xe0, 0x35, 0x2c, - 0x1b, 0x2b, 0x62, 0x5a, 0xf7, 0x1c, 0x21, 0xad, 0x17, 0x43, 0x08, 0xc7, - 0x80, 0x62, 0x8c, 0x5a, 0x29, 0x3a, 0xcd, 0x16, 0x0c, 0xee, 0xe3, 0x11, - 0xd9, 0x58, 0xbb, 0x66, 0x42, 0xac, 0x61, 0x49, 0x5c, 0x57, 0xce, 0x8d, - 0xde, 0x25, 0x42, 0xe2, 0xd7, 0xa7, 0x49, 0xfd, 0xa9, 0x11, 0x84, 0x35, - 0x13, 0x80, 0x66, 0x57, 0x0d, 0xc3, 0x31, 0x09, 0xf5, 0xa7, 0x27, 0x20, - 0x5e, 0xbf, 0x4d, 0xbe, 0xeb, 0x0e, 0x59, 0xc3, 0xfb, 0x1f, 0x4b, 0x32, - 0x2d, 0x7f, 0x37, 0x49, 0xda, 0x2e, 0x4a, 0x10, 0x12, 0x12, 0x56, 0x8b, - 0x59, 0x8b, 0xa1, 0x8d, 0x03, 0xc6, 0x0f, 0xb8, 0x30, 0xb4, 0x61, 0xcd, - 0x64, 0x58, 0x43, 0x63, 0x2c, 0xca, 0xb4, 0x8e, 0xbb, 0x84, 0x34, 0x0e, - 0x0b, 0xaa, 0xc2, 0xae, 0x22, 0xb3, 0x16, 0x43, 0x41, 0x06, 0xea, 0x4f, - 0x8d, 0xc0, 0x3d, 0x47, 0xc8, 0xfd, 0xd0, 0x41, 0x6b, 0x78, 0xff, 0x61, - 0x41, 0xa6, 0xe5, 0xef, 0x26, 0x49, 0xe3, 0x59, 0x01, 0xb2, 0x52, 0x21, - 0x63, 0x6e, 0x5e, 0x04, 0xd6, 0x9b, 0xf4, 0x92, 0xdc, 0x0f, 0x02, 0x9d, - 0x4f, 0xf2, 0x48, 0x29, 0x32, 0x44, 0x19, 0xe8, 0xfa, 0x30, 0x52, 0x56, - 0x39, 0x0c, 0x9d, 0xc2, 0xd4, 0xeb, 0xde, 0x15, 0x2d, 0xd2, 0x5a, 0x92, - 0x0b, 0x64, 0xe0, 0xfc, 0x10, 0xa2, 0xff, 0x13, 0x41, 0x6e, 0xe2, 0x1f, - 0xe5, 0xe1, 0x78, 0xdc, 0x01, 0xcb, 0x63, 0x16, 0x44, 0xea, 0xeb, 0xff, - 0x29, 0x6c, 0xe9, 0x96, 0xe4, 0x02, 0x09, 0xfd, 0x2d, 0x04, 0xeb, 0x23, - 0x56, 0x84, 0xac, 0x5b, 0x57, 0x65, 0x26, 0x72, 0xcf, 0x2d, 0x90, 0xfe, - 0x77, 0xfb, 0x21, 0x7e, 0x2e, 0x42, 0xbc, 0x23, 0x81, 0xdf, 0x66, 0x81, - 0x6f, 0xbf, 0x0f, 0xe2, 0xae, 0xc6, 0x8a, 0xcb, 0xbf, 0x17, 0xf2, 0x19, - 0xa1, 0xa1, 0xf7, 0xc0, 0x71, 0x97, 0x10, 0xeb, 0x70, 0x08, 0xb2, 0x5c, - 0xa6, 0x37, 0x00, 0x29, 0x9c, 0xf9, 0xb1, 0x0b, 0x2d, 0x8f, 0xb0, 0xa8, - 0x79, 0x20, 0x4f, 0x3f, 0xf4, 0x14, 0x91, 0x80, 0xc6, 0xf3, 0xa1, 0xb2, - 0xbd, 0x0c, 0x0c, 0xaa, 0x53, 0x5c, 0xef, 0x1c, 0x21, 0xbd, 0xa7, 0xfb, - 0x31, 0x70, 0x3e, 0x00, 0x06, 0x0c, 0x64, 0xc8, 0xba, 0x0c, 0x46, 0xff, - 0xbb, 0xf6, 0xba, 0x70, 0xe2, 0x57, 0xc7, 0x11, 0xb8, 0xcf, 0x4d, 0x12, - 0xe1, 0xad, 0x6e, 0x12, 0xba, 0x14, 0x82, 0x0c, 0x19, 0x57, 0xfe, 0x6b, - 0x64, 0x55, 0x14, 0x43, 0xfc, 0x79, 0x27, 0x19, 0x9d, 0x08, 0x65, 0xf5, - 0x87, 0xcd, 0x69, 0x87, 0xeb, 0xa4, 0xbf, 0xe2, 0x81, 0x7c, 0x2f, 0xe4, - 0x33, 0xc2, 0x3c, 0xa6, 0x75, 0x2f, 0x11, 0x52, 0x7f, 0x2a, 0x54, 0x11, - 0xc3, 0x5e, 0xfb, 0x89, 0x1b, 0x2e, 0x4b, 0x0d, 0x8a, 0x25, 0xdf, 0xd5, - 0x28, 0x06, 0x6e, 0xca, 0x65, 0x95, 0x97, 0xcb, 0xb8, 0xa9, 0x5f, 0x7a, - 0x11, 0xa0, 0xca, 0x57, 0xa8, 0xc8, 0xa1, 0x76, 0x32, 0x21, 0x08, 0x7a, - 0xdd, 0x1c, 0x67, 0x82, 0x77, 0xaf, 0x07, 0x0d, 0xb6, 0x06, 0x88, 0x77, - 0x04, 0x28, 0x09, 0x05, 0x83, 0xe1, 0x20, 0x12, 0x89, 0xb8, 0x7e, 0xcf, - 0xb9, 0xbf, 0x9c, 0x81, 0x64, 0x7f, 0xea, 0xbe, 0x66, 0xdc, 0x96, 0xe4, - 0x02, 0x09, 0x5e, 0x0d, 0x82, 0xdf, 0x66, 0x83, 0xf0, 0xec, 0x8e, 0x15, - 0x2b, 0x84, 0xed, 0xc6, 0x6d, 0xd2, 0xf6, 0xb3, 0xb6, 0xac, 0xe7, 0xd4, - 0x60, 0xb3, 0x21, 0xd0, 0x1f, 0xa8, 0x6a, 0x00, 0xaf, 0xb6, 0x7c, 0x85, - 0x30, 0x8f, 0x69, 0x07, 0xdf, 0x9f, 0x24, 0x61, 0x51, 0x2a, 0xdf, 0xef, - 0xfa, 0xb0, 0x09, 0xa4, 0xdd, 0x81, 0x62, 0xc9, 0x3e, 0x14, 0x81, 0xf0, - 0x95, 0x52, 0x95, 0x3f, 0x17, 0x34, 0x0b, 0x27, 0x07, 0x74, 0xbc, 0xfa, - 0x42, 0x59, 0x0a, 0xe5, 0x99, 0x49, 0x92, 0x46, 0xcf, 0x0b, 0x3a, 0xa3, - 0x7a, 0x76, 0x7b, 0x60, 0xe9, 0xee, 0x32, 0xec, 0x40, 0xd7, 0x17, 0x49, - 0x12, 0xf9, 0x2c, 0x02, 0xac, 0x07, 0x94, 0x97, 0xf6, 0x14, 0x2c, 0xdf, - 0xf5, 0x45, 0x92, 0xb0, 0x66, 0x76, 0x45, 0x4c, 0xdc, 0x92, 0x5c, 0x20, - 0x43, 0x9b, 0x0a, 0x6f, 0xa8, 0xb8, 0xa6, 0xa6, 0x49, 0xb5, 0xcc, 0xe4, - 0x9e, 0x5d, 0x20, 0x09, 0x39, 0x51, 0x11, 0xb3, 0x79, 0x66, 0x92, 0xa4, - 0xe9, 0x15, 0x2f, 0xe4, 0x79, 0x19, 0x40, 0x7a, 0x06, 0xea, 0x3c, 0xd0, - 0x01, 0x74, 0x1c, 0x2c, 0xd8, 0x5f, 0xa1, 0xed, 0x9b, 0x2a, 0x1e, 0xd8, - 0x95, 0xca, 0xe7, 0x9e, 0x5d, 0x20, 0xa0, 0x81, 0x42, 0xfd, 0x95, 0xc5, - 0xb4, 0xf4, 0xad, 0x24, 0x69, 0xbb, 0x18, 0x42, 0x25, 0x4c, 0xd8, 0xfe, - 0x0c, 0x83, 0xc0, 0x6e, 0xbb, 0x81, 0xaa, 0xaa, 0xa9, 0x5a, 0x86, 0xcd, - 0xc5, 0x73, 0xfb, 0x2c, 0xa0, 0x9f, 0xd8, 0x5a, 0xb2, 0xc3, 0x2c, 0xd1, - 0x49, 0xd2, 0xf6, 0xda, 0x41, 0xbd, 0x7e, 0xa7, 0xd3, 0x55, 0xf1, 0x54, - 0x67, 0xbb, 0x71, 0x9b, 0xf4, 0x9c, 0xea, 0x81, 0x78, 0x47, 0x34, 0x6c, - 0x93, 0xd3, 0xe9, 0xc2, 0x91, 0x43, 0x1d, 0x88, 0x6e, 0xdf, 0x4a, 0xf9, - 0x9f, 0x77, 0x12, 0x79, 0x5e, 0x06, 0x53, 0xc7, 0xa0, 0xef, 0x0f, 0x7d, - 0x79, 0x0c, 0xc3, 0x5e, 0x1f, 0x27, 0xc7, 0x7f, 0x7d, 0x1c, 0xf2, 0xbc, - 0x0c, 0x7e, 0x1b, 0x0f, 0xf7, 0xf0, 0x10, 0xa5, 0xc9, 0x19, 0x18, 0x1e, - 0x85, 0x70, 0x33, 0xa4, 0x2b, 0x8e, 0x96, 0xf8, 0x6d, 0x3c, 0x1a, 0x9e, - 0xb6, 0xa1, 0xe3, 0x40, 0x47, 0xd6, 0x83, 0x83, 0xff, 0x0c, 0xf1, 0x0f, - 0xf7, 0x03, 0x00, 0xfa, 0xde, 0xe9, 0x43, 0x6a, 0x67, 0x23, 0x95, 0xf8, - 0xf3, 0x19, 0xd2, 0xff, 0x6e, 0xbf, 0x9e, 0x97, 0xe3, 0x4c, 0xfa, 0x0c, - 0x72, 0xf2, 0x8d, 0x6e, 0x24, 0x5a, 0xf7, 0x18, 0x3e, 0x78, 0x4d, 0x6e, - 0xa3, 0x74, 0xe6, 0xb7, 0x7d, 0x10, 0x77, 0x35, 0x52, 0xfc, 0xd8, 0x38, - 0x39, 0xfe, 0xc7, 0x9e, 0xac, 0x19, 0x49, 0x4b, 0x4c, 0x1d, 0x03, 0x8f, - 0xd3, 0x8b, 0xd6, 0x43, 0x1e, 0x7d, 0x4d, 0x50, 0x8e, 0x7c, 0x4c, 0x1d, - 0xa3, 0xb7, 0x57, 0x93, 0xcf, 0xb7, 0x44, 0x48, 0xff, 0xd9, 0x00, 0x46, - 0x2e, 0x05, 0xf3, 0xea, 0xe2, 0x38, 0x13, 0x7c, 0x7b, 0xdb, 0xe1, 0x69, - 0x76, 0x63, 0x74, 0xd9, 0x8f, 0xbf, 0x5e, 0x6b, 0x88, 0x8f, 0x10, 0xc2, - 0xfe, 0x61, 0xa4, 0x62, 0x26, 0x64, 0x69, 0x8b, 0x61, 0xc3, 0x01, 0xd5, - 0x86, 0x1d, 0xf8, 0x74, 0x59, 0x61, 0xab, 0x60, 0xd8, 0x4c, 0x3c, 0x1c, - 0x06, 0x52, 0x4b, 0x84, 0x94, 0x8a, 0x61, 0xe0, 0xcd, 0x7c, 0x96, 0x0c, - 0x42, 0x38, 0x0a, 0x71, 0x8f, 0x9b, 0xb4, 0xee, 0xf6, 0x80, 0x7d, 0x9c, - 0x87, 0x7d, 0xb3, 0xa9, 0xe4, 0x88, 0xd7, 0xa6, 0x4c, 0xa6, 0x8e, 0x41, - 0xef, 0x9b, 0x27, 0xc0, 0x9b, 0x79, 0xc8, 0xdf, 0xca, 0xe8, 0x3d, 0xdb, - 0x0f, 0x21, 0x1c, 0x45, 0x38, 0x1c, 0x82, 0x70, 0x33, 0x8a, 0xc8, 0x1c, - 0x21, 0x03, 0x2f, 0x79, 0x21, 0x43, 0x86, 0x3c, 0x2f, 0xe3, 0xc8, 0xdb, - 0x47, 0xd1, 0x91, 0x53, 0xde, 0xd0, 0x1f, 0xfa, 0xf5, 0x87, 0x94, 0xfa, - 0x46, 0x06, 0x03, 0x40, 0xf2, 0x9f, 0x24, 0x5d, 0xcb, 0x03, 0x8b, 0x01, - 0x03, 0x8e, 0x33, 0xc1, 0x6e, 0xe1, 0x91, 0x52, 0x52, 0x48, 0xdc, 0x91, - 0x21, 0xde, 0x11, 0x21, 0xde, 0x11, 0x31, 0x30, 0x3c, 0x84, 0x33, 0x63, - 0xe3, 0x44, 0x5b, 0x14, 0x75, 0x0d, 0xf7, 0xeb, 0x8c, 0x18, 0x97, 0xe2, - 0x50, 0x00, 0x48, 0x9f, 0x65, 0x0f, 0x2c, 0x25, 0xb1, 0xa8, 0xff, 0x3d, - 0xf1, 0xa5, 0x00, 0x5b, 0x81, 0x76, 0x1e, 0xfb, 0x45, 0x27, 0x8e, 0xfe, - 0xb6, 0x0b, 0xb9, 0x36, 0xbe, 0x67, 0xb7, 0x07, 0x8e, 0xe7, 0x1c, 0x88, - 0x03, 0x38, 0xf8, 0xe6, 0x61, 0x5d, 0xbe, 0x13, 0xbf, 0x38, 0x06, 0x7a, - 0x33, 0x0d, 0x96, 0x66, 0xd1, 0x7b, 0xb6, 0x1f, 0xe1, 0x70, 0x08, 0x03, - 0x97, 0x02, 0x98, 0xb8, 0x19, 0x81, 0x7b, 0xb9, 0xdc, 0x72, 0xe4, 0xc3, - 0x7c, 0xfa, 0xcf, 0x89, 0x2f, 0x05, 0x74, 0xce, 0x24, 0x89, 0x63, 0x97, - 0x4b, 0xef, 0x23, 0x9e, 0xe3, 0xc1, 0x3f, 0xc3, 0xc3, 0xb2, 0xd9, 0x84, - 0xd1, 0xbf, 0x85, 0x10, 0xbf, 0x13, 0x47, 0xcf, 0xbb, 0x3d, 0xe8, 0x1f, - 0xee, 0xc7, 0x95, 0x65, 0xa6, 0x5f, 0xaf, 0x35, 0xc4, 0x77, 0x29, 0x0a, - 0x59, 0xc9, 0x60, 0x38, 0xa5, 0x3c, 0xf4, 0xdf, 0x91, 0xd0, 0xeb, 0x34, - 0x56, 0xdc, 0x40, 0x4c, 0x2a, 0xbb, 0x9c, 0x52, 0x28, 0xcb, 0x29, 0x9c, - 0xf8, 0x24, 0x0e, 0x53, 0x01, 0x45, 0xd3, 0x70, 0x68, 0x53, 0x2d, 0x75, - 0x66, 0x6c, 0x9c, 0x1c, 0x7c, 0xf3, 0x30, 0x00, 0xa8, 0x0f, 0x24, 0x21, - 0xc3, 0x7f, 0x36, 0x3d, 0xda, 0xf1, 0xb4, 0x8d, 0x30, 0x75, 0x0c, 0xf8, - 0x6d, 0x16, 0xb8, 0x9e, 0x77, 0xa1, 0x65, 0xb7, 0x27, 0x8b, 0xd1, 0xc6, - 0x83, 0xd7, 0x20, 0x7d, 0x2d, 0x41, 0xb2, 0x3f, 0x45, 0x89, 0x7f, 0x6d, - 0x44, 0x7c, 0xf9, 0x77, 0x17, 0x80, 0xf0, 0xd3, 0x36, 0x02, 0x00, 0xf2, - 0xbc, 0x0c, 0xe9, 0x2b, 0x09, 0xee, 0x66, 0x37, 0x34, 0x26, 0x49, 0x24, - 0xe2, 0x70, 0xcc, 0xce, 0x12, 0x8d, 0x79, 0x3c, 0x33, 0x49, 0xe2, 0xf7, - 0xbc, 0xa0, 0x57, 0x7b, 0xe2, 0x17, 0xc7, 0x90, 0x7a, 0xed, 0x67, 0x64, - 0xe0, 0xed, 0x2e, 0x00, 0xea, 0xa0, 0xe8, 0x38, 0xe4, 0x03, 0xdd, 0xda, - 0x46, 0xc9, 0x18, 0xd5, 0xdb, 0xc1, 0x0d, 0x5e, 0x26, 0x47, 0x4f, 0xa9, - 0xf7, 0x1c, 0x7c, 0xf3, 0x30, 0xce, 0xdd, 0xb8, 0x4d, 0x84, 0x67, 0x77, - 0x50, 0x7a, 0x7b, 0x00, 0xc8, 0x8a, 0x0c, 0x0e, 0x40, 0x4a, 0x49, 0x65, - 0xf5, 0xb9, 0x0c, 0x95, 0xd1, 0xdb, 0x9b, 0x5b, 0xa0, 0xbc, 0xb4, 0x87, - 0x52, 0x06, 0xcf, 0x91, 0xe8, 0x27, 0x12, 0x14, 0x59, 0x02, 0x00, 0xd8, - 0x9f, 0x76, 0xa0, 0xe3, 0x80, 0x0f, 0x81, 0x0d, 0x14, 0x75, 0x32, 0xa3, - 0x1e, 0x00, 0xf0, 0xec, 0xf5, 0xc0, 0xf2, 0x9b, 0x2e, 0x6a, 0xa4, 0xbb, - 0x2b, 0xbf, 0x1f, 0x2e, 0xab, 0xf2, 0x09, 0x00, 0x7a, 0x93, 0x0b, 0xe4, - 0xa9, 0xb0, 0xba, 0x78, 0x13, 0x13, 0x22, 0x7a, 0x97, 0x4d, 0x9b, 0x4a, - 0xe5, 0x6b, 0x7a, 0xb1, 0x09, 0xbe, 0x0e, 0x9f, 0xae, 0xb0, 0x4e, 0xa7, - 0x0b, 0xf6, 0x93, 0x7e, 0x8a, 0xb9, 0xbc, 0x6c, 0x52, 0x41, 0x5d, 0xdc, - 0x05, 0x2f, 0x05, 0x55, 0x52, 0x38, 0x79, 0x04, 0x0e, 0x2c, 0xfb, 0x69, - 0xbd, 0x4b, 0x84, 0x04, 0xa5, 0x0c, 0x46, 0xac, 0x04, 0x65, 0x60, 0xea, - 0xef, 0x30, 0x4c, 0x27, 0x1e, 0xb3, 0xac, 0xaa, 0x1f, 0xd7, 0x1f, 0x8e, - 0xc0, 0x47, 0x4a, 0xfb, 0x6f, 0xc5, 0x5d, 0x8d, 0xd4, 0xe4, 0xd8, 0x38, - 0xda, 0x9b, 0x5b, 0xd0, 0xf0, 0x78, 0x83, 0xaa, 0x20, 0x60, 0xb2, 0x85, - 0x9b, 0x07, 0x26, 0x04, 0x01, 0x3d, 0xa7, 0xfc, 0x78, 0x6a, 0x57, 0x23, - 0xe8, 0xf7, 0x2e, 0xe8, 0xe5, 0x06, 0xb7, 0x6c, 0xa2, 0x32, 0x17, 0x65, - 0x96, 0xe8, 0x24, 0xe1, 0x82, 0x97, 0x49, 0xff, 0x1e, 0x37, 0xd1, 0xb2, - 0x33, 0x60, 0x00, 0x05, 0xe8, 0x38, 0xe0, 0x03, 0x53, 0xc7, 0xe8, 0xe5, - 0x87, 0x3e, 0x8c, 0xa4, 0xa7, 0xe0, 0x8c, 0x81, 0xc2, 0x73, 0x3c, 0xc4, - 0x5d, 0x8d, 0x94, 0xff, 0x2f, 0x43, 0x7a, 0xfe, 0xb4, 0xc2, 0x66, 0xcb, - 0x9f, 0x68, 0xdd, 0x43, 0xb5, 0xef, 0xf7, 0xe9, 0x79, 0x03, 0xe7, 0x07, - 0x72, 0xa5, 0x07, 0x43, 0x33, 0x90, 0x01, 0xb0, 0x34, 0x9b, 0xd5, 0xbe, - 0x63, 0x3f, 0x3f, 0x06, 0xf7, 0xf0, 0x10, 0xa5, 0xd9, 0xe8, 0x3d, 0xa7, - 0xfc, 0x08, 0x87, 0x83, 0x10, 0x05, 0x09, 0x13, 0x82, 0x80, 0xfe, 0x77, - 0xfb, 0x31, 0x78, 0x7e, 0x10, 0x0c, 0x00, 0x2b, 0x6f, 0x4d, 0x97, 0x07, - 0x06, 0xbe, 0xbd, 0xad, 0x59, 0x72, 0xe4, 0xf6, 0x83, 0x3d, 0x3a, 0x4d, - 0xb8, 0xe0, 0x65, 0xd2, 0xf4, 0xca, 0xcb, 0x59, 0xfd, 0x99, 0x52, 0x52, - 0x55, 0xc9, 0x97, 0x8a, 0xa5, 0xa0, 0x2d, 0x98, 0x19, 0x30, 0x48, 0x7c, - 0x15, 0x87, 0xed, 0xc6, 0x6d, 0x62, 0x89, 0x4e, 0x12, 0xd7, 0xd4, 0x34, - 0xb1, 0x5c, 0xbf, 0x4d, 0xe2, 0xb1, 0xb8, 0x7e, 0x7d, 0x42, 0x10, 0xd0, - 0x92, 0x5c, 0x20, 0xeb, 0x65, 0x00, 0x83, 0x9f, 0xa7, 0x54, 0xf7, 0x56, - 0x95, 0x4c, 0xd8, 0xf6, 0xe1, 0x14, 0xa2, 0x2d, 0x56, 0xe4, 0x26, 0xd3, - 0x83, 0xc0, 0xc9, 0x1f, 0x59, 0x70, 0xf4, 0x43, 0xac, 0x0a, 0xe3, 0xca, - 0x60, 0x31, 0xf8, 0xa9, 0x54, 0x96, 0x6d, 0x3a, 0xb4, 0xa9, 0x96, 0xb2, - 0xa8, 0x39, 0xe1, 0x06, 0xe0, 0x98, 0x9d, 0x25, 0x8a, 0xac, 0x40, 0xfa, - 0x3a, 0x8e, 0xb8, 0x18, 0x47, 0xf0, 0xaf, 0x41, 0xc8, 0x77, 0xd2, 0xd3, - 0x62, 0xcf, 0x9f, 0x7a, 0x30, 0x3e, 0x93, 0x24, 0xc1, 0x2d, 0x9b, 0x28, - 0x7e, 0x6c, 0x9c, 0x0c, 0x7e, 0x18, 0x84, 0x70, 0x33, 0xaa, 0xb2, 0x40, - 0x86, 0x8d, 0xac, 0x25, 0x19, 0x32, 0x40, 0x03, 0x81, 0x0d, 0x14, 0xe5, - 0xf1, 0x9f, 0x24, 0x03, 0xc3, 0xaa, 0x32, 0xf6, 0x9f, 0xed, 0x47, 0x64, - 0x89, 0x10, 0x29, 0x26, 0xc1, 0xff, 0x5a, 0x5a, 0xf1, 0xda, 0x5f, 0x6b, - 0x81, 0x72, 0x79, 0x08, 0x89, 0x84, 0xa8, 0xe7, 0x6f, 0xdd, 0xdf, 0x8a, - 0x40, 0x6b, 0x9b, 0xb1, 0x6d, 0xde, 0x60, 0xc3, 0xc0, 0x79, 0x35, 0xaf, - 0x70, 0x33, 0x8a, 0xdc, 0x55, 0x43, 0x2e, 0x93, 0x69, 0x0c, 0xa7, 0xbc, - 0xfa, 0x72, 0x96, 0xe9, 0xe3, 0xd9, 0xeb, 0x41, 0xfc, 0x2b, 0x49, 0xcf, - 0x47, 0x33, 0x2c, 0x3c, 0xbb, 0x3d, 0x18, 0x02, 0x00, 0x3a, 0xbf, 0x3d, - 0x59, 0xa6, 0x56, 0x5e, 0x3f, 0x78, 0xf3, 0xfa, 0x01, 0x50, 0x15, 0x53, - 0xc8, 0xf9, 0xad, 0x1c, 0xf9, 0xe4, 0x6f, 0xd3, 0xfd, 0x2f, 0x43, 0x86, - 0x7c, 0x47, 0x46, 0xae, 0x37, 0x23, 0xf3, 0x3a, 0x53, 0xc7, 0x00, 0xeb, - 0x97, 0x6d, 0xda, 0x9e, 0x89, 0x0a, 0xbc, 0x05, 0x06, 0x28, 0x7c, 0x29, - 0x02, 0xc8, 0x57, 0x5a, 0x00, 0x38, 0xd2, 0x60, 0x01, 0xc0, 0xe0, 0xe8, - 0x47, 0xc2, 0x8a, 0x6c, 0x5a, 0x0d, 0x03, 0x02, 0x83, 0x56, 0x14, 0x57, - 0x58, 0x23, 0xcc, 0xdc, 0x40, 0x50, 0xa0, 0x4e, 0x3d, 0xf4, 0x07, 0x97, - 0x49, 0xcf, 0xdb, 0x7e, 0xbd, 0x43, 0x87, 0xae, 0x06, 0xb1, 0xf8, 0xfb, - 0x3e, 0xdd, 0xb4, 0x60, 0xea, 0x18, 0x9c, 0x7c, 0xb3, 0x1b, 0xae, 0xe7, - 0x5c, 0xa8, 0xd9, 0x5c, 0x83, 0xc1, 0xf3, 0x83, 0xe8, 0x3f, 0x15, 0xd0, - 0xef, 0xd7, 0x18, 0xa6, 0xa5, 0xb9, 0x03, 0x9a, 0xd2, 0x62, 0x1e, 0xb0, - 0x3a, 0xd2, 0x2a, 0xc6, 0x80, 0x01, 0xcd, 0xd5, 0x64, 0x78, 0x27, 0xd2, - 0x36, 0x64, 0xf4, 0xb3, 0x68, 0x41, 0x79, 0x4d, 0x4a, 0x46, 0xfe, 0xba, - 0x1a, 0x9d, 0xc9, 0xb4, 0xfc, 0xb9, 0x4c, 0x96, 0xae, 0x2f, 0x67, 0x51, - 0xf9, 0x9b, 0x2e, 0xca, 0x92, 0xf3, 0xfb, 0xd0, 0x49, 0x3f, 0x18, 0x5d, - 0x7e, 0x26, 0xaf, 0x3d, 0x32, 0x54, 0xff, 0xad, 0x7f, 0xd9, 0xa6, 0xa5, - 0xb9, 0x1a, 0x9c, 0x79, 0xf3, 0x04, 0x6c, 0x8f, 0xd9, 0xf4, 0x7e, 0xe8, - 0x39, 0xe5, 0xd7, 0xe5, 0x81, 0x82, 0xaa, 0xe4, 0xcb, 0xad, 0xdf, 0xb3, - 0xd7, 0x03, 0xdf, 0xfe, 0xd6, 0xa2, 0x6b, 0x8e, 0xd1, 0x8d, 0xb5, 0x58, - 0xe7, 0x98, 0x5b, 0x20, 0x42, 0x42, 0x5e, 0x31, 0x13, 0x8e, 0x88, 0xf9, - 0x2b, 0x4c, 0x2d, 0x1d, 0x69, 0x60, 0x71, 0x61, 0x2f, 0x9f, 0x56, 0xc0, - 0x15, 0xd4, 0x13, 0x8e, 0x49, 0x68, 0x59, 0x2a, 0x6e, 0x22, 0x78, 0x66, - 0x92, 0xc4, 0x12, 0x9d, 0x24, 0xbe, 0x12, 0xf7, 0x59, 0x1f, 0xb1, 0x66, - 0x2d, 0x42, 0x16, 0xe7, 0x53, 0x18, 0x38, 0x1f, 0x50, 0x3b, 0xb7, 0x8e, - 0x41, 0x64, 0x2c, 0x84, 0x84, 0x67, 0x0f, 0x35, 0x5a, 0x5f, 0x4b, 0x05, - 0x36, 0x50, 0x14, 0xdd, 0xda, 0x46, 0xd1, 0x5c, 0xda, 0x1f, 0xcd, 0xd2, - 0xac, 0xda, 0x91, 0xf5, 0xb5, 0x94, 0x67, 0xaf, 0x07, 0x00, 0xb2, 0xca, - 0xd3, 0xfe, 0xf7, 0xed, 0x6d, 0xd7, 0xeb, 0xe5, 0x6d, 0x69, 0xfb, 0x3f, - 0x30, 0x3c, 0x54, 0x70, 0xa0, 0x69, 0xa6, 0x85, 0x0c, 0x19, 0xee, 0x67, - 0xdc, 0x7a, 0xa9, 0x99, 0x36, 0xa3, 0xf6, 0xe0, 0xb3, 0xeb, 0x2b, 0x7f, - 0x20, 0xab, 0xf2, 0xa7, 0xe5, 0xd5, 0xda, 0x63, 0xbb, 0x71, 0x3b, 0xbd, - 0xe1, 0x50, 0x07, 0x84, 0x82, 0x57, 0x20, 0xee, 0x6a, 0xd4, 0xfb, 0xa1, - 0x75, 0x7f, 0x6b, 0x56, 0xfb, 0x34, 0x86, 0xae, 0x54, 0x3e, 0xfb, 0x63, - 0xf6, 0xbc, 0x45, 0x60, 0x21, 0x85, 0x75, 0xcc, 0xce, 0xea, 0xc1, 0x54, - 0xeb, 0x42, 0x31, 0x05, 0x2b, 0xb6, 0x39, 0x37, 0x2f, 0xc2, 0xcb, 0x9b, - 0x50, 0x2c, 0x79, 0x79, 0x13, 0x16, 0x5e, 0x77, 0xa1, 0xf3, 0x47, 0xf6, - 0x15, 0xd7, 0x17, 0xfc, 0x3c, 0x55, 0x54, 0x61, 0x1b, 0x3d, 0x2f, 0xe0, - 0xf0, 0x6b, 0x47, 0x60, 0x75, 0xd8, 0x21, 0x75, 0x75, 0x13, 0x65, 0xf0, - 0x1c, 0x71, 0x4d, 0x4d, 0x13, 0xef, 0x1c, 0x21, 0x9e, 0x99, 0x24, 0x61, - 0xaf, 0x8f, 0x93, 0xd4, 0xef, 0xfb, 0x48, 0xd3, 0x2b, 0xe9, 0xe9, 0x8e, - 0xe7, 0x78, 0xb8, 0x9e, 0x71, 0xa7, 0x05, 0x9e, 0x07, 0xa4, 0xcf, 0xd2, - 0xa6, 0x48, 0x4b, 0x72, 0x81, 0x84, 0x8e, 0x76, 0x12, 0xcd, 0x25, 0x93, - 0xc9, 0x30, 0x32, 0x80, 0x63, 0x3f, 0xef, 0xcc, 0x6a, 0xaf, 0x66, 0xc3, - 0x31, 0x75, 0x0c, 0x5a, 0x9b, 0xbd, 0xfa, 0x7d, 0xc7, 0xdf, 0x38, 0xae, - 0x5f, 0x0f, 0x87, 0x43, 0x90, 0xba, 0xba, 0x89, 0xeb, 0x8b, 0xa4, 0xfe, - 0x40, 0x6c, 0x37, 0x6e, 0x93, 0xa1, 0xe6, 0x16, 0x12, 0xbf, 0x13, 0xd7, - 0xf3, 0x77, 0x1c, 0x6a, 0x2f, 0xdb, 0x66, 0xac, 0x64, 0x06, 0x82, 0x92, - 0x23, 0xef, 0x72, 0x7b, 0xe2, 0x09, 0x29, 0xcb, 0x66, 0xcd, 0x9c, 0x11, - 0x5a, 0x92, 0x0b, 0xc4, 0xfb, 0x6a, 0x6b, 0xd5, 0x36, 0x6d, 0x66, 0xfd, - 0x81, 0x0d, 0x14, 0xd5, 0x79, 0xa0, 0x43, 0xbf, 0x7e, 0xf8, 0x97, 0x87, - 0xc1, 0x8f, 0x8d, 0xeb, 0x44, 0xe3, 0x9d, 0x23, 0x84, 0x7e, 0xef, 0x02, - 0xf1, 0x3f, 0xef, 0x24, 0xde, 0x97, 0x5a, 0xa1, 0xf5, 0xd3, 0xfa, 0x50, - 0x6c, 0x39, 0x7a, 0xab, 0x5a, 0x06, 0xac, 0x5b, 0xc4, 0xc2, 0xa1, 0x26, - 0x94, 0x93, 0x6a, 0x1e, 0x00, 0x7a, 0x9d, 0x16, 0xb4, 0x3f, 0x6a, 0xc1, - 0x8e, 0x4b, 0x21, 0x20, 0x51, 0x5d, 0xbd, 0x82, 0x2c, 0x83, 0x2f, 0xf0, - 0x20, 0x68, 0x86, 0x06, 0xbf, 0x8d, 0xd7, 0x7d, 0xac, 0xc1, 0xab, 0x41, - 0xe0, 0x2a, 0x00, 0xf8, 0x0b, 0xca, 0xc5, 0x6f, 0xe3, 0xd1, 0xfb, 0xd6, - 0x09, 0x58, 0xcc, 0x16, 0x3d, 0xaf, 0x0c, 0x19, 0x4d, 0xaf, 0x79, 0xc1, - 0xed, 0x71, 0x13, 0x65, 0x7e, 0x11, 0xfe, 0x5d, 0x8d, 0x59, 0x79, 0x72, - 0x6d, 0xc0, 0xa1, 0x4d, 0xb5, 0x54, 0xfb, 0x5b, 0x7d, 0x64, 0xe0, 0x52, - 0x20, 0x7d, 0x1d, 0x40, 0x6b, 0x73, 0x2b, 0x46, 0x36, 0xa6, 0xdd, 0x74, - 0x21, 0xeb, 0x56, 0xea, 0xdc, 0x8d, 0xdb, 0xe4, 0xc8, 0xdb, 0x47, 0x81, - 0x84, 0x8c, 0xe0, 0xd5, 0xa0, 0x2a, 0xe3, 0xb2, 0x57, 0x02, 0x19, 0x36, - 0x1d, 0xc7, 0x99, 0x10, 0x78, 0xa7, 0x0f, 0xa3, 0xf5, 0xcb, 0x71, 0xc6, - 0x19, 0x3e, 0x4e, 0x23, 0x9b, 0x91, 0xe3, 0x4c, 0x90, 0x21, 0x94, 0xad, - 0xb8, 0x96, 0xc7, 0xd2, 0xac, 0x2f, 0x43, 0x06, 0xb3, 0x59, 0x55, 0x34, - 0xdf, 0x8b, 0x4d, 0xe8, 0x79, 0xc7, 0x0f, 0xcc, 0xab, 0x5e, 0x92, 0xb6, - 0xd7, 0x0e, 0x16, 0xec, 0x07, 0x20, 0x6d, 0xd3, 0x66, 0xfa, 0x88, 0x8d, - 0xe4, 0x63, 0xea, 0x98, 0x7c, 0x39, 0x3a, 0x0e, 0x52, 0xed, 0xda, 0x9a, - 0x60, 0x5e, 0xf5, 0x96, 0x68, 0x7d, 0xd1, 0xe5, 0x4a, 0x9b, 0x59, 0xfc, - 0x36, 0x1e, 0xac, 0x59, 0x9d, 0x09, 0x28, 0xcf, 0xfb, 0x93, 0x24, 0x58, - 0xc9, 0x0e, 0x58, 0x06, 0xba, 0xb7, 0x31, 0xb8, 0xb0, 0xc7, 0x8e, 0x9a, - 0x1f, 0x14, 0xd4, 0x87, 0xa2, 0xe9, 0x48, 0x78, 0x0a, 0xfe, 0x9b, 0x89, - 0x2a, 0x76, 0xc8, 0x18, 0x74, 0xbc, 0x5a, 0x7c, 0xcb, 0xd5, 0x76, 0xe3, - 0x36, 0x09, 0x7d, 0x34, 0x8a, 0x44, 0x2c, 0x81, 0xdc, 0xbd, 0x75, 0x19, - 0x32, 0x1a, 0x1e, 0x6f, 0x80, 0xc9, 0x6c, 0x82, 0xbb, 0xc1, 0x95, 0x15, - 0x1c, 0xe2, 0x5b, 0x22, 0x64, 0xf0, 0xfc, 0x20, 0x84, 0x8f, 0x05, 0xa4, - 0x94, 0x14, 0x58, 0x9a, 0xc5, 0x22, 0x0d, 0xb8, 0x1b, 0x9c, 0x30, 0x99, - 0x4d, 0x10, 0x3f, 0x95, 0x20, 0x2b, 0x32, 0xcc, 0x0f, 0x72, 0x68, 0xfa, - 0xd7, 0xa6, 0x2c, 0x85, 0xd4, 0x62, 0x1e, 0xc4, 0x2f, 0xa3, 0x60, 0x69, - 0x16, 0x9c, 0x99, 0x83, 0xa5, 0xf3, 0x68, 0x41, 0x39, 0xe9, 0x0f, 0x2e, - 0x13, 0x51, 0x8c, 0xe9, 0xf7, 0x6b, 0xf5, 0x71, 0x66, 0x0e, 0xae, 0x1f, - 0xb9, 0xf3, 0x36, 0x2a, 0xe8, 0x0f, 0x2e, 0x93, 0xf0, 0x87, 0x61, 0x2c, - 0xd2, 0x40, 0x7f, 0x4f, 0xaf, 0xba, 0x08, 0x9c, 0x49, 0x92, 0x23, 0xdd, - 0x47, 0xc0, 0xd2, 0x2c, 0xdc, 0xfb, 0x3d, 0x48, 0xed, 0x6c, 0xac, 0x78, - 0x23, 0x65, 0x42, 0x98, 0x80, 0xf9, 0x41, 0x0e, 0x09, 0xcf, 0x9e, 0xac, - 0x7e, 0xe8, 0x3f, 0x1b, 0x80, 0xf4, 0x99, 0xa8, 0xcb, 0x45, 0x6f, 0x66, - 0xd0, 0xf0, 0xb8, 0x0d, 0x56, 0xb3, 0x15, 0x91, 0x4f, 0xa3, 0x7a, 0x3f, - 0x68, 0xf9, 0x32, 0xe5, 0xeb, 0xfd, 0x55, 0x37, 0x86, 0x36, 0xd5, 0x66, - 0xc9, 0xe7, 0xfc, 0xb1, 0xb3, 0xe0, 0xce, 0xa3, 0x16, 0xdc, 0x94, 0x59, - 0x5f, 0x4a, 0x49, 0x81, 0x7f, 0xc4, 0x0e, 0x57, 0x83, 0x13, 0xd2, 0xce, - 0x1d, 0x7a, 0x3e, 0x0a, 0x6f, 0x5d, 0x20, 0x15, 0xef, 0x50, 0xd5, 0x99, - 0x70, 0x61, 0xb7, 0xa5, 0xa4, 0x49, 0x50, 0x4e, 0x8a, 0x48, 0x29, 0x34, - 0x5e, 0x15, 0x81, 0xaf, 0xe3, 0x65, 0xd7, 0xcf, 0xd0, 0x2c, 0x7a, 0xff, - 0xfd, 0x85, 0xfb, 0x3a, 0xb8, 0x65, 0x0d, 0xef, 0x1d, 0xae, 0xab, 0xd4, - 0xa6, 0x6c, 0x79, 0xc6, 0x8e, 0x85, 0x43, 0x8e, 0x55, 0x51, 0x58, 0x00, - 0x70, 0x58, 0x58, 0x2c, 0xfc, 0xc4, 0x01, 0xe7, 0xe3, 0x7c, 0xd9, 0x72, - 0xc8, 0x4a, 0x61, 0x9b, 0x96, 0x81, 0xea, 0x77, 0xa6, 0x6f, 0x25, 0xc9, - 0xc4, 0xd8, 0x34, 0x19, 0x7c, 0x7f, 0x92, 0x4c, 0x8c, 0x4d, 0x12, 0xfa, - 0x56, 0x92, 0x78, 0x97, 0xc8, 0xda, 0x9b, 0x11, 0xff, 0x0f, 0x70, 0x7d, - 0xd9, 0xb6, 0x64, 0x99, 0xd1, 0x5c, 0xd5, 0xa4, 0x9a, 0x07, 0x80, 0xd0, - 0x3e, 0x2b, 0x4e, 0x70, 0x6c, 0x3a, 0xde, 0xb6, 0xd4, 0x0e, 0x59, 0x91, - 0x86, 0x59, 0x4e, 0x47, 0x10, 0x93, 0xd3, 0xcc, 0x1d, 0x06, 0x0b, 0x20, - 0x04, 0x3f, 0x67, 0x41, 0xc7, 0x0a, 0x3b, 0xcc, 0xbd, 0x44, 0x88, 0x24, - 0x03, 0xb2, 0x92, 0x82, 0x83, 0x61, 0x0d, 0xc3, 0x26, 0x5b, 0x96, 0x08, - 0x11, 0x12, 0x29, 0xf0, 0xcb, 0x03, 0x0c, 0x5b, 0x2a, 0x0f, 0x32, 0x29, - 0x56, 0xff, 0xa8, 0xc1, 0x56, 0xb6, 0x69, 0x26, 0x49, 0x12, 0x09, 0x05, - 0xe2, 0xb2, 0x09, 0xc5, 0x31, 0x00, 0xb3, 0x7d, 0xe5, 0x71, 0xc2, 0x89, - 0x1b, 0xd3, 0x24, 0x2a, 0x03, 0x52, 0x5c, 0x82, 0x97, 0xe7, 0xc1, 0x9b, - 0x69, 0x88, 0x0f, 0xa5, 0xdb, 0x43, 0x7f, 0x31, 0x4b, 0x06, 0x27, 0xe2, - 0x59, 0x39, 0x5d, 0x3c, 0x0f, 0xf7, 0xa3, 0x4c, 0x55, 0xef, 0xfc, 0xf1, - 0x77, 0x93, 0x24, 0x18, 0x53, 0x30, 0x28, 0x8a, 0xfa, 0x15, 0x96, 0xe5, - 0xd0, 0xf7, 0xbc, 0x45, 0x2f, 0x6f, 0x7d, 0x59, 0xb6, 0x64, 0xdd, 0x22, - 0x66, 0x7f, 0xe2, 0x85, 0xa9, 0x48, 0xac, 0xec, 0x6a, 0xa4, 0xe3, 0x0d, - 0x26, 0x98, 0x68, 0x17, 0x0e, 0x5e, 0x0a, 0x95, 0xb4, 0x6d, 0x8b, 0x35, - 0x3c, 0x26, 0xc7, 0x8d, 0xfd, 0xc9, 0x89, 0xf2, 0x36, 0x26, 0x8a, 0x61, - 0xd3, 0x70, 0x14, 0xe1, 0x58, 0x7a, 0x0d, 0x30, 0x3b, 0x47, 0x48, 0xa6, - 0xe2, 0xf2, 0x33, 0x0b, 0xa4, 0xf6, 0x77, 0x39, 0x31, 0x1c, 0x6f, 0x5d, - 0x20, 0xdd, 0x4e, 0x3b, 0xb0, 0xb3, 0xfa, 0xf8, 0xd2, 0x96, 0x25, 0x42, - 0xea, 0x4f, 0x8d, 0x40, 0x3e, 0x15, 0x82, 0xed, 0xcf, 0x93, 0xe4, 0xd8, - 0x4f, 0xd3, 0x36, 0xbd, 0x7b, 0x8e, 0x90, 0xda, 0xd3, 0x99, 0x71, 0xca, - 0x22, 0x40, 0xb3, 0x18, 0x9f, 0x21, 0x04, 0x5b, 0xaa, 0x3f, 0x6f, 0x22, - 0x71, 0x23, 0x49, 0x0e, 0x87, 0xd3, 0xfe, 0xf5, 0x70, 0x2c, 0x02, 0x86, - 0x61, 0xd1, 0x9b, 0x71, 0x5f, 0xe4, 0xf3, 0x54, 0x46, 0x7f, 0x48, 0xfa, - 0x7d, 0x5d, 0x61, 0x16, 0x9d, 0x63, 0x93, 0xa4, 0x61, 0x57, 0x79, 0xe1, - 0x9e, 0x3e, 0x42, 0x88, 0xf7, 0x7c, 0x04, 0xc1, 0xb3, 0x99, 0xfe, 0xfc, - 0xe5, 0x72, 0x63, 0x22, 0xba, 0x9f, 0xb3, 0xea, 0xf7, 0xaf, 0x2b, 0x87, - 0x61, 0x6f, 0xef, 0x6b, 0xba, 0xe7, 0x0a, 0xab, 0x25, 0x9f, 0x8d, 0xc5, - 0x99, 0xbd, 0xae, 0x15, 0x31, 0x6d, 0xb5, 0xf9, 0xca, 0xc1, 0x70, 0x8e, - 0xb7, 0x25, 0xf0, 0xc9, 0x54, 0xd6, 0x75, 0xff, 0xc7, 0x82, 0x61, 0xbd, - 0x5d, 0xe1, 0x28, 0x06, 0xdf, 0x9b, 0x24, 0xee, 0x2a, 0x4d, 0x94, 0x81, - 0x4f, 0x24, 0x35, 0x36, 0x44, 0x49, 0x41, 0x48, 0xc8, 0x59, 0xd7, 0x25, - 0x05, 0x86, 0xed, 0x0d, 0x25, 0xe2, 0x2b, 0x6a, 0x6f, 0x0a, 0x8a, 0x61, - 0x0c, 0x48, 0xe6, 0xab, 0x50, 0x7d, 0x2f, 0x5a, 0xe1, 0x34, 0x5b, 0x0c, - 0xeb, 0xf7, 0x4f, 0xc8, 0xe8, 0xf9, 0xf3, 0x35, 0x52, 0xca, 0xaf, 0xee, - 0x9e, 0x23, 0xc4, 0xf2, 0xc7, 0x2b, 0x08, 0x8a, 0xf9, 0xf5, 0x41, 0x49, - 0xa1, 0xdb, 0x69, 0xc7, 0xc4, 0x43, 0x69, 0x62, 0x28, 0x69, 0xd3, 0x5e, - 0xdb, 0xef, 0x86, 0x75, 0x75, 0xcc, 0x57, 0x3d, 0x4d, 0xfd, 0x1d, 0x70, - 0x5d, 0x9c, 0xc2, 0x88, 0xb8, 0x68, 0x78, 0x5d, 0x57, 0xdc, 0x22, 0x72, - 0x15, 0xeb, 0xf0, 0x6a, 0xf3, 0x95, 0x83, 0xb9, 0xe5, 0x69, 0x0e, 0xf9, - 0xf4, 0x83, 0xa6, 0x0b, 0xd6, 0x1f, 0x8e, 0x49, 0x68, 0x1c, 0x8e, 0x82, - 0xbf, 0x5b, 0xb9, 0xe2, 0x22, 0xa7, 0xdc, 0xac, 0xeb, 0x4a, 0xbe, 0x5c, - 0x2a, 0x53, 0xad, 0x6c, 0x80, 0xa2, 0x40, 0xb9, 0x92, 0x9c, 0xbe, 0x6f, - 0x74, 0x03, 0x45, 0xb5, 0xbe, 0xfa, 0x14, 0x75, 0x6e, 0x9f, 0x0b, 0x0c, - 0x9d, 0xdf, 0x6e, 0xed, 0x9d, 0x3f, 0xee, 0x8b, 0xa4, 0xa1, 0xe2, 0xd2, - 0xb7, 0x92, 0xc4, 0x7a, 0x7a, 0x04, 0x31, 0xa5, 0x26, 0xaf, 0x1e, 0x33, - 0xb3, 0x88, 0xdb, 0xaf, 0x7b, 0x61, 0xca, 0x99, 0xa1, 0xd6, 0x9b, 0xe9, - 0x45, 0xc4, 0x14, 0x63, 0x66, 0xb2, 0x3d, 0xc2, 0xdf, 0x13, 0x1b, 0xd6, - 0xfa, 0x20, 0x10, 0xfe, 0x54, 0x44, 0xf8, 0xd3, 0x04, 0xb0, 0x79, 0x11, - 0x27, 0x1b, 0x1c, 0x68, 0x7d, 0x84, 0x86, 0xe9, 0x81, 0x74, 0x5d, 0x3e, - 0x1b, 0x8b, 0x60, 0xc2, 0x8e, 0xd1, 0x9b, 0xd1, 0x7c, 0xb9, 0x38, 0xcb, - 0x77, 0xc6, 0xb4, 0xb9, 0xe5, 0xa5, 0x94, 0x14, 0xf8, 0xcc, 0xeb, 0x8b, - 0x72, 0xd1, 0xfa, 0x85, 0x98, 0x84, 0xc6, 0xb3, 0x32, 0xae, 0xcc, 0x24, - 0x49, 0x25, 0xb6, 0xae, 0x58, 0xa4, 0x1d, 0x3c, 0x9d, 0x2f, 0x17, 0x94, - 0x14, 0x58, 0xba, 0xfa, 0x76, 0xca, 0x00, 0xd8, 0x02, 0xe5, 0x32, 0x34, - 0x20, 0xe6, 0xde, 0xff, 0xc4, 0x26, 0x6a, 0x6a, 0x8e, 0x90, 0xa6, 0xe1, - 0x10, 0x84, 0x1c, 0xff, 0xbb, 0x0c, 0x16, 0x7b, 0x86, 0x43, 0x30, 0xff, - 0xc7, 0x38, 0xf1, 0xd9, 0x18, 0x80, 0x66, 0x21, 0xca, 0x71, 0x8c, 0x88, - 0x0a, 0x62, 0xb9, 0xf1, 0xdb, 0xcb, 0xf9, 0x3a, 0x6d, 0x0c, 0x7a, 0x7f, - 0xec, 0x32, 0x7c, 0x63, 0x65, 0xbd, 0xdd, 0x64, 0x42, 0x4c, 0x36, 0xf0, - 0xd3, 0x32, 0x2c, 0xc6, 0xf7, 0xe6, 0xc7, 0x13, 0x8c, 0x88, 0x29, 0x0c, - 0x7c, 0x2e, 0x63, 0xf4, 0x9b, 0x14, 0xa0, 0xbd, 0x56, 0xce, 0x71, 0x20, - 0xaf, 0x18, 0xc7, 0x1e, 0x14, 0x4c, 0x0f, 0x9b, 0x80, 0xaf, 0xe2, 0xc0, - 0x3c, 0x8b, 0xa3, 0x97, 0x42, 0x38, 0x4a, 0xb3, 0x00, 0x47, 0x63, 0x76, - 0x9f, 0x43, 0x37, 0x45, 0x2e, 0xfc, 0x8b, 0x05, 0xb5, 0x52, 0x5c, 0x75, - 0x87, 0x65, 0xc8, 0x67, 0x61, 0xe9, 0xf2, 0x98, 0xb6, 0x42, 0x5b, 0xb8, - 0x2c, 0xc6, 0xcb, 0x29, 0x8f, 0xa5, 0xf9, 0xec, 0x07, 0x5d, 0xc3, 0x00, - 0x28, 0xee, 0xf7, 0x96, 0x95, 0x14, 0x1a, 0x87, 0x05, 0x9c, 0xbb, 0x95, - 0x24, 0xcc, 0x13, 0xe5, 0x29, 0xae, 0xba, 0xb3, 0x24, 0x1a, 0xb6, 0x43, - 0x67, 0xc4, 0x9c, 0x7a, 0x72, 0x07, 0x54, 0xa5, 0x28, 0x16, 0x90, 0xbf, - 0x10, 0x83, 0x8f, 0x6e, 0xa4, 0xa8, 0x63, 0x00, 0xc4, 0xeb, 0xb3, 0xa4, - 0x6b, 0x42, 0xcc, 0xcb, 0x17, 0x93, 0xe3, 0xe8, 0x9a, 0x50, 0x74, 0x9b, - 0xdb, 0xf0, 0xf9, 0x30, 0x2c, 0xc6, 0xf7, 0xb9, 0x20, 0x6e, 0xa1, 0xa8, - 0x40, 0x01, 0xb9, 0xd6, 0xd9, 0x38, 0x1a, 0x46, 0x8c, 0xe0, 0xfe, 0x21, - 0x93, 0xf5, 0x92, 0xe2, 0x91, 0xf0, 0x14, 0xa8, 0x77, 0x22, 0x78, 0xf9, - 0x7c, 0x08, 0xa3, 0x9f, 0x4a, 0x40, 0x4c, 0x02, 0x96, 0x19, 0xba, 0x7b, - 0x1b, 0x5b, 0x81, 0xb6, 0x2e, 0xa7, 0x6f, 0x95, 0xfc, 0x7a, 0x63, 0x0a, - 0xea, 0x4f, 0x8f, 0x20, 0x24, 0xa9, 0x66, 0x43, 0xcd, 0x03, 0x80, 0xdb, - 0x94, 0x2f, 0x9f, 0xcb, 0xfc, 0xdd, 0xd9, 0xb4, 0x46, 0x4c, 0x9b, 0x79, - 0x3d, 0x55, 0x82, 0x69, 0x33, 0xb1, 0xed, 0xa2, 0x80, 0xd1, 0x0f, 0x26, - 0xcb, 0x32, 0x15, 0x52, 0x45, 0xda, 0x81, 0x02, 0x8c, 0x08, 0x7a, 0x65, - 0xed, 0x2d, 0x18, 0x2b, 0x52, 0x82, 0xc1, 0xf9, 0x9d, 0xf5, 0xd4, 0x78, - 0xb3, 0x0d, 0x4c, 0xa1, 0xfc, 0x05, 0xd0, 0xc6, 0x59, 0x30, 0xfb, 0xba, - 0xaa, 0xb0, 0xc5, 0xca, 0x5f, 0xe7, 0x32, 0x5b, 0x60, 0x64, 0x83, 0x9d, - 0x7c, 0x4e, 0xdd, 0x42, 0x5b, 0xfc, 0x07, 0x40, 0xfd, 0xe7, 0x15, 0x75, - 0xe7, 0x6a, 0x3e, 0x6e, 0x68, 0xe3, 0xf0, 0x0c, 0x63, 0xa8, 0x97, 0x45, - 0x13, 0xcd, 0x18, 0xd6, 0x0b, 0xb0, 0x78, 0xe1, 0xbf, 0x47, 0x11, 0x10, - 0x52, 0x00, 0x80, 0xf6, 0x47, 0xf3, 0xe5, 0xb3, 0x71, 0xa6, 0xfb, 0xd6, - 0xa6, 0x45, 0x4d, 0x76, 0xbb, 0x6c, 0x66, 0x0b, 0x2e, 0x37, 0x37, 0x80, - 0xfc, 0xda, 0x8b, 0x33, 0xbf, 0x79, 0x81, 0x9a, 0x7d, 0xc3, 0x8b, 0x6e, - 0x27, 0x07, 0x86, 0x51, 0xaf, 0x0f, 0x88, 0x32, 0x86, 0xde, 0x1f, 0x27, - 0xa5, 0xe2, 0x84, 0xd9, 0x62, 0xed, 0xf8, 0x3f, 0xb6, 0x69, 0xe5, 0x32, - 0xca, 0x8d, 0x6f, 0xd9, 0x44, 0x4d, 0x1d, 0x70, 0xe9, 0xed, 0x2c, 0xb5, - 0x76, 0x32, 0x73, 0x26, 0x44, 0x0f, 0xd8, 0x61, 0xe4, 0xce, 0xcb, 0xc5, - 0x75, 0xe2, 0x96, 0x5a, 0xca, 0x48, 0xf3, 0xad, 0x26, 0x55, 0x61, 0x6b, - 0xdf, 0xbd, 0x02, 0x7c, 0x5d, 0x53, 0x74, 0x84, 0x78, 0xf9, 0x2a, 0xec, - 0x5e, 0xa5, 0x38, 0x23, 0x1d, 0xbc, 0x14, 0x82, 0x7d, 0x68, 0x0a, 0x2f, - 0x7f, 0x24, 0x64, 0xfd, 0xce, 0x20, 0x85, 0x78, 0x89, 0x91, 0xf8, 0x5d, - 0x32, 0x6d, 0xae, 0x4d, 0x3b, 0xde, 0x6c, 0x87, 0xb2, 0xbd, 0x96, 0xd2, - 0x6c, 0xb3, 0xd1, 0x8d, 0x14, 0xc5, 0xef, 0xdc, 0x41, 0xcd, 0xbe, 0xee, - 0x82, 0xc7, 0xc6, 0x03, 0x4a, 0x0a, 0x41, 0x51, 0x81, 0xfd, 0x2f, 0xa1, - 0xa2, 0x07, 0x94, 0x48, 0x9a, 0xdf, 0xd9, 0xa8, 0x1d, 0xdf, 0x81, 0x4d, - 0x5b, 0xde, 0x00, 0x47, 0x65, 0x71, 0xda, 0x3f, 0x28, 0xaf, 0xdc, 0x75, - 0x0c, 0x00, 0x0f, 0xcf, 0xe5, 0x69, 0xfe, 0x91, 0xb0, 0x84, 0xda, 0x3f, - 0x5d, 0x01, 0xe6, 0xf3, 0x57, 0x75, 0x59, 0xf8, 0x70, 0x95, 0xae, 0x85, - 0x42, 0x4c, 0x9b, 0x81, 0xc2, 0x97, 0xe2, 0x72, 0xeb, 0xd3, 0xbf, 0xfb, - 0x6c, 0xf6, 0xd2, 0x8a, 0xf5, 0x1d, 0x32, 0x2d, 0x9b, 0xc1, 0xb4, 0x4e, - 0x33, 0x0a, 0x32, 0xc7, 0xe8, 0x06, 0x8a, 0x72, 0xbf, 0xb4, 0x83, 0xea, - 0x76, 0xaa, 0x51, 0x6f, 0xda, 0x2a, 0x3b, 0x7e, 0x7d, 0x9a, 0xb8, 0xe7, - 0xd2, 0xde, 0x05, 0x1f, 0x21, 0x24, 0x71, 0x23, 0x49, 0xfc, 0x82, 0x5c, - 0xb0, 0x1d, 0x85, 0x18, 0x31, 0x77, 0x40, 0x55, 0x8a, 0x29, 0xa5, 0x40, - 0x3f, 0x96, 0xc1, 0xb4, 0x0d, 0x77, 0x09, 0xb1, 0x9e, 0x1e, 0x29, 0xc9, - 0xb0, 0x1a, 0xc6, 0x94, 0x9a, 0x92, 0x03, 0x57, 0xc3, 0x75, 0x32, 0x80, - 0xee, 0xe7, 0xad, 0x79, 0x9a, 0xef, 0xff, 0x28, 0xaa, 0x2a, 0x6c, 0xa9, - 0x11, 0xb2, 0xbe, 0x80, 0x52, 0x96, 0x4a, 0x25, 0x98, 0xb6, 0x10, 0x7a, - 0x1e, 0x65, 0x4a, 0x2b, 0x56, 0x91, 0xfc, 0x8e, 0xb9, 0x85, 0x15, 0x6d, - 0xe5, 0x56, 0x66, 0xd3, 0x1a, 0x44, 0x35, 0xe5, 0xa0, 0x69, 0xe7, 0x56, - 0xea, 0x72, 0xb3, 0x0b, 0xcc, 0xb2, 0x69, 0xd4, 0x15, 0x8e, 0xa2, 0xf6, - 0x54, 0x08, 0x2f, 0xbf, 0x75, 0x81, 0x1c, 0x7c, 0xeb, 0x1a, 0xa1, 0xde, - 0x1e, 0xc1, 0xe1, 0xab, 0x42, 0xd1, 0x19, 0xe3, 0x5e, 0xd9, 0xb4, 0x32, - 0x0a, 0x3c, 0xff, 0x22, 0x4c, 0xeb, 0x5e, 0x3e, 0xac, 0x70, 0xc7, 0xe9, - 0x11, 0xdd, 0xaf, 0x5c, 0xee, 0xf3, 0x15, 0x12, 0x80, 0xf5, 0xf4, 0x08, - 0xe8, 0x5b, 0xc6, 0xee, 0x31, 0x0d, 0xd7, 0x31, 0x00, 0x26, 0x1e, 0xa2, - 0x28, 0x33, 0x67, 0x42, 0x25, 0x31, 0x08, 0x1a, 0x76, 0xfe, 0xf0, 0xde, - 0x31, 0x6d, 0x2e, 0x3a, 0xcd, 0x0c, 0xe2, 0x65, 0xb8, 0x89, 0x8a, 0x95, - 0x13, 0x8a, 0x29, 0xf7, 0x94, 0x69, 0xb3, 0x6d, 0xda, 0xf2, 0xca, 0x4d, - 0x6c, 0xdf, 0x44, 0x4d, 0xbd, 0xee, 0x35, 0xf4, 0x73, 0x96, 0x33, 0x63, - 0xc0, 0x40, 0x2e, 0xd0, 0x2c, 0x42, 0x62, 0x7c, 0x45, 0x4c, 0x3b, 0x22, - 0x4c, 0x19, 0x96, 0x9b, 0xe9, 0xe8, 0xcf, 0x44, 0x7e, 0x86, 0x10, 0xeb, - 0xd9, 0x08, 0xba, 0xc2, 0x51, 0xc3, 0x7c, 0x36, 0x8e, 0x41, 0xdf, 0x6e, - 0x07, 0xba, 0x9d, 0x1c, 0xba, 0x9d, 0x76, 0x98, 0x99, 0xc5, 0xbc, 0xfb, - 0x64, 0xb0, 0x68, 0xbb, 0x18, 0xc2, 0xe8, 0xfb, 0x93, 0x59, 0x33, 0x4e, - 0x26, 0xae, 0xd3, 0xfe, 0x38, 0xd1, 0x60, 0x42, 0x35, 0xcc, 0x27, 0xca, - 0x29, 0x43, 0x9d, 0x2c, 0x99, 0xaa, 0x88, 0xe3, 0xed, 0xdb, 0x5d, 0xda, - 0x34, 0x90, 0x81, 0xa2, 0xe5, 0xb4, 0x5d, 0x0c, 0x21, 0x32, 0x36, 0x4d, - 0x4c, 0x33, 0x49, 0x52, 0xcd, 0x21, 0x77, 0x95, 0xd9, 0xb4, 0xe5, 0x97, - 0x3b, 0xba, 0x91, 0xa2, 0x52, 0xbf, 0xf4, 0xc2, 0xc6, 0x14, 0x97, 0xdf, - 0x88, 0x69, 0xc5, 0x87, 0x28, 0xca, 0x4c, 0x2f, 0xe6, 0xdd, 0x17, 0x8e, - 0xc9, 0x55, 0x1f, 0x9f, 0x3a, 0xfa, 0xc1, 0x24, 0x89, 0xc9, 0xf9, 0x4c, - 0xdb, 0xce, 0xe7, 0xcf, 0x74, 0x3e, 0x42, 0xc8, 0xc4, 0xd8, 0x24, 0xd9, - 0x71, 0x36, 0x84, 0x58, 0x22, 0x9e, 0x27, 0x87, 0x8d, 0xb3, 0x60, 0xbc, - 0xd9, 0x86, 0x63, 0x3f, 0x7d, 0x8a, 0xa2, 0x9f, 0xad, 0xa7, 0xf8, 0x9d, - 0x3b, 0x28, 0xd3, 0xce, 0xad, 0x54, 0xef, 0xbf, 0xed, 0xa1, 0xfa, 0x9c, - 0x16, 0x43, 0x2f, 0x43, 0x50, 0x94, 0x50, 0x7f, 0x3a, 0x84, 0xc4, 0x8d, - 0xe9, 0x3c, 0xf9, 0xf5, 0x73, 0x0f, 0x98, 0x27, 0xb6, 0x52, 0xce, 0xf7, - 0xae, 0x91, 0xb0, 0xe6, 0x18, 0x2e, 0xe2, 0x67, 0xcc, 0xc4, 0xd1, 0x6f, - 0xf4, 0xb1, 0x5e, 0x76, 0x9a, 0x92, 0x50, 0x76, 0xf9, 0x1a, 0x3a, 0x79, - 0x4b, 0xc1, 0x11, 0x9e, 0xa7, 0x58, 0x45, 0xfc, 0xb4, 0xa0, 0x59, 0x0c, - 0x4c, 0x44, 0x31, 0x20, 0x2c, 0xff, 0xff, 0xfb, 0x6b, 0xa4, 0x58, 0xbd, - 0x1e, 0xde, 0x82, 0xf6, 0x27, 0x79, 0x30, 0xdb, 0xd5, 0x60, 0x8d, 0x8a, - 0xfc, 0xb4, 0x90, 0x2b, 0x52, 0x94, 0x00, 0xa5, 0xfa, 0x39, 0x47, 0x3f, - 0x98, 0x26, 0x03, 0x22, 0x8a, 0xf6, 0x47, 0x6e, 0xfe, 0xc8, 0x81, 0x26, - 0x58, 0x4f, 0x8f, 0x20, 0xf7, 0x64, 0xf6, 0xae, 0x09, 0xc0, 0xf9, 0xde, - 0x24, 0xe9, 0x6c, 0xb0, 0xa0, 0xc9, 0xcc, 0x16, 0xfd, 0xf6, 0x85, 0x77, - 0x89, 0x90, 0x2b, 0xb1, 0x14, 0x0e, 0x5f, 0x8d, 0x20, 0x26, 0xca, 0x79, - 0xfd, 0x68, 0x63, 0x80, 0xc0, 0x5e, 0x3b, 0x32, 0xfd, 0xa7, 0x0d, 0x77, - 0xd5, 0x2d, 0xd8, 0xac, 0x1d, 0xad, 0x8c, 0xfa, 0xbb, 0x9d, 0x76, 0x98, - 0x76, 0x6e, 0xa5, 0xe2, 0x05, 0xda, 0xcd, 0x3d, 0xbb, 0x95, 0x9a, 0x9d, - 0x23, 0xa4, 0xf1, 0x62, 0x14, 0x42, 0x2c, 0xdb, 0xbf, 0x2d, 0x2b, 0xea, - 0x79, 0x17, 0xce, 0xf7, 0x26, 0xc9, 0x95, 0x66, 0xbb, 0x7e, 0xfc, 0xeb, - 0xfa, 0xcc, 0x02, 0x06, 0xf7, 0xb9, 0x50, 0x7f, 0x6a, 0x24, 0x6f, 0xa4, - 0x14, 0xc5, 0x98, 0x84, 0xc5, 0x7f, 0x54, 0x16, 0x08, 0xee, 0xaf, 0xf0, - 0x3c, 0x04, 0x86, 0x66, 0x31, 0xf8, 0x22, 0x8f, 0x48, 0x99, 0x0a, 0x50, - 0xcd, 0x8c, 0x51, 0x08, 0x83, 0xa2, 0x84, 0xa0, 0x28, 0x43, 0x3b, 0x89, - 0xc7, 0x88, 0x69, 0x33, 0x1d, 0xf8, 0x0c, 0x9d, 0x59, 0x7f, 0xf1, 0x9d, - 0xbb, 0x42, 0xe8, 0x7e, 0x69, 0x2b, 0xe5, 0xba, 0x35, 0x4d, 0xda, 0x2e, - 0xe6, 0xd7, 0x67, 0xc4, 0xb4, 0x1a, 0x53, 0x4f, 0xcd, 0x11, 0xe2, 0x38, - 0x7b, 0x25, 0x6f, 0x87, 0x33, 0x1c, 0x03, 0xc2, 0x31, 0x75, 0xe7, 0xc9, - 0xf9, 0xde, 0x35, 0xc2, 0xb2, 0x9c, 0xca, 0xe8, 0x34, 0x8b, 0xb8, 0x92, - 0x42, 0x48, 0x52, 0x20, 0x2b, 0x32, 0x5e, 0xfe, 0x5d, 0x08, 0xfa, 0xce, - 0x54, 0xce, 0x0e, 0x95, 0x93, 0x03, 0x42, 0xaf, 0xa4, 0x77, 0xa8, 0xdc, - 0x4b, 0x84, 0x74, 0x7d, 0x2c, 0xe1, 0xe0, 0xe9, 0x11, 0xc3, 0xfb, 0x19, - 0xa4, 0x70, 0xae, 0xd9, 0x85, 0xc4, 0xf6, 0x4d, 0x25, 0x89, 0x66, 0x74, - 0x23, 0x45, 0x75, 0x00, 0x88, 0x8c, 0x4d, 0x93, 0x81, 0x89, 0xfc, 0x1d, - 0xd0, 0x70, 0x4c, 0x5d, 0xa4, 0x9e, 0xfb, 0x22, 0x49, 0x12, 0xdb, 0x37, - 0x51, 0x79, 0x67, 0x79, 0xe9, 0x47, 0x23, 0x55, 0xc0, 0x84, 0x9d, 0xcf, - 0x70, 0xe8, 0x75, 0x96, 0xbf, 0x23, 0x46, 0xbd, 0x73, 0x05, 0xf8, 0xb6, - 0xa6, 0xec, 0xf2, 0xcf, 0xed, 0xb6, 0x81, 0x2e, 0x73, 0xe7, 0x88, 0x01, - 0x30, 0x31, 0x36, 0xa9, 0xae, 0xb6, 0x2b, 0x60, 0xf2, 0x62, 0xd8, 0xdd, - 0xc0, 0x81, 0x5f, 0x8e, 0x9c, 0xcf, 0x3d, 0xeb, 0xec, 0xf6, 0xeb, 0xde, - 0xac, 0x19, 0x80, 0x9f, 0x59, 0x20, 0x8d, 0x67, 0x47, 0xd5, 0x7e, 0xb1, - 0x59, 0xd0, 0xb0, 0xab, 0xf4, 0x51, 0x4e, 0x85, 0x90, 0x9f, 0x21, 0xa4, - 0x2d, 0x9c, 0xcf, 0x40, 0x36, 0x06, 0x38, 0xf6, 0xd3, 0xc2, 0x67, 0x9b, - 0x29, 0xb7, 0x92, 0x24, 0x20, 0x48, 0x59, 0xd1, 0x68, 0xd5, 0x62, 0xbb, - 0x8d, 0x87, 0xef, 0x49, 0x6b, 0x9e, 0x9b, 0x31, 0xf2, 0xc1, 0x6d, 0x32, - 0x20, 0x18, 0xef, 0x6c, 0x39, 0x39, 0x95, 0x00, 0xab, 0x39, 0xa6, 0x95, - 0x9f, 0x59, 0x20, 0x4d, 0xc3, 0xa3, 0x79, 0x33, 0x86, 0x86, 0x97, 0x9b, - 0x5d, 0xc6, 0xe7, 0xd3, 0x66, 0x69, 0x7c, 0xee, 0xc8, 0x33, 0x42, 0x9a, - 0xc5, 0xf8, 0x7e, 0x1b, 0x1c, 0x16, 0xb6, 0xa4, 0xc2, 0x56, 0x7a, 0xb6, - 0x57, 0xbb, 0x8d, 0x81, 0xfb, 0xa5, 0xca, 0x4f, 0x33, 0xcc, 0x8c, 0x7b, - 0x65, 0x96, 0xa7, 0x9a, 0x6a, 0xd0, 0xc8, 0x24, 0x69, 0xb8, 0x4b, 0x88, - 0xac, 0xa4, 0x60, 0xe3, 0x58, 0xc3, 0x13, 0xcb, 0x7d, 0x84, 0x90, 0xf8, - 0x37, 0x8b, 0xab, 0xf6, 0x0d, 0x09, 0x2d, 0x3e, 0x57, 0x93, 0xab, 0x9c, - 0xc5, 0xa8, 0x96, 0x2f, 0xf8, 0xb9, 0x04, 0x49, 0xa1, 0x11, 0x5a, 0x8e, - 0x4f, 0x95, 0xe4, 0x38, 0x62, 0x72, 0x4d, 0x5e, 0x3f, 0xdb, 0x38, 0x80, - 0xa1, 0x19, 0xb0, 0x35, 0x0a, 0x6c, 0x66, 0x0b, 0x6c, 0x0c, 0xd0, 0xb4, - 0xcd, 0x54, 0xf0, 0xb4, 0x4a, 0xe5, 0xd6, 0x34, 0x09, 0x08, 0x29, 0x08, - 0x09, 0x35, 0xfa, 0xcc, 0x69, 0x06, 0x58, 0x96, 0x43, 0xfb, 0xa3, 0x2c, - 0x94, 0x15, 0xc6, 0xf1, 0xfa, 0x08, 0x21, 0xfd, 0x37, 0x25, 0x8c, 0x88, - 0x29, 0x00, 0x32, 0xc2, 0x31, 0xc0, 0xcc, 0x2c, 0xc2, 0xc2, 0x98, 0x30, - 0xb8, 0x8f, 0x37, 0x3e, 0x9f, 0xd6, 0xbb, 0x44, 0x88, 0x7d, 0x38, 0x7f, - 0x84, 0x17, 0xc5, 0x87, 0x4d, 0x18, 0x77, 0x3a, 0xe0, 0x28, 0x7c, 0xb4, - 0x17, 0x7a, 0x27, 0x52, 0x15, 0x9d, 0x7f, 0x50, 0xc9, 0x69, 0x89, 0x6b, - 0xf8, 0xfd, 0xc1, 0x82, 0x27, 0x81, 0xbb, 0x97, 0x08, 0x69, 0x3c, 0x1b, - 0xad, 0xf8, 0x5b, 0x0b, 0xee, 0xc7, 0x19, 0x9c, 0x7c, 0xce, 0x9e, 0x15, - 0xce, 0x38, 0x15, 0x07, 0xda, 0xc2, 0x11, 0x08, 0x5f, 0x2a, 0x65, 0x97, - 0xe3, 0xe1, 0x69, 0x8c, 0xec, 0x77, 0x54, 0x74, 0x2e, 0xed, 0x1a, 0x7e, - 0x3f, 0xb0, 0xe4, 0x77, 0xc4, 0xc4, 0xb1, 0x69, 0xe2, 0x9f, 0x88, 0xae, - 0x8a, 0x6d, 0x58, 0x2e, 0xb6, 0xf3, 0xd5, 0x99, 0x04, 0x6b, 0xf8, 0xfd, - 0xc0, 0xb2, 0xbe, 0x23, 0x46, 0xdf, 0x4a, 0x92, 0xc3, 0x57, 0x43, 0xd9, - 0xa7, 0x2a, 0xde, 0x03, 0x64, 0x68, 0x16, 0x7d, 0xbb, 0xcb, 0x3b, 0x87, - 0x76, 0x0d, 0xbf, 0xbf, 0x58, 0xd6, 0x77, 0xc4, 0x94, 0x27, 0x36, 0x51, - 0xb3, 0x6f, 0x78, 0xe1, 0x34, 0xa3, 0xa2, 0x1d, 0xac, 0x4a, 0xd0, 0xc9, - 0xab, 0x61, 0x69, 0x6b, 0x0a, 0xbb, 0x86, 0xa5, 0xb0, 0xe2, 0x6f, 0xe3, - 0x9a, 0x66, 0x92, 0xe4, 0x78, 0x58, 0x4a, 0xbf, 0x2b, 0xb5, 0x42, 0x86, - 0x75, 0x9a, 0x81, 0xbe, 0xdd, 0xae, 0xb2, 0x37, 0x0e, 0xd6, 0x70, 0x0d, - 0xab, 0xfa, 0x36, 0xae, 0x0c, 0xf5, 0x65, 0x34, 0xff, 0xc7, 0x12, 0x46, - 0xa4, 0xb8, 0xba, 0x75, 0x57, 0x81, 0xcd, 0x6a, 0xa6, 0x17, 0xe1, 0xb3, - 0x59, 0xd1, 0xf9, 0xa4, 0xfa, 0x5d, 0xdc, 0xfb, 0xa1, 0x23, 0xd6, 0xf0, - 0x9f, 0x07, 0x57, 0xf4, 0x15, 0x72, 0x0d, 0x35, 0x7f, 0xa0, 0xac, 0xd0, - 0x08, 0xc5, 0x24, 0xa4, 0x16, 0xe9, 0xac, 0x3b, 0xb4, 0x1d, 0x18, 0x1b, - 0x67, 0x82, 0x9d, 0xa3, 0xd7, 0xbe, 0x81, 0xbb, 0x86, 0x2b, 0xc2, 0xff, - 0x05, 0x0b, 0xe4, 0xdf, 0xad, 0x23, 0xe1, 0xfb, 0xe0, 0x00, 0x00, 0x00, - 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 + 0x08, 0x06, 0x00, 0x00, 0x00, 0x63, 0x57, 0xfa, 0xde, 0x00, 0x00, 0x16, + 0x58, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0x5d, 0x7f, 0x6c, 0x53, + 0xd7, 0xbd, 0xff, 0x1c, 0xc4, 0xd0, 0x0d, 0xca, 0xaa, 0xeb, 0x6a, 0x45, + 0xd7, 0x51, 0x5b, 0xe5, 0x22, 0x40, 0xdc, 0xb0, 0xa2, 0xda, 0x2d, 0xd5, + 0xec, 0xbc, 0x21, 0x6c, 0xb4, 0x6a, 0x18, 0xa8, 0x5e, 0x62, 0x56, 0x89, + 0x84, 0x6e, 0x22, 0xe6, 0x87, 0x3a, 0xa2, 0x3d, 0x75, 0x09, 0x7d, 0xda, + 0x0a, 0xad, 0x28, 0xa0, 0x4a, 0x90, 0x80, 0x3a, 0x25, 0x45, 0x6b, 0x71, + 0x50, 0x57, 0x9c, 0x48, 0x05, 0x87, 0x09, 0x48, 0x78, 0x6a, 0x87, 0x5d, + 0xf1, 0x5e, 0xec, 0xa9, 0xbc, 0xf8, 0x4e, 0xf4, 0x81, 0x2b, 0x40, 0xb9, + 0x11, 0xab, 0x62, 0x2b, 0x9b, 0xe2, 0x2b, 0x86, 0x12, 0x2b, 0xaf, 0xd2, + 0xf7, 0xfd, 0xe1, 0xf8, 0xc6, 0x3f, 0xae, 0xed, 0x6b, 0x27, 0xb4, 0xec, + 0x35, 0xe7, 0x9f, 0x0f, 0xe1, 0xde, 0xf3, 0x3d, 0xdf, 0x73, 0xce, 0xf7, + 0x7c, 0xce, 0xf7, 0x7c, 0xcf, 0xb9, 0xc7, 0xec, 0x2a, 0x00, 0x05, 0x80, + 0x38, 0x4f, 0xe8, 0x21, 0xa2, 0xd8, 0x5f, 0x01, 0xf0, 0x80, 0xa2, 0x02, + 0x22, 0x0f, 0x98, 0x39, 0x20, 0xb0, 0x84, 0xb1, 0xf9, 0x2c, 0x67, 0x01, + 0xbf, 0xbb, 0xc8, 0x4e, 0xcf, 0x41, 0x80, 0x87, 0x88, 0x06, 0xee, 0xc4, + 0x30, 0xaa, 0x72, 0xf0, 0xca, 0x32, 0xe4, 0x38, 0x5f, 0x34, 0x87, 0xa3, + 0xd6, 0x0a, 0xab, 0x39, 0x81, 0x96, 0x67, 0xad, 0x08, 0x2f, 0x63, 0xec, + 0x51, 0x68, 0x80, 0x05, 0xfc, 0xe7, 0x43, 0x56, 0x09, 0xd3, 0x0a, 0xe3, + 0x44, 0xfd, 0x5f, 0x26, 0xd0, 0x19, 0x0e, 0x40, 0xe5, 0x44, 0x40, 0x55, + 0x00, 0xbe, 0x3c, 0xe4, 0x05, 0x11, 0x8d, 0x66, 0x11, 0x5d, 0x3f, 0x35, + 0xa1, 0x77, 0xc9, 0x82, 0x01, 0x2f, 0xa0, 0x71, 0x2c, 0x8b, 0x69, 0x9b, + 0xa6, 0x89, 0x36, 0xf7, 0x05, 0x10, 0x1c, 0x2d, 0xce, 0xa8, 0xe5, 0xa2, + 0xa3, 0xd6, 0x0a, 0x5f, 0x83, 0x80, 0xe8, 0x63, 0x4b, 0x17, 0x5c, 0x88, + 0x05, 0x2c, 0x89, 0x8b, 0x8c, 0xbc, 0xe8, 0x9c, 0x26, 0x4a, 0x7e, 0x3e, + 0x41, 0x4b, 0xdf, 0xf1, 0x23, 0xa8, 0xce, 0x18, 0x2c, 0x2f, 0xce, 0x1b, + 0x06, 0x47, 0x23, 0xa8, 0x39, 0x13, 0x45, 0xe8, 0xda, 0x4d, 0xfa, 0xb6, + 0x1b, 0x64, 0x01, 0x1f, 0x7d, 0x2c, 0xc9, 0xb4, 0xc2, 0xf8, 0x04, 0xed, + 0xb8, 0xa0, 0x42, 0x8e, 0x47, 0xbe, 0x11, 0x95, 0x2c, 0x82, 0x8a, 0xc8, + 0x1e, 0x27, 0xbc, 0x6c, 0xc1, 0x65, 0x58, 0x40, 0x7d, 0x2c, 0xca, 0xb4, + 0xb6, 0x71, 0xa2, 0xfa, 0x0b, 0x4a, 0xca, 0x60, 0xe7, 0x91, 0x59, 0x8b, + 0xa1, 0x9c, 0xe4, 0x61, 0x3a, 0xe6, 0x47, 0xd3, 0x34, 0xd1, 0xa3, 0xd0, + 0x40, 0x0b, 0xf8, 0xe8, 0x61, 0x41, 0xa6, 0x15, 0xc6, 0x27, 0xa8, 0xfe, + 0x8c, 0x02, 0x35, 0x59, 0xa6, 0xc8, 0x1f, 0xc4, 0x80, 0xc5, 0x75, 0xda, + 0xdf, 0xae, 0x27, 0x12, 0x38, 0xfc, 0x23, 0x2b, 0x14, 0x35, 0x86, 0x78, + 0x92, 0x47, 0xeb, 0xa5, 0x41, 0x43, 0x72, 0x78, 0x4e, 0xc1, 0xd8, 0x6b, + 0xee, 0x39, 0x2d, 0xd2, 0x3c, 0x93, 0x44, 0xdd, 0x1f, 0x79, 0x11, 0xf9, + 0xef, 0x10, 0x72, 0x93, 0xb4, 0x5a, 0x82, 0x6b, 0x9d, 0x0b, 0xa6, 0x15, + 0x26, 0x44, 0x6b, 0x6a, 0xfe, 0x29, 0x7c, 0x69, 0xcf, 0x24, 0xd1, 0xc0, + 0x9f, 0x06, 0x60, 0xff, 0xa1, 0x1d, 0xfd, 0xcb, 0x1f, 0x9f, 0x97, 0x99, + 0xc8, 0x76, 0x7f, 0x92, 0xba, 0x3f, 0xe8, 0x46, 0xf4, 0xcb, 0x28, 0xa2, + 0x77, 0x14, 0x48, 0x2b, 0x45, 0xb4, 0xbd, 0xd2, 0x86, 0xf0, 0xfa, 0x35, + 0x65, 0xcb, 0x7f, 0x18, 0xfa, 0xe9, 0xa1, 0x6e, 0xf4, 0xc0, 0x36, 0x4e, + 0x54, 0x73, 0x21, 0x02, 0x35, 0xae, 0x18, 0x8b, 0x06, 0x24, 0x15, 0x9c, + 0xdf, 0xea, 0x86, 0xfb, 0x49, 0x00, 0xdf, 0xcf, 0xb3, 0x0f, 0x2d, 0x85, + 0x14, 0xa0, 0xfe, 0x52, 0xc4, 0x70, 0x94, 0x81, 0x4f, 0x56, 0x66, 0xb8, + 0xce, 0xfb, 0x44, 0xde, 0x33, 0x3e, 0x74, 0x7e, 0xd4, 0x09, 0x11, 0x22, + 0x14, 0x28, 0x9a, 0x0e, 0x7a, 0x7f, 0x5b, 0xb6, 0x5a, 0x70, 0xe4, 0xcd, + 0x03, 0x8f, 0xbc, 0x4b, 0x12, 0x38, 0x74, 0x98, 0xe4, 0x4b, 0x32, 0x14, + 0x28, 0x18, 0xfa, 0xf8, 0xea, 0xbc, 0x18, 0x46, 0x6c, 0xff, 0x61, 0xea, + 0x0d, 0xf6, 0x67, 0xb5, 0x47, 0xad, 0x43, 0x84, 0xfb, 0x78, 0x67, 0xd9, + 0x03, 0xf9, 0x61, 0xe8, 0x67, 0x88, 0x69, 0x6d, 0xd3, 0x44, 0x35, 0x27, + 0x23, 0x65, 0x31, 0xec, 0xf0, 0x6e, 0x37, 0xac, 0x66, 0x14, 0x4d, 0x9e, + 0x2b, 0x11, 0xf4, 0x5c, 0x47, 0xd9, 0x2a, 0xf2, 0x9c, 0x82, 0xc4, 0xeb, + 0xee, 0xb2, 0x0c, 0x2a, 0xb4, 0xbb, 0x85, 0xc2, 0xb2, 0xac, 0x95, 0x2d, + 0x08, 0x66, 0xb8, 0xb7, 0x36, 0xc2, 0x65, 0x73, 0x21, 0xf4, 0x45, 0x00, + 0x5c, 0x9c, 0x43, 0x77, 0xb0, 0x07, 0xf1, 0x78, 0x4c, 0x7b, 0xe7, 0xec, + 0xfb, 0xa7, 0xa1, 0x5a, 0x9f, 0x7b, 0xa4, 0x19, 0xd7, 0x33, 0x49, 0xe4, + 0xbb, 0xe0, 0x83, 0xfd, 0x19, 0x27, 0x02, 0x6b, 0x6b, 0xe6, 0x6c, 0x10, + 0xce, 0x1b, 0x63, 0xb4, 0xb9, 0x65, 0x73, 0x56, 0x3f, 0xd9, 0x2c, 0x16, + 0x78, 0xdf, 0xf7, 0x56, 0x34, 0x80, 0xe7, 0x5b, 0x3f, 0xc3, 0x4c, 0xeb, + 0xbb, 0x38, 0x42, 0x41, 0x39, 0x62, 0x3c, 0xee, 0xba, 0x42, 0x02, 0x35, + 0xd5, 0xa1, 0x58, 0xb2, 0xf6, 0x86, 0x20, 0xff, 0x9d, 0xab, 0x28, 0x9e, + 0x0b, 0x5e, 0x84, 0x83, 0x57, 0x71, 0xe0, 0x17, 0x1b, 0x0d, 0x19, 0x94, + 0x67, 0x9c, 0xa8, 0xce, 0x65, 0xd5, 0x18, 0xb5, 0xa5, 0xc1, 0x03, 0xd3, + 0x1b, 0xad, 0xba, 0x0d, 0xe8, 0xbc, 0x4d, 0x14, 0xba, 0x1b, 0x42, 0xe2, + 0xeb, 0x04, 0xaa, 0x5e, 0xda, 0x52, 0x50, 0xbe, 0xf3, 0x36, 0x11, 0x56, + 0x62, 0x4e, 0x4c, 0xec, 0x99, 0x24, 0xf2, 0x2e, 0x2d, 0x9c, 0xbf, 0x71, + 0x64, 0x82, 0x2a, 0x65, 0x26, 0xe7, 0x18, 0x51, 0x22, 0x99, 0x28, 0x8b, + 0xd9, 0x3c, 0xe3, 0x44, 0xf6, 0x97, 0x9d, 0x50, 0x1f, 0xa8, 0x00, 0xa0, + 0xb5, 0xd7, 0xe1, 0x57, 0xdf, 0x80, 0xea, 0xd9, 0x56, 0xb0, 0xbd, 0x02, + 0xab, 0xca, 0xdf, 0xd9, 0x2c, 0x57, 0x3f, 0xe7, 0x18, 0x11, 0x78, 0xa0, + 0x50, 0x7b, 0x65, 0x31, 0xad, 0x70, 0x9b, 0x68, 0x4b, 0x9f, 0xbf, 0x2c, + 0x95, 0x5a, 0xd6, 0x01, 0xde, 0x4d, 0x56, 0x1d, 0x53, 0x4d, 0xa5, 0x4a, + 0x19, 0x36, 0x17, 0xcf, 0x36, 0x98, 0x60, 0x5e, 0xbb, 0xbc, 0x64, 0x83, + 0xf1, 0x91, 0x61, 0xda, 0xb1, 0x67, 0x97, 0x56, 0xbe, 0xc3, 0xe1, 0x2c, + 0x7b, 0xaa, 0x73, 0xde, 0x18, 0xa3, 0xf6, 0x77, 0xda, 0x11, 0xbd, 0x13, + 0xd5, 0xad, 0x93, 0xc3, 0xe1, 0x44, 0xfb, 0xee, 0x7d, 0x50, 0x56, 0x2d, + 0x67, 0x07, 0x36, 0x38, 0x48, 0x7d, 0xa0, 0x82, 0xaf, 0xe6, 0xe1, 0xfb, + 0x9d, 0x2f, 0x8f, 0x61, 0x70, 0x6d, 0x88, 0x0e, 0xbc, 0x79, 0x00, 0xea, + 0x03, 0x15, 0xd2, 0x4a, 0x09, 0xae, 0xbe, 0x5e, 0x96, 0xd6, 0xd3, 0xdb, + 0x17, 0x86, 0x7c, 0xdd, 0xaf, 0x19, 0x4e, 0x3a, 0x49, 0x2b, 0x25, 0xd8, + 0x9e, 0xb7, 0xa0, 0xfd, 0xd5, 0xf6, 0xac, 0x8e, 0xe3, 0x3b, 0xcf, 0xd3, + 0xc1, 0xbe, 0xa3, 0x00, 0x80, 0xae, 0x13, 0x5d, 0xc0, 0xfa, 0x7a, 0x16, + 0xfd, 0xfd, 0x69, 0xea, 0xfe, 0xa0, 0x5b, 0xcb, 0x2b, 0x08, 0x66, 0x6d, + 0x06, 0x39, 0xfd, 0x5a, 0x17, 0xa2, 0xcd, 0xf5, 0xba, 0x1d, 0x9f, 0xd6, + 0x5b, 0x2f, 0x9d, 0x3f, 0x71, 0x16, 0xe1, 0xf5, 0x6b, 0x98, 0xed, 0xda, + 0x4d, 0x6a, 0x3d, 0xb6, 0x3f, 0x6b, 0x46, 0x4a, 0x27, 0xbe, 0x9a, 0x87, + 0xc7, 0xe1, 0x81, 0x6b, 0xb7, 0x53, 0x5b, 0x13, 0x18, 0xd1, 0x8f, 0xaf, + 0xe6, 0xb5, 0xfa, 0xa6, 0xf5, 0xf3, 0x10, 0x51, 0xf7, 0xfb, 0x5e, 0xf8, + 0x2f, 0xf5, 0xe7, 0x95, 0x25, 0x08, 0x66, 0xb4, 0x6d, 0x6d, 0x83, 0x73, + 0xbb, 0x0d, 0xe1, 0x99, 0x38, 0xfe, 0x62, 0x6d, 0xe4, 0x11, 0x91, 0xe9, + 0x98, 0xbf, 0x6c, 0x26, 0x14, 0x79, 0xa7, 0x6e, 0xc5, 0x81, 0x94, 0x0f, + 0xdb, 0x73, 0x07, 0xa9, 0x12, 0x2a, 0x60, 0xd8, 0x4c, 0x6c, 0x0d, 0x03, + 0x89, 0x69, 0xa2, 0x52, 0x67, 0x18, 0x3c, 0xab, 0xb3, 0x07, 0xd0, 0x68, + 0x50, 0xc1, 0x91, 0x2d, 0x2e, 0xda, 0xd7, 0xd0, 0x02, 0xac, 0x34, 0xa3, + 0xf1, 0x49, 0xa9, 0xe4, 0x88, 0x4f, 0x4f, 0x99, 0x7c, 0x35, 0x8f, 0xd3, + 0x6f, 0x77, 0xa1, 0x4e, 0xa8, 0x43, 0x62, 0x71, 0x02, 0x07, 0x4e, 0x1d, + 0xc5, 0x68, 0x50, 0x41, 0x30, 0x18, 0x80, 0x7c, 0x3d, 0x82, 0xd0, 0x7d, + 0x22, 0xf1, 0x25, 0x0f, 0x64, 0xc8, 0x50, 0x1f, 0xa8, 0xf0, 0xfc, 0x76, + 0x17, 0x0e, 0xe4, 0xc8, 0x0b, 0x1d, 0xf3, 0x69, 0x9d, 0x94, 0xf8, 0x87, + 0x0a, 0x11, 0x40, 0xa4, 0xf3, 0x38, 0xf5, 0xcc, 0x0c, 0x2c, 0x11, 0x22, + 0x38, 0xa1, 0x0a, 0x4e, 0xc9, 0x06, 0x45, 0x55, 0x90, 0xbc, 0x03, 0xc8, + 0x77, 0x64, 0x44, 0xef, 0x44, 0xd1, 0xd3, 0xd7, 0x8b, 0xf3, 0xd7, 0x6e, + 0x52, 0x7a, 0x51, 0xd4, 0xda, 0x77, 0x54, 0x63, 0xc4, 0xa9, 0x78, 0x12, + 0x71, 0x00, 0x89, 0xbb, 0xd9, 0x9d, 0x5c, 0x15, 0xe7, 0xb4, 0x7f, 0x0f, + 0xde, 0x0d, 0xc0, 0x59, 0xa0, 0x9e, 0x1d, 0xaf, 0x1f, 0xc1, 0xae, 0x37, + 0x5b, 0x91, 0xeb, 0xe3, 0xb7, 0x34, 0x78, 0x50, 0xf7, 0x6c, 0x1d, 0xe2, + 0x00, 0xb6, 0xfd, 0x7a, 0x07, 0x44, 0x88, 0x10, 0x04, 0x33, 0xba, 0x5e, + 0x3f, 0x8e, 0x64, 0x75, 0x12, 0x22, 0x2f, 0xe2, 0xc0, 0xa9, 0xa3, 0x08, + 0x06, 0x03, 0xe8, 0xbc, 0xd4, 0x89, 0xd0, 0xf5, 0x10, 0xec, 0x33, 0x72, + 0x8d, 0xe8, 0x67, 0x7a, 0x60, 0x82, 0x0a, 0x55, 0xd3, 0xef, 0xc8, 0x38, + 0x91, 0xdd, 0x31, 0xcb, 0xf8, 0x36, 0xc1, 0x06, 0xf3, 0x3a, 0x33, 0x2c, + 0x4f, 0x4a, 0xe8, 0xfd, 0x53, 0x3f, 0x92, 0x77, 0x92, 0xd8, 0xff, 0x41, + 0x3b, 0xf8, 0x3e, 0x1e, 0xa1, 0x19, 0xa6, 0xd7, 0x98, 0xd6, 0x77, 0x71, + 0x98, 0x7a, 0x64, 0x14, 0xe9, 0xca, 0x02, 0x28, 0xa8, 0xa0, 0x3d, 0xfa, + 0x86, 0xeb, 0x09, 0x27, 0xd0, 0xf3, 0x69, 0xa0, 0x3c, 0x79, 0x45, 0xf0, + 0x70, 0x83, 0x0b, 0xe6, 0xb5, 0x4b, 0x4b, 0x4e, 0x31, 0xb6, 0x6b, 0x37, + 0x69, 0xdb, 0xaf, 0x77, 0xe8, 0xea, 0x94, 0x4e, 0x7c, 0x35, 0x0f, 0x69, + 0xa5, 0x08, 0xe7, 0x06, 0x27, 0x9a, 0x1b, 0x9a, 0xb3, 0x18, 0xcd, 0x33, + 0x4e, 0x14, 0xf9, 0x2a, 0xa2, 0xeb, 0xe3, 0xb6, 0x3e, 0x6f, 0xa1, 0xb4, + 0x8c, 0xa1, 0x8f, 0xaf, 0xa2, 0xf7, 0xd3, 0x7e, 0x64, 0x32, 0xc9, 0xd5, + 0x8b, 0x03, 0x1a, 0xf3, 0xa4, 0x5d, 0x95, 0x74, 0x3a, 0x7f, 0xe2, 0x2c, + 0x14, 0x55, 0xc1, 0xfe, 0xb7, 0x0f, 0x6a, 0x3a, 0xec, 0xdb, 0xed, 0x81, + 0xb9, 0x79, 0x47, 0x56, 0x39, 0x92, 0x6f, 0x88, 0x76, 0x9d, 0x6c, 0xd5, + 0xf2, 0x0d, 0xf4, 0x0c, 0x20, 0xb0, 0xb6, 0x86, 0x75, 0x66, 0x94, 0xbd, + 0x6f, 0xf7, 0x3e, 0x48, 0x7b, 0x77, 0x31, 0x5f, 0x8e, 0xff, 0x0e, 0xa4, + 0x98, 0xba, 0x65, 0x7b, 0x13, 0xaa, 0x5e, 0xda, 0xc2, 0x62, 0xbe, 0xb3, + 0x14, 0xfa, 0x8b, 0x8a, 0xa4, 0x1a, 0x01, 0x00, 0x58, 0x9f, 0xb7, 0x63, + 0xdf, 0x1e, 0x0f, 0xbc, 0x8c, 0xb1, 0xdc, 0x72, 0x1a, 0xb7, 0x36, 0x42, + 0x7c, 0xeb, 0x60, 0xc9, 0x76, 0xf0, 0x4c, 0x12, 0xd5, 0xad, 0x9f, 0xad, + 0x57, 0x7a, 0xd1, 0x55, 0xae, 0x7e, 0x9b, 0xb7, 0x6e, 0x86, 0x67, 0x8f, + 0x07, 0xe9, 0xe7, 0x0e, 0x87, 0x13, 0xd6, 0xe3, 0x9d, 0x59, 0xfd, 0x1b, + 0x38, 0x74, 0x98, 0xfa, 0x2f, 0xa5, 0x16, 0x8a, 0x36, 0x8b, 0x05, 0xf6, + 0x0f, 0x7a, 0xd8, 0x22, 0x11, 0xa9, 0x1d, 0xaf, 0xfe, 0x18, 0xa0, 0x31, + 0x62, 0x39, 0x98, 0xe4, 0x71, 0xeb, 0x6f, 0xb9, 0x26, 0x91, 0x4a, 0x5d, + 0x2b, 0x4c, 0xf3, 0x1a, 0xc7, 0xed, 0xbc, 0x32, 0x08, 0x0f, 0x95, 0x8e, + 0xdf, 0x86, 0xd7, 0xaf, 0x61, 0xb7, 0xae, 0x45, 0xd0, 0xb2, 0xbd, 0x09, + 0xae, 0x75, 0x2e, 0x00, 0x29, 0x46, 0xcb, 0x4c, 0xa6, 0x07, 0x26, 0x84, + 0x65, 0x19, 0x47, 0x4f, 0x76, 0xa2, 0x6e, 0xbd, 0x15, 0xc2, 0xc7, 0x57, + 0x35, 0xb9, 0xde, 0x65, 0x8c, 0x65, 0x76, 0x14, 0x1f, 0x19, 0x26, 0x69, + 0x70, 0x88, 0x8e, 0x6c, 0x71, 0x69, 0x9d, 0x22, 0x42, 0x04, 0x54, 0x60, + 0xdf, 0x1e, 0x0f, 0xf8, 0x6a, 0x5e, 0x93, 0x1f, 0xf9, 0xaf, 0xe8, 0xec, + 0x14, 0x7c, 0xea, 0x88, 0x56, 0x9e, 0x4d, 0xb0, 0x21, 0xbc, 0x7e, 0x0d, + 0xeb, 0x7c, 0x7f, 0x50, 0xcb, 0xaf, 0x67, 0xb0, 0x22, 0x80, 0x68, 0x73, + 0x3d, 0x6b, 0x7b, 0xa5, 0x4d, 0xcb, 0xdb, 0xf1, 0x51, 0x67, 0x8e, 0xf6, + 0x40, 0xad, 0x49, 0x4c, 0xbd, 0xcf, 0x8b, 0x59, 0xf5, 0x3b, 0xfe, 0x7a, + 0x07, 0x5c, 0x7d, 0xbd, 0x2c, 0xed, 0xa3, 0x1f, 0x3d, 0xd9, 0x89, 0x60, + 0xd0, 0x8b, 0xb8, 0xac, 0x22, 0x2c, 0xcb, 0xe8, 0xfe, 0xa0, 0x1b, 0xbe, + 0x5e, 0x1f, 0x44, 0x00, 0x92, 0x24, 0x69, 0xf2, 0x44, 0x88, 0x68, 0xdb, + 0xba, 0x2f, 0x4b, 0x8f, 0xdc, 0x76, 0xb0, 0x44, 0x26, 0x48, 0x1a, 0x1c, + 0x22, 0xe7, 0xcb, 0x9b, 0xb3, 0xda, 0x53, 0x51, 0x95, 0x8a, 0xf4, 0xc3, + 0x1d, 0x68, 0x06, 0x2b, 0x42, 0x44, 0xfc, 0xab, 0x18, 0x9c, 0x37, 0xc6, + 0x88, 0x8f, 0x0c, 0x53, 0xe3, 0xc8, 0x04, 0x89, 0xd7, 0xc6, 0x28, 0x19, + 0x4f, 0x6a, 0xcf, 0xc3, 0xb2, 0x0c, 0xcf, 0x24, 0xd1, 0x62, 0x05, 0xc0, + 0xc0, 0x28, 0x52, 0xe1, 0x2d, 0xcc, 0x4c, 0xc9, 0x65, 0xe2, 0x8e, 0x4f, + 0xcd, 0x88, 0x34, 0xe5, 0x87, 0x0f, 0xaa, 0x9e, 0x00, 0x8e, 0xaf, 0x33, + 0x61, 0xff, 0xa7, 0xa8, 0x48, 0x6e, 0x2e, 0xaa, 0x10, 0xe1, 0xfb, 0x42, + 0x31, 0xc4, 0xcb, 0xde, 0xa5, 0x8c, 0x59, 0x67, 0xfe, 0x76, 0x01, 0x90, + 0xc6, 0x06, 0x88, 0x4b, 0x72, 0x90, 0xbf, 0x8a, 0x62, 0x2a, 0x9a, 0x44, + 0xcf, 0x67, 0x5e, 0xe0, 0xce, 0xac, 0xae, 0xfb, 0x8f, 0xb5, 0xe3, 0xd6, + 0x38, 0x91, 0x77, 0x19, 0x63, 0xb6, 0x6b, 0x37, 0xa9, 0xfb, 0x52, 0x0f, + 0xe4, 0xeb, 0x91, 0xd4, 0xb4, 0x95, 0xe1, 0x23, 0x6b, 0x1d, 0x05, 0x05, + 0xe0, 0x53, 0x8b, 0xb3, 0xc6, 0xce, 0xe3, 0xd4, 0xd3, 0xd7, 0x0b, 0x00, + 0x38, 0xfa, 0xde, 0x11, 0x84, 0x88, 0x48, 0xb9, 0xa3, 0x20, 0xb0, 0xc7, + 0xa3, 0xbd, 0xdf, 0xb8, 0xc7, 0x85, 0xaa, 0xcb, 0xef, 0x21, 0x1e, 0x0f, + 0x6b, 0xf9, 0x9b, 0x9b, 0x9a, 0xe1, 0x6d, 0xde, 0xa1, 0xab, 0x3f, 0x2c, + 0x22, 0xf0, 0x51, 0x2a, 0xaf, 0x7c, 0x3d, 0x82, 0xdc, 0x55, 0xc3, 0x68, + 0x42, 0x81, 0x04, 0x20, 0xa4, 0x2a, 0xb3, 0xfa, 0x00, 0x88, 0xbf, 0xbc, + 0x31, 0x8b, 0xa9, 0x1a, 0xb7, 0x36, 0x22, 0xf6, 0x55, 0xea, 0x99, 0x00, + 0x0b, 0x38, 0xde, 0x94, 0x9a, 0x59, 0xb0, 0x03, 0x16, 0x3e, 0xbf, 0x3e, + 0xb9, 0x33, 0x56, 0x76, 0x3b, 0x6c, 0xcc, 0x6b, 0x07, 0x20, 0x65, 0x98, + 0xfd, 0x39, 0xff, 0x67, 0x44, 0xbf, 0xc4, 0xe2, 0x44, 0x76, 0xf9, 0x77, + 0x66, 0x5d, 0x33, 0xbd, 0xf6, 0xe6, 0xab, 0x79, 0x4c, 0x7d, 0x3d, 0x95, + 0xf2, 0x69, 0xdb, 0x83, 0x73, 0xf3, 0x39, 0xe5, 0xbb, 0x21, 0x00, 0x6e, + 0xdd, 0xc2, 0xda, 0x6d, 0x22, 0x00, 0x11, 0xfb, 0xaf, 0x63, 0x4e, 0x3e, + 0x6d, 0x1a, 0xfb, 0xbf, 0x34, 0xc1, 0x85, 0xe2, 0x06, 0xab, 0x87, 0xc8, + 0xd8, 0x40, 0x88, 0x03, 0xd8, 0x07, 0x60, 0xea, 0xe2, 0x65, 0x7a, 0xef, + 0xed, 0x1e, 0xad, 0x41, 0xbb, 0x2f, 0x78, 0xc1, 0xbd, 0x7b, 0x56, 0x73, + 0x2d, 0xd2, 0x3e, 0xad, 0xfd, 0x59, 0x3b, 0x60, 0x06, 0x7c, 0xbd, 0x3e, + 0xf4, 0x9e, 0xec, 0xd7, 0xde, 0x4f, 0x33, 0x8c, 0x67, 0x7b, 0x3b, 0xd2, + 0x46, 0x6b, 0x7a, 0x60, 0x42, 0xdd, 0xba, 0x59, 0x13, 0x13, 0x21, 0x62, + 0x4a, 0x48, 0x66, 0x44, 0x27, 0x66, 0x7d, 0xc8, 0x88, 0x1c, 0x29, 0xec, + 0xe2, 0x80, 0x9f, 0xcd, 0x5f, 0x9d, 0xd4, 0x98, 0x2c, 0x9d, 0x3f, 0x93, + 0xc9, 0xc2, 0x90, 0x33, 0xca, 0xcb, 0x59, 0x54, 0xbe, 0x75, 0x30, 0x8f, + 0xc9, 0xbd, 0xc7, 0x53, 0xcc, 0x9d, 0xd2, 0x5f, 0xcc, 0xab, 0x8f, 0x82, + 0x54, 0xfc, 0x36, 0xed, 0xd3, 0x72, 0x42, 0x15, 0xce, 0xbf, 0xdd, 0x85, + 0xba, 0x15, 0x75, 0x5a, 0x3b, 0x1c, 0x3d, 0xd9, 0xa9, 0xe9, 0x03, 0x15, + 0x95, 0xe9, 0x97, 0x53, 0x7e, 0xe3, 0xd6, 0x46, 0xb4, 0xbd, 0xb2, 0xaf, + 0xe8, 0x9a, 0x23, 0xfc, 0xd8, 0x52, 0x2c, 0x92, 0xee, 0x4f, 0x92, 0x1c, + 0x4f, 0xcc, 0x99, 0x09, 0xfd, 0xd1, 0xfc, 0x15, 0x66, 0x3a, 0xb5, 0xdb, + 0x80, 0xf3, 0x3f, 0x31, 0x03, 0x5c, 0xe5, 0xf2, 0xd3, 0xd8, 0x1f, 0x8d, + 0x94, 0x74, 0x11, 0x3c, 0xe3, 0x44, 0x7c, 0x64, 0x98, 0x4a, 0xbd, 0x67, + 0xff, 0xa1, 0x3d, 0x6b, 0x11, 0x32, 0xf5, 0x20, 0x81, 0xce, 0x8f, 0x52, + 0x9d, 0xc1, 0x57, 0xf3, 0x08, 0x05, 0x03, 0x88, 0xba, 0xea, 0x59, 0xa0, + 0x86, 0x31, 0x2f, 0x63, 0xcc, 0xdc, 0xbc, 0x83, 0x4d, 0x09, 0xc9, 0xd9, + 0x0e, 0xe0, 0x53, 0x1d, 0x13, 0xa8, 0x61, 0xac, 0x71, 0x6b, 0x63, 0xaa, + 0xe3, 0x33, 0xe4, 0xa5, 0xff, 0x6e, 0xdb, 0xda, 0xa6, 0x95, 0x2b, 0x64, + 0xd0, 0x9b, 0xb7, 0xaf, 0xb7, 0xe0, 0x40, 0x3b, 0x78, 0xea, 0xa8, 0x96, + 0xbf, 0x79, 0x5d, 0x93, 0x26, 0x35, 0x2d, 0x7f, 0x34, 0xa1, 0x68, 0x1d, + 0x9f, 0x5d, 0x5e, 0x19, 0x03, 0x9a, 0xcf, 0xd9, 0x68, 0x99, 0xa9, 0x8f, + 0xf3, 0xc6, 0x98, 0xb6, 0xe1, 0x90, 0xa8, 0x4e, 0x20, 0x70, 0x69, 0x00, + 0xe1, 0xf5, 0x6b, 0xb4, 0x76, 0x68, 0x6e, 0x6a, 0xce, 0xaa, 0x5f, 0x9a, + 0xa1, 0xcb, 0xd5, 0xcf, 0x6a, 0xb1, 0x66, 0x95, 0xef, 0x69, 0xf0, 0x14, + 0x34, 0x58, 0x69, 0x6c, 0x4c, 0x3b, 0x4c, 0xb5, 0x28, 0x12, 0xaf, 0xc2, + 0x9c, 0x7d, 0xce, 0x1f, 0xc4, 0xe0, 0x96, 0x8a, 0xef, 0x2e, 0xb8, 0x25, + 0x33, 0xe8, 0x55, 0x2b, 0x0e, 0xff, 0xc4, 0x39, 0xe7, 0xf2, 0x7c, 0x5f, + 0x14, 0x37, 0xd8, 0x3a, 0x97, 0x15, 0x07, 0xf7, 0x1c, 0x45, 0xdd, 0x3a, + 0x2b, 0x12, 0x47, 0xbb, 0x28, 0xe6, 0x3b, 0x4b, 0x8d, 0x23, 0x13, 0xe4, + 0xbc, 0x4f, 0xe4, 0x19, 0x27, 0xc2, 0xb5, 0x21, 0xc2, 0xbb, 0x67, 0xa9, + 0xfe, 0xe5, 0xd9, 0xe9, 0xce, 0x26, 0xd8, 0xe0, 0x5e, 0x37, 0xdb, 0x19, + 0xa6, 0x07, 0x26, 0x24, 0xe4, 0xc4, 0xac, 0xdc, 0x49, 0x22, 0xff, 0xfe, + 0x36, 0x4a, 0x87, 0x64, 0x32, 0x19, 0x46, 0x01, 0x70, 0x64, 0xff, 0x81, + 0xac, 0xfa, 0xa6, 0x7d, 0x38, 0xbe, 0x9a, 0xc7, 0xe6, 0xed, 0x4e, 0xed, + 0xbd, 0x8e, 0xdf, 0x74, 0x68, 0xcf, 0x83, 0xc1, 0x00, 0x12, 0x47, 0xbb, + 0xc8, 0x79, 0x9b, 0xb4, 0x0e, 0x71, 0xde, 0x18, 0xa3, 0xee, 0xed, 0x4d, + 0x94, 0xbc, 0x93, 0xd4, 0xf2, 0x7b, 0x76, 0x37, 0x1b, 0xf6, 0x19, 0xcb, + 0x99, 0x81, 0x90, 0x11, 0xf1, 0xca, 0xac, 0xcf, 0xad, 0xd1, 0x08, 0x32, + 0x7d, 0xd6, 0xcc, 0x19, 0xc1, 0x33, 0x49, 0xe4, 0x6e, 0x6a, 0x46, 0xa5, + 0x3e, 0x6d, 0x16, 0xe3, 0x33, 0xc6, 0x0e, 0xbf, 0xfa, 0x86, 0xf6, 0xbc, + 0xf9, 0x57, 0xcd, 0xb0, 0x5d, 0xbb, 0xa9, 0x11, 0x8d, 0xf3, 0x3e, 0x91, + 0xf0, 0xf1, 0x55, 0x3a, 0xb0, 0xc1, 0x41, 0xbb, 0x5e, 0x6a, 0x45, 0xba, + 0x9d, 0x58, 0x4b, 0xa5, 0x51, 0x83, 0x34, 0xfe, 0x20, 0x06, 0xfa, 0xa5, + 0xbe, 0x1f, 0x52, 0x28, 0xc5, 0x12, 0x40, 0xcd, 0xb9, 0x00, 0x50, 0xe2, + 0x4b, 0x87, 0x42, 0xd8, 0xb6, 0xc9, 0x09, 0xe9, 0x05, 0xfd, 0x11, 0x69, + 0xbb, 0x3f, 0x49, 0x3b, 0xf6, 0xee, 0x2a, 0x18, 0x63, 0xd5, 0x4b, 0xd2, + 0x4a, 0x09, 0x1d, 0x87, 0x8e, 0x40, 0x5c, 0x29, 0xc2, 0xdd, 0xd4, 0x9c, + 0x95, 0x57, 0x10, 0xcc, 0x48, 0x3e, 0x98, 0xca, 0x8b, 0xa5, 0x02, 0xc8, + 0xdb, 0xaa, 0xc4, 0xa1, 0xb3, 0xd4, 0x79, 0xa9, 0x33, 0xeb, 0x9d, 0xf4, + 0x2a, 0x3a, 0x6b, 0xca, 0xbe, 0x31, 0x46, 0x9e, 0xdf, 0xee, 0xd2, 0x8d, + 0x7f, 0x66, 0x26, 0x41, 0x30, 0xc3, 0x7b, 0xa2, 0x0b, 0xca, 0xaa, 0x54, + 0x7c, 0xda, 0x9b, 0x11, 0x5b, 0xd5, 0x5b, 0x9d, 0x0b, 0x82, 0x19, 0xcd, + 0x97, 0x07, 0x8d, 0x6f, 0x30, 0x10, 0x51, 0xa6, 0x2b, 0x73, 0x6b, 0x30, + 0x02, 0xef, 0x32, 0xc6, 0x3c, 0x94, 0x1d, 0x86, 0x32, 0xda, 0x0e, 0xbe, + 0x2d, 0x2e, 0x6d, 0x50, 0xeb, 0xe9, 0xc7, 0x57, 0xf3, 0xf0, 0x7c, 0x16, + 0xcc, 0xd3, 0x2f, 0x91, 0xb1, 0x26, 0x28, 0x94, 0xa4, 0x95, 0x12, 0xfc, + 0xbd, 0x3e, 0x78, 0x19, 0x63, 0xac, 0xe5, 0xe2, 0x08, 0xf5, 0x94, 0xb3, + 0x03, 0x96, 0x81, 0xae, 0xa7, 0x80, 0x81, 0x86, 0xc2, 0x1b, 0x0b, 0xa5, + 0x52, 0x7b, 0xf0, 0x16, 0x3a, 0xbf, 0x48, 0x56, 0xb0, 0x43, 0x66, 0xc2, + 0x81, 0x5f, 0x14, 0xdf, 0x68, 0x70, 0xde, 0x18, 0x23, 0xff, 0xa7, 0x3e, + 0x24, 0xe3, 0x49, 0xe4, 0xee, 0xad, 0x2b, 0x50, 0xe0, 0x5a, 0xe7, 0x02, + 0x27, 0x70, 0x68, 0x72, 0x34, 0x66, 0x1d, 0x0e, 0xf1, 0x10, 0x91, 0xaf, + 0xd7, 0x07, 0xe5, 0x2f, 0x4a, 0x8a, 0x41, 0x78, 0x11, 0x31, 0xa8, 0x68, + 0x76, 0x34, 0x82, 0x13, 0x38, 0xc4, 0xee, 0xaa, 0x18, 0x4d, 0x28, 0xb0, + 0x3c, 0x25, 0xc1, 0xfe, 0x2f, 0x76, 0x04, 0x1e, 0x9b, 0x8d, 0x1b, 0xa7, + 0xcf, 0x3c, 0x84, 0xfe, 0x27, 0x00, 0x91, 0x17, 0xc1, 0x09, 0x1c, 0xac, + 0x6d, 0xfb, 0x0b, 0xea, 0x39, 0x75, 0xf1, 0x32, 0xc5, 0x15, 0x55, 0x7b, + 0x3f, 0x5d, 0x1e, 0x27, 0x70, 0x70, 0xff, 0xa4, 0x39, 0x6f, 0xa3, 0x62, + 0xea, 0xe2, 0x65, 0x92, 0xff, 0x53, 0x46, 0x0c, 0x2a, 0xba, 0x8f, 0x75, + 0xc0, 0xcb, 0x18, 0xf3, 0x8c, 0x13, 0x79, 0xde, 0xf0, 0x40, 0xe4, 0x45, + 0xd8, 0xb7, 0x3a, 0x81, 0xf5, 0xf5, 0x65, 0x6f, 0xa4, 0x0c, 0x86, 0x07, + 0x61, 0x79, 0x4a, 0x42, 0xd4, 0x55, 0x9f, 0xd5, 0x0e, 0xdd, 0xef, 0x7b, + 0x91, 0xb8, 0x1b, 0xd3, 0xf4, 0x4a, 0x56, 0x03, 0xae, 0x75, 0x4e, 0x48, + 0x82, 0x84, 0xd0, 0x5d, 0x59, 0x6b, 0x87, 0x74, 0xbe, 0x2c, 0xfd, 0x0e, + 0x75, 0xc0, 0xbb, 0x34, 0x5b, 0x3f, 0xcb, 0x8f, 0x2d, 0x05, 0x77, 0x1e, + 0xd3, 0x87, 0x9b, 0x32, 0xcb, 0x53, 0x54, 0x05, 0xf6, 0x1f, 0x3a, 0xe1, + 0xb4, 0x38, 0xa1, 0xac, 0x9f, 0x5d, 0x93, 0x30, 0x1c, 0x3a, 0x4f, 0x65, + 0x33, 0x6c, 0xb5, 0x84, 0xf3, 0x9b, 0x4c, 0x25, 0x5d, 0x02, 0x23, 0x29, + 0xa4, 0x24, 0x50, 0x7f, 0x25, 0x06, 0xfc, 0x3d, 0x6a, 0xb8, 0x7c, 0x9e, + 0x13, 0xd1, 0xf1, 0xef, 0xcf, 0x3d, 0xd2, 0x87, 0x5b, 0x16, 0xf0, 0xe1, + 0xe1, 0xa2, 0x72, 0x7d, 0xca, 0x5f, 0xfe, 0xd8, 0x09, 0xda, 0x5d, 0x37, + 0x2f, 0x06, 0x0b, 0x00, 0x76, 0xd1, 0x04, 0x7a, 0xa5, 0x0e, 0x8d, 0xeb, + 0xec, 0x86, 0xf5, 0x50, 0x93, 0xc5, 0xc3, 0x5e, 0xce, 0x69, 0x22, 0xe1, + 0x36, 0xd1, 0xe0, 0x27, 0x13, 0xe4, 0xbb, 0x38, 0x42, 0x83, 0x9f, 0x0c, + 0x93, 0x70, 0x9b, 0xc8, 0x39, 0x4d, 0x0b, 0x5f, 0x46, 0xfc, 0x3f, 0xc0, + 0xc5, 0x86, 0x57, 0xef, 0x49, 0x63, 0xa7, 0xb9, 0x2a, 0x4a, 0xdf, 0x07, + 0xfc, 0x9b, 0xcc, 0xe8, 0x16, 0x5c, 0xb3, 0xe7, 0x6d, 0x4b, 0xe8, 0x53, + 0xb4, 0x62, 0xa7, 0x6e, 0x61, 0x54, 0x9d, 0x65, 0xee, 0x20, 0x44, 0x74, + 0x86, 0xfd, 0xb0, 0x08, 0x56, 0xec, 0x9b, 0x63, 0x83, 0xd9, 0xa6, 0x89, + 0xa4, 0x24, 0xa0, 0xa8, 0x09, 0x34, 0x09, 0xfa, 0x1f, 0x65, 0x7a, 0x88, + 0x28, 0xf4, 0xd7, 0x04, 0x5c, 0xbc, 0x09, 0x8a, 0x9a, 0x80, 0xf8, 0xf4, + 0xe3, 0xf3, 0x76, 0x7a, 0xcc, 0x36, 0x4d, 0x04, 0x9d, 0xad, 0x6c, 0xee, + 0xde, 0x04, 0x71, 0x2a, 0x97, 0x8a, 0x8b, 0xf2, 0x22, 0x04, 0x4e, 0x85, + 0xb8, 0x6a, 0xee, 0xe7, 0x84, 0xa3, 0x9f, 0x8f, 0x50, 0x24, 0xc9, 0xc3, + 0xa7, 0xc8, 0xf0, 0x58, 0x6c, 0x10, 0x84, 0x24, 0xb0, 0x6c, 0xb6, 0x3e, + 0x53, 0xb7, 0xc7, 0xc8, 0x17, 0x4e, 0x66, 0xe5, 0x74, 0x5a, 0x6c, 0x70, + 0xd5, 0x02, 0xa8, 0xe0, 0x9b, 0x3f, 0x61, 0x7c, 0x82, 0x42, 0x71, 0x0e, + 0x3e, 0x39, 0xac, 0x3d, 0x31, 0x99, 0x25, 0x74, 0xfd, 0x88, 0xd7, 0xe4, + 0x31, 0xbc, 0x3b, 0x4c, 0x25, 0x7d, 0xc9, 0xc5, 0x31, 0x4c, 0xee, 0xde, + 0x8c, 0xaa, 0xef, 0x19, 0xb1, 0xc0, 0xb9, 0x25, 0x7f, 0x14, 0xd8, 0x76, + 0xae, 0xf4, 0x19, 0x88, 0xab, 0x6f, 0x6d, 0x2b, 0xd8, 0x20, 0x1b, 0x0f, + 0x9d, 0xa7, 0x4a, 0xf2, 0x19, 0xc1, 0xc1, 0x8f, 0x47, 0xa8, 0x3f, 0x3a, + 0xbb, 0x06, 0x98, 0xfc, 0x4d, 0xf6, 0x79, 0x5f, 0xdb, 0x38, 0xd1, 0x9a, + 0x53, 0xf9, 0xfa, 0x77, 0x6d, 0x72, 0x82, 0x2b, 0xb0, 0x78, 0x34, 0x82, + 0xe9, 0xb3, 0x21, 0x2a, 0x27, 0xc2, 0xc2, 0x99, 0xd0, 0xb1, 0x77, 0x79, + 0x96, 0x2f, 0xcd, 0xce, 0xe4, 0x9f, 0x53, 0x1e, 0x6a, 0xb0, 0x22, 0xf9, + 0x74, 0xe5, 0xf7, 0x4d, 0x44, 0x3f, 0x27, 0x6a, 0x0d, 0x67, 0xcb, 0xe5, + 0x05, 0x11, 0x1d, 0x7b, 0x67, 0x5d, 0xb3, 0xe8, 0x27, 0x63, 0xd4, 0x19, + 0x0e, 0xe9, 0xf6, 0x53, 0x9b, 0x04, 0xb8, 0x5e, 0x34, 0x76, 0xdc, 0xd3, + 0x43, 0x44, 0xee, 0x73, 0x21, 0xf4, 0xc7, 0xf4, 0x4f, 0x03, 0x8e, 0xbd, + 0xe6, 0xc6, 0xe0, 0x63, 0xa9, 0x76, 0x5e, 0x64, 0x84, 0x61, 0xc7, 0xb6, + 0x7f, 0x33, 0x06, 0x0b, 0x00, 0x6e, 0x09, 0x38, 0xff, 0x33, 0xf7, 0x9c, + 0x98, 0xb6, 0x62, 0x86, 0x36, 0x80, 0xfd, 0xd1, 0x48, 0x96, 0xbc, 0xce, + 0x3f, 0xdf, 0xca, 0x7a, 0x7e, 0xf0, 0xb3, 0x90, 0x6e, 0xb9, 0xad, 0x57, + 0x02, 0xf0, 0x7d, 0x38, 0x42, 0xb6, 0x0a, 0x5d, 0x94, 0xee, 0xeb, 0x0a, + 0xd4, 0x64, 0x4a, 0x9e, 0x1c, 0x4f, 0x64, 0x3d, 0x8f, 0x01, 0xba, 0xf5, + 0x0d, 0xa8, 0x53, 0x73, 0xaa, 0x6f, 0x82, 0x9b, 0xca, 0x93, 0xab, 0xc6, + 0x95, 0xac, 0x4f, 0xa1, 0x0e, 0x6f, 0x30, 0xc3, 0x51, 0x6b, 0xd5, 0x2d, + 0xbf, 0x33, 0x0c, 0xb4, 0xff, 0xfe, 0x6a, 0xc9, 0x78, 0x79, 0xd3, 0x34, + 0x91, 0xf8, 0xbb, 0x01, 0xf4, 0x47, 0x39, 0x5d, 0x39, 0x5d, 0x9b, 0x9c, + 0x9a, 0xc1, 0x2a, 0x30, 0xe0, 0xd3, 0x0e, 0xbf, 0xe2, 0x86, 0xd9, 0x54, + 0xda, 0xd8, 0xca, 0x49, 0xb7, 0xfe, 0x06, 0xb8, 0xaf, 0xc4, 0xe0, 0x2f, + 0x10, 0x95, 0xd2, 0x0c, 0xb7, 0x88, 0x5e, 0xc5, 0x1a, 0xbc, 0xd2, 0x7c, + 0x46, 0x30, 0x4f, 0x1e, 0x2f, 0xe6, 0x74, 0xb4, 0xb9, 0x60, 0xf9, 0xc1, + 0xd1, 0x08, 0xea, 0xff, 0xa8, 0x40, 0x18, 0x2f, 0xdf, 0x70, 0x79, 0x4e, + 0x28, 0x5c, 0x0f, 0x35, 0x5f, 0xaf, 0x14, 0x43, 0xcd, 0x6d, 0x80, 0xa2, + 0x80, 0x5c, 0x39, 0x3e, 0xfb, 0x5e, 0x78, 0x09, 0x63, 0xcd, 0xbf, 0x58, + 0xce, 0x2e, 0x6f, 0x77, 0x83, 0xe7, 0xf2, 0xeb, 0x9d, 0xfe, 0xe6, 0x4f, + 0xba, 0xa7, 0x6f, 0xb8, 0xc2, 0x6d, 0xa2, 0x9a, 0x93, 0x7e, 0x8c, 0x22, + 0xbf, 0xdd, 0x6a, 0xf9, 0x18, 0xc6, 0x5e, 0x73, 0xe7, 0xcd, 0x50, 0x8b, + 0x6a, 0x11, 0x43, 0x21, 0x66, 0xb2, 0xac, 0xb0, 0x3f, 0x14, 0x1f, 0xb6, + 0xee, 0x09, 0xa0, 0xff, 0x7a, 0x08, 0xdb, 0xce, 0x45, 0xc0, 0xde, 0x1b, + 0x40, 0x47, 0x14, 0x88, 0xfd, 0x63, 0x2a, 0xeb, 0x1d, 0xb7, 0x04, 0xb8, + 0x7e, 0xec, 0xd4, 0xd7, 0x4b, 0xb0, 0x7e, 0x6b, 0x4c, 0x9b, 0x27, 0x4f, + 0xcd, 0x36, 0x20, 0x24, 0xf4, 0xcb, 0x4d, 0xa3, 0x1c, 0x8d, 0xa0, 0xfe, + 0x4c, 0x04, 0xdc, 0xbd, 0x89, 0xf2, 0x0c, 0xb7, 0x58, 0x3d, 0xf8, 0x7c, + 0xbd, 0xa0, 0x2a, 0x10, 0xf9, 0xca, 0xeb, 0x59, 0xae, 0xdc, 0xf8, 0x2a, + 0xc6, 0xc6, 0x5e, 0x73, 0xc3, 0xc2, 0xa9, 0x79, 0xef, 0xab, 0x49, 0x11, + 0xf5, 0x67, 0xfc, 0x38, 0xf2, 0xee, 0x4d, 0x0a, 0x5d, 0xbb, 0x49, 0xb1, + 0x1b, 0x93, 0x34, 0xf8, 0xc9, 0x30, 0x1d, 0x79, 0xf7, 0x26, 0x6d, 0xe9, + 0xf3, 0x6b, 0x33, 0x48, 0x66, 0xbe, 0x36, 0x09, 0x50, 0x7e, 0xb5, 0x39, + 0x8b, 0x61, 0x35, 0xa6, 0x75, 0x8a, 0x33, 0x1f, 0x21, 0xe6, 0x8e, 0x28, + 0x41, 0x44, 0x64, 0x6b, 0xbe, 0xc5, 0x06, 0x14, 0x60, 0xf3, 0x85, 0x04, + 0xd8, 0x39, 0x05, 0xec, 0x98, 0x1f, 0xec, 0x77, 0x11, 0xb0, 0x3f, 0x14, + 0x0f, 0x92, 0xeb, 0xa6, 0x15, 0x52, 0xaa, 0x9c, 0xaf, 0xcd, 0xd8, 0x7f, + 0xce, 0x8f, 0x9a, 0x33, 0x51, 0xb0, 0x3f, 0x84, 0x30, 0xf5, 0xbf, 0xb3, + 0xaf, 0x0c, 0x3c, 0x6f, 0x02, 0x9e, 0x94, 0xf2, 0xf4, 0x13, 0x4d, 0xdc, + 0x23, 0xcb, 0xb4, 0xa2, 0x49, 0xbf, 0xdc, 0xdc, 0xe8, 0x47, 0xfd, 0x05, + 0x05, 0xb1, 0x1b, 0x65, 0x30, 0x6e, 0x91, 0x7a, 0x14, 0x62, 0xc4, 0xdc, + 0x01, 0x55, 0x36, 0xaa, 0x05, 0xea, 0x51, 0x80, 0xc1, 0x7b, 0x97, 0x30, + 0xd6, 0xb1, 0x77, 0x23, 0x3b, 0xec, 0x90, 0x74, 0xf3, 0x8d, 0xaa, 0x51, + 0x1c, 0x94, 0x93, 0x38, 0x78, 0x61, 0x10, 0x9d, 0x51, 0xa4, 0x16, 0xcb, + 0x39, 0xef, 0xf1, 0x82, 0x88, 0xb1, 0x9d, 0x56, 0xb8, 0x5e, 0x7c, 0x8e, + 0x15, 0xfa, 0x52, 0x64, 0x91, 0xc8, 0x4f, 0x41, 0x8f, 0x11, 0x5c, 0x4f, + 0x20, 0xeb, 0x23, 0xc5, 0xf6, 0xe0, 0x2d, 0xb0, 0x13, 0xb7, 0xb0, 0xf1, + 0x23, 0x3f, 0x06, 0xbf, 0x50, 0x80, 0x68, 0x04, 0x98, 0x19, 0x21, 0x5d, + 0xcf, 0x64, 0x1c, 0x17, 0x32, 0x9a, 0x1e, 0xc4, 0xf3, 0xcb, 0x1d, 0xe5, + 0xb0, 0xf4, 0xa4, 0x1f, 0x91, 0xf4, 0x18, 0xf8, 0x3e, 0xe0, 0x32, 0x25, + 0xf3, 0xde, 0x6b, 0x5c, 0xcd, 0x3f, 0xb2, 0x4c, 0xab, 0x94, 0x60, 0xda, + 0x4c, 0xdc, 0x71, 0x21, 0x02, 0xdf, 0xc5, 0x61, 0x63, 0x86, 0x5b, 0xa4, + 0x1e, 0x28, 0xc0, 0x88, 0xc8, 0x1d, 0x50, 0x65, 0xa2, 0xb6, 0x18, 0xca, + 0x93, 0x5b, 0x3c, 0x9f, 0x7d, 0xfd, 0x52, 0x36, 0xd4, 0x20, 0x82, 0x2f, + 0xf3, 0xac, 0x89, 0x45, 0xb0, 0x62, 0x6c, 0xa7, 0x15, 0xd1, 0x12, 0x8b, + 0xc7, 0x45, 0x8d, 0xab, 0xf5, 0x99, 0xf6, 0xf4, 0x86, 0xd9, 0x9d, 0x2e, + 0xf6, 0xde, 0x40, 0x6a, 0xe7, 0xea, 0x41, 0xfe, 0xc8, 0x48, 0x85, 0x57, + 0xaa, 0x50, 0x76, 0x2a, 0xc4, 0x48, 0x9c, 0x88, 0xe7, 0x3e, 0xf0, 0x6b, + 0xfe, 0x6e, 0xdb, 0xb3, 0xd6, 0xbc, 0xf7, 0x44, 0xbe, 0xea, 0x91, 0x65, + 0xda, 0xdc, 0x7a, 0x59, 0x24, 0x2b, 0x2e, 0x6f, 0xb7, 0x82, 0xde, 0x74, + 0xe3, 0xf4, 0x5b, 0xcf, 0xb1, 0xc9, 0xdf, 0xb8, 0x71, 0xd8, 0xc1, 0x81, + 0x17, 0x52, 0xcf, 0x7b, 0x14, 0xa0, 0xfb, 0xe3, 0xa1, 0x92, 0x8b, 0x95, + 0x62, 0x4c, 0xfb, 0x4d, 0xfb, 0xb4, 0x8a, 0x01, 0xb9, 0xc9, 0xa7, 0x1f, + 0x67, 0x63, 0x3b, 0xad, 0x5a, 0x3d, 0x4b, 0xad, 0x9d, 0x6a, 0x6b, 0x25, + 0x44, 0x76, 0x8a, 0x08, 0x1b, 0xb8, 0x5d, 0x73, 0x51, 0x78, 0x19, 0x63, + 0x7a, 0x96, 0x9f, 0x5e, 0x7c, 0xb1, 0xf7, 0x06, 0x80, 0xbf, 0x9b, 0x8b, + 0x8e, 0x10, 0xf7, 0xec, 0x59, 0x62, 0xe3, 0xa9, 0x04, 0x23, 0x6d, 0x3b, + 0xe7, 0x87, 0xb5, 0x37, 0x86, 0x8d, 0xe9, 0x2f, 0x1f, 0x66, 0xfe, 0x9f, + 0x4f, 0x2a, 0x25, 0xc3, 0x38, 0xdf, 0x26, 0xd3, 0xe6, 0xd6, 0x6b, 0xe8, + 0x5f, 0x45, 0x54, 0xad, 0x62, 0xda, 0x54, 0xd7, 0xbb, 0x84, 0x31, 0xfb, + 0xfa, 0x35, 0x6c, 0x6c, 0xa7, 0x15, 0x8d, 0x36, 0x3b, 0xa0, 0x2a, 0xe8, + 0x8f, 0x72, 0xb0, 0xbe, 0x1f, 0x28, 0x7a, 0x41, 0x89, 0x1c, 0x8f, 0x16, + 0xae, 0xc7, 0x23, 0xe0, 0xd3, 0xea, 0x0f, 0x70, 0x94, 0x77, 0x4e, 0xfb, + 0x7b, 0xc6, 0xe4, 0x2e, 0x12, 0x01, 0x34, 0x4a, 0xe6, 0x3c, 0xcb, 0x3f, + 0x12, 0x4e, 0x80, 0x9d, 0x18, 0x00, 0xbe, 0x2e, 0xbc, 0x1a, 0x06, 0x94, + 0x19, 0xdf, 0xb4, 0x82, 0x64, 0xc0, 0xf7, 0x93, 0xef, 0x86, 0x80, 0x64, + 0xf6, 0xdd, 0x61, 0x6d, 0x36, 0x67, 0x69, 0xc3, 0xfa, 0x16, 0x99, 0x36, + 0xd3, 0xa7, 0x75, 0xd4, 0xaa, 0x05, 0x99, 0x23, 0xbc, 0x84, 0x31, 0xd7, + 0x8b, 0x35, 0xac, 0x6b, 0x53, 0xea, 0xd4, 0x9b, 0x9c, 0xe4, 0xb1, 0xf4, + 0x1d, 0x3f, 0x92, 0x9f, 0xa7, 0x4e, 0xa3, 0xa5, 0xdf, 0xf3, 0x10, 0x51, + 0xf4, 0x73, 0xa2, 0xce, 0x68, 0x76, 0xbd, 0x8c, 0x31, 0xe2, 0x37, 0xeb, + 0xd3, 0x66, 0xa2, 0xeb, 0x7e, 0x2a, 0x2a, 0x50, 0x8a, 0x61, 0xd3, 0x38, + 0x0a, 0x73, 0xc9, 0x81, 0xab, 0x31, 0xad, 0x02, 0xa0, 0xeb, 0xa7, 0xf9, + 0x4c, 0x7a, 0xf0, 0xd3, 0x00, 0xf0, 0xa0, 0x38, 0xc3, 0x02, 0x22, 0xf0, + 0xb5, 0xfe, 0x17, 0x9d, 0x25, 0x53, 0x19, 0xbe, 0x5f, 0x26, 0x3a, 0x6b, + 0x0d, 0x18, 0x56, 0x91, 0xfc, 0xd2, 0xfd, 0xc9, 0x39, 0x6d, 0xe5, 0x96, + 0xeb, 0xd3, 0x96, 0x92, 0xc7, 0xbd, 0xf0, 0x38, 0x1b, 0xda, 0xe9, 0x06, + 0x3f, 0x73, 0xcf, 0x44, 0xeb, 0x95, 0x00, 0xd8, 0xc9, 0x08, 0x36, 0x1e, + 0x3a, 0x4f, 0xbb, 0x0e, 0x0d, 0x13, 0x7b, 0xdb, 0x8f, 0xd6, 0x2b, 0x91, + 0xa2, 0x33, 0xc6, 0xc3, 0xf2, 0x69, 0x55, 0xae, 0x40, 0xff, 0x17, 0x61, + 0x5a, 0xdb, 0xcc, 0x65, 0x85, 0x35, 0x27, 0xf5, 0xa3, 0x02, 0xc5, 0x50, + 0x8e, 0xf3, 0xa8, 0x39, 0xe9, 0x87, 0x70, 0xbb, 0xb8, 0xe1, 0x2e, 0x12, + 0x01, 0x0c, 0x3e, 0xc6, 0x58, 0x6d, 0x6d, 0xfe, 0x2a, 0xdd, 0x08, 0x1e, + 0x5e, 0xf9, 0xf0, 0x98, 0x36, 0x17, 0x1d, 0xb5, 0x26, 0x24, 0x0d, 0x6c, + 0x89, 0x16, 0x93, 0x13, 0x89, 0x57, 0x3d, 0x54, 0xa6, 0xcd, 0xae, 0x97, + 0x31, 0xb9, 0xd1, 0xa7, 0x53, 0xe1, 0x22, 0xbd, 0x38, 0xa7, 0x91, 0x19, + 0x23, 0x91, 0xcc, 0xd7, 0x0b, 0xbc, 0x88, 0x80, 0x1c, 0x9f, 0x13, 0xd3, + 0xfa, 0xd3, 0x3b, 0x5d, 0x39, 0x72, 0xf5, 0xc2, 0x50, 0x22, 0x00, 0xe9, + 0x1e, 0x51, 0x5d, 0xdf, 0x2d, 0xb4, 0x5e, 0x09, 0xe8, 0xe6, 0xb3, 0x08, + 0x26, 0x9c, 0x6d, 0x70, 0xe1, 0xb0, 0x83, 0x43, 0xd7, 0x26, 0x27, 0x6a, + 0xf9, 0x58, 0xde, 0x7b, 0x2a, 0x27, 0x62, 0x4b, 0x9f, 0x1f, 0xa1, 0x8b, + 0x23, 0x59, 0x33, 0x4e, 0x1e, 0xd3, 0x8a, 0x00, 0x8e, 0x58, 0xaa, 0x50, + 0x09, 0xf3, 0x85, 0xe3, 0x4a, 0x41, 0xbb, 0x2c, 0x9a, 0x72, 0x76, 0x96, + 0x8c, 0xa0, 0xaf, 0xc1, 0x18, 0x73, 0x14, 0x93, 0xb3, 0xa5, 0xcf, 0x8f, + 0xd0, 0xb5, 0x09, 0xe2, 0xee, 0x4d, 0x50, 0x25, 0x97, 0xdc, 0x95, 0xe7, + 0xd3, 0x1a, 0x97, 0xdb, 0xbb, 0x84, 0xb1, 0xc4, 0xeb, 0xfa, 0x71, 0x4e, + 0x3d, 0xcc, 0xcc, 0x1f, 0x5f, 0xc6, 0x98, 0x5e, 0xbc, 0x3d, 0x38, 0x9a, + 0xa8, 0xf8, 0xfa, 0x54, 0xdf, 0xc5, 0x61, 0x1a, 0x55, 0xf3, 0x99, 0xb6, + 0x45, 0xcc, 0x7f, 0xdf, 0x43, 0x44, 0x83, 0x9f, 0x0c, 0x53, 0xcd, 0x99, + 0x08, 0x46, 0x47, 0xa3, 0x79, 0x7a, 0x58, 0x04, 0x2b, 0x86, 0x1a, 0x44, + 0x74, 0xec, 0x5d, 0xce, 0x92, 0x6b, 0x97, 0x32, 0xfb, 0xfa, 0x35, 0x8c, + 0x7b, 0xe1, 0x71, 0xe6, 0xfd, 0xb7, 0x2d, 0xac, 0xcb, 0x66, 0xd2, 0x8d, + 0x32, 0xf4, 0xc8, 0x11, 0x98, 0x4e, 0x45, 0x10, 0xfd, 0x7c, 0x24, 0x4f, + 0x7f, 0xed, 0xde, 0x03, 0x71, 0xed, 0x72, 0xe6, 0xf8, 0xf0, 0x2a, 0x05, + 0x55, 0x51, 0x9b, 0x5a, 0x8c, 0xe0, 0xe0, 0xd7, 0xe5, 0x9f, 0xa7, 0x8d, + 0x29, 0x30, 0x2c, 0x3f, 0x8d, 0x0e, 0x8b, 0xb5, 0xe0, 0x08, 0x2f, 0xc8, + 0xb4, 0x05, 0xe4, 0xf5, 0x04, 0x03, 0xe8, 0xe1, 0x45, 0x40, 0x0d, 0xa0, + 0xd4, 0xd9, 0x8b, 0x16, 0x8b, 0x15, 0xc2, 0x6a, 0x11, 0xe2, 0xcc, 0xcd, + 0x2a, 0xb9, 0xcf, 0x45, 0xde, 0x95, 0x5d, 0xbe, 0x29, 0xb5, 0xf3, 0x55, + 0x89, 0x0f, 0xed, 0x65, 0x8c, 0x75, 0x00, 0xf0, 0x5d, 0x9c, 0xa0, 0x1e, + 0x05, 0x45, 0xdb, 0x23, 0x37, 0xff, 0xad, 0x57, 0x37, 0xa3, 0xe6, 0xa4, + 0x3f, 0xef, 0x66, 0xf6, 0x83, 0xb2, 0x08, 0xc7, 0x87, 0x23, 0x74, 0xc4, + 0x21, 0xc2, 0x2e, 0x14, 0xff, 0xed, 0x0b, 0xe7, 0x34, 0x51, 0x28, 0x0e, + 0x34, 0x5f, 0x18, 0xc0, 0xa8, 0x92, 0xdf, 0x8e, 0x16, 0x4e, 0x85, 0x77, + 0xab, 0x13, 0xde, 0x8c, 0x7c, 0xae, 0xfb, 0xa9, 0x2d, 0xd8, 0xac, 0x1d, + 0xad, 0x8c, 0xf2, 0xd3, 0x67, 0x2e, 0x92, 0x05, 0xea, 0x2d, 0xbd, 0xb0, + 0x9c, 0x25, 0xee, 0x13, 0x59, 0xff, 0xc3, 0x04, 0x39, 0x9a, 0x7d, 0xae, + 0x5b, 0x55, 0x53, 0xf7, 0x5d, 0x38, 0x3e, 0x1c, 0xa1, 0xc0, 0xcf, 0x45, + 0xed, 0x86, 0x9f, 0xc5, 0x99, 0x02, 0x06, 0xb6, 0x3b, 0xb1, 0xf4, 0x1d, + 0x7f, 0xde, 0x48, 0x29, 0x8a, 0x69, 0xc6, 0x2c, 0x23, 0x1d, 0x28, 0xf3, + 0x9b, 0x34, 0x9e, 0x13, 0xe1, 0xdb, 0x20, 0x20, 0x6a, 0xd0, 0x00, 0x2a, + 0x99, 0x31, 0x0a, 0x61, 0x8f, 0x1c, 0x41, 0x8f, 0x9c, 0x40, 0xfa, 0x7e, + 0x08, 0x3d, 0xa6, 0xb5, 0x67, 0x94, 0x1f, 0x42, 0x62, 0xf6, 0x39, 0x5f, + 0x7c, 0xe7, 0xae, 0x10, 0x36, 0xbf, 0xf4, 0x38, 0x73, 0xde, 0x18, 0xa1, + 0x1d, 0x17, 0xf2, 0xcb, 0x2b, 0xe4, 0x2b, 0xf7, 0x2e, 0x61, 0x6c, 0x6c, + 0x9a, 0xa8, 0xee, 0xd4, 0x00, 0x46, 0x73, 0x19, 0x57, 0x05, 0xea, 0xcf, + 0xa4, 0xfa, 0xc9, 0xf1, 0xe1, 0x55, 0x32, 0x99, 0x25, 0x58, 0xb8, 0x04, + 0xc0, 0x8b, 0x88, 0xa9, 0x0a, 0x02, 0x4a, 0x15, 0xd4, 0xa4, 0x02, 0xf9, + 0x9d, 0x48, 0xbe, 0x46, 0x33, 0x72, 0x1c, 0xbc, 0x8a, 0xc0, 0xcf, 0x67, + 0xef, 0x0d, 0xb6, 0x4d, 0x13, 0x0d, 0xfe, 0x25, 0x81, 0x5d, 0x27, 0xfd, + 0xba, 0xef, 0xf3, 0x49, 0x05, 0x03, 0x3b, 0xdd, 0x88, 0x3e, 0x5d, 0x9a, + 0x68, 0x02, 0x8f, 0x31, 0xb6, 0x0f, 0x40, 0xe8, 0xda, 0x04, 0xf5, 0x04, + 0x03, 0x79, 0xf5, 0x0d, 0xaa, 0x80, 0xe9, 0x58, 0x04, 0x03, 0xf7, 0x88, + 0xa2, 0x4f, 0x33, 0x96, 0x77, 0x97, 0x97, 0x76, 0x35, 0x52, 0x19, 0x4c, + 0xd8, 0xf6, 0x0c, 0x87, 0x0e, 0x47, 0xf1, 0xfb, 0xbc, 0x32, 0x13, 0x3b, + 0x31, 0x00, 0x2c, 0x36, 0x1b, 0x96, 0x7f, 0xd6, 0x61, 0x85, 0x79, 0xad, + 0xf1, 0xd3, 0x4a, 0x83, 0x9f, 0x0c, 0xa7, 0x56, 0xdb, 0x65, 0x30, 0x79, + 0x31, 0x3c, 0x6c, 0xe1, 0x60, 0x5f, 0xbf, 0x86, 0x29, 0xc8, 0xbf, 0xeb, + 0x2c, 0xf3, 0xf4, 0x91, 0x82, 0xec, 0x53, 0x5e, 0x6d, 0x92, 0x08, 0xd7, + 0x8b, 0x95, 0x1f, 0x4b, 0x94, 0xee, 0x11, 0x6d, 0xfe, 0xb3, 0x92, 0xc7, + 0x40, 0x16, 0x4e, 0x45, 0xc7, 0xde, 0xc2, 0x77, 0x9b, 0xc5, 0x6e, 0x10, + 0xf5, 0x7f, 0xa9, 0xa0, 0x3f, 0x5a, 0xd9, 0x17, 0x29, 0x99, 0xd8, 0x66, + 0xb3, 0xa3, 0x71, 0xb5, 0x39, 0x2f, 0xcc, 0x58, 0xec, 0x74, 0x97, 0x83, + 0x57, 0x31, 0xb0, 0xdd, 0x59, 0xd1, 0x35, 0xad, 0xb6, 0x71, 0xa2, 0xfa, + 0x33, 0xf9, 0x33, 0x46, 0x1a, 0x87, 0x76, 0xba, 0xf5, 0xef, 0xa7, 0xcd, + 0xb2, 0x78, 0x23, 0x45, 0x71, 0x22, 0x86, 0x7e, 0x26, 0xc2, 0x2e, 0x96, + 0x3e, 0x59, 0x53, 0xee, 0xdd, 0x5e, 0x2d, 0x16, 0xa0, 0xf9, 0xa5, 0xf2, + 0x6f, 0x33, 0xb4, 0x4d, 0x13, 0xa9, 0x33, 0xe7, 0x5e, 0xc5, 0xf4, 0xb9, + 0xd6, 0x0a, 0x50, 0xcf, 0x25, 0x71, 0xdd, 0x27, 0x52, 0xd4, 0x04, 0xec, + 0x4f, 0x99, 0x74, 0x2f, 0xa5, 0xf3, 0x10, 0x51, 0xec, 0x1f, 0x53, 0xf3, + 0xf6, 0x1b, 0x12, 0xe9, 0xf3, 0xb9, 0x69, 0xbd, 0x8c, 0x2c, 0x46, 0xd3, + 0xf9, 0x7c, 0x5f, 0x28, 0x50, 0x20, 0x20, 0x30, 0x73, 0x3e, 0x55, 0x51, + 0x6f, 0x61, 0x54, 0x35, 0xe7, 0xe5, 0xb0, 0x08, 0x2a, 0x78, 0x4e, 0x84, + 0xa9, 0x2a, 0x06, 0xcb, 0x6a, 0x0b, 0x2c, 0x9c, 0x8a, 0xcd, 0x2b, 0xcd, + 0x05, 0x2f, 0xdd, 0x8b, 0xdd, 0x18, 0x21, 0xaf, 0x0c, 0xc8, 0xf1, 0x08, + 0xd4, 0x64, 0x2a, 0xbc, 0x67, 0x32, 0x4b, 0x68, 0xa9, 0x05, 0xaa, 0xe6, + 0x78, 0x8e, 0xd7, 0x43, 0x44, 0xdd, 0xd7, 0x95, 0x99, 0x0d, 0x26, 0x05, + 0xc1, 0x51, 0x1e, 0xb5, 0x7c, 0x0c, 0x22, 0x5f, 0x07, 0x5f, 0x83, 0xa0, + 0x7f, 0x3f, 0xad, 0x73, 0x9a, 0xc8, 0xfa, 0xc7, 0xfc, 0x11, 0x5e, 0x14, + 0x57, 0x48, 0x18, 0xb2, 0xd5, 0xc1, 0x2e, 0x16, 0x36, 0xd8, 0x8e, 0x30, + 0xb0, 0xff, 0xba, 0xf1, 0xfb, 0x69, 0xcb, 0xb9, 0x2d, 0x71, 0x01, 0xbf, + 0x3b, 0x58, 0xf0, 0x26, 0x70, 0xdb, 0x34, 0x51, 0xfd, 0x19, 0xa5, 0xec, + 0xdf, 0x5a, 0x70, 0x3d, 0x03, 0x9c, 0xde, 0x60, 0xcd, 0x3a, 0xce, 0x18, + 0x4b, 0x00, 0x9b, 0xaf, 0x84, 0x20, 0xdf, 0xe5, 0x0c, 0xcb, 0x69, 0x94, + 0x92, 0xf0, 0xff, 0xcc, 0xfe, 0xc8, 0x5f, 0x74, 0xbc, 0x80, 0xdf, 0x3c, + 0x96, 0xfc, 0x1d, 0xb1, 0xd8, 0xb5, 0x09, 0x3a, 0x18, 0x0c, 0xcc, 0x8b, + 0x6f, 0x68, 0x14, 0x5b, 0xc4, 0xca, 0x5c, 0x82, 0x05, 0xfc, 0x6e, 0xa0, + 0xa1, 0xdf, 0x11, 0x13, 0x6e, 0x13, 0xed, 0xb8, 0x30, 0xb3, 0xc3, 0xf1, + 0x10, 0x55, 0xe2, 0x39, 0x11, 0x5d, 0x9b, 0x8c, 0xdd, 0x43, 0xbb, 0x80, + 0xdf, 0x5d, 0x34, 0xf4, 0x3b, 0x62, 0xf1, 0x55, 0xa9, 0xc0, 0xb7, 0xa3, + 0x56, 0x2d, 0x7b, 0xc7, 0xcc, 0x28, 0x3a, 0x2c, 0x56, 0x24, 0x5e, 0xb5, + 0x2e, 0x18, 0xec, 0x02, 0x96, 0xc4, 0xb2, 0x7f, 0x1b, 0x97, 0xbb, 0x37, + 0x41, 0x07, 0x82, 0x6a, 0x2a, 0x80, 0x3e, 0x0f, 0x2a, 0x38, 0x6a, 0x55, + 0xf8, 0x1a, 0x9c, 0x86, 0x37, 0x0e, 0x16, 0x70, 0x01, 0x2b, 0xfa, 0x6d, + 0x5c, 0x05, 0xa9, 0x8f, 0xd1, 0x0e, 0x7e, 0x96, 0x80, 0x3f, 0x16, 0x4b, + 0x6d, 0xdd, 0x95, 0xe1, 0xb3, 0xd6, 0x22, 0x86, 0x76, 0x9b, 0x1d, 0xfb, + 0x56, 0x9b, 0xb2, 0x6e, 0x68, 0x59, 0xc0, 0x05, 0x34, 0x82, 0x73, 0xfa, + 0x15, 0xf2, 0x34, 0xa6, 0xe3, 0x81, 0x80, 0x80, 0xfe, 0x2f, 0x65, 0x24, + 0xa6, 0xb2, 0xe3, 0x80, 0xe9, 0x1d, 0x18, 0x67, 0x6d, 0x1d, 0x44, 0x7e, + 0xfe, 0xe2, 0x97, 0x0b, 0xf8, 0xdd, 0xc4, 0xff, 0x03, 0x84, 0x87, 0x80, + 0xe5, 0xeb, 0x02, 0x31, 0x17, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, + 0x44, 0xae, 0x42, 0x60, 0x82 }; @@ -996,12 +975,13 @@ ConfigurationPanel::ConfigurationPanel( QWidget* parent, const char* name, bool stunButtonGroupLayout = new QVBoxLayout( stunButtonGroup->layout() ); stunButtonGroupLayout->setAlignment( Qt::AlignTop ); - useStunYes = new QRadioButton( stunButtonGroup, "useStunYes" ); - stunButtonGroupLayout->addWidget( useStunYes ); - useStunNo = new QRadioButton( stunButtonGroup, "useStunNo" ); useStunNo->setChecked( TRUE ); stunButtonGroupLayout->addWidget( useStunNo ); + + useStunYes = new QRadioButton( stunButtonGroup, "useStunYes" ); + useStunYes->setChecked( FALSE ); + stunButtonGroupLayout->addWidget( useStunYes ); Tab_Signalisations->insertTab( STUNPage, QString("") ); DTMFPage = new QWidget( Tab_Signalisations, "DTMFPage" ); @@ -1203,13 +1183,13 @@ ConfigurationPanel::ConfigurationPanel( QWidget* parent, const char* name, bool DriversPage_5 = new QWidget( Tab_About, "DriversPage_5" ); - textLabel2_2 = new QLabel( DriversPage_5, "textLabel2_2" ); - textLabel2_2->setGeometry( QRect( 20, 170, 371, 121 ) ); - pixmapLabel1 = new QLabel( DriversPage_5, "pixmapLabel1" ); pixmapLabel1->setGeometry( QRect( 50, 40, 312, 91 ) ); pixmapLabel1->setPixmap( image0 ); pixmapLabel1->setScaledContents( TRUE ); + + textLabel2_2 = new QLabel( DriversPage_5, "textLabel2_2" ); + textLabel2_2->setGeometry( QRect( 20, 170, 371, 121 ) ); Tab_About->insertTab( DriversPage_5, QString("") ); CodecsPage_4 = new QWidget( Tab_About, "CodecsPage_4" ); @@ -1252,8 +1232,8 @@ ConfigurationPanel::ConfigurationPanel( QWidget* parent, const char* name, bool setTabOrder( zoneToneChoice, confirmationToQuit ); setTabOrder( confirmationToQuit, checkedTray ); setTabOrder( checkedTray, voicemailNumber ); - setTabOrder( voicemailNumber, useStunNo ); - setTabOrder( useStunNo, STUNserver ); + setTabOrder( voicemailNumber, useStunYes ); + setTabOrder( useStunYes, STUNserver ); setTabOrder( STUNserver, playTones ); setTabOrder( playTones, pulseLength ); setTabOrder( pulseLength, sendDTMFas ); @@ -1309,8 +1289,8 @@ void ConfigurationPanel::languageChange() groupBox3->setTitle( tr( "Settings " ) ); textLabel1_5->setText( tr( "STUN server (address:port)" ) ); stunButtonGroup->setTitle( tr( "Use STUN" ) ); - useStunYes->setText( tr( "Yes" ) ); useStunNo->setText( tr( "No" ) ); + useStunYes->setText( tr( "Yes" ) ); Tab_Signalisations->changeTab( STUNPage, tr( "STUN" ) ); SettingsDTMF->setTitle( tr( "Settings" ) ); playTones->setText( tr( "Play tones locally" ) ); @@ -1374,9 +1354,9 @@ void ConfigurationPanel::languageChange() textLabel1_6->setText( tr( "Voicemail:" ) ); Tab_Preferences->changeTab( TabPage, tr( "Options" ) ); textLabel2_2->setText( tr( "<p align=\"center\">\n" -"Copyright (C) 2004 Savoir-faire Linux inc.<br><br>\n" +"Copyright (C) 2004-2005 Savoir-faire Linux inc.<br><br>\n" "Laurielle LEA <laurielle.lea@savoirfairelinux.com><br><br>\n" -"SFLPhone-0.3 is released under the General Public License.<br>\n" +"SFLPhone-0.4 is released under the General Public License.<br>\n" "For more information, see http://www.sflphone.org<br>\n" "</p>" ) ); Tab_About->changeTab( DriversPage_5, tr( "About SFLPhone" ) ); diff --git a/src/configurationpanelui.h b/src/gui/qt/configurationpanelui.h similarity index 97% rename from src/configurationpanelui.h rename to src/gui/qt/configurationpanelui.h index a6684acf38396e124cdf14f56baf497860683f6b..5b6774411ce70d377a0bbfd02319c67120e73449 100644 --- a/src/configurationpanelui.h +++ b/src/gui/qt/configurationpanelui.h @@ -1,7 +1,7 @@ /**************************************************************************** -** Form interface generated from reading ui file 'configurationpanel.ui' +** Form interface generated from reading ui file 'gui/qt/configurationpanel.ui' ** -** Created: Wed Apr 27 10:37:34 2005 +** Created: Fri May 20 14:27:25 2005 ** by: The User Interface Compiler ($Id$) ** ** WARNING! All changes made in this file will be lost! @@ -70,8 +70,8 @@ public: QLabel* textLabel1_5; QLineEdit* STUNserver; QButtonGroup* stunButtonGroup; - QRadioButton* useStunYes; QRadioButton* useStunNo; + QRadioButton* useStunYes; QWidget* DTMFPage; QGroupBox* SettingsDTMF; QCheckBox* playTones; @@ -117,8 +117,8 @@ public: QLineEdit* voicemailNumber; QTabWidget* Tab_About; QWidget* DriversPage_5; - QLabel* textLabel2_2; QLabel* pixmapLabel1; + QLabel* textLabel2_2; QWidget* CodecsPage_4; QLabel* textLabel1; QLabel* pixmapLabel2; diff --git a/src/jpushbutton.cpp b/src/gui/qt/jpushbutton.cpp similarity index 99% rename from src/jpushbutton.cpp rename to src/gui/qt/jpushbutton.cpp index fb2e3c9e1e7df0067e0f63b72a7daa22676edc06..99cdddb06cc4d755579553174c230cd0c47bcb22 100644 --- a/src/jpushbutton.cpp +++ b/src/gui/qt/jpushbutton.cpp @@ -27,7 +27,7 @@ #include <qevent.h> #include "jpushbutton.h" -#include "skin.h" +#include "../../skin.h" #include "qtGUImainwindow.h" // This is the default constructor, it must be called with pixname being diff --git a/src/jpushbutton.h b/src/gui/qt/jpushbutton.h similarity index 100% rename from src/jpushbutton.h rename to src/gui/qt/jpushbutton.h diff --git a/src/mydisplay.cpp b/src/gui/qt/mydisplay.cpp similarity index 77% rename from src/mydisplay.cpp rename to src/gui/qt/mydisplay.cpp index 455ecd4785ca7fdc54d29aa75321b6a36dac0887..017990103fbcce0fb33aa13e59d986ac0cd374b9 100644 --- a/src/mydisplay.cpp +++ b/src/gui/qt/mydisplay.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -26,8 +26,8 @@ #include <stdio.h> #include <math.h> +#include "../../skin.h" #include "mydisplay.h" -#include "skin.h" #include "qtGUImainwindow.h" #define TABULATION 1 @@ -88,23 +88,23 @@ MyDisplay::MyDisplay (void) : QWidget () { */ MyDisplay::MyDisplay (QWidget *parent, const char *name, QtGUIMainWindow* qtgui) : QWidget (parent, name) { - this->qtgui = qtgui; + _qtgui = qtgui; this->initGraphics(); this->initText(); // Graphics engine animation thread - this->animationThread = new MyDisplayThread (this); - this->animationThread->start(); + _animationThread = new MyDisplayThread (this); + _animationThread->start(); } /** * Destructor. */ MyDisplay::~MyDisplay (void) { - delete animationThread; - delete textBuffer; - delete qtgui; - delete status; - delete time; + delete _animationThread; + delete _textBuffer; + delete _qtgui; + delete _status; + delete _time; } /** @@ -115,7 +115,7 @@ void MyDisplay::paintEvent (QPaintEvent *e) { if (e); - QPixmap pm(centerImage.width(),centerImage.height()); + QPixmap pm(_centerImage.width(),_centerImage.height()); // Painter settings QFont font(FONT_FAMILY, FONT_SIZE); @@ -128,23 +128,23 @@ MyDisplay::paintEvent (QPaintEvent *e) { // Text image (static) bitBlt (&pm, 0, 0, - &(this->centerImage), 0, 0, - this->centerImage.width(), - this->centerImage.height(), + &(_centerImage), 0, 0, + _centerImage.width(), + _centerImage.height(), Qt::CopyROP); // Display text render - renderText (p, fm, *textBuffer); + renderText (p, fm, *_textBuffer); // Display status - renderStatus (p, fm, *status); + renderStatus (p, fm, *_status); // Display time renderTime (p, fm); // Overscreen image bitBlt (&pm, 0, 0, - &(this->overImage), 0, 0, - this->overImage.width(), - this->overImage.height(), + &(_overImage), 0, 0, + _overImage.width(), + _overImage.height(), Qt::CopyROP); p.end(); @@ -152,8 +152,8 @@ MyDisplay::paintEvent (QPaintEvent *e) { // "Swap" buffers bitBlt (this, 0, 0, &pm, 0, 0, - this->centerImage.width(), - this->centerImage.height(), + _centerImage.width(), + _centerImage.height(), Qt::CopyROP); } @@ -167,24 +167,20 @@ MyDisplay::initGraphics (void) { // Load display screen image QPixmap *pixcenter = new QPixmap(Skin::getPath(QString(SKINDIR), - qtgui->setPathSkin(), + _qtgui->setPathSkin(), QString(PIXMAP_SCREEN))); - this->centerImage = pixcenter->convertToImage(); + _centerImage = pixcenter->convertToImage(); delete pixcenter; // Load overscreen image QPixmap *pixover = new QPixmap(Skin::getPath(QString(SKINDIR), - qtgui->setPathSkin(), + _qtgui->setPathSkin(), QString(PIXMAP_OVERSCREEN))); - this->overImage = pixover->convertToImage(); + _overImage = pixover->convertToImage(); delete pixover; // Adjust our size to the picture's one - this->setGeometry (0, 0, centerImage.width(), centerImage.height()); - - // Graphics engine animation thread -// this->animationThread = new MyDisplayThread (this); -// this->animationThread->start(); + this->setGeometry (0, 0, _centerImage.width(), _centerImage.height()); } /** @@ -192,11 +188,11 @@ MyDisplay::initGraphics (void) { */ void MyDisplay::initText (void) { - this->textBuffer = new QString (); + _textBuffer = new QString (); // Init status to free-status - this->status = new QString(FREE_STATUS); + _status = new QString(FREE_STATUS); // No display call-timer - inFunction = false; + _inFunction = false; } /** @@ -210,7 +206,7 @@ MyDisplay::initText (void) { void MyDisplay::renderText (QPainter &painter, QFontMetrics &fm, QString &str) { // cpl = how many chars per line. - static unsigned int cpl = centerImage.width() / fm.width("a"); + static unsigned int cpl = _centerImage.width() / fm.width("a"); // If the texts length is greater than the screen, we have to // scroll the WHOLE text towards the left. @@ -219,7 +215,7 @@ MyDisplay::renderText (QPainter &painter, QFontMetrics &fm, QString &str) { QString backup_string("."); // If the string is larger than the screen... - if (fm.width(str) > (centerImage.width() - 5)) { + if (fm.width(str) > (_centerImage.width() - 5)) { extra_chars = str.length() - cpl; x_offset = fm.width(str[0]) * extra_chars; x_offset = fm.width(str[0]) * extra_chars; @@ -232,7 +228,7 @@ MyDisplay::renderText (QPainter &painter, QFontMetrics &fm, QString &str) { // Render text painter.drawText (TABULATION - x_offset, fm.height() * TEXT_LINE, str); - if (fm.width(str) > (centerImage.width() - 5)) { + if (fm.width(str) > (_centerImage.width() - 5)) { // Restore initial string. str.replace(extra_chars, backup_string.length(), backup_string); } @@ -263,8 +259,8 @@ MyDisplay::renderTime (QPainter &painter, QFontMetrics &fm) { QString minStr,secStr; // If conversation, display call-timer - if (inFunction) { - elapse = qtgui->callmanager->phLines[qtgui->currentLineNumber]->timer->elapsed() / 1000; + if (_inFunction) { + elapse = _qtgui->getElapse(); // To calculate minutes if (elapse % 60 == 0) { minute = elapse / 60; @@ -283,11 +279,11 @@ MyDisplay::renderTime (QPainter &painter, QFontMetrics &fm) { minStr = QString::number(minute); } - time = new QString(minStr + ":" + secStr); + _time = new QString(minStr + ":" + secStr); } else { // If no conversation, display the current time - time = new QString(QDateTime::currentDateTime().toString("hh:mm")); + _time = new QString(QDateTime::currentDateTime().toString("hh:mm")); /** For english time format: * time = new QString(QDateTime::currentDateTime().toString("h:m AP")); @@ -296,10 +292,10 @@ MyDisplay::renderTime (QPainter &painter, QFontMetrics &fm) { // Handle time separator blinking. static unsigned int i = 0; if (i++ % 12 > 5) { - time->replace (':', ' '); + _time->replace (':', ' '); } } - painter.drawText (TABULATION, fm.height() * TIMER_LINE, *time); + painter.drawText (TABULATION, fm.height() * TIMER_LINE, *_time); } /** @@ -310,11 +306,11 @@ MyDisplay::renderTime (QPainter &painter, QFontMetrics &fm) { void MyDisplay::setStatus (const QString &status) { // Free if previously allocated - if (this->status != NULL) { - delete this->status; + if (_status != NULL) { + delete _status; } - this->status = new QString (status); + _status = new QString (status); } /** @@ -324,7 +320,7 @@ MyDisplay::setStatus (const QString &status) { */ QString & MyDisplay::getStatus (void) { - return *status; + return *_status; } /** @@ -335,10 +331,10 @@ MyDisplay::getStatus (void) { void MyDisplay::setTimer (const QString &time) { // Free if previously allocated - if (this->time != NULL) { - delete this->time; + if (_time != NULL) { + delete _time; } - this->time = new QString (time); + _time = new QString (time); } /** @@ -348,7 +344,31 @@ MyDisplay::setTimer (const QString &time) { */ QString & MyDisplay::getTimer (void) { - return *time; + return *_time; +} + +/** + * To modify text buffer. + * + * @param text buffer + */ +void +MyDisplay::setTextBuffer (const QString& text) { + // Free if previously allocated + if (_textBuffer != NULL) { + delete _textBuffer; + } + _textBuffer = new QString (text); +} + +/** + * To access text buffer. + * + * @return text buffer + */ +QString & +MyDisplay::getTextBuffer (void) { + return *_textBuffer; } /////////////////////////////////////////////////////////////////////////////// @@ -356,42 +376,42 @@ MyDisplay::getTimer (void) { /////////////////////////////////////////////////////////////////////////////// void MyDisplay::appendText (const QString &strToAdd) { - this->textBuffer->append(strToAdd); + _textBuffer->append(strToAdd); } void MyDisplay::appendText (const char *text) { - this->textBuffer->append(QString(text)); + _textBuffer->append(QString(text)); } void MyDisplay::appendText (const QChar &key) { - this->textBuffer->append(QChar(key)); + _textBuffer->append(QChar(key)); } void MyDisplay::clear (void) { // Remove everything in the buffer. - this->textBuffer->remove(0, this->textBuffer->length()); + _textBuffer->remove(0, _textBuffer->length()); setStatus(QString(FREE_STATUS)); } void MyDisplay::clearBuffer (void) { - this->textBuffer->remove(0, this->textBuffer->length()); + _textBuffer->remove(0, _textBuffer->length()); } void MyDisplay::clear (const QString &newstatus) { // Remove everything in the buffer and set the new status. - this->textBuffer->remove(0, this->textBuffer->length()); + _textBuffer->remove(0, _textBuffer->length()); setStatus(newstatus); } void MyDisplay::backspace (void) { // Remove the last char of the string - this->textBuffer->remove(this->textBuffer->length() - 1, 1); + _textBuffer->remove(_textBuffer->length() - 1, 1); } // EOF diff --git a/src/mydisplay.h b/src/gui/qt/mydisplay.h similarity index 80% rename from src/mydisplay.h rename to src/gui/qt/mydisplay.h index 6d3cf44bd5d900acdf42c6f870b4c3a0b6c773fb..881975b4bb7d1360f6c31a893d77695a6e7f1de1 100644 --- a/src/mydisplay.h +++ b/src/gui/qt/mydisplay.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -27,10 +27,9 @@ #include <qthread.h> #include <qwidget.h> -#include "global.h" +#include "../../global.h" -#define FREE_STATUS QObject::tr("Welcome to SFLPhone") -#define ONHOLD_STATUS QObject::tr("On hold ...") +#define FREE_STATUS "Welcome to SFLPhone" // Screen animation thread class MyDisplayThread : public QThread { @@ -58,11 +57,12 @@ public: QString & getStatus (void); void setTimer (const QString &); QString & getTimer (void); + void setTextBuffer (const QString &); + QString & getTextBuffer (void); void initGraphics (void); - bool inFunction; - QString *textBuffer; - QString *time; + inline bool getInFunction (void) { return _inFunction; } + inline void setInFunction (bool b) { _inFunction = b; } public slots: void appendText (const QString &); @@ -77,11 +77,14 @@ protected: void paintEvent (QPaintEvent *); private: - QImage centerImage; // text zone - QImage overImage; - QString *status; - MyDisplayThread *animationThread; - QtGUIMainWindow *qtgui; + QImage _centerImage; // text zone + QImage _overImage; + QString* _status; + MyDisplayThread* _animationThread; + QtGUIMainWindow* _qtgui; + QString* _textBuffer; + QString* _time; + bool _inFunction; void initText (void); void renderText (QPainter &, QFontMetrics &, QString &); diff --git a/src/numerickeypad.cpp b/src/gui/qt/numerickeypad.cpp similarity index 96% rename from src/numerickeypad.cpp rename to src/gui/qt/numerickeypad.cpp index e43158559759e6ed234526deb5bd578b61f10eae..502e47880090b997dc9ebd0549639e4edfe27bae 100644 --- a/src/numerickeypad.cpp +++ b/src/gui/qt/numerickeypad.cpp @@ -17,12 +17,15 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <string> #include <qapplication.h> #include "jpushbutton.h" #include "numerickeypad.h" #include "qtGUImainwindow.h" -#include "skin.h" +#include "../../skin.h" + +using namespace std; NumericKeypad::NumericKeypad (QWidget *parent, const char *name, WFlags f) @@ -44,7 +47,7 @@ NumericKeypad::NumericKeypad (QWidget *parent, const char *name, WFlags f) // Calculate just one time the transparency mask bit to bit transparencyMask (); - QString skinfilename(Skin::getPath(QString(SKINDIR), + string skinfilename(Skin::getPath(QString(SKINDIR), gui->setPathSkin(), QString(FILE_INI))); pt = new Point(skinfilename); diff --git a/src/numerickeypad.h b/src/gui/qt/numerickeypad.h similarity index 100% rename from src/numerickeypad.h rename to src/gui/qt/numerickeypad.h diff --git a/src/numerickeypadtools.cpp b/src/gui/qt/numerickeypadtools.cpp similarity index 100% rename from src/numerickeypadtools.cpp rename to src/gui/qt/numerickeypadtools.cpp diff --git a/src/numerickeypadtools.h b/src/gui/qt/numerickeypadtools.h similarity index 100% rename from src/numerickeypadtools.h rename to src/gui/qt/numerickeypadtools.h diff --git a/src/phonebook.ui b/src/gui/qt/phonebook.ui similarity index 100% rename from src/phonebook.ui rename to src/gui/qt/phonebook.ui diff --git a/src/phonebookui.cpp b/src/gui/qt/phonebookui.cpp similarity index 96% rename from src/phonebookui.cpp rename to src/gui/qt/phonebookui.cpp index 3cb38a9af1daf85cf6ec3bf8186078e311ee3c48..50570c2965044ad8d0d9df060762321f3dae0e57 100644 --- a/src/phonebookui.cpp +++ b/src/gui/qt/phonebookui.cpp @@ -1,13 +1,13 @@ /**************************************************************************** -** Form implementation generated from reading ui file 'phonebook.ui' +** Form implementation generated from reading ui file 'gui/qt/phonebook.ui' ** -** Created: Wed Apr 27 10:37:33 2005 +** Created: Fri May 20 14:27:25 2005 ** by: The User Interface Compiler ($Id$) ** ** WARNING! All changes made in this file will be lost! ****************************************************************************/ -#include "phonebookui.h" +#include "gui/qt/phonebookui.h" #include <qvariant.h> #include <qpushbutton.h> diff --git a/src/phonebookui.h b/src/gui/qt/phonebookui.h similarity index 91% rename from src/phonebookui.h rename to src/gui/qt/phonebookui.h index 23076321818c2c9792a4e0d37900079f85f44281..b4944c4b63c62cbea0d1921b35d6d4b71dda430a 100644 --- a/src/phonebookui.h +++ b/src/gui/qt/phonebookui.h @@ -1,7 +1,7 @@ /**************************************************************************** -** Form interface generated from reading ui file 'phonebook.ui' +** Form interface generated from reading ui file 'gui/qt/phonebook.ui' ** -** Created: Wed Apr 27 10:37:33 2005 +** Created: Fri May 20 14:27:25 2005 ** by: The User Interface Compiler ($Id$) ** ** WARNING! All changes made in this file will be lost! diff --git a/src/phoneline.cpp b/src/gui/qt/phoneline.cpp similarity index 99% rename from src/phoneline.cpp rename to src/gui/qt/phoneline.cpp index 817ba8de96db4428b7c14e8ba9e9ea392fb602a3..a196ce46fe8256aac4f765826929ccc9204aab31 100644 --- a/src/phoneline.cpp +++ b/src/gui/qt/phoneline.cpp @@ -30,6 +30,7 @@ PhoneLine::PhoneLine (void) { timer = new QTime(); b_dial = false; b_ringing = false; + _callid = 0; } PhoneLine::~PhoneLine (void) { diff --git a/src/phoneline.h b/src/gui/qt/phoneline.h similarity index 83% rename from src/phoneline.h rename to src/gui/qt/phoneline.h index e49064614edbcd60a917841e6c0fed701e3609d9..0a3829c93c4973824f0522ded8e1750a9435f2b4 100644 --- a/src/phoneline.h +++ b/src/gui/qt/phoneline.h @@ -53,10 +53,11 @@ public: void startTimer (void); void stopTimer (void); QTime *timer; - QString status; QString text; bool first; + inline QString getStatus (void) { return _status; } + inline void setStatus (const QString& status) { _status = status; } inline void setbDial (bool dial) { b_dial = dial; } inline bool getbDial (void) { return b_dial; } inline void setbInProgress (bool prog) { b_inProgress = prog; } @@ -66,16 +67,21 @@ public: inline void setbRinging (bool ring) { if (this->b_ringing != ring) this->b_ringing = ring; } + inline short getCallId (void) { return _callid; } + inline void setCallId (short id) { _callid = id; } private: - JPushButton *jpb; - enum line_state state; - enum line_state stateLine; + short _callid; + QString _status; + + JPushButton* jpb; + enum line_state state; + enum line_state stateLine; - bool b_dial; // if ok button is clicked (->true) - bool b_inProgress; // if outgoing call, + bool b_dial; // if ok button is clicked (->true) + bool b_inProgress; // if outgoing call, // before remote callee answers (->true) - bool b_ringing; // if incoming call, + bool b_ringing; // if incoming call, // before IP-phone user answers (->true) }; diff --git a/src/point.cpp b/src/gui/qt/point.cpp similarity index 91% rename from src/point.cpp rename to src/gui/qt/point.cpp index aa31c6de46c1c76de84dc4e4599acffc025a1cb7..ce124d033dc28f4b2da6627de5d3c7927cbdf147 100644 --- a/src/point.cpp +++ b/src/gui/qt/point.cpp @@ -17,7 +17,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "configurationtree.h" +#include "../../configurationtree.h" #include "point.h" #include <string> @@ -26,7 +26,7 @@ using namespace std; /** * Create a config-tree from file 'filename' */ -Point::Point (const char* filename) { +Point::Point (const string& filename) { skinConfigTree = new ConfigurationTree(); skinConfigTree->populateFromFile (filename); } @@ -44,10 +44,9 @@ Point::~Point (void) { */ string Point::getSubstrX (const char* key) { - char * value = skinConfigTree->getValue(NULL, key); - string tmp(value); - int index = tmp.find(','); - return tmp.substr(0, index); + string value = skinConfigTree->getValue("", string(key)); + int index = value.find(','); + return value.substr(0, index); } /** @@ -55,10 +54,9 @@ Point::getSubstrX (const char* key) { */ string Point::getSubstrY (const char* key) { - char * value = skinConfigTree->getValue(NULL, key); - string tmp(value); - int index = tmp.find(','); - return tmp.substr(index + 1, tmp.length() - index); + string value = skinConfigTree->getValue("", string(key)); + int index = value.find(','); + return value.substr(index + 1, value.length() - index); } /** diff --git a/src/point.h b/src/gui/qt/point.h similarity index 98% rename from src/point.h rename to src/gui/qt/point.h index 37d11bb34f729eab53c1ee5e65b47c34ed4d5749..8b814815e7641c1e0d43950eff5b57108bc0e004 100644 --- a/src/point.h +++ b/src/gui/qt/point.h @@ -31,7 +31,7 @@ class ConfigurationTree; class Point { public: - Point (const char*); + Point (const string&); ~Point (void); int getX (const char*); diff --git a/src/qjlistboxpixmap.cpp b/src/gui/qt/qjlistboxpixmap.cpp similarity index 100% rename from src/qjlistboxpixmap.cpp rename to src/gui/qt/qjlistboxpixmap.cpp diff --git a/src/qjlistboxpixmap.h b/src/gui/qt/qjlistboxpixmap.h similarity index 100% rename from src/qjlistboxpixmap.h rename to src/gui/qt/qjlistboxpixmap.h diff --git a/src/gui/qt/qtGUImainwindow.cpp b/src/gui/qt/qtGUImainwindow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..565731d815e3a09e60d97f22f6cfc4ba7856659d --- /dev/null +++ b/src/gui/qt/qtGUImainwindow.cpp @@ -0,0 +1,1688 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +//////////////////////////////////////////////////////////////////////////////// +// QtGUIMainWindow Implementation // +//////////////////////////////////////////////////////////////////////////////// + +#include "../../configurationtree.h" +#include <stdio.h> + +#include <qbitmap.h> +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qevent.h> +#include <qinputdialog.h> +#include <qlineedit.h> +#include <qmessagebox.h> +#include <qpushbutton.h> +#include <qregexp.h> +#include <qsettings.h> +#include <qspinbox.h> +#include <qtimer.h> +#include <qtooltip.h> + +#include "../../audio/audiodrivers.h" +#include "../../configuration.h" +#include "../../global.h" +#include "../../manager.h" +#include "../../user_cfg.h" +#include "../../skin.h" +#include "configurationpanelui.h" +#include "jpushbutton.h" +#include "numerickeypadtools.h" +#include "point.h" +#include "phoneline.h" +#include "qtGUImainwindow.h" +#include "vector.h" +#include "volumecontrol.h" + +#define QCHAR_TO_STRIP "-" +#define REG_EXPR "(-|\\(|\\)| )" + + +/////////////////////////////////////////////////////////////////////////////// +// Tray Icon implementation +/////////////////////////////////////////////////////////////////////////////// +MyTrayIcon::MyTrayIcon(const QPixmap &icon, const QString &tooltip, + QPopupMenu *_mypop, QObject *parent, const char *name) + : TrayIcon (icon, tooltip, _mypop, parent, name) +{ + menu = _mypop; +} + +void +MyTrayIcon::mousePressEvent (QMouseEvent *e) +{ + switch ( e->button() ) { + case RightButton: + menu->popup( e->globalPos() ); + e->accept(); + break; + + case LeftButton: + emit clickedLeft(); + break; + + case MidButton: + break; + + default: + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// QtGUIMainWindow implementation +/////////////////////////////////////////////////////////////////////////////// + +/** + * Default Constructor + * Init, Connections + */ +QtGUIMainWindow::QtGUIMainWindow (QWidget *parent, const char *name, WFlags f, + Manager *mngr) + : TransQWidget (parent, name, f), + GuiFramework(mngr) { + + for (int i = 0; i < NUMBER_OF_LINES; i++) { + phLines[i] = new PhoneLine(); + phLines[i]->setState(FREE); + _TabIncomingCalls[i] = -1; + } + + // Create configuration _panel + _panel = new ConfigurationPanel (0, 0, false); + + // Address book dialog + + // URL input dialog + _urlinput = new URL_Input (this); + + // For managing + _callmanager = mngr; + + // For DTMF + _key = new DTMF (); + _buf = new short[SIZEBUF]; + + // Create new display and numeric _keypad + _lcd = new MyDisplay(this, 0, this); + _keypad = new NumericKeypad (this, NULL, Qt::WDestructiveClose | + Qt::WStyle_Customize | + Qt::WStyle_NoBorder); + _currentLine = -1; + _chosenLine = -1; + _freeLine = -1; + _first = true; + _chooseLine = false; + _transfer = false; + _dialtone = false; + _msgVar = false; + _apply = false; + + // Initialisation of all that concern the skin + initSkin(); + this->initBlinkTimer(); + + + // By default, keyboard mapping mode is numerical mode + this->setMode(NUM_MODE); + + // Move + setMainLCD (); + + // Change window title and Icon. + this->setCaption(PROGNAME); + this->setIcon(QPixmap(Skin::getPathPixmap(QString(PIXDIR), + QString(SFLPHONE_LOGO)))); + + // Show the GUI + this->show(); + + // Handle the tray icon system + _mypop = new QPopupMenu(this); + _mypop->insertItem ("Quit", qApp, SLOT(quit())); + + _trayicon = new MyTrayIcon(QPixmap( + Skin::getPathPixmap(QString(PIXDIR), QString(TRAY_ICON))), + NULL, _mypop, parent, name); + _trayicon->show(); + + // Connect to handle _trayicon + connect(_trayicon, SIGNAL(clickedLeft()), this, SLOT(clickHandle())); + // Connect _blinkTimer signals to blink slot + connect(_blinkTimer, SIGNAL(timeout()),this, SLOT(blinkMessageSlot())); + connect (_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkRingSlot()) ); + connect (_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkLineSlot())); + // Connect to append url in display + connect (_urlinput->buttonOK, SIGNAL(clicked()), this, SLOT(stripSlot())); + // Connect to save settings + connect (_panel->buttonSave, SIGNAL(clicked()), this, SLOT(save())); + // Connect to apply skin + connect (_panel->buttonApplySkin, SIGNAL(clicked()), this,SLOT(applySkin())); + // Connect to register manually + connect (_panel->Register, SIGNAL(clicked()), this, SLOT(registerSlot())); +} + +/** + * Destructor + */ +QtGUIMainWindow::~QtGUIMainWindow(void) { + deleteButtons(); + delete _panel; + delete _blinkTimer; + delete _keypad; + delete _lcd; + delete _urlinput; + delete _callmanager; + delete _mypop; + delete _trayicon; + delete pt; + delete _key; + delete[] _buf; + delete[] phLines; +} + +void +QtGUIMainWindow::deleteButtons (void) { + delete phoneKey_transf; + delete phoneKey_msg; + delete phoneKey_conf; + delete phoneKey_line0; + delete phoneKey_line1; + delete phoneKey_line2; + delete phoneKey_line3; + delete reduce_button; + delete quit_button; + delete addr_book_button; + delete configuration_button; + delete hangup_button; + delete dial_button; + delete mute_button; + delete dtmf_button; + delete vol_mic; + delete vol_spkr; + delete micVolVector; + delete spkrVolVector; + + for (int j = 0; j < NUMBER_OF_LINES; j++) { + delete phLines[j]->button(); + } + +} + +/////////////////////////////////////////////////////////////////////////////// +// Private Methods implementations +/////////////////////////////////////////////////////////////////////////////// +/** + * Init and start blink timer for all blinking pixmap, with 500 ms timeout. + */ +void +QtGUIMainWindow::initBlinkTimer(void) { + _blinkTimer = new QTimer(this); + _blinkTimer->start(500); +} + +/** + * Init variable with skin choice + */ +QString +QtGUIMainWindow::setPathSkin (void) { + return QString(Config::getchar( + "Preferences", "Themes.skinChoice", "metal")); +} + +/** + * Init variable with ring choice + */ +string +QtGUIMainWindow::ringFile(void) { + return get_config_fields_str(AUDIO, RING_CHOICE); +} + +/** + * Get whole path for rings + */ +string +QtGUIMainWindow::getRingtoneFile (void) { + string ringFilename(Skin::getPathRing(string(RINGDIR), ringFile())); + return ringFilename; +} + +void +QtGUIMainWindow::initSkin (void) { + // Load file configuration skin + string skinfilename(Skin::getPath(QString(SKINDIR), setPathSkin(), + QString(FILE_INI))); + + if (!_apply) { + this->pt = new Point(skinfilename); + } else { + // If click on apply button + delete pt; + deleteButtons(); + this->pt = new Point(skinfilename); + } + // Initialisation of the buttons + initSpkrVolumePosition(); + initMicVolumePosition(); + initButtons(); + initVolume(); + // Connections of the buttons + connections(); + + // Load background image phone + setbgPixmap (new QPixmap (Skin::getPath(QString(SKINDIR), + setPathSkin(), + QString(PIXMAP_PHONE)))); + // Transform pixmap to QImage + setSourceImage (); + this->setMaximumSize (getSourceImage().width(), getSourceImage().height()); + this->setGeometry (MAIN_INITIAL_POSITION, + MAIN_INITIAL_POSITION, + getSourceImage().width(), + getSourceImage().height()); + // Calculate just one time the transparency mask bit to bit + transparencyMask (); + + // Line pixmaps initialisation + for (int i = 0; i < NUMBER_OF_LINES; i++) { + for (int j = 0; j < NUMBER_OF_STATES; j++) { + TabLinePixmap[i][j] = QPixmap(Skin::getPath(QString(SKINDIR), + setPathSkin(), + QString(PIXMAP_LINE(i, j)))); + } + } + // Message pixmaps initialisation + TabMsgPixmap[0] = QPixmap(Skin::getPath(QString(SKINDIR), setPathSkin(), + PIXMAP_MESSAGE_OFF)); + TabMsgPixmap[1] = QPixmap(Skin::getPath(QString(SKINDIR), setPathSkin(), + PIXMAP_MESSAGE_ON)); +} + +void +QtGUIMainWindow::initSpkrVolumePosition (void) { + if (pt->getDirection(VOL_SPKR) == VERTICAL) { + vol_spkr_x = Config::get("Audio", "Volume.speakers_x", + pt->getX(VOL_SPKR)); + vol_spkr_y = Config::get("Audio", "Volume.speakers_y", + pt->getVariation(VOL_SPKR)); + } else if (pt->getDirection(VOL_SPKR) == HORIZONTAL) { + vol_spkr_x = Config::get("Audio", "Volume.speakers_x", + pt->getX(VOL_SPKR) + pt->getVariation(VOL_SPKR)); + vol_spkr_y = Config::get("Audio", "Volume.speakers_y", + pt->getY(VOL_SPKR)); + } +} + +void +QtGUIMainWindow::initMicVolumePosition (void) { + if (pt->getDirection(VOL_MIC) == VERTICAL) { + vol_mic_x = Config::get("Audio", "Volume.micro_x", pt->getX(VOL_MIC)); + vol_mic_y = Config::get("Audio", "Volume.micro_y", + pt->getVariation(VOL_MIC)); + } else if (pt->getDirection(VOL_MIC) == HORIZONTAL) { + vol_mic_x = Config::get("Audio", "Volume.micro_x", + pt->getX(VOL_MIC) + pt->getVariation(VOL_MIC)); + vol_mic_y = Config::get("Audio", "Volume.micro_y", pt->getY(VOL_MIC)); + } +} + +void +QtGUIMainWindow::initVolume (void) +{ + _callmanager->setSpkrVolume(spkrVolVector->Y() - vol_spkr_y); + _callmanager->setMicroVolume(micVolVector->Y() - vol_mic_y); +} + +/** + * Inits all phonekey buttons. + * Create new QPushButtons, set up tool tip, disable focus, set button geometry + * set palette. + */ +void +QtGUIMainWindow::initButtons (void) { + // Buttons initialisation + phoneKey_msg= new JPushButton(this, NULL, VOICEMAIL); + phoneKey_transf = new JPushButton(this, NULL, TRANSFER); + phoneKey_conf = new JPushButton(this, NULL, CONFERENCE); + reduce_button = new JPushButton(this, NULL, MINIMIZE); + quit_button = new JPushButton(this, NULL, CLOSE); + addr_book_button = new JPushButton(this, NULL, DIRECTORY); + configuration_button = new JPushButton(this, NULL, SETUP); + hangup_button = new JPushButton(this, NULL, HANGUP); + dial_button = new JPushButton(this, NULL, CONNECT); + mute_button = new JPushButton(this, NULL, MUTE); + dtmf_button = new JPushButton(this, NULL, DTMF_SHOW); + + // Set tooltip buttons + QToolTip::add(reduce_button, tr("Minimize window")); + QToolTip::add(quit_button, tr("Close window (Ctrl+Q)")); + QToolTip::add(phoneKey_msg, tr("Get your message")); + QToolTip::add(phoneKey_transf, tr("Call transfer")); + QToolTip::add(phoneKey_conf, tr("Conference")); + QToolTip::add(addr_book_button, tr("Address book")); + QToolTip::add(configuration_button, tr("Configuration tools (Ctrl+C)")); + QToolTip::add(hangup_button, tr("Hangup")); + QToolTip::add(dial_button, tr("Dial")); + QToolTip::add(mute_button, tr("Mute")); + QToolTip::add(dtmf_button, tr("Show DTMF _keypad (Ctrl+D)")); + + // Buttons position + phoneKey_msg->move (pt->getX(VOICEMAIL), pt->getY(VOICEMAIL)); + phoneKey_transf->move (pt->getX(TRANSFER), pt->getY(TRANSFER)); + phoneKey_conf->move (pt->getX(CONFERENCE), pt->getY(CONFERENCE)); + reduce_button->move (pt->getX(MINIMIZE), pt->getY(MINIMIZE)); + addr_book_button->move (pt->getX(DIRECTORY), pt->getY(DIRECTORY)); + quit_button->move (pt->getX(CLOSE), pt->getY(CLOSE)); + configuration_button->move (pt->getX(SETUP), pt->getY(SETUP)); + hangup_button->move (pt->getX(HANGUP), pt->getY(HANGUP)); + dial_button->move (pt->getX(CONNECT), pt->getY(CONNECT)); + mute_button->move (pt->getX(MUTE), pt->getY(MUTE)); + dtmf_button->move (pt->getX(DTMF_SHOW), pt->getY(DTMF_SHOW)); + + // Loop for line buttons + // Initialisation, set no focus, set geometry, set palette, pixmap + for (int j = 0; j < NUMBER_OF_LINES; j++) { + QString lnum; + + lnum = "l" + lnum.setNum (j + 1); + phLines[j]->setButton(new JPushButton( + this, NULL, lnum.ascii())); + phLines[j]->button()->move (pt->getX(lnum),pt->getY(lnum)); + } + + // Set pixmaps volume + micVolVector = new Vector(this, VOL_MIC, pt); + spkrVolVector = new Vector(this, VOL_SPKR, pt); + + vol_mic = new VolumeControl(this, NULL, VOLUME, micVolVector); + vol_spkr = new VolumeControl(this, NULL, VOLUME, spkrVolVector); + vol_mic->move(vol_mic_x, vol_mic_y); + vol_spkr->move(vol_spkr_x, vol_spkr_y); +} + +void +QtGUIMainWindow::connections (void) { + // Connect for clicked numeric _keypad button + connect ((QObject*)_keypad->key0, SIGNAL(clicked()), this, + SLOT(pressedKey0())); + connect ((QObject*)_keypad->key1, SIGNAL(clicked()), this, + SLOT(pressedKey1())); + connect ((QObject*)_keypad->key2, SIGNAL(clicked()), this, + SLOT(pressedKey2())); + connect ((QObject*)_keypad->key3, SIGNAL(clicked()), this, + SLOT(pressedKey3())); + connect ((QObject*)_keypad->key4, SIGNAL(clicked()), this, + SLOT(pressedKey4())); + connect ((QObject*)_keypad->key5, SIGNAL(clicked()), this, + SLOT(pressedKey5())); + connect ((QObject*)_keypad->key6, SIGNAL(clicked()), this, + SLOT(pressedKey6())); + connect ((QObject*)_keypad->key7, SIGNAL(clicked()), this, + SLOT(pressedKey7())); + connect ((QObject*)_keypad->key8, SIGNAL(clicked()), this, + SLOT(pressedKey8())); + connect ((QObject*)_keypad->key9, SIGNAL(clicked()), this, + SLOT(pressedKey9())); + connect ((QObject*)_keypad->keyStar, SIGNAL(clicked()), this, + SLOT(pressedKeyStar())); + connect ((QObject*)_keypad->keyHash, SIGNAL(clicked()), this, + SLOT(pressedKeyHash())); + connect ((QObject*)_keypad->keyClose, SIGNAL(clicked()), this, + SLOT(dtmfKeypad())); + + // Connections for the lines + connect (phLines[0]->button(), SIGNAL(clicked()), this, + SLOT(button_line0())); + connect (phLines[1]->button(), SIGNAL(clicked()), this, + SLOT(button_line1())); + connect (phLines[2]->button(), SIGNAL(clicked()), this, + SLOT(button_line2())); + connect (phLines[3]->button(), SIGNAL(clicked()), this, + SLOT(button_line3())); + connect (phLines[4]->button(), SIGNAL(clicked()), this, + SLOT(button_line4())); + connect (phLines[5]->button(), SIGNAL(clicked()), this, + SLOT(button_line5())); + + // Misc + connect (phoneKey_msg, SIGNAL(clicked()), this, SLOT(button_msg())); + connect (phoneKey_transf, SIGNAL(clicked()), this, SLOT(button_transfer())); + connect (phoneKey_conf, SIGNAL(clicked()), this, SLOT(button_conf())); + connect (dial_button, SIGNAL(clicked()), this, SLOT(dial())); + connect (mute_button, SIGNAL(clicked()), this, SLOT(button_mute())); + connect (hangup_button, SIGNAL(clicked()), this, SLOT(hangupLine())); + connect (configuration_button,SIGNAL(clicked()),this,SLOT(configuration())); + connect (addr_book_button, SIGNAL(clicked()), this,SLOT(addressBook())); + connect (dtmf_button, SIGNAL(clicked()), this, SLOT(dtmfKeypad())); + + // Connect to reduce + connect (reduce_button, SIGNAL(clicked()), this, SLOT(reduceHandle())); + // Connect to quit with keyboard + connect (this, SIGNAL(keyPressed(int)), this, SLOT(qt_quitApplication())); + // Connect to quit with quit button + connect (quit_button, SIGNAL(clicked()), this, SLOT(qt_quitApplication())); + + // Connections for volume control + connect(vol_spkr, SIGNAL(setVolumeValue(int)), this, + SLOT(volumeSpkrChanged(int))); + connect(vol_mic, SIGNAL(setVolumeValue(int)), this, + SLOT(volumeMicChanged(int))); + +} + +/** + * Returns true if the keyboard mapping returns letters. + * + * @return bool + */ +bool +QtGUIMainWindow::isInTextMode (void) { + if (modeType == TEXT_MODE) { + return true; + } else { + return false; + } +} + +/** + * Returns true if the keyboard mapping returns digits. + * + * @return bool + */ +bool +QtGUIMainWindow::isInNumMode (void) { + if (modeType == NUM_MODE) { + return true; + } else { + return false; + } +} + +/** + * Sets up the keyboard mapping mode. + */ +void +QtGUIMainWindow::setMode (int mode) { + this->modeType = mode; +} + +short +QtGUIMainWindow::isThereIncomingCall (void) +{ + for (int i = 0; i < NUMBER_OF_LINES; i++) { + if (_TabIncomingCalls[i] > 0) { + return _TabIncomingCalls[i]; + } + } + + return -1; +} + +short +QtGUIMainWindow::isIncomingCall (int line) +{ + if (_TabIncomingCalls[line] > 0) { + return _TabIncomingCalls[line]; + } else { + return -1; + } +} + +int +QtGUIMainWindow::id2line (short id) +{ + int i; + for (i = 0; i < NUMBER_OF_LINES; i++) { + if (phLines[i]->getCallId() == id) { + return i; + } + } + if (i == NUMBER_OF_LINES) { + _debug("Id is not attributed to a phoneline\n"); + return -1; + } + return -1; +} + +short +QtGUIMainWindow::line2id (int line) +{ + short i; + if (line != -1) { + i = phLines[line]->getCallId(); + } else { + return -1; + } + + if (i > 0) { + return i; + } else { + return -1; + } +} + +void +QtGUIMainWindow::changeLineStatePixmap (int line, line_state state) +{ + // Set free-status for current line + phLines[line]->setState(state); + // Set free-pixmap + if (state == ONHOLD) { + // Because for the state of line pixmap there are just 2 states + // (FREE and BUSY), so we associate ONHOLD to BUSY + state = state - 1; + } + phLines[line]->button()->setPixmap(TabLinePixmap[line][state]); +} + +int +QtGUIMainWindow::busyLineNumber (void) +{ + int temp = -1; + for (int i = 0; i < NUMBER_OF_LINES; i++) { + if (phLines[i]->isBusy() and i != _currentLine) { + temp = i; + } + } + return temp; +} + +int +QtGUIMainWindow::putOnHoldBusyLine (int line) +{ + if (line != -1 and !phLines[line]->getbRinging()) { + // Occurs when newly off-hook line replaces another one. + qt_onHoldCall(line2id(line)); + changeLineStatePixmap(line, ONHOLD); + return 1; + } + return 0; +} + +void +QtGUIMainWindow::dialtone (bool var) { + if (_callmanager->error()->getError() == 0) { + if (_dialtone != var) { + _dialtone = var; + } + _callmanager->setTonezone(var); + _callmanager->getTonegenerator()->toneHandle(ZT_TONE_DIALTONE); + } else { + _callmanager->error()->errorName(DEVICE_NOT_OPEN, NULL); + } +} + +//////////////////////////////////////////////////////////////////////////// +// Public functions +//////////////////////////////////////////////////////////////////////////// +Call* +QtGUIMainWindow::getCall (short id) +{ + return _callmanager->getCall(id); +} + +int +QtGUIMainWindow::associateCall2Line (short id) +{ + int i; + + if (getChooseLine()) { + i = getCurrentLine(); + phLines[i]->setState(BUSY); + phLines[i]->setCallId(id); + return i; + } else { + for (i = 0 ; i < NUMBER_OF_LINES; i++) { + if (phLines[i]->isFree()) { + phLines[i]->setState(BUSY); + phLines[i]->setCallId(id); + return i; + } + } + if (i == NUMBER_OF_LINES) { + displayError("All the lines are busy"); + return -1; + } + return -1; + } +} + +PhoneLine* +QtGUIMainWindow::getPhoneLine (short id) +{ + int i; + for (i = 0; i < NUMBER_OF_LINES; i++) { + if (phLines[i]->getCallId() == id) { + return phLines[i]; + } + } + if (i == NUMBER_OF_LINES) { + _debug("Id is not attributed to a phoneline\n"); + return NULL; + } + return NULL; +} + +int +QtGUIMainWindow::getCurrentLine (void) +{ + return _currentLine; +} + +void +QtGUIMainWindow::setCurrentLine (int current) +{ + _currentLine = current; +} + +int +QtGUIMainWindow::getElapse (void) +{ + int line = getCurrentLine(); + return (phLines[line]->timer->elapsed() / 1000); +} + +///////////////////////////////////////////////////////////////////////////// +// Reimplementation of virtual functions +///////////////////////////////////////////////////////////////////////////// + +int +QtGUIMainWindow::incomingCall (short id) +{ + int i; + // Associate call id with a phoneline i. + i = associateCall2Line(id); + if (i >= 0) { + _TabIncomingCalls[i] = id; + _debug("Phoneline %d associated to id %d\n", i, id); + if (getPhoneLine(id) != NULL) { + // Set boolean to true to blink pixmap to notify the ringing line + getPhoneLine(id)->setbRinging(true); + // Set the status to the phoneline + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + } + } + return i; +} + +int +QtGUIMainWindow::peerAnsweredCall (short id) +{ + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + // Afficher call-timer + startCallTimer(id); + setChooseLine(false); + + return 1; +} + +int +QtGUIMainWindow::peerRingingCall (short id) +{ + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + return 1; +} + + +int +QtGUIMainWindow::peerHungupCall (short id) +{ + int line = id2line(id); + + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + changeLineStatePixmap(line, FREE); + stopCallTimer(id); + getPhoneLine(id)->setbRinging(false); + getPhoneLine(id)->setCallId(0); + setChooseLine(false); + setCurrentLine(-1); + _TabIncomingCalls[line] = -1; + + return 1; +} + +void +QtGUIMainWindow::displayTextMessage (short id, const string& message) +{ + _lcd->clearBuffer(); + _lcd->appendText(message); +} + +void +QtGUIMainWindow::displayError (const string& error) +{ + _lcd->clearBuffer(); + _lcd->appendText(error); +} + +void +QtGUIMainWindow::displayStatus (const string& status) +{ + if (status.compare(HUNGUP_STATUS) == 0 or + status.compare(TRANSFER_STATUS) == 0) { + _lcd->clearBuffer(); + } + _lcd->setStatus(status); +} + +void +QtGUIMainWindow::displayContext (short id) +{ + displayStatus(getCall(id)->getStatus()); + if (getCall(id)->isIncomingType()) { + displayTextMessage (id, getCall(id)->getCallerIdName()); + } else if (getCall(id)->isOutgoingType()) { + displayTextMessage (id, getCall(id)->getCallerIdNumber()); + } else { + _debug("No call with id *%d\n", id); + } +} + +void +QtGUIMainWindow::setup (void) +{ + configuration(); +} + +//////////////////////////////////////////////////////////////////////////// +// IP-phone user actions +//////////////////////////////////////////////////////////////////////////// + +int +QtGUIMainWindow::qt_outgoingCall (void) +{ + int id, line; + if (_lcd->getTextBuffer() == NULL) { + _debug("Enter a phone number\n"); + _callmanager->displayStatus(ENTER_NUMBER_STATUS); + return -1; + } + const string to(_lcd->getTextBuffer().ascii()); + if (to.empty()) { + _debug("Enter a phone number\n"); + _callmanager->displayStatus(ENTER_NUMBER_STATUS); + return -1; + } + + id = outgoingCall(to); + if (id > 0) { + line = associateCall2Line(id); + + setCurrentLine(line); + displayStatus(TRYING_STATUS); + _callmanager->getCall(id)->setCallerIdNumber(to); + changeLineStatePixmap(line, BUSY); + } + + return line; +} + +int +QtGUIMainWindow::qt_hangupCall (short id) +{ + int i; + i = hangupCall(id); + stopCallTimer(id); + displayStatus(HUNGUP_STATUS); + setCurrentLine(-1); + return i; +} + +int +QtGUIMainWindow::qt_answerCall (short id) +{ + int i; + i = answerCall(id); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + startCallTimer(id); + displayStatus(CONNECTED_STATUS); + getPhoneLine(id)->setbRinging(false); + return i; +} + +int +QtGUIMainWindow::qt_onHoldCall (short id) +{ + int i; + i = onHoldCall(id); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + return i; +} + +int +QtGUIMainWindow::qt_offHoldCall (short id) +{ + int i; + i = offHoldCall(id); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + displayStatus(CONNECTED_STATUS); + return i; +} + +int +QtGUIMainWindow::qt_transferCall (short id) +{ + int i; + const string to(_lcd->getTextBuffer().ascii());; + _debug("Transfer call %d to %s\n", id, to.data()); + i = transferCall(id, to); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + return i; +} + +void +QtGUIMainWindow::qt_muteOn (short id) +{ + int i; + i = muteOn(id); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + displayStatus(MUTE_ON_STATUS); +} + +void +QtGUIMainWindow::qt_muteOff (short id) +{ + int i; + i = muteOff(id); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + displayStatus(CONNECTED_STATUS); +} + +int +QtGUIMainWindow::qt_refuseCall (short id) +{ + int i; + i = refuseCall(id); +// getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + displayStatus(HUNGUP_STATUS); + getPhoneLine(id)->setbRinging(false); + _TabIncomingCalls[id2line(id)] = -1; + return i; +} + +int +QtGUIMainWindow::qt_cancelCall (short id) +{ + int i; + i = cancelCall(id); + getPhoneLine(id)->setStatus(QString(getCall(id)->getStatus())); + displayStatus(HUNGUP_STATUS); + + return i; +} + +/////////////////////////////////////////////////////////////////////////////// +// Public Methods implementations // +/////////////////////////////////////////////////////////////////////////////// + + +/** + * Initializes LCD display, and move it at the configuration file position. + */ +void +QtGUIMainWindow::setMainLCD (void) { + // Screen initialisation + this->_lcd->move (pt->getX(SCREEN), pt->getY(SCREEN)); +} + + +int +QtGUIMainWindow::toggleLine (int line) +{ + int id; + int busyLine; + Call* call; + + if (line == -1) { + return -1; + } + + setCurrentLine(line); + busyLine = busyLineNumber(); + + id = line2id(line); + _debug("line2id(line) = %d and busy line = %d\n", id, busyLine); + if (id > 0) { + // If the call-id already exists + call = getCall(id); + if (call == NULL) { + // Check if the call exists + return -1; + } else if (call->isBusy()){ + // If call is busy, put this call on hold + _debug("CASE 1\n"); + changeLineStatePixmap(line, ONHOLD); + displayStatus(ONHOLD_STATUS); + qt_onHoldCall(id); + } else if (call->isOnHold()) { + // If call is on hold, put this call on busy state + _debug("CASE 2\n"); + changeLineStatePixmap(line, BUSY); + putOnHoldBusyLine(busyLine); + if (getChooseLine()) { + // If a free line is off-hook, set this line to free state + setChooseLine(false); + changeLineStatePixmap(getChosenLine(), FREE); + dialtone(false); + } + _lcd->setInFunction(true); + qt_offHoldCall(id); + displayContext(id); + } else if (call->isIncomingType()) { + // If incoming call occurs + _debug("CASE 3\n"); + changeLineStatePixmap(line, BUSY); + putOnHoldBusyLine(busyLine); + qt_answerCall(id); + } else { + _debug("Others cases to handle\n"); + return -1; + } + } else { + // If just click on free line + _debug("CASE 4\n"); + phLines[line]->button()->setPixmap(TabLinePixmap[line][BUSY]); + displayStatus(ENTER_NUMBER_STATUS); + setChooseLine(true); + setChosenLine(line); + + putOnHoldBusyLine(busyLine); + if (getFreeLine() != -1 and getFreeLine() != line) { + changeLineStatePixmap(getFreeLine(), FREE); + } + + setFreeLine(line); + _lcd->setInFunction(false); + _lcd->clearBuffer(); + dialtone(true); + } + return 1; +} + +/** + * Actions occur when click on hang off button. + * Use to validate incoming and outgoing call, transfer call. + */ +void +QtGUIMainWindow::dial (void) +{ + short i; + int line = -1; + + if ((i = isThereIncomingCall()) > 0) { + // If new incoming call + _debug("Dial : new incoming call\n"); + line = id2line(i); + _TabIncomingCalls[line] = -1; + toggleLine(line); + } else if (getTransfer()){ + // If call transfer + _debug("Dial : call transfer\n"); + qt_transferCall (line2id(getCurrentLine())); + } else { + // If new outgoing call + _debug("Dial : outgoing call\n"); + if (getCurrentLine() < 0 or getChooseLine()) { + line = qt_outgoingCall(); + } + } +} + +/** + * Hangup the current call. + */ +void +QtGUIMainWindow::hangupLine (void) +{ + int i; + int line = getCurrentLine(); + int id = phLines[line]->getCallId(); + if (_callmanager->getbCongestion()) { + _callmanager->congestion(false); + _lcd->clear(QString(ENTER_NUMBER_STATUS)); + } + + if (line >= 0 and id > 0) { + qt_hangupCall(id); + changeLineStatePixmap(line, FREE); + phLines[line]->setCallId(0); + setChooseLine(false); + } else if ((i = isThereIncomingCall()) > 0){ + // To refuse new incoming call + qt_refuseCall(i); + changeLineStatePixmap(id2line(i), FREE); + } else if (line >= 0) { + _debug("Just load free pixmap for the line %d\n", line); + changeLineStatePixmap(line, FREE); + dialtone(false); + setChooseLine(false); + } +} + +/** + * Stop the blinking message slot and load the message-off button pixmap. + */ +void +QtGUIMainWindow::stopTimerMessage (void) { + _msgVar = false; + phoneKey_msg->setPixmap(TabMsgPixmap[FREE]); +} + +/** + * Stop the call timer. + * + * @param line: number of line + */ +void +QtGUIMainWindow::stopCallTimer (short id) { + // Stop the call timer when hang up + if (getPhoneLine(id)->timer != NULL) { + getPhoneLine(id)->stopTimer(); + } + // No display call timer, display current hour + _lcd->setInFunction(false); + getPhoneLine(id)->first = true; +} + +/** + * Start the call timer. + * + * @param line: number of line + */ +void +QtGUIMainWindow::startCallTimer (short id) { + // Call-timer enable + _lcd->setInFunction(true); + + // To start the timer for display text just one time + if (getPhoneLine(id)->first) { + getPhoneLine(id)->startTimer(); + getPhoneLine(id)->first = false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Public slot implementations // +/////////////////////////////////////////////////////////////////////////////// +void +QtGUIMainWindow::volumeSpkrChanged (int val) { + _callmanager->setSpkrVolume(val); +} + +void +QtGUIMainWindow::volumeMicChanged (int val) { + _callmanager->setMicroVolume(val); +} + +void +QtGUIMainWindow::registerSlot (void) { + registerVoIPLink(); +} + +/** + * Slot to blink with free and busy pixmaps when line is hold. + */ +void +QtGUIMainWindow::blinkLineSlot (void) { + static bool isOn = false; + int state = BUSY; + if (!isOn) { + state = FREE; + } + + for (int i = 0; i < NUMBER_OF_LINES; i++) { + // If lines are hold on, set blinking pixmap + if (phLines[i]->isOnHold()) { + phLines[i]->button()->setPixmap(TabLinePixmap[i][state]); + } + } + isOn = !isOn; +} + +// Dial the voicemail Number automatically when button is clicked +void +QtGUIMainWindow::button_msg (void) { + stopTimerMessage(); + _lcd->appendText(get_config_fields_str(PREFERENCES, VOICEMAIL_NUM)); + qt_outgoingCall(); +} + +// Allow to enter a phone number to transfer the current call. +// This number is validated by ok-button or typing Enter +void +QtGUIMainWindow::button_transfer (void) { + if (getCurrentLine() != -1) { + setTransfer(true); + onHoldCall(line2id(getCurrentLine())); + displayStatus(TRANSFER_STATUS); + } +} + +void +QtGUIMainWindow::button_conf (void) { +//TODO: This feature is not implemented yet + QMessageBox::information(this, "Conference", + "This feature is not implemented yet", QMessageBox::Yes); +} + +void +QtGUIMainWindow::button_line0 (void) { + toggleLine (0); + +} + +void +QtGUIMainWindow::button_line1 (void) { + toggleLine (1); +} + +void +QtGUIMainWindow::button_line2 (void) { + toggleLine (2); +} + +void +QtGUIMainWindow::button_line3 (void) { + toggleLine (3); +} + +void +QtGUIMainWindow::button_line4 (void) { + toggleLine (4); +} + +void +QtGUIMainWindow::button_line5 (void) { + toggleLine (5); +} + +void +QtGUIMainWindow::button_mute(void) +{ + // Disable micro sound + static bool isOn = true; + + int id = line2id(getCurrentLine()); + + if (_callmanager->getNumberOfCalls() > 0) { + // If there is at least a pending call + if(!isOn) { + qt_muteOff(id); + } else { + qt_muteOn(id); + } + isOn = !isOn; + } +} + +// Show the setup _panel +void +QtGUIMainWindow::configuration (void) { + _panel->show(); +} + +void +QtGUIMainWindow::addressBook (void) { +// TODO: phonebook->show(); + QMessageBox::information(this, "Directory", + "This feature is not implemented yet", QMessageBox::Yes); +} + +// Handle the dtmf-button click +void +QtGUIMainWindow::dtmfKeypad (void) { + if (_keypad->isVisible()) { + // Hide _keypad if it's visible. + _keypad->hide(); + } else { + + if (_first and !getMoved()) { + // If it's the first time that _keypad is shown. + // The position is fixed with the main initial position. + _first = false; + _keypad->setGeometry (MAIN_INITIAL_POSITION + + this->getSourceImage().width(), + MAIN_INITIAL_POSITION, + _keypad->getSourceImage().width(), + _keypad->getSourceImage().height()); + } else { + // If main window is moved, we calculate the _keypad new position + // to fix it with main window + if (getMoved()) { + _keypad->setGeometry (positionOffsetX(), + getGlobalMouseY() - getMouseY(), + _keypad->getSourceImage().width(), + _keypad->getSourceImage().height()); + } + if (_keypad->getMoved()) { + // If _keypad is moved, it shows at the previous position + _keypad->setGeometry ( + _keypad->getGlobalMouseX()-_keypad->getMouseX(), + _keypad->getGlobalMouseY()-_keypad->getMouseY(), + _keypad->getSourceImage().width(), + _keypad->getSourceImage().height()); + } + } + + // Show _keypad if it's hidden. + _keypad->show(); + } + +} + +// Get x-position offset, related to the screen size, to show numeric _keypad +int +QtGUIMainWindow::positionOffsetX (void) { + QRect screenRect; + int offset; + + // Get the screen geometry + screenRect = (QApplication::desktop())->screenGeometry (0); + + offset = this->getSourceImage().width() - getMouseX() + getGlobalMouseX(); + if (offset + _keypad->getSourceImage().width() > screenRect.right()) { + return getGlobalMouseX() - ( + getMouseX() + _keypad->getSourceImage().width()); + } else { + return offset; + } +} + +/** + * Slot when receive a message, blink pixmap. + */ +void +QtGUIMainWindow::blinkMessageSlot (void) { + static bool isOn = false; + int stateMsg = BUSY; + + if(!isOn) { + stateMsg = FREE; + } + + if (_msgVar) { + phoneKey_msg->setPixmap(TabMsgPixmap[stateMsg]); + } + isOn = !isOn; +} + + +/** + * Slot when phone is ringing, blink pixmap. + */ +void +QtGUIMainWindow::blinkRingSlot (void) +{ + static bool isOn = false; + int state = BUSY; + int line; + int i; + + if (isThereIncomingCall() != -1) { + // For the line + if (!isOn) { + state = FREE; + } + for (i = 0; i < NUMBER_OF_LINES; i++) { + if (isIncomingCall(i) != -1 and phLines[i]->getbRinging()) { + phLines[i]->button()->setPixmap(TabLinePixmap[i][state]); + } + } + isOn = !isOn; + } +} + +/** + * Slot to quit application with or without QMessageBox confirmation + */ +void +QtGUIMainWindow::qt_quitApplication (void) +{ + // Save volume positions + // TODO: save position if direction is horizontal + Config::set("Audio", "Volume.speakers_x", pt->getX(VOL_SPKR)); + if (vol_spkr->getValue() != 0) { + Config::set("Audio", "Volume.speakers_y", pt->getY(VOL_SPKR) - + vol_spkr->getValue()); + } + Config::set("Audio", "Volume.micro_x", pt->getX(VOL_MIC)); + if (vol_mic->getValue() != 0) { + Config::set("Audio", "Volume.micro_y", pt->getY(VOL_MIC) - + vol_mic->getValue()); + } + // Save current position of the controls volume + save(); + + if (get_config_fields_int(PREFERENCES, CONFIRM_QUIT) == YES) { + // If message-box + if (QMessageBox::question(this, "Confirm quit", + "Are you sure you want to quit SFLPhone ?", + QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + QApplication::exit(0); + if (!quitApplication()) { + _debug("Your data didn't be saved\n"); + } + } + } else { + // If no message-box + QApplication::exit(0); + if (!quitApplication()) { + _debug("Your data didn't be saved\n"); + } + } +} + +/** + * Slot to strip _urlinput. + */ +void +QtGUIMainWindow::stripSlot (void) { + QRegExp rx(REG_EXPR); + _lcd->appendText(_urlinput->url->text().remove(rx)); + _urlinput->close(); +} + +/** + * Slot when numeric _keypad phonekey is pressed, append the key text and setup + * the DTMF _keypad . + * + * @param button group id + */ +void +QtGUIMainWindow::pressedKeySlot (int id) { + char code = 0; + int pulselen = 0; + int a = 0; + int callid; + + // Stop dial tone + if (_dialtone) { + dialtone(false); + } + + switch (id) { + case KEYPAD_ID_0: code = '0'; break; + case KEYPAD_ID_1: code = '1'; break; + case KEYPAD_ID_2: code = '2'; break; + case KEYPAD_ID_3: code = '3'; break; + case KEYPAD_ID_4: code = '4'; break; + case KEYPAD_ID_5: code = '5'; break; + case KEYPAD_ID_6: code = '6'; break; + case KEYPAD_ID_7: code = '7'; break; + case KEYPAD_ID_8: code = '8'; break; + case KEYPAD_ID_9: code = '9'; break; + case KEYPAD_ID_STAR: code = '*'; break; + case KEYPAD_ID_HASH: code = '#'; break; + } + + callid = line2id(getCurrentLine()); + if (callid != -1 and getCall(callid)->isBusy()) { + sendDtmf(callid, code); // pour envoyer DTMF + } + + // Handle dtmf + _key->startTone(code); + _key->generateDTMF(_buf, SAMPLING_RATE); + _callmanager->audiodriver->audio_buf.resize(SAMPLING_RATE); + _callmanager->audiodriver->audio_buf.setData( + _buf, _callmanager->getSpkrVolume()); + + pulselen = get_config_fields_int(SIGNALISATION, PULSE_LENGTH); + _callmanager->audiodriver->audio_buf.resize(pulselen * (OCTETS/1000)); + a = _callmanager->audiodriver->writeBuffer(); + if (a == 1) { + pressedKeySlot(id); + } else { + //if (_callmanager->error->getError() == 0) + _lcd->appendText (code); + /*else if (callmanager->error->getError() == 1) { + _lcd->clearBuffer(); + _callmanager->error->setError(0); + _lcd->appendText (code); + }*/ + } +} + +// Save settings in config-file +void +QtGUIMainWindow::save() { + saveConfig(); +} + +void +QtGUIMainWindow::applySkin (void) { + _apply = true; + // For skin of the screen + _lcd->initGraphics(); + setMainLCD(); + // For skin of the gui + initSkin(); + +} + + +// Handle operation to minimize the application +void +QtGUIMainWindow::reduceHandle (void) { + if (get_config_fields_int(PREFERENCES, CHECKED_TRAY)) { + clickHandle(); + } else { + showMinimized(); + } +} + +// Handle mouse left-button click to minimize/maximize the application +void +QtGUIMainWindow::clickHandle (void) { + if (this->isShown()) { + hide(); + } + + else if (this->isMinimized()) { + showMaximized(); + } + + else { + show(); + } +} + +void +QtGUIMainWindow::pressedKey0 (void) { + pressedKeySlot (KEYPAD_ID_0); +} + +void +QtGUIMainWindow::pressedKey1 (void) { + pressedKeySlot (KEYPAD_ID_1); +} + +void +QtGUIMainWindow::pressedKey2 (void) { + pressedKeySlot (KEYPAD_ID_2); +} +void +QtGUIMainWindow::pressedKey3 (void) { + pressedKeySlot (KEYPAD_ID_3); +} +void +QtGUIMainWindow::pressedKey4 (void) { + pressedKeySlot (KEYPAD_ID_4); +} +void +QtGUIMainWindow::pressedKey5 (void) { + pressedKeySlot (KEYPAD_ID_5); +} +void +QtGUIMainWindow::pressedKey6 (void) { + pressedKeySlot (KEYPAD_ID_6); +} +void +QtGUIMainWindow::pressedKey7 (void) { + pressedKeySlot (KEYPAD_ID_7); +} +void +QtGUIMainWindow::pressedKey8 (void) { + pressedKeySlot (KEYPAD_ID_8); +} +void +QtGUIMainWindow::pressedKey9 (void) { + pressedKeySlot (KEYPAD_ID_9); +} +void +QtGUIMainWindow::pressedKeyStar (void) { + pressedKeySlot (KEYPAD_ID_STAR); +} +void +QtGUIMainWindow::pressedKeyHash (void) { + pressedKeySlot (KEYPAD_ID_HASH); +} + +/////////////////////////////////////////////////////////////////////////////// +// Protected Methods implementations // +/////////////////////////////////////////////////////////////////////////////// +/** + * Reimplementation of keyPressEvent() to handle the keyboard mapping. + */ +void +QtGUIMainWindow::keyPressEvent(QKeyEvent *e) { + // Misc. key + switch (e->key()) { + case Qt::Key_At: + case Qt::Key_Colon: + case Qt::Key_Period: + case Qt::Key_Comma: + case Qt::Key_Plus: + case Qt::Key_Minus: + case Qt::Key_Slash: + _lcd->appendText(QChar(e->key())); + return; + break; + + case Qt::Key_Backspace: + _lcd->backspace(); + return; + break; + + case Qt::Key_Escape: + hangupLine(); + return; + break; + + case Qt::Key_Return: + case Qt::Key_Enter: + dial(); + return; + break; + + case Qt::Key_F1: + case Qt::Key_F2: + case Qt::Key_F3: + case Qt::Key_F4: + case Qt::Key_F5: + case Qt::Key_F6: + this->toggleLine(e->key() - Qt::Key_F1); + return; + break; + + case Qt::Key_L: + if (e->state() == Qt::ControlButton ) { + _lcd->clear(); + return; + } + break; + case Qt::Key_Q : + if (e->state() == Qt::ControlButton ) { + emit keyPressed(e->key()); + return; + } + break; + case Qt::Key_O : + if (e->state() == Qt::ControlButton ) { + _urlinput->show(); + return; + } + break; + case Qt::Key_C : + if (e->state() == Qt::ControlButton ) { + configuration(); + return; + } + break; + case Qt::Key_D : + if (e->state() == Qt::ControlButton ) { + dtmfKeypad(); + return; + } + break; + case Qt::Key_Space: + if (this->isInNumMode()) { + this->setMode(TEXT_MODE); + } else { + this->setMode(NUM_MODE); + } + return; + break; + + case Qt::Key_Alt: + case Qt::Key_CapsLock: + case Qt::Key_Shift: + case Qt::Key_Tab: + case Qt::Key_Control: + return; + break; + + default: + break; + } + + if (QChar(e->key()).isDigit() ) { + // Numeric _keypad + if (e->key() == Qt::Key_0) { + pressedKeySlot(KEYPAD_ID_0); + } else { + pressedKeySlot(e->key() - Qt::Key_0 - 1); + } + } + + // Handle * and # too. + else if ((e->key() == Qt::Key_Asterisk) + or (e->key() == Qt::Key_NumberSign)) { + (e->key() == Qt::Key_Asterisk) ? + pressedKeySlot(KEYPAD_ID_STAR) + : pressedKeySlot(KEYPAD_ID_HASH); + } + + // If letter _keypad and numeric mode, display digit. + else if (QChar(e->key()).isLetter() && this->isInNumMode() ) { + pressedKeySlot( + (NumericKeypadTools::keyToNumber(e->key())- Qt::Key_0) - 1); + } + + // If letter _keypad and text mode, display letter. + else if (QChar(e->key()).isLetter() && this->isInTextMode()) { + _lcd->appendText(QChar(e->key()).lower()); + } +} + +// EOF diff --git a/src/gui/qt/qtGUImainwindow.h b/src/gui/qt/qtGUImainwindow.h new file mode 100644 index 0000000000000000000000000000000000000000..8726377c0e748ba6fd231ed434ba4658afdac54a --- /dev/null +++ b/src/gui/qt/qtGUImainwindow.h @@ -0,0 +1,364 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __QT_GUI_MAIN_WINDOW_H__ +#define __QT_GUI_MAIN_WINDOW_H__ + +#include <qbitmap.h> +#include <qimage.h> +#include <qdragobject.h> +#include <qevent.h> +#include <qpixmap.h> +#include <qpopupmenu.h> +#include <qpushbutton.h> +#include <qsettings.h> +#include <qthread.h> +#include <qwidget.h> + +#include "../../call.h" +#include "../../configuration.h" +#include "../../manager.h" +#include "../../sipvoiplink.h" +#include "../../skin.h" +#include "../guiframework.h" +#include "../../audio/dtmf.h" +#include "configurationpanelui.h" +#include "jpushbutton.h" +#include "mydisplay.h" +#include "numerickeypad.h" +#include "point.h" +#include "phoneline.h" +#include "transqwidget.h" +#include "trayicon.h" +#include "url_inputui.h" +#include "vector.h" +#include "volumecontrol.h" + +#define MAIN_INITIAL_POSITION 20 +#define TEXT_MODE 0 +#define NUM_MODE 1 + +/////////////////////////////////////////////////////////////////////////////// +// Tray Icon class +/////////////////////////////////////////////////////////////////////////////// +class MyTrayIcon : public TrayIcon +{ + Q_OBJECT +public: + MyTrayIcon(const QPixmap &, const QString &, QPopupMenu *popup = 0, + QObject *parent = 0, const char *name = 0 ); + ~MyTrayIcon(){}; + + QPopupMenu *menu; + +signals: + void clickedLeft(void); +protected: + void mousePressEvent (QMouseEvent *); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GUI main window +/////////////////////////////////////////////////////////////////////////////// +class QtGUIMainWindow : public TransQWidget, public GuiFramework { + Q_OBJECT +public: + // Default Constructor and destructor + QtGUIMainWindow (QWidget* = 0, const char* = 0,WFlags = 0,Manager * = NULL); + ~QtGUIMainWindow(void); + + QPixmap TabLinePixmap[NUMBER_OF_LINES][NUMBER_OF_STATES]; + + // Reimplementation of virtual functions + virtual int incomingCall (short id); + virtual int peerAnsweredCall (short id); + virtual int peerRingingCall (short id); + virtual int peerHungupCall (short id); + virtual void displayTextMessage (short id, const string& message); + virtual void displayError (const string& error); + virtual void displayStatus (const string& status); + virtual void displayContext (short id); + virtual string getRingtoneFile (void); + virtual void setup (void); + + // Handle IP-phone user actions + int qt_outgoingCall (void); + int qt_hangupCall (short id); + int qt_answerCall (short id); + int qt_onHoldCall (short id); + int qt_offHoldCall (short id); + int qt_transferCall (short id); + void qt_muteOn (short id); + void qt_muteOff (short id); + int qt_refuseCall (short id); + int qt_cancelCall (short id); + + /* + * Return the call corresponding to the id + */ + Call* getCall (short id); + + /* + * Return the phoneline corresponding to the call-id + */ + PhoneLine* getPhoneLine (short id); + + /* + * Accessor of the current line + */ + int getCurrentLine (void); + + /* + * Modifior of the current line + */ + void setCurrentLine (int current); + + /* + * Return elapse for call-timer + */ + int getElapse (void); + + // Public functions + void setMainLCD (void); + QString setPathSkin (void); + + /** + * Sets the corresponding pixmap button according to its state. + * Handle operations between lines (on hold, off hold) when you click on + * a line. + * + * @param line: number of the current line + */ + int toggleLine (int); + + /* + * Functions to handle timer call + */ + void stopCallTimer (short); + void startCallTimer (short); + + void stopTimerMessage (void); + void dialTone (bool); + + inline void setChooseLine (bool b) { _chooseLine = b; } + inline bool getChooseLine (void) { return _chooseLine; } + + inline void setChosenLine (int line) { _chosenLine = line; } + inline int getChosenLine (void) { return _chosenLine; } + + inline void setTransfer (bool b) { _transfer = b; } + inline bool getTransfer (void) {return _transfer; } + +signals: + void keyPressed (int); + +public slots: + void dial (void); + void blinkRingSlot (void); + void blinkLineSlot (void); + void blinkMessageSlot (void); + void qt_quitApplication (void); + void pressedKeySlot (int); + void stripSlot (void); + void addressBook (void); + void dtmfKeypad (void); + void configuration (void); + void hangupLine (void); + void button_mute (void); + void button_line0 (void); + void button_line1 (void); + void button_line2 (void); + void button_line3 (void); + void button_line4 (void); + void button_line5 (void); + void button_msg (void); + void button_transfer (void); + void button_conf (void); + void clickHandle (void); + void reduceHandle (void); + void save (void); + void applySkin (void); + + void pressedKey0 (void); + void pressedKey1 (void); + void pressedKey2 (void); + void pressedKey3 (void); + void pressedKey4 (void); + void pressedKey5 (void); + void pressedKey6 (void); + void pressedKey7 (void); + void pressedKey8 (void); + void pressedKey9 (void); + void pressedKeyStar (void); + void pressedKeyHash (void); + + void volumeSpkrChanged (int); + void volumeMicChanged (int); + void registerSlot (void); + +protected: + // To handle the key pressed event + void keyPressEvent (QKeyEvent *); + +private: + NumericKeypad* _keypad; + MyDisplay* _lcd; + QTimer* _blinkTimer; + URL_Input* _urlinput; + ConfigurationPanel* _panel; + Manager* _callmanager; + QPopupMenu* _mypop; + MyTrayIcon* _trayicon; + DTMF* _key; + PhoneLine* phLines[NUMBER_OF_LINES]; + + JPushButton* phoneKey_msg; + JPushButton* phoneKey_transf; + JPushButton* phoneKey_conf; + JPushButton* phoneKey_line0; + JPushButton* phoneKey_line1; + JPushButton* phoneKey_line2; + JPushButton* phoneKey_line3; + JPushButton* reduce_button; + JPushButton* quit_button; + JPushButton* addr_book_button; + JPushButton* configuration_button; + JPushButton* hangup_button; + JPushButton* dial_button; + JPushButton* mute_button; + JPushButton* dtmf_button; + short* _buf; + // Configuration skin file + Point* pt; + + // For volume buttons + VolumeControl* vol_mic; + VolumeControl* vol_spkr; + Vector* micVolVector; + Vector* spkrVolVector; + int vol_mic_x, + vol_mic_y; + int vol_spkr_x, + vol_spkr_y; + + // To construct ring rect pixmap + QImage imageRing; + QImage imageNoRing; + + QPixmap TabMsgPixmap[NUMBER_OF_STATES]; + int modeType; + bool _apply; + + bool b_dialtone; + + // For numeric keypad + bool _first; + + bool _transfer; + bool _msgVar; + + // For outgoing call, if it occurs on the first free line or on the chosen + // line by the user + bool _chooseLine; + int _chosenLine; + int _freeLine; + inline void setFreeLine(int line) { _freeLine = line; } + inline int getFreeLine(void) { return _freeLine; } + + // Array of incoming calls + int _TabIncomingCalls[NUMBER_OF_LINES]; + + // The current phoneline + int _currentLine; + + bool _dialtone; + + void setMode (int); + bool isInTextMode (void); + bool isInNumMode (void); + void initSkin (void); + void initVolume (void); + void initButtons (void); + void initBlinkTimer (void); + void initSpkrVolumePosition (void); + void initMicVolumePosition (void); + void connections (void); + string ringFile (void); + + int positionOffsetX (void); + void deleteButtons (void); + + /* + * Associate a phoneline number to a call identifiant + * @param identifiant call + * @return phoneline number + */ + int associateCall2Line (short id); + + /** + * Search the busy line number among all lines and different with + * current line number. + * + * @return number of busy line + */ + int busyLineNumber (void); + + /* + * Put in state on-hold the line 'line' + */ + int putOnHoldBusyLine (int line); + + /* + * Return the id of the first line number if incoming call occurs + * Otherwise return -1 + */ + short isThereIncomingCall (void); + + /* + * Return the id of the line number 'line' if an incoming call occurs + * Otherwise return -1 + */ + short isIncomingCall (int line); + + /* + * Return phoneline number according to the identifiant 'id' + * id2line returns -1 if phoneline number is not found + */ + int id2line (short id); + + /* + * Return id according to the phoneline number 'line' + * line2id returns -1 if id is not found + */ + short line2id (int line); + + /* + * Change state and pixmap of the line 'line' according to the state 'state' + */ + void changeLineStatePixmap (int line, line_state state); + + /* + * Handle dial tone + */ + void dialtone (bool var); + +}; + + +#endif // __QT_GUI_MAIN_WIDOW_H__ diff --git a/src/transqwidget.cpp b/src/gui/qt/transqwidget.cpp similarity index 100% rename from src/transqwidget.cpp rename to src/gui/qt/transqwidget.cpp diff --git a/src/transqwidget.h b/src/gui/qt/transqwidget.h similarity index 100% rename from src/transqwidget.h rename to src/gui/qt/transqwidget.h diff --git a/src/trayicon.cpp b/src/gui/qt/trayicon.cpp similarity index 100% rename from src/trayicon.cpp rename to src/gui/qt/trayicon.cpp diff --git a/src/trayicon.h b/src/gui/qt/trayicon.h similarity index 100% rename from src/trayicon.h rename to src/gui/qt/trayicon.h diff --git a/src/gui/qt/trayicon_x11.cpp b/src/gui/qt/trayicon_x11.cpp new file mode 100644 index 0000000000000000000000000000000000000000..053eeda9c4f6baedbd11a19adbdf21a0ec401fcd --- /dev/null +++ b/src/gui/qt/trayicon_x11.cpp @@ -0,0 +1,407 @@ +/* + * trayicon_x11.cpp - X11 trayicon (for use with KDE and GNOME) + * Copyright (C) 2003 Justin Karneges + * GNOME2 Notification Area support: Tomasz Sterna + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "trayicon.h" + +#include<qapplication.h> +#include<qimage.h> +#include<qpixmap.h> +#include<qtooltip.h> +#include<qpainter.h> + +#include<X11/Xlib.h> +#include<X11/Xutil.h> +#include<X11/Xatom.h> + +//#if QT_VERSION < 0x030200 +extern Time qt_x_time; +//#endif + +//---------------------------------------------------------------------------- +// common stuff +//---------------------------------------------------------------------------- + +// for Gnome2 Notification Area +static XErrorHandler old_handler = 0; +static int dock_xerror = 0; +extern "C" int dock_xerrhandler(Display* dpy, XErrorEvent* err) +{ + dock_xerror = err->error_code; + return old_handler(dpy, err); +} + +static void trap_errors() +{ + dock_xerror = 0; + old_handler = XSetErrorHandler(dock_xerrhandler); +} + +static bool untrap_errors() +{ + XSetErrorHandler(old_handler); + return (dock_xerror == 0); +} + +static bool send_message( + Display* dpy, /* display */ + Window w, /* sender (tray icon window) */ + long message, /* message opcode */ + long data1, /* message data 1 */ + long data2, /* message data 2 */ + long data3 /* message data 3 */ +) { + XEvent ev; + + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = w; + ev.xclient.message_type = XInternAtom (dpy, "_NET_SYSTEM_TRAY_OPCODE", False ); + ev.xclient.format = 32; + ev.xclient.data.l[0] = CurrentTime; + ev.xclient.data.l[1] = message; + ev.xclient.data.l[2] = data1; + ev.xclient.data.l[3] = data2; + ev.xclient.data.l[4] = data3; + + trap_errors(); + XSendEvent(dpy, w, False, NoEventMask, &ev); + XSync(dpy, False); + return untrap_errors(); +} + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +//---------------------------------------------------------------------------- +// TrayIcon::TrayIconPrivate +//---------------------------------------------------------------------------- + +class TrayIcon::TrayIconPrivate : public QWidget +{ +public: + TrayIconPrivate(TrayIcon *object, int size); + ~TrayIconPrivate() { } + + virtual void initWM(WId icon); + + virtual void setPixmap(const QPixmap &pm); + + virtual void paintEvent(QPaintEvent *); + virtual void enterEvent(QEvent *); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void closeEvent(QCloseEvent *e); + +private: + TrayIcon *iconObject; + QPixmap pix; + int size; +}; + +TrayIcon::TrayIconPrivate::TrayIconPrivate(TrayIcon *object, int _size) + : QWidget(0, "psidock", WRepaintNoErase) +{ + iconObject = object; + size = _size; + + setFocusPolicy(NoFocus); + setBackgroundMode(X11ParentRelative); + + setMinimumSize(size, size); + setMaximumSize(size, size); +} + +// This base stuff is required by both FreeDesktop specification and WindowMaker +void TrayIcon::TrayIconPrivate::initWM(WId icon) +{ + Display *dsp = x11Display(); + WId leader = winId(); + + // set the class hint + XClassHint classhint; + classhint.res_name = (char*)"psidock"; + classhint.res_class = (char*)"Psi"; + XSetClassHint(dsp, leader, &classhint); + + // set the Window Manager hints + XWMHints *hints; + hints = XGetWMHints(dsp, leader); // init hints + hints->flags = WindowGroupHint | IconWindowHint | StateHint; // set the window group hint + hints->window_group = leader; // set the window hint + hints->initial_state = WithdrawnState; // initial state + hints->icon_window = icon; // in WM, this should be winId() of separate widget + hints->icon_x = 0; + hints->icon_y = 0; + XSetWMHints(dsp, leader, hints); // set the window hints for WM to use. + XFree( hints ); +} + +void TrayIcon::TrayIconPrivate::setPixmap(const QPixmap &pm) +{ + pix = pm; + setIcon(pix); + repaint(); +} + +void TrayIcon::TrayIconPrivate::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.drawPixmap((width() - pix.width())/2, (height() - pix.height())/2, pix); +} + +void TrayIcon::TrayIconPrivate::enterEvent(QEvent *e) +{ + // Taken from KSystemTray.. +//#if QT_VERSION < 0x030200 + //if ( !qApp->focusWidget() ) { + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xfocus.display = qt_xdisplay(); + ev.xfocus.type = FocusIn; + ev.xfocus.window = winId(); + ev.xfocus.mode = NotifyNormal; + ev.xfocus.detail = NotifyAncestor; + Time time = qt_x_time; + qt_x_time = 1; + qApp->x11ProcessEvent( &ev ); + qt_x_time = time; + //} +//#endif + QWidget::enterEvent(e); +} + +void TrayIcon::TrayIconPrivate::mouseMoveEvent(QMouseEvent *e) +{ + QApplication::sendEvent(iconObject, e); +} + +void TrayIcon::TrayIconPrivate::mousePressEvent(QMouseEvent *e) +{ + QApplication::sendEvent(iconObject, e); +} + +void TrayIcon::TrayIconPrivate::mouseReleaseEvent(QMouseEvent *e) +{ + QApplication::sendEvent(iconObject, e); +} + +void TrayIcon::TrayIconPrivate::mouseDoubleClickEvent(QMouseEvent *e) +{ + QApplication::sendEvent(iconObject, e); +} + +void TrayIcon::TrayIconPrivate::closeEvent(QCloseEvent *e) +{ + iconObject->gotCloseEvent(); + e->accept(); +} + +//---------------------------------------------------------------------------- +// TrayIconFreeDesktop +//---------------------------------------------------------------------------- + +class TrayIconFreeDesktop : public TrayIcon::TrayIconPrivate +{ +public: + TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm); +protected: + virtual bool x11Event(XEvent*); +}; + +TrayIconFreeDesktop::TrayIconFreeDesktop(TrayIcon *object, const QPixmap &pm) + : TrayIconPrivate(object, 22) +{ + initWM( winId() ); + + // initialize NetWM + Display *dsp = x11Display(); + + // dock the widget (adapted from SIM-ICQ) + Screen *screen = XDefaultScreenOfDisplay(dsp); // get the screen + int screen_id = XScreenNumberOfScreen(screen); // and it's number + + // tell X that we want to see ClientMessage and Deleted events, which + // are picked up by QApplication::x11EventFilter + Window root_window = QApplication::desktop()->winId(); + XWindowAttributes attr; + + XGetWindowAttributes(dsp, root_window, &attr); + XSelectInput(dsp, root_window, attr.your_event_mask | StructureNotifyMask); + + char buf[32]; + snprintf(buf, sizeof(buf), "_NET_SYSTEM_TRAY_S%d", screen_id); + Atom selection_atom = XInternAtom(dsp, buf, false); + XGrabServer(dsp); + Window manager_window = XGetSelectionOwner(dsp, selection_atom); + if ( manager_window != None ) + XSelectInput(dsp, manager_window, StructureNotifyMask); + XUngrabServer(dsp); + XFlush(dsp); + + if ( manager_window != None ) + send_message(dsp, manager_window, SYSTEM_TRAY_REQUEST_DOCK, winId(), 0, 0); + else + { + object->hide(); + return; + } + + // some KDE mumbo-jumbo... why is it there? anybody? + Atom kwm_dockwindow_atom = XInternAtom(dsp, "KWM_DOCKWINDOW", false); + Atom kde_net_system_tray_window_for_atom = XInternAtom(dsp, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", false); + + long data = 0; + XChangeProperty(dsp, winId(), kwm_dockwindow_atom, kwm_dockwindow_atom, 32, PropModeReplace, (uchar*)&data, 1); + XChangeProperty(dsp, winId(), kde_net_system_tray_window_for_atom, XA_WINDOW, 32, PropModeReplace, (uchar*)&data, 1); + + setPixmap(pm); +} + +bool TrayIconFreeDesktop::x11Event(XEvent *ev) +{ + switch(ev->type) + { + case ReparentNotify: + show(); + + } + return false; +} + +//---------------------------------------------------------------------------- +// TrayIconWindowMaker +//---------------------------------------------------------------------------- + +class TrayIconWharf : public TrayIcon::TrayIconPrivate +{ +public: + TrayIconWharf(TrayIcon *object, const QPixmap &pm) + : TrayIconPrivate(object, 44) + { + // set the class hint + XClassHint classhint; + classhint.res_name = (char*)"psidock-wharf"; + classhint.res_class = (char*)"Psi"; + XSetClassHint(x11Display(), winId(), &classhint); + + setPixmap(pm); + } + + void setPixmap(const QPixmap &_pm) + { + QPixmap pm; + QImage i = _pm.convertToImage(); + i = i.scale(i.width() * 2, i.height() * 2); + pm.convertFromImage(i); + + TrayIconPrivate::setPixmap(pm); + + // thanks to Robert Spier for this: + // for some reason the repaint() isn't being honored, or isn't for + // the icon. So force one on the widget behind the icon + erase(); + QPaintEvent pe( rect() ); + paintEvent(&pe); + } +}; + +class TrayIconWindowMaker : public TrayIcon::TrayIconPrivate +{ +public: + TrayIconWindowMaker(TrayIcon *object, const QPixmap &pm); + ~TrayIconWindowMaker(); + + void setPixmap(const QPixmap &pm); + +private: + TrayIconWharf *wharf; +}; + +TrayIconWindowMaker::TrayIconWindowMaker(TrayIcon *object, const QPixmap &pm) + : TrayIconPrivate(object, 32) +{ + wharf = new TrayIconWharf(object, pm); + + initWM( wharf->winId() ); +} + +TrayIconWindowMaker::~TrayIconWindowMaker() +{ + delete wharf; +} + +void TrayIconWindowMaker::setPixmap(const QPixmap &pm) +{ + wharf->setPixmap(pm); +} + +//---------------------------------------------------------------------------- +// TrayIcon +//---------------------------------------------------------------------------- + +void TrayIcon::sysInstall() +{ + if ( d ) + return; + + if ( v_isWMDock ) + d = (TrayIconPrivate *)(new TrayIconWindowMaker(this, pm)); + else + d = (TrayIconPrivate *)(new TrayIconFreeDesktop(this, pm)); + + sysUpdateToolTip(); + + if ( v_isWMDock ) + d->show(); +} + +void TrayIcon::sysRemove() +{ + if ( !d ) + return; + + delete d; + d = 0; +} + +void TrayIcon::sysUpdateIcon() +{ + if ( !d ) + return; + + QPixmap pix = pm; + d->setPixmap(pix); +} + +void TrayIcon::sysUpdateToolTip() +{ + if ( !d ) + return; + + if ( tip.isEmpty() ) + QToolTip::remove(d); + else + QToolTip::add(d, tip); +} + diff --git a/src/url_input.ui b/src/gui/qt/url_input.ui similarity index 100% rename from src/url_input.ui rename to src/gui/qt/url_input.ui diff --git a/src/url_input.ui.h b/src/gui/qt/url_input.ui.h similarity index 100% rename from src/url_input.ui.h rename to src/gui/qt/url_input.ui.h diff --git a/src/url_inputui.cpp b/src/gui/qt/url_inputui.cpp similarity index 94% rename from src/url_inputui.cpp rename to src/gui/qt/url_inputui.cpp index 946dba5b55eaed847024251e7826aed34253cd86..179f1048c6ed4ed06e089f4eacd5f18561fc0489 100644 --- a/src/url_inputui.cpp +++ b/src/gui/qt/url_inputui.cpp @@ -1,13 +1,13 @@ /**************************************************************************** -** Form implementation generated from reading ui file 'url_input.ui' +** Form implementation generated from reading ui file 'gui/qt/url_input.ui' ** -** Created: Wed Apr 27 10:37:33 2005 +** Created: Fri May 20 14:27:25 2005 ** by: The User Interface Compiler ($Id$) ** ** WARNING! All changes made in this file will be lost! ****************************************************************************/ -#include "url_inputui.h" +#include "gui/qt/url_inputui.h" #include <qvariant.h> #include <qpushbutton.h> diff --git a/src/url_inputui.h b/src/gui/qt/url_inputui.h similarity index 88% rename from src/url_inputui.h rename to src/gui/qt/url_inputui.h index 0a9011232d41f3edc843418887f86abc1d748d57..26c7c91f2a780b9618744ef7ed71846fb958436b 100644 --- a/src/url_inputui.h +++ b/src/gui/qt/url_inputui.h @@ -1,7 +1,7 @@ /**************************************************************************** -** Form interface generated from reading ui file 'url_input.ui' +** Form interface generated from reading ui file 'gui/qt/url_input.ui' ** -** Created: Wed Apr 27 10:37:33 2005 +** Created: Fri May 20 14:27:25 2005 ** by: The User Interface Compiler ($Id$) ** ** WARNING! All changes made in this file will be lost! diff --git a/src/vector.cpp b/src/gui/qt/vector.cpp similarity index 100% rename from src/vector.cpp rename to src/gui/qt/vector.cpp diff --git a/src/vector.h b/src/gui/qt/vector.h similarity index 100% rename from src/vector.h rename to src/gui/qt/vector.h diff --git a/src/volumecontrol.cpp b/src/gui/qt/volumecontrol.cpp similarity index 99% rename from src/volumecontrol.cpp rename to src/gui/qt/volumecontrol.cpp index a9def147053f306eea8fe032349e0f17a66a701c..be1367d0a2742842ec444dfa0b650fce9ffa32a6 100644 --- a/src/volumecontrol.cpp +++ b/src/gui/qt/volumecontrol.cpp @@ -20,8 +20,8 @@ #include <qapplication.h> #include "qtGUImainwindow.h" -#include "skin.h" #include "volumecontrol.h" +#include "../../skin.h" VolumeControl::VolumeControl (QWidget *parent, const char *name, const char* pixname, Vector *v) : JPushButton(parent, name, pixname) { diff --git a/src/volumecontrol.h b/src/gui/qt/volumecontrol.h similarity index 99% rename from src/volumecontrol.h rename to src/gui/qt/volumecontrol.h index 109ed4a01837d57fa8ff96f7c2dffb7fa779a412..c59e859228d93816f2f0edb1bda714eb592383b8 100644 --- a/src/volumecontrol.h +++ b/src/gui/qt/volumecontrol.h @@ -35,6 +35,7 @@ public: signals: void setVolumeValue (int); private: + void mouseMoveEvent (QMouseEvent*); void mousePressEvent (QMouseEvent*); int offset (int); diff --git a/src/main.cpp b/src/main.cpp index ae517e3d628db7faa818f95f15fbd61b312ce221..e38ae9bf5cf5777c968515cf1407b3b5ff1b1e49 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -16,90 +16,102 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "configurationtree.h" #include <getopt.h> - +#include "user_cfg.h" +#ifdef QT_GUI #include <qapplication.h> -#include <qmessagebox.h> -#include <qtranslator.h> -#include <qwidget.h> +#include "gui/qt/qtGUImainwindow.h" +#endif +#include "configuration.h" +#include "configurationtree.h" #include "manager.h" -#include "mydisplay.h" -#include "numerickeypad.h" -#include "skin.h" -#include "qtGUImainwindow.h" -void OptionProcess (int argc,char **argv) ; -QString *pOption = NULL; - +int OptionProcess (int argc,char **argv, Manager* manager); int main (int argc, char **argv) { - QApplication a(argc, argv); - Manager *manager; - + Manager* manager; Config::setTree(new ConfigurationTree()); - - OptionProcess (argc,argv); - manager = new Manager(pOption); -/* - if ( pOption ) - manager = new Manager(pOption); - else - manager = new Manager(new QString()); -*/ - -#if 0 - QTranslator translator (0); - translator.load("app_fr.qm", "."); - a.installTranslator (&translator); -#endif - - a.setMainWidget(manager->gui()); - return a.exec(); + manager = new Manager(); + + // Faire partir la gui selon l'option choisie + QApplication a(argc, argv); + QtGUIMainWindow *qtgui = new QtGUIMainWindow (0, 0 , + Qt::WDestructiveClose | + Qt::WStyle_Customize | + Qt::WStyle_NoBorder, + manager); + manager->setGui(qtgui); + manager->init(); + + a.setMainWidget(qtgui); + return a.exec(); +// int ret = OptionProcess (argc,argv, manager); +// return ret; } - -void OptionProcess (int argc,char **argv) { -int c; - - while (1) { - int option_index = 0; - static struct option long_options[] = - { - {"phonenumber", 1, 0, 'p'}, - {"stun", 1, 0, 's'}, - {"verbose", 0, 0, 'v'}, - {"help", 0, 0, 'h'}, - {0, 0, 0, 0} - }; - - c = getopt_long (argc, argv, "p:s:vh", long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'v': - break; - case 'p': - printf("Phone number to call : %s\n",optarg); - pOption = new QString(optarg); - break; - case 's': - break; - case '?': - case 'h': - break; - default: - printf ("?? caractère de code 0%o ??\n", c); - } - } +int OptionProcess (int argc,char **argv, Manager* manager) +{ + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = + { + {"ui", 1, 0, 'i'}, + {"phonenumber", 1, 0, 'p'}, + {"stun", 1, 0, 's'}, + {"verbose", 0, 0, 'v'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "i:p:s:vh", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'i': + { + string optStr(optarg); + if (optStr.compare("qt") == 0) { +#ifdef QT_GUI + QApplication a(argc, argv); + QtGUIMainWindow *qtgui = new QtGUIMainWindow (0, 0 , + Qt::WDestructiveClose | + Qt::WStyle_Customize | + Qt::WStyle_NoBorder, + manager); + cout << "a.setMainWidget(qtgui);" << endl; + a.setMainWidget(qtgui); + return a.exec(); +#endif + } else if (optStr.compare("text")) { + } else { + } + } + break; + case 'v': + break; + case 'p': + cout << "Phone number to call : " << optarg << endl; + break; + case 's': + break; + case '?': + case 'h': + break; + default: + cout << "Option " << c << "doesn't exist" <<endl; + break; + } + } + return 0; } - diff --git a/src/manager.cpp b/src/manager.cpp index a9021f5a3f488b9dcbdb799e4253c2a0f9ed883a..2d0cb3ded2eecc7cb3a5b7efd35b69f0ebae0489 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -16,649 +16,717 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + #include <errno.h> -#include <stdlib.h> -#include <string.h> +#include <time.h> + +// For using inet_ntoa() +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + #include <sys/types.h> #include <sys/stat.h> - -#include <cstdio> +#include <cc++/thread.h> +#include <cc++/file.h> #include <cstdlib> -#include <ccrtp/rtp.h> - -#include <qapplication.h> -#include <qhostaddress.h> -#include <qwidget.h> +#include <iostream> +#include <fstream> +#include <string> +#include <vector> -#include "../stund/udp.h" -#include "../stund/stun.h" -#include "audiodriversoss.h" +#include "user_cfg.h" +#include "audio/audiocodec.h" +#include "audio/codecDescriptor.h" +#include "audio/tonegenerator.h" #ifdef ALSA -#include "audiodriversalsa.h" +#include "audio/audiodriversalsa.h" #endif +#ifdef OSS +#include "audio/audiodriversoss.h" +#endif + +#include "call.h" #include "configuration.h" #include "configurationtree.h" -#include "global.h" #include "manager.h" -#include "audiortp.h" -#include "sip.h" -#include "qtGUImainwindow.h" -#include "error.h" +#include "sipvoiplink.h" +#include "skin.h" +#include "voIPLink.h" +#include "../stund/udp.h" -#include <string> using namespace std; - -Manager::Manager (QString *Dc = NULL) { - DirectCall = Dc; - for (int i = 0; i < NUMBER_OF_LINES; i++) { - phLines[i] = new PhoneLine (); - } - - exist = createSettingsPath(); - - phonegui = new QtGUIMainWindow (0, 0 , - Qt::WDestructiveClose | - Qt::WStyle_Customize | - Qt::WStyle_NoBorder,this); - - error = new Error(this); - selectAudioDriver(); - sip = new SIP(this); - tone = new ToneGenerator(this); - audioRTP = new AudioRtp(this); +using namespace ost; + +Manager::Manager (void) +{ + // initialize random generator + srand (time(NULL)); + + // Init private variables + _callVector = new CallVector(); + _voIPLinkVector = new VoIPLinkVector(); + _error = new Error(this); + _tone = new ToneGenerator(this); + + // Set a sip voip link by default + _voIPLinkVector->push_back(new SipVoIPLink(DFT_VOIP_LINK, this)); + _nCalls = 0; + _nCodecs = 0; + _currentCallId = 0; + _startTime = 0; + _endTime = 0; + _path = ""; + _tonezone = false; + _congestion = false; + _ringback = false; + _ringback = false; + _useAlsa = false; + _exist = 0; - sip_init(); - - // Init variables - b_ringing = false; - b_ringtone = false; - b_congestion = false; - mute = false; - - if (DirectCall) { - qWarning ("Direct call....."); - gui()->lcd->textBuffer = DirectCall ; - gui()->dial(); - } - - if (!exist){ - // If config file $HOME/.PROGNAME/PROGNAMErc doesn't exist, - // show configuration panel - gui()->configuration(); + initConfigFile(); + _exist = createSettingsPath(); + if (_exist == 0) { + _debug("Cannot create config file in your home directory\n"); } - - initVolume (); - } -Manager::~Manager (void) { - delete phonegui; - delete sip; - delete audioRTP; - delete audiodriver; -#ifdef ALSA - delete audiodriverReadAlsa; -#endif - delete tone; - delete[] phLines; -} +Manager::~Manager (void) +{ + delete _callVector; + delete _voIPLinkVector; + delete _error; + delete _tone; +} -/** - * Create .PROGNAME directory in home user and create configuration tree from - * the settings file if this file exists. - * - * @return true if config-file exists or false if not. - */ -bool -Manager::createSettingsPath (void) { - // - bool exist = true; - char * buffer; - // Get variable $HOME - buffer = getenv ("HOME"); - path = string(buffer); - path = path + "/." + PROGNAME; - - if (mkdir (path.data(), 0755) != 0) { - // If directory creation failed - if (errno != EEXIST) { - printf ("Cannot create directory: %s\n", strerror(errno)); - } - } - - // Load user's config - path = path + "/" + PROGNAME + "rc"; - if (Config::tree()->populateFromFile(path.data()) == 0){ - exist = false; - } - return exist; -} - -/** - * Call audio driver constructor according to the selected driver in setup - */ void -Manager::selectAudioDriver (void) { - if (Config::geti("Audio", "Drivers.driverName") == OSS_DRIVER) { - useAlsa = false; - this->audiodriver = new AudioDriversOSS (AudioDrivers::ReadWrite, error); - } - if (Config::geti("Audio", "Drivers.driverName") == ALSA_DRIVER) { -#ifdef ALSA - useAlsa = true; - this->audiodriver = new AudioDriversALSA (AudioDrivers::WriteOnly, error); - this->audiodriverReadAlsa = new AudioDriversALSA (AudioDrivers::ReadOnly, error); -#endif +Manager::init (void) +{ + if (_exist == 2) { + // If config-file doesn't exist, launch configuration setup + _gui->setup(); } + initAudioCodec(); + selectAudioDriver(); + _voIPLinkVector->at(DFT_VOIP_LINK)->init(); + if (get_config_fields_int(SIGNALISATION, AUTO_REGISTER) == YES and + _exist == 1) { + registerVoIPLink(); + } } void -Manager::initVolume (void) { - spkr_volume = gui()->spkrVolVector->Y() - gui()->vol_spkr_y; - mic_volume = gui()->micVolVector->Y() - gui()->vol_mic_y; +Manager::setGui (GuiFramework* gui) +{ + _gui = gui; } -/** - * Init the SIP stack - */ -void -Manager::sip_init (void) { - if ( sip->initSIP () != -1) { - sip->initRtpmapCodec (); - } - - if (Config::getb("Signalisations", "SIP.autoregister")) { - // Register to the known proxies if available - if (Config::gets("Signalisations", "SIP.password").length() > 0) { - sip->setRegister (); - } else { - if (exist) - error->errorName(PASSWD_FIELD_EMPTY, NULL); - } - } +ToneGenerator* +Manager::getTonegenerator (void) +{ + return _tone; } -void -Manager::quitLibrary (void) { - sip->quitSIP(); +Error* +Manager::error (void) +{ + return _error; } -int -Manager::outgoingNewCall (void) { - return sip->outgoingInvite(); +unsigned int +Manager::getNumberOfCalls (void) +{ + return _nCalls; } -bool -Manager::ringing (void) { - return this->b_ringing; +void +Manager::setNumberOfCalls (unsigned int nCalls) +{ + _nCalls = nCalls; } -// When IP-phone user receives a call -void -Manager::ring (bool var) { - if (sip->getNumberPendingCalls() != 1 and tonezone == true - and var == false) { - // If more than one line is ringing - for (int i = 0; i < NUMBER_OF_LINES; i++) { - if (i != getCurrentLineNumber() and phLines[i]->getbRinging()) { - tonezone = false; - tone->playRingtone ((gui()->getRingFile()).ascii()); - } - } - } - - if (this->b_ringing != var) { - this->b_ringing = var; - } - - tonezone = ringing(); +short +Manager::getCurrentCallId (void) +{ + return _currentCallId; +} - if (sip->getNumberPendingCalls() == 1) { - // If just one line is ringing - tone->playRingtone ((gui()->getRingFile()).ascii()); - } +void +Manager::setCurrentCallId (short currentCallId) +{ + _currentCallId = currentCallId; } -// When IP-phone user makes call -void -Manager::ringTone (bool var) { - if (this->b_ringtone != var) { - this->b_ringtone = var; - } - tonezone = var; - tone->toneHandle(ZT_TONE_RINGTONE); +CallVector* +Manager::getCallVector (void) +{ + return _callVector; } -void -Manager::congestion (bool var) { - if (error->getError() == 0) { - if (this->b_congestion != var) { - this->b_congestion = var; +Call* +Manager::getCall (short id) +{ + if (id > 0 and _callVector->size() > 0) { + for (unsigned int i = 0; i < _nCalls; i++) { + if (_callVector->at(i)->getId() == id) { + return _callVector->at(i); + } } - tonezone = var; - tone->toneHandle(ZT_TONE_CONGESTION); + return NULL; } else { - error->errorName(DEVICE_NOT_OPEN, NULL); + return NULL; } } + +unsigned int +Manager::getNumberOfCodecs (void) +{ + return _nCodecs; +} + void -Manager::notificationIncomingCall (void) { - short *buffer = new short[SAMPLING_RATE]; - - tone->generateSin(440, 0, AMPLITUDE, SAMPLING_RATE, buffer); - - audiodriver->audio_buf.resize(SAMPLING_RATE/2); - audiodriver->audio_buf.setData(buffer, getSpkrVolume()); - delete[] buffer; +Manager::setNumberOfCodecs (unsigned int nb_codec) +{ + _nCodecs = nb_codec; } -#if 0 -bool -Manager::getCallInProgress (void) { - return gui()->callinprogress; + +VoIPLinkVector* +Manager::getVoIPLinkVector (void) +{ + return _voIPLinkVector; } -void -Manager::setCallInProgress (bool inprogress) { - gui()->callinprogress = inprogress; +CodecDescriptorVector* +Manager::getCodecDescVector (void) +{ + return _codecDescVector; } -#endif -bool -Manager::transferedCall(void) { - return gui()->transfer; +void +Manager::pushBackNewCall (short id, enum CallType type) +{ + Call* call = new Call(this, id, type, _voIPLinkVector->at(DFT_VOIP_LINK)); + // Set the wanted voip-link (first of the list) + _debug("new Call @ 0X%d\n", call); + _callVector->push_back(call); } -/** - * Handle IP_Phone user's actions - * - * @param lineNumber: line where occurs the action - * @param action: type of action - */ void -Manager::actionHandle (int lineNumber, int action) { - switch (action) { - case REFUSE_CALL: - sip->manageActions (lineNumber, REFUSE_CALL); - this->ring(false); - phLines[lineNumber]->setbRinging(false); - gui()->lcd->setStatus(REFUSED_CALL_STATUS); - break; - - case ANSWER_CALL: - // TODO - // Stopper l'etat "ringing" du main (audio, signalisation, gui) - this->ring(false); - sip->manageActions (lineNumber, ANSWER_CALL); - phLines[lineNumber]->setbRinging(false); - gui()->lcd->setStatus(CONNECTED_STATUS); - gui()->startCallTimer(lineNumber); - break; - - case CLOSE_CALL: - // If the call is active TODO: or is ringing for the callee - if (sip->call[lineNumber] != NULL) { - // stop call timer - gui()->stopCallTimer(lineNumber); - sip->manageActions (lineNumber, CLOSE_CALL); - sip->notUsedLine = -1; - } - break; - - case ONHOLD_CALL: - if (sip->call[lineNumber] != NULL) { - sip->manageActions (lineNumber, ONHOLD_CALL); - } - break; - - case OFFHOLD_CALL: - if (sip->call[lineNumber] != NULL) { - sip->manageActions (lineNumber, OFFHOLD_CALL); - } - break; - - case TRANSFER_CALL: - if (sip->call[lineNumber] != NULL) { - sip->manageActions (lineNumber, TRANSFER_CALL); - } - break; - - case CANCEL_CALL: - if (sip->call[lineNumber] != NULL) { - sip->manageActions (lineNumber, CANCEL_CALL); +Manager::deleteCall (short id) +{ + unsigned int i = 0; + while (i < _callVector->size()) { + if (_callVector->at(i)->getId() == id) { + _callVector->erase(_callVector->begin()+i); + // delete getCall(id); + return; + } else { + i++; } - break; + } +} - default: - break; +/////////////////////////////////////////////////////////////////////////////// +// Management of events' IP-phone user +/////////////////////////////////////////////////////////////////////////////// +int +Manager::outgoingCall (const string& to) +{ + short id; + Call* call; + + id = generateNewCallId(); + pushBackNewCall(id, Outgoing); + + _debug("\nOutgoing Call with identifiant %d\n", id); + call = getCall(id); + + call->setStatus(string(TRYING_STATUS)); + call->setState(Progressing); + if (call->outgoingCall(to) == 0) { + return id; + } else { + return 0; } } +int +Manager::hangupCall (short id) +{ + Call* call; + + call = getCall(id); + call->setStatus(string(HUNGUP_STATUS)); + call->setState(Hungup); + getCall(id)->hangup(); + _mutex.enterMutex(); + _nCalls -= 1; + _mutex.leaveMutex(); + deleteCall(id); + return 1; +} + +int +Manager::answerCall (short id) +{ + Call* call; + + call = getCall(id); + call->setStatus(string(CONNECTED_STATUS)); + call->setState(Answered); + call->answer(); + ringtone(false); + return 1; +} + +int +Manager::onHoldCall (short id) +{ + Call* call; + call = getCall(id); + call->setStatus(string(ONHOLD_STATUS)); + call->setState(OnHold); + call->onHold(); + return 1; +} + +int +Manager::offHoldCall (short id) +{ + Call* call; + call = getCall(id); + call->setStatus(string(CONNECTED_STATUS)); + call->setState(OffHold); + call->offHold(); + return 1; +} + +int +Manager::transferCall (short id, const string& to) +{ + Call* call; + call = getCall(id); + call->setStatus(string(TRANSFER_STATUS)); + call->setState(Transfered); + call->transfer(to); + return 1; +} + +int +Manager::muteOn (short id) +{ + Call* call; + call = getCall(id); + call->setStatus(string(MUTE_ON_STATUS)); + call->setState(MuteOn); + call->muteOn(); + return 1; +} + +int +Manager::muteOff (short id) +{ + Call* call; + call = getCall(id); + call->setStatus(string(CONNECTED_STATUS)); + call->setState(MuteOff); + call->muteOff(); + return 1; +} + +int +Manager::refuseCall (short id) +{ + Call *call; + call = getCall(id); + call->setStatus(string(HUNGUP_STATUS)); + call->setState(Refused); + call->refuse(); + ringtone(false); + delete call; + return 1; +} + +int +Manager::cancelCall (short id) +{ + Call *call; + call = getCall(id); + call->setStatus(string(LOGGED_IN_STATUS)); + call->setState(Cancelled); + call->cancel(); + ringback(false); + delete call; + return 1; +} + int -Manager::newCallLineNumber (void) { - return sip->notUsedLine; +Manager::saveConfig (void) +{ + return (Config::tree()->saveToFile(_path.data()) ? 1 : 0); } -bool -Manager::isRingingLine (int line) { - if (line == this->newCallLineNumber()) { - return true; - } - return false; +int +Manager::registerVoIPLink (void) +{ + _voIPLinkVector->at(DFT_VOIP_LINK)->setRegister(); + return 1; } -bool -Manager::isNotUsedLine (int line) { - for (int k = 0; k < NUMBER_OF_LINES; k++) { - if (line == k) { - if (sip->call[line] == NULL) { - return true; - } - } +int +Manager::quitApplication (void) +{ + // Quit VoIP-link library + _voIPLinkVector->at(DFT_VOIP_LINK)->quit(); + if (saveConfig()) { + return 1; + } else { + return 0; } - return false; + Config::deleteTree(); +} + +int +Manager::sendTextMessage (short id, const string& message) +{ + return 1; +} + +int +Manager::accessToDirectory (void) +{ + return 1; +} + +int +Manager::sendDtmf (short id, char code) +{ + int sendType = get_config_fields_int(SIGNALISATION, SEND_DTMF_AS); + + switch (sendType) { + // SIP INFO + case 0: + _voIPLinkVector->at(DFT_VOIP_LINK)->carryingDTMFdigits(id, code); + return 1; + break; + + // Audio way + case 1: + return 1; + break; + + // rfc 2833 + case 2: + return 1; + break; + + default: + return -1; + break; + } } -bool -Manager::isUsedLine (int line) { - for (int k = 0; k < NUMBER_OF_LINES; k++) { - if (line == k) { - if (sip->call[line] != NULL) { - return true; - } - } - } - return false; +/////////////////////////////////////////////////////////////////////////////// +// Management of event peer IP-phone +/////////////////////////////////////////////////////////////////////////////// + +int +Manager::incomingCall (short id) +{ + Call* call; + call = getCall(id); + call->setType(Incoming); + call->setStatus(string(RINGING_STATUS)); + call->setState(Progressing); + ringtone(true); + _gui->incomingCall(id); + return 1; +} + +// L'autre personne a repondu +int +Manager::peerAnsweredCall (short id) +{ + Call* call; + + call = getCall(id); + call->setStatus(string(CONNECTED_STATUS)); + call->setState(Answered); + _gui->peerAnsweredCall(id); + ringback(false); + return 1; +} + +int +Manager::peerRingingCall (short id) +{ + Call* call; + + call = getCall(id); + call->setStatus(string(RINGING_STATUS)); + call->setState(Ringing); + _gui->peerRingingCall(id); + ringback(true); + + return 1; } +int +Manager::peerHungupCall (short id) +{ + Call* call; -int -Manager::findLineNumberNotUsed (void) { - return sip->findLineNumberNotUsed(); + call = getCall(id); + call->setStatus(string(HUNGUP_STATUS)); + call->setState(Hungup); + _gui->peerHungupCall(id); + ringback(false); + _mutex.enterMutex(); + _nCalls -= 1; + _mutex.leaveMutex(); + deleteCall(id); + return 1; } -int -Manager::getNumberPendingCalls (void) { - return sip->getNumberPendingCalls(); +void +Manager::displayTextMessage (short id, const string& message) +{ + _gui->displayTextMessage(id, message); } -/** - * Handle the remote callee's events - * - * @param code: event code - * @param reason: event reason - * @param remotetype: event type - */ -void -Manager::handleRemoteEvent (int code, char * reason, int remotetype, int line) { - QString qinfo; - - switch (remotetype) { - // Registration success - case EXOSIP_REGISTRATION_SUCCESS: - gui()->lcd->setStatus(LOGGED_IN_STATUS); - break; - - // Registration failure - case EXOSIP_REGISTRATION_FAILURE: - gui()->lcd->setStatus(REG_FAIL_STATUS); - break; - - // Remote callee answered - case EXOSIP_CALL_ANSWERED: - if (!gui()->transfer) - gui()->lcd->setStatus(CONNECTED_STATUS); - // Start call timer - gui()->startCallTimer(getCurrentLineNumber()); - phLines[getCurrentLineNumber()]->setbDial(false); - break; - - // Remote callee hangup - case EXOSIP_CALL_CLOSED: - if (sip->getNumberPendingCalls() == 0 - or !phLines[line]->isOnHold()) { - // Show HUNGUP_STATUS if there's not pending call - // or line is not onhold - gui()->lcd->clear(QString(HUNGUP_STATUS)); - if (getCurrentLineNumber() != -1 and line != getCurrentLineNumber()) { - // To show current status and phone number - gui()->lcd->appendText(phLines[getCurrentLineNumber()]->text); - gui()->lcd->setStatus(phLines[getCurrentLineNumber()]->status); - } - } - - if (phLines[line]->getbInProgress()) { - this->ring(false); - phLines[line]->setbRinging(false); - // gui()->setFreeStateLine(newCallLineNumber()); - gui()->setFreeStateLine(line); - - //setCallInProgress(false); - phLines[line]->setbInProgress(false); - } else { - if (line == getCurrentLineNumber()) { - // set free line - gui()->setCurrentLineNumber(-1); - } - // Stop call timer - gui()->stopCallTimer(line); - // set state free pixmap line - gui()->setFreeStateLine(line); - } - phLines[line]->setbDial(false); - sip->notUsedLine = -1; - - break; - - // Remote call ringing - case EXOSIP_CALL_RINGING: - gui()->lcd->setStatus(RINGING_STATUS); - phLines[getCurrentLineNumber()]->setbDial(false); - break; - - default: - break; - } - // If dialog is established - if (code == DIALOG_ESTABLISHED) { - gui()->lcd->setStatus(RINGING_STATUS); - // if error code - } else { - if (code == AUTH_REQUIRED) { - gui()->lcd->setStatus(TRYING_STATUS); - } else if (code > 399 and code != AUTH_REQUIRED and - code != REQ_TERMINATED) { - qinfo = QString::number(code, 10) + " " + - QString(reason); - gui()->lcd->setStatus(qinfo); - } else if (0 < code < 400) { - qinfo = QString(reason); - } - } - - // Store the status of current line - if(getCurrentLineNumber() != -1) { -/* if (remotetype == EXOSIP_CALL_NEW) { - theline = line; - } else { - theline = getCurrentLineNumber(); - } - if ((code != REQ_TERMINATED and code != DIALOG_ESTABLISHED) - or line == getCurrentLineNumber()) {*/ - // If the remote-user close its remote call. - // phLines[theline]->status = getStatusRender(); - phLines[getCurrentLineNumber()]->status = getStatusRender(); - // } - } +void +Manager::displayError (const string& error) +{ + _gui->displayStatus(error); } -bool -Manager::tryingState (int line) { - if (phLines[line]->status == TRYING_STATUS) { - return true; - } else { - return false; - } +void +Manager::displayStatus (const string& status) +{ + _gui->displayStatus(status); } + void -Manager::startDialTone (void) { - gui()->dialTone(true); +Manager::congestion (bool var) { + if (_error->getError() == 0) { + if (_congestion != var) { + _congestion = var; + } + _tonezone = var; + _tone->toneHandle(ZT_TONE_CONGESTION); + } else { + _error->errorName(DEVICE_NOT_OPEN, NULL); + } } -int -Manager::startSound (SipCall *ca) { - return audioRTP->createNewSession(ca); +void +Manager::ringback (bool var) { + if (_ringback != var) { + _ringback = var; + } + _tonezone = var; + _tone->toneHandle(ZT_TONE_RINGTONE); } void -Manager::closeSound (SipCall *ca) { - audioRTP->closeRtpSession (ca); -} +Manager::ringtone (bool var) { -QString -Manager::bufferTextRender (void) { - return QString(*gui()->lcd->textBuffer); + if (getNumberOfCalls() > 1 and _tonezone and var == false) { + // If more than one line is ringing + _tonezone = false; + _tone->playRingtone((_gui->getRingtoneFile()).data()); + } + + if (_ringtone != var) { + _ringtone = var; + } + + _tonezone = var; + if (getNumberOfCalls() == 1) { + // If just one line is ringing + _tone->playRingtone((_gui->getRingtoneFile()).data()); + } } -QString -Manager::getStatusRender (void) { - return (gui()->lcd->getStatus()); +void +Manager::notificationIncomingCall (void) { + short *buffer = new short[SAMPLING_RATE]; + + _tone->generateSin(440, 0, AMPLITUDE, SAMPLING_RATE, buffer); + + audiodriver->audio_buf.resize(SAMPLING_RATE/2); + audiodriver->audio_buf.setData(buffer, 50/*getSpkrVolume()*/); + delete[] buffer; } void -Manager::getInfoStun (StunAddress4& stunSvrAddr) { - StunAddress4 mappedAddr; - - int fd3, fd4; +Manager::getStunInfo (StunAddress4& stunSvrAddr) { + StunAddress4 mappedAddr; + struct in_addr in; + char* addr; + char to[16]; + bzero (to, 16); + + int fd3, fd4; bool ok = stunOpenSocketPair(stunSvrAddr, &mappedAddr, &fd3, &fd4); - if (ok) { - closesocket(fd3); + if (ok) { + closesocket(fd3); closesocket(fd4); - qDebug("Got port pair at %d", mappedAddr.port); - firewallPort = mappedAddr.port; - QHostAddress ha(mappedAddr.addr); - firewallAddr = ha.toString(); - qDebug("address firewall = %s",firewallAddr.ascii()); - } else { - qDebug("Opened a stun socket pair FAILED"); + _debug("Got port pair at %d\n", mappedAddr.port); + _firewallPort = mappedAddr.port; + + // Convert ipv4 address to host byte ordering + in.s_addr = ntohl (mappedAddr.addr); + addr = inet_ntoa(in); + _firewallAddr = string(addr); + _debug("address firewall = %s\n",_firewallAddr.data()); + } else { + _debug("Opened a stun socket pair FAILED\n"); } } -int -Manager::getFirewallPort (void) { - return firewallPort; +bool +Manager::useStun (void) { + if (get_config_fields_int(SIGNALISATION, USE_STUN) == YES) { + return true; + } else { + return false; + } } -void -Manager::setFirewallPort (int port) { - firewallPort = port; -} -QString -Manager::getFirewallAddress (void) { - return firewallAddr; -} +/////////////////////////////////////////////////////////////////////////////// +// Private functions +/////////////////////////////////////////////////////////////////////////////// -/** - * Returns true if the current line replaces another one - */ -bool -Manager::otherLine (void) { - if (gui()->busyNum != -1) { - if (phLines[getCurrentLineNumber()]->status != TRYING_STATUS and - gui()->busyNum != getCurrentLineNumber()) { - return true; - } +short +Manager::generateNewCallId (void) +{ + short random_id = rand(); + + // Check if already a call with this id exists + while (getCall(random_id) != NULL or random_id <= 0) { + random_id = rand(); } - return false; + _mutex.enterMutex(); + _nCalls += 1; + _mutex.leaveMutex(); + // If random_id is not attributed, returns it. + return random_id; } -int -Manager::getCurrentLineNumber (void) { - return gui()->currentLineNumber; -} - -bool -Manager::isChosenLine (void) { - return gui()->choose; +unsigned int +Manager::callVectorSize (void) +{ + return _callVector->size(); } int -Manager::chosenLine (void) { - return gui()->chosenLine; -} - -void -Manager::setChoose (bool b, bool b2) { - gui()->choose = b; - gui()->noChoose = b2; -} +Manager::createSettingsPath (void) { + int exist = 1; + _path = string(HOMEDIR) + "/." + PROGNAME; + + if (mkdir (_path.data(), 0755) != 0) { + // If directory creation failed + if (errno != EEXIST) { + _debug("Cannot create directory: %d\n", strerror(errno)); + return -1; + } + } -bool -Manager::useStun () { - if (Config::geti("Signalisations", "STUN.useStun") == YES) { - return true; - } else { - return false; - } -} + // Load user's config + _path = _path + "/" + PROGNAME + "rc"; -/** - * Handle choice of the DTMF-send-way - * - * @param line: number of the line. - * @param digit: pressed key. - */ -void -Manager::dtmf (int line, char digit) { - int sendType = Config::geti ("Signalisations", "DTMF.sendDTMFas"); + exist = Config::tree()->populateFromFile(_path); - switch (sendType) { - // SIP INFO - case 0: - if (sip->call[line] != NULL) { - sip->carryingDTMFdigits(line, digit); - } - break; - - // Audio way - case 1: - break; - - // rfc 2833 - case 2: - break; - - default: - break; + if (exist == 0){ + // If populateFromFile failed + return 0; + } else if (exist == 2) { + // If file doesn't exist yet + return 2; } + return exist; } void -Manager::errorDisplay (char *error) { - gui()->lcd->clearBuffer(); - gui()->lcd->appendText(error); +Manager::initConfigFile (void) +{ + fill_config_fields_int(SIGNALISATION, VOIP_LINK_ID, DFT_VOIP_LINK); + fill_config_fields_str(SIGNALISATION, FULL_NAME, EMPTY_FIELD); + fill_config_fields_str(SIGNALISATION, USER_PART, EMPTY_FIELD); + fill_config_fields_str(SIGNALISATION, AUTH_USER_NAME, EMPTY_FIELD); + fill_config_fields_str(SIGNALISATION, PASSWORD, EMPTY_FIELD); + fill_config_fields_str(SIGNALISATION, HOST_PART, EMPTY_FIELD); + fill_config_fields_str(SIGNALISATION, PROXY, EMPTY_FIELD); + fill_config_fields_int(SIGNALISATION, AUTO_REGISTER, YES); + fill_config_fields_int(SIGNALISATION, PLAY_TONES, YES); + fill_config_fields_int(SIGNALISATION, PULSE_LENGTH, DFT_PULSE_LENGTH); + fill_config_fields_int(SIGNALISATION, SEND_DTMF_AS, SIP_INFO); + fill_config_fields_str(SIGNALISATION, STUN_SERVER, DFT_STUN_SERVER); + fill_config_fields_int(SIGNALISATION, USE_STUN, NO); + + fill_config_fields_int(AUDIO, DRIVER_NAME, DFT_DRIVER); + fill_config_fields_int(AUDIO, NB_CODEC, DFT_NB_CODEC); + fill_config_fields_str(AUDIO, CODEC1, DFT_CODEC); + fill_config_fields_str(AUDIO, CODEC2, DFT_CODEC); + fill_config_fields_str(AUDIO, CODEC3, DFT_CODEC); + fill_config_fields_str(AUDIO, CODEC4, DFT_CODEC); + fill_config_fields_str(AUDIO, CODEC5, DFT_CODEC); + fill_config_fields_str(AUDIO, RING_CHOICE, DFT_RINGTONE); + fill_config_fields_int(AUDIO, VOLUME_SPKR_X, DFT_VOL_SPKR_X); + fill_config_fields_int(AUDIO, VOLUME_SPKR_Y, DFT_VOL_SPKR_Y); + fill_config_fields_int(AUDIO, VOLUME_MICRO_X, DFT_VOL_MICRO_X); + fill_config_fields_int(AUDIO, VOLUME_MICRO_Y, DFT_VOL_MICRO_Y); + + fill_config_fields_str(PREFERENCES, SKIN_CHOICE, DFT_SKIN); + fill_config_fields_int(PREFERENCES, CONFIRM_QUIT, YES); + fill_config_fields_str(PREFERENCES, ZONE_TONE, DFT_ZONE); + fill_config_fields_int(PREFERENCES, CHECKED_TRAY, NO); + fill_config_fields_str(PREFERENCES, VOICEMAIL_NUM, DFT_VOICEMAIL); } void -Manager::nameDisplay (char *name) { - gui()->lcd->clearBuffer(); - gui()->lcd->appendText(name); +Manager::initAudioCodec (void) +{ + _nCodecs = 3;//get_config_fields_int(AUDIO, NB_CODEC); + _codecDescVector = new CodecDescriptorVector(); + _codecDescVector->push_back(new CodecDescriptor(PAYLOAD_CODEC_ULAW, + CODEC_ULAW)); + _codecDescVector->push_back(new CodecDescriptor(PAYLOAD_CODEC_ALAW, + CODEC_ALAW)); + _codecDescVector->push_back(new CodecDescriptor(PAYLOAD_CODEC_GSM, + CODEC_GSM)); + // TODO: put to 1 when these codec will be implemented +#if 0 + _codecDescVector->push_back(new CodecDescriptor(PAYLOAD_CODEC_ILBC, + CODEC_ILBC)); + _codecDescVector->push_back(new CodecDescriptor(PAYLOAD_CODEC_SPEEX, + CODEC_SPEEX)); +#endif } -void -Manager::spkrSoundVolume (int val) { - spkr_volume = val; -} void -Manager::micSoundVolume (int val) { - mic_volume = val; +Manager::selectAudioDriver (void) +{ + if (get_config_fields_int(AUDIO, DRIVER_NAME) == OSS_DRIVER) { + _useAlsa = false; + audiodriver = new AudioDriversOSS (AudioDrivers::ReadWrite, _error); + } else { + _useAlsa = true; + audiodriver = new AudioDriversALSA (AudioDrivers::WriteOnly, _error); + audiodriverReadAlsa = new AudioDriversALSA (AudioDrivers::ReadOnly, _error); + } } + + + diff --git a/src/manager.h b/src/manager.h index 8d32d97065fff0d29cd62c935693dcd5927a05a0..c4e04e9d0dbb8f08c728d903743b4e2b392bfa47 100644 --- a/src/manager.h +++ b/src/manager.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -20,119 +20,250 @@ #ifndef __MANAGER_H__ #define __MANAGER_H__ -#include "audiodrivers.h" -#include "phoneline.h" +#include <cc++/thread.h> + +#include <string> +#include <vector> + +#include "audio/codecDescriptor.h" +#include "audio/audiodrivers.h" +#include "error.h" +#include "call.h" +#include "user_cfg.h" +#include "voIPLink.h" +#include "gui/guiframework.h" #include "../stund/stun.h" -// Status list -#define LOGGED_IN_STATUS "Logged in" -#define REG_FAIL_STATUS "Registration failure" +using namespace std; +using namespace ost; + +// Status #define CONNECTED_STATUS "Connected" -#define HUNGUP_STATUS "Hung up" +#define LOGGED_IN_STATUS "Logged in" #define RINGING_STATUS "Ringing" -#define TRYING_STATUS "Trying..." -#define REFUSED_CALL_STATUS "Refused call" -#define ENTER_NUMBER_STATUS "Enter Phone Number:" -#define TRANSFER_STATUS "Transfer to:" +#define TRYING_STATUS "Trying ..." +#define HUNGUP_STATUS "Hung up" +#define ONHOLD_STATUS "On hold ..." +#define TRANSFER_STATUS "Transfer to:" +#define MUTE_ON_STATUS "Mute on" +#define ENTER_NUMBER_STATUS "Enter Phone Number:" -#include <string> -using namespace std; +/* + * Define a type for a list of call + */ +typedef vector<Call*, allocator<Call*> > CallVector; -class AudioRtp; -class SIP; -class SipCall; -class ToneGenerator; -class QtGUIMainWindow; -class Error; +/* + * Define a type for a list of VoIPLink + */ +typedef vector<VoIPLink*, allocator<VoIPLink*> > VoIPLinkVector; +/* + * Define a type for a list of CodecDescriptor + */ +typedef vector<CodecDescriptor*, allocator<CodecDescriptor*> > CodecDescriptorVector; + +class GuiFramework; +class ToneGenerator; class Manager { public: - Manager (QString *); + Manager (void); ~Manager (void); - QtGUIMainWindow *phonegui; - SIP *sip; - PhoneLine *phLines[NUMBER_OF_LINES]; - AudioRtp *audioRTP; - - AudioDrivers *audiodriver; #ifdef ALSA - AudioDrivers *audiodriverReadAlsa; -#endif - Error *error; - - bool useAlsa; - ToneGenerator *tone; - QString *DirectCall; // from -p argv - bool mute; - bool tonezone; - std::string path; - - inline - QtGUIMainWindow*gui (void) { return this->phonegui; } - bool ringing (void); - inline - void ring (void) { this->ring(true); } - void ring (bool); - void quitLibrary (void); - int outgoingNewCall (void); - void actionHandle (int, int); - int findLineNumberNotUsed (void); - int getNumberPendingCalls (void); - void handleRemoteEvent (int, char *, int, int = -1); - int startSound (SipCall *); - void closeSound (SipCall *); - void selectAudioDriver (void); - QString bufferTextRender (void); - QString getStatusRender (void); - bool isNotUsedLine (int); - bool isUsedLine (int); - bool isRingingLine (int); - int newCallLineNumber (void); - void getInfoStun (StunAddress4 &); - int getFirewallPort (void); - void setFirewallPort (int); - QString getFirewallAddress (void); - bool otherLine (void); - bool isChosenLine (void); - int chosenLine (void); - void setChoose (bool, bool); - bool useStun (void); - void dtmf (int, char); - int getCurrentLineNumber (void); -#if 0 - bool getCallInProgress (void); - void setCallInProgress (bool); + AudioDrivers* audiodriverReadAlsa; #endif - bool transferedCall (void); - - void ringTone (bool); - void startDialTone (void); - void congestion (bool); - inline bool getbCongestion (void) { return b_congestion; } - void notificationIncomingCall(void); - void errorDisplay (char*); - void nameDisplay (char*); - void spkrSoundVolume (int); - void micSoundVolume (int); - inline int getSpkrVolume (void) { return spkr_volume; } - inline int getMicVolume (void) { return mic_volume; } - bool tryingState (int); + AudioDrivers* audiodriver; + + void init (void); + void setGui (GuiFramework* gui); + ToneGenerator* getTonegenerator(void); + Error* error(void); + + // Accessor to number of calls + unsigned int getNumberOfCalls (void); + // Modifior of number of calls + void setNumberOfCalls (unsigned int nCalls); + + // Accessor to current call id + short getCurrentCallId (void); + // Modifior of current call id + void setCurrentCallId (short currentCallId); + + // Accessor to the Call vector + CallVector* getCallVector (void); + // Accessor to the Call with the id 'id' + Call* getCall (short id); + + unsigned int getNumberOfCodecs (void); + void setNumberOfCodecs (unsigned int nb_codec); + + VoIPLinkVector* getVoIPLinkVector (void); + + CodecDescriptorVector* getCodecDescVector(void); + + inline bool getTonezone (void) { return _tonezone; } + inline void setTonezone (bool b) { _tonezone = b; } + + /* + * Attribute a new random id for a new call + * and check if it's already attributed to existing calls. + * If not exists, returns 'id' otherwise return 0 + */ + short generateNewCallId (void); + + /* + * Add a new call at the end of the CallVector with identifiant 'id' + */ + void pushBackNewCall (short id, enum CallType type); + void deleteCall (short id); + + int outgoingCall (const string& to); + int hangupCall (short id); + int answerCall (short id); + int onHoldCall (short id); + int offHoldCall (short id); + int transferCall (short id, const string& to); + int muteOn (short id); + int muteOff (short id); + int refuseCall (short id); + int cancelCall (short id); + + int saveConfig (void); + int registerVoIPLink (void); + int quitApplication (void); + int sendTextMessage (short id, const string& message); + int accessToDirectory (void); + + /** + * Handle choice of the DTMF-send-way + * + * @param id: callid of the line. + * @param code: pressed key. + */ + int sendDtmf (short id, char code); + + + int incomingCall (short id); + int peerAnsweredCall (short id); + int peerRingingCall (short id); + int peerHungupCall (short id); + void displayTextMessage (short id, const string& message); + void displayError (const string& error); + void displayStatus (const string& status); + + + /* + * Handle audio sounds heard by a caller while they wait for their + * connection to a called party to be completed. + */ + void ringback (bool var); + + void ringtone (bool var); + void congestion (bool var); + void notificationIncomingCall (void); + + /* + * Get information about firewall + * @param stunSvrAddr: stun server + */ + void getStunInfo (StunAddress4& stunSvrAddr); + bool useStun (void); + + inline bool getbCongestion (void) { return _congestion; } + inline bool getbRingback (void) { return _ringback; } + inline bool getbRingtone (void) { return _ringtone; } + inline bool useAlsa (void) { return _useAlsa; } + + inline int getSpkrVolume (void) { return _spkr_volume; } + inline void setSpkrVolume (int spkr_vol) { _spkr_volume = spkr_vol; } + inline int getMicroVolume (void) { return _mic_volume; } + inline void setMicroVolume (int mic_vol) { _mic_volume = mic_vol; } + + inline int getFirewallPort (void) { return _firewallPort; } + inline void setFirewallPort (int port) { _firewallPort = port; } + inline string getFirewallAddress (void) { return _firewallAddr; } private: - bool exist; - bool b_ringing; - bool b_ringtone; - bool b_congestion; - int firewallPort; - QString firewallAddr; - int spkr_volume; - int mic_volume; - - void sip_init (void); - void initVolume (void); - bool createSettingsPath (void); + /* + * Returns the number of calls in the vector + */ + unsigned int callVectorSize (void); + + /** + * Create .PROGNAME directory in home user and create + * configuration tree from the settings file if this file exists. + * + * @return 0 if creating file failed + * 1 if config-file exists + * 2 if file doesn't exist yet. + */ + int createSettingsPath (void); + + /* + * Init default values for the different fields + */ + void initConfigFile (void); + + void initAudioCodec(void); + void selectAudioDriver (void); + +///////////////////// +// Private variables +///////////////////// + ToneGenerator* _tone; + Error* _error; + GuiFramework* _gui; + /* + * Vector of VoIPLink + */ + VoIPLinkVector* _voIPLinkVector; + + /* + * Vector of calls + */ + CallVector* _callVector; + + /* + * Vector of CodecDescriptor + */ + CodecDescriptorVector* _codecDescVector; + + /* + * Mutex to protect access to code section + */ + Mutex _mutex; + + unsigned int _nCalls; + short _currentCallId; + + /* + * For the call timer + */ + unsigned int _startTime; + unsigned int _endTime; + + /* Path of the ConfigFile + */ + string _path; + int _exist; + + unsigned int _nCodecs; + bool _tonezone; + bool _congestion; + bool _ringback; + bool _ringtone; + + bool _useAlsa; + + // To handle volume control + int _spkr_volume; + int _mic_volume; + + // To handle firewall + int _firewallPort; + string _firewallAddr; }; #endif // __MANAGER_H__ diff --git a/src/qtGUImainwindow.cpp b/src/qtGUImainwindow.cpp deleted file mode 100644 index 463a4cb48a17ae347d1b806a7597029ed9049526..0000000000000000000000000000000000000000 --- a/src/qtGUImainwindow.cpp +++ /dev/null @@ -1,1449 +0,0 @@ -/** - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -//////////////////////////////////////////////////////////////////////////////// -// QtGUIMainWindow Implementation // -//////////////////////////////////////////////////////////////////////////////// - -#include "configurationtree.h" -#include <stdio.h> - -#include <qbitmap.h> -#include <qcheckbox.h> -#include <qcombobox.h> -#include <qevent.h> -#include <qinputdialog.h> -#include <qlineedit.h> -#include <qmessagebox.h> -#include <qpushbutton.h> -#include <qregexp.h> -#include <qsettings.h> -#include <qspinbox.h> -#include <qtimer.h> -#include <qtooltip.h> - -#include "audiodrivers.h" -#include "configuration.h" -#include "configurationpanelui.h" -#include "error.h" -#include "global.h" -#include "jpushbutton.h" -#include "manager.h" -#include "numerickeypadtools.h" -#include "point.h" -#include "skin.h" -#include "qtGUImainwindow.h" -#include "vector.h" -#include "volumecontrol.h" - -#define QCHAR_TO_STRIP "-" -#define REG_EXPR "(-|\\(|\\)| )" - - -/////////////////////////////////////////////////////////////////////////////// -// Tray Icon implementation -/////////////////////////////////////////////////////////////////////////////// -MyTrayIcon::MyTrayIcon(const QPixmap &icon, const QString &tooltip, - QPopupMenu *mypop, QObject *parent, const char *name) - : TrayIcon (icon, tooltip, mypop, parent, name) -{ - menu = mypop; -} - -void -MyTrayIcon::mousePressEvent (QMouseEvent *e) -{ - switch ( e->button() ) { - case RightButton: - menu->popup( e->globalPos() ); - e->accept(); - break; - - case LeftButton: - emit clickedLeft(); - break; - - case MidButton: - break; - - default: - break; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// QtGUIMainWindow implementation -/////////////////////////////////////////////////////////////////////////////// - -/** - * Default Constructor - * Init, Connections - */ -QtGUIMainWindow::QtGUIMainWindow (QWidget *parent, const char *name, WFlags f, - Manager *mngr) - : TransQWidget (parent, name, f) { - // Create configuration panel - panel = new ConfigurationPanel (0, 0, false); - - // Address book dialog - phonebook = new PhoneBook (0, 0, false); - - // URL input dialog - urlinput = new URL_Input (this); - - // For managing - this->callmanager = mngr; - - // For DTMF - key = new DTMF (); - buf = new short[SIZEBUF]; - - // Create new display and numeric keypad - lcd = new MyDisplay(this, 0, this); - keypad = new NumericKeypad (this, NULL, Qt::WDestructiveClose | - Qt::WStyle_Customize | - Qt::WStyle_NoBorder); - // Initialisation of variables - currentLineNumber = -1; - onLine = currentLineNumber; - chosenLine = -1; - choose = false; - noChoose = false; - transfer = false; - msgVar = false; - b_dialtone = false; - apply = false; - this->first = true; - - // Initialisation of all that concern the skin - initSkin(); - this->initBlinkTimer(); - - - // By default, keyboard mapping mode is numerical mode - this->setMode(NUM_MODE); - - // Move - setMainLCD (); - - // Change window title and Icon. - this->setCaption(PROGNAME); - this->setIcon(QPixmap(Skin::getPathPixmap(QString(PIXDIR), - QString(SFLPHONE_LOGO)))); - - // Show the GUI - this->show(); - - // Handle the tray icon system - mypop = new QPopupMenu(this); - mypop->insertItem ("Quit", qApp, SLOT(quit())); - - trayicon = new MyTrayIcon(QPixmap( - Skin::getPathPixmap(QString(PIXDIR), QString(TRAY_ICON))), - NULL, mypop, parent, name); - trayicon->show(); - - // Connect to handle trayicon - connect(trayicon, SIGNAL(clickedLeft()), this, SLOT(clickHandle())); - // Connect blinkTimer signals to blink slot - connect(blinkTimer, SIGNAL(timeout()),this, SLOT(blinkMessageSlot())); - connect (blinkTimer, SIGNAL(timeout()), this, SLOT(blinkRingSlot()) ); - connect (blinkTimer, SIGNAL(timeout()), this, SLOT(blinkLineSlot())); - // Connect to append url in display - connect (urlinput->buttonOK, SIGNAL(clicked()), this, SLOT(stripSlot())); - // Connect to save settings - connect (panel->buttonSave, SIGNAL(clicked()), this, SLOT(save())); - // Connect to apply skin - connect (panel->buttonApplySkin, SIGNAL(clicked()), this,SLOT(applySkin())); - // Connect to register manually - connect (panel->Register, SIGNAL(clicked()), this, SLOT(registerSlot())); -} - -/** - * Destructor - */ -QtGUIMainWindow::~QtGUIMainWindow(void) { - deleteButtons(); - delete panel; - delete blinkTimer; - delete keypad; - delete lcd; - delete urlinput; - delete callmanager; - delete mypop; - delete trayicon; - delete pt; -} - -void -QtGUIMainWindow::deleteButtons (void) { - delete phoneKey_transf; - delete phoneKey_msg; - delete phoneKey_conf; - delete phoneKey_line0; - delete phoneKey_line1; - delete phoneKey_line2; - delete phoneKey_line3; - delete reduce_button; - delete quit_button; - delete addr_book_button; - delete configuration_button; - delete hangup_button; - delete dial_button; - delete mute_button; - delete dtmf_button; - delete vol_mic; - delete vol_spkr; - delete micVolVector; - delete spkrVolVector; - for (int j = 0; j < NUMBER_OF_LINES; j++) { - delete callmanager->phLines[j]->button(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Private Methods implementations -/////////////////////////////////////////////////////////////////////////////// -/** - * Init and start blink timer for all blinking pixmap, with 500 ms timeout. - */ -void -QtGUIMainWindow::initBlinkTimer(void) { - blinkTimer = new QTimer(this); - blinkTimer->start(500); -} - -/** - * Init variable with skin choice - */ -QString -QtGUIMainWindow::setPathSkin (void) { - return QString(Config::getchar( - "Preferences", "Themes.skinChoice", "metal")); -} - -/** - * Init variable with ring choice - */ -QString -QtGUIMainWindow::ringFile(void) { - return QString(Config::getchar( - "Audio", "Rings.ringChoice", "konga.ul")); -} - -/** - * Get whole path for rings - */ -QString -QtGUIMainWindow::getRingFile (void) { - QString ringFilename(Skin::getPathRing(QString(RINGDIR), ringFile())); - return ringFilename; -} - -void -QtGUIMainWindow::initSkin (void) { - // Load file configuration skin - QString skinfilename(Skin::getPath(QString(SKINDIR), setPathSkin(), - QString(FILE_INI))); - - if (!apply) { - this->pt = new Point(skinfilename.ascii()); - } else { - // If click on apply button - delete pt; - deleteButtons(); - this->pt = new Point(skinfilename.ascii()); - } - // Initialisation of the buttons - this->initSpkrVolumePosition(); - this->initMicVolumePosition(); - this->initButtons(); - // Connections of the buttons - this->connections(); - - // Load background image phone - setbgPixmap (new QPixmap (Skin::getPath(QString(SKINDIR), - setPathSkin(), - QString(PIXMAP_PHONE)))); - // Transform pixmap to QImage - setSourceImage (); - this->setMaximumSize (getSourceImage().width(), getSourceImage().height()); - this->setGeometry (MAIN_INITIAL_POSITION, - MAIN_INITIAL_POSITION, - getSourceImage().width(), - getSourceImage().height()); - // Calculate just one time the transparency mask bit to bit - transparencyMask (); - - // Line pixmaps initialisation - for (int i = 0; i < NUMBER_OF_LINES; i++) { - for (int j = 0; j < NUMBER_OF_STATES; j++) { - TabLinePixmap[i][j] = QPixmap(Skin::getPath(QString(SKINDIR), - setPathSkin(), - QString(PIXMAP_LINE(i, j)))); - } - } - // Message pixmaps initialisation - TabMsgPixmap[0] = QPixmap(Skin::getPath(QString(SKINDIR), setPathSkin(), - PIXMAP_MESSAGE_OFF)); - TabMsgPixmap[1] = QPixmap(Skin::getPath(QString(SKINDIR), setPathSkin(), - PIXMAP_MESSAGE_ON)); -} - -void -QtGUIMainWindow::initSpkrVolumePosition (void) { - if (pt->getDirection(VOL_SPKR) == VERTICAL) { - vol_spkr_x = Config::get("Audio", "Volume.speakers_x", - pt->getX(VOL_SPKR)); - vol_spkr_y = Config::get("Audio", "Volume.speakers_y", - pt->getVariation(VOL_SPKR)); - } else if (pt->getDirection(VOL_SPKR) == HORIZONTAL) { - vol_spkr_x = Config::get("Audio", "Volume.speakers_x", - pt->getX(VOL_SPKR) + pt->getVariation(VOL_SPKR)); - vol_spkr_y = Config::get("Audio", "Volume.speakers_y", - pt->getY(VOL_SPKR)); - } -} - -void -QtGUIMainWindow::initMicVolumePosition (void) { - if (pt->getDirection(VOL_MIC) == VERTICAL) { - vol_mic_x = Config::get("Audio", "Volume.micro_x", pt->getX(VOL_MIC)); - vol_mic_y = Config::get("Audio", "Volume.micro_y", - pt->getVariation(VOL_MIC)); - } else if (pt->getDirection(VOL_MIC) == HORIZONTAL) { - vol_mic_x = Config::get("Audio", "Volume.micro_x", - pt->getX(VOL_MIC) + pt->getVariation(VOL_MIC)); - vol_mic_y = Config::get("Audio", "Volume.micro_y", pt->getY(VOL_MIC)); - } -} - -/** - * Inits all phonekey buttons. - * Create new QPushButtons, set up tool tip, disable focus, set button geometry - * set palette. - */ -void -QtGUIMainWindow::initButtons (void) { - // Buttons initialisation - phoneKey_msg= new JPushButton(this, NULL, VOICEMAIL); - phoneKey_transf = new JPushButton(this, NULL, TRANSFER); - phoneKey_conf = new JPushButton(this, NULL, CONFERENCE); - reduce_button = new JPushButton(this, NULL, MINIMIZE); - quit_button = new JPushButton(this, NULL, CLOSE); - addr_book_button = new JPushButton(this, NULL, DIRECTORY); - configuration_button = new JPushButton(this, NULL, SETUP); - hangup_button = new JPushButton(this, NULL, HANGUP); - dial_button = new JPushButton(this, NULL, CONNECT); - mute_button = new JPushButton(this, NULL, MUTE); - dtmf_button = new JPushButton(this, NULL, DTMF_SHOW); - - // Set tooltip buttons - QToolTip::add(reduce_button, tr("Minimize window")); - QToolTip::add(quit_button, tr("Close window (Ctrl+Q)")); - QToolTip::add(phoneKey_msg, tr("Get your message")); - QToolTip::add(phoneKey_transf, tr("Call transfer")); - QToolTip::add(phoneKey_conf, tr("Conference")); - QToolTip::add(addr_book_button, tr("Address book")); - QToolTip::add(configuration_button, tr("Configuration tools (Ctrl+C)")); - QToolTip::add(hangup_button, tr("Hangup")); - QToolTip::add(dial_button, tr("Dial")); - QToolTip::add(mute_button, tr("Mute")); - QToolTip::add(dtmf_button, tr("Show DTMF keypad (Ctrl+D)")); - - // Buttons position - phoneKey_msg->move (pt->getX(VOICEMAIL), pt->getY(VOICEMAIL)); - phoneKey_transf->move (pt->getX(TRANSFER), pt->getY(TRANSFER)); - phoneKey_conf->move (pt->getX(CONFERENCE), pt->getY(CONFERENCE)); - reduce_button->move (pt->getX(MINIMIZE), pt->getY(MINIMIZE)); - addr_book_button->move (pt->getX(DIRECTORY), pt->getY(DIRECTORY)); - quit_button->move (pt->getX(CLOSE), pt->getY(CLOSE)); - configuration_button->move (pt->getX(SETUP), pt->getY(SETUP)); - hangup_button->move (pt->getX(HANGUP), pt->getY(HANGUP)); - dial_button->move (pt->getX(CONNECT), pt->getY(CONNECT)); - mute_button->move (pt->getX(MUTE), pt->getY(MUTE)); - dtmf_button->move (pt->getX(DTMF_SHOW), pt->getY(DTMF_SHOW)); - - // Loop for line buttons - // Initialisation, set no focus, set geometry, set palette, pixmap - for (int j = 0; j < NUMBER_OF_LINES; j++) { - QString lnum; - - lnum = "l" + lnum.setNum (j + 1); - callmanager->phLines[j]->setButton(new JPushButton( - this, NULL, lnum.ascii())); - callmanager->phLines[j]->button()->move (pt->getX(lnum),pt->getY(lnum)); - } - - // Set pixmaps volume - micVolVector = new Vector(this, VOL_MIC, pt); - spkrVolVector = new Vector(this, VOL_SPKR, pt); - - vol_mic = new VolumeControl(this, NULL, VOLUME, micVolVector); - vol_spkr = new VolumeControl(this, NULL, VOLUME, spkrVolVector); - vol_mic->move(vol_mic_x, vol_mic_y); - vol_spkr->move(vol_spkr_x, vol_spkr_y); -} - -void -QtGUIMainWindow::connections (void) { - // Connect for clicked numeric keypad button - connect ((QObject*)keypad->key0, SIGNAL(clicked()), this, - SLOT(pressedKey0())); - connect ((QObject*)keypad->key1, SIGNAL(clicked()), this, - SLOT(pressedKey1())); - connect ((QObject*)keypad->key2, SIGNAL(clicked()), this, - SLOT(pressedKey2())); - connect ((QObject*)keypad->key3, SIGNAL(clicked()), this, - SLOT(pressedKey3())); - connect ((QObject*)keypad->key4, SIGNAL(clicked()), this, - SLOT(pressedKey4())); - connect ((QObject*)keypad->key5, SIGNAL(clicked()), this, - SLOT(pressedKey5())); - connect ((QObject*)keypad->key6, SIGNAL(clicked()), this, - SLOT(pressedKey6())); - connect ((QObject*)keypad->key7, SIGNAL(clicked()), this, - SLOT(pressedKey7())); - connect ((QObject*)keypad->key8, SIGNAL(clicked()), this, - SLOT(pressedKey8())); - connect ((QObject*)keypad->key9, SIGNAL(clicked()), this, - SLOT(pressedKey9())); - connect ((QObject*)keypad->keyStar, SIGNAL(clicked()), this, - SLOT(pressedKeyStar())); - connect ((QObject*)keypad->keyHash, SIGNAL(clicked()), this, - SLOT(pressedKeyHash())); - connect ((QObject*)keypad->keyClose, SIGNAL(clicked()), this, - SLOT(dtmfKeypad())); - - // Connections for the lines - connect (callmanager->phLines[0]->button(), SIGNAL(clicked()), this, - SLOT(button_line0())); - connect (callmanager->phLines[1]->button(), SIGNAL(clicked()), this, - SLOT(button_line1())); - connect (callmanager->phLines[2]->button(), SIGNAL(clicked()), this, - SLOT(button_line2())); - connect (callmanager->phLines[3]->button(), SIGNAL(clicked()), this, - SLOT(button_line3())); - connect (callmanager->phLines[4]->button(), SIGNAL(clicked()), this, - SLOT(button_line4())); - connect (callmanager->phLines[5]->button(), SIGNAL(clicked()), this, - SLOT(button_line5())); - - // Misc - connect (phoneKey_msg, SIGNAL(clicked()), this, SLOT(button_msg())); - connect (phoneKey_transf, SIGNAL(clicked()), this, SLOT(button_transfer())); - connect (phoneKey_conf, SIGNAL(clicked()), this, SLOT(button_conf())); - connect (dial_button, SIGNAL(clicked()), this, SLOT(dial())); - connect (mute_button, SIGNAL(clicked()), this, SLOT(button_mute())); - connect (hangup_button, SIGNAL(clicked()), this, SLOT(hangupLine())); - connect (configuration_button,SIGNAL(clicked()),this,SLOT(configuration())); - connect (addr_book_button, SIGNAL(clicked()), this,SLOT(addressBook())); - connect (dtmf_button, SIGNAL(clicked()), this, SLOT(dtmfKeypad())); - - // Connect to reduce - connect (reduce_button, SIGNAL(clicked()), this, SLOT(reduceHandle())); - // Connect to quit with keyboard - connect (this, SIGNAL(keyPressed(int)), this, SLOT(quitApplication())); - // Connect to quit with quit button - connect (quit_button, SIGNAL(clicked()), this, SLOT(quitApplication())); - - // Connections for volume control - connect(vol_spkr, SIGNAL(setVolumeValue(int)), this, - SLOT(volumeSpkrChanged(int))); - connect(vol_mic, SIGNAL(setVolumeValue(int)), this, - SLOT(volumeMicChanged(int))); -} - -/** - * Returns true if the keyboard mapping returns letters. - * - * @return bool - */ -bool -QtGUIMainWindow::isInTextMode (void) { - if (modeType == TEXT_MODE) { - return true; - } else { - return false; - } -} - -/** - * Returns true if the keyboard mapping returns digits. - * - * @return bool - */ -bool -QtGUIMainWindow::isInNumMode (void) { - if (modeType == NUM_MODE) { - return true; - } else { - return false; - } -} - -/** - * Sets up the keyboard mapping mode. - */ -void -QtGUIMainWindow::setMode (int mode) { - this->modeType = mode; -} - -/** - * Search the busy line number among all lines and different of current line - * number. - * - * @return number of busy line - */ -int -QtGUIMainWindow::numLineBusy(void) { - int temp = -1; - for (int i = 0; i < NUMBER_OF_LINES; i++) { - if (callmanager->phLines[i]->isBusy() && i != currentLineNumber) { - temp = i; - } - } - return temp; -} - -void -QtGUIMainWindow::dialTone (bool var) { - if (callmanager->error->getError() == 0) { - if (this->b_dialtone != var) { - this->b_dialtone = var; - } - callmanager->tonezone = var; - callmanager->tone->toneHandle(ZT_TONE_DIALTONE); - } else { - callmanager->error->errorName(DEVICE_NOT_OPEN, NULL); - } -} -/////////////////////////////////////////////////////////////////////////////// -// Public Methods implementations // -/////////////////////////////////////////////////////////////////////////////// - -/** - * Initializes LCD display, and move it at the configuration file position. - */ -void -QtGUIMainWindow::setMainLCD (void) { - // Screen initialisation - this->lcd->move (pt->getX(SCREEN), pt->getY(SCREEN)); -} - -void -QtGUIMainWindow::setCurrentLineNumber(int newcur) { - this->currentLineNumber = newcur; -} - -/** - * Sets the corresponding pixmap button according to its state. - * Handle operations between lines (on hold, off hold) and dial tone - * - * @param num_line: number of the current line - */ -void -QtGUIMainWindow::toggleLine (int num_line) { - if ( num_line == -1 ){ - qDebug("Should not arrived !!!!!!!%d\n", num_line); - return ; - } - //Current line number - currentLineNumber = num_line; - // Change state when click on line button - callmanager->phLines[currentLineNumber]->toggleState(); - // If another line is busy - busyNum = numLineBusy(); - - if (callmanager->isNotUsedLine(currentLineNumber) and busyNum == -1) { - qDebug("GUI: FIRST LINE IN USED %d", currentLineNumber); - lcd->setStatus(ENTER_NUMBER_STATUS); - chosenLine = currentLineNumber; - if (!noChoose) { - choose = true; - if (!callmanager->phLines[currentLineNumber]->getbDial()) { - this->dialTone(true); - } - } - callmanager->phLines[currentLineNumber]->setStateLine(BUSY); - } - - // Occurs when newly off-hook line replaces another one. - if (busyNum != currentLineNumber && busyNum != -1) { - if (callmanager->isNotUsedLine(currentLineNumber)) { - qDebug("GUI: Line %d replaces another one", currentLineNumber); - lcd->clear(QString(ENTER_NUMBER_STATUS)); - chosenLine = currentLineNumber; - lcd->inFunction = false; - if (!noChoose) { - choose = true; - } - } - // Change state to ONHOLD - callmanager->phLines[busyNum]->setState(ONHOLD); - callmanager->phLines[busyNum]->setStateLine(ONHOLD); - callmanager->actionHandle (busyNum, ONHOLD_CALL); - if (!callmanager->phLines[currentLineNumber]->getbDial()) { - // If ok-button not clicked before - if (callmanager->sip->call[busyNum] == NULL and - !callmanager->tryingState(busyNum)) { - setInactiveLine(busyNum); - } - callmanager->phLines[busyNum]->setbDial(false); - - if (!callmanager->phLines[currentLineNumber]->getbRinging() - and callmanager->sip->call[currentLineNumber] == NULL and - !callmanager->tryingState(currentLineNumber)) { - // If no occured incoming call (not ringing and call[current]=null) - this->dialTone(true); - } - } - callmanager->phLines[currentLineNumber]->setState(BUSY); - qDebug("GUI: state ON-HOLD line busyNum %d", busyNum); - } - - if (callmanager->phLines[currentLineNumber]->isBusy()) { - qDebug("GUI: isBusy line %d", currentLineNumber); - - // Change line button pixmap to "line in use" state. - callmanager->phLines[currentLineNumber]->button()->setPixmap( - TabLinePixmap[currentLineNumber][BUSY]); - callmanager->phLines[currentLineNumber]->setState(BUSY); - - // Answer new call - if ((callmanager->isRingingLine(currentLineNumber) || - (callmanager->isUsedLine(currentLineNumber) and - callmanager->phLines[currentLineNumber]->getbRinging())) and - callmanager->phLines[currentLineNumber]->getStateLine() != ONHOLD){ - - qDebug("GUI: -- New answered call %d --", currentLineNumber); - callmanager->actionHandle (currentLineNumber, ANSWER_CALL); - callmanager->phLines[currentLineNumber]->setState(BUSY); - callmanager->phLines[currentLineNumber]->setStateLine(BUSY); - - lcd->clearBuffer(); - lcd->appendText(callmanager->phLines[currentLineNumber]->text); - } - else if (callmanager->phLines[currentLineNumber]->getStateLine() - == ONHOLD - or callmanager->tryingState(currentLineNumber)) { - qDebug("GUI: state OFF-HOLD line %d", currentLineNumber); - lcd->clear(QString(CONNECTED_STATUS)); - if (callmanager->sip->call[currentLineNumber] != NULL) { - if (!callmanager->phLines[currentLineNumber]->getbInProgress()){ - // Timer-call function - lcd->inFunction = true; - } else { - // Time function - lcd->inFunction = false; - } - } - callmanager->phLines[currentLineNumber]->setStateLine(OFFHOLD); - callmanager->actionHandle (currentLineNumber, OFFHOLD_CALL); - lcd->appendText(callmanager->phLines[currentLineNumber]->text); - lcd->setStatus(callmanager->phLines[currentLineNumber]->status); - } - } - else if (callmanager->phLines[currentLineNumber]->isOnHold()){ - // Change state to ONHOLD - callmanager->phLines[currentLineNumber]->setState(ONHOLD); - callmanager->phLines[currentLineNumber]->setStateLine(ONHOLD); - qDebug("GUI: state ON-HOLD line %d", currentLineNumber); - lcd->setStatus(ONHOLD_STATUS); - callmanager->actionHandle (currentLineNumber, ONHOLD_CALL); - - if (callmanager->sip->call[currentLineNumber] == NULL and - !callmanager->phLines[currentLineNumber]->getbDial()) { - setInactiveLine(currentLineNumber); - lcd->clear(QString(ENTER_NUMBER_STATUS)); - } - } -} - -/** - * Actions occur when click on hang off button. - * Use to validate incoming and outgoing call, transfer call. - */ -void -QtGUIMainWindow::dial (void) { - int i = 0; - - if (transfer and callmanager->sip->call[currentLineNumber] != NULL - and currentLineNumber != -1) { - // If transfer button clicked, validate the number for transfer. - callmanager->actionHandle (currentLineNumber, TRANSFER_CALL); - transfer = false; - } else { - qDebug("GUI: LINE CURRENT %d", currentLineNumber); - - if (callmanager->ringing()) { - // If new incoming call - currentLineNumber = callmanager->newCallLineNumber(); - toggleLine (currentLineNumber); - // Set used-state pixmap 'currentLineNumber' line - callmanager->phLines[currentLineNumber]->button()->setPixmap( - TabLinePixmap[currentLineNumber][BUSY]); - callmanager->phLines[currentLineNumber]->setState(BUSY); - } else if (callmanager->phLines[currentLineNumber]->isOnHold() or - callmanager->phLines[currentLineNumber]->getStateLine() == OFFHOLD - or (callmanager->phLines[currentLineNumber]->isBusy() and - callmanager->sip->call[currentLineNumber] != NULL)){ - // If line is used - // NOTHING - } else { - // If new outgoing call - - if (callmanager->bufferTextRender() != "") { - // If a phone number is entered - - // To make outgoing call - i = callmanager->outgoingNewCall(); - - if (i == 0) { - // If outgoing call succeeded - if (!choose) { - // If find not used line - noChoose = true; - currentLineNumber=callmanager->findLineNumberNotUsed(); - } else { - // If choose line - currentLineNumber = chosenLine; - } - - // Store the phone number - callmanager->phLines[currentLineNumber]->text = - callmanager->bufferTextRender(); - - // Dial button is clicked - callmanager->phLines[currentLineNumber]->setbDial(true); - // Main function - toggleLine (currentLineNumber); - // Call in progress - callmanager->phLines[currentLineNumber]->setbInProgress(true); - // Set used-state pixmap 'currentLineNumber' line - callmanager->phLines[currentLineNumber]->button()->setPixmap( - TabLinePixmap[currentLineNumber][BUSY]); - callmanager->phLines[currentLineNumber]->setState(BUSY); - } - } - } - } -} - -/** - * Hangup the current call. - */ -void -QtGUIMainWindow::hangupLine (void) { - qDebug("HANGUP: line %d", currentLineNumber); - int line = callmanager->sip->notUsedLine; - - if (line != -1 and callmanager->phLines[line]->getbRinging()) { - // If the IP-phone user want to refuse an incoming call - int tmp = currentLineNumber; // store currentLineNumber - callmanager->actionHandle (line, REFUSE_CALL); - setCurrentLineNumber(tmp); // set currentLineNumber - if (currentLineNumber != -1) { - // If there is current call, put on-hold this call - callmanager->phLines[currentLineNumber]->setState(ONHOLD); - callmanager->phLines[currentLineNumber]->setStateLine(ONHOLD); - callmanager->actionHandle (currentLineNumber, ONHOLD_CALL); - } - } - else if (currentLineNumber != -1 and - !(callmanager->phLines[currentLineNumber]->isOnHold()) and - !callmanager->tryingState(currentLineNumber)) { - // If there is current line opened and state line not onHold - // set free pixmap - setFreeStateLine (currentLineNumber); - callmanager->phLines[currentLineNumber]->setbDial(false); - - if (callmanager->phLines[currentLineNumber]->getbInProgress()) { - // If call is progressing, cancel the call - lcd->clear(QString(HUNGUP_STATUS)); - callmanager->actionHandle (currentLineNumber, CANCEL_CALL); - } else if (b_dialtone - and callmanager->sip->call[currentLineNumber] == NULL){ - // If stop dialtone - this->dialTone(false); - lcd->clear(QString(ENTER_NUMBER_STATUS)); - } else if (callmanager->getbCongestion()){ - // If stop congestion tone - callmanager->congestion(false); - lcd->clear(QString(ENTER_NUMBER_STATUS)); - } else { - // Hang up the call - callmanager->actionHandle (currentLineNumber, CLOSE_CALL); - callmanager->phLines[currentLineNumber]->setbDial(false); - lcd->clear(QString(HUNGUP_STATUS)); - } - // Make free hung current line - setCurrentLineNumber(-1); - choose = false; - noChoose = false; - } - - // Just to test when receive a message -#if 0 - msgVar = true; -#endif -} - -void -QtGUIMainWindow::setInactiveLine (int num) { - // set free pixmap - setFreeStateLine (num); - // Stop dial tone - this->dialTone(false); - callmanager->phLines[num]->setbDial(false); -} - -/** - * Stop the blinking message slot and load the message-off button pixmap. - */ -void -QtGUIMainWindow::stopTimerMessage (void) { - msgVar = false; - phoneKey_msg->setPixmap(TabMsgPixmap[FREE]); -} - -/** - * Set pixamp of free state. - */ -void -QtGUIMainWindow::setFreeStateLine (int line) { - // Set free-status for current line - callmanager->phLines[line]->setState (FREE); - callmanager->phLines[line]->setStateLine (FREE); - // Set free-pixmap - callmanager->phLines[line]->button()->setPixmap(TabLinePixmap[line][FREE]); -} - -/** - * Stop the call timer. - * - * @param line: number of line - */ -void -QtGUIMainWindow::stopCallTimer (int line) { - // Stop the call timer when hang up - if (callmanager->phLines[line]->timer != NULL) { - callmanager->phLines[line]->stopTimer(); - } - // No display call timer, display current hour - lcd->inFunction = false; - callmanager->phLines[line]->first = true; -} - -/** - * Start the call timer. - * - * @param line: number of line - */ -void -QtGUIMainWindow::startCallTimer (int line) { - // Call-timer enable - lcd->inFunction = true; - - // To start the timer for display text just one time - if (callmanager->phLines[line]->first) { - callmanager->phLines[line]->startTimer(); - callmanager->phLines[line]->first = false; - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Public slot implementations // -/////////////////////////////////////////////////////////////////////////////// -void -QtGUIMainWindow::volumeSpkrChanged (int val) { - callmanager->spkrSoundVolume (val); -} - -void -QtGUIMainWindow::volumeMicChanged (int val) { - callmanager->micSoundVolume (val); -} - -void -QtGUIMainWindow::registerSlot (void) { - callmanager->sip->setRegister(); -} - -/** - * Slot to blink with free and busy pixmaps when line is hold. - */ -void -QtGUIMainWindow::blinkLineSlot (void) { - static bool isOn = false; - int state = BUSY; - - if (!isOn) { - state = FREE; - } - - for (int i = 0; i < NUMBER_OF_LINES; i++) { - // If lines are hold on, set blinking pixmap - if (callmanager->phLines[i]->isOnHold()) { - callmanager->phLines[i]->button()->setPixmap(TabLinePixmap[i][state]); - } - } - isOn = !isOn; -} - -// Dial the voicemail Number automatically when button is clicked -void -QtGUIMainWindow::button_msg (void) { - stopTimerMessage(); - lcd->clear("Voicemail"); - lcd->appendText(Config::getchar("Preferences","Options.voicemailNumber","")); - dial(); -} - -// Allow to enter a phone number to transfer the current call. -// This number is validated by ok-button or typing Enter -void -QtGUIMainWindow::button_transfer (void) { - if (currentLineNumber != -1) { - transfer = true; - callmanager->actionHandle (currentLineNumber, ONHOLD_CALL); - lcd->clear(QString(TRANSFER_STATUS)); - } -} - -void -QtGUIMainWindow::button_conf (void) { -//TODO: This feature is not implemented yet - QMessageBox::information(this, "Conference", - "This feature is not implemented yet", QMessageBox::Yes); -} - -void -QtGUIMainWindow::button_line0 (void) { - toggleLine (0); - -} - -void -QtGUIMainWindow::button_line1 (void) { - toggleLine (1); -} - -void -QtGUIMainWindow::button_line2 (void) { - toggleLine (2); -} - -void -QtGUIMainWindow::button_line3 (void) { - toggleLine (3); -} - -void -QtGUIMainWindow::button_line4 (void) { - toggleLine (4); -} - -void -QtGUIMainWindow::button_line5 (void) { - toggleLine (5); -} - -void -QtGUIMainWindow::button_mute(void) { - // Disable micro sound - static bool isOn = true; - - if (callmanager->sip->getNumberPendingCalls() != 0) { - // If there is at least a pending call - if(!isOn) { - callmanager->mute = false; - lcd->clear("Mute off"); - } else { - callmanager->mute = true; - lcd->clear("Mute on"); - } - - isOn = !isOn; - } -} - -// Show the setup panel -void -QtGUIMainWindow::configuration (void) { - panel->show(); -} - -void -QtGUIMainWindow::addressBook (void) { -// TODO: phonebook->show(); - QMessageBox::information(this, "Directory", - "This feature is not implemented yet", QMessageBox::Yes); -} - -// Handle the dtmf-button click -void -QtGUIMainWindow::dtmfKeypad (void) { - if (keypad->isVisible()) { - // Hide keypad if it's visible. - keypad->hide(); - } else { - - if (this->first and !getMoved()) { - // If it's the first time that keypad is shown. - // The position is fixed with the main initial position. - this->first = false; - keypad->setGeometry (MAIN_INITIAL_POSITION + - this->getSourceImage().width(), - MAIN_INITIAL_POSITION, - keypad->getSourceImage().width(), - keypad->getSourceImage().height()); - } else { - // If main window is moved, we calculate the keypad new position - // to fix it with main window - if (getMoved()) { - keypad->setGeometry (positionOffsetX(), - getGlobalMouseY() - getMouseY(), - keypad->getSourceImage().width(), - keypad->getSourceImage().height()); - } - if (keypad->getMoved()) { - // If keypad is moved, it shows at the previous position - keypad->setGeometry ( - keypad->getGlobalMouseX()-keypad->getMouseX(), - keypad->getGlobalMouseY()-keypad->getMouseY(), - keypad->getSourceImage().width(), - keypad->getSourceImage().height()); - } - } - - // Show keypad if it's hidden. - keypad->show(); - } -} - -// Get x-position offset, related to the screen size, to show numeric keypad -int -QtGUIMainWindow::positionOffsetX (void) { - QRect screenRect; - int offset; - - // Get the screen geometry - screenRect = (QApplication::desktop())->screenGeometry (0); - - offset = this->getSourceImage().width() - getMouseX() + getGlobalMouseX(); - if (offset + keypad->getSourceImage().width() > screenRect.right()) { - return getGlobalMouseX() - ( - getMouseX() + keypad->getSourceImage().width()); - } else { - return offset; - } -} - -/** - * Slot when receive a message, blink pixmap. - */ -void -QtGUIMainWindow::blinkMessageSlot (void) { - static bool isOn = false; - int stateMsg = BUSY; - - if(!isOn) { - stateMsg = FREE; - } - - if (msgVar) { - phoneKey_msg->setPixmap(TabMsgPixmap[stateMsg]); - } - isOn = !isOn; -} - - -/** - * Slot when phone is ringing, blink pixmap. - */ -void -QtGUIMainWindow::blinkRingSlot (void) { - static bool isOn = false; - int state = BUSY; - int line = callmanager->sip->notUsedLine; - if ( line == -1 ) - return; - - // If more than one line is ringing - for (int i = 0; i < NUMBER_OF_LINES; i++) { - if (callmanager->isUsedLine(i) - and callmanager->phLines[i]->getbRinging()) { - if (!isOn) - state = FREE; - callmanager->phLines[i]->button()->setPixmap( - TabLinePixmap[i][state]); - } - } - - //If new line is ringing - if (callmanager->phLines[line]->getbRinging()) { - // For the line - if (!isOn) { - state = FREE; - } - - callmanager->phLines[line]->button()->setPixmap( - TabLinePixmap[line][state]); - isOn = !isOn; - } -} - - -/** - * Slot to quit application with or without QMessageBox confirmation - */ -void -QtGUIMainWindow::quitApplication (void) { - bool confirm; - - // Save volume positions - // TODO: save position if direction is horizontal - Config::set("Audio", "Volume.speakers_x", pt->getX(VOL_SPKR)); - if (vol_spkr->getValue() != 0) { - Config::set("Audio", "Volume.speakers_y", pt->getY(VOL_SPKR) - - vol_spkr->getValue()); - } - Config::set("Audio", "Volume.micro_x", pt->getX(VOL_MIC)); - if (vol_mic->getValue() != 0) { - Config::set("Audio", "Volume.micro_y", pt->getY(VOL_MIC) - - vol_mic->getValue()); - } - // Save current position of the controls volume - save(); - - // Show QMessageBox - confirm = Config::get("Preferences", "Options.confirmQuit", (int)true); - if (confirm) { - if (QMessageBox::question(this, "Confirm quit", - "Are you sure you want to quit SFLPhone ?", - QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - QApplication::exit(0); - callmanager->quitLibrary(); - } - } - // No QMessageBox - else { - QApplication::exit(0); - callmanager->quitLibrary(); - qDebug("QUIT"); - } -} - -/** - * Slot to strip urlinput. - */ -void -QtGUIMainWindow::stripSlot (void) { - QRegExp rx(REG_EXPR); - lcd->appendText(urlinput->url->text().remove(rx)); - urlinput->close(); -} - -/** - * Slot when numeric keypad phonekey is pressed, append the key text and setup - * the DTMF keypad . - * - * @param button group id - */ -void -QtGUIMainWindow::pressedKeySlot (int id) { - char code = 0; - int pulselen = 0; - int a = 0; - - // Stop dial tone - if (b_dialtone) { - this->dialTone(false); - } - - switch (id) { - case KEYPAD_ID_0: code = '0'; break; - case KEYPAD_ID_1: code = '1'; break; - case KEYPAD_ID_2: code = '2'; break; - case KEYPAD_ID_3: code = '3'; break; - case KEYPAD_ID_4: code = '4'; break; - case KEYPAD_ID_5: code = '5'; break; - case KEYPAD_ID_6: code = '6'; break; - case KEYPAD_ID_7: code = '7'; break; - case KEYPAD_ID_8: code = '8'; break; - case KEYPAD_ID_9: code = '9'; break; - case KEYPAD_ID_STAR: code = '*'; break; - case KEYPAD_ID_HASH: code = '#'; break; - } - - onLine = currentLineNumber; - if (onLine != -1) { - callmanager->dtmf(onLine, code); - } - - // Handle dtmf - key->startTone(code); - key->generateDTMF(buf, SAMPLING_RATE); - callmanager->audiodriver->audio_buf.resize(SAMPLING_RATE); - callmanager->audiodriver->audio_buf.setData( - buf, callmanager->getSpkrVolume()); - - pulselen = Config::get("Signalisations", "DTMF.pulseLength", 250); - callmanager->audiodriver->audio_buf.resize(pulselen * (OCTETS/1000)); - a = callmanager->audiodriver->writeBuffer(); - if (a == 1) { - pressedKeySlot(id); - } else { - if (callmanager->error->getError() == 0) - lcd->appendText (code); - else if (callmanager->error->getError() == 1) { - lcd->clearBuffer(); - callmanager->error->setError(0); - lcd->appendText (code); - } - } -} - -// Save settings in config-file -void -QtGUIMainWindow::save() { - Config::tree()->saveToFile(callmanager->path.data()); -} - -void -QtGUIMainWindow::applySkin (void) { - apply = true; - // For skin of the screen - lcd->initGraphics(); - setMainLCD(); - // For skin of the gui - initSkin(); -} - - -// Handle operation to minimize the application -void -QtGUIMainWindow::reduceHandle (void) { - if (Config::getb("Preferences", "Options.checkedTray")) { - clickHandle(); - } else { - showMinimized(); - } -} - -// Handle mouse left-button click to minimize/maximize the application -void -QtGUIMainWindow::clickHandle (void) { - if (this->isShown()) { - hide(); - } - - else if (this->isMinimized()) { - showMaximized(); - } - - else { - show(); - } -} - -void -QtGUIMainWindow::pressedKey0 (void) { - pressedKeySlot (KEYPAD_ID_0); -} - -void -QtGUIMainWindow::pressedKey1 (void) { - pressedKeySlot (KEYPAD_ID_1); -} - -void -QtGUIMainWindow::pressedKey2 (void) { - pressedKeySlot (KEYPAD_ID_2); -} -void -QtGUIMainWindow::pressedKey3 (void) { - pressedKeySlot (KEYPAD_ID_3); -} -void -QtGUIMainWindow::pressedKey4 (void) { - pressedKeySlot (KEYPAD_ID_4); -} -void -QtGUIMainWindow::pressedKey5 (void) { - pressedKeySlot (KEYPAD_ID_5); -} -void -QtGUIMainWindow::pressedKey6 (void) { - pressedKeySlot (KEYPAD_ID_6); -} -void -QtGUIMainWindow::pressedKey7 (void) { - pressedKeySlot (KEYPAD_ID_7); -} -void -QtGUIMainWindow::pressedKey8 (void) { - pressedKeySlot (KEYPAD_ID_8); -} -void -QtGUIMainWindow::pressedKey9 (void) { - pressedKeySlot (KEYPAD_ID_9); -} -void -QtGUIMainWindow::pressedKeyStar (void) { - pressedKeySlot (KEYPAD_ID_STAR); -} -void -QtGUIMainWindow::pressedKeyHash (void) { - pressedKeySlot (KEYPAD_ID_HASH); -} - -/////////////////////////////////////////////////////////////////////////////// -// Protected Methods implementations // -/////////////////////////////////////////////////////////////////////////////// -/** - * Reimplementation of keyPressEvent() to handle the keyboard mapping. - */ -void -QtGUIMainWindow::keyPressEvent(QKeyEvent *e) { - // Misc. key - switch (e->key()) { - case Qt::Key_At: - case Qt::Key_Colon: - case Qt::Key_Period: - case Qt::Key_Comma: - case Qt::Key_Plus: - case Qt::Key_Minus: - case Qt::Key_Slash: - lcd->appendText(QChar(e->key())); - return; - break; - - case Qt::Key_Backspace: - lcd->backspace(); - return; - break; - - case Qt::Key_Escape: - hangupLine(); - return; - break; - - - - case Qt::Key_Return: - case Qt::Key_Enter: - dial(); - return; - break; - - case Qt::Key_F1: - case Qt::Key_F2: - case Qt::Key_F3: - case Qt::Key_F4: - case Qt::Key_F5: - case Qt::Key_F6: - this->toggleLine(e->key() - Qt::Key_F1); - return; - break; - - case Qt::Key_L: - if (e->state() == Qt::ControlButton ) { - lcd->clear(); - return; - } - break; - case Qt::Key_Q : - if (e->state() == Qt::ControlButton ) { - emit keyPressed(e->key()); - return; - } - break; - case Qt::Key_O : - if (e->state() == Qt::ControlButton ) { - urlinput->show(); - return; - } - break; - case Qt::Key_C : - if (e->state() == Qt::ControlButton ) { - configuration(); - return; - } - break; - case Qt::Key_D : - if (e->state() == Qt::ControlButton ) { - dtmfKeypad(); - return; - } - break; - case Qt::Key_Space: - if (this->isInNumMode()) { - this->setMode(TEXT_MODE); - } else { - this->setMode(NUM_MODE); - } - return; - break; - - case Qt::Key_Alt: - case Qt::Key_CapsLock: - case Qt::Key_Shift: - case Qt::Key_Tab: - case Qt::Key_Control: - return; - break; - - default: - break; - } - - if (QChar(e->key()).isDigit() ) { - // Numeric keypad - if (e->key() == Qt::Key_0) { - pressedKeySlot(KEYPAD_ID_0); - } else { - pressedKeySlot(e->key() - Qt::Key_0 - 1); - } - } - - // Handle * and # too. - else if ((e->key() == Qt::Key_Asterisk) - or (e->key() == Qt::Key_NumberSign)) { - (e->key() == Qt::Key_Asterisk) ? - pressedKeySlot(KEYPAD_ID_STAR) - : pressedKeySlot(KEYPAD_ID_HASH); - } - - // If letter keypad and numeric mode, display digit. - else if (QChar(e->key()).isLetter() && this->isInNumMode() ) { - pressedKeySlot( - (NumericKeypadTools::keyToNumber(e->key())- Qt::Key_0) - 1); - } - - // If letter keypad and text mode, display letter. - else if (QChar(e->key()).isLetter() && this->isInTextMode()) { - lcd->appendText(QChar(e->key()).lower()); - } -} - -// EOF diff --git a/src/qtGUImainwindow.h b/src/qtGUImainwindow.h deleted file mode 100644 index cef45807dfdac9e269bf2016ba21c615eecc5ce6..0000000000000000000000000000000000000000 --- a/src/qtGUImainwindow.h +++ /dev/null @@ -1,243 +0,0 @@ -/** - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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 this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __QT_GUI_MAIN_WINDOW_H__ -#define __QT_GUI_MAIN_WINDOW_H__ - -#include <qbitmap.h> -#include <qimage.h> -#include <qdragobject.h> -#include <qevent.h> -#include <qpixmap.h> -#include <qpopupmenu.h> -#include <qpushbutton.h> -#include <qsettings.h> -#include <qthread.h> -#include <qwidget.h> - -#include "configuration.h" -#include "configurationpanelui.h" -#include "dtmf.h" -#include "jpushbutton.h" -#include "manager.h" -#include "mydisplay.h" -#include "numerickeypad.h" -#include "phonebookui.h" -#include "point.h" -#include "skin.h" -#include "sip.h" -#include "transqwidget.h" -#include "trayicon.h" -#include "url_inputui.h" -#include "volumecontrol.h" - -#define MAIN_INITIAL_POSITION 20 -#define TEXT_MODE 0 -#define NUM_MODE 1 - -/////////////////////////////////////////////////////////////////////////////// -// Tray Icon class -/////////////////////////////////////////////////////////////////////////////// -class MyTrayIcon : public TrayIcon -{ - Q_OBJECT -public: - MyTrayIcon(const QPixmap &, const QString &, QPopupMenu *popup = 0, - QObject *parent = 0, const char *name = 0 ); - ~MyTrayIcon(){}; - - QPopupMenu *menu; - -signals: - void clickedLeft(void); -protected: - void mousePressEvent (QMouseEvent *); -}; - -/////////////////////////////////////////////////////////////////////////////// -// GUI main window -/////////////////////////////////////////////////////////////////////////////// - -class QtGUIMainWindow : public TransQWidget { - Q_OBJECT -public: - // Default Constructor and destructor - QtGUIMainWindow (QWidget* = 0, const char* = 0,WFlags = 0,Manager * = NULL); - ~QtGUIMainWindow(void); - - // Public member variables - NumericKeypad *keypad; - MyDisplay *lcd; - QTimer *blinkTimer; - PhoneBook *phonebook; - URL_Input *urlinput; - ConfigurationPanel *panel; - QSettings settings; - Manager *callmanager; - - JPushButton *phoneKey_msg; - JPushButton *phoneKey_transf; - JPushButton *phoneKey_conf; - JPushButton *phoneKey_line0; - JPushButton *phoneKey_line1; - JPushButton *phoneKey_line2; - JPushButton *phoneKey_line3; - JPushButton *reduce_button; - JPushButton *quit_button; - JPushButton *addr_book_button; - JPushButton *configuration_button; - JPushButton *hangup_button; - JPushButton *dial_button; - JPushButton *mute_button; - JPushButton *dtmf_button; - VolumeControl *vol_mic; - VolumeControl *vol_spkr; - - int currentLineNumber; - int busyNum; - bool ringVar; - bool msgVar; - int chosenLine; - bool choose; - bool noChoose; - bool transfer; - bool callinprogress; - - QPixmap TabLinePixmap[NUMBER_OF_LINES][NUMBER_OF_STATES]; - - QPopupMenu *mypop; - MyTrayIcon *trayicon; - - Vector *micVolVector; - Vector *spkrVolVector; - int vol_mic_x, vol_mic_y; - int vol_spkr_x, vol_spkr_y; - - // Public functions - void setMainLCD (void); - void toggleLine (int); - void stopTimerMessage (void); - QString setPathSkin (void); - QString ringFile (void); - QString getRingFile (void); - inline - void ring (bool b) { this->ringVar = b; } - inline - bool ringing (void) { return this->ringVar; } - void stopCallTimer (int); - void startCallTimer (int); - void setFreeStateLine (int); - void setCurrentLineNumber(int); - void dialTone (bool); - void setInactiveLine (int); - -signals: - void keyPressed (int); - -public slots: - void dial (void); - void blinkRingSlot (void); - void blinkLineSlot (void); - void blinkMessageSlot (void); - void quitApplication (void); - void pressedKeySlot (int); - void stripSlot (void); - void addressBook (void); - void dtmfKeypad (void); - void configuration (void); - void hangupLine (void); - void button_mute (void); - void button_line0 (void); - void button_line1 (void); - void button_line2 (void); - void button_line3 (void); - void button_line4 (void); - void button_line5 (void); - void button_msg (void); - void button_transfer (void); - void button_conf (void); - void clickHandle (void); - void reduceHandle (void); - void save (void); - void applySkin (void); - - void pressedKey0 (void); - void pressedKey1 (void); - void pressedKey2 (void); - void pressedKey3 (void); - void pressedKey4 (void); - void pressedKey5 (void); - void pressedKey6 (void); - void pressedKey7 (void); - void pressedKey8 (void); - void pressedKey9 (void); - void pressedKeyStar (void); - void pressedKeyHash (void); - - void volumeSpkrChanged (int); - void volumeMicChanged (int); - void registerSlot (void); - -protected: - // To handle the key pressed event - void keyPressEvent (QKeyEvent *); - -private: - // Configuration skin file - Point *pt; - - - // To construct ring rect pixmap - QImage imageRing; - QImage imageNoRing; - - QPixmap TabMsgPixmap[NUMBER_OF_STATES]; - int modeType; - DTMF *key; - short *buf; - bool apply; - - int onLine; - - // For numeric keypad - bool first; - - bool b_dialtone; - - - // Private functions - void setMode (int); - bool isInTextMode (void); - bool isInNumMode (void); - void initSkin (void); - void initButtons (void); - void initBlinkTimer (void); - void initSpkrVolumePosition (void); - void initMicVolumePosition (void); - void connections (void); - - int numLineBusy (void); - - int positionOffsetX (void); - void deleteButtons (void); - -}; - - -#endif // __QT_GUI_MAIN_WIDOW_H__ diff --git a/src/sip.cpp b/src/sip.cpp deleted file mode 100644 index 87d215385f1fa9fbec001c3dd5bd913b5766b43d..0000000000000000000000000000000000000000 --- a/src/sip.cpp +++ /dev/null @@ -1,1098 +0,0 @@ -/* - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * - * Portions Copyright (C) 2002,2003 Aymeric Moizard <jack@atosc.org> - * - * This 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 2, - * or (at your option) any later version. - * - * This 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 dpkg; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> -#include <time.h> - -#include <linux/socket.h> - -#include <qlineedit.h> -#include <qcheckbox.h> - -#include "audiocodec.h" -#include "configuration.h" -#include "error.h" -#include "global.h" -#include "sip.h" -#include "sipcall.h" - -#include <string> -using namespace std; - -// TODO : mettre dans config -#define DEFAULT_SIP_PORT 5060 -#define RANDOM_SIP_PORT rand() % 64000 + 1024 -#define DEFAULT_LOCAL_PORT 10500 -#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 - - -/////////////////////////////////////////////////////////////////////////////// -// Thread implementation -/////////////////////////////////////////////////////////////////////////////// -EventThread::EventThread (SIP *sip) : Thread () { - this->sipthread = sip; -} - -EventThread::~EventThread (void) { - this->terminate(); -} - -/** - * Reimplementation of run() to update widget - */ -void -EventThread::run (void) { - for (;;) { - sipthread->getEvent(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// SIP implementation -/////////////////////////////////////////////////////////////////////////////// - -SIP::SIP (Manager *_manager) { - this->callmanager = _manager; - this->myIPAddress = NULL; - - // For EventThread - evThread = new EventThread (this); - - // Call init to NULL - for (int i = 0; i < NUMBER_OF_LINES; i++) { - assert (i < NUMBER_OF_LINES); - this->call[i] = NULL; - } - notUsedLine = -1; -} - -SIP::~SIP (void) { - if (evThread != NULL) { - delete evThread; - evThread = NULL; - } - delete[] call; - delete myIPAddress; -} - -// Init eXosip and set user agent -int -SIP::initSIP (void) { - QString tmp; - - tmp = QString(PROGNAME) + "/" + QString(VERSION); - - // Set IP address - if ( getLocalIp() == -1 ) - return -1; - - srand (time(NULL)); - if (eXosip_init (NULL, NULL, DEFAULT_SIP_PORT) != 0) { - if (eXosip_init (NULL, NULL, RANDOM_SIP_PORT) != 0) { - qDebug("Cannot init eXosip"); - return -1; - } - } - - // If use STUN server, firewall address setup - if (callmanager->useStun()) { - eXosip_set_user_agent(tmp.ascii()); - StunAddress4 stunSvrAddr; - stunSvrAddr.addr = 0; - - // Stun server - string svr = Config::gets("Signalisations", "STUN.STUNserver"); - qDebug("address server stun = %s", svr.data()); - - // Convert char* to StunAddress4 structure - bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr); - if (!ret) { - qDebug("SIP: Stun server address not valid"); - } - - // Firewall address - callmanager->getInfoStun(stunSvrAddr); - eXosip_set_firewallip((callmanager->getFirewallAddress()).ascii()); - } - eXosip_set_user_agent(tmp.ascii()); - - evThread->start(); - return 0; -} - -void -SIP::quitSIP (void) { - eXosip_quit(); -} - -bool -SIP::isInRtpmap (int index, int payload, AudioCodec &codec) { - for (int i = 0; i < index; i++) { - if (codec.handleCodecs[i] == payload) { - return true; - } - } - return false; -} - -void -SIP::initRtpmapCodec (void) { - int payload; - char rtpmap[128]; - char tmp[64]; - - bzero(rtpmap, 128); - bzero(tmp, 64); - - AudioCodec codec; - /* reset all payload to fit application capabilities */ - eXosip_sdp_negotiation_remove_audio_payloads(); - - // Set rtpmap according to the supported codec order - for (int i = 0; i < NB_CODECS; i++) { - payload = codec.handleCodecs[i]; - - // Add payload to rtpmap if it is not already added - if (!isInRtpmap(i, payload, codec)) { - snprintf(rtpmap, 127, "%d %s/%d", payload, - codec.rtpmapPayload(payload), SAMPLING_RATE); - snprintf(tmp, 63, "%d", payload); - - eXosip_sdp_negotiation_add_codec( osip_strdup(tmp), NULL, - osip_strdup("RTP/AVP"), NULL, NULL, NULL, NULL,NULL, - osip_strdup(rtpmap)); - } - } -#if 0 - eXosip_sdp_negotiation_add_codec(osip_strdup("111"), - NULL, - osip_strdup("RTP/AVP"), - NULL, NULL, NULL, - NULL,NULL, - osip_strdup("111 speex/16000")); - - /* Those attributes should be added for speex - b=AS:110 20 - b=AS:111 20 - */ -#endif -} - -int -SIP::getLocalIp (void) { - if (myIPAddress == NULL) { - myIPAddress = new char[64]; - } - int ret = eXosip_guess_localip (2, myIPAddress, 64); - return ret; -} - -//Number of lines used -int -SIP::getNumberPendingCalls(void) { - int pos = 0; - int k; - - for (k = 0; k < NUMBER_OF_LINES; k++) { - assert (k < NUMBER_OF_LINES); - if (call[k] != NULL) { - pos++; - } - } - return pos; -} - -// Return the first line number not used -int -SIP::findLineNumberNotUsed (void) { - int k; - - for (k = 0; k < NUMBER_OF_LINES; k++) { - assert (k < NUMBER_OF_LINES); - if (call[k] == NULL) { - return k; - } - } - - // Every line is busy - // TODO: ATTENTION AU RETOUR - return -1; -} - -// For ringing, answered, proceeding call -int -SIP::findLineNumber (eXosip_event_t *e) { - int k; - - // Look for call with same cid/did - for (k = 0; k < NUMBER_OF_LINES; k++) { - if (call[k] != NULL) { - if (call[k]->cid == e->cid and call[k]->did == e->did) { - return k; - } - } - } - - // If not found, return free line ? - return findLineNumberNotUsed(); -} - -// For redirected, request,server,global failure, onhold, offhold call -int -SIP::findLineNumberUsed (eXosip_event_t *e) { - return findLineNumber (e); -} - -int -SIP::findLineNumberClosed (eXosip_event_t *e) { - for (int k = 0; k < NUMBER_OF_LINES; k++) { - if (call[k] != NULL) { - if (call[k]->cid == e->cid) { - return k; - } - } - } - - // TODO ATTENTION AU RETOUR - return -1; -} - -// Return call corresponding to pos -SipCall* -SIP::findCall (int pos) { - for (int k = 0; k < NUMBER_OF_LINES; k++) { - if (call[k] != NULL) { - if (pos == 0) { - return call[k]; - } - pos--; - } - } - return NULL; -} - -int -SIP::checkURI (const char *buffer) { - osip_uri_t *uri; - int i; - - // To parse a buffer containing a sip URI - i = osip_uri_init(&uri); - if (i != 0) { - qWarning ("Cannot allocate"); - return -1; - } - i = osip_uri_parse(uri, buffer); - if (i != 0) { - qWarning("Cannot parse uri"); - } - - // Free memory - osip_uri_free(uri); - return 0; -} - -// Parse url -int -SIP::checkUrl(char *url) { - int i; - - osip_from_t *to; - i = osip_from_init(&to); - if (i != 0) { - qWarning ("Cannot initialize"); - return -1; - } - i = osip_from_parse(to, url); - if (i != 0) { - qWarning ("Cannot parse url"); - return -1; - } - - // Free memory - osip_from_free (to); - return 0; -} - -int -SIP::setRegister (void) { - int reg_id = -1; - - string qproxy = "sip:" + Config::gets("Signalisations", "SIP.sipproxy"); - char * proxy = (char*)qproxy.data(); - - string qhostname = "sip:" + Config::gets("Signalisations", "SIP.hostPart"); - char * hostname = (char*)qhostname.data(); - - string qfrom = fromHeader(Config::gets("Signalisations", "SIP.userPart"), - Config::gets("Signalisations", "SIP.hostPart")); - char * from = (char*)qfrom.data(); - - qDebug("proxy = %s", proxy); - qDebug("from = %s", from); - - if (Config::gets("Signalisations", "SIP.userPart") == "") { - callmanager->error->errorName(USER_PART_FIELD_EMPTY, NULL); - return -1; - } - if (Config::gets("Signalisations", "SIP.hostPart") == "") { - callmanager->error->errorName(HOST_PART_FIELD_EMPTY, NULL); - return -1; - } - - eXosip_lock(); - if (setAuthentication() == -1) { - eXosip_unlock(); - return -1; - } - - if (Config::gets("Signalisations", "SIP.sipproxy") != "") { - reg_id = eXosip_register_init(from, proxy, NULL); - } else { - reg_id = eXosip_register_init(from, hostname, NULL); - } - - if (reg_id < 0) { - eXosip_unlock(); - return -1; - } - - // TODO: port SIP session timer dans config - int i = eXosip_register(reg_id, 3600); - if (i == -2) { - qDebug("cannot build registration, check the setup"); - } - if (i == -1) { - qDebug("Registration Failed"); - } - - eXosip_unlock(); - return 0; -} - -int -SIP::setAuthentication (void) { - string login, pass, realm; - login = Config::gets("Signalisations", "SIP.username"); - if (login == "") { - login = Config::gets("Signalisations", "SIP.userPart"); - } - pass = Config::gets("Signalisations", "SIP.password"); - if (pass == "") { - callmanager->error->errorName(PASSWD_FIELD_EMPTY, NULL); - return -1; - } - - if (callmanager->useStun()) { - realm = Config::gets("Signalisations", "SIP.hostPart"); - } else { - if (Config::gets("Signalisations", "SIP.sipproxy") != "") { - realm = Config::gets("Signalisations", "SIP.sipproxy"); - } else { - realm = Config::gets("Signalisations", "SIP.hostPart"); - } - } - - if (eXosip_add_authentication_info(login.data(), login.data(), - pass.data(), NULL, NULL) != 0) { - qDebug ("No authentication"); - return -1; - } - return 0; -} - -string -SIP::fromHeader (string user, string host) { - string displayname = Config::gets("Signalisations", "SIP.fullName"); - return ("\"" + displayname + "\"" + " <sip:" + user + "@" + host + ">"); -} - - -string -SIP::toHeader(string to) { - if (to.find("sip:") == string::npos) { - return ("sip:" + to ); - } else { - return to; - } -} - -int -SIP::startCall ( char *from, char *to, char *subject, char *route) { - osip_message_t *invite; - int i; - - if (checkUrl(from) != 0) { - callmanager->error->errorName(FROM_ERROR, NULL); - return -1; - } - if (checkUrl(to) != 0) { - callmanager->error->errorName(TO_ERROR, NULL); - return -1; - } - - i = eXosip_build_initial_invite(&invite, to, from, route, subject); - if (i != 0) { - return -1; - } - - eXosip_lock(); - if (!callmanager->useStun()) { - char port[64]; - - // Set random port for outgoing call - setLocalPort(RANDOM_LOCAL_PORT); - - bzero (port, 64); - snprintf (port, 63, "%d", getLocalPort()); - - i = eXosip_initiate_call (invite, NULL, NULL, port); - - } else { - QString qport; - qport = qport.number(callmanager->getFirewallPort()); - - i = eXosip_initiate_call(invite, NULL, NULL, (char*)qport.ascii()); - - qDebug("sip invite: port = %s",(char*)qport.ascii()); - } - if (i <= 0) { - qDebug("NO initiate_call = %d", i); - eXosip_unlock(); - return -1; - } - - eXosip_unlock(); - return i; -} - -int -SIP::outgoingInvite (void) { - char * from; - char * to; - - // Form the From header field basis on configuration panel - string qfrom = fromHeader(Config::gets("Signalisations", "SIP.userPart"), - Config::gets("Signalisations", "SIP.hostPart")); - from = (char*)qfrom.data(); - - // Form the To header field - string qto; - if (callmanager->bufferTextRender().ascii() == NULL) - return -1; - else - qto = toHeader(string(callmanager->bufferTextRender().ascii())); - - if (qto.find("@") == string::npos and - Config::getb("Signalisations", "SIP.autoregister")) { - qto = qto + "@" + Config::gets("Signalisations", "SIP.hostPart"); - } - to = (char*)qto.data(); - - qDebug ("From: %s", from); - qDebug ("To: <%s>", to); - - if (Config::gets("Signalisations", "SIP.sipproxy") == "") { - // If no SIP proxy setting for direct call with only IP address - if (startCall(from, to, NULL, NULL) <= 0) { - qDebug("SIP: no start call"); - return -1; - } - return 0; - } else { - // If SIP proxy setting - string qroute = "<sip:" + - Config::gets("Signalisations", "SIP.sipproxy") + ";lr>"; - char * route = (char*)qroute.data(); - if (startCall(from, to, NULL, route) <= 0) { - qDebug("SIP: no start call"); - return -1; - } - return 0; - } -} - -int -SIP::getLocalPort (void) { - return local_port; -} - -void -SIP::setLocalPort (int port) { - this->local_port = port; -} - -void -SIP::carryingDTMFdigits (int line, char digit) { - int duration = Config::geti("Signalisations", "DTMF.pulseLength"); - - static const int body_len = 128; - - char *dtmf_body = new char[body_len]; - snprintf(dtmf_body, body_len - 1, - "Signal=%c\r\nDuration=%d\r\n", - digit, duration); - - eXosip_lock(); - eXosip_info_call(call[line]->did, "application/dtmf-relay", dtmf_body); - eXosip_unlock(); - - delete[] dtmf_body; -} - -// Handle IP-Phone user actions -int -SIP::manageActions (int usedLine, int action) { - int i; - string referTo; - - char tmpbuf[64]; - - assert (usedLine < NUMBER_OF_LINES); - assert (usedLine >= 0); - - bzero (tmpbuf, 64); - // Get local port - snprintf (tmpbuf, 63, "%d", call[usedLine]->getLocalAudioPort()); - - switch (action) { - - // IP-Phone user want to refuse a call. - case REFUSE_CALL: - qDebug("REFUSE_CALL line %d, cid = %d, did = %d", - usedLine, call[usedLine]->cid, call[usedLine]->did); - - callmanager->phLines[usedLine]->setbInProgress(false); - - eXosip_lock(); - i = eXosip_answer_call(call[usedLine]->did, BUSY_HERE, tmpbuf); - eXosip_unlock(); - - callmanager->ringTone(false); - - // Delete the call when I hangup - if (call[usedLine] != NULL) { - delete call[usedLine]; - call[usedLine] = NULL; - } - - break; - - // IP-Phone user is answering a call. - case ANSWER_CALL: - qDebug("ANSWER_CALL line %d, cid = %d, did = %d", - usedLine, call[usedLine]->cid, call[usedLine]->did); - - callmanager->phLines[usedLine]->setbInProgress(false); - - eXosip_lock(); - i = eXosip_answer_call(call[usedLine]->did, 200, tmpbuf); - eXosip_unlock(); - - // Incoming call is answered, start the sound channel. - if (callmanager->startSound (call[usedLine]) < 0) { - qDebug ("FATAL: Unable to start sound (%s:%d)", - __FILE__, __LINE__); - exit(1); - } - break; - - // IP-Phone user is hanging up. - case CLOSE_CALL: // 1 - qDebug("CLOSE_CALL: cid = %d et did = %d", call[usedLine]->cid, - call[usedLine]->did); - - call[usedLine]->usehold = false; - // Release SIP stack. - eXosip_lock(); - i = eXosip_terminate_call (call[usedLine]->cid, call[usedLine]->did); - eXosip_unlock(); - - // Release RTP channels - call[usedLine]->closedCall(); - - // Delete the call when I hangup - if (call[usedLine] != NULL) { - qDebug("SIP: CLOSE_CALL delete call[%d]", usedLine); - delete call[usedLine]; - call[usedLine] = NULL; - } - break; - - // IP-Phone user is parking peer on HOLD - case ONHOLD_CALL: - qDebug("ONHOLD_CALL: cid = %d et did = %d", call[usedLine]->cid, - call[usedLine]->did); - - call[usedLine]->usehold = true; - - eXosip_lock(); - i = eXosip_on_hold_call(call[usedLine]->did); - eXosip_unlock(); - - // Disable audio - call[usedLine]->closedCall(); - break; - - // IP-Phone user is parking peer OFF HOLD - case OFFHOLD_CALL: - qDebug("OFF-HOLD_CALL: cid = %d et did = %d", call[usedLine]->cid, - call[usedLine]->did); - call[usedLine]->usehold = true; - eXosip_lock(); - i = eXosip_off_hold_call(call[usedLine]->did, NULL, 0); - eXosip_unlock(); - - // Enable audio - if (callmanager->startSound (call[usedLine]) < 0) { - qDebug ("FATAL: Unable to start sound (%s:%d)", - __FILE__, __LINE__); - exit(1); - } - break; - - // IP-Phone user is transfering call - case TRANSFER_CALL: - referTo = toHeader(string(callmanager->bufferTextRender().ascii())); - if (referTo.find("@") == string::npos) { - referTo = referTo + "@" + - Config::gets("Signalisations", "SIP.hostPart"); - } - - eXosip_lock(); - i = eXosip_transfer_call(call[usedLine]->did, (char*)referTo.data()); - eXosip_unlock(); - break; - - // IP-Phone user is hanging up during ringing - case CANCEL_CALL: - qDebug("SIP: CANCEL_CALL: terminate cid=%d did=%d", - call[usedLine]->cid,call[usedLine]->did); - - eXosip_lock(); - i = eXosip_terminate_call (call[usedLine]->cid, call[usedLine]->did); - eXosip_unlock(); - - callmanager->ringTone(false); - - // Delete the call when I hangup - if (call[usedLine] != NULL) { - delete call[usedLine]; - call[usedLine] = NULL; - } - break; - - default: - return -1; - break; - } - return 0; -} - - -// Receive events from the other -int -SIP::getEvent (void) { - eXosip_event_t *event; - int theline = -1; - int curLine; - char *name; - static int countReg = 0; - - event = eXosip_event_wait (0, 50); - if (event == NULL) { - return -1; - } - - callmanager->handleRemoteEvent(event->status_code, - event->reason_phrase, - -1); -// qDebug("status-code %d %s", event->status_code, event->reason_phrase); - switch (event->type) { - // IP-Phone user receives a new call - case EXOSIP_CALL_NEW: //13 - qDebug("<- (%i %i) INVITE from: %s", event->cid, event->did, - event->remote_uri); - - // Set local random port for incoming call - setLocalPort(RANDOM_LOCAL_PORT); - - theline = findLineNumberNotUsed(); - notUsedLine = theline; - - if (theline < 0) { - // TODO: remonter erreur au manager (on refuse l'appel) - } - assert (theline >= 0); assert (theline < NUMBER_OF_LINES); - - // Display the name which the call comes from - osip_from_t *from; - osip_from_init(&from); - osip_from_parse(from, event->remote_uri); - name = osip_from_get_displayname(from); - callmanager->nameDisplay(name); - callmanager->phLines[theline]->text = QString(name); - osip_from_free(from); - - if (call[theline] == NULL) { - //callmanager->setCallInProgress(true); - callmanager->phLines[theline]->setbInProgress(true); - call[theline] = new SipCall (callmanager); - call[theline]->newIncomingCall(event); - // callmanager->handleRemoteEvent ( - // 0, NULL, EXOSIP_CALL_NEW, theline); - - // Associate an audio port with a call - call[theline]->setLocalAudioPort(local_port); - - // Ringing starts when somebody calls IP-phone user - callmanager->ring(); - callmanager->phLines[theline]->setbRinging(true); - } - break; - - // The callee answers - case EXOSIP_CALL_ANSWERED: // 8, start call: event->status_code = 200 - qDebug("ANSWERED<- (%i %i) [%i %s] %s", event->cid, event->did, - event->status_code, event->reason_phrase, - event->remote_uri); - - // TODO: stop the ringtone - callmanager->ringTone(false); - - //theline = callmanager->getCurrentLineNumber(); - curLine = callmanager->getCurrentLineNumber(); - - // If we have chosen the line before validating the phone number - if (callmanager->isChosenLine() and !callmanager->otherLine()) { - theline = callmanager->chosenLine(); - } else { - theline = findLineNumber(event); - } - - if (call[theline] == NULL) { - call[theline] = new SipCall(callmanager); - } - - // Stop the call progress - //callmanager->setCallInProgress(false); - callmanager->phLines[theline]->setbInProgress(false); - - // Conditions to not start sound when callee answers for onhold - // and offhold state - if (callmanager->phLines[theline]->getStateLine() == BUSY or - callmanager->phLines[theline]->getStateLine() == OFFHOLD - or !call[theline]->usehold) { - if (!callmanager->transferedCall()) { - if (!call[theline]->usehold) { - // Associate an audio port with a call - call[theline]->setLocalAudioPort(local_port); - } - - // Answer - call[theline]->answeredCall(event); - - // Handle event - callmanager->handleRemoteEvent ( - 0, NULL, EXOSIP_CALL_ANSWERED); - - callmanager->setChoose (false, false); - - // Outgoing call is answered, start the sound channel. - if (callmanager->startSound (call[theline]) < 0) { - qDebug ("FATAL: Unable to start sound (%s:%d)", - __FILE__, __LINE__); - exit(1); - } - } - } else if (callmanager->otherLine() and - call[curLine] == NULL) { - // If a new line replaces the used current line and - // it's not ringing. - callmanager->startDialTone(); - } - break; - - case EXOSIP_CALL_RINGING: - qDebug("RINGING<- (%i %i) [%i %s] %s", event->cid, event->did, - event->status_code, event->reason_phrase, - event->remote_uri); - - callmanager->handleRemoteEvent (0, NULL, EXOSIP_CALL_RINGING); - // TODO : rajouter tonalite qd ca sonne - callmanager->ringTone(true); - - // If we have chosen the line before validating the phone number - if (callmanager->isChosenLine() and !callmanager->otherLine()) { - theline = callmanager->chosenLine(); - } else { - theline = findLineNumber(event); - } - - if (theline < 0) { - // TODO: remonter erreur au manager (on refuse l'appel) - } - assert (theline >= 0); - assert (theline < NUMBER_OF_LINES); - - callmanager->phLines[theline]->setbInProgress(true); - - if (call[theline] == NULL) { - call[theline] = new SipCall(callmanager); - call[theline]->setLocalAudioPort(local_port); - call[theline]->ringingCall(event); - } - break; - - case EXOSIP_CALL_REDIRECTED: - qDebug("REDIRECTED<- (%i %i) [%i %s] %s",event->cid, event->did, - event->status_code, event->reason_phrase, - event->remote_uri); - // theline = findLineNumberUsed(event); - //assert (theline >= 0); - //assert (theline < NUMBER_OF_LINES); - //? call[theline] = new SipCall(callmanager); - // call[theline]->redirectedCall(event); - break; - - case EXOSIP_CALL_REQUESTFAILURE: -#if 0 - qDebug("REQUESTFAILURE<- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); -#endif - if (callmanager->isChosenLine() and !callmanager->otherLine()) { - theline = callmanager->chosenLine(); - } else { - theline = findLineNumber(event); - } - - assert (theline >= 0); - assert (theline < NUMBER_OF_LINES); - // call[theline]->requestfailureCall(event); - callmanager->phLines[theline]->setbInProgress(false); - - // Handle 4XX errors - switch (event->status_code) { - case AUTH_REQUIRED: - if (setAuthentication() == -1) { - break; - } - eXosip_lock(); - eXosip_retry_call (event->cid); - eXosip_unlock(); - break; - case FORBIDDEN: - case NOT_FOUND: - case REQ_TIMEOUT: - case ADDR_INCOMPLETE: - callmanager->congestion(true); - break; - case REQ_TERMINATED: - break; - default: - //callmanager->setCallInProgress(false); - break; - } - break; - - case EXOSIP_CALL_SERVERFAILURE: - qDebug("SERVERFAILURE<- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - // Handle 5XX errors - switch (event->status_code) { - case SERVICE_UNAVAILABLE: - callmanager->ringTone(false); - callmanager->congestion(true); - break; - default: - break; - } - //theline = findLineNumberUsed(event); - //assert (theline >= 0); - //assert (theline < NUMBER_OF_LINES); - // call[theline]->serverfailureCall(event); - break; - - case EXOSIP_CALL_GLOBALFAILURE: - qDebug("GLOBALFAILURE<- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - // Handle 6XX errors - - // call[findLineNumberUsed(event)]->globalfailureCall(event); - break; - - // The remote peer closed the phone call(we received BYE). - case EXOSIP_CALL_CLOSED: - qDebug("<- (%i %i) BYE", event->cid, event->did); - - call[theline]->usehold = false; - theline = findLineNumber(event); - if (!callmanager->phLines[theline]->getbInProgress()) { - // If callee answered before closing call - // or callee not onhold by caller - theline = findLineNumberClosed(event); - call[theline]->closedCall(); - } else { - // If caller closes call before callee answers - //theline = notUsedLine; - theline = findLineNumber(event); - // Stop ringTone - callmanager->ringTone(false); - } - - assert (theline >= 0); - assert (theline < NUMBER_OF_LINES); - - if (call[theline] != NULL) { - delete call[theline]; - call[theline] = NULL; - callmanager->handleRemoteEvent( - 0, NULL, EXOSIP_CALL_CLOSED, theline); - } - break; - - case EXOSIP_CALL_HOLD: - qDebug("<- (%i %i) INVITE (On Hold) from: %s", event->cid, - event->did, event->remote_uri); - theline = findLineNumber(event); - call[theline]->closedCall(); - call[theline]->onholdCall(event); - break; - - case EXOSIP_CALL_OFFHOLD: - qDebug("<- (%i %i) INVITE (Off Hold) from: %s", event->cid, - event->did, event->remote_uri); - theline = findLineNumber(event); - if (callmanager->startSound (call[theline]) < 0) { - qDebug ("FATAL: Unable to start sound (%s:%d)", - __FILE__, __LINE__); - exit(1); - } - call[theline]->offholdCall(event); - break; - - case EXOSIP_REGISTRATION_SUCCESS: - qDebug("REGISTRATION_SUCCESS <- (%i) [%i %s] %s for REGISTER %s", - event->rid, event->status_code, event->reason_phrase, - event->remote_uri, event->req_uri); - callmanager->handleRemoteEvent(0,NULL,EXOSIP_REGISTRATION_SUCCESS); - break; - - case EXOSIP_REGISTRATION_FAILURE: -#if 0 - qDebug("REGISTRATION_FAILURE <- (%i) [%i %s] %s for REGISTER %s", - event->rid, event->status_code, event->reason_phrase, - event->remote_uri, event->req_uri); -#endif - if (countReg <= 3) { - setRegister(); - countReg++; - } - callmanager->handleRemoteEvent(0,NULL,EXOSIP_REGISTRATION_FAILURE); - break; - - case EXOSIP_OPTIONS_NEW: - qDebug("OPT_NEW <- (%i %i) OPTIONS from: %s", event->cid, - event->did, event->remote_uri); - - /* answer the OPTIONS method */ - /* 1: search for an existing call */ - int k; - for (k = 0; k < NUMBER_OF_LINES; k++) { - if (call[k] != NULL) { - if (call[k]->cid == event->cid) { - break; - } - } - } - // TODO: Que faire si rien trouve?? - eXosip_lock(); - if (call[k]->cid == event->cid) { - /* already answered! */ - } - else if (k == NUMBER_OF_LINES) { - /* answer 200 ok */ - eXosip_answer_options (event->cid, event->did, 200); - } else { - /* answer 486 ok */ - eXosip_answer_options (event->cid, event->did, 486); - } - eXosip_unlock(); - break; - - case EXOSIP_OPTIONS_ANSWERED: - qDebug("OPT_ANSWERED <- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - break; - - case EXOSIP_OPTIONS_PROCEEDING: - qDebug("OPT_PROCEEDING <- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - break; - - case EXOSIP_OPTIONS_REDIRECTED: - qDebug("OPT_REDIR <- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - break; - - case EXOSIP_OPTIONS_REQUESTFAILURE: - qDebug ("OPT_REQ_FAIL <- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - break; - - case EXOSIP_OPTIONS_SERVERFAILURE: - qDebug("OPT_SVR_FAIL <- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - break; - - case EXOSIP_OPTIONS_GLOBALFAILURE: - qDebug("OPT_GL_FAIL <- (%i %i) [%i %s] %s", event->cid, - event->did, event->status_code, event->reason_phrase, - event->remote_uri); - break; - - case EXOSIP_SUBSCRIPTION_NOTIFY: - qDebug("SUBS_NOTIFY <- (%i %i) NOTIFY from: %s", event->sid, - event->did, event->remote_uri); - qDebug("<- (%i %i) online=%i [status: %i reason:%i]", - event->sid, event->did, event->online_status, - event->ss_status, event->ss_reason); - //subscription_notify(je); - break; - - default: - return -1; - break; - } - eXosip_event_free(event); - - return 0; -} diff --git a/src/sip.h b/src/sip.h deleted file mode 100644 index f91b2360c9e38adc0c704e3daf9ea13087830eed..0000000000000000000000000000000000000000 --- a/src/sip.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2004 Savoir-Faire Linux inc. - * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> - * - * Portions Copyright (C) 2002,2003 Aymeric Moizard <jack@atosc.org> - * - * This 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 2, - * or (at your option) any later version. - * - * This 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 dpkg; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __SIP_H__ -#define __SIP_H__ - -#include <sys/time.h> - -#include <eXosip/eXosip.h> -#include <osip2/osip.h> -#include <osipparser2/osip_const.h> -#include <osipparser2/osip_headers.h> -#include <osipparser2/osip_body.h> - -#include <qthread.h> - -#include "audiocodec.h" -#include "manager.h" -#include "phoneline.h" -#include "sipcall.h" - -#include <cc++/thread.h> -#include <string> -using namespace std; -using namespace ost; - -// List of actions -#define ANSWER_CALL 0 -#define CLOSE_CALL 1 -#define ONHOLD_CALL 2 -#define OFFHOLD_CALL 3 -#define TRANSFER_CALL 4 -#define CANCEL_CALL 5 -#define REFUSE_CALL 6 - -// 1XX responses -#define DIALOG_ESTABLISHED 101 -// 4XX Errors -#define FORBIDDEN 403 -#define NOT_FOUND 404 -#define AUTH_REQUIRED 407 -#define REQ_TIMEOUT 408 -#define ADDR_INCOMPLETE 484 -#define BUSY_HERE 486 -#define REQ_TERMINATED 487 -// 5XX errors -#define SERVICE_UNAVAILABLE 503 - -class EventThread : public Thread { -public: - EventThread (SIP *); - ~EventThread (void); - virtual void run (); -private: - SIP * sipthread; -}; - - -class SIP { -public: - SIP (void) {} - SIP (Manager*); - ~SIP (void); - - int getNumberPendingCalls (void); - int findLineNumberNotUsed (void); - int findLineNumber (eXosip_event_t *); - int findLineNumberUsed (eXosip_event_t *); - int findLineNumberClosed (eXosip_event_t *); - SipCall *findCall (int pos); - int initSIP (void); - void quitSIP (void); - void initRtpmapCodec (void); - int checkURI (const char *); - int startCall (char *, char *, char *, char *); - int setRegister (void); - int setAuthentication (void); - string fromHeader (string, string); - string toHeader (string); - int outgoingInvite (void); - int manageActions (int, int); - int getEvent (void); - void carryingDTMFdigits (int, char); - int getLocalPort (void); - void setLocalPort (int); - - static int checkUrl (char *url); - - char *myIPAddress; - SipCall *call[NUMBER_OF_LINES]; - Manager *callmanager; - char displayName[64]; - - int notUsedLine; - -private: - EventThread *evThread; - - int getLocalIp (void); - bool isInRtpmap (int, int, AudioCodec&); - int local_port; -}; - -#endif // __SIP_H__ diff --git a/src/sipcall.cpp b/src/sipcall.cpp index 2a0318e4245e574281994d6b0fcd82d871756983..69aaec0aa40720a58e277d6998c180a67ee52aa9 100644 --- a/src/sipcall.cpp +++ b/src/sipcall.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (C) 2002,2003 Aymeric Moizard <jack@atosc.org> @@ -22,31 +22,100 @@ #include <osipparser2/sdp_message.h> #include <string.h> -#include "audiocodec.h" +#include <iostream> + +#include "audio/audiocodec.h" +#include "global.h" #include "sipcall.h" -SipCall::SipCall (void) { - this->alloc(); +using namespace std; + +SipCall::SipCall (short id, CodecDescriptorVector* cdv) +{ + _id = id; // Same id of Call object + alloc(); + _cdv = cdv; + _audiocodec = NULL; + _standby = false; + //this->usehold = false; +} + + +SipCall::~SipCall (void) +{ + dealloc(); +} + +void +SipCall::setLocalAudioPort (int newport) +{ + _local_audio_port = newport; +} + +int +SipCall::getLocalAudioPort (void) +{ + return _local_audio_port; +} + +void +SipCall::setId (short id) +{ + _id = id; +} + +short +SipCall::getId (void) +{ + return _id; } -SipCall::SipCall (Manager *mngr) { - this->alloc(); - this->manager = mngr; - this->usehold = false; +void +SipCall::setDid (int did) +{ + _did = did; } -SipCall::~SipCall (void) { - this->dealloc(); +int +SipCall::getDid (void) +{ + return _did; } void -SipCall::setLocalAudioPort (int newport) { - this->local_audio_port = newport; +SipCall::setCid (int cid) +{ + _cid = cid; +} + +int +SipCall::getCid (void) +{ + return _cid; } int -SipCall::getLocalAudioPort (void) { - return this->local_audio_port; +SipCall::getRemoteSdpAudioPort (void) +{ + return _remote_sdp_audio_port; +} + +char* +SipCall::getRemoteSdpAudioIp (void) +{ + return _remote_sdp_audio_ip; +} + +AudioCodec* +SipCall::getAudioCodec (void) +{ + return _audiocodec; +} + +void +SipCall::setAudioCodec (AudioCodec* ac) +{ + _audiocodec = ac; } // newIncomingCall is called when the IP-Phone user receives a new call. @@ -54,34 +123,37 @@ int SipCall::newIncomingCall (eXosip_event_t *event) { SipCall *ca = this; - this->cid = event->cid; - this->did = event->did; + _cid = event->cid; + _did = event->did; - if (this->did < 1 && this->cid < 1) { + if (_did < 1 && _cid < 1) { return -1; /* not enough information for this event?? */ } - osip_strncpy (this->textinfo, event->textinfo, 255); - osip_strncpy (this->req_uri, event->req_uri, 255); - osip_strncpy (this->local_uri, event->local_uri, 255); - osip_strncpy (this->local_uri, event->remote_uri, 255); - osip_strncpy (this->subject, event->subject, 255); + osip_strncpy (_textinfo, event->textinfo, 255); + osip_strncpy (_req_uri, event->req_uri, 255); + osip_strncpy (_local_uri, event->local_uri, 255); + osip_strncpy (_local_uri, event->remote_uri, 255); + osip_strncpy (_subject, event->subject, 255); + + osip_strncpy (ca->_remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); + ca->_remote_sdp_audio_port = event->remote_sdp_audio_port; - osip_strncpy (ca->remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); - ca->remote_sdp_audio_port = event->remote_sdp_audio_port; ca->payload = event->payload; - qDebug("For incoming ca->payload = %d",ca->payload); - osip_strncpy (ca->payload_name, event->payload_name, 49); - osip_strncpy (ca->sdp_body, event->sdp_body, 1000); - osip_strncpy (ca->sdp_body, event->sdp_body, 1000); - qDebug("%s", ca->sdp_body); + _debug("For incoming ca->_payload = %d\n", ca->payload); + osip_strncpy (ca->_payload_name, event->payload_name, 49); + setAudioCodec(_cdv->at(0)->alloc(ca->payload, ca->_payload_name)); + + osip_strncpy (ca->_sdp_body, event->sdp_body, 1000); + osip_strncpy (ca->_sdp_body, event->sdp_body, 1000); + _debug("\n%s\n", ca->_sdp_body); if (event->reason_phrase[0] != '\0') { - osip_strncpy(this->reason_phrase, event->reason_phrase, 49); - this->status_code = event->status_code; + osip_strncpy(this->_reason_phrase, event->reason_phrase, 49); + this->_status_code = event->status_code; } - this->state = event->type; + this->_state = event->type; return 0; } @@ -90,73 +162,73 @@ int SipCall::ringingCall (eXosip_event_t *event) { SipCall *ca = this; - this->cid = event->cid; - this->did = event->did; + this->_cid = event->cid; + this->_did = event->did; - if (this->did < 1 && this->cid < 1) { + if (this->_did < 1 && this->_cid < 1) { return -1; } - osip_strncpy(this->textinfo, event->textinfo, 255); - osip_strncpy(this->req_uri, event->req_uri, 255); - osip_strncpy(this->local_uri, event->local_uri, 255); - osip_strncpy(this->remote_uri, event->remote_uri, 255); - osip_strncpy(this->subject, event->subject, 255); + osip_strncpy(this->_textinfo, event->textinfo, 255); + osip_strncpy(this->_req_uri, event->req_uri, 255); + osip_strncpy(this->_local_uri, event->local_uri, 255); + osip_strncpy(this->_remote_uri, event->remote_uri, 255); + osip_strncpy(this->_subject, event->subject, 255); - osip_strncpy(ca->remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); - ca->remote_sdp_audio_port = event->remote_sdp_audio_port; + osip_strncpy(ca->_remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); + ca->_remote_sdp_audio_port = event->remote_sdp_audio_port; ca->payload = event->payload; - osip_strncpy(ca->payload_name, event->payload_name, 49); + osip_strncpy(ca->_payload_name, event->payload_name, 49); if (event->reason_phrase[0]!='\0') { - osip_strncpy(this->reason_phrase, event->reason_phrase, 49); - this->status_code = event->status_code; + osip_strncpy(this->_reason_phrase, event->reason_phrase, 49); + this->_status_code = event->status_code; } - this->state = event->type;; + this->_state = event->type;; return 0; } int SipCall::answeredCall(eXosip_event_t *event) { - AudioCodec* ac = new AudioCodec(); SipCall *ca = this; - this->cid = event->cid; - this->did = event->did; + _cid = event->cid; + _did = event->did; - if (this->did < 1 && this->cid < 1) { + if (_did < 1 && _cid < 1) { return -1; /* not enough information for this event?? */ } - osip_strncpy(this->textinfo, event->textinfo, 255); - osip_strncpy(this->req_uri, event->req_uri, 255); - osip_strncpy(this->local_uri, event->local_uri, 255); - osip_strncpy(this->remote_uri, event->remote_uri, 255); - osip_strncpy(this->subject, event->subject, 255); - - osip_strncpy(ca->remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); - ca->remote_sdp_audio_port = event->remote_sdp_audio_port; - osip_strncpy(ca->payload_name, event->payload_name, 49); - osip_strncpy (ca->sdp_body, event->sdp_body, 1000); - qDebug("%s", ca->sdp_body); - + osip_strncpy(this->_textinfo, event->textinfo, 255); + osip_strncpy(this->_req_uri, event->req_uri, 255); + osip_strncpy(this->_local_uri, event->local_uri, 255); + osip_strncpy(this->_remote_uri, event->remote_uri, 255); + osip_strncpy(this->_subject, event->subject, 255); + + osip_strncpy(ca->_remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); + ca->_remote_sdp_audio_port = event->remote_sdp_audio_port; + osip_strncpy(ca->_payload_name, event->payload_name, 49); + osip_strncpy (ca->_sdp_body, event->sdp_body, 1000); + _debug("\n%s\n", ca->_sdp_body); + // For outgoing calls, find the first payload of the remote user - int i; + int i, size; char temp[64]; bzero(temp, 64); + size = _cdv->size(); // Codec array in common with the 2 parts - int m_audio[NB_CODECS]; - for (int a = 0; a < NB_CODECS; a++) + int m_audio[size]; + for (int a = 0; a < size; a++) m_audio[a] = -1; sdp_message_t *sdp; i = sdp_message_init(&sdp); - if (i != 0) qDebug("Cannot allocate a SDP packet"); - i = sdp_message_parse(sdp, ca->sdp_body); - if (i != 0) qDebug("Cannot parse the SDP body"); + if (i != 0) _debug("Cannot allocate a SDP packet\n"); + i = sdp_message_parse(sdp, ca->_sdp_body); + if (i != 0) _debug("Cannot parse the SDP body\n"); int pos = 0; - if (sdp == NULL) qDebug("SDP = NULL"); + if (sdp == NULL) _debug("SDP = NULL\n"); /*********/ while (!sdp_message_endof_media (sdp, pos)) { int k = 0; @@ -167,10 +239,10 @@ SipCall::answeredCall(eXosip_event_t *event) { int c = 0; do { payload = sdp_message_m_payload_get (sdp, pos, k); - for (int j = 0; j < NB_CODECS; j++) { - snprintf(temp, 63, "%d", ac->handleCodecs[j]); + for (int j = 0; j < size; j++) { + snprintf(temp, 63, "%d", _cdv->at(j)->getPayload()); if (payload != NULL and strcmp(temp, payload) == 0) { - m_audio[c] = ac->handleCodecs[j]; + m_audio[c] = _cdv->at(j)->getPayload(); c++; break; } @@ -181,22 +253,23 @@ SipCall::answeredCall(eXosip_event_t *event) { pos++; } /***********/ - if (m_audio[0] == -1) - ac->noSupportedCodec(); - else + if (m_audio[0] == -1) { + noSupportedCodec(); + } else { ca->payload = m_audio[0]; + setAudioCodec(_cdv->at(0)->alloc(ca->payload, "")); + } - qDebug ("For outgoing call: ca->payload = %d", ca->payload); + _debug("For outgoing call: ca->_payload = %d\n", ca->payload); - sdp_message_free(sdp); - + if (event->reason_phrase[0]!='\0') { - osip_strncpy(this->reason_phrase, event->reason_phrase, 49); - this->status_code = event->status_code; + osip_strncpy(this->_reason_phrase, event->reason_phrase, 49); + this->_status_code = event->status_code; } - this->state = event->type; - delete ac; + this->_state = event->type; +// delete ac; return 0; } @@ -204,15 +277,15 @@ int SipCall::onholdCall (eXosip_event_t *event) { SipCall *ca = this; - osip_strncpy(ca->textinfo, event->textinfo, 255); + osip_strncpy(ca->_textinfo, event->textinfo, 255); - osip_strncpy(ca->remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); - ca->remote_sdp_audio_port = event->remote_sdp_audio_port; + osip_strncpy(ca->_remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); + ca->_remote_sdp_audio_port = event->remote_sdp_audio_port; - osip_strncpy(ca->reason_phrase, event->reason_phrase, 49); - ca->status_code = event->status_code; + osip_strncpy(ca->_reason_phrase, event->reason_phrase, 49); + ca->_status_code = event->status_code; - ca->state = event->type; + ca->_state = event->type; return 0; } @@ -220,15 +293,15 @@ int SipCall::offholdCall (eXosip_event_t *event) { SipCall *ca = this; - osip_strncpy(ca->textinfo, event->textinfo, 255); + osip_strncpy(ca->_textinfo, event->textinfo, 255); - osip_strncpy(ca->remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); - ca->remote_sdp_audio_port = event->remote_sdp_audio_port; + osip_strncpy(ca->_remote_sdp_audio_ip, event->remote_sdp_audio_ip, 49); + ca->_remote_sdp_audio_port = event->remote_sdp_audio_port; - osip_strncpy(ca->reason_phrase, event->reason_phrase, 49); - ca->status_code = event->status_code; + osip_strncpy(ca->_reason_phrase, event->reason_phrase, 49); + ca->_status_code = event->status_code; - ca->state = event->type; + ca->_state = event->type; return 0; } @@ -240,7 +313,7 @@ SipCall::redirectedCall(eXosip_event_t *event) { os_sound_close(ca); } */ - this->state = NOT_USED; + this->_state = NOT_USED; return 0; } @@ -249,7 +322,7 @@ SipCall::closedCall (void) { SipCall *ca = this; if (ca->enable_audio > 0) { - manager->closeSound(ca); + // manager->closeSound(ca); } return 0; @@ -257,27 +330,32 @@ SipCall::closedCall (void) { void SipCall::alloc(void) { - this->reason_phrase = new char[50]; - this->textinfo = new char[256]; - this->req_uri = new char[256]; - this->local_uri = new char[256]; - this->remote_uri = new char[256]; - this->subject = new char[256]; - this->remote_sdp_audio_ip = new char[50]; - this->payload_name = new char[50]; - this->sdp_body = new char[1000]; + this->_reason_phrase = new char[50]; + this->_textinfo = new char[256]; + this->_req_uri = new char[256]; + this->_local_uri = new char[256]; + this->_remote_uri = new char[256]; + this->_subject = new char[256]; + this->_remote_sdp_audio_ip = new char[50]; + this->_payload_name = new char[50]; + this->_sdp_body = new char[1000]; } void SipCall::dealloc(void) { - delete reason_phrase; - delete textinfo; - delete req_uri; - delete local_uri; - delete remote_uri; - delete subject; - delete remote_sdp_audio_ip; - delete payload_name; - delete sdp_body; + delete _reason_phrase; + delete _textinfo; + delete _req_uri; + delete _local_uri; + delete _remote_uri; + delete _subject; + delete _remote_sdp_audio_ip; + delete _payload_name; + delete _sdp_body; +} + +void +SipCall::noSupportedCodec (void) { + _debug("Codec no supported\n"); } diff --git a/src/sipcall.h b/src/sipcall.h index 60377058aab4d8dcef348f9c754d178da3dd26b6..ea7f9c3ac5b3d7c186003e639ce5df7652263c8f 100644 --- a/src/sipcall.h +++ b/src/sipcall.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * Portions Copyright (C) 2002,2003 Aymeric Moizard <jack@atosc.org> @@ -23,41 +23,25 @@ #define __SIP_CALL_H__ #include <eXosip/eXosip.h> -#include <qapplication.h> +#include <vector> -#include "manager.h" +#include "audio/audiocodec.h" +#include "audio/codecDescriptor.h" #define NOT_USED 0 +using namespace std; +typedef vector<CodecDescriptor*, allocator<CodecDescriptor*> > CodecDescriptorVector; + +class AudioCodec; class SipCall { public: - SipCall (void); - SipCall (Manager *); + SipCall (short id, CodecDescriptorVector* cdv); ~SipCall (void); - Manager *manager; - bool usehold; - - int cid; // call id - int did; // dialog id - - int status_code; - - char *reason_phrase; - char *textinfo; - char *req_uri; - char *local_uri; - char *remote_uri; - char *subject; - - char *remote_sdp_audio_ip; - char *payload_name; - char *sdp_body; - int local_audio_port; - int remote_sdp_audio_port; - int payload; - int state; - int enable_audio; /* 1 started, -1 stopped */ + bool usehold; + int payload; + int enable_audio; /* 1 started, -1 stopped */ int newIncomingCall (eXosip_event_t *); int answeredCall (eXosip_event_t *); @@ -74,11 +58,49 @@ public: void setLocalAudioPort (int); int getLocalAudioPort (void); + void setId (short id); + short getId (void); + void setDid (int did); + int getDid (void); + void setCid (int cid); + int getCid (void); + int getRemoteSdpAudioPort (void); + char* getRemoteSdpAudioIp (void); + AudioCodec* getAudioCodec (void); + void setAudioCodec (AudioCodec* ac); + + inline void setStandBy (bool standby) { _standby = standby; } + inline bool getStandBy (void) { return _standby; } private: - void alloc (void); - void dealloc (void); - + void alloc (void); + void dealloc (void); + void noSupportedCodec(void); + + CodecDescriptorVector* _cdv; + AudioCodec* _audiocodec; + + short _id; + int _cid; // call id + int _did; // dialog id + bool _standby; + + int _status_code; + + char* _reason_phrase; + char* _textinfo; + char* _req_uri; + char* _local_uri; + char* _remote_uri; + char* _subject; + + char* _remote_sdp_audio_ip; + char* _payload_name; + char* _sdp_body; + int _state; + int _local_audio_port; + int _remote_sdp_audio_port; + }; #endif // __SIP_CALL_H__ diff --git a/src/sipvoiplink.cpp b/src/sipvoiplink.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b4e45e1b524e4885fd8844f355ef97d847e7238b --- /dev/null +++ b/src/sipvoiplink.cpp @@ -0,0 +1,862 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <sys/time.h> + +#include <eXosip/eXosip.h> +#include <osip2/osip.h> +#include <osipparser2/osip_const.h> +#include <osipparser2/osip_headers.h> +#include <osipparser2/osip_body.h> + +#include <cc++/thread.h> +#include <iostream> +#include <string> +#include <vector> + +#include "audio/audiortp.h" +#include "call.h" +#include "eventthread.h" +#include "global.h" +#include "manager.h" +#include "sipcall.h" +#include "sipvoiplink.h" +#include "user_cfg.h" +#include "voIPLink.h" + +using namespace ost; +using namespace std; + +#define DEFAULT_SIP_PORT 5060 +#define RANDOM_SIP_PORT rand() % 64000 + 1024 +#define DEFAULT_LOCAL_PORT 10500 +#define RANDOM_LOCAL_PORT ((rand() % 27250) + 5250)*2 + + +SipVoIPLink::SipVoIPLink (void) : VoIPLink () +{ +// _evThread = new EventThread (this); + _sipcallVector = new SipCallVector(); + _localPort = 0; + _manager = NULL; +// _audiortp = new AudioRtp(_manager); +} + +SipVoIPLink::SipVoIPLink (short id, Manager* manager) : VoIPLink (id, manager) +{ + setId(id); + _localPort = 0; + _manager = manager; + _evThread = new EventThread (this); + _sipcallVector = new SipCallVector(); + _audiortp = new AudioRtp(_manager); +} + +SipVoIPLink::~SipVoIPLink (void) +{ + if (_evThread != NULL) { + delete _evThread; + _evThread = NULL; + } + delete _sipcallVector; + delete _audiortp; +} + +int +SipVoIPLink::init (void) +{ + string tmp; + + tmp = string(PROGNAME) + "/" + string(VERSION); + + // Set IP address + if (getLocalIp() == -1) + return -1; + + srand (time(NULL)); + if (eXosip_init (NULL, NULL, DEFAULT_SIP_PORT) != 0) { + if (eXosip_init (NULL, NULL, RANDOM_SIP_PORT) != 0) { + _debug("Cannot init eXosip\n"); + return -1; + } + } + // If use STUN server, firewall address setup + if (_manager->useStun()) { + eXosip_set_user_agent(tmp.data()); + StunAddress4 stunSvrAddr; + stunSvrAddr.addr = 0; + + // Stun server + string svr = get_config_fields_str(SIGNALISATION, STUN_SERVER); + + // Convert char* to StunAddress4 structure + bool ret = stunParseServerName ((char*)svr.data(), stunSvrAddr); + if (!ret) { + _debug("SIP: Stun server address not valid\n"); + } + + // Firewall address + _debug("STUN server: %s\n", svr.data()); + _manager->getStunInfo(stunSvrAddr); + eXosip_set_firewallip((_manager->getFirewallAddress()).data()); + } + + eXosip_set_user_agent(tmp.data()); + _evThread->start(); + initRtpmapCodec(); + return 1; +} + +bool +SipVoIPLink::isInRtpmap (int index, int payload, CodecDescriptorVector* cdv) { + for (int i = 0; i < index; i++) { + if (cdv->at(i)->getPayload() == payload) { + return true; + } + } + return false; +} + +void +SipVoIPLink::initRtpmapCodec (void) +{ + int payload; + unsigned int nb; + char rtpmap[128]; + char tmp[64]; + + bzero(rtpmap, 128); + bzero(tmp, 64); + + /* reset all payload to fit application capabilities */ + eXosip_sdp_negotiation_remove_audio_payloads(); + + // Set rtpmap according to the supported codec order + nb = _manager->getNumberOfCodecs(); + for (unsigned int i = 0; i < nb; i++) { + payload = _manager->getCodecDescVector()->at(i)->getPayload(); + + // Add payload to rtpmap if it is not already added + if (!isInRtpmap(i, payload, _manager->getCodecDescVector())) { + snprintf(rtpmap, 127, "%d %s/%d", payload, + _manager->getCodecDescVector()->at(i)->rtpmapPayload(payload).data(), + SAMPLING_RATE); + snprintf(tmp, 63, "%d", payload); + + eXosip_sdp_negotiation_add_codec( osip_strdup(tmp), NULL, + osip_strdup("RTP/AVP"), NULL, NULL, NULL, NULL,NULL, + osip_strdup(rtpmap)); + } + } +} + +void +SipVoIPLink::quit(void) +{ + eXosip_quit(); +} + +int +SipVoIPLink::setRegister (void) +{ + int reg_id = -1; + + string proxy = "sip:" + get_config_fields_str(SIGNALISATION, PROXY); + + string hostname = "sip:" + get_config_fields_str(SIGNALISATION, HOST_PART); + + string from = fromHeader(get_config_fields_str(SIGNALISATION, USER_PART), + get_config_fields_str(SIGNALISATION, HOST_PART)); + + if (get_config_fields_str(SIGNALISATION, HOST_PART).empty()) { + _manager->error()->errorName(HOST_PART_FIELD_EMPTY, NULL); + return -1; + } + if (get_config_fields_str(SIGNALISATION, USER_PART).empty()) { + _manager->error()->errorName(USER_PART_FIELD_EMPTY, NULL); + return -1; + } + + eXosip_lock(); + if (setAuthentication() == -1) { + eXosip_unlock(); + return -1; + } + + _debug("register From: %s\n", from.data()); + if (!get_config_fields_str(SIGNALISATION, PROXY).empty()) { + reg_id = eXosip_register_init((char*)from.data(), (char*)proxy.data(), + NULL); + } else { + reg_id = eXosip_register_init((char*)from.data(),(char*)hostname.data(), + NULL); + } + if (reg_id < 0) { + eXosip_unlock(); + return -1; + } + + // TODO: port SIP session timer dans config + int i = eXosip_register(reg_id, EXPIRES_VALUE); + if (i == -2) { + _debug("cannot build registration, check the setup\n"); + eXosip_unlock(); + return -1; + } + if (i == -1) { + _debug("Registration Failed\n"); + eXosip_unlock(); + return -1; + } + + eXosip_unlock(); + return 0; +} +int +SipVoIPLink::outgoingInvite (const string& to_url) +{ + string from; + string to; + + // Form the From header field basis on configuration panel + from = fromHeader(get_config_fields_str(SIGNALISATION, USER_PART), + get_config_fields_str(SIGNALISATION, HOST_PART)); + + to = toHeader(to_url); + + if (to.find("@") == string::npos and + get_config_fields_int(SIGNALISATION, AUTO_REGISTER) == YES) { + to = to + "@" + get_config_fields_str(SIGNALISATION, HOST_PART); + } + + _debug("From: %s\n", from.data()); + _debug("To: %s\n", to.data()); + + if (get_config_fields_str(SIGNALISATION, PROXY).empty()) { + // If no SIP proxy setting for direct call with only IP address + if (startCall(from, to, "", "") <= 0) { + _debug("Warning SipVoIPLink: call not started\n"); + return -1; + } + return 0; + } else { + // If SIP proxy setting + string route = "<sip:" + + get_config_fields_str(SIGNALISATION, PROXY) + ";lr>"; + if (startCall(from, to, "", route) <= 0) { + _debug("Warning SipVoIPLink: call not started\n"); + return -1; + } + return 0; + } +} + +int +SipVoIPLink::answer (short id) +{ + int i; + char tmpbuf[64]; + bzero (tmpbuf, 64); + // Get local port + snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort()); + + _debug("Answer call [id = %d, cid = %d, did = %d]\n", + id, getSipCall(id)->getCid(), getSipCall(id)->getDid()); + _debug("Local audio port: %d\n", getSipCall(id)->getLocalAudioPort()); + + eXosip_lock(); + i = eXosip_answer_call(getSipCall(id)->getDid(), 200, tmpbuf); + eXosip_unlock(); + + // Incoming call is answered, start the sound channel. + if (_audiortp->createNewSession (getSipCall(id)) < 0) { + _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); + exit(1); + } + return i; +} + +int +SipVoIPLink::hangup (short id) +{ + int i; + _debug("Hang up call [id = %d, cid = %d, did = %d]\n", + id, getSipCall(id)->getCid(), getSipCall(id)->getDid()); + // Release SIP stack. + eXosip_lock(); + i = eXosip_terminate_call (getSipCall(id)->getCid(), + getSipCall(id)->getDid()); + eXosip_unlock(); + + // Release RTP channels + _audiortp->closeRtpSession(getSipCall(id)); + + deleteSipCall(id); + return i; +} + +int +SipVoIPLink::onhold (short id) +{ + int i; + + eXosip_lock(); + i = eXosip_on_hold_call(getSipCall(id)->getDid()); + eXosip_unlock(); + + // Disable audio + _audiortp->closeRtpSession(getSipCall(id)); + return i; +} + +int +SipVoIPLink::offhold (short id) +{ + int i; + + eXosip_lock(); + i = eXosip_off_hold_call(getSipCall(id)->getDid(), NULL, 0); + eXosip_unlock(); + + // Enable audio + if (_audiortp->createNewSession (getSipCall(id)) < 0) { + _debug("FATAL: Unable to start sound (%s:%d)\n", __FILE__, __LINE__); + exit(1); + } + return i; +} + +int +SipVoIPLink::transfer (short id, const string& to) +{ + int i; + string tmp_to; + to = toHeader(to); + if (to.find("@") == string::npos) { + tmp_to = to + "@" + get_config_fields_str(SIGNALISATION, HOST_PART); + } + + eXosip_lock(); + i = eXosip_transfer_call(getSipCall(id)->getDid(), (char*)tmp_to.data()); + eXosip_unlock(); + return i; +} + +int +SipVoIPLink::refuse (short id) +{ + int i; + char tmpbuf[64]; + bzero (tmpbuf, 64); + // Get local port + snprintf (tmpbuf, 63, "%d", getSipCall(id)->getLocalAudioPort()); + + eXosip_lock(); + i = eXosip_answer_call(getSipCall(id)->getDid(), BUSY_HERE, tmpbuf); + eXosip_unlock(); + return i; +} + +int +SipVoIPLink::cancel (short id) +{ + int i; + eXosip_lock(); + i = eXosip_terminate_call (getSipCall(id)->getCid(), + getSipCall(id)->getDid()); + eXosip_unlock(); + return i; +} + +int +SipVoIPLink::getEvent (void) +{ + eXosip_event_t *event; + short id; + char *name; + static int countReg = 0; + + event = eXosip_event_wait (0, 50); + //event = eXosip_event_get(); + if (event == NULL) { + return -1; + } + switch (event->type) { + // IP-Phone user receives a new call + case EXOSIP_CALL_NEW: // + // Set local random port for incoming call + setLocalPort(RANDOM_LOCAL_PORT); + + id = _manager->generateNewCallId(); + _manager->pushBackNewCall(id, Incoming); + _debug("\nIncoming Call with identifiant %d [cid = %d, did = %d]\n", + id, event->cid, event->did); + _debug("Local audio port: %d\n", _localPort); + + // Display the callerId-name + osip_from_t *from; + osip_from_init(&from); + osip_from_parse(from, event->remote_uri); + name = osip_from_get_displayname(from); + _manager->displayTextMessage(id, name); + _manager->getCall(id)->setCallerIdName(name); + _debug("From: %s\n", name); + osip_from_free(from); + + getSipCall(id)->newIncomingCall(event); + _manager->incomingCall(id); + + // Associate an audio port with a call + getSipCall(id)->setLocalAudioPort(_localPort); + + _manager->displayStatus(RINGING_STATUS); + break; + + // The peer-user answers + case EXOSIP_CALL_ANSWERED: + id = findCallId(event); + if (id == 0) { + id = findCallIdWhenRinging(); + } + _debug("Call is answered [id = %d, cid = %d, did = %d]\n", + id, event->cid, event->did); + + // Answer + if (id > 0 and !_manager->getCall(id)->isOnHold() + and !_manager->getCall(id)->isOffHold()) { + getSipCall(id)->setLocalAudioPort(_localPort); + getSipCall(id)->answeredCall(event); + _manager->peerAnsweredCall(id); + + // Outgoing call is answered, start the sound channel. + if (_audiortp->createNewSession (getSipCall(id)) < 0) { + _debug("FATAL: Unable to start sound (%s:%d)\n", + __FILE__, __LINE__); + exit(1); + } + _manager->displayStatus(CONNECTED_STATUS); + } + break; + + case EXOSIP_CALL_RINGING: + id = findCallIdWhenRinging(); + + _debug("Call is ringing [id = %d, cid = %d, did = %d]\n", + id, event->cid, event->did); + + if (id > 0) { + getSipCall(id)->ringingCall(event); + _manager->peerRingingCall(id); + } + _manager->displayStatus(RINGING_STATUS); + break; + + case EXOSIP_CALL_REDIRECTED: + break; + + // The remote peer closed the phone call(we received BYE). + case EXOSIP_CALL_CLOSED: + id = findCallId(event); + _debug("Call is closed [id = %d, cid = %d, did = %d]\n", + id, event->cid, event->did); + + if (id > 0) { + _manager->peerHungupCall(id); + _audiortp->closeRtpSession(getSipCall(id)); + deleteSipCall(id); + _manager->displayStatus(HUNGUP_STATUS); + } + break; + + case EXOSIP_CALL_HOLD: + id = findCallId(event); + if (id > 0) { + getSipCall(id)->onholdCall(event); + } + break; + + case EXOSIP_CALL_OFFHOLD: + id = findCallId(event); + if (id > 0) { + getSipCall(id)->offholdCall(event); + } + break; + + case EXOSIP_CALL_REQUESTFAILURE: + id = findCallId(event); + + // Handle 4XX errors + switch (event->status_code) { + case AUTH_REQUIRED: + if (setAuthentication() == -1) { + break; + } + eXosip_lock(); + eXosip_retry_call (event->cid); + eXosip_unlock(); + break; + case FORBIDDEN: + case NOT_FOUND: + case REQ_TIMEOUT: + case ADDR_INCOMPLETE: + // Faire remonter l'erreur + _manager->displayError(event->reason_phrase); + _manager->congestion(true); + break; + case REQ_TERMINATED: + break; + default: + break; + } + break; + + case EXOSIP_CALL_SERVERFAILURE: + // Handle 5XX errors + switch (event->status_code) { + case SERVICE_UNAVAILABLE: + _manager->ringback(false); + _manager->congestion(true); + break; + default: + break; + } + break; + + case EXOSIP_CALL_GLOBALFAILURE: + // Handle 6XX errors + break; + + case EXOSIP_REGISTRATION_SUCCESS: + _debug("-- Registration succeeded --\n"); + _manager->displayStatus(LOGGED_IN_STATUS); + break; + + case EXOSIP_REGISTRATION_FAILURE: + if (countReg <= 3) { + setRegister(); + countReg++; + } + break; + + case EXOSIP_OPTIONS_NEW: + /* answer the OPTIONS method */ + /* 1: search for an existing call */ + unsigned int k; + + for (k = 0; k < _sipcallVector->size(); k++) { + if (_sipcallVector->at(k)->getCid() == event->cid) { + break; + } + } + + // TODO: Que faire si rien trouve?? + eXosip_lock(); + if (_sipcallVector->at(k)->getCid() == event->cid) { + /* already answered! */ + } + else if (k == _sipcallVector->size()) { + /* answer 200 ok */ + eXosip_answer_options (event->cid, event->did, 200); + } else { + /* answer 486 ok */ + eXosip_answer_options (event->cid, event->did, 486); + } + eXosip_unlock(); + break; + + case EXOSIP_OPTIONS_ANSWERED: + break; + + case EXOSIP_OPTIONS_PROCEEDING: + break; + + case EXOSIP_OPTIONS_REDIRECTED: + break; + + case EXOSIP_OPTIONS_REQUESTFAILURE: + break; + + case EXOSIP_OPTIONS_SERVERFAILURE: + break; + + case EXOSIP_OPTIONS_GLOBALFAILURE: + break; + + case EXOSIP_SUBSCRIPTION_NOTIFY: + break; + + default: + return -1; + break; + } + eXosip_event_free(event); + + return 0; +} + +int +SipVoIPLink::getLocalPort (void) +{ + return _localPort; +} + +void +SipVoIPLink::setLocalPort (int port) +{ + _localPort = port; +} + +void +SipVoIPLink::carryingDTMFdigits (short id, char code) { + int duration = get_config_fields_int(SIGNALISATION, PULSE_LENGTH); + + static const int body_len = 128; + + char *dtmf_body = new char[body_len]; + snprintf(dtmf_body, body_len - 1, + "Signal=%c\r\nDuration=%d\r\n", + code, duration); + + eXosip_lock(); + eXosip_info_call(getSipCall(id)->getDid(), "application/dtmf-relay", + dtmf_body); + eXosip_unlock(); + + delete[] dtmf_body; +} + +void +SipVoIPLink::newOutgoingCall (short callid) +{ + _sipcallVector->push_back(new SipCall(callid, + _manager->getCodecDescVector())); + if (getSipCall(callid) != NULL) { + getSipCall(callid)->setStandBy(true); + } +} + +void +SipVoIPLink::newIncomingCall (short callid) +{ + SipCall* sipcall = new SipCall(callid, _manager->getCodecDescVector()); + _debug("new SipCall @ 0X%d\n", sipcall); + _sipcallVector->push_back(sipcall); +} + +void +SipVoIPLink::deleteSipCall (short callid) +{ + unsigned int i = 0; + while (i < _sipcallVector->size()) { + if (_sipcallVector->at(i)->getId() == callid) { + _sipcallVector->erase(_sipcallVector->begin()+i); + // delete getSipCall(callid); + return; + } else { + i++; + } + } +} + +SipCall* +SipVoIPLink::getSipCall (short callid) +{ + for (unsigned int i = 0; i < _sipcallVector->size(); i++) { + if (_sipcallVector->at(i)->getId() == callid) { + return _sipcallVector->at(i); + } + } + return NULL; +} + +AudioCodec* +SipVoIPLink::getAudioCodec (short callid) +{ + return getSipCall(callid)->getAudioCodec(); +} +/////////////////////////////////////////////////////////////////////////////// +// Private functions +/////////////////////////////////////////////////////////////////////////////// + +int +SipVoIPLink::getLocalIp (void) +{ + char* myIPAddress; + if (getLocalIpAddress().empty()) { + myIPAddress = new char[64]; + } + int ret = eXosip_guess_localip (2, myIPAddress, 64); + setLocalIpAddress(string(myIPAddress)); + + return ret; +} + +int +SipVoIPLink::checkUrl (const string& url) +{ + int i; + + osip_from_t *to; + i = osip_from_init(&to); + if (i != 0) { + _debug("Warning: Cannot initialize\n"); + return -1; + } + i = osip_from_parse(to, url.data()); + if (i != 0) { + _debug("Warning: Cannot parse url\n"); + return -1; + } + + // Free memory + osip_from_free (to); + return 0; +} + +int +SipVoIPLink::setAuthentication (void) +{ + string login, pass, realm; + login = get_config_fields_str(SIGNALISATION, AUTH_USER_NAME); + if (login.empty()) { + login = get_config_fields_str(SIGNALISATION, USER_PART); + } + pass = get_config_fields_str(SIGNALISATION, PASSWORD); + if (pass.empty()) { + _manager->error()->errorName(PASSWD_FIELD_EMPTY, NULL); + return -1; + } + +#if 0 + if (_manager->useStun()) { + realm = get_config_fields_str(SIGNALISATION, HOST_PART); + } else { + if (!get_config_fields_str(SIGNALISATION, PROXY).empty()) { + realm = get_config_fields_str(SIGNALISATION, PROXY); + } else { + realm = get_config_fields_str(SIGNALISATION, HOST_PART); + } + } +#endif + + if (eXosip_add_authentication_info(login.data(), login.data(), + pass.data(), NULL, NULL) != 0) { + _debug("No authentication\n"); + return -1; + } + return 0; +} + +string +SipVoIPLink::fromHeader (const string& user, const string& host) +{ + string displayname = get_config_fields_str(SIGNALISATION, FULL_NAME); + return ("\"" + displayname + "\"" + " <sip:" + user + "@" + host + ">"); +} + + +string +SipVoIPLink::toHeader(const string& to) +{ + if (to.find("sip:") == string::npos) { + return ("sip:" + to ); + } else { + return to; + } +} + +int +SipVoIPLink::startCall (const string& from, const string& to, + const string& subject, const string& route) +{ + osip_message_t *invite; + int i; + + if (checkUrl(from) != 0) { + _manager->error()->errorName(FROM_ERROR, NULL); + return -1; + } + if (checkUrl(to) != 0) { + _manager->error()->errorName(TO_ERROR, NULL); + return -1; + } + + i = eXosip_build_initial_invite(&invite,(char*)to.data(),(char*)from.data(), + (char*)route.data(), (char*)subject.data()); + if (i != 0) { + return -1; + } + + eXosip_lock(); + + char port[64]; + if (!_manager->useStun()) { + // Set random port for outgoing call + setLocalPort(RANDOM_LOCAL_PORT); + _debug("Local audio port: %d\n",_localPort); + + bzero (port, 64); + snprintf (port, 63, "%d", getLocalPort()); + + i = eXosip_initiate_call (invite, NULL, NULL, port); + + } else { + // If use Stun server + bzero (port, 64); + snprintf (port, 63, "%d", _manager->getFirewallPort()); + + i = eXosip_initiate_call(invite, NULL, NULL, port); + + _debug("sip invite: firewall port = %s\n", port); + } + + if (i <= 0) { + eXosip_unlock(); + return -1; + } + + eXosip_unlock(); + return i; +} + +short +SipVoIPLink::findCallId (eXosip_event_t *e) +{ + unsigned int k; + + for (k = 0; k < _sipcallVector->size(); k++) { + if (_sipcallVector->at(k)->getCid() == e->cid and + _sipcallVector->at(k)->getDid() == e->did) { + return _sipcallVector->at(k)->getId(); + } + } + return 0; +} + +short +SipVoIPLink::findCallIdWhenRinging (void) +{ + unsigned int k; + + for (k = 0; k < _sipcallVector->size(); k++) { + if (_sipcallVector->at(k)->getStandBy()) { + return _sipcallVector->at(k)->getId(); + } + } + return 0; +} diff --git a/src/sipvoiplink.h b/src/sipvoiplink.h new file mode 100644 index 0000000000000000000000000000000000000000..deeca33368fba35dbd173579706c1c922a3f0bb0 --- /dev/null +++ b/src/sipvoiplink.h @@ -0,0 +1,124 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SIP_VOIP_LINK_H__ +#define __SIP_VOIP_LINK_H__ + +#include <eXosip/eXosip.h> + +#include <string> +#include <vector> + +#include "audio/audiocodec.h" +#include "audio/codecDescriptor.h" +#include "voIPLink.h" + +using namespace std; + +#define EXPIRES_VALUE 3600 +// 1XX responses +#define DIALOG_ESTABLISHED 101 +// 4XX Errors +#define FORBIDDEN 403 +#define NOT_FOUND 404 +#define AUTH_REQUIRED 407 +#define REQ_TIMEOUT 408 +#define ADDR_INCOMPLETE 484 +#define BUSY_HERE 486 +#define REQ_TERMINATED 487 +// 5XX errors +#define SERVICE_UNAVAILABLE 503 + +class AudioCodec; +class AudioRtp; +class EventThread; +class Manager; +class SipCall; + +typedef vector<SipCall*, allocator<SipCall*> > SipCallVector; +typedef vector<CodecDescriptor*, allocator<CodecDescriptor*> > CodecDescriptorVector; + +class SipVoIPLink : public VoIPLink { +public: + SipVoIPLink (void); + SipVoIPLink (short id, Manager* manager); + virtual ~SipVoIPLink (void); + + virtual int init (void); + virtual void initRtpmapCodec (void); + virtual void quit (void); + virtual int setRegister (void); + virtual int outgoingInvite (const string& to_url); + virtual int answer (short id); + virtual int hangup (short id); + virtual int onhold (short id); + virtual int offhold (short id); + virtual int transfer (short id, const string& to); + virtual int refuse (short id); + virtual int cancel (short id); + virtual int getEvent (void); + virtual void carryingDTMFdigits (short id, char code); + + int getLocalPort (void); + void setLocalPort (int); + + /* + * Add a new SipCall at the end of the SipCallVector with identifiant 'id' + */ + void newOutgoingCall(short callid); + void newIncomingCall(short callid); + + /* + * Erase the call(id) from the SipCallVector + * and delete this call + */ + void deleteSipCall(short callid); + + /* + * Return a pointer to the SipCall with identifiant 'id' + */ + SipCall* getSipCall(short callid); + + AudioCodec* getAudioCodec(short callid); + +private: + int getLocalIp (void); + int checkUrl(const string& url); + int setAuthentication (void); + string fromHeader (const string& user, const string& host); + string toHeader(const string& to); + int startCall (const string& from, const string& to, + const string& subject, const string& route); + /* + * Look for call with same cid/did + * Return the id of the found call + */ + short findCallId (eXosip_event_t *e); + short findCallIdWhenRinging (void); + bool isInRtpmap (int index, int payload, CodecDescriptorVector* cdv); + + Manager* _manager; + EventThread* _evThread; + SipCallVector* _sipcallVector; + AudioRtp* _audiortp; + int _localPort; + +}; + +#endif // __SIP_VOIP_LINK_H__ diff --git a/src/skin.cpp b/src/skin.cpp index 20c538330664c73c1f4279643f86d7df6f18b9d2..eb02d22bd22ead90088864965b55543a6567802d 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -17,7 +17,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <qstring.h> #include "global.h" #include "skin.h" @@ -47,33 +46,33 @@ Skin::Skin (void) { Skin::~Skin (void) { } -QString -Skin::getPath (const QString & prefix, const QString & progname, - const QString & skindir, const QString & skin, - const QString & filename) { +string +Skin::getPath (const string & prefix, const string & progname, + const string & skindir, const string & skin, + const string & filename) { return (prefix + "/" + progname + "/" + skindir + "/" + skin + "/" + filename); } -QString -Skin::getPath (const QString & skindir, const QString & skintype, - const QString & filename) { - return (QString(PROGSHAREDIR) + "/" + skindir + "/" + skintype + "/" + filename); +string +Skin::getPath (const string & skindir, const string & skintype, + const string & filename) { + return (string(PROGSHAREDIR) + "/" + skindir + "/" + skintype + "/" + filename); } -QString -Skin::getPath (const QString & dir) { - return (QString(PROGSHAREDIR) + "/" + dir); +string +Skin::getPath (const string & dir) { + return (string(PROGSHAREDIR) + "/" + dir); } -QString -Skin::getPathPixmap (const QString & pixdir, const QString & filename) { - return (QString(PROGSHAREDIR) + "/" + pixdir + "/" + filename); +string +Skin::getPathPixmap (const string & pixdir, const string & filename) { + return (string(PROGSHAREDIR) + "/" + pixdir + "/" + filename); } -QString -Skin::getPathRing (const QString & ringdir, const QString & filename) { - return (QString(PROGSHAREDIR) + "/" + ringdir + "/" + filename); +string +Skin::getPathRing (const string & ringdir, const string & filename) { + return (string(PROGSHAREDIR) + "/" + ringdir + "/" + filename); } // EOF diff --git a/src/skin.h b/src/skin.h index bb175e2a1f7a217518ac4e454b5ea082386e963c..025a0bcfbda31dac1756cb253cb542b7d1e5b72e 100644 --- a/src/skin.h +++ b/src/skin.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2004 Savoir-Faire Linux inc. + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,8 @@ #ifndef __SKIN_H__ #define __SKIN_H__ +#include <string> + #include "global.h" #ifdef __cplusplus @@ -98,18 +100,19 @@ extern const char* PIXMAP_LINE_NAMES[]; } #endif +using namespace std; class Skin { public: Skin (void); ~Skin (void); - static QString getPath(const QString &, const QString &, const QString &, - const QString &, const QString &); - static QString getPath(const QString &,const QString &, const QString &); - static QString getPath(const QString &); - static QString getPathPixmap (const QString &, const QString &); - static QString getPathRing (const QString &, const QString &); + static string getPath(const string &, const string &, const string &, + const string &, const string &); + static string getPath(const string &,const string &, const string &); + static string getPath(const string &); + static string getPathPixmap (const string &, const string &); + static string getPathRing (const string &, const string &); }; #endif // __SKIN_H__ diff --git a/src/user_cfg.h b/src/user_cfg.h new file mode 100644 index 0000000000000000000000000000000000000000..c0d6701cd77e738d44a25e7874e8ee61a4ffc89e --- /dev/null +++ b/src/user_cfg.h @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2004-2005 Savoir-Faire Linux inc. + * Author: Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __USER_CFG_H__ +#define __USER_CFG_H__ + +#define ALSA 1 +#define OSS 1 +#define QT_GUI 1 + +#include <stdlib.h> + +#include "configuration.h" + +#define fill_config_fields_str(main_menu, value, default_value) \ + (Config::get(string(main_menu), string(value), string(default_value))) + +#define fill_config_fields_int(main_menu, value, default_value) \ + (Config::get(string(main_menu), string(value), default_value)) + +#define get_config_fields_str(main_menu, value) \ + (Config::gets(string(main_menu), string(value))) + +#define get_config_fields_int(main_menu, value) \ + (Config::geti(string(main_menu), value)) + +// Home directory +#define HOMEDIR (getenv ("HOME")) + +// Main menu +#define SIGNALISATION "VoIPLink" +#define AUDIO "Audio" +#define VIDEO "Video" +#define NETWORK "Network" +#define PREFERENCES "Preferences" + +// Fields to fill +#define VOIP_LINK_ID "VoIPLink.index" +#define FULL_NAME "SIP.fullName" +#define USER_PART "SIP.userPart" +#define AUTH_USER_NAME "SIP.username" +#define PASSWORD "SIP.password" +#define HOST_PART "SIP.hostPart" +#define PROXY "SIP.proxy" +#define AUTO_REGISTER "SIP.autoregister" +#define PLAY_TONES "DTMF.playTones" +#define PULSE_LENGTH "DTMF.pulseLength" +#define SEND_DTMF_AS "DTMF.sendDTMFas" +#define STUN_SERVER "STUN.STUNserver" +#define USE_STUN "STUN.useStun" +#define DRIVER_NAME "Drivers.driverName" +#define NB_CODEC "Codecs.nbCodec" +#define CODEC1 "Codecs.codec1" +#define CODEC2 "Codecs.codec2" +#define CODEC3 "Codecs.codec3" +#define CODEC4 "Codecs.codec4" +#define CODEC5 "Codecs.codec5" +#define RING_CHOICE "Rings.ringChoice" +#define VOLUME_SPKR_X "Volume.speakers_x" +#define VOLUME_SPKR_Y "Volume.speakers_y" +#define VOLUME_MICRO_X "Volume.micro_x" +#define VOLUME_MICRO_Y "Volume.micro_y" +#define SKIN_CHOICE "Themes.skinChoice" +#define CONFIRM_QUIT "Options.confirmQuit" +#define ZONE_TONE "Options.zoneToneChoice" +#define CHECKED_TRAY "Options.checkedTray" +#define VOICEMAIL_NUM "Themes.voicemailNumber" + +// Default values +#define DFT_VOIP_LINK 0 // index of the first VoIP link by default +#define EMPTY_FIELD "" +#define YES 1 +#define NO 0 +#define DFT_PULSE_LENGTH 250 +#define SIP_INFO 0 +#define DFT_STUN_SERVER "stun.fwdnet.net:3478" +#define DFT_DRIVER OSS_DRIVER +#define DFT_NB_CODEC 3 +#define DFT_CODEC "G711u" +#define DFT_VOL_SPKR_X 365 +#define DFT_VOL_SPKR_Y 100 +#define DFT_VOL_MICRO_X 347 +#define DFT_VOL_MICRO_Y 100 +#define DFT_RINGTONE "konga.ul" +#define DFT_SKIN "metal" +#define DFT_ZONE "North America" +#define DFT_VOICEMAIL "888" + + +#endif // __USER_CFG_H__ diff --git a/src/voIPLink.cpp b/src/voIPLink.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e84c2a2481b068370b0549a110a66f2f3e09e99d --- /dev/null +++ b/src/voIPLink.cpp @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2005 Savoir-Faire Linux inc. + * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <string> + +#include "user_cfg.h" +#include "voIPLink.h" + + +using namespace std; + +VoIPLink::VoIPLink (void) +{ + _id = 1; + _manager = NULL; + initConstructor(); +} + +VoIPLink::VoIPLink (short id, Manager* manager) +{ + _id = id; + _manager = manager; + initConstructor(); +} + +VoIPLink::~VoIPLink (void) +{ + +} + +void +VoIPLink::setId (short id) +{ + _id = id; +} + +short +VoIPLink::getId (void) +{ + return _id; +} + +void +VoIPLink::setType (VoIPLinkType type) +{ + _type = type; +} + +VoIPLinkType +VoIPLink::getType (void) +{ + return _type; +} + +void +VoIPLink::setFullName (const string& fullname) +{ + _fullname = fullname; +} + +string +VoIPLink::getFullName (void) +{ + return _fullname; +} + +void +VoIPLink::setHostName (const string& hostname) +{ + _hostname = hostname; +} + +string +VoIPLink::getHostName (void) +{ + return _hostname; +} + +void +VoIPLink::setLocalIpAddress (const string& ipAdress) +{ + _localIpAddress = ipAdress; +} + +string +VoIPLink::getLocalIpAddress (void) +{ + return _localIpAddress; +} + +void +VoIPLink::initConstructor(void) +{ + _type = Sip; + _fullname = get_config_fields_str(SIGNALISATION,FULL_NAME); + _hostname = get_config_fields_str(SIGNALISATION,HOST_PART); + _localIpAddress = ""; +} diff --git a/src/voIPLink.h b/src/voIPLink.h new file mode 100644 index 0000000000000000000000000000000000000000..362d6abc1d941f0bf902e8265318a0fb8f9f5a43 --- /dev/null +++ b/src/voIPLink.h @@ -0,0 +1,86 @@ +/** + * Copyright (C) 2005 Savoir-Faire Linux inc. + * Author : Laurielle Lea <laurielle.lea@savoirfairelinux.com> + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __VOIP_LINK_H__ +#define __VOIP_LINK_H__ + +#include <string> +#include "audio/audiocodec.h" + +using namespace std; + +enum VoIPLinkType { + Sip = 0, + Iax +}; + +class Call; +class Manager; +class VoIPLink { +public: + VoIPLink (void); + VoIPLink (short id, Manager* manager); + virtual ~VoIPLink (void); + + virtual int getEvent (void) = 0; + virtual int init (void) = 0; + virtual void initRtpmapCodec (void) = 0; + virtual void quit (void) = 0; + virtual void newOutgoingCall (short callid) = 0; + virtual void newIncomingCall (short callid) = 0; + virtual void deleteSipCall (short callid) = 0; + virtual int outgoingInvite (const string& to_url) = 0; + virtual int answer (short id) = 0; + virtual int hangup (short id) = 0; + virtual int onhold (short id) = 0; + virtual int offhold (short id) = 0; + virtual int transfer (short id, const string& to) = 0; + virtual int refuse (short id) = 0; + virtual int cancel (short id) = 0; + virtual int setRegister (void) = 0; + virtual void carryingDTMFdigits(short id, char code) = 0; + + virtual AudioCodec* getAudioCodec (short callid) = 0; + + void setId (short id); + short getId (void); + void setType (VoIPLinkType type); + VoIPLinkType getType (void); + void setFullName (const string& fullname); + string getFullName (void); + void setHostName (const string& hostname); + string getHostName (void); + void setLocalIpAddress (const string& ipAdress); + string getLocalIpAddress (void); + + +protected: + +private: + void initConstructor(void); + + Manager* _manager; + short _id; + VoIPLinkType _type; + string _fullname; + string _hostname; + string _localIpAddress; +}; + +#endif // __VOIP_LINK_H__ diff --git a/stund/client b/stund/client index 3df9eb511fe93a26e2a706ec1b75bc6403c2a36a..7a2ed7846fb75a5434f69c417c2f007572411965 100755 Binary files a/stund/client and b/stund/client differ diff --git a/stund/server b/stund/server index 13a3a0586404eb0fc6d98fc8950e3cad95be9ba9..ad65a85d87bfd058ede3b75d283fcd1912e82d19 100755 Binary files a/stund/server and b/stund/server differ diff --git a/stund/udp.h b/stund/udp.h index e3ec4e204350f66b862f1f93c05aa41550b8c63a..28af798d180c0ac2c7441d6ade2c03008c04dfc8 100644 --- a/stund/udp.h +++ b/stund/udp.h @@ -57,8 +57,8 @@ inline int getErrno() { return WSAGetLastError(); } #else -typedef int Socket; -//#define Socket int +//typedef int Socket; +#define Socket int #define INVALID_SOCKET -1 #define SOCKET_ERROR -1