diff options
Diffstat (limited to 'SwifTools')
35 files changed, 718 insertions, 125 deletions
diff --git a/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp b/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp index 5eb0e16..e7a47a7 100644 --- a/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp +++ b/SwifTools/Application/UnitTest/ApplicationPathProviderTest.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -10,4 +10,5 @@ #include <boost/algorithm/string.hpp> +#include <Swiften/Base/Path.h> #include <SwifTools/Application/PlatformApplicationPathProvider.h> @@ -41,5 +42,5 @@ class ApplicationPathProviderTest : public CppUnit::TestFixture { boost::filesystem::path dir = testling_->getExecutableDir(); CPPUNIT_ASSERT(boost::filesystem::is_directory(dir)); - CPPUNIT_ASSERT(boost::ends_with(dir.string(), "UnitTest")); + CPPUNIT_ASSERT(boost::ends_with(pathToString(dir), "UnitTest")); } diff --git a/SwifTools/Application/UnixApplicationPathProvider.cpp b/SwifTools/Application/UnixApplicationPathProvider.cpp index 784256f..6af9b24 100644 --- a/SwifTools/Application/UnixApplicationPathProvider.cpp +++ b/SwifTools/Application/UnixApplicationPathProvider.cpp @@ -7,4 +7,5 @@ #include <SwifTools/Application/UnixApplicationPathProvider.h> +#include <stdlib.h> #include <unistd.h> #include <boost/algorithm/string.hpp> diff --git a/SwifTools/Application/WindowsApplicationPathProvider.cpp b/SwifTools/Application/WindowsApplicationPathProvider.cpp index 8514fa7..2c61208 100644 --- a/SwifTools/Application/WindowsApplicationPathProvider.cpp +++ b/SwifTools/Application/WindowsApplicationPathProvider.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -8,4 +8,6 @@ #include <windows.h> +#include <cassert> +#include <Swiften/Base/String.h> namespace Swift { @@ -16,3 +18,20 @@ WindowsApplicationPathProvider::WindowsApplicationPathProvider(const std::string } +boost::filesystem::path WindowsApplicationPathProvider::getDataDir() const { + wchar_t* appDirRaw = _wgetenv(L"APPDATA"); + assert(appDirRaw); + boost::filesystem::path result( + boost::filesystem::path(appDirRaw) / getApplicationName()); + boost::filesystem::create_directory(result); + return result; +} + +boost::filesystem::path WindowsApplicationPathProvider::getHomeDir() const { + //FIXME: This should be My Documents + wchar_t* homeDirRaw = _wgetenv(L"USERPROFILE"); + assert(homeDirRaw); + return boost::filesystem::path(homeDirRaw); +} + + } diff --git a/SwifTools/Application/WindowsApplicationPathProvider.h b/SwifTools/Application/WindowsApplicationPathProvider.h index f288326..a4e8668 100644 --- a/SwifTools/Application/WindowsApplicationPathProvider.h +++ b/SwifTools/Application/WindowsApplicationPathProvider.h @@ -14,17 +14,6 @@ namespace Swift { WindowsApplicationPathProvider(const std::string& name); - boost::filesystem::path getDataDir() const { - char* appDirRaw = getenv("APPDATA"); - boost::filesystem::path result(boost::filesystem::path(appDirRaw) / getApplicationName()); - boost::filesystem::create_directory(result); - return result; - } - - boost::filesystem::path getHomeDir() const { - //FIXME: This should be My Documents - - char* homeDirRaw = getenv("USERPROFILE"); - return boost::filesystem::path(homeDirRaw); - } + boost::filesystem::path getDataDir() const; + boost::filesystem::path getHomeDir() const; virtual std::vector<boost::filesystem::path> getResourceDirs() const { diff --git a/SwifTools/Cocoa/CocoaAction.h b/SwifTools/Cocoa/CocoaAction.h index a46ef7c..0ef993e 100644 --- a/SwifTools/Cocoa/CocoaAction.h +++ b/SwifTools/Cocoa/CocoaAction.h @@ -10,7 +10,5 @@ #include <boost/function.hpp> -@interface CocoaAction : NSObject { - boost::function<void ()>* function; -} +@interface CocoaAction : NSObject /** diff --git a/SwifTools/Cocoa/CocoaAction.mm b/SwifTools/Cocoa/CocoaAction.mm index d560787..7162cd2 100644 --- a/SwifTools/Cocoa/CocoaAction.mm +++ b/SwifTools/Cocoa/CocoaAction.mm @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -7,8 +7,10 @@ #include <SwifTools/Cocoa/CocoaAction.h> -@implementation CocoaAction +@implementation CocoaAction { + boost::function<void ()>* function; +} - (id) initWithFunction: (boost::function<void()>*) f { - if ([super init]) { + if ((self = [super init])) { function = f; } diff --git a/SwifTools/CrashReporter.cpp b/SwifTools/CrashReporter.cpp new file mode 100644 index 0000000..47015df --- /dev/null +++ b/SwifTools/CrashReporter.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2012-2014 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swiften/Base/Log.h> +#include <SwifTools/CrashReporter.h> +#include <Swiften/Base/Platform.h> +#include <Swiften/Base/Path.h> + +#if defined(HAVE_BREAKPAD) + +#pragma GCC diagnostic ignored "-Wold-style-cast" + +#include <boost/smart_ptr/make_shared.hpp> + +#ifdef SWIFTEN_PLATFORM_MACOSX +#include "client/mac/handler/exception_handler.h" +#endif +#ifdef SWIFTEN_PLATFORM_WINDOWS +#include "client/windows/handler/exception_handler.h" +#endif + +#if defined(SWIFTEN_PLATFORM_WINDOWS) +static bool handleDump(const wchar_t* /* dir */, const wchar_t* /* id*/, void* /* context */, EXCEPTION_POINTERS*, MDRawAssertionInfo*, bool /* succeeded */) { + return false; +} +#else +static bool handleDump(const char* /* dir */, const char* /* id*/, void* /* context */, bool /* succeeded */) { + return false; +} +#endif + +namespace Swift { + +struct CrashReporter::Private { + boost::shared_ptr<google_breakpad::ExceptionHandler> handler; +}; + +CrashReporter::CrashReporter(const boost::filesystem::path& path) { + // Create the path that will contain the crash dumps + if (!boost::filesystem::exists(path)) { + try { + boost::filesystem::create_directories(path); + } + catch (const boost::filesystem::filesystem_error& e) { + SWIFT_LOG(error) << "ERROR: " << e.what() << std::endl; + } + } + + p = boost::make_shared<Private>(); +#if defined(SWIFTEN_PLATFORM_WINDOWS) + // FIXME: Need UTF8 conversion from string to wstring + std::string pathString = pathToString(path); + p->handler = boost::shared_ptr<google_breakpad::ExceptionHandler>( + // Not using make_shared, because 'handleDump' seems to have problems with VC2010 + new google_breakpad::ExceptionHandler( + std::wstring(pathString.begin(), pathString.end()), + (google_breakpad::ExceptionHandler::FilterCallback) 0, + handleDump, + (void*) 0, + google_breakpad::ExceptionHandler::HANDLER_ALL)); +// Turning it off for Mac, because it doesn't really help us +//#elif defined(SWIFTEN_PLATFORM_MACOSX) +// p->handler = boost::make_shared<google_breakpad::ExceptionHandler>(pathToString(path), (google_breakpad::ExceptionHandler::FilterCallback) 0, handleDump, (void*) 0, true, (const char*) 0); +#endif +} + +}; + +#else + +// Dummy implementation +namespace Swift { + CrashReporter::CrashReporter(const boost::filesystem::path&) {} +} + +#endif diff --git a/SwifTools/CrashReporter.h b/SwifTools/CrashReporter.h new file mode 100644 index 0000000..834e8af --- /dev/null +++ b/SwifTools/CrashReporter.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2012 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/filesystem.hpp> +#include <string> + +namespace Swift { + class CrashReporter { + public: + CrashReporter(const boost::filesystem::path& path); + + private: + struct Private; + boost::shared_ptr<Private> p; + }; +} diff --git a/SwifTools/Dock/MacOSXDock.mm b/SwifTools/Dock/MacOSXDock.mm index b8374fa..3164998 100644 --- a/SwifTools/Dock/MacOSXDock.mm +++ b/SwifTools/Dock/MacOSXDock.mm @@ -1,2 +1,5 @@ +// Fix Boost-Cocoa conflict +#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 + #include <SwifTools/Dock/MacOSXDock.h> diff --git a/SwifTools/HunspellChecker.cpp b/SwifTools/HunspellChecker.cpp new file mode 100644 index 0000000..e9bc558 --- /dev/null +++ b/SwifTools/HunspellChecker.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <SwifTools/HunspellChecker.h> + +#include <algorithm> +#include <hunspell/hunspell.hxx> +#include <boost/algorithm/string.hpp> + + +namespace Swift { + +HunspellChecker::HunspellChecker(const char* affix_path, const char* dictionary_path) { + speller_ = new Hunspell(affix_path, dictionary_path); +} + +HunspellChecker::~HunspellChecker() { + delete speller_; +} + +bool HunspellChecker::isCorrect(const std::string& word) { + return speller_->spell(word.c_str()); +} + +void HunspellChecker::getSuggestions(const std::string& word, std::vector<std::string>& list) { + char **suggestList = NULL; + int words_returned; + if (!word.empty()) { + words_returned = speller_->suggest(&suggestList, word.c_str()); + } + for (int i = 0; i < words_returned; ++i) { + list.push_back(suggestList[i]); + free(suggestList[i]); + } + free(suggestList); +} + +void HunspellChecker::checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) { + if (!fragment.empty()) { + parser_->check(fragment, misspelledPositions); + for (PositionPairList::iterator it = misspelledPositions.begin(); it != misspelledPositions.end();) { + if (isCorrect(fragment.substr(boost::get<0>(*it), boost::get<1>(*it) - boost::get<0>(*it)))) { + it = misspelledPositions.erase(it); + } + else { + ++it; + } + } + } +} + +} diff --git a/SwifTools/HunspellChecker.h b/SwifTools/HunspellChecker.h new file mode 100644 index 0000000..12c0485 --- /dev/null +++ b/SwifTools/HunspellChecker.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <vector> +#include <boost/algorithm/string.hpp> +#include <boost/tuple/tuple.hpp> +#include <SwifTools/SpellChecker.h> + +#pragma once + +class Hunspell; + +namespace Swift { + class HunspellChecker : public SpellChecker { + public: + HunspellChecker(const char* affix_path, const char* dict_path); + virtual ~HunspellChecker(); + virtual bool isCorrect(const std::string& word); + virtual void getSuggestions(const std::string& word, std::vector<std::string>& list); + virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions); + private: + Hunspell* speller_; + }; +} diff --git a/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp b/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp index df233a2..41238a0 100644 --- a/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp +++ b/SwifTools/Idle/IdleQuerierTest/IdleQuerierTest.cpp @@ -6,4 +6,5 @@ #include <iostream> +#include <cassert> #include <SwifTools/Idle/PlatformIdleQuerier.h> @@ -18,5 +19,5 @@ int main() { Swift::sleep(1000); } - + assert(false); return 0; } diff --git a/SwifTools/Idle/MacOSXIdleQuerier.cpp b/SwifTools/Idle/MacOSXIdleQuerier.cpp index 233d7c6..8eaece6 100644 --- a/SwifTools/Idle/MacOSXIdleQuerier.cpp +++ b/SwifTools/Idle/MacOSXIdleQuerier.cpp @@ -11,4 +11,5 @@ #include <cassert> #include <iostream> +#include <boost/numeric/conversion/cast.hpp> #include <CoreFoundation/CoreFoundation.h> @@ -29,5 +30,5 @@ int MacOSXIdleQuerier::getIdleTimeSeconds() { (void) result; CFRelease(property); - return idle / 1000000000; + return boost::numeric_cast<int>(idle / 1000000000); } diff --git a/SwifTools/Linkify.cpp b/SwifTools/Linkify.cpp index 906026d..8ecbb09 100644 --- a/SwifTools/Linkify.cpp +++ b/SwifTools/Linkify.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -50,3 +50,54 @@ std::string Linkify::linkify(const std::string& input) { } +std::pair<std::vector<std::string>, size_t> Linkify::splitLink(const std::string& input) { + std::vector<std::string> result; + std::pair<std::vector<std::string>, size_t> pair; + std::vector<char> currentURL; + bool inURL = false; + size_t urlStartsAt = 0; + for (size_t i = 0; i < input.size(); ++i) { + char c = input[i]; + if (inURL) { + if (c != ' ' && c != '\t' && c != '\n' && !(c == '*' && i == input.size() - 1 && input[0] == '*')) { + // Keep parsing + } + else { + std::string url(input.substr(urlStartsAt, i - urlStartsAt)); + result.push_back(url); + inURL = false; + size_t remaining = input.size() - i; + if (remaining > 0) { + result.push_back(input.substr(i, remaining)); + } + pair.first = result; + pair.second = urlStartsAt == 0 ? 0 : 1; + return pair; + } + } + else { + if (boost::regex_match(input.substr(i, 8), linkifyRegexp)) { + urlStartsAt = i; + inURL = true; + if (i > 0) { + result.push_back(input.substr(0, i)); + } + } + else { + // Just keep swimming + } + } + } + if (urlStartsAt > 0 || inURL) { + std::string url(input.substr(urlStartsAt, input.size() - urlStartsAt)); + result.push_back(url); + pair.first = result; + pair.second = urlStartsAt == 0 ? 0 : 1; + } + else { + pair.first.push_back(input); + pair.second = 1; + } + return pair; +} + } diff --git a/SwifTools/Linkify.h b/SwifTools/Linkify.h index ebe232f..0a9c132 100644 --- a/SwifTools/Linkify.h +++ b/SwifTools/Linkify.h @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -7,4 +7,5 @@ #pragma once +#include <vector> #include <string> @@ -12,4 +13,15 @@ namespace Swift { namespace Linkify { std::string linkify(const std::string&); + /** + * Parse the string for a URI. The string will be split by the URI, and the segments plus index of the URI returned. + * If no URI is found the index will be result.size() (i.e. an invalid index) + * + * Examples: + * "not a URI" -> <<"not a URI">, -1> + * "http://swift.im" -> <<"http://swift.im">, 0 + * " See http://swift.im" -> <<" See ", "http://swift.im">, 1> + * "Right, http://swift.im it is" -> <<"Right, ", "http://swift.im", " it is">, 1> + */ + std::pair<std::vector<std::string>, size_t> splitLink(const std::string& text); } } diff --git a/SwifTools/MacOSXChecker.h b/SwifTools/MacOSXChecker.h new file mode 100644 index 0000000..f2f8ebc --- /dev/null +++ b/SwifTools/MacOSXChecker.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <vector> +#include <boost/tuple/tuple.hpp> +#include <SwifTools/SpellChecker.h> + +namespace Swift { + class MacOSXChecker : public SpellChecker { + public: + MacOSXChecker(); + virtual ~MacOSXChecker(); + virtual bool isCorrect(const std::string& word); + virtual void getSuggestions(const std::string& word, std::vector<std::string>& list); + virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions); + }; +} diff --git a/SwifTools/MacOSXChecker.mm b/SwifTools/MacOSXChecker.mm new file mode 100644 index 0000000..3e687d1 --- /dev/null +++ b/SwifTools/MacOSXChecker.mm @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <SwifTools/MacOSXChecker.h> + +#include <algorithm> +#include <boost/algorithm/string.hpp> + +#include <AppKit/AppKit.h> + +namespace Swift { + +MacOSXChecker::MacOSXChecker() { +} + +MacOSXChecker::~MacOSXChecker() { +} + +bool MacOSXChecker::isCorrect(const std::string& /*word*/) { + // No content since it doesn't seem to be used anywhere. + return false; +} + +void MacOSXChecker::getSuggestions(const std::string& word, std::vector<std::string>& list) { + NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; + NSString* wordString = [[NSString alloc] initWithUTF8String: word.c_str()]; + NSArray* suggestions = [spellChecker guessesForWord: wordString]; + for(unsigned int i = 0; i < [suggestions count]; ++i) { + list.push_back(std::string([[suggestions objectAtIndex:i] UTF8String])); + } + [wordString release]; +} + +void MacOSXChecker::checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) { + NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker]; + size_t nextLocation = 0; + NSRange range; + NSString *fragmentString = [[NSString alloc] initWithUTF8String: fragment.c_str()]; + do { + range = [spellChecker checkSpellingOfString:fragmentString startingAt:static_cast<long>(nextLocation)]; + if (range.location != NSNotFound) { + if (range.location < nextLocation) + break; + misspelledPositions.push_back(PositionPair(static_cast<int>(range.location), static_cast<int>(range.location + range.length))); + nextLocation = range.location + range.length + 1; + } + } while (range.location != NSNotFound); + [fragmentString release]; +} + +} diff --git a/SwifTools/Notifier/GNTPNotifier.cpp b/SwifTools/Notifier/GNTPNotifier.cpp index 9bc05bd..757594f 100644 --- a/SwifTools/Notifier/GNTPNotifier.cpp +++ b/SwifTools/Notifier/GNTPNotifier.cpp @@ -15,4 +15,5 @@ #include <Swiften/Base/foreach.h> +#include <Swiften/Base/Path.h> #include <Swiften/Network/ConnectionFactory.h> @@ -24,5 +25,5 @@ GNTPNotifier::GNTPNotifier(const std::string& name, const boost::filesystem::pat message << "GNTP/1.0 REGISTER NONE\r\n"; message << "Application-Name: " << name << "\r\n"; - message << "Application-Icon: file://" << icon.string() << "\r\n"; + message << "Application-Icon: file://" << pathToString(icon) << "\r\n"; message << "Notifications-Count: " << getAllTypes().size() << "\r\n"; std::vector<Notifier::Type> defaultTypes = getDefaultTypes(); @@ -60,5 +61,5 @@ void GNTPNotifier::showMessage(Type type, const std::string& subject, const std: message << "Notification-Title: " << subject << "\r\n"; message << "Notification-Text: " << description << "\r\n"; - message << "Notification-Icon: " << picture.string() << "\r\n"; + message << "Notification-Icon: " << pathToString(picture) << "\r\n"; message << "\r\n"; send(message.str()); diff --git a/SwifTools/Notifier/GrowlNotifier.mm b/SwifTools/Notifier/GrowlNotifier.mm index c1996d9..2ababf4 100644 --- a/SwifTools/Notifier/GrowlNotifier.mm +++ b/SwifTools/Notifier/GrowlNotifier.mm @@ -66,5 +66,5 @@ GrowlNotifier::~GrowlNotifier() { void GrowlNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picturePath, boost::function<void()> callback) { ByteArray picture; - readByteArrayFromFile(picture, picturePath.string()); + readByteArrayFromFile(picture, picturePath); Context* context = new Context(callback); diff --git a/SwifTools/Notifier/GrowlNotifierDelegate.mm b/SwifTools/Notifier/GrowlNotifierDelegate.mm index 6952cab..e184da6 100644 --- a/SwifTools/Notifier/GrowlNotifierDelegate.mm +++ b/SwifTools/Notifier/GrowlNotifierDelegate.mm @@ -9,5 +9,5 @@ #include <SwifTools/Notifier/GrowlNotifier.h> -@implementation GrowlNotifierDelegate; +@implementation GrowlNotifierDelegate @synthesize registrationDictionary; diff --git a/SwifTools/SConscript b/SwifTools/SConscript index e5085cc..b9822e0 100644 --- a/SwifTools/SConscript +++ b/SwifTools/SConscript @@ -17,5 +17,6 @@ if env["SCONS_STAGE"] == "flags" : if env["SCONS_STAGE"] == "build" : swiftools_env = env.Clone() - swiftools_env.MergeFlags(swiftools_env["BOOST_FLAGS"]) + swiftools_env.UseFlags(swiftools_env["SWIFTEN_FLAGS"]) + swiftools_env.UseFlags(swiftools_env["BOOST_FLAGS"]) sources = [ @@ -31,6 +32,22 @@ if env["SCONS_STAGE"] == "build" : ] + if swiftools_env["HAVE_HUNSPELL"] : + swiftools_env.UseFlags(swiftools_env["HUNSPELL_FLAGS"]) + swiftools_env.Append(CPPDEFINES = ["HAVE_HUNSPELL"]) + sources += [ + "SpellCheckerFactory.cpp", + "HunspellChecker.cpp", + "SpellParser.cpp", + ] + elif swiftools_env["PLATFORM"] == "darwin" and env["target"] == "native" : + sources += [ + "SpellCheckerFactory.cpp", + "MacOSXChecker.mm", + "SpellParser.cpp", + ] + + if swiftools_env.get("HAVE_SPARKLE", 0) : - swiftools_env.MergeFlags(swiftools_env["SPARKLE_FLAGS"]) + swiftools_env.UseFlags(swiftools_env["SPARKLE_FLAGS"]) swiftools_env.Append(CPPDEFINES = ["HAVE_SPARKLE"]) sources += ["AutoUpdater/SparkleAutoUpdater.mm"] @@ -45,4 +62,9 @@ if env["SCONS_STAGE"] == "build" : sources += ["Idle/XSSIdleQuerier.cpp"] + if env.get("HAVE_BREAKPAD", False) : + swiftools_env.UseFlags(swiftools_env["BREAKPAD_FLAGS"]) + swiftools_env.Append(CPPDEFINES = ["HAVE_BREAKPAD"]) + sources += ["CrashReporter.cpp"] + swiftools_env["SWIFTOOLS_OBJECTS"] = [] Export("swiftools_env") diff --git a/SwifTools/SpellChecker.h b/SwifTools/SpellChecker.h new file mode 100644 index 0000000..fd38418 --- /dev/null +++ b/SwifTools/SpellChecker.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <SwifTools/SpellParser.h> + +#include <boost/algorithm/string.hpp> +#include <boost/tuple/tuple.hpp> +#include <vector> + +#pragma once + +namespace Swift { + class SpellChecker { + public: + SpellChecker() { + parser_ = new SpellParser(); + } + virtual ~SpellChecker() { + delete parser_; + } + virtual bool isCorrect(const std::string& word) = 0; + virtual void getSuggestions(const std::string& word, std::vector<std::string>& list) = 0; + virtual void checkFragment(const std::string& fragment, PositionPairList& misspelledPositions) = 0; + protected: + SpellParser *parser_; + }; +} diff --git a/SwifTools/SpellCheckerFactory.cpp b/SwifTools/SpellCheckerFactory.cpp new file mode 100644 index 0000000..428e1a5 --- /dev/null +++ b/SwifTools/SpellCheckerFactory.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <boost/filesystem/operations.hpp> + +#include <SwifTools/SpellChecker.h> +#include <SwifTools/HunspellChecker.h> +#include <SwifTools/SpellCheckerFactory.h> +#include <Swiften/Base/Platform.h> + +#ifdef HAVE_HUNSPELL +#include <hunspell/hunspell.hxx> +#elif defined(SWIFTEN_PLATFORM_MACOSX) +#include <SwifTools/MacOSXChecker.h> +#endif + +namespace Swift { + +SpellCheckerFactory::SpellCheckerFactory() { +} + +#ifdef HAVE_HUNSPELL +SpellChecker* SpellCheckerFactory::createSpellChecker(const std::string& dictFile) { + std::string affixFile(dictFile); + boost::replace_all(affixFile, ".dic", ".aff"); + if ((boost::filesystem::exists(dictFile)) && (boost::filesystem::exists(affixFile))) { + return new HunspellChecker(affixFile.c_str(), dictFile.c_str()); + } + // If dictionaries don't exist disable the checker + return NULL; +} +#elif defined(SWIFTEN_PLATFORM_MACOSX) +SpellChecker* SpellCheckerFactory::createSpellChecker(const std::string& /*dictFile*/) { + return new MacOSXChecker(); +} +#endif + +} diff --git a/SwifTools/SpellCheckerFactory.h b/SwifTools/SpellCheckerFactory.h new file mode 100644 index 0000000..91118f9 --- /dev/null +++ b/SwifTools/SpellCheckerFactory.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Base/Platform.h> + +#ifdef HAVE_HUNSPELL +#define HAVE_SPELLCHECKER +#elif defined(SWIFTEN_PLATFORM_MACOSX) +#define HAVE_SPELLCHECKER +#endif + +namespace Swift { + class SpellChecker; + class SpellCheckerFactory { + public: + SpellCheckerFactory(); + SpellChecker* createSpellChecker(const std::string& dictFile); + }; +} diff --git a/SwifTools/SpellParser.cpp b/SwifTools/SpellParser.cpp new file mode 100644 index 0000000..8cb42e4 --- /dev/null +++ b/SwifTools/SpellParser.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011-2013 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <SwifTools/SpellParser.h> + +#include <boost/spirit/include/lex_lexertl.hpp> +#include <boost/bind.hpp> +#include <boost/ref.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <string> + +namespace lex = boost::spirit::lex; + +namespace Swift { + +template <typename Lexer> +struct word_count_tokens : lex::lexer<Lexer> +{ + word_count_tokens() + { + // define tokens (regular expresions) to match strings + // order is important + this->self.add + ("w{3}.[^ ]+", ID_WWW) + ("http:\\/\\/[^ ]+", ID_HTTP) + ("\\w{1,}['?|\\-?]?\\w{1,}", ID_WORD) + (".", ID_CHAR); + } +}; + +struct counter +{ + typedef bool result_type; + // the function operator gets called for each of the matched tokens + template <typename Token> + bool operator()(Token const& t, PositionPairList& wordPositions, std::size_t& position) const + { + switch (t.id()) { + case ID_WWW: + position += boost::numeric_cast<size_t>(t.value().size()); + break; + case ID_HTTP: + position += boost::numeric_cast<size_t>(t.value().size()); + break; + case ID_WORD: // matched a word + wordPositions.push_back(boost::tuples::make_tuple(position, position + boost::numeric_cast<size_t>(t.value().size()))); + position += boost::numeric_cast<size_t>(t.value().size()); + break; + case ID_CHAR: // match a simple char + ++position; + break; + } + return true; // always continue to tokenize + } +}; + +void SpellParser::check(const std::string& fragment, PositionPairList& wordPositions) { + std::size_t position = 0; + // create the token definition instance needed to invoke the lexical analyzer + word_count_tokens<lex::lexertl::lexer<> > word_count_functor; + char const* first = fragment.c_str(); + char const* last = &first[fragment.size()]; + lex::tokenize(first, last, word_count_functor, boost::bind(counter(), _1, boost::ref(wordPositions), boost::ref(position))); +} + +} diff --git a/SwifTools/SpellParser.h b/SwifTools/SpellParser.h new file mode 100644 index 0000000..b64565c --- /dev/null +++ b/SwifTools/SpellParser.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <boost/algorithm/string.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/algorithm/string.hpp> + +#include <vector> + +namespace Swift { + enum token_ids + { + ID_WWW = 1, + ID_HTTP = 2, + ID_WORD = 3, + ID_CHAR = 4 + }; + + typedef boost::tuple<int, int> PositionPair; + typedef std::vector<PositionPair > PositionPairList; + + class SpellParser{ + public: + void check(const std::string& fragment, PositionPairList& wordPositions); + }; +} diff --git a/SwifTools/URIHandler/MacOSXURIHandler.mm b/SwifTools/URIHandler/MacOSXURIHandler.mm index 0575d47..cdfba33 100644 --- a/SwifTools/URIHandler/MacOSXURIHandler.mm +++ b/SwifTools/URIHandler/MacOSXURIHandler.mm @@ -1,4 +1,4 @@ /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2011-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -13,13 +13,18 @@ using namespace Swift; @interface MacOSXURIEventHandler : NSObject { - URIHandler* handler; } + - (id) initWithHandler: (URIHandler*) handler; - (void) getUrl: (NSAppleEventDescriptor*) event withReplyEvent: (NSAppleEventDescriptor*) replyEvent; @end + @implementation MacOSXURIEventHandler + { + URIHandler* handler; + } + - (id) initWithHandler: (URIHandler*) h { - if ([super init]) { + if ((self = [super init])) { handler = h; } diff --git a/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp index 8d03b60..35020da 100644 --- a/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp +++ b/SwifTools/URIHandler/UnitTest/XMPPURITest.cpp @@ -66,7 +66,7 @@ class XMPPURITest : public CppUnit::TestFixture { void testFromString_AuthorityWithIntlChars() { - XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com"); + XMPPURI testling = XMPPURI::fromString("xmpp://nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com"); - CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority()); + CPPUNIT_ASSERT_EQUAL(JID("nasty!#$%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getAuthority()); } @@ -119,7 +119,7 @@ class XMPPURITest : public CppUnit::TestFixture { void testFromString_PathWithIntlChars() { - XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=\%3F\%5B\%5C\%5D\%5E_\%60\%7B\%7C\%7D~node@example.com"); + XMPPURI testling = XMPPURI::fromString("xmpp:nasty!%23$%25()*+,-.;=%3F%5B%5C%5D%5E_%60%7B%7C%7D~node@example.com"); - CPPUNIT_ASSERT_EQUAL(JID("nasty!#$\%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath()); + CPPUNIT_ASSERT_EQUAL(JID("nasty!#$%()*+,-.;=?[\\]^_`{|}~node@example.com"), testling.getPath()); } diff --git a/SwifTools/URIHandler/XMPPURI.cpp b/SwifTools/URIHandler/XMPPURI.cpp index cb81391..cf99ae6 100644 --- a/SwifTools/URIHandler/XMPPURI.cpp +++ b/SwifTools/URIHandler/XMPPURI.cpp @@ -7,4 +7,5 @@ #include <SwifTools/URIHandler/XMPPURI.h> +#include <Swiften/Base/URL.h> #include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/find_format.hpp> @@ -19,81 +20,4 @@ using namespace Swift; -// Disabling this code for now, since GCC4.5+boost1.42 (on ubuntu) seems to -// result in a bug. Replacing it with naive code. -#if 0 -// Should be in anonymous namespace, but older GCCs complain if we do that -struct PercentEncodedCharacterFinder { - template<typename Iterator> - boost::iterator_range<Iterator> operator()(Iterator begin, Iterator end) { - boost::iterator_range<Iterator> r = boost::first_finder("%")(begin, end); - if (r.end() == end) { - return r; - } - else { - if (r.end() + 1 == end || r.end() + 2 == end) { - throw std::runtime_error("Incomplete escape character"); - } - else { - r.advance_end(2); - return r; - } - } - } -}; - -struct PercentUnencodeFormatter { - template<typename FindResult> - std::string operator()(const FindResult& match) const { - std::stringstream s; - s << std::hex << std::string(match.begin() + 1, match.end()); - unsigned int value; - s >> value; - if (s.fail() || s.bad()) { - throw std::runtime_error("Invalid escape character"); - } - unsigned char charValue = static_cast<unsigned char>(value); - return std::string(reinterpret_cast<const char*>(&charValue), 1); - } -}; - -namespace { - std::string unescape(const std::string& s) { - try { - return boost::find_format_all_copy(s, PercentEncodedCharacterFinder(), PercentUnencodeFormatter()); - } - catch (const std::exception&) { - return ""; - } - } -} -#endif -namespace { - std::string unescape(const std::string& str) { - std::string result; - for (size_t i = 0; i < str.size(); ++i) { - if (str[i] == '%') { - if (i + 3 < str.size()) { - std::stringstream s; - s << std::hex << str.substr(i+1, 2); - unsigned int value; - s >> value; - if (s.fail() || s.bad()) { - return ""; - } - unsigned char charValue = static_cast<unsigned char>(value); - result += std::string(reinterpret_cast<const char*>(&charValue), 1); - i += 2; - } - else { - return ""; - } - } - else { - result += str[i]; - } - } - return result; - } -} XMPPURI::XMPPURI() { @@ -111,5 +35,5 @@ XMPPURI XMPPURI::fromString(const std::string& s) { if (boost::starts_with(uri, "//")) { size_t i = uri.find_first_of("/#?", 2); - result.setAuthority(JID(unescape(uri.substr(2, i - 2)))); + result.setAuthority(JID(URL::unescape(uri.substr(2, i - 2)))); if (i == uri.npos) { uri = ""; @@ -130,5 +54,5 @@ XMPPURI XMPPURI::fromString(const std::string& s) { if (parsePath) { size_t i = uri.find_first_of("#?"); - result.setPath(JID(unescape(uri.substr(0, i)))); + result.setPath(JID(URL::unescape(uri.substr(0, i)))); if (i == uri.npos) { uri = ""; @@ -154,12 +78,12 @@ XMPPURI XMPPURI::fromString(const std::string& s) { boost::split(keyValue, *it, boost::is_any_of("=")); if (keyValue.size() == 1) { - result.addQueryParameter(unescape(keyValue[0]), ""); + result.addQueryParameter(URL::unescape(keyValue[0]), ""); } else if (keyValue.size() >= 2) { - result.addQueryParameter(unescape(keyValue[0]), unescape(keyValue[1])); + result.addQueryParameter(URL::unescape(keyValue[0]), URL::unescape(keyValue[1])); } } else { - result.setQueryType(unescape(boost::copy_range<std::string>(*it))); + result.setQueryType(URL::unescape(boost::copy_range<std::string>(*it))); haveType = true; } @@ -170,5 +94,5 @@ XMPPURI XMPPURI::fromString(const std::string& s) { // Parse fragment if (parseFragment) { - result.setFragment(unescape(uri)); + result.setFragment(URL::unescape(uri)); } } diff --git a/SwifTools/URIHandler/XMPPURI.h b/SwifTools/URIHandler/XMPPURI.h index 266b79b..36bfc41 100644 --- a/SwifTools/URIHandler/XMPPURI.h +++ b/SwifTools/URIHandler/XMPPURI.h @@ -13,4 +13,5 @@ namespace Swift { + // TODO: Implement using Base/URI class XMPPURI { public: diff --git a/SwifTools/UnitTest/LastLineTrackerTest.cpp b/SwifTools/UnitTest/LastLineTrackerTest.cpp index a7046ed..97790e5 100644 --- a/SwifTools/UnitTest/LastLineTrackerTest.cpp +++ b/SwifTools/UnitTest/LastLineTrackerTest.cpp @@ -22,5 +22,5 @@ class LastLineTrackerTest : public CppUnit::TestFixture { public: LastLineTrackerTest () { - }; + } void testFocusNormal() { LastLineTracker testling; diff --git a/SwifTools/UnitTest/LinkifyTest.cpp b/SwifTools/UnitTest/LinkifyTest.cpp index 5df1a96..c464b50 100644 --- a/SwifTools/UnitTest/LinkifyTest.cpp +++ b/SwifTools/UnitTest/LinkifyTest.cpp @@ -1,4 +1,4 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. @@ -33,4 +33,10 @@ class LinkifyTest : public CppUnit::TestFixture { CPPUNIT_TEST(testLinkify_Tab); CPPUNIT_TEST(testLinkify_Action); + + CPPUNIT_TEST(testLinkify_SplitNone); + CPPUNIT_TEST(testLinkify_SplitAll); + CPPUNIT_TEST(testLinkify_SplitFirst); + CPPUNIT_TEST(testLinkify_SplitSecond); + CPPUNIT_TEST(testLinkify_SplitMiddle); CPPUNIT_TEST_SUITE_END(); @@ -188,4 +194,49 @@ class LinkifyTest : public CppUnit::TestFixture { result); } + + void checkResult(const std::string& testling, size_t expectedIndex, std::string expectedSplit[]) { + std::pair<std::vector<std::string>, size_t> result = Linkify::splitLink(testling); + CPPUNIT_ASSERT_EQUAL(expectedIndex, result.second); + for (size_t i = 0; i < result.first.size(); i++) { + CPPUNIT_ASSERT_EQUAL(expectedSplit[i], result.first[i]); + } + } + + void testLinkify_SplitNone() { + std::string testling = "http this ain't"; + size_t expectedIndex = 1; + std::string expectedSplit[] = {"http this ain't"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitAll() { + std::string testling = "http://swift.im"; + size_t expectedIndex = 0; + std::string expectedSplit[] = {"http://swift.im"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitFirst() { + std::string testling = "http://swift.im is a link"; + size_t expectedIndex = 0; + std::string expectedSplit[] = {"http://swift.im", " is a link"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitSecond() { + std::string testling = "this is a link: http://swift.im"; + size_t expectedIndex = 1; + std::string expectedSplit[] = {"this is a link: ", "http://swift.im"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + void testLinkify_SplitMiddle() { + std::string testling = "Shove a link like http://swift.im in the middle"; + size_t expectedIndex = 1; + std::string expectedSplit[] = {"Shove a link like ","http://swift.im", " in the middle"}; + checkResult(testling, expectedIndex, expectedSplit); + } + + }; diff --git a/SwifTools/UnitTest/SConscript b/SwifTools/UnitTest/SConscript index e469deb..dbd1ce5 100644 --- a/SwifTools/UnitTest/SConscript +++ b/SwifTools/UnitTest/SConscript @@ -6,2 +6,7 @@ env.Append(UNITTEST_SOURCES = [ File("LastLineTrackerTest.cpp"), ]) + +if env["HAVE_HUNSPELL"] : + env.Append(UNITTEST_SOURCES = [ + File("SpellParserTest.cpp"), + ]) diff --git a/SwifTools/UnitTest/SpellParserTest.cpp b/SwifTools/UnitTest/SpellParserTest.cpp new file mode 100644 index 0000000..09e686c --- /dev/null +++ b/SwifTools/UnitTest/SpellParserTest.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/algorithm/string.hpp> + +#include <SwifTools/SpellParser.h> + +using namespace Swift; + +class SpellParserTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(SpellParserTest); + CPPUNIT_TEST(testSimpleCheckFragment); + CPPUNIT_TEST(testWWWCheckFragment); + CPPUNIT_TEST_SUITE_END(); + public: + SpellParserTest() { + parser_ = new SpellParser(); + }; + void tearDown() { + position_.clear(); + } + void testSimpleCheckFragment() { + parser_->check("fragment test", position_); + int size = position_.size(); + CPPUNIT_ASSERT_EQUAL(2, size); + CPPUNIT_ASSERT_EQUAL(0, boost::get<0>(position_.front())); + CPPUNIT_ASSERT_EQUAL(8, boost::get<1>(position_.front())); + CPPUNIT_ASSERT_EQUAL(9, boost::get<0>(position_.back())); + CPPUNIT_ASSERT_EQUAL(13, boost::get<1>(position_.back())); + } + void testWWWCheckFragment() { + parser_->check("www.link.com fragment test", position_); + int size = position_.size(); + CPPUNIT_ASSERT_EQUAL(2, size); + CPPUNIT_ASSERT_EQUAL(13, boost::get<0>(position_.front())); + CPPUNIT_ASSERT_EQUAL(21, boost::get<1>(position_.front())); + CPPUNIT_ASSERT_EQUAL(22, boost::get<0>(position_.back())); + CPPUNIT_ASSERT_EQUAL(26, boost::get<1>(position_.back())); + } + private: + SpellParser *parser_; + PositionPairList position_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(SpellParserTest); diff --git a/SwifTools/UnitTest/TabCompleteTest.cpp b/SwifTools/UnitTest/TabCompleteTest.cpp index 0466484..cdb0296 100644 --- a/SwifTools/UnitTest/TabCompleteTest.cpp +++ b/SwifTools/UnitTest/TabCompleteTest.cpp @@ -25,5 +25,5 @@ class TabCompleteTest : public CppUnit::TestFixture { public: - TabCompleteTest() {}; + TabCompleteTest() {} void setUp() { |