diff --git a/CMakeLists.txt b/CMakeLists.txt index 63b7aaf7c..3e81017be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,154 +1,155 @@ project(kdepimlibs) set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules ) # Used e.g. in KdepimLibsConfig.cmake, Alex set(KDEPIMLIBS_VERSION_MAJOR 4) set(KDEPIMLIBS_VERSION_MINOR 3) set(KDEPIMLIBS_VERSION_PATCH 0) set(KDEPIMLIBS_VERSION ${KDEPIMLIBS_VERSION_MAJOR}.${KDEPIMLIBS_VERSION_MINOR}.${KDEPIMLIBS_VERSION_PATCH} ) find_package(KDE4 4.2.0 REQUIRED) include (KDE4Defaults) include (MacroLibrary) find_package(Boost REQUIRED) macro_log_feature(Boost_FOUND "boost" "Boost C++ Libraries" "http://www.boost.org" TRUE "1.33.1" "Needed by several critical PIM libraries.") if (NOT ONLY_KLEO) macro_optional_find_package(Sasl2) macro_log_feature(SASL2_FOUND "cyrus-sasl" "Cyrus SASL API" "http://asg.web.cmu.edu/sasl/sasl-library.html" FALSE "" "STRONGLY RECOMMENDED: Needed to support authentication of logins. IMAP and Sieve kioslaves will not be built.") endif (NOT ONLY_KLEO) # gpgme is a hard dependency find_package(Gpgme REQUIRED) include (gpgme++/ConfigureChecks.cmake) include (ConfigureChecks.cmake) add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}) # TODO(move to kdesupport scripts) temporary way to support gpgme-qt under win32 if (EXISTS ${CMAKE_SOURCE_DIR}/gpgme-qt) message(STATUS "gpgme-qt for Windows will be compiled: ${CMAKE_SOURCE_DIR}/gpgme-qt") add_subdirectory(gpgme-qt) endif (EXISTS ${CMAKE_SOURCE_DIR}/gpgme-qt) macro_optional_add_subdirectory(doc) add_subdirectory(gpgme++) add_subdirectory(qgpgme) add_subdirectory(kmime) if (NOT ONLY_KLEO) find_package(Akonadi REQUIRED) if(Akonadi_FOUND) add_subdirectory(akonadi) endif(Akonadi_FOUND) # (gpgme++/qgpgme handle their conditions inside their own CMakeLists.txt files) add_subdirectory(kabc) add_subdirectory(kblog) add_subdirectory(kcal) add_subdirectory(kholidays) add_subdirectory(kimap) add_subdirectory(kldap) add_subdirectory(kpimidentities) add_subdirectory(kpimutils) add_subdirectory(kresources) add_subdirectory(ktnef) add_subdirectory(kxmlrpcclient) add_subdirectory(mailtransport) add_subdirectory(syndication) add_subdirectory(kioslave) +add_subdirectory(pimtextedit) endif (NOT ONLY_KLEO) add_subdirectory(cmake) macro_display_feature_log() # now create the KdepimLibsConfig.cmake file, which will be loaded by # kdelibs/cmake/modules/FindKdepimLibs.cmake and which has to contain all information # about the installed kdepimlibs anybody would like to have. Alex # we need the absolute directories where stuff will be installed too # but since the variables which contain the destinations can be relative # or absolute paths, we need this macro to make them all absoulte, Alex macro(MAKE_INSTALL_PATH_ABSOLUTE out in) if (IS_ABSOLUTE "${in}") # IS_ABSOLUTE is new since cmake 2.4.8 set(${out} "${in}") else (IS_ABSOLUTE "${in}") set(${out} "\${KDEPIMLIBS_INSTALL_DIR}/${in}") endif (IS_ABSOLUTE "${in}") endmacro(MAKE_INSTALL_PATH_ABSOLUTE out in) # all the following variables are put into KdepimLibsConfig.cmake, so # they are usable by projects using kdepimlibs. Alex make_install_path_absolute(KDEPIMLIBS_DATA_DIR ${DATA_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_DBUS_INTERFACES_DIR ${DBUS_INTERFACES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_DBUS_SERVICES_DIR ${DBUS_SERVICES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_INCLUDE_DIR ${INCLUDE_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_LIB_DIR ${LIB_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_BIN_DIR ${BIN_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_LIBEXEC_DIR ${LIBEXEC_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SBIN_DIR ${SBIN_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_HTML_DIR ${HTML_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_CONFIG_DIR ${CONFIG_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_ICON_DIR ${ICON_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_KCFG_DIR ${KCFG_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_LOCALE_DIR ${LOCALE_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_MIME_DIR ${MIME_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SOUND_DIR ${SOUND_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_TEMPLATES_DIR ${TEMPLATES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_WALLPAPER_DIR ${WALLPAPER_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_KCONF_UPDATE_DIR ${KCONF_UPDATE_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_AUTOSTART_DIR ${AUTOSTART_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_XDG_APPS_DIR ${XDG_APPS_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_XDG_DIRECTORY_DIR ${XDG_DIRECTORY_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SYSCONF_DIR ${SYSCONF_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_MAN_DIR ${MAN_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_INFO_DIR ${INFO_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SERVICES_DIR ${SERVICES_INSTALL_DIR}) make_install_path_absolute(KDEPIMLIBS_SERVICETYPES_DIR ${SERVICETYPES_INSTALL_DIR}) # Used in configure_file() and install(EXPORT) set(KDEPIMLIBS_TARGET_PREFIX KDEPIMLibs__) # this file is installed and contains all necessary information about the installed kdepimibs, it also loads the file with the exported targets configure_file(KdepimLibsConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfig.cmake" @ONLY) # this file will be installed too and will be used by cmake when searching for the Config.cmake file to check the version of kdepimlibs, Alex macro_write_basic_cmake_version_file(${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfigVersion.cmake ${KDEPIMLIBS_VERSION_MAJOR} ${KDEPIMLIBS_VERSION_MINOR} ${KDEPIMLIBS_VERSION_PATCH}) set(_KdepimLibsConfig_INSTALL_DIR ${LIB_INSTALL_DIR}/KdepimLibs-${KDEPIMLIBS_VERSION}/cmake) # places where find_package() looks for FooConfig.cmake files: # CMake >= 2.6.0 looks in lib/Foo*/cmake/, CMake >= 2.6.3 also looks in # lib/cmake/Foo*/, which packagers prefer. So they can set the KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR # option to have kdepimlibs install its Config file there. Alex if(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) set(_KdepimLibsConfig_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/KdepimLibs-${KDEPIMLIBS_VERSION} ) endif(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfigVersion.cmake ${CMAKE_CURRENT_BINARY_DIR}/KdepimLibsConfig.cmake DESTINATION ${_KdepimLibsConfig_INSTALL_DIR} ) # Install the file with the exported targets, use ${KDEPIMLIBS_TARGET_PREFIX} as prefix for the names of these targets, Alex install(EXPORT kdepimlibsLibraryTargets NAMESPACE ${KDEPIMLIBS_TARGET_PREFIX} DESTINATION ${_KdepimLibsConfig_INSTALL_DIR} FILE KDEPimLibsLibraryTargetsWithPrefix.cmake ) # Install a KDEPimLibsDependencies.cmake so people using kdepimlibs 4.2 with kdelibs < 4.2 get a useful error message, Alex file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/KDEPimLibsDependencies.cmake "\n message(FATAL_ERROR \"For using this version of kdepimlibs (${KDEPIMLIBS_VERSION}) you need a newer version of kdelibs, please update.\")\n") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KDEPimLibsDependencies.cmake DESTINATION ${DATA_INSTALL_DIR}/cmake/modules) add_subdirectory(includes) diff --git a/MAINTAINERS b/MAINTAINERS index 1bee637d2..afd2693b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1,26 +1,28 @@ akonadi Volker Krause gpgme++ Marc Mutz kabc Cornelius Schumacher kblog Christian Weilbach kcal Cornelius Schumacher Reinhold Kainhofer kcal/libical Allen Winter Cornelius Schumacher Reinhold Kainhofer kholidays Allen Winter kimap Allen Winter Tom Albers Volker Krause kioslave various kldap Szombathelyi György kmime Volker Krause Marc Mutz kpimidenties Tom Albers kpimutils Matt Douhan kresources Cornelius Schumacher Tobias Koenig ktnef Michael Goffioul kxmlrpcclient Narayan Newton mailtransport Tom Albers qgpgme Marc Mutz syndication Frank Osterfeld +pimtextedit Thomas McGuire + Stephen Kelly diff --git a/README b/README index 6fd119858..53b982e38 100644 --- a/README +++ b/README @@ -1,125 +1,127 @@ In this file: * About kdepimlibs * What's included * Licensing * Reporting Bugs * Debugging * Mailing List, Getting involved * More Info About kdepimlibs ---------------- This module includes libraries that are central to the development and execution of a KDE-PIM application. The KDE-PIM project aims to bring together those who wish to help design, implement, test, etc. anything that's to do with personal information management. This rather broad scope encompasses mail clients, addressbooks, usenet news, scheduling and even sticky notes. What's Included --------------- Here is an alphabetical list: * akonadi: client access library for using the Akonadi PIM data server. * kblog: C++ API for weblogging access * kcal: C++ API for the iCalendar and vCalendar formats * kcal/libical: a basic iCalendar protocol implementation, see RFCs 2245,2246 * kholidays: C++ library for calendar holidays * kimap: C++ API for IMAP support * kldap: C++ API for LDAP access * kmime: C++ API for MIME handling * kpimidentities: C++ API for shared identities for mail applications * kpimutils: A library for general purpose PIM utilities * ktnef: C++ API for the handling of TNEF data * kxmlrpclient: An XML-RPC client library * mailtransport: C++ API and support code for managing mail transport * syndication: An RSS/Atom feed parser library * qgpgme: A C++ wrapper for gpgme * kioslaves/sieve: Sieve kioslave +* pimtextedit: Provides a textedit with PIM-specific features and + can convert the textedit content to various markup formats Licensing --------- The libraries themselves are covered by the GNU Library General Public License (LGPL). Any other programs (such as the examples) are covered by the GNU General Public License (GPL). All the gory details for the LGPL reside in COPYING.LIB, and for the GPL reside in COPYING. Various parts are also covered under a BSD style license, detailed in COPYING.BSD. Currently, code covered under such license is copyrighted by Theo de Raadt. When in doubt, check the individual file, they should all have license headings and other identifying marks. Mailing List, getting involved ------------------------------ If you'd like to get involved with the project, subscribe to kde-pim@kde.org with an email to kde-pim-request@kde.org with the subject line: subscribe my@email.address If you have questions relating to development of this module please post them on the developers mailing list (kde-pim@kde.org). If you have user questions, please use kdepim-users@kde.org If you are interested in kmail development use the mailing list kmail-devel@kde.org. Debugging --------- You can use --enable-debug with the configure script, if you want to have debug code in your KDE libs. If you have the space and can stand code that's somewhat slower, this is worth it. The extra information really helps debugging and thus bugfixing. On the other hand, --disable-debug removes all debug messages, leading to a faster and cleaner desktop. See also the file DEBUG. Reporting Bugs -------------- Reporting bugs is an art. Why? Because bug reports can help and hinder. They hinder if the developers are just buried in an avalanche of bug reports. They spend hours figuring out which bug reports are valid and which aren't, which bug reports are due to bugs or due to installation problems. They can be of tremendous help to notify developers on problems in areas that they normally don't have access (e.g. KDE on AIX) to. So, here are some tips on bug reporting: * make sure your bug is due to KDE ... and not due to a packaging problem of your Linux distributor. For example, most "I can not install the XYZ.rpm" problem are due to packaging issues. Refer with such questions to your Linux Distributor and his appropriate mailing list or bug reporting tool. * The chance is high that your bug has already been dealt with ... so look if there is a newer version of kdepimlibs available. Reporting bugs for older, deprecated versions usually don't get that much attention :-) * Also the chance is high that another one experienced your problem. The bug report wizard at http://bugs.kde.org will help you to find out if your problem has already been reported. * The best bug report for a project based on voluntary work is of course one that comes with a patch that solves the problem. :-) More info --------- http://www.kde.org is a good starting point for info on KDE. If you are a developer, you may also point your browser to http://developer.kde.org. There is a plethora of mailing lists available, you can gain an overview quickly by looking at http://lists.kde.org. diff --git a/includes/CMakeLists.txt b/includes/CMakeLists.txt index 3707f684c..cfb9e085a 100644 --- a/includes/CMakeLists.txt +++ b/includes/CMakeLists.txt @@ -1,23 +1,24 @@ if (NOT ONLY_KLEO) add_subdirectory( tests ) macro(install_headers _dir) file(GLOB _includes "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}/[A-Za-z]*") install(FILES ${_includes} DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/${_dir} COMPONENT Devel) endmacro(install_headers) if(Akonadi_FOUND) install_headers( Akonadi ) endif(Akonadi_FOUND) install_headers( KHolidays ) install_headers( KABC ) install_headers( KBlog ) install_headers( KCal ) install_headers( KLDAP ) install_headers( KPIMIdentities ) install_headers( KPIMUtils ) install_headers( KResources ) install_headers( Syndication ) +install_headers( PimTextEdit ) endif (NOT ONLY_KLEO) diff --git a/includes/KPIMTextEdit/TextEdit b/includes/KPIMTextEdit/TextEdit new file mode 100644 index 000000000..3d2e99722 --- /dev/null +++ b/includes/KPIMTextEdit/TextEdit @@ -0,0 +1 @@ +#include "../../pimtextedit/textedit.h" \ No newline at end of file diff --git a/includes/tests/CMakeLists.txt b/includes/tests/CMakeLists.txt index 945bf634b..1c89aae7d 100644 --- a/includes/tests/CMakeLists.txt +++ b/includes/tests/CMakeLists.txt @@ -1,38 +1,39 @@ if (KDE4_BUILD_TESTS) # clear the file initially file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/all_includes.h "/* all forwarding includes */\n" ) macro(add_includes _dir) file(GLOB _includes "${CMAKE_CURRENT_SOURCE_DIR}/../${_dir}/[A-Z]*") foreach( file ${_includes} ) file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/all_includes.h "#include <${file}>\n" ) endforeach(file) endmacro(add_includes) include_directories( ${CMAKE_SOURCE_DIR}/kabc ${CMAKE_BINARY_DIR}/kabc ${CMAKE_BINARY_DIR}/kcal ) if ( Akonadi_FOUND ) add_includes( Akonadi ) endif( Akonadi_FOUND ) add_includes( KHolidays ) add_includes( KABC ) add_includes( KBlog ) add_includes( KCal ) add_includes( KLDAP ) add_includes( KResources ) add_includes( KPIMIdentities ) add_includes( Syndication ) add_includes( KPIMUtils ) +add_includes( PimTextEdit ) add_definitions( -DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII -DQT_NO_KEYWORDS ) kde4_add_executable( headertest header_compile.cpp ) add_dependencies( headertest kabc ) # ensure addressee.h is generated in parallel builds add_dependencies( headertest kcal ) endif (KDE4_BUILD_TESTS) diff --git a/pimtextedit/CMakeLists.txt b/pimtextedit/CMakeLists.txt new file mode 100644 index 000000000..31c796689 --- /dev/null +++ b/pimtextedit/CMakeLists.txt @@ -0,0 +1,26 @@ +add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") + +set(pimtextedit_lib_srcs + emailquotehighlighter.cpp + textedit.cpp +) + +kde4_add_library(pimtextedit SHARED ${pimtextedit_lib_srcs}) + +target_link_libraries(pimtextedit + ${KDE4_KDEUI_LIBS} + ${KDE4_KIO_LIBS} + kpimidentities + kmime +) + +set_target_properties(pimtextedit PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) + +install(TARGETS pimtextedit EXPORT kdepimlibsLibraryTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES + textedit.h + emailquotehighlighter.h + DESTINATION ${INCLUDE_INSTALL_DIR}/pimtextedit COMPONENT Devel +) + + diff --git a/pimtextedit/emailquotehighlighter.cpp b/pimtextedit/emailquotehighlighter.cpp new file mode 100644 index 000000000..248a8aea0 --- /dev/null +++ b/pimtextedit/emailquotehighlighter.cpp @@ -0,0 +1,122 @@ +/** + * Copyright (C) 2006 Laurent Montel + * Copyright (C) 2008 Thomas McGuire + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "emailquotehighlighter.h" + +#include "textedit.h" + +namespace PimTextEdit { + +class EMailQuoteHighlighter::EMailQuoteHighlighterPrivate +{ +public: + QColor col1, col2, col3, misspelledColor; + bool spellCheckingEnabled; + TextEdit *parent; +}; + +EMailQuoteHighlighter::EMailQuoteHighlighter( TextEdit *textEdit, + const QColor &normalColor, + const QColor "eDepth1, + const QColor "eDepth2, + const QColor "eDepth3, + const QColor &misspelledColor ) + : Highlighter( textEdit ), + d( new EMailQuoteHighlighterPrivate() ) +{ + Q_UNUSED( normalColor ); + // Don't automatically disable the spell checker, for example because there + // are too many misspelled words. That would also disable quote highlighting. + // FIXME: disable this spell checking! + setAutomatic( false ); + + setActive( true ); + d->col1 = quoteDepth1; + d->col2 = quoteDepth2; + d->col3 = quoteDepth3; + d->misspelledColor = misspelledColor; + d->spellCheckingEnabled = false; + d->parent = textEdit; +} + +EMailQuoteHighlighter::~EMailQuoteHighlighter() +{ +} + +void EMailQuoteHighlighter::setQuoteColor( const QColor &normalColor, + const QColor "eDepth1, + const QColor "eDepth2, + const QColor "eDepth3, + const QColor &misspelledColor ) +{ + Q_UNUSED( normalColor ); + d->col1 = quoteDepth1; + d->col2 = quoteDepth2; + d->col3 = quoteDepth3; + d->misspelledColor = misspelledColor; +} + +void EMailQuoteHighlighter::toggleSpellHighlighting( bool on ) +{ + if ( on != d->spellCheckingEnabled ) { + d->spellCheckingEnabled = on; + rehighlight(); + } +} + +void EMailQuoteHighlighter::highlightBlock( const QString & text ) +{ + QString simplified = text; + simplified = simplified.replace( QRegExp( QLatin1String( "\\s" ) ), QString() ) + .replace( QLatin1Char( '|' ), QLatin1String(">") ); + while ( simplified.startsWith( QLatin1String(">>>>") ) ) + simplified = simplified.mid( 3 ); + if ( simplified.startsWith( QLatin1String(">>>") ) || + simplified.startsWith( QLatin1String("> > >") ) ) + setFormat( 0, text.length(), d->col1 ); + else if ( simplified.startsWith( QLatin1String(">>") ) || + simplified.startsWith( QLatin1String("> >") ) ) + setFormat( 0, text.length(), d->col2 ); + else if ( simplified.startsWith( QLatin1String(">") ) ) + setFormat( 0, text.length(), d->col3 ); + /*else if ( !d->parent->quotePrefixName().simplified().isEmpty() && + text.startsWith( d->parent->quotePrefixName() ) ) + setFormat( 0, text.length(), d->col1 );*/ //FIXME !!!!! + else + { + if ( d->spellCheckingEnabled ) + Highlighter::highlightBlock( text ); + } + setCurrentBlockState( 0 ); +} + +void EMailQuoteHighlighter::unsetMisspelled( int start, int count ) +{ + Q_UNUSED( start ) + Q_UNUSED( count ) +} + +void EMailQuoteHighlighter::setMisspelled( int start, int count ) +{ + setMisspelledColor( d->misspelledColor ); + Sonnet::Highlighter::setMisspelled( start, count ); +} + +} diff --git a/pimtextedit/emailquotehighlighter.h b/pimtextedit/emailquotehighlighter.h new file mode 100644 index 000000000..74f81c431 --- /dev/null +++ b/pimtextedit/emailquotehighlighter.h @@ -0,0 +1,96 @@ +/** + * Copyright (C) 2006 Laurent Montel + * Copyright (C) 2008 Thomas McGuire + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ +#ifndef KDEPIM_KEMAILQUOTINGHIGHTER_H +#define KDEPIM_KEMAILQUOTINGHIGHTER_H + +#include "pimtextedit_export.h" + +#include + +#include + +// TODO: fix include + +namespace PimTextEdit +{ + class TextEdit; + + /** + * This highlighter highlights spelling mistakes and also highlightes + * quotes. + * + * Spelling mistakes inside quotes will not be highlighted. + * The quote highlighting color is configurable. + * + * Spell highlighting is disabled by default but can be toggled. + */ + class PIMTEXTEDIT_EXPORT EMailQuoteHighlighter : public Sonnet::Highlighter + { + public: + + // FIXME: Default colors don't obey color scheme + // The normalColor parameter will be ignored, only provided for KNode + // compatibility. + explicit EMailQuoteHighlighter( TextEdit *textEdit, + const QColor &normalColor = Qt::black, + const QColor "eDepth1 = QColor( 0x00, 0x80, 0x00 ), + const QColor "eDepth2 = QColor( 0x00, 0x80, 0x00 ), + const QColor "eDepth3 = QColor( 0x00, 0x80, 0x00 ), + const QColor &misspelledColor = Qt::red ); + + ~EMailQuoteHighlighter(); + + // The normalColor parameter will be ignored, only provided for KNode + // compatibility. + void setQuoteColor( const QColor &normalColor, + const QColor "eDepth1, + const QColor "eDepth2, + const QColor "eDepth3, + const QColor &misspelledColor = Qt::red ); + + /** + * Turns spellcheck highlighting on or off. + * + * @param on if true, spelling mistakes will be highlighted + */ + void toggleSpellHighlighting( bool on ); + + // Reimplemented to highlight quote blocks. + virtual void highlightBlock ( const QString & text ); + + protected: + + // Reimplemented, the base version sets the text color to black, which + // is not what we want. We do nothing, the format is already reset by + // Qt. + virtual void unsetMisspelled( int start, int count ); + + // Reimplemented to set the color of the misspelled word to a color + // defined by setQuoteColor(). + virtual void setMisspelled( int start, int count ); + + private: + class EMailQuoteHighlighterPrivate; + std::auto_ptr d; + + }; +} + +#endif diff --git a/pimtextedit/pimtextedit_export.h b/pimtextedit/pimtextedit_export.h new file mode 100644 index 000000000..387753a25 --- /dev/null +++ b/pimtextedit/pimtextedit_export.h @@ -0,0 +1,35 @@ +/* + Copyright (c) 2009 Thomas McGuire + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef PIMTEXTEDIT_PIMTEXTEDIT_EXPORT_H +#define PIMTEXTEDIT_PIMTEXTEDIT_EXPORT_H + +#include + +#ifndef PIMTEXTEDIT_EXPORT +# if defined(MAKE_PIMTEXTEDIT_LIB) + /* We are building this library */ +# define PIMTEXTEDIT_EXPORT KDE_EXPORT +# else + /* We are using this library */ +# define PIMTEXTEDIT_EXPORT KDE_IMPORT +# endif +#endif + +#endif diff --git a/pimtextedit/textedit.cpp b/pimtextedit/textedit.cpp new file mode 100644 index 000000000..9e52282a4 --- /dev/null +++ b/pimtextedit/textedit.cpp @@ -0,0 +1,604 @@ +/* + Copyright (c) 2009 Thomas McGuire + + Based on KMail and libkdepim code by: + Copyright 2007 Laurent Montel + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#include "textedit.h" + +#include "emailquotehighlighter.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace PimTextEdit { + +class TextEditPrivate +{ + public: + + TextEditPrivate( TextEdit *parent ) + : q( parent ) + { + } + + /** + * Helper function for addImage(), which does the actual work of adding the QImage as a + * resource to the document, pasting it and adding it to the image name list. + * + * @param imageName the desired image name. If it is already taken, a number will + * be appended to it + * @param image the actual image to add + */ + void addImageHelper( const QString &imageName, const QImage &image ); + + /** + * Helper function to get the list of all QTextImageFormats in the document. + */ + QList embeddedImageFormats() const; + + QList< QPair > signaturePositions( const KPIMIdentities::Signature &sig ) const; + + void fixupTextEditString( QString &text ) const; + + // Returns the text of the signature. If the signature is HTML, the HTML + // tags will be stripped. + QString plainSignatureText( const KPIMIdentities::Signature &signature ) const; + + void init(); + + void _k_slotAddImage(); + + KAction *actionAddImage; + TextEdit *q; + + /** + * The names of embedded images. + * Used to easily obtain the names of the images. + * New images are compared to the the list and not added as resource if already present. + */ + QStringList mImageNames; + + + // Although KTextEdit keeps track of the spell checking state, we override + // it here, because we have a highlighter which does quote highlighting. + // And since disabling spellchecking in KTextEdit simply would turn off our + // quote highlighter, we never actually deactivate spell checking in the + // base class, but only tell our own email highlighter to not highlight + // spelling mistakes. + // For this, we use the KTextEditSpellInterface, which is basically a hack + // that makes it possible to have our own enabled/disabled state in a binary + // compatible way. + bool spellCheckingEnabled; +}; + +} // namespace + +using namespace PimTextEdit; + +void TextEditPrivate::fixupTextEditString( QString &text ) const +{ + // Remove line separators. Normal \n chars are still there, so no linebreaks get lost here + text.remove( QChar::LineSeparator ); + + // Get rid of embedded images, see QTextImageFormat documentation: + // "Inline images are represented by an object replacement character (0xFFFC in Unicode) " + text.remove( 0xFFFC ); + + // In plaintext mode, each space is non-breaking. + text.replace( QChar::Nbsp, QChar::fromAscii( ' ' ) ); +} + +QList< QPair > TextEditPrivate::signaturePositions( const KPIMIdentities::Signature &sig ) const +{ + QList< QPair > signaturePositions; + if ( !sig.rawText().isEmpty() ) { + + QString sigText = plainSignatureText( sig ); + + int currentSearchPosition = 0; + forever { + + // Find the next occurrence of the signature text + QString text = q->document()->toPlainText(); + int currentMatch = text.indexOf( sigText, currentSearchPosition ); + currentSearchPosition = currentMatch + sigText.length(); + if ( currentMatch == -1 ) + break; + + signaturePositions.append( QPair( currentMatch, + currentMatch + sigText.length() ) ); + } + } + return signaturePositions; +} + +QString TextEditPrivate::plainSignatureText( const KPIMIdentities::Signature &signature ) const +{ + QString sigText = signature.rawText(); + if ( signature.isInlinedHtml() && + signature.type() == KPIMIdentities::Signature::Inlined ) { + + // Use a QTextDocument as a helper, it does all the work for us and + // strips all HTML tags. + QTextDocument helper; + QTextCursor helperCursor( &helper ); + helperCursor.insertHtml( sigText ); + sigText = helper.toPlainText(); + } + return sigText; +} + +TextEdit::TextEdit( const QString& text, QWidget *parent ) + : KRichTextWidget( text, parent ), + d( new TextEditPrivate( this ) ) +{ + d->init(); +} + +TextEdit::TextEdit( QWidget *parent ) + : KRichTextWidget( parent ), + d( new TextEditPrivate( this ) ) +{ + d->init(); +} + +TextEdit::~TextEdit() +{ +} + +bool TextEdit::eventFilter( QObject*o, QEvent* e ) +{ + if (o == this) + KCursor::autoHideEventFilter( o, e ); + return KRichTextWidget::eventFilter( o, e ); +} + +void TextEditPrivate::init() +{ + q->setSpellInterface(q); + // We tell the KRichTextWidget to enable spell checking, because only then it will + // call createHighlighter() which will create our own highlighter which also + // does quote highlighting. + // However, *our* spellchecking is still disabled. Our own highlighter only + // cares about our spellcheck status, it will not highlight missspelled words + // if our spellchecking is disabled. + // See also KEMailQuotingHighlighter::highlightBlock(). + spellCheckingEnabled = false; + q->setCheckSpellingEnabledInternal( true ); + + KCursor::setAutoHideCursor( q, true, true ); + q->installEventFilter( q ); +} + +void TextEdit::insertFromMimeData( const QMimeData * source ) +{ + // Attempt to paste HTML contents into the text edit in plain text mode, + // prevent this and prevent plain text instead. + if ( textMode() == KRichTextEdit::Plain && source->hasHtml() ) { + if ( source->hasText() ) { + insertPlainText( source->text() ); + } + } + else + KRichTextWidget::insertFromMimeData( source ); +} + +void TextEdit::keyPressEvent ( QKeyEvent * e ) +{ + if ( e->key() == Qt::Key_Return ) { + QTextCursor cursor = textCursor(); + int oldPos = cursor.position(); + int blockPos = cursor.block().position(); + + //selection all the line. + cursor.movePosition( QTextCursor::StartOfBlock ); + cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); + QString lineText = cursor.selectedText(); + if ( ( ( oldPos -blockPos ) > 0 ) && + ( ( oldPos-blockPos ) < int( lineText.length() ) ) ) { + bool isQuotedLine = false; + int bot = 0; // bot = begin of text after quote indicators + while ( bot < lineText.length() ) { + if( ( lineText[bot] == QChar::fromAscii( '>' ) ) || + ( lineText[bot] == QChar::fromAscii( '|' ) ) ) { + isQuotedLine = true; + ++bot; + } + else if ( lineText[bot].isSpace() ) { + ++bot; + } + else { + break; + } + } + KRichTextWidget::keyPressEvent( e ); + // duplicate quote indicators of the previous line before the new + // line if the line actually contained text (apart from the quote + // indicators) and the cursor is behind the quote indicators + if ( isQuotedLine + && ( bot != lineText.length() ) + && ( ( oldPos-blockPos ) >= int( bot ) ) ) { + // The cursor position might have changed unpredictably if there was selected + // text which got replaced by a new line, so we query it again: + cursor.movePosition( QTextCursor::StartOfBlock ); + cursor.movePosition( QTextCursor::EndOfBlock, QTextCursor::KeepAnchor ); + QString newLine = cursor.selectedText(); + + // remove leading white space from the new line and instead + // add the quote indicators of the previous line + int leadingWhiteSpaceCount = 0; + while ( ( leadingWhiteSpaceCount < newLine.length() ) + && newLine[leadingWhiteSpaceCount].isSpace() ) { + ++leadingWhiteSpaceCount; + } + newLine = newLine.replace( 0, leadingWhiteSpaceCount, + lineText.left( bot ) ); + cursor.insertText( newLine ); + //cursor.setPosition( cursor.position() + 2); + cursor.movePosition( QTextCursor::StartOfBlock ); + setTextCursor( cursor ); + } + } + else + KRichTextWidget::keyPressEvent( e ); + } + else + { + KRichTextWidget::keyPressEvent( e ); + } +} + + +bool TextEdit::isSpellCheckingEnabled() const +{ + return d->spellCheckingEnabled; +} + +void TextEdit::setSpellCheckingEnabled( bool enable ) +{ + EMailQuoteHighlighter *hlighter = + dynamic_cast( highlighter() ); + if ( hlighter ) + hlighter->toggleSpellHighlighting( enable ); + + d->spellCheckingEnabled = enable; + emit checkSpellingChanged( enable ); +} + +bool TextEdit::shouldBlockBeSpellChecked(const QString& block) const +{ + // FIXME: more generic, overwrite in kmeditor with the whole quote mess + return true; + /*return quotePrefixName().simplified().isEmpty() || + !block.startsWith( quotePrefixName() );*/ +} + +void TextEdit::createHighlighter() +{ + EMailQuoteHighlighter *emailHighLighter = + new EMailQuoteHighlighter( this ); + + //FIXME changeHighlighterColors( emailHighLighter ); + + //TODO change config + KRichTextWidget::setHighlighter( emailHighLighter ); + + if ( !spellCheckingLanguage().isEmpty() ) + setSpellCheckingLanguage( spellCheckingLanguage() ); + setSpellCheckingEnabled( isSpellCheckingEnabled() ); +} + +void TextEdit::changeHighlighterColors( EMailQuoteHighlighter * ) +{ + // FIXME (?) + // Should probably only be overwritten in the derived classes +} + + +void TextEdit::insertSignature( const KPIMIdentities::Signature &sig, + Placement placement, bool addSeparator ) +{ + QString signature; + if ( addSeparator ) + signature = sig.withSeparator(); + else + signature = sig.rawText(); + + insertSignature( signature, placement, + ( sig.isInlinedHtml() && + sig.type() == KPIMIdentities::Signature::Inlined ) ); +} + +void TextEdit::insertSignature( const QString &signature, Placement placement, + bool isHtml ) +{ + if ( !signature.isEmpty() ) { + + // Save the modified state of the document, as inserting a signature + // shouldn't change this. Restore it at the end of this function. + bool isModified = document()->isModified(); + + // Move to the desired position, where the signature should be inserted + QTextCursor cursor = textCursor(); + QTextCursor oldCursor = cursor; + cursor.beginEditBlock(); + + if ( placement == End ) + cursor.movePosition( QTextCursor::End ); + else if ( placement == Start ) + cursor.movePosition( QTextCursor::Start ); + setTextCursor( cursor ); + + // Insert the signature and newlines depending on where it was inserted. + bool hackForCursorsAtEnd = false; + int oldCursorPos = -1; + if ( placement == End ) { + + if ( oldCursor.position() == toPlainText().length() ) { + hackForCursorsAtEnd = true; + oldCursorPos = oldCursor.position(); + } + + if ( isHtml ) { + insertHtml( QLatin1String( "
" ) + signature ); + } else { + insertPlainText( QLatin1Char( '\n' ) + signature ); + } + } else if ( placement == Start || placement == AtCursor ) { + if ( isHtml ) { + insertHtml( QLatin1String( "
" ) + signature + QLatin1String( "
" ) ); + } else { + insertPlainText( QLatin1Char( '\n' ) + signature + QLatin1Char( '\n' ) ); + } + } + + cursor.endEditBlock(); + + // There is one special case when re-setting the old cursor: The cursor + // was at the end. In this case, QTextEdit has no way to know + // if the signature was added before or after the cursor, and just decides + // that it was added before (and the cursor moves to the end, but it should + // not when appending a signature). See bug 167961 + if ( hackForCursorsAtEnd ) + oldCursor.setPosition( oldCursorPos ); + + setTextCursor( oldCursor ); + ensureCursorVisible(); + + document()->setModified( isModified ); + + if ( isHtml ) + enableRichTextMode(); + } +} + +void TextEdit::replaceSignature( const KPIMIdentities::Signature &oldSig, + const KPIMIdentities::Signature &newSig ) +{ + QTextCursor cursor( document() ); + cursor.beginEditBlock(); + + QString oldSigText = d->plainSignatureText( oldSig ); + + int currentSearchPosition = 0; + forever { + + // Find the next occurrence of the signature text + QString text = document()->toPlainText(); + int currentMatch = text.indexOf( oldSigText, currentSearchPosition ); + currentSearchPosition = currentMatch; + if ( currentMatch == -1 ) + break; + + // Select the signature + QTextCursor cursor( document() ); + cursor.setPosition( currentMatch ); + + // If the new signature is completely empty, we also want to remove the + // signature separator, so include it in the selection + int additionalMove = 0; + if ( newSig.rawText().isEmpty() && + text.mid( currentMatch - 4, 4) == QLatin1String( "-- \n" ) ) { + cursor.movePosition( QTextCursor::PreviousCharacter, + QTextCursor::MoveAnchor, 4 ); + additionalMove = 4; + } + cursor.movePosition( QTextCursor::NextCharacter, QTextCursor::KeepAnchor, + oldSigText.length() + additionalMove ); + + + // Skip quoted signatures + // FIXME!!! + /*if ( cursor.block().text().startsWith( quotePrefixName() ) ) { + currentSearchPosition += d->plainSignatureText( oldSig ).length(); + continue; + }*/ + + // Remove the old and instert the new signature + cursor.removeSelectedText(); + if ( newSig.isInlinedHtml() && + newSig.type() == KPIMIdentities::Signature::Inlined ) { + cursor.insertHtml( newSig.rawText() ); + enableRichTextMode(); + } + else + cursor.insertText( newSig.rawText() ); + + currentSearchPosition += d->plainSignatureText( newSig ).length(); + } + + cursor.endEditBlock(); +} + +QString TextEdit::toWrappedPlainText() const +{ + QString temp; + QTextDocument* doc = document(); + QTextBlock block = doc->begin(); + while ( block.isValid() ) { + QTextLayout* layout = block.layout(); + for ( int i = 0; i < layout->lineCount(); i++ ) { + QTextLine line = layout->lineAt( i ); + temp += block.text().mid( line.textStart(), line.textLength() ) + QLatin1Char( '\n' ); + } + block = block.next(); + } + + d->fixupTextEditString( temp ); + return temp; +} + +QString TextEdit::toCleanPlainText() const +{ + QString temp = toPlainText(); + d->fixupTextEditString( temp ); + return temp; +} + +void TextEdit::createActions( KActionCollection *actionCollection ) +{ + KRichTextWidget::createActions( actionCollection ); + + d->actionAddImage = new KAction( KIcon( QLatin1String( "insert-image" ) ), i18n( "Add Image" ), this ); + actionCollection->addAction( QLatin1String( "add_image" ), d->actionAddImage ); + connect( d->actionAddImage, SIGNAL(triggered(bool) ), SLOT( _k_slotAddImage() ) ); +} + + +void TextEdit::addImage( const KUrl &url ) +{ + QImage image; + if ( !image.load( url.path() ) ) { + KMessageBox::error( this, + i18nc( "@info", "Unable to load image %1.", url.path() ) ); + return; + } + QFileInfo fi( url.path() ); + QString imageName = fi.baseName().isEmpty() ? QLatin1String( "image.png" ) + : fi.baseName() + QLatin1String( ".png" ); + d->addImageHelper( imageName, image ); +} + +void TextEditPrivate::addImageHelper( const QString &imageName, const QImage &image ) +{ + QString imageNameToAdd = imageName; + QTextDocument *document = q->document(); + + // determine the imageNameToAdd + int imageNumber = 1; + while ( mImageNames.contains( imageNameToAdd ) ) { + QVariant qv = document->resource( QTextDocument::ImageResource, QUrl( imageNameToAdd ) ); + if ( qv == image ) { + // use the same name + break; + } + imageNameToAdd = imageName + QString::number( imageNumber++ ); + } + + if ( !mImageNames.contains( imageNameToAdd ) ) { + document->addResource( QTextDocument::ImageResource, QUrl( imageNameToAdd ), image ); + mImageNames << imageNameToAdd; + } + q->textCursor().insertImage( imageNameToAdd ); + q->enableRichTextMode(); +} + +QList< QSharedPointer > TextEdit::embeddedImages() const +{ + QList< QSharedPointer > retImages; + QStringList seenImageNames; + QList imageFormats = d->embeddedImageFormats(); + foreach( const QTextImageFormat &imageFormat, imageFormats ) { + if ( !seenImageNames.contains( imageFormat.name() ) ) { + QVariant data = document()->resource( QTextDocument::ImageResource, QUrl( imageFormat.name() ) ); + QImage image = qvariant_cast( data ); + QBuffer buffer; + buffer.open( QIODevice::WriteOnly ); + image.save( &buffer, "PNG" ); + + qsrand( QDateTime::currentDateTime().toTime_t() + qHash( imageFormat.name() ) ); + QSharedPointer embeddedImage( new EmbeddedImage() ); + retImages.append( embeddedImage ); + embeddedImage->image = KMime::Codec::codecForName( "base64" )->encode( buffer.buffer() ); + embeddedImage->imageName = imageFormat.name(); + embeddedImage->contentID = QString( QLatin1String( "%1" ) ).arg( qrand() ); + seenImageNames.append( imageFormat.name() ); + } + } + return retImages; +} + +QList TextEditPrivate::embeddedImageFormats() const +{ + QTextDocument *doc = q->document(); + QList retList; + + QTextBlock currentBlock = doc->begin(); + while ( currentBlock.isValid() ) { + QTextBlock::iterator it; + for ( it = currentBlock.begin(); !it.atEnd(); ++it ) { + QTextFragment fragment = it.fragment(); + if ( fragment.isValid() ) { + QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); + if ( imageFormat.isValid() ) { + retList.append( imageFormat ); + } + } + } + currentBlock = currentBlock.next(); + } + return retList; +} + +void TextEditPrivate::_k_slotAddImage() +{ + KFileDialog fdlg( QString(), QString(), q ); + fdlg.setOperationMode( KFileDialog::Other ); + fdlg.setCaption( i18n("Add Image") ); + fdlg.okButton()->setGuiItem( KGuiItem( i18n("&Add"), QLatin1String( "document-open" ) ) ); + fdlg.setMode( KFile::Files ); + if ( fdlg.exec() != KDialog::Accepted ) + return; + + const KUrl::List files = fdlg.selectedUrls(); + foreach ( const KUrl& url, files ) { + q->addImage( url ); + } +} + + + + + +#include "textedit.moc" diff --git a/pimtextedit/textedit.h b/pimtextedit/textedit.h new file mode 100644 index 000000000..d83137565 --- /dev/null +++ b/pimtextedit/textedit.h @@ -0,0 +1,253 @@ +/* + Copyright (c) 2009 Thomas McGuire + + Based on KMail and libkdepim code by: + Copyright 2007 Laurent Montel + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#ifndef PIMTEXTEDIT_TEXTEDIT_H +#define PIMTEXTEDIT_TEXTEDIT_H + +#include "pimtextedit_export.h" + +#include + +#include + +#include + +#include + +class KUrl; + +namespace PimTextEdit { + +class TextEditPrivate; +class EMailQuoteHighlighter; + +/** + * Holds information about an embedded HTML image. + * A list with all images can be retrieved with KMComposerEditor::embeddedImages(). + */ +struct EmbeddedImage +{ + QByteArray image; ///< The image, encoded as PNG with base64 encoding + QString contentID; ///< The content id of the embedded image + QString imageName; ///< Name of the image as it is available as a resource in the editor +}; + +// TODO: + +// APIDOX +// Proofreading +// coding style +// FIXMEs (!!) +// GUI test app +// clean up mime insert/paste mess +// clean up quoting mess + +// Unittests: +// inssert signature at various places +// replace signature at various places + not inside qutoes +// ^ same for HTML signature +// ^ same for multiline sigs +// test inserting images -> embeddedImages() returns the correct thing? images there? +// toWrappedPlainText() +// toCleanPlainText() +// enter key: normal + in quote + +class PIMTEXTEDIT_EXPORT TextEdit : public KRichTextWidget, + protected KTextEditSpellInterface // TODO: KDE5: get rid of the spell interface +{ + Q_OBJECT + + public: + + /** + * Describes the placement of a text which is to be inserted into this + * textedit. + */ + enum Placement { + Start, ///< The text is placed at the start of the textedit + End, ///< The text is placed at the end of the textedit + AtCursor ///< The text is placed at the current cursor position + }; + + /** + * Constructs a TextEdit object + */ + explicit TextEdit( const QString& text, QWidget *parent = 0 ); + + /** + * Constructs a TextEdit object. + */ + explicit TextEdit( QWidget *parent = 0 ); + + /** + * Reimplemented from KMEditor, to support more actions. + * + * The additional action XML names are: + * - add_image + * + * @reimp + */ + virtual void createActions( KActionCollection *actionCollection ); + + /** + * Adds an image. The image is loaded from file and then pasted to the current + * cursor position. + * + * @param url The URL of the file which contains the image + */ + void addImage( const KUrl &url ); + + /** + * Get a list with all embedded HTML images. + * If the same image is contained twice or more in the editor, it will have only + * one entry in this list. + * + * @return a list of embedded HTML images of the editor. + */ + QList< QSharedPointer > embeddedImages() const; + + /** + * Inserts the signature @p sig into the textedit. + * The cursor position is preserved. + * A leading or trailing newline is also added automatically, depending on + * the placement. + * For undo/redo, this is treated as one operation. + * + * Rich text mode will be enabled if the signature is in inlined HTML format. + * + * @param placement defines where in the textedit the signature should be + * inserted. + * @param addSeparator if true, the separator '-- \n' will be added in front + * of the signature + */ + void insertSignature( const KPIMIdentities::Signature &sig, + Placement placement = End, bool addSeparator = true ); + + /** + * Inserts the signature @p sig into the textedit. + * The cursor position is preserved. + * A leading or trailing newline is also added automatically, depending on + * the placement. + * For undo/redo, this is treated as one operation. + * A separator is not added. + * + * Use the other insertSignature() function if possible, as it has support + * for separators and does HTML detection automatically. + * + * Rich text mode will be enabled if @p isHtml is true. + * + * @param placement defines where in the textedit the signature should be + * inserted. + * @param isHtml defines whether the signature should be inserted as text or html + */ + void insertSignature( const QString &signature, Placement placement = End, + bool isHtml = false ); + + /** + * Replaces all occurrences of the old signature with the new signature. + * Text in quotes will be ignored. + * For undo/redo, this is treated as one operation. + * If the old signature is empty, nothing is done. + * If the new signature is empty, the old signature including the + * separator is removed. + * + * @param oldSig the old signature, which will be replaced + * @param newSig the new signature + */ + void replaceSignature( const KPIMIdentities::Signature &oldSig, + const KPIMIdentities::Signature &newSig ); + + /** + * Returns the text of the editor as plain text, with linebreaks inserted + * where word-wrapping occurred. + */ + QString toWrappedPlainText() const; + + /** + * Same as toPlainText() from QTextEdit, only that it removes embedded images. + */ + QString toCleanPlainText() const; + + ~TextEdit(); + + virtual void changeHighlighterColors( EMailQuoteHighlighter * ); + + protected: + + virtual void dropEvent( QDropEvent *e ) { + //FIXME !! + } + virtual bool canInsertFromMimeData( const QMimeData *source ) const { + // FIXME + } + virtual void insertFromMimeData( const QMimeData *source ); + virtual bool eventFilter( QObject*o, QEvent* e ); + virtual void keyPressEvent ( QKeyEvent * e ); + + // For the explaination for these three methods, see the comment at the + // spellCheckingEnabled variable of the private class. + + /** + * Reimplemented from KTextEditSpellInterface + * @reimp + */ + virtual bool isSpellCheckingEnabled() const; + + /** + * Reimplemented from KTextEditSpellInterface + * @reimp + */ + virtual void setSpellCheckingEnabled( bool enable ); + + /** + * Reimplemented from KTextEditSpellInterface, to avoid spellchecking + * quoted text. + * @reimp + */ + virtual bool shouldBlockBeSpellChecked( const QString& block ) const; + + /** + * Reimplemented to create our own highlighter which does quote and + * spellcheck highlighting + * @reimp + */ + virtual void createHighlighter(); + + public Q_SLOTS: + + /** + * Pastes the content of the clipboard into the editor, if the + * mime type of the clipboard's contents in supported. + */ + virtual void paste() { + // FIXME !! + } + + private: + + std::auto_ptr const d; + friend class TextEditPrivate; + Q_PRIVATE_SLOT( d, void _k_slotAddImage() ); +}; + +} // namespace + +#endif