Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0823ec1137 | |||
| 1c4ebcccd1 | |||
| 9fd7e99274 | |||
| bf567146d8 | |||
| 3bade98d2f | |||
| bd03359d79 | |||
| 89b4218853 | |||
| f9f479588f | |||
| 40e81af6c6 | |||
| 10db33e21e | |||
| 78cb2bc641 | |||
| 0c67ad480c | |||
| 141bff5dd3 | |||
| ae8f851b06 | |||
| 65d3f78a14 | |||
| f2fbc7463b | |||
| 91626a9c87 | |||
| 858a70fe82 | |||
| 3200be221c | |||
| 452059cb18 | |||
| c84d96043e | |||
| 3179f447ce | |||
| 9c0c6480e3 | |||
| f0ec035c02 | |||
| dd04b72a67 | |||
| 9d0ee5010b | |||
| 919dbe2a0f | |||
| 8c44bbd89b |
@@ -8,6 +8,8 @@
|
||||
Valerio Pilo <amroth@coldshock.net>
|
||||
Antonio Nastasi <sifcenter@gmail.com>
|
||||
Ruben Vandamme <vandammeru@gmail.com>
|
||||
Sjors Gielen <dazjorz@dazjorz.com>
|
||||
Adam Goossens <fontknocker@gmail.com>
|
||||
|
||||
-- Translations --
|
||||
|
||||
@@ -19,16 +21,19 @@
|
||||
|
||||
Mohamed Aser <mohasr@link.net>
|
||||
Youssef Chahibi <chahibi@gmail.com>
|
||||
Ma'moun Diraneyya <mamoun.diraneyya@gmail.com>
|
||||
|
||||
Brazilian Portuguese:
|
||||
|
||||
Mauricio Rother <mauricio@digicomm.com.br>
|
||||
Leonel Freire <leonelfreire@gmail.com>
|
||||
Maurício Arozi Moraes
|
||||
Sergio Rafael Lemke <bedi.com@gmail.com>
|
||||
Morris Arozi Moraes
|
||||
|
||||
Catalan:
|
||||
|
||||
Jaume Cornadó <jaumec@lleida.net>
|
||||
Adrià Arrufat <swiftscythe@gmail.com>
|
||||
|
||||
Chinese (Simplified):
|
||||
|
||||
@@ -53,16 +58,23 @@
|
||||
Jaap Woldringh <jjh.woldringh@planet.nl>
|
||||
Elve <elve@savage-elve.net>
|
||||
Sander Pientka <cumulus0007@gmail.com>
|
||||
Heimen Stoffels <vistausss@gmail.com>
|
||||
|
||||
Estonian:
|
||||
|
||||
Jyri Toomessoo <nuubik@hotmail.com>
|
||||
Lauri Võsandi <v6sa@hotmail.com>
|
||||
Mihkel Tõnnov <mihhkel@gmail.com>
|
||||
Rivo Laks <rivolaks@hot.ee>
|
||||
Timo Tambet <ttambet@gmail.com>
|
||||
|
||||
Finnish:
|
||||
|
||||
Markus Vuori <markus@vuoret.net>
|
||||
Joonas Niilola <juippis@roskakori.org>
|
||||
Jussi Timperi <jussi.timperi@gmail.com>
|
||||
Antony Hussi <antony.hussi@tranciend.com>
|
||||
Marko Mäkilä <maruchan86@gmail.com>
|
||||
|
||||
French:
|
||||
|
||||
@@ -70,6 +82,13 @@
|
||||
Vincent Fretin <fretinvincent@hotmail.com>
|
||||
Andrea Blankenstijn <darkan9el@gmail.com>
|
||||
Barthe Guillaume <gu_barthe@yahoo.fr>
|
||||
Scias <shining.scias@gmail.com>
|
||||
Grégory Bellier <gregory.bellier@gmail.com>
|
||||
Anthony Rey <gnuk@mailoo.org>
|
||||
|
||||
Galician:
|
||||
|
||||
Indalecio Freiría Santos <ifreiria@gmail.com>
|
||||
|
||||
German:
|
||||
|
||||
@@ -80,6 +99,7 @@
|
||||
|
||||
Panagiotis Papadopoulos <pano_90@gmx.net>
|
||||
Dimitrios Glentadakis <dglent@gmail.com>
|
||||
George Kiagiadakis <kiagiadakis.george@gmail.com>
|
||||
|
||||
Hungarian:
|
||||
|
||||
@@ -91,6 +111,11 @@
|
||||
David Vignoni <dvgn@libero.it>
|
||||
Valerio Pilo <amroth@coldshock.net>
|
||||
Vincenzo Reale <smart2128@baslug.org>
|
||||
Andrea Decorte <adecorte@gmail.com>
|
||||
|
||||
Japanese:
|
||||
|
||||
Daniel E. Moctezuma <dmoctezuma@kmess.org>
|
||||
|
||||
Korean:
|
||||
|
||||
@@ -100,6 +125,14 @@
|
||||
|
||||
Øyvind Sæther <oyvind@sather.tk>
|
||||
|
||||
Portuguese:
|
||||
|
||||
Bruno Almeida <bruno.r.almeida@gmail.com>
|
||||
|
||||
Russian:
|
||||
|
||||
Dmitriy Simbiriatin <slpiv@mail.ru>
|
||||
|
||||
Serbian:
|
||||
|
||||
Zoran Milovanović <provalisam@gmail.com>
|
||||
@@ -110,7 +143,7 @@
|
||||
|
||||
Slovenian:
|
||||
|
||||
Matjaž kaše <kase@g-kabel.si>
|
||||
Matjaž Kaše <kase@g-kabel.si>
|
||||
|
||||
Spanish:
|
||||
|
||||
@@ -121,6 +154,8 @@
|
||||
Christian Kaiser <k39@users.sourceforge.net>
|
||||
Juan Pablo González Tognarelli <jotapesan@gmail.com>
|
||||
Alexis Daniel Medina Medina <alexismedina@gmail.com>
|
||||
Manuel Ramírez <elpreto@kde.org.ar>
|
||||
Mauricio Muñoz Lucero <real.mml@gmail.com>
|
||||
|
||||
Swedish:
|
||||
|
||||
@@ -189,20 +224,37 @@
|
||||
Matteo Nardi <91.matteo@gmail.com>
|
||||
- KWallet support
|
||||
|
||||
Adam Goossens <fontknocker@gmail.com>
|
||||
- Notifications blocking option
|
||||
- Winks disabling option
|
||||
- Last message date feature
|
||||
|
||||
Sjors Gielen <dazjorz@dazjorz.com>
|
||||
- IRC-like commands in the chat window
|
||||
|
||||
Andrea Decorte <adecorte@gmail.com>
|
||||
- Group selection in 'contact added user' dialog
|
||||
|
||||
Dario Freddi <drf_av@users.sourceforge.net>
|
||||
- Chat history dialog
|
||||
|
||||
Camille Begue <prsieux@gmail.com>
|
||||
- Chat History functionality when disconnected from MSN
|
||||
- Automatic login checkbox on initialview
|
||||
|
||||
David López <grannost@gmail.com>
|
||||
- Nudge button in chat
|
||||
|
||||
Pieterjan Camerlynck <pieterjan.camerlynck@gmail.com>
|
||||
- Roaming Service support
|
||||
|
||||
Anastasios Bourazanis <a.brzns@gmail.com>
|
||||
- Emoticon preview in settings page
|
||||
- Clickable contact properties dialog text
|
||||
|
||||
Marco Mentasti <marcomentasti@gmail.com>
|
||||
- Internationalization fixes
|
||||
- Drag'n'drop of images into display pictures
|
||||
|
||||
Francesco Nwokeka
|
||||
- Now Listening toggle button above the contact list.
|
||||
|
||||
Timo Tambet <ttambet@gmail.com>
|
||||
- Contact Changed and Group Changed notifications.
|
||||
- Contact added user dialog tabbing
|
||||
|
||||
KMerlin <http://kmerlin.olsd.de/>
|
||||
- Inspiration and assorted code
|
||||
|
||||
@@ -218,4 +270,8 @@
|
||||
- Close-to-tray icon screenshot code
|
||||
|
||||
Amarok
|
||||
- Custom crash handler implementation
|
||||
- Custom crash handler implementation
|
||||
- System tray icon overlay implementation
|
||||
|
||||
Quassel
|
||||
- KNotify not giving focus bug fix and KWin focus stealing prevention workaround
|
||||
|
||||
+86
-161
@@ -1,176 +1,101 @@
|
||||
PROJECT( kmess )
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(kmess VERSION 6.0.0 LANGUAGES C CXX)
|
||||
|
||||
#### System checks ####
|
||||
find_package(ECM REQUIRED NO_MODULE)
|
||||
set(CMAKE_MODULE_PATH
|
||||
${CMAKE_MODULE_PATH}
|
||||
${ECM_MODULE_PATH}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
# To add checks, see in one or more of these paths
|
||||
# /usr/share/cmake/Modules
|
||||
# /usr/share/kde4/apps/cmake/modules
|
||||
# /usr/share/apps/cmake
|
||||
set(BUILD_WITH_QT6 ON CACHE BOOL "Build against Qt 6" FORCE)
|
||||
set(QT_DEFAULT_MAJOR_VERSION 6)
|
||||
|
||||
# Check for cmake, same as minimum for KDE 4.0.0
|
||||
CMAKE_MINIMUM_REQUIRED( VERSION 2.4.5 )
|
||||
include(KDEInstallDirs)
|
||||
include(KDECMakeSettings)
|
||||
include(KDECompilerSettings NO_POLICY_SCOPE)
|
||||
|
||||
# Check for KDE 4
|
||||
FIND_PACKAGE( KDE4 REQUIRED )
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Core5Compat DBus Gui Multimedia Network Test WebEngineWidgets Widgets Xml)
|
||||
find_package(KF6 REQUIRED COMPONENTS
|
||||
Codecs
|
||||
Completion
|
||||
Config
|
||||
ConfigWidgets
|
||||
CoreAddons
|
||||
Crash
|
||||
DBusAddons
|
||||
IdleTime
|
||||
I18n
|
||||
IconThemes
|
||||
KIO
|
||||
Notifications
|
||||
NotifyConfig
|
||||
Parts
|
||||
Service
|
||||
Solid
|
||||
StatusNotifierItem
|
||||
TextWidgets
|
||||
WidgetsAddons
|
||||
WindowSystem
|
||||
Wallet
|
||||
XmlGui
|
||||
)
|
||||
|
||||
# Check for libxml2
|
||||
FIND_PACKAGE( LibXml2 )
|
||||
MACRO_LOG_FEATURE( LIBXML2_FOUND "LibXML2" "Libraries used to develop XML-aware applications" "http://www.xmlsoft.org/" TRUE ""
|
||||
" Required for the chat styles system.
|
||||
* You will need this package and the matching development package: for example, 'kmess' and 'kmess-dev' or 'kmess-devel'.
|
||||
* Usually the package names for this library are on Ubuntu: 'libxml2' and 'libxml2-dev', and on openSUSE: 'libxml2' and 'libxml2-devel'.
|
||||
* Please refer to the KMess board for more info, and specially to this guide: http://www.kmess.org/board/viewtopic.php?t=3100" )
|
||||
find_package(LibXml2 REQUIRED)
|
||||
find_package(LibXslt REQUIRED)
|
||||
find_package(GCrypt REQUIRED)
|
||||
|
||||
# Check for libxslt
|
||||
FIND_PACKAGE( LibXslt )
|
||||
MACRO_LOG_FEATURE( LIBXSLT_FOUND "LibXSLT" "A library to transform XML into other formats" "http://www.xmlsoft.org/XSLT" TRUE ""
|
||||
" Required for the chat styles system.
|
||||
* You will need this package and the matching development package: for example, 'kmess' and 'kmess-dev' or 'kmess-devel'.
|
||||
* Usually the package names for this library are on Ubuntu: 'libxslt' and 'libxslt-dev', and on openSUSE: 'libxslt' and 'libxslt-devel'.
|
||||
* Please refer to the KMess board for more info, and specially to this guide: http://www.kmess.org/board/viewtopic.php?t=3100" )
|
||||
set(ISFQT_IS_BUNDLED TRUE)
|
||||
set(USE_BUNDLED_LIBRARIES ISFQT)
|
||||
add_subdirectory(contrib/isf-qt)
|
||||
|
||||
# Check for qca2
|
||||
FIND_PACKAGE( QCA2 )
|
||||
MACRO_LOG_FEATURE( QCA2_FOUND "QCA 2" "A cryptographic library written in Qt to access various encryption methods" "http://delta.affinix.com/qca/" TRUE ""
|
||||
" Required for the Windows Live Messenger login process.
|
||||
* You will need this package and the matching development package: for example, 'kmess' and 'kmess-dev' or 'kmess-devel'.
|
||||
* Usually the package names for this library are on Ubuntu: 'libqca2' and 'libqca2-dev', and on openSUSE: 'libqca2' and 'libqca2-devel'.
|
||||
* Please refer to the KMess board for more info, and specially to this guide: http://www.kmess.org/board/viewtopic.php?t=3100" )
|
||||
option(KMESS_DISABLE_LIKEBACK "Disable the old LikeBack feedback UI" ON)
|
||||
if(KMESS_DISABLE_LIKEBACK)
|
||||
set(KMESS_DISABLE_LIKEBACK 1)
|
||||
else()
|
||||
set(KMESS_DISABLE_LIKEBACK 0)
|
||||
endif()
|
||||
|
||||
# As far as I know, it is impossible to find out if the required QCA plugin for OpenSSL is installed:
|
||||
# so we need to ask QCA itself it it's got such a plugin. Pretty darned unportable stuff.
|
||||
IF( QCA2_FOUND )
|
||||
IF( UNIX )
|
||||
EXECUTE_PROCESS( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check-qca2-ossl.sh OUTPUT_VARIABLE HAS_QCA2_OSSL_PLUGIN )
|
||||
IF( HAS_QCA2_OSSL_PLUGIN EQUAL 1 )
|
||||
SET( QCA2_OSSL_PLUGIN_FOUND TRUE )
|
||||
ELSE( HAS_QCA2_OSSL_PLUGIN EQUAL 1 )
|
||||
SET( QCA2_OSSL_PLUGIN_FOUND FALSE )
|
||||
ENDIF( HAS_QCA2_OSSL_PLUGIN EQUAL 1 )
|
||||
MACRO_LOG_FEATURE( QCA2_OSSL_PLUGIN_FOUND "QCA 2 - OpenSSL Plugin" "OpenSSL interaction library for QCA2" "http://delta.affinix.com/qca/" TRUE ""
|
||||
" Required for the Windows Live Messenger login process.
|
||||
* This package should have been installed along QCA2: if you have QCA2 installed
|
||||
but still see this message, your distribution probably packages it separately.
|
||||
Ubuntu does this; the package name is 'libqca2-plugin-ossl'.
|
||||
* Please refer to the KMess forum for more info, and specially to this guide: http://www.kmess.org/board/viewtopic.php?t=3100" )
|
||||
ELSE( UNIX )
|
||||
# TODO: Find another *portable* way to detect the OSSL plugin.
|
||||
MESSAGE( STATUS "NOTE: The qca2 OpenSSL can't be detected on this platform yet. Make sure you have it installed." )
|
||||
ENDIF( UNIX )
|
||||
ENDIF( QCA2_FOUND )
|
||||
set(KMESS_VERSION "6.0_alpha6")
|
||||
set(KMESS_ENABLE_DEBUG_OUTPUT 0)
|
||||
set(KMESS_BUILT_DEBUGFULL FALSE)
|
||||
set(HAVE_LIBISFQT 1)
|
||||
set(HAVE_NEW_TRAY 1)
|
||||
|
||||
# Check for XScreenSaver extension
|
||||
MACRO_BOOL_TO_01( X11_Xscreensaver_FOUND HAVE_XSCREENSAVER )
|
||||
MACRO_OPTIONAL_FIND_PACKAGE(X11)
|
||||
IF( X11_FOUND )
|
||||
MACRO_LOG_FEATURE( X11_Xscreensaver_FOUND "XScreenSaver" "X11 extension used to check idle state" "" FALSE ""
|
||||
" Required for the auto-away feature.
|
||||
* Usually the package names for this library are on Ubuntu, 'libxss-dev' and on openSUSE, 'xorg-x11-libs'.
|
||||
* Please refer to the KMess board for more info, and specially to this guide: http://www.kmess.org/board/viewtopic.php?t=3100" )
|
||||
ELSE( X11_FOUND )
|
||||
MESSAGE( STATUS "NOTE: The auto-away feature is not supported on this platform yet." )
|
||||
ENDIF( X11_FOUND )
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Check for libkonq
|
||||
# QUIET is to fix compiling on systems like Debian Lenny, which don't have FindLibKonq.cmake
|
||||
# (for an optional feature, we don't want that to be fatal)
|
||||
MACRO_OPTIONAL_FIND_PACKAGE( LibKonq QUIET )
|
||||
MACRO_LOG_FEATURE( LIBKONQ_FOUND "LibKonq" "Integration with Konqueror context menu" "http://www.kde.org/" FALSE ""
|
||||
" Optional context menu integration with Konqueror.
|
||||
* You will need this package and the matching development package: for example, 'kmess' and 'kmess-dev' or 'kmess-devel'.
|
||||
* Usually the package names for this library are on Ubuntu: 'libkonq5' and 'libkonq5-dev', and on openSUSE: 'libkonq5' and 'libkonq-devel'.
|
||||
* Please refer to the KMess board for more info, and specially to this guide: http://www.kmess.org/board/viewtopic.php?t=3100" )
|
||||
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050F00 -DQT_NO_URL_CAST_FROM_STRING)
|
||||
|
||||
# Compatibility variables kept for old subdirectory install rules while the
|
||||
# source tree is being ported away from KDE4-era CMake.
|
||||
set(BIN_INSTALL_DIR ${KDE_INSTALL_BINDIR})
|
||||
set(CONFIG_INSTALL_DIR ${KDE_INSTALL_CONFDIR})
|
||||
set(DATA_INSTALL_DIR ${KDE_INSTALL_DATADIR})
|
||||
set(HTML_INSTALL_DIR ${KDE_INSTALL_DOCBUNDLEDIR})
|
||||
set(ICON_INSTALL_DIR ${KDE_INSTALL_ICONDIR})
|
||||
set(LOCALE_INSTALL_DIR ${KDE_INSTALL_LOCALEDIR})
|
||||
set(PLUGIN_INSTALL_DIR ${KDE_INSTALL_PLUGINDIR})
|
||||
set(SERVICES_INSTALL_DIR ${KDE_INSTALL_KSERVICESDIR})
|
||||
set(SHARE_INSTALL_PREFIX ${KDE_INSTALL_DATADIR})
|
||||
set(SOUND_INSTALL_DIR ${KDE_INSTALL_SOUNDDIR})
|
||||
set(XDG_APPS_INSTALL_DIR ${KDE_INSTALL_APPDIR})
|
||||
|
||||
# Display the dependency collection results
|
||||
MACRO_DISPLAY_FEATURE_LOG()
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/config-kmess.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/config-kmess.h)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${LIBXML2_INCLUDE_DIR}
|
||||
${LIBXSLT_INCLUDE_DIR}
|
||||
${GCRYPT_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
|
||||
#### Configure build ####
|
||||
|
||||
INCLUDE( MacroLibrary )
|
||||
INCLUDE( KDE4Defaults )
|
||||
|
||||
ADD_DEFINITIONS( ${QT_DEFINITIONS} ${KDE4_DEFINITIONS} )
|
||||
|
||||
INCLUDE_DIRECTORIES( ${KDE4_INCLUDES} ${KDE4_INCLUDE_DIR} ${QT_INCLUDES} ${LIBXML2_INCLUDE_DIR} )
|
||||
|
||||
|
||||
#### Define the app version number ####
|
||||
|
||||
# Switch between the following two commands to force using a predefined version number
|
||||
# EXECUTE_PROCESS( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cmake/get-svn-version.sh OUTPUT_VARIABLE KMESS_VERSION )
|
||||
SET( KMESS_VERSION "2.0beta1" )
|
||||
|
||||
|
||||
#### Define compiler flags ####
|
||||
|
||||
# Tune the debug build with even more flags
|
||||
# also see FindKDE4Internal.cmake for more defaults
|
||||
IF( CMAKE_COMPILER_IS_GNUCXX )
|
||||
# skipped: -ansi -pendantic -Wfatal-errors -Wold-style-cast -Wconversion
|
||||
SET( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -Wall" )
|
||||
SET( CMAKE_CXX_FLAGS_DEBUGFULL "-O0 -g3 -fno-inline -Wall -Woverloaded-virtual -Wsign-compare -Wundef -fvisibility=default" )
|
||||
ENDIF( CMAKE_COMPILER_IS_GNUCXX )
|
||||
|
||||
|
||||
# Define the default build type
|
||||
# Possible values:
|
||||
# - none
|
||||
# - release
|
||||
# - debug
|
||||
# - debugfull (even fewer optimisations)
|
||||
# - relwithdebinfo (release with debug info)
|
||||
# - minsizerel (minsize release)
|
||||
# Uncomment the next line to always force building in full debug mode
|
||||
SET( CMAKE_BUILD_TYPE debugfull )
|
||||
|
||||
# Enable the KMess debug output when compiling a debug build
|
||||
IF( CMAKE_BUILD_TYPE STREQUAL debugfull OR KMESS_DEBUG_OUTPUT EQUAL 1 )
|
||||
SET( KMESS_ENABLE_DEBUG_OUTPUT "1" )
|
||||
ELSE( CMAKE_BUILD_TYPE STREQUAL debugfull OR KMESS_DEBUG_OUTPUT EQUAL 1 )
|
||||
SET( KMESS_ENABLE_DEBUG_OUTPUT "0" )
|
||||
ENDIF( CMAKE_BUILD_TYPE STREQUAL debugfull OR KMESS_DEBUG_OUTPUT EQUAL 1 )
|
||||
|
||||
|
||||
# Change symbol visibility, to obtain clearer KDE backtraces
|
||||
SET( __KDE_HAVE_GCC_VISIBILITY 0 )
|
||||
|
||||
# Set a default value for relocatable binary support
|
||||
IF( KMESS_ENABLE_BINRELOC EQUAL 1 )
|
||||
SET( KMESS_ENABLE_BINRELOC "1" )
|
||||
ELSE( KMESS_ENABLE_BINRELOC EQUAL 1 )
|
||||
SET( KMESS_ENABLE_BINRELOC "0" )
|
||||
ENDIF( KMESS_ENABLE_BINRELOC EQUAL 1 )
|
||||
|
||||
|
||||
# Enable the experimental webcam connection if needed
|
||||
IF( NOT DEFINED KMESS_ENABLE_WEBCAM )
|
||||
SET( KMESS_ENABLE_WEBCAM "0" )
|
||||
ENDIF( NOT DEFINED KMESS_ENABLE_WEBCAM )
|
||||
|
||||
|
||||
# Set a default installation path
|
||||
IF( NOT CMAKE_INSTALL_PREFIX )
|
||||
SET( CMAKE_INSTALL_PREFIX "/usr" )
|
||||
ENDIF( NOT CMAKE_INSTALL_PREFIX )
|
||||
|
||||
|
||||
#### Other settings ####
|
||||
|
||||
# Generate config-kmess.h
|
||||
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/config-kmess.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-kmess.h )
|
||||
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
|
||||
# continue in directories
|
||||
ADD_SUBDIRECTORY( src )
|
||||
ADD_SUBDIRECTORY( po )
|
||||
ADD_SUBDIRECTORY( data )
|
||||
ADD_SUBDIRECTORY( doc )
|
||||
|
||||
# Also add the tests if compiling in development mode
|
||||
IF( CMAKE_BUILD_TYPE STREQUAL debugfull AND KMESS_DEBUG_OUTPUT EQUAL 1 )
|
||||
ADD_SUBDIRECTORY( tests )
|
||||
ENDIF( CMAKE_BUILD_TYPE STREQUAL debugfull AND KMESS_DEBUG_OUTPUT EQUAL 1 )
|
||||
add_subdirectory(po)
|
||||
add_subdirectory(data)
|
||||
add_subdirectory(src)
|
||||
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -5,7 +5,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = KMess
|
||||
PROJECT_NUMBER = 2.0-beta1
|
||||
PROJECT_NUMBER = 2.0.6.2
|
||||
OUTPUT_DIRECTORY = ./apidocs
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
KMess Feature list
|
||||
========================
|
||||
- 2.0.3 -
|
||||
Setting your friendly name and personal message by clicking on their labels in the contact list.
|
||||
A display of the unread email count in the system tray tooltip.
|
||||
Ability to change personal message using DBus.
|
||||
|
||||
- 2.0.2 -
|
||||
Enabled drag-and-drop of images into display picture frames.
|
||||
|
||||
- 2.0.1 -
|
||||
Synchronization of friendly names, personal messages and display pictures with Messenger.
|
||||
Support to send screenshots by dragging and dropping them to a chat.
|
||||
Custom emoticon preview in the settings.
|
||||
Chat window buttons to change message font and color.
|
||||
|
||||
- 2.0 -
|
||||
Support to receive and send Ink (hand-written) messages.
|
||||
Support to send huge messages.
|
||||
@@ -97,4 +111,4 @@ Chats can be configured to show timestamps when contacts send messages.
|
||||
A "compact" chat format can be used to remove extraneous line breaks and reduce the size of the chat.
|
||||
The chat window caption in the taskbar alternates between lower and upper case to inform the user of a received message when the window is not on top.
|
||||
Internationalization support, with translations in: English, Dutch, German, Italian, Portuguese, Spanish, Turkish, Chinese, French, Catalan, Arabic, Korean, Norsk, Thai, Danish, and Estonian
|
||||
There is now a method, though not absolute, to tell if a contact is blocking you. If a chat is started with a contact that appears offline, you will be informed if the contact is offline or online and blocking you.
|
||||
There is now a method, though not absolute, to tell if a contact is blocking you. If a chat is started with a contact that appears offline, you will be informed if the contact is offline or online and blocking you.
|
||||
|
||||
@@ -1,242 +1,61 @@
|
||||
|
||||
Installation Tip:
|
||||
=================
|
||||
Installation Hints:
|
||||
===================
|
||||
|
||||
NOTE: This package is used to build KMess from source code.
|
||||
If you're looking for a simple way to install KMess,
|
||||
take a look at http://www.kmess.org/download/.
|
||||
take a look at http://www.kmess.org/download/.
|
||||
|
||||
NOTE: These instructions are meant for UNIX-like systems. If they don't
|
||||
work for you, please tell us at http://www.kmess.org/board/.
|
||||
For building on Windows, see this Wiki page:
|
||||
http://trac.kmess.org/wiki/Compiling%20KMess%20for%20Windows
|
||||
|
||||
To install KMess from this source package you need to create a separate
|
||||
build directory first. It must be created outside the source folder.
|
||||
build directory first. It must be created outside the source folder. After
|
||||
that, you must configure your build using cmake.
|
||||
|
||||
mkdir kmess2-build
|
||||
|
||||
Then configure your build using cmake,
|
||||
and point to the source folder:
|
||||
|
||||
cd kmess2-build
|
||||
cmake ../kmess2
|
||||
|
||||
If the cmake scripts did not display any errors, you can run:
|
||||
To ease the building process, KMess comes with a 'configure' script that
|
||||
creates the directory and runs CMake for you. To build this package, simply
|
||||
run:
|
||||
|
||||
cd kmess
|
||||
./configure
|
||||
make
|
||||
|
||||
and then, as root, type
|
||||
To install on most systems, you can run:
|
||||
|
||||
make install
|
||||
sudo make install
|
||||
|
||||
If your system doesn't come with sudo, you can use:
|
||||
|
||||
su -c 'make install'
|
||||
|
||||
That's it!
|
||||
Of course, building without the configure script is also possible:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Useful parameters:
|
||||
==================
|
||||
|
||||
Useful cmake parameters:
|
||||
========================
|
||||
The configure script automatically makes the right build for you. For a release (non-alpha or beta)
|
||||
package, the configure script builds KMess in release mode; for an alpha or beta package, KMess will
|
||||
run in debug mode. When something goes wrong, debug mode will help us find the cause, and fix it. :-)
|
||||
|
||||
Try the following switches the change the build.
|
||||
If you want to modify your build, you can try the following options to the configure script:
|
||||
|
||||
-D CMAKE_INSTALL_PREFIX=/usr
|
||||
--prefix=/usr
|
||||
|
||||
Install in the /usr directory instead.
|
||||
|
||||
-D CMAKE_BUILD_TYPE=[none,debug,debugfull,release,relwithdebuginfo,minsizerel]
|
||||
--build-type=(one of debug,debugfull,release,relwithdebuginfo,minsizerel)
|
||||
|
||||
Set the preferred build type, which sets recommended compiler flags automatically.
|
||||
Tell CMake to build this specific build type.
|
||||
|
||||
Also consider running "ccmake" for an curses-interface to cmake.
|
||||
That only works after the build directory is configured with cmake before.
|
||||
|
||||
|
||||
|
||||
|
||||
* * * * * * The parts below do not apply to KMess 2 yet! * * * * * *
|
||||
|
||||
|
||||
|
||||
Important:
|
||||
==========
|
||||
|
||||
The configure script tries to detect your KDE3 directory automatically.
|
||||
If configure fails to detect KDE, you should tell KMess which directory
|
||||
to use. Basically, we're talking about the parent directory
|
||||
of /share/apps/k~bunch of kapps~
|
||||
|
||||
Not doing this will result in emoticons being displayed
|
||||
as text, and icons being displayed incorrectly as well.
|
||||
|
||||
If configure tells you you're missing something that you think you
|
||||
have installed on your system, you probably need to install the
|
||||
"devel" version of that thing. i.e. If it tells you you're missing
|
||||
libfam, install the libfam-devel rpm.
|
||||
|
||||
More information can be found in the README file.
|
||||
The following paragraphs below provide generic installation instructions:
|
||||
|
||||
|
||||
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, a file
|
||||
`config.cache' that saves the results of its tests to speed up
|
||||
reconfiguring, and a file `config.log' containing compiler output
|
||||
(useful mainly for debugging `configure').
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If at some point `config.cache'
|
||||
contains results you don't want to keep, you may remove or edit it.
|
||||
|
||||
The file `configure.in' is used to create `configure' by a program
|
||||
called `autoconf'. You only need `configure.in' if you want to change
|
||||
it or regenerate `configure' using a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes a while. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
4. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. You can give `configure'
|
||||
initial values for variables by setting them in the environment. Using
|
||||
a Bourne-compatible shell, you can do that on the command line like
|
||||
this:
|
||||
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
|
||||
|
||||
Or on systems that have the `env' program, you can do it like this:
|
||||
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not supports the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a time
|
||||
in the source code directory. After you have installed the package for
|
||||
one architecture, use `make distclean' before reconfiguring for another
|
||||
architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' will install the package's files in
|
||||
`/usr/local/bin', `/usr/local/man', etc. You can specify an
|
||||
installation prefix other than `/usr/local' by giving `configure' the
|
||||
option `--prefix=PATH'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
give `configure' the option `--exec-prefix=PATH', the package will use
|
||||
PATH as the prefix for installing programs and libraries.
|
||||
Documentation and other data files will still use the regular prefix.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' can not figure out
|
||||
automatically, but needs to determine by the type of host the package
|
||||
will run on. Usually `configure' can figure that out, but if it prints
|
||||
a message saying it can not guess the host type, give it the
|
||||
`--host=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name with three fields:
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the host type.
|
||||
|
||||
If you are building compiler tools for cross-compiling, you can also
|
||||
use the `--target=TYPE' option to select the type of system they will
|
||||
produce code for and the `--build=TYPE' option to select the type of
|
||||
system on which you are compiling the package.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Operation Controls
|
||||
==================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Use and save the results of the tests in FILE instead of
|
||||
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
|
||||
debugging `configure'.
|
||||
|
||||
`--help'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made.
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--version'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options.
|
||||
|
||||
ccmake only works after the build directory has been configured with cmake before.
|
||||
|
||||
@@ -1,7 +1,259 @@
|
||||
|
||||
Changes between KMess SVN and 2.0-beta1:
|
||||
Changes between KMess Git and 2.0.6.2:
|
||||
--------------------------------------
|
||||
- None yet.
|
||||
|
||||
- None yet
|
||||
|
||||
Changes between KMess 2.0.6.2 and 2.0.6.1:
|
||||
------------------------------------------
|
||||
|
||||
- Follow redirects, fixing connection issue. Thanks to Lekensteyn.
|
||||
- Moved file transfer settings into own tab.
|
||||
- updated Japanese translation by Daniel E. Moctezuma.
|
||||
|
||||
Changes between KMess 2.0.6.1 and 2.0.6:
|
||||
----------------------------------------
|
||||
|
||||
- fix bug where KMess couldn't login due to missing language preference information.
|
||||
|
||||
Changes between KMess 2.0.6 and 2.0.5:
|
||||
--------------------------------------
|
||||
|
||||
- fix bug where KMess couldn't login due to badly formed URL commands.
|
||||
- fixed crash when taking any action in the Contact Added User dialog.
|
||||
- fixed a possible crash when using the Contact Added User dialog.
|
||||
- Added a fading effect when switching between the initial view and the contact list.
|
||||
- fixed retrieval of display pictures from online MSN storage.
|
||||
- fixed a problem where roaming service profile data was invalid.
|
||||
- fixed "Visit Link" context-menu icon in chat. Thanks to IRC's agostino.
|
||||
- improved Contact Added User dialog with tabs. Thanks ti Timo Tambet.
|
||||
- updated Japanese translation by Daniel E. Moctezuma.
|
||||
- updated Spanish translation by Daniel E. Moctezuma.
|
||||
- updated German translation.
|
||||
|
||||
|
||||
Changes between KMess 2.0.5 and 2.0.4:
|
||||
--------------------------------------
|
||||
|
||||
- added Chat History integration with the Chat Window, with a menu item, toolbar
|
||||
action, and a context menu item.
|
||||
- added KDE notifications for contact and group changes (Patch by Timo Tambet).
|
||||
- fixed a crash due to changes by Microsoft in how Offline Messages are sent.
|
||||
- fixed a possible crash during login.
|
||||
- fixed a crash when closing a chat window.
|
||||
- fixed logging in with long passwords (17+ characters). MSN cuts them to 16, hehehe.
|
||||
- removed all HTML from KDE notifications to fix weird text cutting problems.
|
||||
- updated French translation by Anthony Rey.
|
||||
- updated Estonian translation by Timo Tambet.
|
||||
- updated Hungarian translation by József Makay.
|
||||
- updated Traditional Chinese translation by Yen-chou Chen.
|
||||
|
||||
Changes between KMess 2.0.4 and 2.0.3:
|
||||
--------------------------------------
|
||||
|
||||
- added a tip text on the contact list's search bar (Patch by Timo Tambet).
|
||||
- added support for a new ISF-Qt feature, PNG fortification, allowing to drag
|
||||
and drop handwriting drawings into the message editor to be edited.
|
||||
- added support for notifications on the Mac.
|
||||
- added a toggle button for Now Listening to the contact list (Patch by Francesco Nwokeka).
|
||||
- added Galician translation by Indalecio Freiría Santos.
|
||||
- fixed invalid characters in contact frame tooltips.
|
||||
- fixed "Contact Added User" dialog pop-ups, appearing for already added contacts.
|
||||
- fixed the application icon resolution.
|
||||
- fixed log in failure, due to a mismanaged remote display picture reception.
|
||||
- fixed URL when retrieving display pictures from MSN.
|
||||
- fixed saving account properties.
|
||||
- fixed removal and blocking of contacts.
|
||||
- fixed saving the automatic login option.
|
||||
- fixed status changing bugs when disconnecting or waking up from sleep.
|
||||
- improved compatibility with KDE 4.5.
|
||||
- improved the Chat History dialog, now using a calendar to list dates with available chat logs.
|
||||
- improved contact list sorting, now done by status, then alphabetically (Patch by dah_ at the boards).
|
||||
- improved management of unknown contacts.
|
||||
- improved widget sizes and behavior on the contact list.
|
||||
- updated the system tray widget to the new KDE 4.3+ one.
|
||||
- updated Japanese translation by Daniel E. Moctezuma.
|
||||
- updated Dutch translation by Heimen Stoffels.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Catalan translation by Adrià Arrufat.
|
||||
- updated Traditional Chinese translation by the Tryneeds-Chinese Translation Platform.
|
||||
- updated French translation by Grégory Bellier.
|
||||
- updated Brazilian Portuguese translation by Morris Arozi Moraes.
|
||||
- updated Traditional Chinese translation by Yen-chou Chen.
|
||||
|
||||
Changes between KMess 2.0.3 and 2.0.2:
|
||||
--------------------------------------
|
||||
- added ability to drag-and-drop images into display picture frames.
|
||||
- added setting friendly name and personal message by clicking on their labels in the contact list.
|
||||
- added Russian translation by Dmitriy Simbiriatin.
|
||||
- added Portuguese translation by Bruno Almeida.
|
||||
- added showing unread email count in the system tray tooltip.
|
||||
- added setting personal message using DBus.
|
||||
- fixed crash when logging in with the wrong password.
|
||||
- fixed bug causing the "Contact Added User" dialog to appear after deleting contacts.
|
||||
- fixed bug causing the chat window size to not be saved under KDE 4.4.
|
||||
- improved performance when drawing complex ink messages.
|
||||
- improved proxy support.
|
||||
- updated French translation by Grégory Bellier.
|
||||
- updated Catalan translation by Adrià Arrufat.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Greek translation by Dimitrios Glentadakis.
|
||||
- updated Brazilian Portuguese translation by Morris Arozi Moraes.
|
||||
- updated Traditional Chinese translation by Yen-chou Chen.
|
||||
- updated Dutch translation by Sjors Gielen.
|
||||
- updated Dutch translation by Heimen Stoffels.
|
||||
- updated Hungarian translation by Pauli Henrik.
|
||||
- updated Spanish translation by Mauricio Muñoz Lucero.
|
||||
- updated Turkish translation by Uğur Çetin.
|
||||
- updated Estonian translation by Rivo Laks.
|
||||
- updated Traditional Chinese translation by Yen-chou Chen.
|
||||
- updated Japanese translation by Daniel E. Moctezuma.
|
||||
- updated Slovak translation by Rastislav Krupanský.
|
||||
|
||||
Changes between KMess 2.0.2 and 2.0.1:
|
||||
--------------------------------------
|
||||
- fixed a bug that was introduced just before the release of KMess 2.0.1,
|
||||
making it impossible to login twice in the same KMess session.
|
||||
- updated Japanese translation by Daniel E. Moctezuma.
|
||||
- updated Brazilian Portuguese translation by Maurício Arozi Moraes.
|
||||
|
||||
Changes between KMess 2.0.1 and 2.0:
|
||||
--------------------------------------
|
||||
- added a "Log in automatically" checkbox to the initial view (patch by Camille Begue).
|
||||
- added ability to open the chat history dialog when not connected (patch by Camille Begue).
|
||||
- added Test Server support.
|
||||
- added shortcut checking of added/edited emoticons for invalid characters.
|
||||
- added usage of smooth pixmap scaling for display pictures in the contact list.
|
||||
- added caching of scaled display pictures when painting the contact list.
|
||||
Mitigates performance hit caused by using smooth pixmap scaling.
|
||||
- added full Handwriting support, thanks to the ISF-Qt external library.
|
||||
- added Roaming content support: display picture, friendly name and personal
|
||||
message are now synchronized with those used in Messenger.
|
||||
- added chat window buttons to quickly change message font and color.
|
||||
- added ability to drag-and-drop an image from KSnapshot (or any drag'n'drop-enabled
|
||||
image application) to a chat, to send it.
|
||||
- added preview of emoticons in emoticon settings page.
|
||||
- fixed improper scaling of the display picture in the chat window and contact list.
|
||||
- fixed maximum emoticon shortcut length.
|
||||
- fixed "close all tabs" dialog: Don't show it when the KDE session is closing.
|
||||
- fixed a crash in NetworkWindow when receiving P2P messages shorter than
|
||||
the usual.
|
||||
- fixed an overzealousness of the IRC-like commands parser.
|
||||
- fixed redrawing of emoticons in the contact list and friendly name label,
|
||||
when emoticon theme is changed.
|
||||
- fixed display picture cache clearing, when pictures is changed.
|
||||
- fixed minimum size of the message editor to one line of text.
|
||||
- fixed appearance of the transfers window: it now appears in the background
|
||||
instead of minimized.
|
||||
- fixed occasional crash when canceling a drag and drop operation on the chat window.
|
||||
- fixed contact notifications, which were not shown for contacts going online.
|
||||
- fixed contact notification spam on login.
|
||||
- fixed a crash when connecting with the fallback HTTP connection (used behind
|
||||
firewalls, proxies et cetera).
|
||||
- fixed KDE 4.3.2 bug causing a crash when closing account settings.
|
||||
- fixed losing the friendly name on login.
|
||||
- removed small delay when auto-connecting on startup.
|
||||
- removed in-place editing of emoticons, now it's only done by AddEmoticonDialog.
|
||||
- removed giflib dependency, as it is now required by ISF-Qt.
|
||||
- updated Brazilian Portuguese translation by Maurício Arozi Moraes.
|
||||
- updated Catalan translation by Adrià Arrufat.
|
||||
- updated Dutch translation by Sjors Gielen.
|
||||
- updated Estonian translation by Lauri Võsandi, Mihkel Tõnnov and Rivo Laks.
|
||||
- updated Finnish translation by Marko Mäkilä.
|
||||
- updated French translation by Émeric Dupont.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Greek translation by George Kiagiadakis.
|
||||
- updated Hungarian translation by Pauli Henrik.
|
||||
- updated Japanese translation by Daniel E. Moctezuma.
|
||||
- updated Simplified Chinese translation by Cheng Yang.
|
||||
- updated Spanish translation by Guillermo Arana.
|
||||
- updated Turkish translation by Uğur Çetin.
|
||||
|
||||
Changes between KMess 2.0 and 2.0-rc1:
|
||||
--------------------------------------
|
||||
- added back meaningful error messages for unsupported applications.
|
||||
- fixed crashing when transferring display pictures.
|
||||
- fixed a small bug in group sorting.
|
||||
- improved further the RTL languages support.
|
||||
- updated Arabic translation by Ma'moun Diraneyya.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Simplified Chinese translation by Cheng Yang.
|
||||
|
||||
Changes between KMess 2.0-rc1 and 2.0-beta2:
|
||||
--------------------------------------
|
||||
- added Japanese translation by Daniel E. Moctezuma.
|
||||
- added display of emoticons in presence messages.
|
||||
- added status restoring on reconnection.
|
||||
- added fading effect for long text labels and the contact list.
|
||||
- changed the image used for unknown display pictures.
|
||||
- changed the application windows icons.
|
||||
- changed the system tray icon with one showing both the bird and the current
|
||||
status.
|
||||
- fixed a crash triggered by clients aborting while we're sending data.
|
||||
- fixed appearance when using right-to-Left languages, like Arabic.
|
||||
- fixed bug preventing temporary accounts to be correctly cleaned up.
|
||||
- fixed file selection errors in chat logging.
|
||||
- fixed newly added contacts appearing as not having added the user in their
|
||||
list.
|
||||
- improved appearance of the contact list when antialiasing is enabled.
|
||||
- removed all experimental features which could not be finished in time.
|
||||
- updated Arabic translation by Ma'moun Diraneyya.
|
||||
- updated Brazilian Portuguese translation by Maurício Arozi Moraes and Sergio
|
||||
Rafael Lemke.
|
||||
- updated Catalan translation by Adrià Arrufat.
|
||||
- updated Finnish translation by Antony Hussi.
|
||||
- updated Dutch translation by Sander Pientka and Sjors Gielen.
|
||||
- updated French translation by Scias.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Greek translation by Dimitrios Glentadakis.
|
||||
- updated Hungarian translation by Pauli Henrik.
|
||||
- updated Italian translation by Valerio Pilo.
|
||||
- updated Slovak translation by Rastislav Krupanský.
|
||||
- updated Spanish translation by Mauricio Muñoz Lucero.
|
||||
- updated Simplified Chinese translation by Cheng Yang.
|
||||
- updated Traditional Chinese translation by Yen-chou Chen.
|
||||
|
||||
Changes between KMess 2.0-beta2 and 2.0-beta1:
|
||||
--------------------------------------
|
||||
- completed drag&drop support.
|
||||
- fixed quick retype.
|
||||
- fixed winks playing and naming support.
|
||||
- fixed disconnections after many hours of use.
|
||||
- fixed losing KDE Wallet passwords.
|
||||
- fixed typing notifications not appearing in WLM 2009.
|
||||
- fixed long words breaking was breaking copy-pastes.
|
||||
- fixed weird characters which were breaking the chat styles.
|
||||
- fixed resending messages already sent as offline messages.
|
||||
- fixed searching for offline contacts.
|
||||
- fixed time of arrival of offline messages.
|
||||
- fixed new chat windows or tabs opened by contacts sometimes stole focus.
|
||||
- fixed contact list group sorting.
|
||||
- fixed many memory leaks.
|
||||
- fixed overflowing contact list background picture and color.
|
||||
- improved the Chat History dialog and its integration with KMess.
|
||||
- improved drag and drop support in contact list.
|
||||
- improved the color of the less important contact status messages in chat.
|
||||
- improved client detection support.
|
||||
- improved handwriting message sending.
|
||||
- improved the file transfers window's behavior.
|
||||
- improved chat session management.
|
||||
- improved the LikeBack buttons bar.
|
||||
- updated English handbook.
|
||||
- updated Arabic translation by Ma'moun Diraneyya.
|
||||
- updated Brazilian Portuguese translation by Sergio Rafael Lemke.
|
||||
- updated Brazilian Portuguese translation by Maurício Arozi Moraes.
|
||||
- updated Catalan translation by Adrià Arrufat.
|
||||
- updated Dutch translation by Heimen Stoffels.
|
||||
- updated Dutch translation by Sander Pientka.
|
||||
- updated Finnish translation by Antony Hussi.
|
||||
- updated French translation by Barthe Guillaume.
|
||||
- updated French translation by Scias.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Greek translation by Dimitrios Glentadakis.
|
||||
- updated Italian translation by Andrea Decorte.
|
||||
- updated Spanish translation by Manuel Ramírez.
|
||||
- updated Spanish translation by Mauricio Muñoz Lucero.
|
||||
- updated Traditional Chinese translation by Yen-chou Chen.
|
||||
- updated Traditional Chinese translation by the Tryneeds-Chinese Translation Platform.
|
||||
|
||||
Changes between KMess 2.0-beta1 and 2.0-alpha2:
|
||||
--------------------------------------
|
||||
@@ -14,7 +266,6 @@ Changes between KMess 2.0-beta1 and 2.0-alpha2:
|
||||
- added Likeback support to collect user feedback, ported from Basket of KDE 3.
|
||||
- added support to configure the toolbars and menus in the chat window.
|
||||
- added contact list export dialog.
|
||||
- added close button in the contact list search box.
|
||||
- added context menu to the contact list, to open the contact menu with the left mouse too.
|
||||
- added confirmation dialogs when the menu gets hidden with Ctrl+M.
|
||||
- added confirmation dialogs when deleting custom emoticons.
|
||||
@@ -93,7 +344,8 @@ Changes between KMess 2.0-beta1 and 2.0-alpha2:
|
||||
- removed custom notification settings in favour of KNotify settings in the global settings panel.
|
||||
- updated English handbook by Heimen Stoffels.
|
||||
- updated Brazilian Portuguese translation by Leonel Freire.
|
||||
- updated Dutch translation by Elvse and Sander Pientka.
|
||||
- updated Brazilian Portuguese translation by Sergio Rafael Lemke.
|
||||
- updated Dutch translation by Elve and Sander Pientka.
|
||||
- updated French translation by Barthe Guillaume.
|
||||
- updated German translation by Panagiotis Papadopoulos.
|
||||
- updated Greek translation by Panagiotis Papadopoulos, Dimitrios Glentadakis.
|
||||
|
||||
@@ -14,240 +14,11 @@ The licensing terms are the GNU General Public License.
|
||||
|
||||
|
||||
|
||||
Installation Tip:
|
||||
Installing KMess:
|
||||
=================
|
||||
|
||||
NOTE: This package is used to build KMess from source code.
|
||||
If you're looking for a simple way to install KMess,
|
||||
take a look at http://www.kmess.org/download/.
|
||||
|
||||
First configure your build using the configure script:
|
||||
|
||||
./configure
|
||||
|
||||
If everything went well, you can run:
|
||||
|
||||
make
|
||||
|
||||
and then, as root, type
|
||||
|
||||
make install
|
||||
|
||||
That's it!
|
||||
|
||||
|
||||
|
||||
Compiling with cmake:
|
||||
=====================
|
||||
|
||||
* * this is for experienced users only * *
|
||||
|
||||
KMess 2 is KDE 4 software, which uses 'cmake' to build the software.
|
||||
To make it a bit more familiar for you, we have created a 'configure'
|
||||
script which does all the cmake magic behind the scenes.
|
||||
|
||||
It's also possible to use 'cmake' directly.
|
||||
The build commands become a little bit different.
|
||||
|
||||
First, you need to create a separate build directory:
|
||||
|
||||
mkdir build
|
||||
|
||||
Then configure your build using cmake,
|
||||
and point to the source folder:
|
||||
|
||||
cd build
|
||||
cmake ../
|
||||
|
||||
You can provide extra parameters with the '-D' option, see below.
|
||||
If the cmake scripts did not display any errors, you can run:
|
||||
|
||||
make
|
||||
|
||||
and then, as root, type
|
||||
|
||||
make install
|
||||
|
||||
|
||||
|
||||
Useful cmake parameters:
|
||||
========================
|
||||
|
||||
Try the following switches to change the build.
|
||||
|
||||
-D CMAKE_INSTALL_PREFIX=/usr
|
||||
|
||||
Install in the /usr directory instead (default is /usr/local).
|
||||
|
||||
-D CMAKE_BUILD_TYPE=[none,debug,debugfull,release,relwithdebuginfo,minsizerel]
|
||||
|
||||
Set the preferred build type, which sets recommended compiler flags automatically.
|
||||
|
||||
-D KMESS_DEBUG_OUTPUT=[1,0]
|
||||
|
||||
Activates/deactivates KMess debugging output to console, even on release builds.
|
||||
This flag is automatically enabled when the build type is "debugfull".
|
||||
|
||||
-D KMESS_ENABLE_BINRELOC=[1,0]
|
||||
|
||||
Enable support for binary relocatable binaries. This allows the binary
|
||||
to find it's resource files whatever directory it is installed in.
|
||||
|
||||
-G "MinGW Makefiles"
|
||||
|
||||
This option is required to build KMess on Windows (still experimental).
|
||||
See http://trac.kmess.org/wiki/Compiling%20KMess%20for%20Windows for details.
|
||||
|
||||
If you find this too difficult, consider using our "./configure" script
|
||||
instead. This is a wrapper these 'cmake' arguments to make it easier,
|
||||
and more familiar, to compile software.
|
||||
|
||||
Also consider running "ccmake" for an curses-interface to cmake.
|
||||
That only works after the build directory is configured with cmake before.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* * * * * * The parts below do not apply to KMess 2 yet! * * * * * *
|
||||
|
||||
|
||||
|
||||
Important:
|
||||
==========
|
||||
|
||||
The configure script tries to detect your KDE3 directory automatically.
|
||||
If configure fails to detect KDE, you should tell KMess which directory
|
||||
to use. Basically, we're talking about the parent directory
|
||||
of /share/apps/k~bunch of kapps~
|
||||
|
||||
Not doing this will result in emoticons being displayed
|
||||
as text, and icons being displayed incorrectly as well.
|
||||
|
||||
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
The following packages are required to install KMess:
|
||||
- make
|
||||
- gcc
|
||||
- gcc-c++ (if available)
|
||||
- qt
|
||||
- kdelibs
|
||||
|
||||
|
||||
If your distribution uses separate "devel" or "dev" packages,
|
||||
make sure you have these packages installed also:
|
||||
- qt-devel (or qt3-devel)
|
||||
- kdelibs-devel (or kdelibs3-devel)
|
||||
- x-devel (or xfree-devel/xorg-x11-devel)
|
||||
- libstdc++-devel
|
||||
|
||||
|
||||
Likely the dependencies of these "devel" packages will
|
||||
be installed automatically. Some worth mentioning are:
|
||||
- fontconfig-devel
|
||||
- freetype2-devel
|
||||
- libfam-devel (or fam-devel)
|
||||
- libjpeg-devel
|
||||
- libpng-devel
|
||||
- zlib-devel
|
||||
- libart_lgpl-devel
|
||||
|
||||
|
||||
If configure tells you you're missing something that you think you
|
||||
have installed on your system, you probably need to install the
|
||||
"devel" package of that thing.
|
||||
|
||||
i.e. If it tells you you're missing libfam, install the libfam-devel rpm.
|
||||
|
||||
|
||||
If you get this error:
|
||||
''configure: error: no acceptable C compiler found in $PATH''
|
||||
you need to install the packages "gcc" and "make".
|
||||
|
||||
|
||||
If you get this error:
|
||||
''Your Installation isn't able to compile simple C++ programs.''
|
||||
you need to install "libstdc++-devel" and some distributions
|
||||
also require a "gcc-c++" package for C++ support.
|
||||
|
||||
|
||||
If you get this error:
|
||||
''error: Can't find X includes. Please check your installation [...]''
|
||||
you need to install the x developer packages. Depending on your distribution,
|
||||
this can be something like "x-devel", "xfree-devel", "xorg-devel".
|
||||
|
||||
|
||||
If you get this eror:
|
||||
''checking for libz... configure: error: not found.''
|
||||
you need to install the "zlib-devel" package.
|
||||
|
||||
|
||||
If you get this error:
|
||||
''configure: WARNING: libjpeg not found. disable JPEG support.''
|
||||
you need to install the "libjpeg-devel" package.
|
||||
|
||||
|
||||
If you get this error:
|
||||
''checking for Qt... configure: error:
|
||||
Qt (>= Qt 3.1 (20021021)) (headers and libraries) not found.
|
||||
Please check your installation!''
|
||||
you need to install the "qt-devel" (or "qt3-devel") package.
|
||||
|
||||
|
||||
If you get this error:
|
||||
''checking for KDE... configure: error:
|
||||
in the prefix, you've chosen, are no KDE headers installed. This will fail.
|
||||
So, check this please and use another prefix!''
|
||||
you need to make sure "kdelibs-devel" (or "kdelibs3-devel") is installed!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Installing from SVN:
|
||||
====================
|
||||
|
||||
If there is any need to install the SVN version, read on.
|
||||
|
||||
The KMess SVN repository contains the latest changes made to KMess.
|
||||
Each new feature or bugfix will be stored into SVN by developers.
|
||||
The code in SVN will eventually become the new KMess release.
|
||||
|
||||
NOTE: The SVN version is the equivalent
|
||||
of an "alpha" or "beta" release; there also is
|
||||
a little possibility that it won't even compile!
|
||||
|
||||
To install KMess from SVN you need to have the packages "autoconf"
|
||||
and "automake" installed.
|
||||
|
||||
|
||||
To download the SVN version, run these commands:
|
||||
|
||||
svn checkout http://kmess.svn.sourceforge.net/svnroot/kmess/trunk/kmess/
|
||||
|
||||
If the download succeeds, run this command:
|
||||
|
||||
make -f Makefile.dist
|
||||
|
||||
This will generate the ./configure script and Makefile templates.
|
||||
Then you can proceed with the normal installation.
|
||||
|
||||
|
||||
Each time the SVN version is updated, you can run
|
||||
|
||||
svn update
|
||||
make -f Makefile.dist
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
and you'll have the latest KMess version installed.
|
||||
|
||||
For more information about accessing the KMess SVN repository,
|
||||
visit the KMess site at http://kmess.org/svn/ .
|
||||
Please see the INSTALL file for help with compiling and installing
|
||||
KMess from source.
|
||||
|
||||
|
||||
|
||||
@@ -258,22 +29,20 @@ You can help translating KMess!
|
||||
|
||||
This is what you need to do:
|
||||
- Get the latest version of KMess
|
||||
- Install "kbabel" (supplied with "kdesdk")
|
||||
- Install "lokalize" (supplied with "kdesdk")
|
||||
- To create a new translation file, copy "kmess.pot"
|
||||
to a new file named "<your language>.po".
|
||||
- Open the language "po" file, and start translating!
|
||||
Also watch for translations marked as "fuzzy",
|
||||
they often need updating.
|
||||
- The the following commands in the "po" folder:
|
||||
make
|
||||
make install
|
||||
(if this doesn't work, run configure first)
|
||||
- Update the translations.
|
||||
- Test the new translation.
|
||||
(to test other languages, run 'KDE_LANG=<code> kmess')
|
||||
- Contact the KMess developers.
|
||||
- Contact the KMess developers to send your changes!
|
||||
|
||||
A more detailed tutorial can be found online
|
||||
at http://www.kmess.org/translating/
|
||||
at http://trac.kmess.org/wiki/Translating%20KMess
|
||||
|
||||
|
||||
|
||||
Building RPM Packages
|
||||
@@ -288,12 +57,23 @@ To build a RPM package for your distribution, run:
|
||||
Building Slackware Packages
|
||||
===========================
|
||||
|
||||
To build a TGZ package for Slackware, also run:
|
||||
To build a TGZ package for Slackware, run:
|
||||
|
||||
rpmbuild -tb kmess-2.0.tar.gz
|
||||
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
|
||||
We provide along with KMess some Oxygen icons which are usually bundled with Kopete.
|
||||
They are released under LGPL 3.0 (see http://www.oxygen-icons.org/).
|
||||
See COPYING_ICONS for full license text.
|
||||
|
||||
The remainder of KMess is released under the GPL2+ . See COPYING for full license text.
|
||||
|
||||
|
||||
|
||||
More Information
|
||||
================
|
||||
|
||||
|
||||
@@ -1,207 +1,9 @@
|
||||
|
||||
|
||||
Please note that this file has been replaced by an online project management tool,
|
||||
and all information here can be completely outdated.
|
||||
Our new project management site can be found at: http://trac.kmess.org/
|
||||
For bugs, future ideas, et cetera, please check our developer site
|
||||
powered by trac at http://trac.kmess.org/.
|
||||
|
||||
If you're looking for junior jobs, you may also contact us
|
||||
at the KMess help forums: http://kmess.org/board/
|
||||
If you're looking for junior jobs, you may also contact us at the
|
||||
KMess help forums: http://kmess.org/board/
|
||||
|
||||
Thanks
|
||||
- The KMess team
|
||||
|
||||
|
||||
|
||||
To do:
|
||||
======
|
||||
|
||||
Unfinished new features:
|
||||
___ Invite contacts for a new chat session when the current chat is used for file transfers.
|
||||
___ Invite contacts for a new chat session when the current chat has multiple participants (display dialog to choose the contact).
|
||||
___ Lookup all "TODO" items in the code.
|
||||
|
||||
Unfinished protocol features:
|
||||
___ Integrate UPnP support with file transfer invitation code.
|
||||
___ Make sure KMess can send two files at once with the classic file transfer method (test this).
|
||||
___ Support a SLP Decline/error message for a sent P2P/SLP transfer invitation, and continue to transfer over the switchboard.
|
||||
___ Support a second invitation for file transfers, detect when remote client only returns it's internal IP or does not listen.
|
||||
___ Detect network settings, make the other client server for file transfer (sending a second transfer-invite).
|
||||
___ Update the classic FileTransfer application to use the new async-cnnect, DirectConnectionPool and showTransfer*() API.
|
||||
|
||||
Critical bugs:
|
||||
___ Restore the documentation
|
||||
___ Restore the translations
|
||||
|
||||
Junior jobs:
|
||||
___ Improve wink playing, include preview, offer "play" button (also see other showWink() issues). Also fixes issues when chat is started with a wink.
|
||||
___ Implement phone settings (PRP and BPR fields).
|
||||
___ Implement privacy settings (GTC and BLP fields, messages like "can't chat with invited contact" because not in the AL).
|
||||
... And if you're up for a challenge, you could try one of the "Low priority protocol features" below too.
|
||||
... Or try to implement a new MSN feature (see "Future ideas" section).
|
||||
|
||||
Annoying bugs:
|
||||
___ Fix repeated picture transfer invitations when contact declines or aborts the invitation (preferably disable only for a few minutes).
|
||||
___ Automatically clean up the displaypics folder, and msnpicture-alias-##.png images that are older then 1 month.
|
||||
___ Add detection for NAK responses to file transfer invitations
|
||||
___ Improve performance of restyling the chat window: combine xml to one string.
|
||||
|
||||
Low-priority features:
|
||||
___ Improve wink processing messages of `cabextract` output in ChatWindow::showWink().
|
||||
___ When chat window closes, inform user the file transfers will also abort.
|
||||
___ See what happens when there is a picture transfer in a multi-chat, and one of the chats becomes busy.
|
||||
___ When opening chat windows, shift their position to make them cascade.
|
||||
|
||||
Low-priority code changes:
|
||||
___ Add Application::get..TransferAbortMessage().
|
||||
___ Only allow plugins/non-local files temporary when displaying winks?
|
||||
___ Implement Application::attachToChat(..) feature to improve aborting?
|
||||
___ Create a WinkArchive class for the code in ChatWindow::showWink().
|
||||
___ Include contact meta-data in receivedMessage() call of ChatWindow::showWink().
|
||||
|
||||
Low-priority protocol features:
|
||||
___ Support adding contacts to multiple groups.
|
||||
___ Detect multiple external IP addresses to advertise in file transfer invite message.
|
||||
___ Support sending the data-preparation ACK later with Windows Live Messenger picture transfers.
|
||||
___ Support direct connection setups when requesting contact pictures.
|
||||
___ Support aborting the data-preparation, also detect when contact does this.
|
||||
___ Support the datacast emulation over MSNP2P for MSNC1-compatible clients (for winks).
|
||||
___ Test whether applications need to be aborted when the switchboard reconnects.
|
||||
___ Check P2P incomingMessages_ / outgoingMessages_ cache for timeouts.
|
||||
___ Leave a P2PApplication half-open to receive a final error message.
|
||||
___ Use "URL PERSON/PROFILE" settings.
|
||||
|
||||
Wishes by users for 1.5:
|
||||
___ XMMS2 now listening: http://www.kmess.org/board/viewtopic.php?t=1569 (use relaytool from autopackage.org for that).
|
||||
|
||||
Implemented for 1.5:
|
||||
_Y_ Implement text/x-clientcaps message and MsnObject name to advertise KMess to other third-party messengers.
|
||||
_Y_ Add a checkbox to the settingspanel to disable the shaking window effect.
|
||||
_Y_ Support emoticons folder with KDE 3.3
|
||||
_Y_ Support "now playing" by other contacts, use dcop for our status.
|
||||
_Y_ Handle closed direct connection, KMess keeps using the direct connection.
|
||||
_Y_ Implement i18n() for chat styles, e.g. use libxml to replace all text nodes, create an XSLT to extract all text nodes.
|
||||
_Y_ Fix the memory-leak of sentMessages_ for chat messages, these are only cleared at a NAK currently.
|
||||
_Y_ Send previews of images in msn file transfers.
|
||||
_Y_ Fix detection of context field splitter in FileTransferP2P::contactStarted1..(), also needed to show image previews.
|
||||
_Y_ Contact sidebar width is not saved/restord anymore.
|
||||
_Y_ Contact sidebar width is changed when the chat window resizes.
|
||||
_Y_ Check if our eventsrc file is found in the KDE-dirs, otherwise warn KMess can't notify or flash the taskbar or play sounds (see: http://www.kmess.org/board/viewtopic.php?t=1619)
|
||||
_N_ (caused by Beryl bug) "Focus stealing prevention", open the new chat windows in the background by default.
|
||||
_Y_ Add save log button to network window.
|
||||
_Y_ Improve removing contacts, offer dialog to block the contact as well (currently always kept in allowed list).
|
||||
_Y_ Display a message box if the chat logs can't be saved because the folder no longer exists.
|
||||
_Y_ Automatically save settings when pressing OK, not when closing KMess.
|
||||
_Y_ Fix settings for balloon notifications, see http://www.kmess.org/board/viewtopic.php?t=559
|
||||
_Y_ Now listening icon is SUSE specific
|
||||
_Y_ Fix MsnConnection::addPercents(), replace with KURL::encode_string(), but test for compatibility.
|
||||
_Y_ Replace warning message "you can't change your friendly name" with something less unobtrusive.
|
||||
_Y_ Emoticons are no longer displayed in the chat settings tab.
|
||||
_Y_ Improve settingsdialog height, for 800x600 displays.
|
||||
_Y_ Hide the register link when editing an account.
|
||||
_Y_ Remove the dependency on QWidget for data-classes.
|
||||
_Y_ Add a balloon if the contact start the chat with an filetransfer/nudge/wink.
|
||||
_Y_ Implement KDE passive popups so the balloon code can be removed later.
|
||||
_Y_ Balloon size fix (2 contacts on balloon), might become obsolute when passive popups are used instead.
|
||||
_Y_ Get rid of the balloons
|
||||
_Y_ Drag and drop of files in the chat window.
|
||||
_Y_ Fix newline shortkey Ctrl+Enter / Shift+Enter in GNOME. In fact it didn't work at all, not only in GNOME. :P
|
||||
_Y_ Delete contact settings when contact no longer exists in the contact list.
|
||||
_Y_ Implement cleaning up of the kmessrc file, remove old Contact entries that are no longer used.
|
||||
_Y_ Make moving of contacts smoother, see MsnNotificationConnection::getAdc().
|
||||
_Y_ Fix error messages with 'actions -> new contact' on a 'non-existing contact', 'existing contact' and 'contact without @'.
|
||||
_N_ Move network/extra/httpsoapconnection to the network root folder.
|
||||
_Y_ Move chat/invitedcontact.h to the contact folder.
|
||||
_Y_ Implement text/x-keepalive message so be sure when a contact really left a chat.
|
||||
_Y_ Update binreloc support to version 2.0.
|
||||
_Y_ Crash with downloading offline-im messages: http://www.kmess.org/board/viewtopic.php?t=1490
|
||||
_Y_ Login blocks all gui-events: make all connection attempts async
|
||||
_Y_ Allow users to scale fonts in the chat window (zoom icons).
|
||||
_Y_ Fix problems with clicking on contacts rapidly, see http://www.kmess.org/board/viewtopic.php?t=533
|
||||
_Y_ Decouple the MsnSwitchboardConnection from the ChatWindow class, for picture transfers in the background.
|
||||
_Y_ Highlight the text of the contact list item when it's active.
|
||||
_Y_ Fix contact list background picture scrolling
|
||||
_Y_ Implement setEnabled() checks for the contact group context menu in KMessView::slotContextMenu().
|
||||
_Y_ Optionally download contact pictures when the picture changed but they're not in a chat yet.
|
||||
_Y_ Test whether a display picture is fully received, and remove the image from the cache otherwise.
|
||||
_Y_ Limit the maximum number of MSN Object simultaneous downloads, to avoid server flooding. See ChatMaster::slotContactChangedMsnObject().
|
||||
_Y_ Fix e-mail support for non-Hotmail accounts, don't attempt to use Hotmail. Use chosen app or default KDE mail app instead).
|
||||
_Y_ Update the accounts listed in the initial list When one of the registered accounts is deleted.
|
||||
_Y_ Improve the contact list contact menu
|
||||
_N_ Check if the mutex lock in MsnConnection is really needed anymore.
|
||||
_Y_ Restore the context menu in the chat window.
|
||||
_Y_ Disallow logging in without having entered a password.
|
||||
_Y_ When the switchboard is disconnected (gray contact pictures), the command to invite new contacts will get ignored when a new switchboard gets ready.
|
||||
_Y_ In the emoticon settings panel, don't assume "smile.png" will always be available (parse theme, preferably with an EmoticonTheme class).
|
||||
_Y_ Include a tooltip in the contact sidebar.
|
||||
_Y_ Include a contact menu in the contact sidebar (see https://www.lab.fi.uva.es/~albgonz/kmesss.jpg)
|
||||
_Y_ When the contact disables it's picture, should we leave the current one active, or revert the the "unknown" picture?
|
||||
_Y__ Group chats: remove contact frame when a user leaves the chat.
|
||||
_Y_ Support removing a contact from just one group.
|
||||
_Y_ Group chats: fix status bar when multiple users are typing a message.
|
||||
_Y_ Implement UPnP support
|
||||
_Y_ Add winks support
|
||||
_Y_ Add emoticon transfer support
|
||||
_N_ "Who has me in his/hers contact list?" dialog (allowed/removed groups already reveal this)
|
||||
_N_ Include the network-window as "--with-network-window" if the network-window-code displays traffic nicer.
|
||||
|
||||
|
||||
Implemented for 2.0:
|
||||
_Y_ Account menu's: only show submenu when there are multiple accounts.
|
||||
_Y_ Add the ability to select another browser for opened links, instead of the kde's default browser.
|
||||
_Y_ Change mouse cursor when hovering over the "personal status message" widget, make non-active background color less dark.
|
||||
_Y_ Compact sidebar list as described in http://www.kmess.org/board/viewtopic.php?t=460
|
||||
(make it optional, let the user choose between "no", "compact", "full")
|
||||
_Y_ Contact list items flicker, specially when there are a lot of contacts.
|
||||
_Y_ Disable cut/copy for the password line (InitialViewInterface, not using KPasswordEdit because it disables too much).
|
||||
_Y_ Group chats: use more compact contact frame.
|
||||
_Y_ Group off-line contacts in one single group.
|
||||
_Y_ Improve the parsing of *bold* /italics/ and _underline_, so http:/website/ does not become italic and avoid mangling
|
||||
<a href="http://website/"> to <a href="http:/<i>/website/</i>">.
|
||||
(looks more clean, though it's a bit kmerlin like though, and the current pictures list is not that bad for multi-chat) - Being worked on
|
||||
_Y_ Maybe: improve chat window layout like http://kde-apps.org/content/show.php?content=24461
|
||||
_Y_ Move utility classes to a separate folder.
|
||||
_Y_ Order of the title in the main window, account before or after. Should distinguish from the chat windows though.
|
||||
_Y_ Put TcpConnectionBase under MsnConnection.
|
||||
_Y_ Remove all #includes for <kdebug.h> and replace it with "kmessdebug.h" (watch for inclusions from subdirs)
|
||||
_Y_ See what happens when there is a picture transfer in a multi-chat, and one of the chats becomes buzy.
|
||||
_Y_ Tabbed chats.
|
||||
_Y_ Use if(KMESS_NULL(..)) checks instead of current if(.. != 0) { .. }
|
||||
_Y_ Use kdWarning() instead of kdDebug() << "WARNING", for warning messages.
|
||||
_Y_ Add HTTP connection method.
|
||||
_Y_ Add Ink support?
|
||||
_Y_ Upgrade to MSNP14, but not until the protocol is fully understood! (it has some radical changes).
|
||||
_Y_ Update contact properties edit dialog, icon button for contact icon.
|
||||
_Y_ Browse/view the contact picture cache (include meta data, like contact handle and date/time)
|
||||
_Y_ GetHotNewStuff (as of KDE 3.4, either support it through a KPart, or wait for KDE 4 conversion)
|
||||
_Y_ KWallet integration
|
||||
|
||||
|
||||
|
||||
Future ideas:
|
||||
=============
|
||||
|
||||
MSN Protocol support:
|
||||
___ Improve old file transfer code, multiple connections like MSN5 does.
|
||||
___ Implement a settings category ("File transfers") where the user can at least choose:
|
||||
- "[x] Allow KMess to control my internet router programmatically [learn more]"
|
||||
- "Use the following port to receive UPnP broadcast responses: [default 1900]"
|
||||
___ Implement STUN support, also for Connection-Type field, see http://en.wikipedia.org/wiki/STUN and http://en.wikipedia.org/wiki/UDP_hole_punching.
|
||||
___ Add voice (SIP) support
|
||||
___ Add Voice clips support, see http://www.hypothetic.org/docs/msn/phorum/read.php?f=1&i=11505&t=11437
|
||||
___ Add webcam (RTP) support, see http://www.hypothetic.org/docs/msn/phorum/read.php?f=1&i=9837&t=9837
|
||||
___ Fix msn remote desktop invitation support (context field with user/pass)
|
||||
___ Implement MSN Spaces integration
|
||||
___ Implement MSN Games support (ActicitiesCollection : public QPtrList<Activity>)
|
||||
___ Implement shared folders.
|
||||
|
||||
Invitation support:
|
||||
___ Check whether gnomemeeting, krdc exists before starting/acceping a session
|
||||
___ Support Konference for netmeeting invitations as well.
|
||||
___ Detect whether gnomemeeting or konference exists before starting a netmeeting session.
|
||||
|
||||
Interface features:
|
||||
___ Show an icon overlay for contact connected with webmessenger.msn.com / MSN Mobile
|
||||
___ Add contact icon (purple?) for unknown contacts added by others in a multi-chat session.
|
||||
|
||||
KDE Integration:
|
||||
___ KDE-IM bridge (http://developer.kde.org/documentation/library/cvs-api/interfaces/html/classKIMIface.html)
|
||||
- The KMess team
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
# -*-shell-script-*-
|
||||
|
||||
[Meta]
|
||||
RootName: @kmess.sourceforge.net/kmess:$SOFTWAREVERSION
|
||||
DisplayName: KMess
|
||||
ShortName: kmess
|
||||
SoftwareVersion: 2.0beta1
|
||||
Maintainer: The KMess Developers
|
||||
Packager: Diederik van der Boor
|
||||
Summary: MSN Messenger client for KDE
|
||||
URL: http://www.kmess.org/
|
||||
License: GNU General Public License, Version 2
|
||||
AutopackageTarget: 1.0
|
||||
|
||||
|
||||
[Description]
|
||||
A MSN Messenger Client for KDE
|
||||
|
||||
KMess is a MSN Messenger client for Linux. It enables Linux
|
||||
users to chat with friends online who are using MSN Messenger
|
||||
in Windows or Mac OS. The strength of KMess is it's integration
|
||||
with the KDE desktop environment, focus on MSN Messenger specific
|
||||
features and an easy-to-use interface.
|
||||
|
||||
|
||||
[BuildPrepare]
|
||||
# The bogus deps solve our warnings about older distro incompatiblity
|
||||
export APBUILD_BOGUS_DEPS='art_lgpl art_lgpl_2 dl expat fontconfig freetype GL ICE idn kdefx kdesu kwalletclient m nsl png pthread resolv SM util X11 Xcursor Xft Xi Xinerama Xmu Xrandr Xrender'
|
||||
# Xrender is suggested by makeinstaller,
|
||||
# the XScreenSaver stuff can be either in libXss or libXext
|
||||
export APBUILD_STATIC='Xrender Xext Xss'
|
||||
prepareBuild
|
||||
|
||||
|
||||
[BuildUnprepare]
|
||||
unprepareBuild
|
||||
|
||||
|
||||
[Imports]
|
||||
# tells autopackage to include all files created by "make install"
|
||||
# make sure the symlink does not get installed. recreate it during Install
|
||||
find . -name common -type d -o -type l -exec rm -Rf {} \;
|
||||
echo "*" | import
|
||||
|
||||
|
||||
[Prepare]
|
||||
# KDE 4.1 is the minimum requirement for KMess 2
|
||||
require @kde.org/kdelibs 4.1
|
||||
|
||||
# flash and cabextract are used for winks.
|
||||
playWinks="1"
|
||||
|
||||
# test for cabextract
|
||||
outputTest "cabextract"
|
||||
if locateCommand -r "cabextract" "-v"; then
|
||||
outputTestPass
|
||||
else
|
||||
outputTestFail
|
||||
playWinks="0"
|
||||
fi
|
||||
|
||||
# test for flash
|
||||
# paths taken from Konqueror.
|
||||
outputTest "flash player"
|
||||
hasFlash="0"
|
||||
for i in /usr/lib/browser-plugins $HOME/.netscape/plugins /usr/lib64/browser-plugins /usr/local/netscape/plugins /opt/mozilla/plugins /opt/mozilla/lib/plugins /opt/netscape/plugins /opt/netscape/communicator/plugins /usr/lib/netscape/plugins /usr/lib/netscape/plugins-libc5 /usr/lib/netscape/plugins-libc6 /usr/lib/mozilla/plugins /usr/lib64/netscape/plugins /usr/lib64/mozilla/plugins $MOZILLA_HOME/plugins
|
||||
do
|
||||
if [ -e "$i/libflashplayer.so" ]
|
||||
then
|
||||
hasFlash="1"
|
||||
fi
|
||||
done
|
||||
if [ "$hasFlash" -eq "1" ];
|
||||
then
|
||||
outputTestPass
|
||||
else
|
||||
outputTestFail
|
||||
playWinks="0"
|
||||
fi
|
||||
|
||||
# notify user
|
||||
if [ "$playWinks" -eq "0" ];
|
||||
then
|
||||
outputStatus "You need to have cabextract and a flash player installed to play winks"
|
||||
# don't abort, KMess can operate without these deps.
|
||||
fi
|
||||
|
||||
|
||||
[Install]
|
||||
# # force to use the KDE path, workarround for broken distro's
|
||||
# # which don't support XDG/KDE stuff in /usr or /usr/local
|
||||
# test "`id -u`" -eq 0 && export PREFIX="`getKdePrefix`" || true
|
||||
|
||||
outputStatus "Installing application..."
|
||||
installExe bin/*
|
||||
installData share/apps/ share/doc/ share/emoticons/ share/sounds/
|
||||
linkFile --silent "`getKdePrefix`/share/doc/HTML/en/common" "$PREFIX/share/doc/HTML/en/kmess/common"
|
||||
installIcon share/icons/hicolor/ share/icons/locolor/
|
||||
installDesktop "Internet" share/applications/kde/kmess.desktop
|
||||
installLocale share/locale/
|
||||
|
||||
|
||||
[Uninstall]
|
||||
uninstallFromLog
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,82 @@
|
||||
# - Try to find GCrypt
|
||||
# Once done this will define
|
||||
#
|
||||
# GCRYPT_FOUND - system has GCrypt
|
||||
# GCRYPT_INCLUDE_DIRS - the GCrypt include directory
|
||||
# GCRYPT_LIBRARIES - Link these to use GCrypt
|
||||
# GCRYPT_DEFINITIONS - Compiler switches required for using GCrypt
|
||||
#
|
||||
# Copyright (c) 2009 Andreas Schneider <mail@cynapses.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
# Imported into KMess, monday october 19, 2009 by Sjors
|
||||
# reference URL:
|
||||
# http://dev.libssh.org/export/27d25752e93c19d3e6584915bd64b72e43f3afc0/ \
|
||||
# cmake/Modules/FindGCrypt.cmake
|
||||
#
|
||||
|
||||
|
||||
if (GCRYPT_LIBRARIES AND GCRYPT_INCLUDE_DIRS)
|
||||
# in cache already
|
||||
set(GCRYPT_FOUND TRUE)
|
||||
else (GCRYPT_LIBRARIES AND GCRYPT_INCLUDE_DIRS)
|
||||
|
||||
find_path(GCRYPT_INCLUDE_DIR
|
||||
NAMES
|
||||
gcrypt.h
|
||||
PATHS
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/opt/local/include
|
||||
/sw/include
|
||||
)
|
||||
mark_as_advanced(GCRYPT_INCLUDE_DIR)
|
||||
|
||||
find_library(GCRYPT_LIBRARY
|
||||
NAMES
|
||||
gcrypt
|
||||
PATHS
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/opt/local/lib
|
||||
/sw/lib
|
||||
)
|
||||
mark_as_advanced(GCRYPT_LIBRARY)
|
||||
|
||||
if (GCRYPT_LIBRARY)
|
||||
set(GCRYPT_FOUND TRUE CACHE INTERNAL "Wether the gcrypt library has been found" FORCE)
|
||||
endif (GCRYPT_LIBRARY)
|
||||
|
||||
set(GCRYPT_INCLUDE_DIRS
|
||||
${GCRYPT_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if (GCRYPT_FOUND)
|
||||
set(GCRYPT_LIBRARIES
|
||||
${GCRYPT_LIBRARIES}
|
||||
${GCRYPT_LIBRARY}
|
||||
)
|
||||
endif (GCRYPT_FOUND)
|
||||
|
||||
if (GCRYPT_INCLUDE_DIRS AND GCRYPT_LIBRARIES)
|
||||
set(GCRYPT_FOUND TRUE)
|
||||
endif (GCRYPT_INCLUDE_DIRS AND GCRYPT_LIBRARIES)
|
||||
|
||||
if (GCRYPT_FOUND)
|
||||
if (NOT GCrypt_FIND_QUIETLY)
|
||||
message(STATUS "Found GCrypt: ${GCRYPT_LIBRARIES}")
|
||||
endif (NOT GCrypt_FIND_QUIETLY)
|
||||
else (GCRYPT_FOUND)
|
||||
if (GCrypt_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find GCrypt")
|
||||
endif (GCrypt_FIND_REQUIRED)
|
||||
endif (GCRYPT_FOUND)
|
||||
|
||||
# show the GCRYPT_INCLUDE_DIRS and GCRYPT_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(GCRYPT_INCLUDE_DIRS GCRYPT_LIBRARIES)
|
||||
|
||||
endif (GCRYPT_LIBRARIES AND GCRYPT_INCLUDE_DIRS)
|
||||
|
||||
Executable → Regular
@@ -11,8 +11,8 @@
|
||||
/* Define if debug output should be enabled */
|
||||
#define KMESS_DEBUG ${KMESS_ENABLE_DEBUG_OUTPUT}
|
||||
|
||||
/* Define if the Relocatable Binary support should be enable */
|
||||
#define ENABLE_BINRELOC ${KMESS_ENABLE_BINRELOC}
|
||||
/* Define what the build type of KMess is */
|
||||
#cmakedefine KMESS_BUILT_DEBUGFULL ${KMESS_BUILD_DEBUGFULL}
|
||||
|
||||
/* Define the fallback prefix */
|
||||
#define KMESS_PREFIX "${CMAKE_INSTALL_PREFIX}"
|
||||
@@ -20,8 +20,11 @@
|
||||
/* Define if you have the XScreenSaver extension */
|
||||
#cmakedefine HAVE_XSCREENSAVER ${HAVE_XSCREENSAVER}
|
||||
|
||||
/* Define if you have KPhone installed (for SIP voice chat) (currently unused) */
|
||||
/* #define HAS_KPHONE 1 */
|
||||
/* Define if LikeBack is not needed */
|
||||
#define KMESS_DISABLE_LIKEBACK ${KMESS_DISABLE_LIKEBACK}
|
||||
|
||||
/* Define whether to enable MSN webcam support (HIGHLY EXPERIMENTAL) */
|
||||
#define ENABLE_WEBCAM ${KMESS_ENABLE_WEBCAM}
|
||||
/* Define whether Ink support is enabled */
|
||||
#define KMESS_ENABLE_INK ${HAVE_LIBISFQT}
|
||||
|
||||
/* Define whether we have the new system tray */
|
||||
#cmakedefine HAVE_NEW_TRAY
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd `dirname $0`/..
|
||||
|
||||
# second sed expression fixes Mac OS X 10.4
|
||||
appver="`egrep 'SET.*KMESS_VERSION' CMakeLists.txt | sed -e 's/.*"\([^"]*\)".*/\1/'`"
|
||||
date="`date +%Y%m%d`"
|
||||
|
||||
# Make sure we have the right repository
|
||||
if [ ! -f kmess.kdev4 ]; then
|
||||
/bin/echo -n "git (unknown)"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Display normal version if git is not installed
|
||||
if [ ! -x `which git 2>/dev/null` ]; then
|
||||
/bin/echo -n "$appver"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Display subversion version if this is a working copy
|
||||
gitname=`LANG=C git describe --all HEAD 2>/dev/null | sed -e 's:^heads/::g'`
|
||||
gitver=`LANG=C git rev-parse --short HEAD 2>/dev/null`
|
||||
|
||||
# see if this is not a repository.
|
||||
if [ -z "$gitver" ]; then
|
||||
if [ -z "$KMESS_VER" ]; then
|
||||
/bin/echo -n "$appver";
|
||||
else
|
||||
/bin/echo -n "$KMESS_VER";
|
||||
fi
|
||||
exit;
|
||||
fi
|
||||
|
||||
# display subversion version and date
|
||||
/bin/echo -n "$appver-git ($gitname@$gitver >= $date)"
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd `dirname $0`/..
|
||||
|
||||
# second sed expression fixes Mac OS X 10.4
|
||||
appver="`cat kmess.lsm | grep Version: | sed -e 's/[^0-9]\+//' -e 's/Version:\( \)*//'`"
|
||||
date="`date +%Y%m%d`"
|
||||
|
||||
# Make sure we have the right repository
|
||||
if [ ! -f kmess.kdevelop ]; then
|
||||
echo -n "svn (unknown)"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Display normal version if subversion is not installed
|
||||
if [ ! -x `which svnversion 2>/dev/null` ]; then
|
||||
echo -n "$appver"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Display subversion version if this is a working copy
|
||||
svnver=`LANG=C svnversion`
|
||||
|
||||
# see if this is not a repository.
|
||||
if [ "$svnver" = "exported" ]; then
|
||||
if [ -z "$KMESS_VER" ]; then
|
||||
echo -n "$appver";
|
||||
else
|
||||
echo -n "$KMESS_VER";
|
||||
fi
|
||||
exit;
|
||||
fi
|
||||
|
||||
# display subversion version and date
|
||||
echo -n "$appver-svn (`svnversion` >= $date)"
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
# This file is included here because the standard FindGIF.cmake file
|
||||
# requires the CHECK_STRUCT_MEMBER macro. It wasn't on my PC, at least, so
|
||||
# I include a copy of the macro here for giflib to use.
|
||||
|
||||
MACRO (CHECK_STRUCT_MEMBER _STRUCT _MEMBER _HEADER _RESULT)
|
||||
SET(_INCLUDE_FILES)
|
||||
FOREACH (it ${_HEADER})
|
||||
SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n")
|
||||
ENDFOREACH (it)
|
||||
|
||||
SET(_CHECK_STRUCT_MEMBER_SOURCE_CODE "
|
||||
${_INCLUDE_FILES}
|
||||
int main()
|
||||
{
|
||||
${_STRUCT}* tmp;
|
||||
tmp->${_MEMBER};
|
||||
return 0;
|
||||
}
|
||||
")
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT})
|
||||
|
||||
ENDMACRO (CHECK_STRUCT_MEMBER)
|
||||
@@ -25,11 +25,22 @@
|
||||
# Variables
|
||||
#
|
||||
|
||||
# auto-detect default prefix
|
||||
KDE4_PREFIX=""
|
||||
DEFAULT_PREFIX="/usr/local"
|
||||
if type -p kde4-config >/dev/null; then
|
||||
KDE4_PREFIX=`kde4-config --prefix`
|
||||
|
||||
# auto-detect KDE Installation on the Mac.
|
||||
KDE4_CONFIG=""
|
||||
for i in `whereis kde4-config` /opt/local/bin/kde4-config
|
||||
do
|
||||
if [ -x "$i" ]; then
|
||||
KDE4_CONFIG="$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# auto-detect default prefix
|
||||
if [ -n "$KDE4_CONFIG" ]; then
|
||||
KDE4_PREFIX=`$KDE4_CONFIG --prefix`
|
||||
DEFAULT_PREFIX="$KDE4_PREFIX"
|
||||
fi
|
||||
|
||||
@@ -39,12 +50,11 @@ BUILD_TYPE="relwithdebinfo"
|
||||
BUILD_DIR="build"
|
||||
REAL_BUILD_DIR=""
|
||||
DEBUG=0
|
||||
BINRELOC=0
|
||||
|
||||
# Globals
|
||||
cd `dirname $0`
|
||||
SRC_DIR="$PWD"
|
||||
KMESS_VER="`./cmake/get-svn-version.sh`"
|
||||
KMESS_VER="`./cmake/get-git-version.sh`"
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +81,6 @@ Installation directories:
|
||||
|
||||
Optional Features:
|
||||
--enable-debug-output enable debugging output. default=no
|
||||
--enable-binreloc enable binary relocation support. default=no
|
||||
--build-dir=PATH path to store the build files. default: ./build
|
||||
--build-type=TYPE define the build configuration to use, values:
|
||||
release - standard release
|
||||
@@ -96,7 +105,7 @@ After running ./configure, you can type 'make' to build this project.
|
||||
else
|
||||
echo " WARNING:
|
||||
|
||||
kde4-config was not found. Please verify that
|
||||
kde4-config was not found. Please verify that
|
||||
you've installed the KDE 4 development packages!
|
||||
"
|
||||
fi
|
||||
@@ -119,7 +128,8 @@ function run_cmake
|
||||
|
||||
# Check build type
|
||||
case "$BUILD_TYPE" in
|
||||
release|relwithdebinfo|minsizerel|debug|debugfull) ;;
|
||||
debugfull) DEBUG=1 ;;
|
||||
release|relwithdebinfo|minsizerel|debug) ;;
|
||||
*)
|
||||
echo "$0: invalid build type: $BUILD_TYPE" >&2
|
||||
exit 1
|
||||
@@ -139,7 +149,6 @@ function run_cmake
|
||||
echo "command: cmake -D CMAKE_INSTALL_PREFIX=$PREFIX \ "
|
||||
echo " -D CMAKE_BUILD_TYPE=$BUILD_TYPE \ "
|
||||
echo " -D KMESS_DEBUG_OUTPUT=$DEBUG \ "
|
||||
echo " -D KMESS_ENABLE_BINRELOC=$BINRELOC \ "
|
||||
echo " "$SRC_DIR" "
|
||||
echo ""
|
||||
|
||||
@@ -147,7 +156,6 @@ function run_cmake
|
||||
cmake -D CMAKE_INSTALL_PREFIX=$PREFIX \
|
||||
-D CMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-D KMESS_DEBUG_OUTPUT=$DEBUG \
|
||||
-D KMESS_ENABLE_BINRELOC=$BINRELOC \
|
||||
"$SRC_DIR" \
|
||||
&& cmake_ok || cmake_error
|
||||
}
|
||||
@@ -158,16 +166,11 @@ function cmake_ok
|
||||
# collect all settings
|
||||
local HAS_XSS="no (extension not detected)"
|
||||
local HAS_DEBUG="no (good!)"
|
||||
local HAS_BINRELOC="no (only available on Linux)"
|
||||
|
||||
if [ $DEBUG = "1" ]; then
|
||||
HAS_DEBUG="yes (KMess will be slow!)"
|
||||
fi
|
||||
|
||||
if [ $BINRELOC = "1" ]; then
|
||||
HAS_BINRELOC="yes"
|
||||
fi
|
||||
|
||||
if grep -q '^#define HAVE_XSCREENSAVER 1$' "config-kmess.h"; then
|
||||
HAS_XSS="yes"
|
||||
fi
|
||||
@@ -181,7 +184,6 @@ function cmake_ok
|
||||
# Install prefix: $PREFIX
|
||||
# Auto-away feature: $HAS_XSS
|
||||
# Debugging messages: $HAS_DEBUG
|
||||
# Binary relocatable: $HAS_BINRELOC
|
||||
# Build type: $BUILD_TYPE
|
||||
# Build directory: $BUILD_DIR
|
||||
#
|
||||
@@ -192,7 +194,6 @@ function cmake_ok
|
||||
# cmake -D CMAKE_INSTALL_PREFIX=$PREFIX \\
|
||||
# -D CMAKE_BUILD_TYPE=$BUILD_TYPE \\
|
||||
# -D KMESS_DEBUG_OUTPUT=$DEBUG \\
|
||||
# -D KMESS_ENABLE_BINRELOC=$BINRELOC \\
|
||||
# \"$SRC_DIR\"
|
||||
#
|
||||
" > $makefile
|
||||
@@ -213,7 +214,6 @@ function cmake_ok
|
||||
Install prefix: $PREFIX
|
||||
Auto-away feature: $HAS_XSS
|
||||
Debugging messages: $HAS_DEBUG
|
||||
Binary relocatable: $HAS_BINRELOC
|
||||
Build type: $BUILD_TYPE
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ Please run cmake directly, for example:
|
||||
fi
|
||||
|
||||
# Parse the arguments
|
||||
longopts="help,prefix:,build-type:,build-dir:,build-folder:,enable-binreloc,enable-debug-output"
|
||||
longopts="help,prefix:,build-type:,build-dir:,build-folder:,enable-debug-output"
|
||||
TEMP=`getopt --name=configure \
|
||||
--options h \
|
||||
--longoptions "$longopts" -- "$@"`
|
||||
@@ -279,7 +279,6 @@ Please run cmake directly, for example:
|
||||
-h|--help) print_help; exit 0 ;;
|
||||
--prefix) PREFIX=$2; shift 2 ;;
|
||||
--enable-debug-output) DEBUG=1; shift ;;
|
||||
--enable-binreloc) BINRELOC=1; shift ;;
|
||||
--build-type) BUILD_TYPE="$2"; shift 2 ;;
|
||||
--build-dir) BUILD_DIR="$2"; shift 2 ;;
|
||||
--builddir|--build-directory|--build-folder) BUILD_DIR="$2"; shift 2 ;; # be forgiving with small errors.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
# Build files and dirs
|
||||
build*
|
||||
|
||||
# KDevelop and other IDE-specific project files
|
||||
*.kdev*
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
LibISF-Qt authors
|
||||
-------------------------
|
||||
|
||||
The developers of this library are:
|
||||
|
||||
- Valerio Pilo <valerio@kmess.org>
|
||||
- Adam Goossens <adam@kmess.org>
|
||||
|
||||
|
||||
Credits
|
||||
-------------------------
|
||||
|
||||
Creating this library would not have been possible without the previous efforts
|
||||
of some great people:
|
||||
|
||||
The tclISF authors:
|
||||
- Ole André Vadla Ravnås (oleavr)
|
||||
- Youness Alaoui (KaKaRoTo)
|
||||
- Boris Faure (billiob)
|
||||
|
||||
The pyISF author:
|
||||
- Kevin Campbell
|
||||
|
||||
We'd also like to thank Microsoft for publishing the file format specifications.
|
||||
@@ -0,0 +1,131 @@
|
||||
CMAKE_MINIMUM_REQUIRED( VERSION 3.16 )
|
||||
PROJECT( isf-qt LANGUAGES CXX )
|
||||
|
||||
|
||||
#### Main switches ####
|
||||
|
||||
# Bundled mode: you can add Isf-Qt as a subdirectory
|
||||
# of your CMake-based prjoect easily. Just use:
|
||||
# SET( USE_BUNDLED_LIBRARIES ISFQT )
|
||||
# and Isf-Qt will be built and statically linked in your
|
||||
# application, without installing anything
|
||||
IF( ISFQT_IS_BUNDLED OR USE_BUNDLED_LIBRARIES MATCHES "ISFQT" )
|
||||
SET( ISFQT_IS_BUNDLED TRUE )
|
||||
MESSAGE( "-- Will build Isf-Qt in bundled mode." )
|
||||
ELSE()
|
||||
SET( ISFQT_IS_BUNDLED FALSE )
|
||||
ENDIF()
|
||||
|
||||
# Only set the build type if we're not being bundled: if we are, we'll
|
||||
# use the build type specified by the parent project
|
||||
IF( NOT ISFQT_IS_BUNDLED )
|
||||
|
||||
# Define the default build type
|
||||
# Possible values:
|
||||
# - none
|
||||
# - release
|
||||
# - debug
|
||||
# - debugfull (even fewer optimisations)
|
||||
# - relwithdebinfo (release with debug info)
|
||||
# - minsizerel (minimum size release)
|
||||
# Uncomment the next line to force building in full debug mode
|
||||
SET( CMAKE_BUILD_TYPE debugfull )
|
||||
ENDIF()
|
||||
|
||||
# Define the library version number
|
||||
SET( ISFQT_VERSION "1.0dev" )
|
||||
|
||||
|
||||
#### Inclusions ####
|
||||
|
||||
FIND_PACKAGE( Qt6 REQUIRED COMPONENTS Core Core5Compat Gui Widgets )
|
||||
|
||||
OPTION( WANT_GIF "Enable support for reading and writing Fortified-GIF images" ON )
|
||||
IF( WANT_GIF )
|
||||
MESSAGE( "-- Looking for GifLib" )
|
||||
|
||||
# Apparently, the KDE CMake macros unset a check which is required by giflib.
|
||||
IF( KDE4_FOUND )
|
||||
INCLUDE( "cmake/giflib_hack.cmake" )
|
||||
ENDIF( KDE4_FOUND )
|
||||
|
||||
FIND_PACKAGE( GIF QUIET )
|
||||
|
||||
# At this point, we can be relatively sure that if it's there, it has been found
|
||||
IF( NOT GIF_FOUND )
|
||||
MESSAGE( "-- Looking for GifLib - not found!" )
|
||||
MESSAGE( FATAL_ERROR
|
||||
"Support to read and write Fortified-GIF images is missing.\n"
|
||||
" * You will need to install both the library and development packages of the GIF Library, giflib.\n"
|
||||
" * Usually the package names for this library are:\n"
|
||||
" on Ubuntu: 'libgif4' and 'libgif-dev'\n"
|
||||
" on openSUSE: 'giflib' and 'giflib-devel'\n"
|
||||
" * The project homepage is http://sourceforge.net/projects/giflib/\n" )
|
||||
ELSE()
|
||||
MESSAGE( "-- Looking for GifLib - found" )
|
||||
ENDIF()
|
||||
ELSE( WANT_GIF )
|
||||
MESSAGE( "-- GifLib support disabled" )
|
||||
ENDIF( WANT_GIF )
|
||||
|
||||
# Set the variable for isfqtconfig.h.in
|
||||
IF( WANT_GIF )
|
||||
SET( GIF_ENABLED 1 )
|
||||
ELSE()
|
||||
SET( GIF_ENABLED 0 )
|
||||
ENDIF()
|
||||
|
||||
|
||||
#### Compilation ####
|
||||
|
||||
IF( CMAKE_COMPILER_IS_GNUCXX )
|
||||
SET( CMAKE_CXX_FLAGS_DEBUGFULL
|
||||
"-O0 -g3 -fno-inline -Wall -Woverloaded-virtual -Wsign-compare -Wundef -fvisibility=default" )
|
||||
ENDIF( CMAKE_COMPILER_IS_GNUCXX )
|
||||
|
||||
IF( NOT ISFQT_DEBUG_OUTPUT STREQUAL "1" AND NOT ISFQT_DEBUG_OUTPUT STREQUAL "0" )
|
||||
IF( CMAKE_BUILD_TYPE MATCHES debug )
|
||||
SET( ISFQT_DEBUG_OUTPUT 1 )
|
||||
ELSE()
|
||||
SET( ISFQT_DEBUG_OUTPUT 0 )
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
|
||||
|
||||
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} src include )
|
||||
|
||||
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/isfqtconfig.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/isfqtconfig.h )
|
||||
|
||||
ADD_SUBDIRECTORY( src )
|
||||
|
||||
IF( NOT ISFQT_IS_BUNDLED )
|
||||
ENABLE_TESTING()
|
||||
ADD_SUBDIRECTORY( tests )
|
||||
ENDIF()
|
||||
|
||||
|
||||
#### Project CMake configuration files creation ####
|
||||
|
||||
# Define the shortcuts for the installation directories
|
||||
SET( INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include/isf-qt"
|
||||
CACHE STRING "Directory where the include files will be installed" )
|
||||
SET( LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib"
|
||||
CACHE STRING "Directory where the lib will be installed" )
|
||||
SET( SHARED_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share"
|
||||
CACHE STRING "Directory where the shared data will be installed" )
|
||||
|
||||
# Prepare thej IsfQtConfig.cmake file, which allows other projects to find us
|
||||
CONFIGURE_FILE( cmake/IsfQtConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/IsfQtConfig.cmake" @ONLY )
|
||||
|
||||
|
||||
#### Installation ####
|
||||
|
||||
# Do not install the resources if we're bundling it into an application
|
||||
IF( NOT ISFQT_IS_BUNDLED )
|
||||
INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/IsfQtConfig.cmake
|
||||
DESTINATION ${LIB_INSTALL_DIR}/isfqt )
|
||||
INSTALL( FILES cmake/FindIsfQt.cmake
|
||||
DESTINATION ${CMAKE_ROOT}/Modules )
|
||||
ENDIF()
|
||||
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -0,0 +1,307 @@
|
||||
2010-07-23 (Valerio)
|
||||
* Fixed the copy constructor in Stroke, it was not copying all the necessary properties.
|
||||
* Fixed some compiler warnings.
|
||||
* Improved bundled compilation mode.
|
||||
|
||||
2010-07-22 (Valerio)
|
||||
* Improved the InkEdit text - added a checkbox to show the usage of Canvas::isEmpty() and a label
|
||||
to show usage of the Canvas::inkChanged() signal.
|
||||
* Fixed inkChanged() signal emission in Canvas: it was emitted too early, when the drawing was not
|
||||
updated yet: slots connected to inkChanged() could then call isEmpty() and get a true return value,
|
||||
even if the drawing wasn't empty.
|
||||
|
||||
2010-06-27 (Adam)
|
||||
* Use Q_ASSERT_X instead of qWarning where appropriate.
|
||||
|
||||
2010-06-19 (Valerio)
|
||||
* Changed the default behavior of InkCanvas::pixmap(), now paints on a transparent
|
||||
background, instead of a white one.
|
||||
|
||||
2010-06-19 (Valerio)
|
||||
* Added support for PNG fortification. Like GIF fortification, this feature allows to
|
||||
write a PNG containing the full ISF drawing inside itself. Of course, it's now also
|
||||
possible to read a PNG containing ISF data and retrieve the actual drawing.
|
||||
|
||||
2010-03-20 (Adam)
|
||||
* Slight optimisation to painting - we no longer draw the background color, Qt does it for us.
|
||||
Also, only render the parts of the buffer that have changed.
|
||||
|
||||
2010-03-13 (Adam)
|
||||
* Add a bezier curve smoothing algorithm. Strokes with the FitToCurve flag set are
|
||||
rendered with an approximate bezier curve. Gives much smoother looking strokes.
|
||||
|
||||
2010-02-21 (Adam)
|
||||
* Optimise the painting of strokes. Cache logic added to IsfQtDrawing and only
|
||||
strokes that have been added are painted. Also, no longer do a full bounding rect
|
||||
recalculation when a stroke is added.
|
||||
|
||||
2009-10-03 (Adam)
|
||||
* Remove unused HIMETRIC conversion methods.
|
||||
|
||||
2009-09-11 (Adam)
|
||||
* Improve doxygen comments of InkCanvas.
|
||||
|
||||
2009-09-10 (Valerio)
|
||||
* Do not add twice single-dot strokes in InkCanvas.
|
||||
* Removed a leftover debug message.
|
||||
* Fixed the Drawing copy constructor, which was copying even empty lists.
|
||||
* Fixed Bit Packing compression and the Delta-Delta transform, now the lib
|
||||
works perfectly again.
|
||||
* Fixed argument parsing in test_decode.
|
||||
|
||||
2009-09-10 (Adam)
|
||||
* Don't extract private members in Doxygen output.
|
||||
|
||||
2009-09-09 (Valerio)
|
||||
* Added Fortified GIF read-write support. New optional dependency, giflib.
|
||||
* Added Base64-encoded input/output data support.
|
||||
* Updated the documentation files.
|
||||
* Added Doxygen documentation infrastructure. There still are a lot of errors.
|
||||
* Created proper Doxygen documentation for all classes.
|
||||
* Moved the resource files into the /data/ directory.
|
||||
* Added a proper copy constructor for Drawing.
|
||||
|
||||
2009-09-09 (Adam)
|
||||
* Add draw-eraser.svgz to comply with Oxygen licensing requirements.
|
||||
|
||||
2009-09-08 (Adam)
|
||||
* Make copyright headers standard across all files.
|
||||
* Replace the eraser cursor with draw-eraser.png take from Oxygen 16x16 icon set.
|
||||
|
||||
2009-08-30 (Valerio)
|
||||
* Rewritten the algorithm detection methods.
|
||||
* Changed the names of some internal data classes, and improved their namespaces.
|
||||
* Added a maximum drawing size check, to avoid errors from resulting in 80k by 70k pixel wide
|
||||
drawings.
|
||||
* Moved all algorithms into src/data/algorithms.
|
||||
* Renamed the Gorilla algorithm to "Bit Packing".
|
||||
* Separated the delta-delta transformation methods into their own files.
|
||||
* Added an algorithm to do bit-packing of bytes.
|
||||
* Fixed bug in DataSource, the last bit of data was never read.
|
||||
* Added parsing the GUID table tag and saving the GUIDs into Drawing.
|
||||
* Added parsing of custom tags and decoding their contents.
|
||||
|
||||
2009-08-29 (Valerio)
|
||||
* Fixed skipping unknown custom tags contents.
|
||||
* Changed more method names to adhere to Qt's method naming style.
|
||||
|
||||
2009-08-29 (Adam)
|
||||
* Start qt-ifying the function names ie, "penSize()" instead of "getPenSize()".
|
||||
* Add methods to return pen size, colour and brush type from InkCanvas.
|
||||
* Always write at least one transformation to the stream: if no custom transforms, write the default one.
|
||||
* No longer take ownership of Isf::Drawing instances set with setDrawing().
|
||||
* By default InkCanvas' initial Isf::Drawing instance that is created is no longer allocated
|
||||
with new. This frees us from the concern of "when do we delete this initial reference?".
|
||||
* Rename Isf::InkEdit to Isf::InkCanvas. A better suited name.
|
||||
* When building bundled isf-qt, link statically and do not install any library components.
|
||||
Non-bundled builds a shared library and installs all components.
|
||||
|
||||
2009-08-28 (Adam)
|
||||
* Fix writing of transformation data: write in the correct order and scale the transform values
|
||||
appropriately before writing.
|
||||
* Write TIDX properly when transformations change between strokes.
|
||||
* Properly apply scaling when calculating the bounding rectangle.
|
||||
* Do not use Compress.decode(*) calls as function parameters. The order of parameter
|
||||
evaluation is platform-dependent and hence cannot be relied on.
|
||||
* Properly apply stroke transforms when drawing; scale pen size appropriately.
|
||||
|
||||
2009-08-24 (Adam)
|
||||
* Fix Drawing::getStrokeAtPoint to properly return point strokes.
|
||||
* Prevent strokes from having a bounding rectangle of size 1x1 (otherwise the eraser never hit them)
|
||||
* Tweak the collision distance to give a 25% margin of error (in practice this works well).
|
||||
|
||||
2009-08-23 (Adam)
|
||||
* Add Drawing::getStrokeAtPoint method. Uses Heron's Formula and triangle heights to determine
|
||||
if the cursor is positioned above a stroke. Seems to work quite well and appears to be fast.
|
||||
* Basic eraser support working. Individual strokes can be erased.
|
||||
* InkEdit now shows a custom cursor - a point drawn with the current stroke colour and width.
|
||||
* Drawing now calculates its bounding rectangle as necessary. Calls to finalizeChanges are not
|
||||
necessary anymore.
|
||||
* Remove cropping. Found a better method whereby InkEdit will draw the rendered ISF using the
|
||||
offset provided by the bounding rectangle of Isf::Drawing.
|
||||
* Supply the FitToCurve flag for all strokes. Windows will render them using Bezier smoothing, making
|
||||
them appear nicer.
|
||||
* Upgrade the TestInkEdit application. Supports multiple stroke colours and pen widths.
|
||||
* Remove InkEdit::getImageBytes and replace with InkEdit::getBytes. InkEdit doesn't write to GIF.
|
||||
* Support changing the colour of the InkEdit canvas.
|
||||
* Add a convenience method, InkEdit::save(), that will write the ISF drawing to a given QIODevice.
|
||||
Optionally will write the drawing in base64.
|
||||
|
||||
2009-08-23 (Adam)
|
||||
* Bug fixes in isf-qt: properly assign stroke attributes when changing colour on InkEdit.
|
||||
* Fix a crash that would occur if stroke attributes or transform were null.
|
||||
* Initial version of isf-qt InkEdit committed. Works reasonably well. Not integrated in KMess yet.
|
||||
* Added a cropping property to Isf::Drawing. When true, Drawing::getImage returns a cropped image
|
||||
and getSize returns the cropped size. When false, does the opposite. Necessary for InkEdit to
|
||||
properly support adding strokes to loaded Ink data.
|
||||
* Add a test application for new InkEdit. Can create, save and load Ink data in both raw ISF and
|
||||
ISF-base64 format.
|
||||
* Fix a bug in tagswriter which cause corrupt ISF data if there were no metric blocks.
|
||||
|
||||
2009-08-20 (Valerio)
|
||||
* Implemented writing metric blocks into ISF streams.
|
||||
* Fixed transformation/attributeSet/metrics index writing, now the index tags are written only
|
||||
when necessary.
|
||||
* Removed all user-side required conversions between HiMetric units and pixels. The library
|
||||
now only offers data expressed in pixels.
|
||||
* Fixed a bug in writeHiMetricSize() which prevented ISF streams from being parsed by older
|
||||
Microsoft.Ink libraries, like the one of Windows Messenger.
|
||||
|
||||
2009-08-20 (Adam)
|
||||
* Fix install path of FindIsfQt.cmake.
|
||||
* Don't attempt to install FindIsfQt.cmake if the library is build bundled.
|
||||
|
||||
2009-08-19 (Valerio)
|
||||
* Added DataSource::reset() method to start reading data back from the start.
|
||||
* Added to DataSource some ways to detect errors.
|
||||
* Rewritten the multi-byte encoding tests.
|
||||
* Switched away from QTransform to QMatrix for transformations.
|
||||
* Fixed the write order of transformation values.
|
||||
* Fixed stroke saving. The library should now be able to generate new ink.
|
||||
* Renamed Isf::Parser::isfToDrawing() and Isf::Parser::drawingToIsf()
|
||||
to Isf::Stream::reader() and Isf::Stream::writer() to enhance readability.
|
||||
* Renamed the Isf::PointInfo structure to Isf::AttributeSet, because the old name made no sense.
|
||||
* Added manipulation methods for strokes, transformations and attribute sets.
|
||||
* Fixed compiler warning. Thanks to Émeric Dupont.
|
||||
* Fixed colors in written streams.
|
||||
* Fixed the Scale&Translate transformation once again.
|
||||
* Isf::Drawings are now created on the heap instead of on the stack, to avoid unwanted object
|
||||
deletions.
|
||||
* Added optional parameter to Isf::Drawing::getPixmap() to choose the background color of
|
||||
the created image.
|
||||
* Added a method to compute some useful values before rendering new and changed drawings.
|
||||
* Added a couple stream writer improvements.
|
||||
|
||||
2009-08-14 (Valerio)
|
||||
* Added Drawing::clean() method to reset a drawing without creating a new one.
|
||||
|
||||
2009-08-13 (Valerio)
|
||||
* Added DataSource method to append bit arrays to the data.
|
||||
* Implemented Huffman encoding. Still missing is the algo to select the best encoding index.
|
||||
|
||||
2009-08-07 (Valerio)
|
||||
* Added an AUTHORS file with credits.
|
||||
* Implemented Gorilla compression.
|
||||
* Added stroke writing in the stream writer.
|
||||
* Moved all of Drawing's object lists to pointer lists, to ease comparing their items.
|
||||
|
||||
2009-08-06 (Valerio)
|
||||
* Added Metrics recognization and saving.
|
||||
* Made sense into the property tags: they're now all in the PacketProperty enum.
|
||||
* Added MultiByteCoding utility methods to find out the encoded size of a value.
|
||||
* Changed the installed include files directory to PREFIX/include/isf-qt .
|
||||
* Added a NOTES file with possibly useful stuff.
|
||||
* Added metrics measurement units, found on MSDN.
|
||||
* Added a couple more checks on data.
|
||||
* Moved include isfdrawing.h to isfqtdrawing.h .
|
||||
* Fixed CMake installing bug and added C++ style includes, <IsfQt> and <IsfQtDrawing>.
|
||||
* Fixed name of the drawing-to-isf converting method.
|
||||
* Fixed crash when given an invalid drawing address.
|
||||
|
||||
2009-08-05 (Valerio)
|
||||
* Extended Adam's last fix to the other main drawing properties.
|
||||
* Added maximum stroke pen size value.
|
||||
* Added stroke and drawing bounding box values.
|
||||
* Added adjusting the drawing's bounding box to include thick strokes.
|
||||
* Enabled transformations.
|
||||
* Fixed transform data order, it was inverted for scale+translate transforms.
|
||||
* Fixed destination QPixmap's size, it's now fit for the drawing.
|
||||
* Completed CMake rules, now the library can be installed on the system and
|
||||
other projects can find it.
|
||||
* Improved debugging rules, now debug mode is controlled (again) from CMake
|
||||
and verbose output is configurable.
|
||||
* Fixed the documentation to reflect the name change "libisf" to "isf-qt",
|
||||
and updated it.
|
||||
* Added updated KDevelop3/4 project files.
|
||||
* Changed the library structure:
|
||||
- separated the public interface includes into the /include/ dir
|
||||
- moved all internally used inclusions and data structures into
|
||||
/src/isfqt-internal.h
|
||||
- reorganized the internal files structure
|
||||
- renamed class Tags to TagsParser as there will be a TagWriter (made up name)
|
||||
- Renamed the IsfData class to DataSource as it is at a lower level than ISF.
|
||||
- Moved the ISF parser into a class Parser, found in include/isfqt.h and src/isfqt.cpp
|
||||
* Added a dummy drawing-to-ISF method in class Parser.
|
||||
* Added multibyte encoding methods taking a DataSource.
|
||||
* Added float encoding method and fixed uint encoding method.
|
||||
* Fixed seeking bug in DataSource::append() methods.
|
||||
* Added DataSource::prepend() methods.
|
||||
* Started working on the ISF stream writer.
|
||||
|
||||
2009-08-05 (Adam)
|
||||
* Correct an issue with incorrect colours for strokes.
|
||||
|
||||
2009-08-04 (Adam)
|
||||
* Re-save test ISF data with official Microsoft Ink classes.
|
||||
* Add functions to convert between HiMetric units and pixels.
|
||||
* Antialiasing whilst drawing ISF to QPixmap.
|
||||
* Fix the drawing of ISF data to QPixmap: draw the lines correctly between points.
|
||||
|
||||
2009-08-04 (Valerio)
|
||||
* Fixed endianness problems when retrieving datastream bits with IsfData.
|
||||
* Added a new parameter to IsfData::atEnd() to check if the buffer was all used,
|
||||
including the current byte's bits.
|
||||
* Changed IsfData::seekByteBack() with seekRelative(), which makes more sense.
|
||||
* Added IsfData::skipToNextByte() to discard the current byte's bits and move on
|
||||
to the next byte.
|
||||
* Added Isf::Drawing method getPixmap() to convert a drawing to a QPixmap. Quite raw
|
||||
at the moment, but still.
|
||||
* Moved actual tag parsing into Isf::Drawing::parseTag().
|
||||
* Added a whole lot of tags to Isf::Tags. Now the parser is able to recognize the most
|
||||
useful tags (and mostly make use of their data).
|
||||
* Added a GUID mapping from the ISF specs, I doubt it'll ever be used though.
|
||||
* Added float decoding method to multibytecoding.cpp.
|
||||
* Added new test ISF files - more complex - and sorted all test ISF files by complexity.
|
||||
* Added new test application "test_decode", which displays a parsed ISF file.
|
||||
* Added pressure data recognization.
|
||||
* Added stroke description blocks recognization.
|
||||
* Fixed stroke payload parsing appearing when not needed (and wasting bytes).
|
||||
* Added a set of data structures to hold metrics and to better contain stroke attributes.
|
||||
* Reorganized the code of the Tags namespace, it's now a class with all static members;
|
||||
this simplifies quite a bit the passing of data between that and IsfDrawing.
|
||||
* Fixed bug in the GUID Table tag parser.
|
||||
* Added more compiler flags (and warnings) to ease debugging.
|
||||
* Added the infrastructure to link metrics and transforms data to the strokes.
|
||||
* Finally linked the drawing attributes and stroke info to the strokes' data:
|
||||
libisf-qt is now correctly decoding all tests (except test 6).
|
||||
* Fixed all compiler warnings and improved the debugging output.
|
||||
* Fixed point rendering didn't work when points are made by a line from a point
|
||||
to the same point.
|
||||
* Fixed broken main parser loop, continued forever.
|
||||
|
||||
2009-07-30 (Valerio)
|
||||
* Added IsfData class to read and write bit-by-bit an array of bytes.
|
||||
* Changed all the code to use it instead of plain QByteArrays.
|
||||
* Completed huffman decompression support.
|
||||
* Added algorithm data to help writing gorilla decompression.
|
||||
* Added tests for the IsfData class.
|
||||
* Added tag recognization.
|
||||
* Fixed some IsfData bugs.
|
||||
* Changed default CMake build type to debugfull.
|
||||
* Completed Gorilla decompression support.
|
||||
* Started tag parsing work, made basic analysis methods.
|
||||
* Made the ParserError enum (now IsfError) the library's error enumeration system.
|
||||
* Continued a bit the tag parsing work, added a couple tags and another testfile.
|
||||
|
||||
2009-07-08 (Adam)
|
||||
* Make decode(UInt|Int) take a reference as a position parameter. This
|
||||
reference is then incremented as the integer is decoded.
|
||||
* Re-work tests for IsfDrawing.
|
||||
* Create IsfDrawing::fromIsfData() static method to create a new IsfDrawing
|
||||
instance from a QByteArray containing ISF data.
|
||||
* Begin state matchine for ISF parser.
|
||||
* Add appropriate unit tests for ISF parser.
|
||||
* Added TODO file for libisf to keep track of things.
|
||||
|
||||
2009-06-30 (Adam)
|
||||
* Parse ISF version number properly; add unit tests for it.
|
||||
* Fix CMakeLists.txt to build with debug info by default
|
||||
* Moved format/types.(cpp|h) into the multibytecoding.(cpp|h) classes.
|
||||
Also moved these into libisf/ and deleted format/ directory.
|
||||
* Added Isf::encodeInt to encode a signed integer into multibyte format
|
||||
* Added test cases for the encode/decode functions.
|
||||
* Re-add libisf.h
|
||||
* Fix wrong e-mail addresses in copyright info.
|
||||
* Created a unit test framework for libisf-qt; run using make test
|
||||
* Created IsfDrawing class which will represent an ISF drawing.
|
||||
* Added some rudimentary unit tests for IsfDrawing
|
||||
@@ -0,0 +1,259 @@
|
||||
# Doxyfile 1.5.5-KDevelop
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = ISF-Qt
|
||||
PROJECT_NUMBER = 0.9
|
||||
OUTPUT_DIRECTORY = ./apidocs
|
||||
CREATE_SUBDIRS = YES
|
||||
OUTPUT_LANGUAGE = English
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF = "The $name class" \
|
||||
"The $name widget" \
|
||||
"The $name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = YES
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
QT_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
DETAILS_AT_TOP = NO
|
||||
INHERIT_DOCS = YES
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 2
|
||||
ALIASES =
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
SIP_SUPPORT = NO
|
||||
IDL_PROPERTY_SUPPORT = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
SUBGROUPING = YES
|
||||
TYPEDEF_HIDES_STRUCT = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_LOCAL_CLASSES = YES
|
||||
EXTRACT_LOCAL_METHODS = YES
|
||||
EXTRACT_ANON_NSPACES = YES
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = NO
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = YES
|
||||
SORT_GROUP_NAMES = NO
|
||||
SORT_BY_SCOPE_NAME = YES
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_TESTLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = NO
|
||||
SHOW_DIRECTORIES = NO
|
||||
SHOW_FILES = YES
|
||||
SHOW_NAMESPACES = YES
|
||||
FILE_VERSION_FILTER =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = YES
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = ./src ./include
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.cpp \
|
||||
*.h \
|
||||
*.dox
|
||||
RECURSIVE = YES
|
||||
EXCLUDE = *.moc \
|
||||
*.moc.cpp \
|
||||
*.moc.h
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS = */build*/*
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS = *
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = NO
|
||||
COLS_IN_ALPHA_INDEX = 4
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER = doc/apidocs_header.html
|
||||
HTML_FOOTER = doc/apidocs_footer.html
|
||||
HTML_STYLESHEET = doc/doxygen.css
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
GENERATE_HTMLHELP = NO
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
DISABLE_INDEX = YES
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_TREEVIEW = YES
|
||||
TREEVIEW_WIDTH = 250
|
||||
FORMULA_FONTSIZE = 10
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = a4wide
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
PDF_HYPERLINKS = NO
|
||||
USE_PDFLATEX = NO
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
XML_PROGRAMLISTING = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE = isf-qt.tag
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = NO
|
||||
MSCGEN_PATH =
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = YES
|
||||
DOT_FONTNAME = FreeSans
|
||||
DOT_FONTPATH =
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
GROUP_GRAPHS = YES
|
||||
UML_LOOK = NO
|
||||
TEMPLATE_RELATIONS = YES
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DIRECTORY_GRAPH = NO
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
DOT_GRAPH_MAX_NODES = 50
|
||||
MAX_DOT_GRAPH_DEPTH = 1000
|
||||
DOT_TRANSPARENT = NO
|
||||
DOT_MULTI_TARGETS = YES
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::additions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
||||
@@ -0,0 +1,163 @@
|
||||
|
||||
This file contains possibly useful stuff.
|
||||
|
||||
|
||||
|
||||
typedef struct _GUID
|
||||
{
|
||||
unsigned long Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[ 8 ];
|
||||
} GUID;
|
||||
#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
const GUID name DECLSPEC_HIDDEN; \
|
||||
const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
|
||||
DEFINE_GUID(GUID_LINENUMBER, 0xdbf29f2c, 0x5289, 0x4be8, 0xb3, 0xd8, 0x6e, 0xf6, 0x32, 0x46, 0x25, 0x3e);
|
||||
DEFINE_GUID(GUID_BOXNUMBER, 0x2c243e3a, 0xf733, 0x4eb6, 0xb1, 0xf8, 0xb5, 0xdc, 0x5c, 0x2c, 0x4c, 0xda);
|
||||
DEFINE_GUID(GUID_SEGMENTATION, 0xb3c0fe6c, 0xfb51, 0x4164, 0xba, 0x2f, 0x84, 0x4a, 0xf8, 0xf9, 0x83, 0xda);// {CA6F40DC-5292-452a-91FB-2181C0BEC0DE}
|
||||
DEFINE_GUID(GUID_HOTPOINT, 0xca6f40dc, 0x5292, 0x452a, 0x91, 0xfb, 0x21, 0x81, 0xc0, 0xbe, 0xc0, 0xde);// {BF0EEC4E-4B7D-47a9-8CFA-234DD24BD22A}
|
||||
DEFINE_GUID(GUID_MAX_STROKE_COUNT, 0xbf0eec4e, 0x4b7d, 0x47a9, 0x8c, 0xfa, 0x23, 0x4d, 0xd2, 0x4b, 0xd2, 0x2a);// {7DFE11A7-FB5D-4958-8765-154ADF0D833F}
|
||||
DEFINE_GUID(GUID_CONFIDENCELEVEL, 0x7dfe11a7, 0xfb5d, 0x4958, 0x87, 0x65, 0x15, 0x4a, 0xdf, 0xd, 0x83, 0x3f);// {8CC24B27-30A9-4b96-9056-2D3A90DA0727}
|
||||
DEFINE_GUID(GUID_LINEMETRICS, 0x8cc24b27, 0x30a9, 0x4b96, 0x90, 0x56, 0x2d, 0x3a, 0x90, 0xda, 0x7, 0x27);
|
||||
DEFINE_GUID(GUID_INRANGE, 0xdc00b1af, 0x7321, 0x4ac1, 0x91, 0x88, 0xe3, 0x20, 0x18, 0xeb, 0xb2, 0x3b);
|
||||
DEFINE_GUID(GUID_TOUCH, 0x65c98c60, 0xcd80, 0x447d, 0xb1, 0x29, 0x25, 0xf6, 0xe, 0x1d, 0x80, 0x5b);
|
||||
DEFINE_GUID(GUID_UNTOUCH, 0x378c85bb, 0x7118, 0x491e, 0x85, 0x16, 0xa7, 0x48, 0x2d, 0xb, 0x68, 0x3a);
|
||||
DEFINE_GUID(GUID_TAP, 0x9eaad4, 0xd133, 0x4ed2, 0xb1, 0x2c, 0x89, 0x1f, 0x8e, 0x82, 0x5e, 0x6a);
|
||||
DEFINE_GUID(GUID_QUALITY, 0xb7fe8008, 0x2df6, 0x4e1b, 0x8c, 0x43, 0xf5, 0xf1, 0x6, 0x8, 0x93, 0x2d);
|
||||
DEFINE_GUID(GUID_DATAVALID, 0xaacf46b5, 0xf107, 0x47b0, 0xb5, 0x77, 0xd9, 0x39, 0xc0, 0x53, 0xed, 0x41);
|
||||
DEFINE_GUID(GUID_TRANSDUCERINDEX, 0xa412b445, 0x7818, 0x4c83, 0x84, 0x55, 0x49, 0x29, 0xc8, 0x70, 0x76, 0x3b);
|
||||
DEFINE_GUID(GUID_TABLETFUNCTIONKEYS, 0xff3b8afe, 0x5f06, 0x494d, 0xa4, 0xf8, 0xd3, 0xe2, 0x85, 0xf9, 0x76, 0x30);
|
||||
DEFINE_GUID(GUID_PROGRAMCHANGEKEYS, 0x869c344a, 0x92a1, 0x4b6f, 0xb4, 0xbc, 0x3, 0x96, 0xc6, 0xa9, 0xf6, 0xaf);
|
||||
DEFINE_GUID(GUID_BATTERYSTRENGTH, 0x4ca0a0dc, 0x3549, 0x43f5, 0xa0, 0x32, 0x99, 0xf4, 0xe3, 0x3d, 0xf4, 0x90);
|
||||
DEFINE_GUID(GUID_INVERT, 0xc3aa28c8, 0x806b, 0x490c, 0xa8, 0x4a, 0xa7, 0x7a, 0xe7, 0x27, 0xc7, 0x18);
|
||||
DEFINE_GUID(GUID_BUTTON4,0x844b06d, 0xaa2c, 0x4c66, 0x99, 0x76, 0x88, 0xdd, 0x2a, 0x59, 0xe4, 0xf0);
|
||||
DEFINE_GUID(GUID_BUTTON5,0x944d1340, 0x2549, 0x4905, 0xbd, 0x54, 0x3e, 0xe3, 0x96, 0x3e, 0xe1, 0x57);
|
||||
DEFINE_GUID(GUID_BUTTON6,0xff19bd41, 0xa463, 0x4eaa, 0xaf, 0x10, 0xb5, 0x6, 0x48, 0x79, 0xe5, 0x4b);
|
||||
DEFINE_GUID(GUID_BUTTON7,0xdedaf13c, 0xb7dc, 0x423b, 0xb9, 0x7f, 0xa2, 0xbd, 0x68, 0xa2, 0xfd, 0x3d);
|
||||
DEFINE_GUID(GUID_BUTTON8,0xa6f70e64, 0x3a67, 0x4552, 0xa0, 0xc4, 0x17, 0x38, 0x4e, 0x49, 0x5a, 0x55);
|
||||
DEFINE_GUID(GUID_BUTTON9,0xbf55916c, 0xd6e6, 0x4fd3, 0x94, 0x62, 0x55, 0xd6, 0x9d, 0xb, 0xe7, 0x9c);
|
||||
DEFINE_GUID(GUID_BUTTON10,0x95f1b222, 0x1159, 0x4f9a, 0xb6, 0xe4, 0x6e, 0xde, 0xdf, 0xc7, 0x56, 0x9b);
|
||||
DEFINE_GUID(GUID_BUTTON11,0xf2dff7da, 0xf458, 0x4b61, 0xa2, 0x15, 0x62, 0xd0, 0x56, 0x48, 0x6, 0x53);
|
||||
DEFINE_GUID(GUID_BUTTON12,0x6a860858, 0x9b68, 0x4da6, 0xb3, 0x2a, 0x55, 0xe9, 0xe9, 0x75, 0xbe, 0xeb);
|
||||
DEFINE_GUID(GUID_BUTTON13,0x42ffb4d9, 0x7f95, 0x475e, 0x9c, 0x97, 0x82, 0xa0, 0x2b, 0xdc, 0x7e, 0xb6);
|
||||
DEFINE_GUID(GUID_BUTTON14,0x215008c8, 0xf09d, 0x48d7, 0x95, 0x2e, 0xc, 0x11, 0x8d, 0x6, 0xe8, 0xca);
|
||||
DEFINE_GUID(GUID_BUTTON15, 0xd1d1fa37, 0x1ee0, 0x4015, 0x9c, 0x33, 0x12, 0xcc, 0x42, 0x57, 0x60, 0x1);
|
||||
DEFINE_GUID(GUID_PEN_TIMESTAMP1, 0x413a7d1a, 0xeede, 0x45ce, 0xa4, 0x32, 0x80, 0x88, 0xca, 0x9e, 0x8e, 0x4a);
|
||||
DEFINE_GUID(GUID_PEN_TIMESTAMP2, 0x876c825, 0xcdc, 0x4395, 0xbf, 0x9b, 0x36, 0x7e, 0x69, 0x8a, 0x75, 0x56);
|
||||
DEFINE_GUID(GUID_WIDTH, 0xbaabe94d, 0x2712, 0x48f5, 0xbe, 0x9d, 0x8f, 0x8b, 0x5e, 0xa0, 0x71, 0x1a);
|
||||
DEFINE_GUID(GUID_HEIGHT, 0xe61858d2, 0xe447, 0x4218, 0x9d, 0x3f, 0x18, 0x86, 0x5c, 0x20, 0x3d, 0xf4);
|
||||
DEFINE_GUID(GUID_FINGERCONTACTCONFIDENCE, 0xe706c804, 0x57f0, 0x4f00, 0x8a, 0x0c, 0x85, 0x3d, 0x57, 0x78, 0x9b, 0xe9);
|
||||
DEFINE_GUID(GUID_TEMPID, 0x2585b91, 0x49b, 0x4750, 0x96, 0x15, 0xdf, 0x89, 0x48, 0xab, 0x3c, 0x9c);
|
||||
|
||||
const GUID FAR KNOWN_GUIDS[38] =
|
||||
{
|
||||
{ 0x598a6a8f, 0x52c0, 0x4ba0, { 0x93, 0xaf, 0xaf, 0x35, 0x74, 0x11, 0xa5, 0x61 } },
|
||||
{ 0xb53f9f75, 0x04e0, 0x4498, { 0xa7, 0xee, 0xc3, 0x0d, 0xbb, 0x5a, 0x90, 0x11 } },
|
||||
{ 0x735adb30, 0x0ebb, 0x4788, { 0xa0, 0xe4, 0x0f, 0x31, 0x64, 0x90, 0x05, 0x5d } },
|
||||
{ 0x6e0e07bf, 0xafe7, 0x4cf7, { 0x87, 0xd1, 0xaf, 0x64, 0x46, 0x20, 0x84, 0x18 } },
|
||||
{ 0x436510c5, 0xfed3, 0x45d1, { 0x8b, 0x76, 0x71, 0xd3, 0xea, 0x7a, 0x82, 0x9d } },
|
||||
{ 0x78a81b56, 0x0935, 0x4493, { 0xba, 0xae, 0x00, 0x54, 0x1a, 0x8a, 0x16, 0xc4 } },
|
||||
{ 0x7307502d, 0xf9f4, 0x4e18, { 0xb3, 0xf2, 0x2c, 0xe1, 0xb1, 0xa3, 0x61, 0x0c } },
|
||||
{ 0x6da4488b, 0x5244, 0x41ec, { 0x90, 0x5b, 0x32, 0xd8, 0x9a, 0xb8, 0x08, 0x09 } },
|
||||
{ 0x8b7fefc4, 0x96aa, 0x4bfe, { 0xac, 0x26, 0x8a, 0x5f, 0x0b, 0xe0, 0x7b, 0xf5 } },
|
||||
{ 0xa8d07b3a, 0x8bf0, 0x40b0, { 0x95, 0xa9, 0xb8, 0x0a, 0x6b, 0xb7, 0x87, 0xbf } },
|
||||
{ 0x0e932389, 0x1d77, 0x43af, { 0xac, 0x00, 0x5b, 0x95, 0x0d, 0x6d, 0x4b, 0x2d } },
|
||||
{ 0x029123b4, 0x8828, 0x410b, { 0xb2, 0x50, 0xa0, 0x53, 0x65, 0x95, 0xe5, 0xdc } },
|
||||
{ 0x82dec5c7, 0xf6ba, 0x4906, { 0x89, 0x4f, 0x66, 0xd6, 0x8d, 0xfc, 0x45, 0x6c } },
|
||||
{ 0x0d324960, 0x13b2, 0x41e4, { 0xac, 0xe6, 0x7a, 0xe9, 0xd4, 0x3d, 0x2d, 0x3b } },
|
||||
{ 0x7f7e57b7, 0xbe37, 0x4be1, { 0xa3, 0x56, 0x7a, 0x84, 0x16, 0x0e, 0x18, 0x93 } },
|
||||
{ 0x5d5d5e56, 0x6ba9, 0x4c5b, { 0x9f, 0xb0, 0x85, 0x1c, 0x91, 0x71, 0x4e, 0x56 } },
|
||||
{ 0x6a849980, 0x7c3a, 0x45b7, { 0xaa, 0x82, 0x90, 0xa2, 0x62, 0x95, 0x0e, 0x89 } },
|
||||
{ 0x33c1df83, 0xecdb, 0x44f0, { 0xb9, 0x23, 0xdb, 0xd1, 0xa5, 0xb2, 0x13, 0x6e } },
|
||||
{ 0x5329cda5, 0xfa5b, 0x4ed2, { 0xbb, 0x32, 0x83, 0x46, 0x01, 0x72, 0x44, 0x28 } },
|
||||
{ 0x002df9af, 0xdd8c, 0x4949, { 0xba, 0x46, 0xd6, 0x5e, 0x10, 0x7d, 0x1a, 0x8a } },
|
||||
{ 0x9d32b7ca, 0x1213, 0x4f54, { 0xb7, 0xe4, 0xc9, 0x05, 0x0e, 0xe1, 0x7a, 0x38 } },
|
||||
{ 0xe71caab9, 0x8059, 0x4c0d, { 0xa2, 0xdb, 0x7c, 0x79, 0x54, 0x47, 0x8d, 0x82 } },
|
||||
{ 0x5c0b730a, 0xf394, 0x4961, { 0xa9, 0x33, 0x37, 0xc4, 0x34, 0xf4, 0xb7, 0xeb } },
|
||||
{ 0x2812210f, 0x871e, 0x4d91, { 0x86, 0x07, 0x49, 0x32, 0x7d, 0xdf, 0x0a, 0x9f } },
|
||||
{ 0x8359a0fa, 0x2f44, 0x4de6, { 0x92, 0x81, 0xce, 0x5a, 0x89, 0x9c, 0xf5, 0x8f } },
|
||||
{ 0x4c4642dd, 0x479e, 0x4c66, { 0xb4, 0x40, 0x1f, 0xcd, 0x83, 0x95, 0x8f, 0x00 } },
|
||||
{ 0xce2d9a8a, 0xe58e, 0x40ba, { 0x93, 0xfa, 0x18, 0x9b, 0xb3, 0x90, 0x00, 0xae } },
|
||||
{ 0xc3c7480f, 0x5839, 0x46ef, { 0xa5, 0x66, 0xd8, 0x48, 0x1c, 0x7a, 0xfe, 0xc1 } },
|
||||
{ 0xea2278af, 0xc59d, 0x4ef4, { 0x98, 0x5b, 0xd4, 0xbe, 0x12, 0xdf, 0x22, 0x34 } },
|
||||
{ 0xb8630dc9, 0xcc5c, 0x4c33, { 0x8d, 0xad, 0xb4, 0x7f, 0x62, 0x2b, 0x8c, 0x79 } },
|
||||
{ 0x15e2f8e6, 0x6381, 0x4e8b, { 0xa9, 0x65, 0x01, 0x1f, 0x7d, 0x7f, 0xca, 0x38 } },
|
||||
{ 0x7066fbe4, 0x473e, 0x4675, { 0x9c, 0x25, 0x00, 0x26, 0x82, 0x9b, 0x40, 0x1f } },
|
||||
{ 0xbbc85b9a, 0xade6, 0x4093, { 0xb3, 0xbb, 0x64, 0x1f, 0xa1, 0xd3, 0x7a, 0x1a } },
|
||||
{ 0x039143d3, 0x78cb, 0x449c, { 0xa8, 0xe7, 0x67, 0xd1, 0x88, 0x64, 0xc3, 0x32 } },
|
||||
{ 0x67743782, 0x0ee5, 0x419a, { 0xa1, 0x2b, 0x27, 0x3a, 0x9e, 0xc0, 0x8f, 0x3d } },
|
||||
{ 0xf0720328, 0x663b, 0x418f, { 0x85, 0xa6, 0x95, 0x31, 0xae, 0x3e, 0xcd, 0xfa } },
|
||||
{ 0xa1718cdd, 0x0dac, 0x4095, { 0xa1, 0x81, 0x7b, 0x59, 0xcb, 0x10, 0x6b, 0xfb } },
|
||||
{ 0x810a74d2, 0x6ee2, 0x4e39, { 0x82, 0x5e, 0x6d, 0xef, 0x82, 0x6a, 0xff, 0xc5 } },
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
** InkDrawingAttributes **
|
||||
DISPID_DAHeight = 1
|
||||
DISPID_DAColor
|
||||
DISPID_DAWidth
|
||||
DISPID_DAFitToCurve
|
||||
DISPID_DAIgnorePressure
|
||||
DISPID_DAAntiAliased
|
||||
DISPID_DATransparency
|
||||
DISPID_DARasterOperation
|
||||
DISPID_DAPenTip
|
||||
DISPID_DAClone
|
||||
DISPID_DAExtendedProperties
|
||||
|
||||
|
||||
DISPID_DAFitToCurve
|
||||
DISPID_DAIgnorePressure
|
||||
DISPID_DAAntiAliased
|
||||
DISPID_DARasterOperation
|
||||
DISPID_DAPenTip
|
||||
DISPID_DAClone
|
||||
DISPID_DAExtendedProperties
|
||||
|
||||
|
||||
** InkBoundingBoxMode **
|
||||
IBBM_Default = 0,
|
||||
IBBM_NoCurveFit,
|
||||
IBBM_CurveFit,
|
||||
IBBM_PointsOnly,
|
||||
IBBM_Union
|
||||
|
||||
** InkRasterOperation **
|
||||
IRO_Black = 1,
|
||||
IRO_NotMergePen = 2,
|
||||
IRO_MaskNotPen = 3,
|
||||
IRO_NotCopyPen = 4,
|
||||
IRO_MaskPenNot = 5,
|
||||
IRO_Not = 6,
|
||||
IRO_XOrPen = 7,
|
||||
IRO_NotMaskPen = 8,
|
||||
IRO_MaskPen = 9,
|
||||
IRO_NotXOrPen = 10,
|
||||
IRO_NoOperation = 11,
|
||||
IRO_MergeNotPen = 12,
|
||||
IRO_CopyPen = 13,
|
||||
IRO_MergePenNot = 14,
|
||||
IRO_MergePen = 15,
|
||||
IRO_White = 16
|
||||
|
||||
|
||||
|
||||
If we scale the Ink Space Rect (canvas_) to or from HiMetric and using the metrics,
|
||||
do we get anything useful?
|
||||
- Valerio
|
||||
|
||||
|
||||
|
||||
To save received ISF from Messenger, put this code in src/chat/chatmaster.cpp at line 1324, below "case FORMAT_ISF:" but above the "break;":
|
||||
---
|
||||
QFile file( "~/output.isf" );
|
||||
file.open( QIODevice::WriteOnly );
|
||||
file.write( QByteArray::fromBase64( inkEncodedData ) );
|
||||
file.close();
|
||||
---
|
||||
You probably have to change the file path :P
|
||||
- Valerio
|
||||
@@ -0,0 +1,101 @@
|
||||
|
||||
About LibISF-Qt:
|
||||
============
|
||||
|
||||
LibISF-Qt is a Qt4 library made to encode and decode Microsoft's
|
||||
Ink Serialized Format (ISF) files.
|
||||
|
||||
Q. What is ISF?
|
||||
A. Ink Serialized Format is an open specification initially
|
||||
made by Microsoft, and then freed up with their "Open
|
||||
Specification Promise" initiative. ISF is a file format
|
||||
made to store handwriting very efficiently: an ISF file
|
||||
with a somewhat complex handwritten drawing takes less than
|
||||
1000 bytes. ISF supports metrics to adapt drawings to
|
||||
different form factors, bezier smoothing of drawn strokes,
|
||||
coloring, stroke size varying by pen pressure, linking
|
||||
in static data, and more.
|
||||
|
||||
Q. How does this library differ from aMSN's or Emesene's?
|
||||
A. It's written with Qt, which makes it easily portable on
|
||||
a lot of different architectures; also Qt's new licensing
|
||||
terms (LGPL) make it possible for everyone to use it
|
||||
easily.
|
||||
The library supports more ISF features than those two,
|
||||
since we've written it using the official specification.
|
||||
Moreover, aMSN's and Emesene's are not distributed by
|
||||
theirselves, ours is.
|
||||
|
||||
Q. What's the status of completion of the library, and which
|
||||
ISF features does/will support?
|
||||
A. Please see the TODO file to have an overview.
|
||||
|
||||
|
||||
|
||||
Compiling and installing:
|
||||
=================
|
||||
|
||||
LibISF-Qt uses CMake, a quick and clean alternative to Autotools.
|
||||
|
||||
First create a new directory within the one which contains this
|
||||
file (we'll have the library built there):
|
||||
$ mkdir build
|
||||
Enter it and run CMake:
|
||||
$ cd build
|
||||
$ cmake -D CMAKE_INSTALL_PREFIX=/usr ..
|
||||
Then, if everything went smoothly, build the library itself:
|
||||
$ make
|
||||
If everything went right again, install it:
|
||||
$ make install
|
||||
Aaand now you're set. :)
|
||||
|
||||
LibISF-Qt natively supports being bundled into other applications.
|
||||
Bundling means that the library does not get installed system-wide
|
||||
along with your application, but instead it gets statically linked
|
||||
into the application.
|
||||
The idea is that you copy the Isf-Qt source tree (with an SVN export
|
||||
or a Git submodule) into your project's tree, and then set up your
|
||||
CMake listfile to add this library as a subdirectory:
|
||||
> SET( ISFQT_IS_BUNDLED TRUE )
|
||||
> ADD_SUBDIRECTORY( contrib/isf-qt )
|
||||
And again, you're done!
|
||||
|
||||
|
||||
Documentation:
|
||||
=================
|
||||
|
||||
The ISF format is (albeit poorly*) documented: here's the
|
||||
sources we used.
|
||||
|
||||
- Original Microsoft ISF specification, made open with their
|
||||
Open Specification Promise:
|
||||
http://download.microsoft.com/download/0/B/E/0BE8BDD7-E5E8-422A-ABFD-4342ED7AD886/InkSerializedFormat(ISF)Specification.pdf
|
||||
- Quick explanation of the algorithms and other very useful
|
||||
details:
|
||||
http://synce.org/moin/FormatDocumentation/InkSerializedFormat
|
||||
- Original code from aMSN:
|
||||
https://amsn.svn.sourceforge.net/svnroot/amsn/trunk/amsn/utils/tclISF/src
|
||||
- Original code from emesene:
|
||||
https://emesene.svn.sourceforge.net/svnroot/emesene/trunk/emesene/pyisf
|
||||
|
||||
* = The specification was probably not meant for use by
|
||||
developers wanting to write an alternative implementation;
|
||||
there's a lot of guesswork to be done.
|
||||
|
||||
|
||||
|
||||
Licensing
|
||||
=========
|
||||
|
||||
Only the Oxygen icon, draw-eraser, is released under LGPL 3.0 (see http://www.oxygen-icons.org/). See COPYING_ICONS for full license text.
|
||||
|
||||
The remainder of ISF-qt is released under LGPL2.1. See COPYING for full license text.
|
||||
|
||||
|
||||
More Information
|
||||
================
|
||||
|
||||
For more information about LibISF-Qt, ask us:
|
||||
- forum board at http://www.kmess.org/board/
|
||||
- e-mail at project@kmess.org
|
||||
- IRC at #kmess on irc.freenode.net
|
||||
@@ -0,0 +1,42 @@
|
||||
libISF-Qt (Ink Serialized Format) TODO
|
||||
-----------------------------------
|
||||
|
||||
OUTSTANDING
|
||||
-----------
|
||||
- Bit Packing Byte codec:
|
||||
- Encoding
|
||||
- Bit Packing Word codec:
|
||||
- Encoding
|
||||
- Decoding
|
||||
- Bit Packing Long codec:
|
||||
- Encoding
|
||||
- Decoding
|
||||
- Read and write user-defined custom tags
|
||||
- Interpret, use and write pressure information
|
||||
- Associating text recognized by OCR to the drawn strokes
|
||||
- Alter stroke points
|
||||
|
||||
DONE
|
||||
----
|
||||
- State machine:
|
||||
- Parser
|
||||
- Writer
|
||||
- Multibyte values (int,uint,float):
|
||||
- Encoding
|
||||
- Decoding
|
||||
- Huffman codec:
|
||||
- Encoding
|
||||
- Decoding
|
||||
- Bit Packing codec:
|
||||
- Encoding
|
||||
- Decoding
|
||||
- Bit Packing Byte codec:
|
||||
- Decoding
|
||||
- Properly parse generated ISF test data
|
||||
- Generate new ISF data
|
||||
- Parse ISF data from WLM
|
||||
- Add new strokes to an IsfDrawing
|
||||
- Stroke attributes: width, colour, pressure
|
||||
- Export IsfDrawing to raw-isf, isf-base64 and fortified-gif
|
||||
- Export IsfDrawing to PNG
|
||||
- Delete strokes
|
||||
@@ -0,0 +1,19 @@
|
||||
# Find LibISF-Qt
|
||||
# Find the Ink Serialized Format library and includes
|
||||
# This module defines
|
||||
# ISFQT_FOUND - If false, do not try to use ISF-Qt
|
||||
# ISFQT_INCLUDE_DIR - where to find isfqtdrawing.h and friends
|
||||
# ISFQT_LIBRARIES - the libraries needed to use ISF-Qt
|
||||
# ISFQT_LIBRARY - Where to find the ISF-Qt library (generally not used)
|
||||
|
||||
FIND_PATH( ISFQT_INCLUDE_DIR isfqtdrawing.h /usr/include/isf-qt /usr/local/include/isf-qt )
|
||||
FIND_LIBRARY( ISFQT_LIBRARY NAMES isf-qt PATH /usr/lib /usr/local/lib )
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set ISFQT_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
INCLUDE( FindPackageHandleStandardArgs )
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS( ISFQT DEFAULT_MSG ISFQT_LIBRARY ISFQT_INCLUDE_DIR )
|
||||
|
||||
IF( ISFQT_FOUND )
|
||||
SET( ISFQT_LIBRARIES ${ISFQT_LIBRARY} )
|
||||
ENDIF( ISFQT_FOUND )
|
||||
@@ -0,0 +1,14 @@
|
||||
# IsfQtConfig.cmake is generated by CMake from cmake/IsfQtConfig.cmake.in.
|
||||
# Any changed value in this file will be overwritten by CMake.
|
||||
|
||||
# Set the version numbers
|
||||
SET( ISFQT_VERSION @ISFQT_VERSION@ )
|
||||
SET( ISFQT_VERSION_STRING "@ISFQT_VERSION_STRING@" )
|
||||
|
||||
# Set the directories
|
||||
IF( NOT ISFQT_INSTALL_DIR )
|
||||
SET( ISFQT_INSTALL_DIR "@CMAKE_INSTALL_PREFIX@" )
|
||||
ENDIF( NOT ISFQT_INSTALL_DIR )
|
||||
|
||||
SET( ISFQT_INCLUDE_DIR "@INCLUDE_INSTALL_DIR@" )
|
||||
SET( ISFQT_LIBRARY_DIR "@LIB_INSTALL_DIR@" )
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
# This file is included here because the standard FindGIF.cmake file
|
||||
# requires the CHECK_STRUCT_MEMBER macro. It wasn't on my PC, at least, so
|
||||
# I include a copy of the macro here for giflib to use.
|
||||
|
||||
MACRO (CHECK_STRUCT_MEMBER _STRUCT _MEMBER _HEADER _RESULT)
|
||||
SET(_INCLUDE_FILES)
|
||||
FOREACH (it ${_HEADER})
|
||||
SET(_INCLUDE_FILES "${_INCLUDE_FILES}#include <${it}>\n")
|
||||
ENDFOREACH (it)
|
||||
|
||||
SET(_CHECK_STRUCT_MEMBER_SOURCE_CODE "
|
||||
${_INCLUDE_FILES}
|
||||
int main()
|
||||
{
|
||||
${_STRUCT}* tmp;
|
||||
tmp->${_MEMBER};
|
||||
return 0;
|
||||
}
|
||||
")
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("${_CHECK_STRUCT_MEMBER_SOURCE_CODE}" ${_RESULT})
|
||||
|
||||
ENDMACRO (CHECK_STRUCT_MEMBER)
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* LibISF-Qt configuration file
|
||||
*
|
||||
* The value for these defines is controlled by CMake at configuration time.
|
||||
*/
|
||||
|
||||
// Define the application version
|
||||
#define ISFQT_VERSION "${ISFQT_VERSION}"
|
||||
|
||||
// Define if debug output should be enabled
|
||||
#if ${ISFQT_DEBUG_OUTPUT}
|
||||
#define ISFQT_DEBUG
|
||||
#endif
|
||||
|
||||
// Define if fortified-GIF support is enabled
|
||||
#define ISFQT_GIF_ENABLED ${GIF_ENABLED}
|
||||
@@ -0,0 +1,5 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file>pics/draw-eraser.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 722 B |
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
|
||||
<!--#########-->
|
||||
</div>
|
||||
|
||||
<address>
|
||||
<small>Generated on $datetime for $projectname by
|
||||
<a href="http://www.doxygen.org/index.html">
|
||||
<img src="doxygen.png" alt="doxygen" align="middle" border="0">
|
||||
</a>
|
||||
$doxygenversion
|
||||
</small>
|
||||
</address>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<title>$title</title>
|
||||
<link href="$relpath$doxygen.css" rel="stylesheet" type="text/css"></link>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<h1>ISF-Qt developer documentation
|
||||
<img class="logo" src="http://kmess.org/projects/isf-qt/isf-qt-logo.png" width="250" height="110" alt="">
|
||||
</h1>
|
||||
|
||||
<div id="menu">
|
||||
<ul>
|
||||
<li><a href="index.html">Main Page</a></li>
|
||||
<li><a href="modules.html">Modules</a></li>
|
||||
<li><a href="hierarchy.html">Class Hierarchy</a></li>
|
||||
<li><a href="annotated.html">Class List</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<!--#########-->
|
||||
@@ -0,0 +1,393 @@
|
||||
|
||||
/* standard fonts */
|
||||
|
||||
body,
|
||||
td,
|
||||
th {
|
||||
font-family: Geneva, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
body,
|
||||
td {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Courier New", monospace;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
/* main top title */
|
||||
font-size: 140%;
|
||||
padding: 5px;
|
||||
margin: 0;
|
||||
|
||||
background-color: #99ccff;
|
||||
color: #0071BB;
|
||||
}
|
||||
|
||||
img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#content h1
|
||||
{
|
||||
/* main content title */
|
||||
text-align: left;
|
||||
font-size: 160%;
|
||||
text-align: center;
|
||||
background-color: transparent;
|
||||
color: #000;
|
||||
padding-bottom: 15px;
|
||||
|
||||
/* center the element too so the float doesn't interfere */
|
||||
position: relative;
|
||||
margin-left: -25%;
|
||||
width: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
|
||||
h2 {
|
||||
/* section title */
|
||||
font-size: 140%;
|
||||
}
|
||||
|
||||
h2,
|
||||
h2 a {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
text-align: center;
|
||||
|
||||
/* center the element too so the float doesn't interfere */
|
||||
position: relative;
|
||||
margin-left: -25%;
|
||||
width: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
a, a:visited {
|
||||
color: #0000cc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:active, a:hover {
|
||||
color: #ff0000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
height: 1px;
|
||||
border: none;
|
||||
border-top: 1px solid black;
|
||||
}
|
||||
|
||||
#content li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
|
||||
/* layout of page */
|
||||
|
||||
html,body {
|
||||
background-color: #e6dede;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
background-color: #fff;
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
#content {
|
||||
padding: 0 10px 5px 10px;
|
||||
}
|
||||
|
||||
|
||||
/* menu and navigation */
|
||||
|
||||
.logo {
|
||||
float: right;
|
||||
display: inline;
|
||||
margin: 0 5px 5px 5px;
|
||||
}
|
||||
|
||||
#menu li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
float: left;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
#menu ul:after {
|
||||
clear: left;
|
||||
display: block;
|
||||
content: '.';
|
||||
line-height: 0%;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
overflow: none;
|
||||
}
|
||||
|
||||
/* for msie \*/
|
||||
* html #menu ul {
|
||||
height: 0.1em;
|
||||
}
|
||||
/* end of msie */
|
||||
|
||||
#menu ul,
|
||||
div.qindex {
|
||||
/* top index */
|
||||
background-color: #99ccff;
|
||||
border-bottom: 1px solid #000;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
div.nav {
|
||||
/* directory location */
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
div.nav a.el {
|
||||
font-style: italic;
|
||||
font-weight: normal;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
#menu a,
|
||||
div.qindex a {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
#menu a:hover,
|
||||
div.qindex a:hover {
|
||||
text-decoration: none;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
div.qindex a.qindexHL {
|
||||
/* highlighted menu option */
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* element reference in text. may occur everywhere */
|
||||
|
||||
a.el,
|
||||
a.el:active,
|
||||
a.el:visited { color: #5F2994; }
|
||||
|
||||
a.el {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.fragment {
|
||||
font-family: monospace
|
||||
}
|
||||
|
||||
pre.fragment {
|
||||
border: 1px solid #CCCCCC;
|
||||
background-color: #f5f5f5;
|
||||
margin: 4px 8px 4px 2px;
|
||||
padding: 4px 6px 4px 4px;
|
||||
}
|
||||
|
||||
td.indexkey,
|
||||
td.indexvalue {
|
||||
background-color: #eeeeff;
|
||||
background-color: #C9E2FF;
|
||||
padding: 2px 10px 2px 10px
|
||||
margin: 2px 0px 2px 0px;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
|
||||
td.indexkey {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
TR.memlist {
|
||||
/* separate member list page */
|
||||
background-color: #D6EEFF;
|
||||
}
|
||||
|
||||
/* not encountered
|
||||
|
||||
P.formulaDsp { text-align: center; }
|
||||
IMG.formulaDsp { }
|
||||
IMG.formulaInl { vertical-align: middle; }
|
||||
|
||||
*/
|
||||
|
||||
/* source code highlights */
|
||||
|
||||
SPAN.keyword { color: #008000 }
|
||||
SPAN.keywordtype { color: #604020 }
|
||||
SPAN.keywordflow { color: #e08000 }
|
||||
SPAN.comment { color: #800000 }
|
||||
SPAN.preprocessor { color: #806020 }
|
||||
SPAN.stringliteral { color: #002080 }
|
||||
SPAN.charliteral { color: #008080 }
|
||||
|
||||
/* Member description header */
|
||||
|
||||
.mdTable {
|
||||
border: 1px solid #868686;
|
||||
background-color: #C9E2FF;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.mdRow {
|
||||
padding: 4px 5px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.md {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td.mdname1 {
|
||||
font-weight: bold;
|
||||
color: #602020;
|
||||
}
|
||||
|
||||
td.mdname {
|
||||
/* parameter name in member description */
|
||||
font-weight: bold;
|
||||
color: #602020;
|
||||
}
|
||||
|
||||
.mdPrefix {
|
||||
/* template prefix in member description */
|
||||
color: #606060;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
|
||||
/* member summary table */
|
||||
|
||||
.mdescLeft,
|
||||
.mdescRight {
|
||||
padding: 0 8px 4px 8px;
|
||||
font-size: 80%;
|
||||
font-style: italic;
|
||||
background-color: #D3E9FF;
|
||||
border: 1px none #E0E0E0;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.memItemLeft,
|
||||
.memTemplItemLeft,
|
||||
.memItemRight,
|
||||
.memTemplItemRight {
|
||||
border-top: 1px solid #868686;
|
||||
background-color: #C9E2FF;
|
||||
font-size: 80%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.memItemLeft,
|
||||
.memTemplItemLeft {
|
||||
padding: 1px 0px 0px 8px;
|
||||
}
|
||||
|
||||
.memItemRight,
|
||||
.memTemplItemRight {
|
||||
padding: 1px 8px 0px 8px;
|
||||
}
|
||||
|
||||
.memTemplParams {
|
||||
/* template prefix in function list */
|
||||
border-top: 1px solid #868686;
|
||||
background-color: #C9E2FF;
|
||||
font-size: 80%;
|
||||
|
||||
padding: 1px 0px 0px 8px;
|
||||
margin: 4px;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
|
||||
/* member sections */
|
||||
|
||||
.memitem {
|
||||
padding-bottom: 2ex;
|
||||
}
|
||||
|
||||
.memproto {
|
||||
border-top: 1px dashed #333;
|
||||
padding: 1ex 4px 0 4px;
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.memproto td {
|
||||
color: #333;
|
||||
font-size: 110%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.memname {
|
||||
white-space: nowrap;
|
||||
font-size: 105%;
|
||||
}
|
||||
|
||||
.memdoc {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.memdoc p:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0.5ex;
|
||||
margin-right: 4em;
|
||||
}
|
||||
|
||||
.memdoc dl,
|
||||
.memdoc dt,
|
||||
.memdoc dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.paramkey {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.paramtype {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.paramname {
|
||||
color: #602020;
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.directory p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1 @@
|
||||
#include "isfinkcanvas.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "isfqt.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "isfqtdrawing.h"
|
||||
@@ -0,0 +1 @@
|
||||
#include "isfqtstroke.h"
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef ISFQT_QMATRIX_COMPAT_H
|
||||
#define ISFQT_QMATRIX_COMPAT_H
|
||||
|
||||
#include <QTransform>
|
||||
|
||||
using QMatrix = QTransform;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFINKCANVAS_H
|
||||
#define ISFINKCANVAS_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "isfqt.h"
|
||||
#include "isfqtdrawing.h"
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @class InkCanvas
|
||||
* @brief This is a control designed for the drawing and display of Ink.
|
||||
*
|
||||
* InkCanvas is used for drawing and displaying Ink. The currently displayed Ink drawing can be retrieved
|
||||
* or set using the drawing() and setDrawing() methods.
|
||||
*
|
||||
* To set the properties of the current pen, use the setPenColor(), setPenSize() and setPenType() methods.
|
||||
* The supported pen types are outlined in the PenType enumeration documentation.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* \code
|
||||
* InkCanvas* canvas = new Isf::InkCanvas( this );
|
||||
* canvas->setPenColor( Qt::blue );
|
||||
* canvas->setPenSize( 10 );
|
||||
*
|
||||
* // the pen is now 10 pixels thick and blue.
|
||||
*
|
||||
* canvas->setPenType( EraserPen );
|
||||
*
|
||||
* // now an eraser will be used and strokes can be erased individually.
|
||||
* \endcode
|
||||
*
|
||||
* To return the currently displayed Ink as a QImage, use image(). To return the raw ISF data, suitable
|
||||
* for saving to disk or sending over a network, use bytes().
|
||||
*
|
||||
* To write the ISF data directly to a QIODevice, such as a file, use the save() method.
|
||||
*
|
||||
* Connect to the inkChanged() signal to be notified when the drawing displayed on the canvas changes,
|
||||
* either through strokes being drawn or deleted, or the current drawing being changed.
|
||||
*
|
||||
* @author Adam Goossens (adam@kmess.org)
|
||||
*/
|
||||
class InkCanvas : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public: // public enumerations
|
||||
/**
|
||||
* The various types of pens supported by InkCanvas.
|
||||
*
|
||||
* The DrawingPen is a standard pen for drawing new strokes onto the canvas.
|
||||
* The pen size and color are controlled by setPenSize() and setPenColor().
|
||||
*
|
||||
* The EraserPen is used for erasing individual strokes. Pixel-by-pixel erase is not
|
||||
* yet supported.
|
||||
*/
|
||||
enum PenType
|
||||
{
|
||||
DrawingPen, ///< Used to draw Ink
|
||||
EraserPen ///< Used to erase by stroke
|
||||
};
|
||||
|
||||
public: // public constructors
|
||||
InkCanvas( QWidget* = 0 );
|
||||
~InkCanvas();
|
||||
|
||||
public: // public methods
|
||||
QByteArray bytes();
|
||||
Isf::Drawing* drawing();
|
||||
QImage image();
|
||||
bool isEmpty();
|
||||
QColor penColor();
|
||||
int penSize();
|
||||
PenType penType();
|
||||
void save( QIODevice&, bool = false );
|
||||
void setDrawing( Isf::Drawing* );
|
||||
QSize sizeHint() const override;
|
||||
|
||||
public slots:
|
||||
void clear();
|
||||
void setCanvasColor( QColor );
|
||||
void setPenColor( QColor );
|
||||
void setPenSize( int );
|
||||
void setPenType( PenType );
|
||||
|
||||
protected: // protected methods
|
||||
void mousePressEvent( QMouseEvent* ) override;
|
||||
void mouseMoveEvent( QMouseEvent* ) override;
|
||||
void mouseReleaseEvent( QMouseEvent* ) override;
|
||||
void paintEvent( QPaintEvent* ) override;
|
||||
void resizeEvent( QResizeEvent* ) override;
|
||||
|
||||
private: // private methods
|
||||
void drawLineTo( const QPoint& );
|
||||
void clearBuffer();
|
||||
void updateCursor();
|
||||
|
||||
private: // private attributes
|
||||
/// Color of the canvas background
|
||||
QColor canvasColor_;
|
||||
/// Color of current pen
|
||||
QColor color_;
|
||||
/// Cursor pixmap
|
||||
QPixmap cursorPixmap_;
|
||||
/// Current cursor
|
||||
QCursor cursor_;
|
||||
/// It's true if the erase brush was selected
|
||||
bool erasingImage_;
|
||||
/// Current drawing being manipulated
|
||||
Isf::Drawing* drawing_;
|
||||
/// Initial drawing, can be overridden with drawing_
|
||||
Isf::Drawing initialDrawing_;
|
||||
/// Last point where the mouse pointer was released
|
||||
QPoint lastPoint_;
|
||||
/// True if the user is painting
|
||||
bool scribbling_;
|
||||
/// Pen size, in pixels.
|
||||
int penSize_;
|
||||
/// Pen type
|
||||
PenType penType_;
|
||||
/// The current stroke being drawn
|
||||
Isf::Stroke* currentStroke_;
|
||||
/// The pixmap buffer where in progress strokes are drawn
|
||||
QPixmap bufferPixmap_;
|
||||
/// Cache pixmap so the Ink isn't redrawn on every mouse move
|
||||
QPixmap isfCachePixmap_;
|
||||
/// Dirty value used to update isf cache pixmap.
|
||||
bool drawingDirty_;
|
||||
|
||||
signals:
|
||||
/// Emitted when the ink representation is modified (stroke drawn,
|
||||
/// stroke deleted, current drawing changed).
|
||||
void inkChanged();
|
||||
};
|
||||
|
||||
|
||||
|
||||
}; // namespace Isf
|
||||
|
||||
|
||||
|
||||
#endif // ISFINKCANVAS_H
|
||||
@@ -0,0 +1,322 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFQT_H
|
||||
#define ISFQT_H
|
||||
|
||||
#include <QColor>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QMatrix>
|
||||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QSizeF>
|
||||
#include <QtGlobal>
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
// Forward declarations
|
||||
class Drawing;
|
||||
class StreamData;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Possible error return values returned by the ISF library.
|
||||
*/
|
||||
enum IsfError
|
||||
{
|
||||
ISF_ERROR_NONE = 0 ///< No error
|
||||
|
||||
/// Stream reader errors
|
||||
, ISF_ERROR_BAD_VERSION ///< Incompatible ISF version
|
||||
, ISF_ERROR_BAD_STREAMSIZE ///< Stream size of ISF data is too small
|
||||
, ISF_ERROR_INVALID_STREAM ///< The stream contains wrong or duplicated tags
|
||||
, ISF_ERROR_INVALID_PAYLOAD ///< A tag's payload was empty
|
||||
, ISF_ERROR_INVALID_BLOCK ///< A block had invalid contents
|
||||
|
||||
/// Other errors
|
||||
, ISF_ERROR_INTERNAL ///< An error within the code has occurred
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List of predefined packet properties.
|
||||
*
|
||||
* These are used with strokes and their attributes.
|
||||
* The Metrics structure defines how to interpret these properties.
|
||||
*/
|
||||
enum PacketProperty
|
||||
{
|
||||
KNOWN_GUID_BASE_INDEX = 50
|
||||
, GUID_X = KNOWN_GUID_BASE_INDEX + 0
|
||||
, GUID_Y = KNOWN_GUID_BASE_INDEX + 1
|
||||
, GUID_Z // = et cetera
|
||||
, GUID_PACKET_STATUS
|
||||
, GUID_TIMER_TICK
|
||||
, GUID_SERIAL_NUMBER
|
||||
, GUID_NORMAL_PRESSURE
|
||||
, GUID_TANGENT_PRESSURE
|
||||
, GUID_BUTTON_PRESSURE
|
||||
, GUID_X_TILT_ORIENTATION
|
||||
, GUID_Y_TILT_ORIENTATION // = KNOWN_GUID_BASE_INDEX + 10
|
||||
, GUID_AZIMUTH_ORIENTATION
|
||||
, GUID_ALTITUDE_ORIENTATION
|
||||
, GUID_TWIST_ORIENTATION
|
||||
, GUID_PITCH_ROTATION
|
||||
, GUID_ROLL_ROTATION
|
||||
, GUID_YAW_ROTATION
|
||||
, GUID_PEN_STYLE
|
||||
, GUID_COLORREF
|
||||
, GUID_PEN_WIDTH
|
||||
, GUID_PEN_HEIGHT // = KNOWN_GUID_BASE_INDEX + 20
|
||||
, GUID_PEN_TIP
|
||||
, GUID_DRAWING_FLAGS
|
||||
, GUID_CURSORID
|
||||
, GUID_WORD_ALTERNATES
|
||||
, GUID_CHAR_ALTERNATES
|
||||
, GUID_INKMETRICS
|
||||
, GUID_GUIDE_STRUCTURE
|
||||
, GUID_TIME_STAMP
|
||||
, GUID_LANGUAGE
|
||||
, GUID_TRANSPARENCY // = KNOWN_GUID_BASE_INDEX + 30
|
||||
, GUID_CURVE_FITTING_ERROR
|
||||
, GUID_RECO_LATTICE
|
||||
, GUID_CURSORDOWN
|
||||
, GUID_SECONDARYTIPSWITCH
|
||||
, GUID_BARRELDOWN
|
||||
, GUID_TABLETPICK
|
||||
, GUID_ROP // = KNOWN_GUID_BASE_INDEX + 37
|
||||
, KNOWN_GUID_LAST_INDEX
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Available pen tips.
|
||||
*/
|
||||
enum PenTip
|
||||
{
|
||||
Ball = 0 ///< The pen tip is a circle
|
||||
, Rectangle = 1 ///< The pen tip is a square or a rectangle
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Stroke drawing flags.
|
||||
*/
|
||||
enum StrokeFlag
|
||||
{
|
||||
FitToCurve = 0x0001 ///< Fit the lines between stroke points to Bezier curves
|
||||
, IgnorePressure = 0x0004 ///< Ignore the pressure level for this stroke
|
||||
, IsHighlighter = 0x0100 ///< This stroke is an highlighter stroke
|
||||
, IsRectangle = 0x0200 ///< Meaning unknown
|
||||
};
|
||||
Q_DECLARE_FLAGS( StrokeFlags, StrokeFlag );
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( StrokeFlags );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Units used for metric measurements.
|
||||
*
|
||||
* @see http://msdn.microsoft.com/en-us/library/ms840884.aspx
|
||||
*/
|
||||
enum MetricScale
|
||||
{
|
||||
UNIT_UNUSED = -1
|
||||
, UNIT_DEFAULT = 0
|
||||
, UNIT_INCH = 1
|
||||
, UNIT_CENTIMETER = 2
|
||||
, UNIT_DEGREE = 3
|
||||
, UNIT_RADIAN = 4
|
||||
, UNIT_SECOND = 5
|
||||
, UNIT_POUND = 6
|
||||
, UNIT_GRAM = 7
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A single metric.
|
||||
*
|
||||
* It is a set of values representing what kind of values some
|
||||
* type of measurement will assume.
|
||||
*/
|
||||
struct Metric
|
||||
{
|
||||
/// Constructor for invalid metrics
|
||||
Metric()
|
||||
{
|
||||
}
|
||||
/// Initialized constructor
|
||||
Metric( qint64 vMin, qint64 vMax, MetricScale vUnits, float vResolution )
|
||||
: min( vMin )
|
||||
, max( vMax )
|
||||
, units( vUnits )
|
||||
, resolution( vResolution )
|
||||
{
|
||||
}
|
||||
/// Quick comparison operator
|
||||
bool operator ==( const Metric& other )
|
||||
{
|
||||
return min == other.min
|
||||
&& max == other.max
|
||||
&& units == other.units
|
||||
&& resolution == other.resolution;
|
||||
}
|
||||
|
||||
/// Minimum value
|
||||
qint64 min;
|
||||
/// Maximum value
|
||||
qint64 max;
|
||||
/// Measurement unit
|
||||
MetricScale units;
|
||||
/// Resolution
|
||||
float resolution;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A table of metrics.
|
||||
*
|
||||
* The default metric values are hard-coded here, different values read from the stream
|
||||
* will override these.
|
||||
*/
|
||||
struct Metrics
|
||||
{
|
||||
/// Constructor
|
||||
Metrics()
|
||||
{
|
||||
items[ GUID_X ] = Metric( 0, 12699, UNIT_CENTIMETER, 1000 );
|
||||
items[ GUID_Y ] = Metric( 0, 9649, UNIT_CENTIMETER, 1000 );
|
||||
items[ GUID_Z ] = Metric( -1023, 1023, UNIT_CENTIMETER, 1000 );
|
||||
items[ GUID_PACKET_STATUS ] = Metric( 0, 1023, UNIT_DEFAULT, 1 );
|
||||
items[ GUID_TIMER_TICK ] = Metric( 0, 1023, UNIT_DEFAULT, 1 );
|
||||
items[ GUID_SERIAL_NUMBER ] = Metric( 0, 1023, UNIT_DEFAULT, 1 );
|
||||
items[ GUID_NORMAL_PRESSURE ] = Metric( 0, 3600, UNIT_DEGREE, 10 );
|
||||
items[ GUID_TANGENT_PRESSURE ] = Metric( 0, 3600, UNIT_DEGREE, 10 );
|
||||
items[ GUID_BUTTON_PRESSURE ] = Metric( 0, 3600, UNIT_DEGREE, 10 );
|
||||
items[ GUID_X_TILT_ORIENTATION ] = Metric( -900, 900, UNIT_DEGREE, 10 );
|
||||
items[ GUID_Y_TILT_ORIENTATION ] = Metric( 0, 3600, UNIT_DEGREE, 10 );
|
||||
items[ GUID_AZIMUTH_ORIENTATION ] = Metric( -1, -1, UNIT_UNUSED, -1 );
|
||||
items[ GUID_ALTITUDE_ORIENTATION ] = Metric( -1, -1, UNIT_UNUSED, -1 );
|
||||
items[ GUID_TWIST_ORIENTATION ] = Metric( -1, -1, UNIT_UNUSED, -1 );
|
||||
items[ GUID_PITCH_ROTATION ] = Metric( -1, -1, UNIT_UNUSED, -1 );
|
||||
items[ GUID_ROLL_ROTATION ] = Metric( -1, -1, UNIT_UNUSED, -1 );
|
||||
items[ GUID_YAW_ROTATION ] = Metric( -1, -1, UNIT_UNUSED, -1 );
|
||||
}
|
||||
|
||||
/// The list of metrics defined in this table
|
||||
QMap<int,Metric> items;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A single point within a stroke.
|
||||
*/
|
||||
struct Point
|
||||
{
|
||||
/// Empty constructor
|
||||
Point()
|
||||
: position( 0, 0 )
|
||||
, pressureLevel( 0 )
|
||||
{
|
||||
}
|
||||
/// Constructor with point only
|
||||
Point( const QPoint& point )
|
||||
: position( point )
|
||||
, pressureLevel( 0 )
|
||||
{
|
||||
}
|
||||
/// Constructor with point and pressure
|
||||
Point( const QPoint& point, const qint64 pressure )
|
||||
: position( point )
|
||||
, pressureLevel( pressure )
|
||||
{
|
||||
}
|
||||
/// Quick comparison operator
|
||||
bool operator ==( const Point& other )
|
||||
{
|
||||
return position == other.position
|
||||
&& pressureLevel == other.pressureLevel;
|
||||
}
|
||||
|
||||
/// coordinates
|
||||
QPoint position;
|
||||
/// Pressure information
|
||||
qint64 pressureLevel;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A list of Point items.
|
||||
*/
|
||||
typedef QList<Point> PointList;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Conversion factor to convert from HiMetric units to pixels.
|
||||
*/
|
||||
const qreal HiMetricToPixel = 26.4572454037811;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @class Stream
|
||||
* @brief The main class of the library.
|
||||
*
|
||||
* This class contains the only methods you need to transform ISF data
|
||||
* into strokes or pictures, and from strokes to ISF data.
|
||||
*/
|
||||
class Stream
|
||||
{
|
||||
|
||||
public: // Public static methods
|
||||
|
||||
static Drawing &reader( const QByteArray&, bool = false );
|
||||
static Drawing &readerGif( const QByteArray&, bool = false );
|
||||
static Drawing &readerPng( const QByteArray&, bool = false );
|
||||
static bool supportsGif();
|
||||
static QByteArray writer( const Drawing&, bool = false );
|
||||
static QByteArray writerGif( const Drawing&, bool = false );
|
||||
static QByteArray writerPng( const Drawing&, bool = false );
|
||||
|
||||
private: // Private static properties
|
||||
static StreamData* streamData_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,133 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFQTDRAWING_H
|
||||
#define ISFQTDRAWING_H
|
||||
|
||||
#include <IsfQt>
|
||||
#include <IsfQtStroke>
|
||||
|
||||
#include <QMap>
|
||||
#include <QMatrix>
|
||||
#include <QPaintDevice>
|
||||
#include <QUuid>
|
||||
#include <QPixmap>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class QByteArray;
|
||||
|
||||
class AttributeSet;
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @class Drawing
|
||||
* @brief This is a manipulable representation of an ISF stream.
|
||||
*
|
||||
* This class is a container for the data of ISF (Ink Serialized Format)
|
||||
* drawings. You can manipulate its contents, or create a new one and
|
||||
* fill it, then save it back to ISF format with the Isf::Stream methods.
|
||||
*
|
||||
* @see Isf::Stream
|
||||
* @author Adam Goossens (adam@kmess.org)
|
||||
* @author Valerio Pilo (valerio@kmess.org)
|
||||
*/
|
||||
class Drawing
|
||||
{
|
||||
friend class Stream;
|
||||
friend class TagsParser;
|
||||
friend class TagsWriter;
|
||||
|
||||
public: // public constructors
|
||||
Drawing();
|
||||
Drawing( const Drawing& );
|
||||
~Drawing();
|
||||
|
||||
public: // public state retrieval methods
|
||||
QRect boundingRect() const;
|
||||
void clear();
|
||||
IsfError error() const;
|
||||
qint32 indexOfStroke( const Stroke* ) const;
|
||||
bool isEmpty() const;
|
||||
bool isNull() const;
|
||||
QPixmap pixmap( const QColor = Qt::transparent );
|
||||
void setBoundingRect( QRect );
|
||||
QSize size() const;
|
||||
Stroke* stroke( quint32 );
|
||||
Stroke* strokeAtPoint( const QPoint& );
|
||||
const QList<Stroke*> strokes();
|
||||
|
||||
public: // public manipulation methods
|
||||
qint32 addStroke( Stroke* );
|
||||
qint32 addStroke( PointList = PointList() );
|
||||
bool deleteStroke( quint32 );
|
||||
bool deleteStroke( Stroke* );
|
||||
|
||||
private:
|
||||
void updateBoundingRect();
|
||||
|
||||
private: // Private properties
|
||||
/// Bounding rectangle of the drawing
|
||||
QRect boundingRect_;
|
||||
/// Cached bounding rectangle
|
||||
QRect cacheRect_;
|
||||
/// The cached pixmap
|
||||
QPixmap cachePixmap_;
|
||||
/// Virtual drawing canvas dimensions
|
||||
QRect canvas_;
|
||||
/// A list of strokes that need to be repainted.
|
||||
QList<Stroke*> changedStrokes_;
|
||||
/// Is the drawing dirty? i.e, requires repainting?
|
||||
bool dirty_;
|
||||
/// Last parsing error (if there is one)
|
||||
IsfError error_;
|
||||
/// List of registered GUIDs
|
||||
QList<QUuid> guids_;
|
||||
/// Whether the drawing contains X coordinates or not
|
||||
bool hasXData_;
|
||||
/// Whether the drawing contains Y coordinates or not
|
||||
bool hasYData_;
|
||||
/// Whether the drawing is invalid or valid
|
||||
bool isNull_;
|
||||
/// Maximum GUID available in the drawing
|
||||
quint64 maxGuid_;
|
||||
/// Maximum thickness of the strokes
|
||||
QSizeF maxPenSize_;
|
||||
/// List of strokes composing this drawing
|
||||
QList<Stroke*> strokes_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}; // namespace Isf
|
||||
|
||||
|
||||
|
||||
#endif // ISFQTDRAWING_H
|
||||
@@ -0,0 +1,117 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2010 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2010 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFQTSTROKE_H
|
||||
#define ISFQTSTROKE_H
|
||||
|
||||
#include <IsfQt>
|
||||
|
||||
#include <QColor>
|
||||
#include <QList>
|
||||
#include <QMatrix>
|
||||
#include <QRect>
|
||||
#include <QSizeF>
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class QPainterPath;
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class AttributeSet;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A pen stroke.
|
||||
*/
|
||||
class Stroke
|
||||
{
|
||||
public:
|
||||
Stroke();
|
||||
Stroke( const Stroke& );
|
||||
~Stroke();
|
||||
|
||||
void addPoint( Point );
|
||||
void addPoints( PointList );
|
||||
QRect boundingRect() const;
|
||||
QColor color() const;
|
||||
void finalize();
|
||||
StrokeFlags flags() const;
|
||||
bool hasPressureData() const;
|
||||
Metrics* metrics();
|
||||
QPainterPath painterPath();
|
||||
QSizeF penSize() const;
|
||||
PointList& points();
|
||||
void setColor( QColor );
|
||||
void setFlag( StrokeFlag, bool = true );
|
||||
void setFlags( StrokeFlags );
|
||||
void setMetrics( Metrics* );
|
||||
void setPenSize( QSizeF );
|
||||
void setTransform( QMatrix* );
|
||||
QMatrix* transform();
|
||||
|
||||
private:
|
||||
void bezierCalculateControlPoints();
|
||||
void bezierGetFirstControlPoints( double[], double*, int );
|
||||
|
||||
private:
|
||||
/// Bezier data
|
||||
QList<QPointF> bezierControlPoints1_;
|
||||
QList<QPointF> bezierControlPoints2_;
|
||||
QList<QPointF> bezierKnots_;
|
||||
/// Bounding rectangle of this stroke
|
||||
QRect boundingRect_;
|
||||
/// The stroke color, optionally with alpha channel
|
||||
QColor color_;
|
||||
/// Whether the stroke data needs to be analyzed or not
|
||||
bool finalized_;
|
||||
/// Mask of StrokeFlags, @see StrokeFlags
|
||||
StrokeFlags flags_;
|
||||
/// Whether the stroke contains pressure information or not
|
||||
bool hasPressureData_;
|
||||
/// Link to this stroke's metrics, if any
|
||||
Metrics* metrics_;
|
||||
/// Dimensions of the pencil in pixels
|
||||
QSizeF penSize_;
|
||||
/// List of points
|
||||
PointList points_;
|
||||
/// Link to this stroke's transformation, if any
|
||||
QMatrix* transform_;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}; // namespace Isf
|
||||
|
||||
|
||||
|
||||
#endif // ISFQTSTROKE_H
|
||||
@@ -0,0 +1,4 @@
|
||||
[Project]
|
||||
Name=isf-qt
|
||||
Manager=KDevCMakeManager
|
||||
VersionControl=
|
||||
@@ -0,0 +1,220 @@
|
||||
<?xml version = '1.0'?>
|
||||
<kdevelop>
|
||||
<general>
|
||||
<projectmanagement>KDevCustomProject</projectmanagement>
|
||||
<projectdirectory>.</projectdirectory>
|
||||
<absoluteprojectpath>false</absoluteprojectpath>
|
||||
<author>The KMess Development Team</author>
|
||||
<email>project@kmess.org</email>
|
||||
<version>0.1</version>
|
||||
<primarylanguage>C++</primarylanguage>
|
||||
<ignoreparts>
|
||||
<part>kdevtools</part>
|
||||
<part>kdevfileview</part>
|
||||
<part>kdevquickopen</part>
|
||||
<part>kdevabbrev</part>
|
||||
<part>kdevastyle</part>
|
||||
<part>kdevsnippet</part>
|
||||
<part>kdevctags2</part>
|
||||
<part>kdevfilelist</part>
|
||||
<part>kdevscripting</part>
|
||||
<part>kdevbookmarks</part>
|
||||
<part>kdevtexttools</part>
|
||||
<part>kdevdoxygen</part>
|
||||
<part>kdevdistpart</part>
|
||||
<part>kdevregexptest</part>
|
||||
</ignoreparts>
|
||||
<secondaryLanguages/>
|
||||
<projectname>isf-qt</projectname>
|
||||
<description>LibISF-Qt is a library to encode and decode Microsoft's Digital Ink format (Ink Serialized Format).</description>
|
||||
<defaultencoding/>
|
||||
<versioncontrol>kdevsubversion</versioncontrol>
|
||||
</general>
|
||||
<kdevcustomproject>
|
||||
<filelistdirectory>/home/valerio/kmess/trunk/contrib/isf-qt/</filelistdirectory>
|
||||
<run>
|
||||
<mainprogram>/home/valerio/kmess/trunk/contrib/isf-qt/build/tests/test_isfdrawing</mainprogram>
|
||||
<directoryradio>custom</directoryradio>
|
||||
<customdirectory>./build/</customdirectory>
|
||||
<programargs># tests/test1.isf</programargs>
|
||||
<terminal>false</terminal>
|
||||
<autocompile>false</autocompile>
|
||||
<envvars>
|
||||
<envvar value="en-US.UTF-8" name="LANG" />
|
||||
</envvars>
|
||||
<globaldebugarguments># tests/line2.isf</globaldebugarguments>
|
||||
<globalcwd>/home/valerio/kmess/trunk/contrib/isf-qt</globalcwd>
|
||||
<useglobalprogram>false</useglobalprogram>
|
||||
<autoinstall>false</autoinstall>
|
||||
<autokdesu>true</autokdesu>
|
||||
</run>
|
||||
<build>
|
||||
<buildtool>make</buildtool>
|
||||
<builddir>/home/valerio/kmess/trunk/contrib/isf-qt/build/</builddir>
|
||||
</build>
|
||||
<make>
|
||||
<abortonerror>false</abortonerror>
|
||||
<numberofjobs>2</numberofjobs>
|
||||
<dontact>false</dontact>
|
||||
<makebin>make</makebin>
|
||||
<selectedenvironment>default</selectedenvironment>
|
||||
<environments>
|
||||
<default/>
|
||||
</environments>
|
||||
<prio>0</prio>
|
||||
<defaulttarget/>
|
||||
<makeoptions/>
|
||||
</make>
|
||||
<filetypes>
|
||||
<filetype>*.h</filetype>
|
||||
<filetype>*.cpp</filetype>
|
||||
<filetype>CMakeLists.txt</filetype>
|
||||
<filetype>*.ui</filetype>
|
||||
</filetypes>
|
||||
<blacklist>
|
||||
<path>build</path>
|
||||
<path>build/CMakeFiles</path>
|
||||
<path>build/CMakeFiles/CompilerIdCXX</path>
|
||||
<path>build/CMakeFiles/CompilerIdCXX/CMakeCXXCompilerId.cpp</path>
|
||||
<path>build/isfqtconfig.h</path>
|
||||
<path>build/tests</path>
|
||||
<path>build/tests/decode</path>
|
||||
<path>build/tests/decode/ui_testdecode.h</path>
|
||||
</blacklist>
|
||||
<other>
|
||||
<prio>0</prio>
|
||||
<otherbin/>
|
||||
<defaulttarget/>
|
||||
<otheroptions/>
|
||||
<selectedenvironment>default</selectedenvironment>
|
||||
<environments>
|
||||
<Debug/>
|
||||
<Release/>
|
||||
<default/>
|
||||
</environments>
|
||||
</other>
|
||||
</kdevcustomproject>
|
||||
<kdevfilecreate>
|
||||
<filetypes/>
|
||||
<useglobaltypes>
|
||||
<type ext="ui" />
|
||||
<type ext="cpp" />
|
||||
<type ext="h" />
|
||||
</useglobaltypes>
|
||||
</kdevfilecreate>
|
||||
<kdevdoctreeview>
|
||||
<projectdoc>
|
||||
<userdocDir>html/</userdocDir>
|
||||
<apidocDir>html/</apidocDir>
|
||||
</projectdoc>
|
||||
<ignoreqt_xml/>
|
||||
<ignoredoxygen/>
|
||||
<ignorekdocs/>
|
||||
<ignoretocs/>
|
||||
<ignoredevhelp/>
|
||||
</kdevdoctreeview>
|
||||
<cppsupportpart>
|
||||
<filetemplates>
|
||||
<interfacesuffix>.h</interfacesuffix>
|
||||
<implementationsuffix>.cpp</implementationsuffix>
|
||||
</filetemplates>
|
||||
</cppsupportpart>
|
||||
<kdevcppsupport>
|
||||
<codecompletion>
|
||||
<includeGlobalFunctions>true</includeGlobalFunctions>
|
||||
<includeTypes>true</includeTypes>
|
||||
<includeEnums>true</includeEnums>
|
||||
<includeTypedefs>false</includeTypedefs>
|
||||
<automaticCodeCompletion>true</automaticCodeCompletion>
|
||||
<automaticArgumentsHint>true</automaticArgumentsHint>
|
||||
<automaticHeaderCompletion>true</automaticHeaderCompletion>
|
||||
<codeCompletionDelay>1500</codeCompletionDelay>
|
||||
<argumentsHintDelay>1500</argumentsHintDelay>
|
||||
<headerCompletionDelay>250</headerCompletionDelay>
|
||||
<showOnlyAccessibleItems>false</showOnlyAccessibleItems>
|
||||
<completionBoxItemOrder>0</completionBoxItemOrder>
|
||||
<howEvaluationContextMenu>true</howEvaluationContextMenu>
|
||||
<showCommentWithArgumentHint>true</showCommentWithArgumentHint>
|
||||
<statusBarTypeEvaluation>true</statusBarTypeEvaluation>
|
||||
<namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx=std</namespaceAliases>
|
||||
<processPrimaryTypes>true</processPrimaryTypes>
|
||||
<processFunctionArguments>true</processFunctionArguments>
|
||||
<preProcessAllHeaders>true</preProcessAllHeaders>
|
||||
<parseMissingHeadersExperimental>false</parseMissingHeadersExperimental>
|
||||
<resolveIncludePathsUsingMakeExperimental>false</resolveIncludePathsUsingMakeExperimental>
|
||||
<alwaysParseInBackground>true</alwaysParseInBackground>
|
||||
<usePermanentCaching>true</usePermanentCaching>
|
||||
<alwaysIncludeNamespaces>true</alwaysIncludeNamespaces>
|
||||
<includePaths>.;</includePaths>
|
||||
</codecompletion>
|
||||
<references/>
|
||||
<qt>
|
||||
<used>true</used>
|
||||
<version>4</version>
|
||||
<includestyle>4</includestyle>
|
||||
<root>/usr/lib/qt3</root>
|
||||
<designerintegration>ExternalDesigner</designerintegration>
|
||||
<qmake>/usr/bin/qmake</qmake>
|
||||
<designer>/usr/bin/designer</designer>
|
||||
<designerpluginpaths/>
|
||||
</qt>
|
||||
<creategettersetter>
|
||||
<prefixGet>get</prefixGet>
|
||||
<prefixSet>set</prefixSet>
|
||||
<prefixVariable/>
|
||||
<parameterName>value</parameterName>
|
||||
<inlineGet>true</inlineGet>
|
||||
<inlineSet>true</inlineSet>
|
||||
</creategettersetter>
|
||||
<splitheadersource>
|
||||
<enabled>false</enabled>
|
||||
<synchronize>true</synchronize>
|
||||
<orientation>Vertical</orientation>
|
||||
</splitheadersource>
|
||||
</kdevcppsupport>
|
||||
<kdevfileview>
|
||||
<groups>
|
||||
<group pattern="CMakeLists.txt;*.cmake;" name="CMake" />
|
||||
<group pattern="*.h;*.hxx" name="Header" />
|
||||
<group pattern="*.cpp;*.c;*.C;*.cxx" name="Sources" />
|
||||
<group pattern="*.ui" name="Qt Designer files" />
|
||||
<hidenonprojectfiles>true</hidenonprojectfiles>
|
||||
<hidenonlocation>false</hidenonlocation>
|
||||
</groups>
|
||||
<tree>
|
||||
<hidepatterns>*.o,*.lo,CVS,*~,cmake*</hidepatterns>
|
||||
<hidenonprojectfiles>true</hidenonprojectfiles>
|
||||
<showvcsfields>false</showvcsfields>
|
||||
</tree>
|
||||
</kdevfileview>
|
||||
<kdevdebugger>
|
||||
<general>
|
||||
<gdbpath/>
|
||||
<dbgshell/>
|
||||
<configGdbScript/>
|
||||
<runShellScript/>
|
||||
<runGdbScript/>
|
||||
<breakonloadinglibs>true</breakonloadinglibs>
|
||||
<separatetty>false</separatetty>
|
||||
<floatingtoolbar>false</floatingtoolbar>
|
||||
<raiseGDBOnStart>false</raiseGDBOnStart>
|
||||
</general>
|
||||
<display>
|
||||
<staticmembers>false</staticmembers>
|
||||
<demanglenames>true</demanglenames>
|
||||
<outputradix>10</outputradix>
|
||||
</display>
|
||||
</kdevdebugger>
|
||||
<kdevdocumentation>
|
||||
<projectdoc>
|
||||
<docsystem/>
|
||||
<docurl/>
|
||||
<usermanualurl/>
|
||||
</projectdoc>
|
||||
</kdevdocumentation>
|
||||
<ctagspart>
|
||||
<customArguments/>
|
||||
<customTagfilePath>./tags</customTagfilePath>
|
||||
<activeTagsFiles/>
|
||||
</ctagspart>
|
||||
</kdevelop>
|
||||
@@ -0,0 +1,77 @@
|
||||
|
||||
#### Source files definitions ####
|
||||
|
||||
SET( ISFQT_SOURCES
|
||||
data/algorithms/bitpacking.cpp
|
||||
data/algorithms/bitpacking_byte.cpp
|
||||
data/algorithms/huffman.cpp
|
||||
data/algorithms/deltatransform.cpp
|
||||
data/compression.cpp
|
||||
data/datasource.cpp
|
||||
data/multibytecoding.cpp
|
||||
isfqtdrawing.cpp
|
||||
tagsparser.cpp
|
||||
tagswriter.cpp
|
||||
isfqt.cpp
|
||||
isfinkcanvas.cpp
|
||||
isfqtstroke.cpp
|
||||
)
|
||||
|
||||
SET( ISFQT_PUBLIC_HEADERS
|
||||
../include/isfqt.h
|
||||
../include/isfqtdrawing.h
|
||||
../include/isfinkcanvas.h
|
||||
../include/isfqtstroke.h
|
||||
../include/IsfQtDrawing
|
||||
../include/IsfInkCanvas
|
||||
../include/IsfQtStroke
|
||||
../include/IsfQt
|
||||
)
|
||||
|
||||
SET( ISFQT_LIBS
|
||||
Qt6::Core
|
||||
Qt6::Core5Compat
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
SET( ISFQT_RCCS
|
||||
../data/isfqtresources.qrc
|
||||
)
|
||||
|
||||
IF( GIF_FOUND )
|
||||
INCLUDE_DIRECTORIES( ${GIF_INCLUDE_DIR} )
|
||||
SET( ISFQT_LIBS ${ISFQT_LIBS} ${GIF_LIBRARIES} )
|
||||
ENDIF( GIF_FOUND )
|
||||
|
||||
|
||||
#### Compilation ####
|
||||
|
||||
QT6_WRAP_CPP( MOC_SRCS ../include/isfinkcanvas.h )
|
||||
|
||||
QT6_ADD_RESOURCES( ISFQT_RCC_SRCS ${ISFQT_RCCS} )
|
||||
|
||||
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
|
||||
# Do not install the library if we're bundling it into an application
|
||||
IF( ISFQT_IS_BUNDLED )
|
||||
ADD_LIBRARY( isf-qt STATIC ${ISFQT_SOURCES} ${MOC_SRCS} ${ISFQT_RCC_SRCS} )
|
||||
ELSE()
|
||||
ADD_LIBRARY( isf-qt SHARED ${ISFQT_SOURCES} ${MOC_SRCS} ${ISFQT_RCC_SRCS} )
|
||||
ENDIF()
|
||||
|
||||
TARGET_LINK_LIBRARIES( isf-qt ${ISFQT_LIBS} )
|
||||
|
||||
SET_TARGET_PROPERTIES( isf-qt PROPERTIES
|
||||
VERSION ${ISFQT_VERSION}
|
||||
SOVERSION ${ISFQT_VERSION} )
|
||||
|
||||
#### Installation ####
|
||||
|
||||
# Do not install the library if we're bundling it in KMess
|
||||
IF( NOT ISFQT_IS_BUNDLED )
|
||||
INSTALL( TARGETS isf-qt LIBRARY
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/lib" )
|
||||
INSTALL( FILES ${ISFQT_PUBLIC_HEADERS}
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/include/isf-qt" )
|
||||
ENDIF()
|
||||
@@ -0,0 +1,196 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008-2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
#include "bitpacking.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
using namespace Isf::Compress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the most appropriate block size for the given data.
|
||||
*
|
||||
* @param data Data to analyze
|
||||
* @return Block size
|
||||
*/
|
||||
quint8 BitPackingAlgorithm::blockSize( const QList<qint64>& data )
|
||||
{
|
||||
quint8 blockSize = 0;
|
||||
qint64 num;
|
||||
|
||||
for( qint32 index = 0; index < data.size(); ++index )
|
||||
{
|
||||
num = data[ index ];
|
||||
|
||||
if( num < 0 )
|
||||
{
|
||||
// We need the positive numbers: the +1 is for the sign bit
|
||||
num += - (num + 1);
|
||||
}
|
||||
|
||||
// Shift right the value by blockSize bits until it becomes zero:
|
||||
// we need to detect the maximum bitwise size required to store
|
||||
// the numbers in the data array.
|
||||
num >>= blockSize;
|
||||
while( num )
|
||||
{
|
||||
++blockSize;
|
||||
num >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The sign bit always takes up a bit
|
||||
++blockSize;
|
||||
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compress data with the Bit Packing algorithm.
|
||||
*
|
||||
* @param encodedData Byte array where to store the compressed data
|
||||
* @param blockSize Block size to use for compression
|
||||
* @param source List of values to compress
|
||||
* @return bool
|
||||
*/
|
||||
bool BitPackingAlgorithm::deflate( QByteArray& encodedData, quint8 blockSize, const QList<qint64>& source )
|
||||
{
|
||||
if( blockSize > 64 )
|
||||
{
|
||||
qWarning() << "A block size of" << blockSize << "is too high!";
|
||||
blockSize = 64; // Fuck it :P
|
||||
}
|
||||
|
||||
quint8 blockSizeLeft; // Remaining bits to encode of the current value
|
||||
quint8 freeBits = 8; // Free bits of the current byte, in LSB order
|
||||
quint8 signMask = 1 << ( blockSize - 1 ); // Mask to add the sign bit
|
||||
|
||||
quint8 byte = 0; // Byte to add to the array
|
||||
qint64 currentValue;
|
||||
|
||||
for( qint64 index = 0; index < source.count(); ++index )
|
||||
{
|
||||
currentValue = source[ index ];
|
||||
|
||||
if( currentValue < 0 )
|
||||
{
|
||||
currentValue |= signMask;
|
||||
}
|
||||
|
||||
blockSizeLeft = blockSize;
|
||||
|
||||
if( freeBits >= blockSizeLeft )
|
||||
{
|
||||
freeBits -= blockSizeLeft;
|
||||
byte |= currentValue << freeBits;
|
||||
|
||||
encodedData.append( byte );
|
||||
|
||||
byte = 0;
|
||||
freeBits = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fill the current byte
|
||||
quint64 mask = 0xFFFFFFFF >> ( 32 - blockSizeLeft );
|
||||
byte |= currentValue >> ( blockSizeLeft - freeBits );
|
||||
|
||||
encodedData.append( byte );
|
||||
|
||||
byte = 0;
|
||||
blockSizeLeft -= freeBits;
|
||||
mask >>= freeBits;
|
||||
currentValue &= mask;
|
||||
|
||||
// Then fill all the next bytes required to encode the current value,
|
||||
// except the last one
|
||||
while( blockSizeLeft > 8 )
|
||||
{
|
||||
blockSizeLeft -= 8;
|
||||
encodedData.append( currentValue >> blockSizeLeft );
|
||||
|
||||
mask >>= 8;
|
||||
currentValue &= mask;
|
||||
}
|
||||
|
||||
// Finally, add the last byte
|
||||
freeBits = 8 - blockSizeLeft;
|
||||
encodedData.append( currentValue << freeBits );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decompress data with the Bit Packing algorithm.
|
||||
*
|
||||
* @param source Data source where to read the compressed bytes
|
||||
* @param length Number of items to read
|
||||
* @param blockSize Block size to use for compression
|
||||
* @param decodedData List where to place decompressed values
|
||||
* @return bool
|
||||
*/
|
||||
bool BitPackingAlgorithm::inflate( DataSource* source, quint64 length, quint8 blockSize, QList<qint64>& decodedData )
|
||||
{
|
||||
if( blockSize > 64 )
|
||||
{
|
||||
qWarning() << "A block size of" << blockSize << "is too high!";
|
||||
blockSize = 64; // Fuck it :P
|
||||
}
|
||||
|
||||
if( source->atEnd() )
|
||||
{
|
||||
qWarning() << "Cannot inflate: no more bits available!";
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 value;
|
||||
quint32 index = 0;
|
||||
quint64 signMask = (quint64)( 0xFFFFFFFF * pow( 2, blockSize - 1 ) ) & 0xFFFFFFFF;
|
||||
|
||||
while( index++ < length )
|
||||
{
|
||||
value = source->getBits( blockSize );
|
||||
|
||||
// If the mask matches, the sign bit is set, so ORing value and mask will
|
||||
// set all leftmost bits to 1, making it a real 64bit signed integer
|
||||
if( value & signMask )
|
||||
{
|
||||
value |= signMask;
|
||||
}
|
||||
|
||||
decodedData.append( value );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFCOMPRESSION_BITPACKING_H
|
||||
#define ISFCOMPRESSION_BITPACKING_H
|
||||
|
||||
#include "../datasource.h"
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
namespace BitPackingAlgorithm
|
||||
{
|
||||
|
||||
quint8 blockSize( const QList<qint64>& data );
|
||||
bool deflate( QByteArray& encodedData, quint8 blockSize, const QList<qint64>& source );
|
||||
bool inflate( DataSource* source, quint64 length, quint8 blockSize, QList<qint64>& decodedData );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,202 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008-2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
#include "bitpacking_byte.h"
|
||||
#include "bitpacking_table.h"
|
||||
|
||||
|
||||
using namespace Isf::Compress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the most appropriate index for the given data.
|
||||
*
|
||||
* @param data Data to analyze
|
||||
* @return Index value
|
||||
*/
|
||||
quint8 BitPackingByteAlgorithm::index( const QList<qint64>& data )
|
||||
{
|
||||
Q_UNUSED( data );
|
||||
|
||||
quint8 index = 0;
|
||||
/*
|
||||
qint64 num;
|
||||
|
||||
for( qint32 index = 0; index < data.size(); ++index )
|
||||
{
|
||||
num = data[ index ];
|
||||
|
||||
if( num < 0 )
|
||||
{
|
||||
// We need the positive numbers: the +1 is for the sign bit
|
||||
num += - (num + 1);
|
||||
}
|
||||
|
||||
// Shift right the value by index bits until it becomes zero:
|
||||
// we need to detect the maximum bitwise size required to store
|
||||
// the numbers in the data array.
|
||||
num >>= index;
|
||||
while( num )
|
||||
{
|
||||
++index;
|
||||
num >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// The sign bit always takes up a bit
|
||||
++index;
|
||||
*/
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compress data with the Bit Packing algorithm.
|
||||
*
|
||||
* @param encodedData Byte array where to store the compressed data
|
||||
* @param index Index to use for compression
|
||||
* @param source List of values to compress
|
||||
* @return bool
|
||||
*/
|
||||
bool BitPackingByteAlgorithm::deflate( QByteArray& encodedData, quint8 index, const QList<qint64>& source )
|
||||
{
|
||||
Q_UNUSED( encodedData );
|
||||
Q_UNUSED( index );
|
||||
Q_UNUSED( source );
|
||||
|
||||
/*
|
||||
if( blockSize > 64 )
|
||||
{
|
||||
qWarning() << "A block size of" << blockSize << "is too high!";
|
||||
blockSize = 64; // Fuck it :P
|
||||
}
|
||||
|
||||
quint8 blockSizeLeft; // Remaining bits to encode of the current value
|
||||
quint8 freeBits = 8; // Free bits of the current byte, in LSB order
|
||||
quint8 signMask = 1 << ( blockSize - 1 ); // Mask to add the sign bit
|
||||
|
||||
quint8 byte = 0; // Byte to add to the array
|
||||
qint64 currentValue;
|
||||
|
||||
for( qint64 index = 0; index < source.count(); ++index )
|
||||
{
|
||||
currentValue = source[ index ];
|
||||
|
||||
if( currentValue < 0 )
|
||||
{
|
||||
currentValue |= signMask;
|
||||
}
|
||||
|
||||
blockSizeLeft = blockSize;
|
||||
|
||||
if( freeBits >= blockSizeLeft )
|
||||
{
|
||||
freeBits -= blockSizeLeft;
|
||||
byte |= currentValue << freeBits;
|
||||
|
||||
encodedData.append( byte );
|
||||
|
||||
byte = 0;
|
||||
freeBits = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fill the current byte
|
||||
quint64 mask = 0xFFFFFFFF >> ( 32 - blockSizeLeft );
|
||||
byte |= currentValue >> ( blockSizeLeft - freeBits );
|
||||
|
||||
encodedData.append( byte );
|
||||
|
||||
byte = 0;
|
||||
blockSizeLeft -= freeBits;
|
||||
mask >>= freeBits;
|
||||
currentValue &= mask;
|
||||
|
||||
// Then fill all the next bytes required to encode the current value,
|
||||
// except the last one
|
||||
while( blockSizeLeft > 8 )
|
||||
{
|
||||
blockSizeLeft -= 8;
|
||||
encodedData.append( currentValue >> blockSizeLeft );
|
||||
|
||||
mask >>= 8;
|
||||
currentValue &= mask;
|
||||
}
|
||||
|
||||
// Finally, add the last byte
|
||||
freeBits = 8 - blockSizeLeft;
|
||||
encodedData.append( currentValue << freeBits );
|
||||
}
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decompress data with the Bit Packing Byte algorithm.
|
||||
*
|
||||
* @param source Data source where to read the compressed bytes
|
||||
* @param length Number of items to read
|
||||
* @param index Index to use for compression
|
||||
* @param decodedData List where to place decompressed values
|
||||
* @return bool
|
||||
*/
|
||||
bool BitPackingByteAlgorithm::inflate( DataSource* source, quint64 length, quint8 index, QList<qint64>& decodedData )
|
||||
{
|
||||
if( index > 24 )
|
||||
{
|
||||
qWarning() << "An index of" << index << "is too high!";
|
||||
index = 24; // Fuck it :P
|
||||
}
|
||||
|
||||
if( source->atEnd() )
|
||||
{
|
||||
qWarning() << "Cannot inflate: no more bits available!";
|
||||
return true;
|
||||
}
|
||||
|
||||
quint8 cBits = BitPackingAlgorithm::bitLookUpTable_[ index ][ CBITS ];
|
||||
// quint8 cPads = BitPackingAlgorithm::bitLookUpTable_[ index ][ CPADS ];
|
||||
quint16 count = ( length * 8 / cBits ); // Number of items in the compressed array
|
||||
|
||||
bool ok;
|
||||
quint64 pos = 0;
|
||||
while( pos++ < count )
|
||||
{
|
||||
quint8 byte = source->getBits( cBits, &ok );
|
||||
|
||||
if( ! ok )
|
||||
{
|
||||
qWarning() << "Decompression failure!";
|
||||
return false;
|
||||
}
|
||||
|
||||
decodedData.append( byte );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFCOMPRESSION_BITPACKING_BYTE_H
|
||||
#define ISFCOMPRESSION_BITPACKING_BYTE_H
|
||||
|
||||
#include "../datasource.h"
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
namespace BitPackingByteAlgorithm
|
||||
{
|
||||
|
||||
quint8 index( const QList<qint64>& data );
|
||||
bool deflate( QByteArray& encodedData, quint8 index, const QList<qint64>& source );
|
||||
bool inflate( DataSource* source, quint64 length, quint8 index, QList<qint64>& decodedData );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFCOMPRESSION_BITPACKING_H
|
||||
#define ISFCOMPRESSION_BITPACKING_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
|
||||
#define CBITS 0
|
||||
#define CPADS 1
|
||||
|
||||
|
||||
namespace Isf {
|
||||
namespace Compress {
|
||||
namespace BitPackingAlgorithm {
|
||||
|
||||
/// Table used to compute the length of bit-packed values and their remainders.
|
||||
quint8 bitLookUpTable_[ 48 ][ 2 ] =
|
||||
{
|
||||
// { cBits, cPads }
|
||||
{ 8, 0 }, // index 0
|
||||
{ 1, 0 },
|
||||
{ 1, 1 },
|
||||
{ 1, 2 },
|
||||
{ 1, 3 },
|
||||
{ 1, 4 },
|
||||
{ 1, 5 },
|
||||
{ 1, 6 },
|
||||
{ 1, 7 },
|
||||
{ 2, 0 },
|
||||
{ 2, 1 }, // index 10
|
||||
{ 2, 2 },
|
||||
{ 2, 3 },
|
||||
{ 3, 0 },
|
||||
{ 3, 1 },
|
||||
{ 3, 2 },
|
||||
{ 4, 0 },
|
||||
{ 4, 1 },
|
||||
{ 5, 0 },
|
||||
{ 5, 1 },
|
||||
{ 6, 0 }, // index 20
|
||||
{ 6, 1 },
|
||||
{ 7, 0 },
|
||||
{ 7, 1 },
|
||||
{ 8, 0 },
|
||||
{ 9, 0 },
|
||||
{ 10, 0 },
|
||||
{ 11, 0 },
|
||||
{ 12, 0 },
|
||||
{ 13, 0 },
|
||||
{ 14, 0 }, // index 30
|
||||
{ 15, 0 },
|
||||
{ 16, 0 },
|
||||
{ 17, 0 },
|
||||
{ 18, 0 },
|
||||
{ 19, 0 },
|
||||
{ 20, 0 },
|
||||
{ 21, 0 },
|
||||
{ 22, 0 },
|
||||
{ 23, 0 },
|
||||
{ 24, 0 }, // index 40
|
||||
{ 25, 0 },
|
||||
{ 26, 0 },
|
||||
{ 27, 0 },
|
||||
{ 28, 0 },
|
||||
{ 29, 0 },
|
||||
{ 30, 0 },
|
||||
{ 31, 0 } // index 47
|
||||
};
|
||||
|
||||
} // Namespace BitPackingAlgorithm
|
||||
} // Namespace Compress
|
||||
} // Namespace Isf
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "deltatransform.h"
|
||||
|
||||
|
||||
using namespace Isf::Compress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Perform a delta-delta transformation on data to be deflated.
|
||||
*
|
||||
* @param data Data to transform - the list will be modified.
|
||||
* @return bool
|
||||
*/
|
||||
bool Delta::transform( QList<qint64>& data )
|
||||
{
|
||||
qint64 currentDelta = 0;
|
||||
qint64 previousDelta = 0;
|
||||
|
||||
for( qint64 index = 0; index < data.size(); ++index )
|
||||
{
|
||||
qint64 currentItem = data[ index ];
|
||||
|
||||
data[ index ] = data[ index ] + previousDelta - ( currentDelta * 2 );
|
||||
|
||||
previousDelta = currentDelta;
|
||||
currentDelta = currentItem;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Perform a delta-delta inverse transformation on just-inflated data.
|
||||
*
|
||||
* @param data Data to transform - the list will be modified.
|
||||
* @return bool
|
||||
*/
|
||||
bool Delta::inverseTransform( QList<qint64>& data )
|
||||
{
|
||||
qint64 currentDelta = 0;
|
||||
qint64 previousDelta = 0;
|
||||
|
||||
for( qint64 index = 0; index < data.size(); ++index )
|
||||
{
|
||||
qint64 delta = ( currentDelta * 2 ) - previousDelta + data[ index ];
|
||||
|
||||
previousDelta = currentDelta;
|
||||
currentDelta = delta;
|
||||
|
||||
data[ index ] = delta;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISF_COMPRESS_DELTATRANSFORM_H
|
||||
#define ISF_COMPRESS_DELTATRANSFORM_H
|
||||
|
||||
#include <QList>
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
namespace Delta
|
||||
{
|
||||
|
||||
bool transform( QList<qint64>& data );
|
||||
bool inverseTransform( QList<qint64>& data );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,284 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "huffman.h"
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#define HUFFMAN_BASES_NUM 8
|
||||
#define HUFFMAN_BASE_SIZE 11
|
||||
|
||||
|
||||
using namespace Isf::Compress;
|
||||
|
||||
|
||||
/// Bit amounts.
|
||||
const int bitAmounts_[HUFFMAN_BASES_NUM][HUFFMAN_BASE_SIZE] =
|
||||
{
|
||||
{0, 1, 2, 4, 6, 8, 12, 16, 24, 32, -1},
|
||||
{0, 1, 1, 2, 4, 8, 12, 16, 24, 32, -1},
|
||||
{0, 1, 1, 1, 2, 4, 8, 14, 22, 32, -1},
|
||||
{0, 2, 2, 3, 5, 8, 12, 16, 24, 32, -1},
|
||||
{0, 3, 4, 5, 8, 12, 16, 24, 32, -1, -1},
|
||||
{0, 4, 6, 8, 12, 16, 24, 32, -1, -1, -1},
|
||||
{0, 6, 8, 12, 16, 24, 32, -1, -1, -1, -1},
|
||||
{0, 7, 8, 12, 16, 24, 32, -1, -1, -1, -1},
|
||||
};
|
||||
|
||||
|
||||
/// Huffman codec value bases.
|
||||
const int huffmanBases_[HUFFMAN_BASES_NUM][HUFFMAN_BASE_SIZE] =
|
||||
{
|
||||
{0, 1, 2, 4, 12, 44, 172, 2220, 34988, 8423596, -1},
|
||||
{0, 1, 2, 3, 5, 13, 141, 2189, 34957, 8423565, -1},
|
||||
{0, 1, 2, 3, 4, 6, 14, 142, 8334, 2105486, -1},
|
||||
{0, 1, 3, 5, 9, 25, 153, 2201, 34969, 8423577, -1},
|
||||
{0, 1, 5, 13, 29, 157, 2205, 34973, 8423581, -1, -1},
|
||||
{0, 1, 9, 41, 169, 2217, 34985, 8423593, -1, -1, -1},
|
||||
{0, 1, 33, 161, 2209, 34977, 8423585, -1, -1, -1, -1},
|
||||
{0, 1, 65, 193, 2241, 35009, 8423617, -1, -1, -1, -1},
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the most appropriate index for the given data.
|
||||
*
|
||||
* @param data Data to analyze
|
||||
* @return index value
|
||||
*/
|
||||
quint8 HuffmanAlgorithm::index( const QList<qint64>& data )
|
||||
{
|
||||
quint8 index = 0;
|
||||
|
||||
// TODO: Find out what the Huffman index algorithm is
|
||||
Q_UNUSED( data )
|
||||
index = 2;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compress data with the Huffman algorithm.
|
||||
*
|
||||
* @param encodedData Byte array where to store the compressed data
|
||||
* @param index Index to use for compression
|
||||
* @param source List of values to compress
|
||||
* @return bool
|
||||
*/
|
||||
bool HuffmanAlgorithm::deflate( QByteArray& encodedData, quint8 index, const QList<qint64>& source )
|
||||
{
|
||||
DataSource output( encodedData );
|
||||
output.skipToNextByte();
|
||||
|
||||
foreach( quint64 value, source )
|
||||
{
|
||||
if( ! deflateValue( &output, index, value ) )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Deflating failure for value:" << value;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush any leftover bit to the byte array
|
||||
output.flush();
|
||||
|
||||
encodedData = output.data();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compress a single value with the Huffman algorithm.
|
||||
*
|
||||
* @param output Data source where to store the compressed value
|
||||
* @param index Index to use for compression
|
||||
* @param value Value to compress
|
||||
* @return bool
|
||||
*/
|
||||
bool HuffmanAlgorithm::deflateValue( DataSource* output, quint8 index, qint64 value )
|
||||
{
|
||||
qint64 temp = value;
|
||||
quint8 requiredBits = 0;
|
||||
QBitArray valueBits;
|
||||
|
||||
// Fill in a bitAmounts local vector
|
||||
QVector<int> bitAmounts;
|
||||
for( int i = 0; i < HUFFMAN_BASE_SIZE; ++i )
|
||||
{
|
||||
if( bitAmounts_[ index ][ i ] == -1 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bitAmounts.append( bitAmounts_[ index ][ i ] );
|
||||
}
|
||||
|
||||
// Find the number of bits needed to store this value
|
||||
while( temp )
|
||||
{
|
||||
temp /= 2;
|
||||
++requiredBits;
|
||||
}
|
||||
|
||||
while( ! bitAmounts.count( requiredBits ) )
|
||||
{
|
||||
++requiredBits;
|
||||
}
|
||||
|
||||
qint64 offset = ( value < 0 ) ? -value : +value;
|
||||
|
||||
quint8 prefixLength;
|
||||
for( prefixLength = 1;
|
||||
( prefixLength < HUFFMAN_BASE_SIZE ) && ( offset >= huffmanBases_[ index ][ prefixLength ] ); prefixLength++ )
|
||||
{
|
||||
// Empty loop on purpose
|
||||
}
|
||||
|
||||
|
||||
// Write prefixLength 1s to the stream, then a 0
|
||||
QBitArray bits( prefixLength );
|
||||
bits.fill( true );
|
||||
bits.clearBit( bits.size() - 1 );
|
||||
|
||||
// Write the number using the minimum possible number of bits
|
||||
qint32 size = bitAmounts_[ index ][ prefixLength - 1 ];
|
||||
qint64 mask = ( 1 << ( size - 1 ) ) - 1;
|
||||
|
||||
valueBits.resize( size );
|
||||
offset = ( offset - huffmanBases_[ index ][ prefixLength - 1 ] ) & mask;
|
||||
offset <<= 1;
|
||||
offset |= ( value < 0 ) ? 1 : 0;
|
||||
|
||||
// Copy the resulting offset
|
||||
for( qint64 i = 0; i < size; ++i )
|
||||
{
|
||||
valueBits.setBit( i, offset & ( 1 << ( size - i - 1 ) ) );
|
||||
}
|
||||
|
||||
// Add the bits to the data source
|
||||
output->append( bits );
|
||||
output->append( valueBits );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decompress data with the Huffman algorithm.
|
||||
*
|
||||
* @param source Data source where to read the compressed bytes
|
||||
* @param length Number of items to read
|
||||
* @param index Index to use for compression
|
||||
* @param decodedData List where to place decompressed values
|
||||
* @return bool
|
||||
*/
|
||||
bool HuffmanAlgorithm::inflate( DataSource* source, quint64 length, quint8 index, QList<qint64>& decodedData )
|
||||
{
|
||||
QVector<int> huffmanBases;
|
||||
QVector<int> bitAmounts( HUFFMAN_BASE_SIZE );
|
||||
|
||||
// Initialize the bit amounts vector
|
||||
memcpy( bitAmounts.data(), bitAmounts_[ index ], sizeof(int)*HUFFMAN_BASE_SIZE );
|
||||
|
||||
int base = 1;
|
||||
huffmanBases.append( 0 );
|
||||
|
||||
// Fill up the huffman bases vector
|
||||
for( quint8 i = 0; i < bitAmounts.size(); ++i )
|
||||
{
|
||||
int value = bitAmounts[ i ];
|
||||
|
||||
// The bit amounts sequence ends in -1
|
||||
if( value == -1 )
|
||||
{
|
||||
bitAmounts.resize( i );
|
||||
break;
|
||||
}
|
||||
|
||||
if( value == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
huffmanBases.append( base );
|
||||
base += pow( 2, value - 1 );
|
||||
}
|
||||
|
||||
|
||||
quint32 count = 0;
|
||||
qint64 value = 0;
|
||||
bool bit;
|
||||
|
||||
while( (uint)decodedData.length() < length )
|
||||
{
|
||||
bit = source->getBit();
|
||||
|
||||
if( bit )
|
||||
{
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( count == 0 )
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
else if( count < (uint)bitAmounts.size() )
|
||||
{
|
||||
quint64 offset = source->getBits( bitAmounts[ count ] );
|
||||
bool sign = offset & 0x1;
|
||||
offset /= 2;
|
||||
value = huffmanBases[ count ] + offset;
|
||||
value *= ( sign ? -1 : +1 );
|
||||
}
|
||||
else if( count == (uint)bitAmounts.size() )
|
||||
{
|
||||
// TODO: Implement 64-bit data decompression :)
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Unsupported 64-bit value found!";
|
||||
#endif
|
||||
value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Decompression error!";
|
||||
#endif
|
||||
value = 0;
|
||||
}
|
||||
|
||||
decodedData.append( value );
|
||||
count = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFCOMPRESSION_HUFFMAN_H
|
||||
#define ISFCOMPRESSION_HUFFMAN_H
|
||||
|
||||
#include "../datasource.h"
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
namespace HuffmanAlgorithm
|
||||
{
|
||||
|
||||
bool deflate( QByteArray& encodedData, quint8 index, const QList<qint64>& source );
|
||||
bool inflate( DataSource* source, quint64 length, quint8 index, QList<qint64>& decodedData );
|
||||
quint8 index( const QList<qint64>& data );
|
||||
|
||||
// Internal use methods
|
||||
|
||||
bool deflateValue( DataSource* output, quint8 index, qint64 value );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,421 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "compression.h"
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
#include "algorithms/bitpacking.h"
|
||||
#include "algorithms/bitpacking_byte.h"
|
||||
#include "algorithms/huffman.h"
|
||||
#include "algorithms/deltatransform.h"
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decompress packet data.
|
||||
*
|
||||
* The algorithm to use for the decompression is autodetected using
|
||||
* the so-called "algorithm byte" within the compressed data.
|
||||
*
|
||||
* @param source Data Source where to read compressed bytes from
|
||||
* @param length Number of items to decompress
|
||||
* @param decodedData List where to store the decoded values
|
||||
* @return bool
|
||||
*/
|
||||
bool Compress::inflatePacketData( DataSource* source, quint64 length, QList<qint64>& decodedData )
|
||||
{
|
||||
if( source->atEnd() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qWarning() << "Source was at end!";
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
uchar byte = source->getByte();
|
||||
qint16 algorithm = ( byte & AlgorithmMask );
|
||||
quint8 algorithmData = 0;
|
||||
|
||||
// Detect which algorithm to use
|
||||
|
||||
// Use the "best compression" algorithm
|
||||
if( ( algorithm & BestCompression ) == BestCompression )
|
||||
{
|
||||
/* TODO: Not implemented yet. We don't know how ISF actually works here.
|
||||
* According to the specs, both bit packing and Huffman may be used; but
|
||||
* where are the rest of the bits stored? In the next byte?
|
||||
* We need an ISF stream with such a characteristic to be sure.
|
||||
*/
|
||||
qWarning() << "Unknown decompression method!";
|
||||
qDebug() << "[Information - type: best, byte:" << QString::number( source->getByte(), 16 ) << "hex]";
|
||||
return false;
|
||||
}
|
||||
// Use the "default compression" algorithm, which is Huffman
|
||||
else if( ( algorithm & DefaultCompression ) == DefaultCompression
|
||||
|| ( algorithm & Huffman ) == Huffman )
|
||||
{
|
||||
algorithm = Huffman;
|
||||
algorithmData = ( HuffmanMask & byte );
|
||||
}
|
||||
// According to the specs, the only other algorithm for packet data
|
||||
// is generic bit packing.
|
||||
else if( algorithm == BitPacking )
|
||||
{
|
||||
algorithm = BitPacking;
|
||||
algorithmData = ( BitPackingMask & byte );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown algorithm. Throw an error below.
|
||||
algorithm = -1;
|
||||
}
|
||||
|
||||
// Apply the inflation algorithm
|
||||
|
||||
switch( algorithm )
|
||||
{
|
||||
case BitPacking:
|
||||
{
|
||||
// Detect if the data is encoded with delta-delta transformation
|
||||
bool requiresDeltaDeltaTransform = ( BitPackingTransformMask & byte );
|
||||
if( requiresDeltaDeltaTransform )
|
||||
{
|
||||
algorithmData ^= BitPackingTransformMask;
|
||||
}
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "items using the Bit Packing algorithm and a block size of" << algorithmData;
|
||||
#endif
|
||||
|
||||
result = BitPackingAlgorithm::inflate( source, length, algorithmData, decodedData );
|
||||
|
||||
if( result && requiresDeltaDeltaTransform )
|
||||
{
|
||||
result = Delta::inverseTransform( decodedData );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Huffman:
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "items using the Huffman algorithm and index" << algorithmData;
|
||||
#endif
|
||||
result = HuffmanAlgorithm::inflate( source, length, algorithmData, decodedData );
|
||||
|
||||
// Always delta-inverse-transform data compressed with Huffman
|
||||
if( result )
|
||||
{
|
||||
result = Delta::inverseTransform( decodedData );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning() << "Unknown decompression method!";
|
||||
qDebug() << "[Information - type: unknown, byte:" << QString::number( byte, 16 ) << "hex]";
|
||||
|
||||
// Go back to the previous read position
|
||||
source->seekRelative( -1 );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Discard any partially read bytes
|
||||
if( ! source->atEnd() )
|
||||
{
|
||||
source->skipToNextByte();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compress packet data.
|
||||
*
|
||||
* The algorithm to use for the decompression is autodetected
|
||||
* by analyzing the data.
|
||||
*
|
||||
* @param encodedData Byte Array where to store the encoded data
|
||||
* @param source List of values to compress
|
||||
* @return bool
|
||||
*/
|
||||
bool Compress::deflatePacketData( QByteArray& encodedData, const QList<qint64>& source )
|
||||
{
|
||||
bool result;
|
||||
|
||||
// TODO Is this really how to detect the algorithm to use?
|
||||
int algorithm;
|
||||
if( source.count() == 1 )
|
||||
{
|
||||
algorithm = BitPacking;
|
||||
}
|
||||
else
|
||||
{
|
||||
algorithm = Huffman;
|
||||
}
|
||||
|
||||
|
||||
switch( algorithm )
|
||||
{
|
||||
case BitPacking:
|
||||
{
|
||||
quint8 blockSize = BitPackingAlgorithm::blockSize( source );
|
||||
|
||||
// Write the data encoding algorithm and block size
|
||||
encodedData.append( blockSize | BitPacking );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Deflating" << source.count() << "items using the bit packing algorithm and a block size of" << blockSize;
|
||||
#endif
|
||||
|
||||
// Deflate the data
|
||||
result = BitPackingAlgorithm::deflate( encodedData, blockSize, source );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case Huffman:
|
||||
{
|
||||
quint8 index = HuffmanAlgorithm::index( source );
|
||||
|
||||
// Write the data encoding algorithm and index
|
||||
encodedData.append( index | Huffman );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Deflating" << source.count() << "items using the Huffman algorithm and index" << index;
|
||||
#endif
|
||||
|
||||
// Deflate the data
|
||||
QList<qint64> list( source );
|
||||
result = Delta::transform( list );
|
||||
|
||||
if( result )
|
||||
{
|
||||
result = HuffmanAlgorithm::deflate( encodedData, index, list );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Encoding algorithm not implemented! (algorithm:" << QString::number( algorithm, 16 ) << ")";
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decompress property data.
|
||||
*
|
||||
* The algorithm to use for the decompression is autodetected using
|
||||
* the so-called "algorithm byte" within the compressed data.
|
||||
*
|
||||
* @param source Data Source where to read compressed bytes from
|
||||
* @param length Number of items to decompress
|
||||
* @param decodedData List where to store the decoded values
|
||||
* @return bool
|
||||
*/
|
||||
bool Compress::inflatePropertyData( DataSource* source, quint64 length, QList<qint64>& decodedData )
|
||||
{
|
||||
if( source->atEnd() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qWarning() << "Source was at end!";
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result;
|
||||
quint8 byte = source->getByte();
|
||||
qint16 algorithm = ( byte & AlgorithmMask );
|
||||
quint8 algorithmData = 0;
|
||||
|
||||
// Detect which algorithm to use
|
||||
|
||||
// Use the "default compression" algorithm
|
||||
if( ( algorithm & DefaultCompression ) == DefaultCompression )
|
||||
{
|
||||
/* TODO: Not implemented yet. We don't know how ISF actually works here.
|
||||
* According to the specs, any of the 3 bit packing techniques may be used;
|
||||
* but where are the rest of the bits stored? In the next byte?
|
||||
* We need an ISF stream with such a characteristic to be sure.
|
||||
*/
|
||||
qWarning() << "Unknown decompression method!";
|
||||
qDebug() << "[Information - type: default, byte:" << QString::number( source->getByte(), 16 ) << "hex]";
|
||||
return false;
|
||||
}
|
||||
// Use the "best compression" algorithm, which is Huffman
|
||||
else if( ( algorithm & BestCompression ) == BestCompression
|
||||
|| ( algorithm & Huffman ) == Huffman )
|
||||
{
|
||||
algorithm = Huffman;
|
||||
algorithmData = ( HuffmanMask & byte );
|
||||
}
|
||||
else if( ( algorithm & LimpelZiv ) == LimpelZiv )
|
||||
{
|
||||
algorithm = LimpelZiv;
|
||||
}
|
||||
else if( ( algorithm & BitPackingByte ) == BitPackingByte )
|
||||
{
|
||||
algorithm = BitPackingByte;
|
||||
algorithmData = ( BitPackingByteMask & byte );
|
||||
}
|
||||
else if( ( algorithm & BitPackingWord ) == BitPackingWord )
|
||||
{
|
||||
algorithm = BitPackingWord;
|
||||
algorithmData = ( BitPackingWordMask & byte );
|
||||
}
|
||||
else if( ( algorithm & BitPackingLong ) == BitPackingLong )
|
||||
{
|
||||
algorithm = BitPackingLong;
|
||||
algorithmData = ( BitPackingLongMask & byte );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown algorithm. Throw an error below.
|
||||
algorithm = -1;
|
||||
}
|
||||
|
||||
// Apply the deflation algorithm
|
||||
|
||||
switch( algorithm )
|
||||
{
|
||||
case BitPackingByte:
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "bytes using the Bit Packing Byte algorithm and an index of" << algorithmData;
|
||||
#endif
|
||||
|
||||
result = BitPackingByteAlgorithm::inflate( source, length, algorithmData, decodedData );
|
||||
break;
|
||||
}
|
||||
|
||||
case BitPackingWord:
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "bytes using the Bit Packing Word algorithm and an index of" << algorithmData;
|
||||
#endif
|
||||
|
||||
qWarning() << "Stream data is compressed with non implemented algorithm!";
|
||||
qDebug() << "[Information - algorithm: Bit Packing Word]";
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BitPackingLong:
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "bytes using the Bit Packing Long algorithm and an index of" << algorithmData;
|
||||
#endif
|
||||
|
||||
qWarning() << "Stream data is compressed with non implemented algorithm!";
|
||||
qDebug() << "[Information - algorithm: Bit Packing Long]";
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LimpelZiv:
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "items using the Limpel-Ziv algorithm and a block size of" << algorithmData;
|
||||
#endif
|
||||
|
||||
qWarning() << "Stream data is compressed with non implemented algorithm!";
|
||||
qDebug() << "[Information - algorithm: Limpel-Ziv]";
|
||||
return false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Huffman:
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Inflating" << length << "items using the Huffman algorithm and index" << algorithmData;
|
||||
#endif
|
||||
result = HuffmanAlgorithm::inflate( source, length, algorithmData, decodedData );
|
||||
|
||||
// Always delta-inverse-transform data compressed with Huffman
|
||||
if( result )
|
||||
{
|
||||
result = Delta::inverseTransform( decodedData );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qWarning() << "Unknown decompression method!";
|
||||
qDebug() << "[Information - type: unknown, byte:" << QString::number( byte, 16 ) << "hex]";
|
||||
|
||||
// Go back to the previous read position
|
||||
source->seekRelative( -1 );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Discard any partially read bytes
|
||||
if( ! source->atEnd() )
|
||||
{
|
||||
source->skipToNextByte();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Compress property data.
|
||||
*
|
||||
* The algorithm to use for the decompression is autodetected
|
||||
* by analyzing the data.
|
||||
*
|
||||
* @todo Implement!
|
||||
* @param encodedData Byte Array where to store the encoded data
|
||||
* @param source List of values to compress
|
||||
* @return bool
|
||||
*/
|
||||
bool Compress::deflatePropertyData( QByteArray& encodedData, const QList<qint64>& source )
|
||||
{
|
||||
Q_UNUSED( encodedData );
|
||||
Q_UNUSED( source );
|
||||
|
||||
bool result = false;
|
||||
|
||||
qWarning() << "Property data saving is not implemented at the moment!";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFCOMPRESSION_H
|
||||
#define ISFCOMPRESSION_H
|
||||
|
||||
#include "datasource.h"
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Algorithm extra data masks.
|
||||
*
|
||||
* These masks are used to obtain the least significant bits which
|
||||
* are useful for the specific algorithm. If an algo takes no
|
||||
* useful bits, the mask is 0.
|
||||
*/
|
||||
enum AlgorithmMasks
|
||||
{
|
||||
AlgorithmMask = 0xF0 ///< Mask used to identify the algorithms
|
||||
, BitPackingMask = 0x3F ///< Mask for bit packing of arbitrary-sized integers
|
||||
, BitPackingTransformMask = 0x20 ///< Mask for delta-delta transformed bit packed data
|
||||
, HuffmanMask = 0x3F ///< Mask for Huffman
|
||||
, BitPackingLongMask = 0x3F ///< Mask for bit packing of 32bit signed integers
|
||||
, BitPackingWordMask = 0x1F ///< Mask for bit packing of 16bit unsigned integers
|
||||
, BitPackingByteMask = 0x1F ///< Mask for bit packing of 8bit unsigned integers
|
||||
, LimpelZivMask = 0x00 ///< Mask for Limpel-Ziv
|
||||
, BestCompressionMask = 0x00 ///< Mask for the best compression algo
|
||||
, DefaultCompressionMask = 0x00 ///< Mask for the default compression algo
|
||||
};
|
||||
|
||||
/**
|
||||
* Compression and decompressions algorithms.
|
||||
*
|
||||
* Bit assignment table for packets (strokes):
|
||||
* ---------------------------
|
||||
* | Algorithm | 7 6 5 4 3 2 1 0 (Bit assignment) |
|
||||
* Bit packing 0 0 D D D D D D
|
||||
* Huffman 1 0 D D D D D D
|
||||
* Default compression 1 1 0 0 X X X X
|
||||
* Best compression 1 1 1 1 X X X X
|
||||
*
|
||||
* Bit assignment table for properties (tags):
|
||||
* ---------------------------
|
||||
* | Algorithm | 7 6 5 4 3 2 1 0 (Bit assignment) |
|
||||
* Bit packing of bytes 0 0 0 D D D D D
|
||||
* Bit packing of words 0 0 1 D D D D D
|
||||
* Bit packing of longs 0 1 D D D D D D
|
||||
* Limpel-Ziv 1 0 1 0 X X X X
|
||||
* Default compression 1 1 0 0 X X X X
|
||||
* Best compression 1 1 1 1 X X X X
|
||||
*
|
||||
* D = Algorithm-specific data
|
||||
* X = Ignored bit
|
||||
*
|
||||
* We use the two most significant bits to identify the algorithms,
|
||||
* because they are guaranteed to not contain algorithm data.
|
||||
*
|
||||
* Also, note that these tables are not identical to the ones in the
|
||||
* specs: we changed them because they differ from our factual
|
||||
* observations (in the specs Huffman is missing, for example).
|
||||
*/
|
||||
enum CommonDataAlgorithms
|
||||
{
|
||||
BestCompression = 0xF0 ///< Best compression algorithm
|
||||
, DefaultCompression = 0xC0 ///< Default compression algorithm (Huffman)
|
||||
, Huffman = 0x80 ///< Huffman encoding
|
||||
};
|
||||
/**
|
||||
* Compression and decompressions algorithms.
|
||||
*
|
||||
* @see CommonDataAlgorithms
|
||||
*/
|
||||
enum PacketDataAlgorithms
|
||||
{
|
||||
BitPacking = 0x00 ///< Bit packing
|
||||
};
|
||||
/**
|
||||
* Compression and decompressions algorithms.
|
||||
*
|
||||
* @see CommonDataAlgorithms
|
||||
*/
|
||||
enum PropertyDataAlgorithms
|
||||
{
|
||||
LimpelZiv = 0xA0 ///< Limpel-Ziv compressionrs
|
||||
, BitPackingLong = 0x40 ///< Bit packing of 32bit signed integers
|
||||
, BitPackingWord = 0x20 ///< Bit packing of 16bit unsigned integers
|
||||
, BitPackingByte = 0x00 ///< Bit packing of 8bit unsigned integers
|
||||
};
|
||||
|
||||
|
||||
bool inflatePacketData( DataSource* source, quint64 length, QList<qint64>& decodedData );
|
||||
bool deflatePacketData( QByteArray& destination, const QList<qint64>& decodedData );
|
||||
|
||||
bool inflatePropertyData( DataSource* source, quint64 length, QList<qint64>& decodedData );
|
||||
bool deflatePropertyData( QByteArray& destination, const QList<qint64>& decodedData );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,720 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "datasource.h"
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
using namespace Isf::Compress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Empty constructor.
|
||||
*/
|
||||
DataSource::DataSource()
|
||||
: currentBitIndex_( 8 ) // Invalid position in the current byte
|
||||
{
|
||||
buffer_.open( QBuffer::ReadWrite );
|
||||
|
||||
currentByte_.resize( 8 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param data Byte array to copy and use as data source
|
||||
*/
|
||||
DataSource::DataSource( const QByteArray& data )
|
||||
: currentBitIndex_( 8 )
|
||||
{
|
||||
buffer_.setData( data );
|
||||
buffer_.open( QBuffer::ReadWrite );
|
||||
buffer_.seek( 0 );
|
||||
|
||||
currentByte_.resize( 8 );
|
||||
|
||||
// Move the first byte of data in the bit array
|
||||
moveByteToBitArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
DataSource::~DataSource()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert a byte at the end of the data.
|
||||
*
|
||||
* @param byte The byte to append
|
||||
*/
|
||||
void DataSource::append( char byte )
|
||||
{
|
||||
bool wasEmpty = ( buffer_.size() == 0 );
|
||||
|
||||
if( ! wasEmpty )
|
||||
{
|
||||
buffer_.seek( buffer_.size() );
|
||||
}
|
||||
|
||||
if( buffer_.write( &byte, 1 ) != 1 )
|
||||
{
|
||||
qWarning() << "DataSource::append() - Write failed at buffer position" << buffer_.pos();
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the first byte to be read
|
||||
if( wasEmpty )
|
||||
{
|
||||
currentBitIndex_ = 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert bits at the end of the data.
|
||||
*
|
||||
* @param constBits The list of bits to append
|
||||
*/
|
||||
void DataSource::append( const QBitArray& constBits )
|
||||
{
|
||||
if( constBits.size() == 0 )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Not writing an empty bit array, skipping.";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if( buffer_.size() != 0 )
|
||||
{
|
||||
buffer_.seek( buffer_.size() );
|
||||
}
|
||||
|
||||
quint8 initialPos = 0;
|
||||
quint8 size = constBits.size();
|
||||
|
||||
// If there are unwritten bits, add them to the input bit array
|
||||
if( currentBitIndex_ != 8 )
|
||||
{
|
||||
size += currentBitIndex_;
|
||||
}
|
||||
|
||||
QBitArray bits( size );
|
||||
|
||||
// Copy on the input bit array the current byte then the input bits
|
||||
if( currentBitIndex_ != 8 )
|
||||
{
|
||||
initialPos = currentBitIndex_;
|
||||
for( quint8 idx = 0; idx < currentBitIndex_; ++idx )
|
||||
{
|
||||
bits[ idx ] = currentByte_[ idx ];
|
||||
}
|
||||
}
|
||||
for( quint8 idx = 0; idx < constBits.size(); ++idx )
|
||||
{
|
||||
bits[ idx + initialPos ] = constBits[ idx ];
|
||||
}
|
||||
|
||||
quint8 bit = 7;
|
||||
quint8 byte = 0;
|
||||
QByteArray bytes;
|
||||
quint8 byteWiseSize = bits.size() - ( bits.size() % 8 );
|
||||
|
||||
// Skip writing if there are no full bytes
|
||||
if( byteWiseSize != 0 )
|
||||
{
|
||||
for( quint8 idx = 0; idx < byteWiseSize; ++idx )
|
||||
{
|
||||
byte |= bits[ idx ] << bit;
|
||||
|
||||
// A byte was filled, push it to the bytearray
|
||||
if( bit-- == 0 )
|
||||
{
|
||||
bytes.append( byte );
|
||||
bit = 7;
|
||||
byte = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the bytes in the buffer
|
||||
if( bytes.size() > 0 && buffer_.write( bytes ) != bytes.size() )
|
||||
{
|
||||
qWarning() << "DataSource::append() - Write failed at buffer position" << buffer_.pos();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Move any remaining bits to the current byte
|
||||
quint8 remainingBitSize = ( bits.size() - byteWiseSize );
|
||||
if( remainingBitSize > 0 )
|
||||
{
|
||||
for( currentBitIndex_ = 0; currentBitIndex_ < remainingBitSize; ++currentBitIndex_ )
|
||||
{
|
||||
currentByte_[ currentBitIndex_ ] = bits[ currentBitIndex_ + byteWiseSize ];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentByte_.clear();
|
||||
currentByte_.resize( 8 );
|
||||
currentBitIndex_ = 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert bytes at the end of the data.
|
||||
*
|
||||
* @param bytes The bytes to append
|
||||
*/
|
||||
void DataSource::append( const QByteArray& bytes )
|
||||
{
|
||||
bool wasEmpty = ( buffer_.size() == 0 );
|
||||
|
||||
if( ! wasEmpty )
|
||||
{
|
||||
buffer_.seek( buffer_.size() );
|
||||
}
|
||||
|
||||
if( buffer_.write( bytes ) != bytes.size() )
|
||||
{
|
||||
qWarning() << "DataSource::append() - Write of" << bytes.size() << "bytes failed at buffer position" << buffer_.pos();
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the first byte to be read
|
||||
if( wasEmpty )
|
||||
{
|
||||
currentBitIndex_ = 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get whether the buffer is finished.
|
||||
*
|
||||
* The buffer is finished (at the end) when there are no more
|
||||
* bytes to read. If the considerBits parameter is set, the
|
||||
* buffer is considered finished when all the bits have been
|
||||
* read.
|
||||
*
|
||||
* @param considerBits Whether to consider the data source as a bits source
|
||||
* @return bool
|
||||
*/
|
||||
bool DataSource::atEnd( bool considerBits ) const
|
||||
{
|
||||
if( considerBits )
|
||||
{
|
||||
// There's no more data only when both the buffer AND all the bits
|
||||
// have been read
|
||||
return buffer_.atEnd() && ( currentBitIndex_ >= 8 );
|
||||
}
|
||||
|
||||
return buffer_.atEnd();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clear the data buffer.
|
||||
*/
|
||||
void DataSource::clear()
|
||||
{
|
||||
buffer_.close();
|
||||
buffer_.setData( QByteArray() );
|
||||
buffer_.open( QIODevice::ReadWrite );
|
||||
buffer_.seek( 0 );
|
||||
|
||||
currentBitIndex_ = 8;
|
||||
|
||||
currentByte_.clear();
|
||||
currentByte_.resize( 8 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return a reference to the internal data array.
|
||||
*
|
||||
* @return byte array
|
||||
*/
|
||||
const QByteArray& DataSource::data() const
|
||||
{
|
||||
return buffer_.data();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Flush the current byte to the buffer.
|
||||
*
|
||||
* When in bit mode, this method can be used to save less than 8 bits
|
||||
* in the stream. The unsed bits are set to 0.
|
||||
*/
|
||||
void DataSource::flush()
|
||||
{
|
||||
if( currentBitIndex_ >= 8 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
quint8 bit = 7;
|
||||
quint8 byte = 0;
|
||||
for( quint8 index = 0; index < currentBitIndex_; ++index )
|
||||
{
|
||||
byte |= currentByte_[ index ] << bit--;
|
||||
}
|
||||
|
||||
// Write the bytes in the buffer
|
||||
if( buffer_.write( (char*)&byte, 1 ) != 1 )
|
||||
{
|
||||
qWarning() << "DataSource::flush() - Write failed at buffer position" << buffer_.pos();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the index of the current bit.
|
||||
*
|
||||
* @return Current position within the byte we're reading at the moment
|
||||
*/
|
||||
quint8 DataSource::getBitIndex()
|
||||
{
|
||||
return currentBitIndex_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the next bit from the data.
|
||||
*
|
||||
* @param ok If set, it will contain whether the call was successful or not.
|
||||
* @return bool
|
||||
*/
|
||||
bool DataSource::getBit( bool* ok )
|
||||
{
|
||||
if( currentBitIndex_ >= 8 )
|
||||
{
|
||||
// Also check the index again, in case somehow moveByteToBitArray()
|
||||
// fails to move the bit index
|
||||
if( ( ! moveByteToBitArray() ) || currentBitIndex_ >= 8 )
|
||||
{
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT( currentBitIndex_ == 0 );
|
||||
}
|
||||
|
||||
Q_ASSERT( currentByte_.size() == 8 );
|
||||
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
++currentBitIndex_;
|
||||
|
||||
return currentByte_.at( currentBitIndex_ - 1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the next [amount] bits from the data.
|
||||
*
|
||||
* @param amount Number of bits to read
|
||||
* @param ok If set, it will contain whether the call was successful or not.
|
||||
* @return quint64 with the set of read bits
|
||||
*/
|
||||
quint64 DataSource::getBits( quint8 amount, bool* ok )
|
||||
{
|
||||
if( amount > 64 )
|
||||
{
|
||||
qWarning() << "DataSource:getBits() - Cannot retrieve" << amount << "bits, the maximum is 64 bits!";
|
||||
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = false;
|
||||
}
|
||||
|
||||
return 0LL;
|
||||
}
|
||||
|
||||
bool gotBitOk = false;
|
||||
quint8 bitIndex = 0;
|
||||
quint64 value = 0;
|
||||
|
||||
while( ! atEnd( true ) && bitIndex < amount )
|
||||
{
|
||||
bool bit = getBit( &gotBitOk );
|
||||
|
||||
if( ! gotBitOk )
|
||||
{
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = false;
|
||||
}
|
||||
|
||||
return 0LL;
|
||||
}
|
||||
|
||||
if( bit )
|
||||
{
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
value |= 1 << bitIndex;
|
||||
#else
|
||||
value |= 1 << ( amount - bitIndex - 1 );
|
||||
#endif
|
||||
}
|
||||
|
||||
++bitIndex;
|
||||
}
|
||||
|
||||
// If more bits must be read, but are not available, abort
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
if( ( bitIndex < amount ) && atEnd( true ) )
|
||||
{
|
||||
qDebug() << "Unable to read all bits: only" << bitIndex << "of" << amount << "were available.";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Done!
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the next byte from the data
|
||||
*
|
||||
* @param ok If set, it will contain whether the call was successful or not.
|
||||
* @return char
|
||||
*/
|
||||
char DataSource::getByte( bool* ok )
|
||||
{
|
||||
bool gotBitOk;
|
||||
quint8 pos = 0;
|
||||
qint8 byte = 0;
|
||||
|
||||
while( pos < 8 )
|
||||
{
|
||||
bool bit = getBit( &gotBitOk );
|
||||
|
||||
if( ! gotBitOk )
|
||||
{
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
byte |= ( bit << pos++ );
|
||||
#else
|
||||
byte |= ( bit << ( 7 - pos++ ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
// Done!
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
return byte;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the next [amount] bytes from the data.
|
||||
*
|
||||
* @param amount Number of bytes to read
|
||||
* @param ok If set, it will contain whether the call was successful or not.
|
||||
* @return byte array of read bytes
|
||||
*/
|
||||
QByteArray DataSource::getBytes( quint8 amount, bool* ok )
|
||||
{
|
||||
QByteArray bytes;
|
||||
bool gotByteOk;
|
||||
quint8 index = 0;
|
||||
|
||||
while( ! atEnd() && index < amount )
|
||||
{
|
||||
quint8 byte = getByte( &gotByteOk );
|
||||
|
||||
if( ! gotByteOk )
|
||||
{
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = false;
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bytes.append( byte );
|
||||
++index;
|
||||
}
|
||||
|
||||
// Done!
|
||||
if( ok != 0 )
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Move a byte from the buffer into the 'current byte' bit array.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool DataSource::moveByteToBitArray()
|
||||
{
|
||||
quint8 byte = 0;
|
||||
|
||||
if( buffer_.size() == 0 )
|
||||
{
|
||||
qWarning() << "DataSource:moveByteToBitArray() - The buffer is empty!";
|
||||
currentByte_.clear();
|
||||
currentBitIndex_ = 8;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( buffer_.atEnd() )
|
||||
{
|
||||
qWarning() << "DataSource:moveByteToBitArray() - The buffer was completely parsed!";
|
||||
currentByte_.clear();
|
||||
currentBitIndex_ = 8;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( buffer_.getChar( (char*)&byte ) != 1 )
|
||||
{
|
||||
qWarning() << "DataSource::moveByteToBitArray() - Read failed at buffer position" << buffer_.pos();
|
||||
return false;
|
||||
}
|
||||
|
||||
for( qint8 i = 0; i < 8; i++ )
|
||||
{
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
currentByte_.setBit( i, byte & ( 1 << i ) );
|
||||
#else
|
||||
currentByte_.setBit( 7 - i, byte & ( 1 << i ) );
|
||||
#endif
|
||||
}
|
||||
|
||||
currentBitIndex_ = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current position within the internal data buffer.
|
||||
*
|
||||
* @return qint64
|
||||
*/
|
||||
qint64 DataSource::pos() const
|
||||
{
|
||||
return buffer_.pos();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert a byte at the beginning of the data.
|
||||
*
|
||||
* @param byte Byte to add
|
||||
*/
|
||||
void DataSource::prepend( char byte )
|
||||
{
|
||||
buffer_.buffer().prepend( byte );
|
||||
|
||||
bool wasAtBeginning = ( buffer_.pos() == 0 );
|
||||
|
||||
seekRelative( 1 );
|
||||
|
||||
if( wasAtBeginning )
|
||||
{
|
||||
moveByteToBitArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Insert bytes at the beginning of the data.
|
||||
*
|
||||
* @param bytes Bytes to add
|
||||
*/
|
||||
void DataSource::prepend( const QByteArray& bytes )
|
||||
{
|
||||
buffer_.buffer().prepend( bytes );
|
||||
|
||||
bool wasAtBeginning = ( buffer_.pos() == 0 );
|
||||
|
||||
seekRelative( bytes.size() );
|
||||
|
||||
if( wasAtBeginning )
|
||||
{
|
||||
moveByteToBitArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
/**
|
||||
* Return to the start of the buffer.
|
||||
*/
|
||||
void DataSource::reset()
|
||||
{
|
||||
buffer_.reset();
|
||||
currentBitIndex_ = 8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
/**
|
||||
* Seek back and forth in the stream.
|
||||
*
|
||||
* A negative pos will move backwards within the stream.
|
||||
*
|
||||
* @param pos Relative position where to move
|
||||
*/
|
||||
void DataSource::seekRelative( int pos )
|
||||
{
|
||||
if( pos < 0 && buffer_.pos() <= 0 )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qWarning() << "Cannot seek back!";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if( pos > 0 && ( buffer_.size() - buffer_.pos() ) < pos )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qWarning() << "Cannot seek forward!";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_.seek( buffer_.pos() + pos );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Replace the data byte array with another.
|
||||
*
|
||||
* @param data New array to copy and use as the data source
|
||||
*/
|
||||
void DataSource::setData( const QByteArray& data )
|
||||
{
|
||||
buffer_.close();
|
||||
buffer_.setData( data );
|
||||
buffer_.open( QIODevice::ReadWrite );
|
||||
buffer_.seek( 0 );
|
||||
|
||||
currentBitIndex_ = 8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Skip the rest of the current byte and read the next one.
|
||||
*
|
||||
* When using the data source in bit mode, this skips the
|
||||
* bits remaining in the current byte and prepares to read
|
||||
* the next byte.
|
||||
*/
|
||||
void DataSource::skipToNextByte()
|
||||
{
|
||||
if( currentBitIndex_ == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
moveByteToBitArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Skip the rest of the current byte and read the previous one.
|
||||
*
|
||||
* When using the data source in bit mode, this skips the
|
||||
* bits remaining in the current byte and prepares to read
|
||||
* the next byte.
|
||||
*/
|
||||
void DataSource::skipToPrevByte()
|
||||
{
|
||||
currentBitIndex_ = 8;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
/**
|
||||
* Get the size of the data.
|
||||
*
|
||||
* @return qint64
|
||||
*/
|
||||
qint64 DataSource::size() const
|
||||
{
|
||||
return buffer_.size();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef DATASOURCE_H
|
||||
#define DATASOURCE_H
|
||||
|
||||
#include <QBitArray>
|
||||
#include <QBuffer>
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
/**
|
||||
* @class DataSource
|
||||
* @brief Class to handle a QBuffer at the bit level.
|
||||
*
|
||||
* You can use this class in two ways: first, reading and writing bytes
|
||||
* normally. Second, reading and writing individual bits.
|
||||
*
|
||||
* Warning: Mixing up the usage modes is not very well tested!
|
||||
*/
|
||||
class DataSource
|
||||
{
|
||||
|
||||
public: // Public constructors
|
||||
DataSource();
|
||||
DataSource( const QByteArray& data );
|
||||
~DataSource();
|
||||
|
||||
public: // Public status retrieval methods
|
||||
bool atEnd( bool considerBits = false ) const;
|
||||
const QByteArray& data() const;
|
||||
qint64 pos() const;
|
||||
qint64 size() const;
|
||||
|
||||
public: // Public data manipulation methods
|
||||
void append( char byte );
|
||||
void append( const QBitArray& bits );
|
||||
void append( const QByteArray& bytes );
|
||||
void clear();
|
||||
void flush();
|
||||
bool getBit( bool* ok = 0 );
|
||||
quint64 getBits( quint8 amount, bool* ok = 0 );
|
||||
quint8 getBitIndex();
|
||||
char getByte( bool* ok = 0 );
|
||||
QByteArray getBytes( quint8 amount, bool* ok = 0 );
|
||||
void prepend( char byte );
|
||||
void prepend( const QByteArray& bytes );
|
||||
void reset();
|
||||
void setData( const QByteArray& data );
|
||||
void seekRelative( int pos );
|
||||
void skipToNextByte();
|
||||
void skipToPrevByte();
|
||||
|
||||
private: // Private methods
|
||||
bool moveByteToBitArray();
|
||||
|
||||
private: // Private properties
|
||||
/// Main data buffer
|
||||
QBuffer buffer_;
|
||||
/// Current bit within the bit array
|
||||
quint8 currentBitIndex_;
|
||||
/// Current byte
|
||||
QBitArray currentByte_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,356 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "multibytecoding.h"
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decodes an unsigned integer into a quint64 using multibyte decoding.
|
||||
*
|
||||
* @param source Data Source where to read bytes from
|
||||
* @return quint64
|
||||
*/
|
||||
quint64 Compress::decodeUInt( DataSource* source )
|
||||
{
|
||||
quint8 byte; // Current byte
|
||||
quint8 flag; // Used to find out if there are more bytes to convert
|
||||
quint8 count = 0; // Count of bits read so far
|
||||
quint64 value = 0; // Destination 64-bit value
|
||||
|
||||
do
|
||||
{
|
||||
byte = source->getByte();
|
||||
flag = byte & 0x80;
|
||||
value |= (byte & 0x7F) << count;
|
||||
count += 7;
|
||||
}
|
||||
while( flag );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a signed integer into a qint64 using multibyte decoding.
|
||||
*
|
||||
* @param source Data Source where to read bytes from
|
||||
* @return qint64
|
||||
*/
|
||||
qint64 Compress::decodeInt( DataSource* source )
|
||||
{
|
||||
bool isNegative = false;
|
||||
qint64 value = decodeUInt( source );
|
||||
|
||||
// The least significant bit contains the sign
|
||||
if( value & 0x01 )
|
||||
{
|
||||
isNegative = true;
|
||||
}
|
||||
|
||||
// Remove the sign bit from the raw value
|
||||
value >>= 1;
|
||||
|
||||
// Apply the sign
|
||||
if( isNegative )
|
||||
{
|
||||
value *= -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a float using multibyte decoding.
|
||||
*
|
||||
* @param source Data Source where to read bytes from
|
||||
* @return float
|
||||
*/
|
||||
float Compress::decodeFloat( DataSource* source )
|
||||
{
|
||||
qint8 index;
|
||||
|
||||
// Union used to decode the float transparently
|
||||
union
|
||||
{
|
||||
float value;
|
||||
uchar bytes[4];
|
||||
} data;
|
||||
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
index = 3;
|
||||
do
|
||||
{
|
||||
data.bytes[ index-- ] = source->getByte();
|
||||
}
|
||||
while( index >= 0 );
|
||||
#else
|
||||
index = 0;
|
||||
do
|
||||
{
|
||||
data.bytes[ index++ ] = source->getByte();
|
||||
}
|
||||
while( index <= 3 );
|
||||
#endif
|
||||
|
||||
return data.value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encodes an unsigned 64-bit integer into a byte array using multibyte encoding.
|
||||
*
|
||||
* @param value The value to encode
|
||||
* @return byte array
|
||||
*/
|
||||
QByteArray Compress::encodeUInt( quint64 value )
|
||||
{
|
||||
quint8 byte;
|
||||
quint8 flag;
|
||||
QByteArray result;
|
||||
|
||||
do
|
||||
{
|
||||
byte = value & 0x7F;
|
||||
value >>= 7;
|
||||
|
||||
flag = ( value == 0 ) ? 0 : 0x80;
|
||||
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
result.prepend( byte | flag );
|
||||
#else
|
||||
result.append( byte | flag );
|
||||
#endif
|
||||
}
|
||||
while( flag );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encodes an unsigned 64-bit integer into the Data Source using multibyte encoding.
|
||||
*
|
||||
* @param source The data source
|
||||
* @param value The value to encode
|
||||
* @param prepend False (default value) to append the encoded value to the existing data
|
||||
* True to prepend the new value to the existing data
|
||||
*/
|
||||
void Compress::encodeUInt( DataSource* source, quint64 value, bool prepend )
|
||||
{
|
||||
QByteArray result( encodeUInt( value ) );
|
||||
|
||||
if( prepend )
|
||||
{
|
||||
source->prepend( result );
|
||||
}
|
||||
else
|
||||
{
|
||||
source->append( result );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a signed 64-bit integer into a byte array using multibyte encoding.
|
||||
*
|
||||
* @param value The value to encode
|
||||
* @return byte array
|
||||
*/
|
||||
QByteArray Compress::encodeInt( qint64 value )
|
||||
{
|
||||
bool isNegative = ( value < 0 );
|
||||
if ( isNegative ) value *= -1;
|
||||
|
||||
value = value << 1;
|
||||
|
||||
QByteArray result( encodeUInt( value ) );
|
||||
|
||||
if ( isNegative )
|
||||
{
|
||||
int pos;
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
pos = result.size() - 1;
|
||||
#else
|
||||
pos = 0;
|
||||
#endif
|
||||
|
||||
result[ pos ] = result[ pos ] | 0x1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a signed 64-bit integer into the Data Source using multibyte encoding.
|
||||
*
|
||||
* @param source The data source
|
||||
* @param value The value to encode
|
||||
* @param prepend False (default value) to append the encoded value to the existing data
|
||||
* True to prepend the new value to the existing data
|
||||
*/
|
||||
void Compress::encodeInt( DataSource* source, qint64 value, bool prepend )
|
||||
{
|
||||
QByteArray result( encodeInt( value ) );
|
||||
|
||||
if( prepend )
|
||||
{
|
||||
source->prepend( result );
|
||||
}
|
||||
else
|
||||
{
|
||||
source->append( result );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a floating-point value into a byte array using multibyte encoding.
|
||||
*
|
||||
* @param value The value to encode
|
||||
* @return byte array
|
||||
*/
|
||||
QByteArray Compress::encodeFloat( float value )
|
||||
{
|
||||
qint8 index;
|
||||
QByteArray result;
|
||||
|
||||
// Union used to encode the float transparently
|
||||
union
|
||||
{
|
||||
float number;
|
||||
uchar bytes[4];
|
||||
} data;
|
||||
|
||||
data.number = value;
|
||||
|
||||
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||
index = 3;
|
||||
do
|
||||
{
|
||||
result.append( data.bytes[ index-- ] );
|
||||
}
|
||||
while( index >= 0 );
|
||||
#else
|
||||
index = 0;
|
||||
do
|
||||
{
|
||||
result.append( data.bytes[ index++ ] );
|
||||
}
|
||||
while( index <= 3 );
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a floating-point value into the Data Source using multibyte encoding.
|
||||
*
|
||||
* @param source The data source
|
||||
* @param value The value to encode
|
||||
* @param prepend False (default value) to append the encoded value to the existing data
|
||||
* True to prepend the new value to the existing data
|
||||
*/
|
||||
void Compress::encodeFloat( DataSource* source, float value, bool prepend )
|
||||
{
|
||||
QByteArray result( encodeFloat( value ) );
|
||||
|
||||
if( prepend )
|
||||
{
|
||||
source->prepend( result );
|
||||
}
|
||||
else
|
||||
{
|
||||
source->append( result );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return how much bytes a certain unsigned value takes up using multi-byte encoding.
|
||||
*
|
||||
* @param value The unsigned value to check
|
||||
* @return Number of bytes required for multi-byte storage of that value
|
||||
*/
|
||||
quint8 Compress::getMultiByteSize( quint64 value )
|
||||
{
|
||||
quint8 bytes = 1;
|
||||
|
||||
while( value )
|
||||
{
|
||||
value >>= 7;
|
||||
|
||||
if( value )
|
||||
{
|
||||
++bytes;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return how much bytes a certain signed value takes up using multi-byte encoding.
|
||||
*
|
||||
* @param value The signed value to check
|
||||
* @return Number of bytes required for multi-byte storage of that value
|
||||
*/
|
||||
quint8 Compress::getMultiByteSize( qint64 value )
|
||||
{
|
||||
quint8 bytes = 1;
|
||||
|
||||
while( value )
|
||||
{
|
||||
value >>= 6;
|
||||
|
||||
if( value )
|
||||
{
|
||||
++bytes;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MULTIBYTECODING_H
|
||||
#define MULTIBYTECODING_H
|
||||
|
||||
#include "datasource.h"
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
namespace Compress
|
||||
{
|
||||
|
||||
quint64 decodeUInt( DataSource* source );
|
||||
qint64 decodeInt( DataSource* source );
|
||||
float decodeFloat( DataSource* source );
|
||||
|
||||
QByteArray encodeUInt( quint64 value );
|
||||
void encodeUInt( DataSource* source, quint64 value, bool prepend = false );
|
||||
|
||||
QByteArray encodeInt( qint64 value );
|
||||
void encodeInt( DataSource* source, qint64 value, bool prepend = false );
|
||||
|
||||
QByteArray encodeFloat( float value );
|
||||
void encodeFloat( DataSource* source, float value, bool prepend = false );
|
||||
|
||||
quint8 getMultiByteSize( quint64 value );
|
||||
quint8 getMultiByteSize( qint64 value );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFQT_GIF_SUPPORT_H
|
||||
#define ISFQT_GIF_SUPPORT_H
|
||||
|
||||
#include <gif_lib.h>
|
||||
|
||||
#define MAX_GIF_BYTE 255
|
||||
|
||||
|
||||
|
||||
// Internal helper function to read GIF data from a byte array
|
||||
int GifReadFromByteArray( GifFileType* gif, GifByteType* bytes, int size )
|
||||
{
|
||||
QBuffer* buffer = (QBuffer*) gif->UserData;
|
||||
return buffer->read( (char*)bytes, size );
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Internal helper function to write GIF data into a byte array
|
||||
int GifWriteToByteArray( GifFileType* gif, const GifByteType* bytes, int size )
|
||||
{
|
||||
QBuffer* buffer = (QBuffer*) gif->UserData;
|
||||
return buffer->write( (char*)bytes, size );
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
// Define contents of the main page
|
||||
|
||||
/**
|
||||
* @mainpage ISF-Qt - Ink Serialized Format library
|
||||
*
|
||||
* @section about About ISF-Qt
|
||||
*
|
||||
* ISF-Qt is a library, written in C++ with the Qt 4 toolkit, capable of reading
|
||||
* and writing the ISF format. ISF stands for "Ink Serialized Format", and is
|
||||
* the disk serialization format used by the Windows.Ink C# library and by the
|
||||
* TabletPC platform, but also by applications like Windows Live Messenger - which
|
||||
* uses ISF to transfer handwriting messages.
|
||||
*
|
||||
* We had the need to interpret these handwriting messages in the best possible
|
||||
* way, and started to reverse-engineer the format, with the help of existing
|
||||
* (but incomplete) libraries and specifications. What we ended up with is ISF-Qt,
|
||||
* a library which can be used to decode an ISF file or data stream, manage it
|
||||
* and the the strokes which comprise the drawing, and write ISF files/streams.
|
||||
* You can also create new drawings from scratch.
|
||||
*
|
||||
*
|
||||
* @section intro About this documentation
|
||||
* This documentation is written towards developers, to be used
|
||||
* as reference manual when writing code which uses ISF-Qt, or when developing
|
||||
* the library itself.
|
||||
* Please take your time to understand the structure of the library, its classes
|
||||
* and methods.
|
||||
*
|
||||
* Since ISF-Qt is built on top of the Qt 4 library, you will also need to
|
||||
* be familiar with it; more information can be found online at:
|
||||
* - http://doc.trolltech.com/4.3/index.html
|
||||
*/
|
||||
@@ -0,0 +1,707 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include "isfinkcanvas.h"
|
||||
#include "isfqtdrawing.h"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a new InkCanvas widget.
|
||||
*
|
||||
* @param parent The parent widget.
|
||||
*/
|
||||
InkCanvas:: InkCanvas( QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, erasingImage_( false )
|
||||
, drawing_( 0 )
|
||||
, scribbling_( false )
|
||||
, currentStroke_( 0 )
|
||||
, drawingDirty_( true )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "** Created new InkCanvas:" << this << "**";
|
||||
#endif
|
||||
|
||||
setCanvasColor( Qt::white );
|
||||
setPenColor( Qt::black );
|
||||
setPenSize( 4 );
|
||||
|
||||
setAttribute( Qt::WA_StaticContents, true ); // only give us paint events for the parts of us that become visible on resize.
|
||||
|
||||
setAutoFillBackground( true );
|
||||
|
||||
clear();
|
||||
|
||||
// prepare the buffer
|
||||
clearBuffer();
|
||||
|
||||
// create a custom cursor
|
||||
updateCursor();
|
||||
|
||||
// start with a drawing pen by default.
|
||||
setPenType( DrawingPen );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*
|
||||
* There are no members on stack, so it's currently not used.
|
||||
*/
|
||||
InkCanvas::~InkCanvas()
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "** Destroyed InkCanvas:" << this << "**";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the drawing's ISF representation.
|
||||
*
|
||||
* @return A QByteArray filled with ISF data.
|
||||
*/
|
||||
QByteArray InkCanvas::bytes()
|
||||
{
|
||||
return Isf::Stream::writer( *drawing_ );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clears the current image, discarding all drawn strokes.
|
||||
*
|
||||
* Stroke attributes such as pen size, pen type and color are not cleared.
|
||||
*
|
||||
*/
|
||||
void InkCanvas::clear()
|
||||
{
|
||||
if ( drawing_ == 0 )
|
||||
{
|
||||
drawing_ = &initialDrawing_;
|
||||
}
|
||||
|
||||
drawing_->clear();
|
||||
drawingDirty_ = true;
|
||||
|
||||
update();
|
||||
|
||||
emit inkChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clear the internal pixmap buffer.
|
||||
*
|
||||
* The buffer used to quickly render on the widget painting area
|
||||
* the new and old strokes: every new stroke is saved here.
|
||||
* This allows to avoid repainting all strokes at every paint event.
|
||||
*/
|
||||
void InkCanvas::clearBuffer()
|
||||
{
|
||||
bufferPixmap_ = QPixmap( size() );
|
||||
bufferPixmap_.fill( Qt::transparent );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the Isf::Drawing instance that the canvas is currently manipulating.
|
||||
*
|
||||
* Beware: if you have not set your own Isf::Drawing instance via setDrawing(), you must not delete
|
||||
* the object that this method returns. In this case this method returns a pointer to an internal
|
||||
* Isf::Drawing instance that must not be deleted. If you have previously used setDrawing() then you
|
||||
* are free to do whatever you like with this pointer.
|
||||
*
|
||||
* \code
|
||||
* InkCanvas* canvas = new InkCanvas( this );
|
||||
*
|
||||
* Isf::Drawing* drawing = canvas->drawing();
|
||||
* delete drawing; // bad! deleting an object internal to InkCanvas.
|
||||
*
|
||||
* canvas->setDrawing( existingIsfDrawingInstance );
|
||||
*
|
||||
* drawing = canvas->drawing();
|
||||
* delete drawing; // good - drawing points to existingIsfDrawingInstance.
|
||||
* \endcode
|
||||
*
|
||||
*
|
||||
* @see setDrawing()
|
||||
* @return The current Isf::Drawing instance.
|
||||
*/
|
||||
Isf::Drawing* InkCanvas::drawing()
|
||||
{
|
||||
return drawing_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Draw a line from the current point to an ending point.
|
||||
*
|
||||
* The current point is initially set in mousePressEvent();
|
||||
* then it's set here.
|
||||
*
|
||||
* @see mousePressEvent()
|
||||
* @param endPoint Final point of the line
|
||||
*/
|
||||
void InkCanvas::drawLineTo( const QPoint& endPoint )
|
||||
{
|
||||
Q_ASSERT_X( drawing_, "drawLineTo", "Drawing is null" );
|
||||
|
||||
// draw the "in progress" strokes on the buffer.
|
||||
QPainter painter( &bufferPixmap_ );
|
||||
painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform, true );
|
||||
|
||||
QColor color = color_;
|
||||
if( erasingImage_ )
|
||||
{
|
||||
color = QColor( "white" );
|
||||
}
|
||||
|
||||
painter.setPen( QPen( color, penSize_, Qt::SolidLine, Qt::RoundCap,
|
||||
Qt::RoundJoin ) );
|
||||
|
||||
// QPainter::drawLine() doesn't draw anything if the two points are the same
|
||||
if( lastPoint_ == endPoint )
|
||||
{
|
||||
painter.drawPoint( endPoint );
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawLine( lastPoint_, endPoint );
|
||||
}
|
||||
|
||||
int rad = (1 / 2) + 2;
|
||||
update( QRect( lastPoint_, endPoint ).normalized()
|
||||
.adjusted( -rad, -rad, +rad, +rad ) );
|
||||
|
||||
lastPoint_ = endPoint;
|
||||
|
||||
// Refresh the widget with the new stroke
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Renders the ink drawing to a QImage and returns it.
|
||||
*
|
||||
* @return A QImage containing the rendered Ink.
|
||||
*/
|
||||
QImage InkCanvas::image()
|
||||
{
|
||||
return drawing_->pixmap( Qt::white ).toImage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get whether the drawing is empty (i.e., contains no strokes).
|
||||
*
|
||||
* @return True if the Ink image is empty, false otherwise
|
||||
*/
|
||||
bool InkCanvas::isEmpty()
|
||||
{
|
||||
return drawing_->isNull();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Start drawing a new stroke.
|
||||
*
|
||||
* Look at mouseMoveEvent() and mouseReleaseEvent() for more
|
||||
* information on how drawing is done.
|
||||
*
|
||||
* This method is usually called by Qt.
|
||||
*
|
||||
* @see mouseMoveEvent()
|
||||
* @see mouseReleaseEvent()
|
||||
* @param event The mouse button press event
|
||||
*/
|
||||
void InkCanvas::mousePressEvent( QMouseEvent* event )
|
||||
{
|
||||
Q_ASSERT_X( drawing_, "mousePressEvent", "Drawing is null" );
|
||||
|
||||
if( event->button() != Qt::LeftButton )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
scribbling_ = true;
|
||||
|
||||
if ( penType_ == EraserPen )
|
||||
{
|
||||
// is there a stroke here?
|
||||
QPoint point = event->pos();
|
||||
Stroke* s = drawing_->strokeAtPoint( point );
|
||||
if ( s != 0 )
|
||||
{
|
||||
drawing_->deleteStroke( s );
|
||||
drawingDirty_ = true;
|
||||
update();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lastPoint_ = event->pos();
|
||||
|
||||
// If there already is a stroke, add it
|
||||
if( currentStroke_ )
|
||||
{
|
||||
currentStroke_->finalize();
|
||||
drawing_->addStroke( currentStroke_ );
|
||||
currentStroke_ = 0;
|
||||
}
|
||||
|
||||
currentStroke_ = new Isf::Stroke();
|
||||
currentStroke_->addPoint( lastPoint_ );
|
||||
currentStroke_->setColor( color_ );
|
||||
currentStroke_->setPenSize( QSizeF( (qreal)penSize_, (qreal)penSize_ ) );
|
||||
currentStroke_->setFlag( FitToCurve, true );
|
||||
|
||||
// Draw the initial point
|
||||
drawLineTo( lastPoint_ );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Continue drawing the current stroke.
|
||||
*
|
||||
* As the cursor moves across the canvas we continue drawing the stroke started in
|
||||
* mousePressEvent(). A line is drawn between the last point and the current point
|
||||
* as given by QMouseEvent::pos().
|
||||
*
|
||||
* Once the mouse button is released, drawing ends.
|
||||
*
|
||||
* @see mousePressEvent()
|
||||
* @see mouseReleaseEvent()
|
||||
* @param event The mouse move event
|
||||
*/
|
||||
void InkCanvas::mouseMoveEvent( QMouseEvent* event )
|
||||
{
|
||||
if( ! ( event->buttons() & Qt::LeftButton ) || ! scribbling_ )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT_X( drawing_, "mouseMoveEvent", "Drawing is null" );
|
||||
|
||||
if ( penType_ == EraserPen )
|
||||
{
|
||||
// is there a stroke here?
|
||||
QPoint point = event->pos();
|
||||
Stroke* s = drawing_->strokeAtPoint( point );
|
||||
if ( s != 0 )
|
||||
{
|
||||
drawing_->deleteStroke( s );
|
||||
drawingDirty_ = true;
|
||||
update();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QPoint position( event->pos() );
|
||||
|
||||
// Don't add duplicate points. Mainly useful when drawing dots.
|
||||
if( lastPoint_ == position )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
drawLineTo( position );
|
||||
|
||||
Q_ASSERT_X( currentStroke_, "mouseMoveEvent", "currentStroke_ is null" );
|
||||
|
||||
// Add the new point to the stroke
|
||||
currentStroke_->addPoint( lastPoint_ );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Finish drawing the current stroke.
|
||||
*
|
||||
* Adds this stroke, with the correct attributes, to the Isf::Drawing strokes collection.
|
||||
*
|
||||
* @see mousePressEvent()
|
||||
* @see mouseMoveEvent()
|
||||
* @param event The mouse release event
|
||||
*/
|
||||
void InkCanvas::mouseReleaseEvent( QMouseEvent* event )
|
||||
{
|
||||
Q_ASSERT_X( drawing_, "mouseReleaseEvent", "Drawing is null" );
|
||||
|
||||
if( event->button() != Qt::LeftButton || ! scribbling_ )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( penType_ == EraserPen )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QPoint position = event->pos();
|
||||
|
||||
// Don't redraw already drawn points or lines
|
||||
if( lastPoint_ != position )
|
||||
{
|
||||
drawLineTo( position );
|
||||
}
|
||||
|
||||
Q_ASSERT_X( currentStroke_, "mouseReleaseEvent", "currentStroke_ is null" );
|
||||
|
||||
#ifdef KMESSDEBUG_INKEDIT_GENERAL
|
||||
qDebug() << "Finishing up stroke";
|
||||
#endif
|
||||
|
||||
// Don't add duplicate points. Mainly useful when drawing dots.
|
||||
if( lastPoint_ != position )
|
||||
{
|
||||
currentStroke_->addPoint( lastPoint_ );
|
||||
}
|
||||
|
||||
currentStroke_->finalize();
|
||||
drawing_->addStroke( currentStroke_ );
|
||||
|
||||
currentStroke_ = 0;
|
||||
|
||||
drawingDirty_ = true;
|
||||
|
||||
// clear the buffer.
|
||||
clearBuffer();
|
||||
|
||||
// update
|
||||
update();
|
||||
|
||||
scribbling_ = false;
|
||||
emit inkChanged();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Repaints the ink canvas.
|
||||
*
|
||||
* For performance reasons an internal buffer is used to ensure that
|
||||
* the entire Ink drawing is not re-rendered on each paintEvent call.
|
||||
* This buffer is invalidated only when a stroke is added or removed, the
|
||||
* drawing is changed or the canvas cleared.
|
||||
*
|
||||
*
|
||||
* @param event The paint event from Qt.
|
||||
*/
|
||||
void InkCanvas::paintEvent( QPaintEvent* event )
|
||||
{
|
||||
Q_UNUSED( event );
|
||||
|
||||
QPainter painter( this );
|
||||
|
||||
Q_ASSERT_X( drawing_, "paintEvent", "Drawing is null" );
|
||||
|
||||
// draw the ISF first, then the buffer over the top.
|
||||
// buffer has a transparent background.
|
||||
QPixmap isfPixmap( drawingDirty_
|
||||
? drawing_->pixmap( Qt::transparent )
|
||||
: isfCachePixmap_ );
|
||||
|
||||
if ( drawingDirty_ )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "ISF pixmap cache dirty; re-caching.";
|
||||
#endif
|
||||
isfCachePixmap_ = isfPixmap;
|
||||
drawingDirty_ = false;
|
||||
}
|
||||
|
||||
QRect boundingRect = drawing_->boundingRect();
|
||||
|
||||
// draw the pixmap starting at the boundingRect_ top left corner.
|
||||
painter.drawPixmap( boundingRect.topLeft(), isfPixmap );
|
||||
|
||||
// draw the buffer from 0,0.
|
||||
painter.drawPixmap( event->rect(), bufferPixmap_, event->rect() );
|
||||
|
||||
QWidget::paintEvent( event );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current pen color.
|
||||
*
|
||||
* @return The current pen color.
|
||||
*/
|
||||
QColor InkCanvas::penColor()
|
||||
{
|
||||
return color_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current pen type.
|
||||
*
|
||||
* @return The current pen type.
|
||||
*/
|
||||
int InkCanvas::penSize()
|
||||
{
|
||||
return penSize_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current pen type.
|
||||
*
|
||||
* See the PenType enum for details.
|
||||
*
|
||||
* @see PenType
|
||||
* @return The current pen type.
|
||||
*/
|
||||
InkCanvas::PenType InkCanvas::penType()
|
||||
{
|
||||
return penType_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The widget has changed size, re-draw everything.
|
||||
*
|
||||
* @param event The resizing event.
|
||||
*/
|
||||
void InkCanvas::resizeEvent( QResizeEvent* event )
|
||||
{
|
||||
// need to resize the buffer pixmap.
|
||||
clearBuffer();
|
||||
|
||||
update();
|
||||
|
||||
QWidget::resizeEvent( event );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Save the current ISF drawing to a QIODevice.
|
||||
*
|
||||
* If the base64 param is true, then the drawing will be written base64-encoded.
|
||||
* This is helpful for transmission over binary-unfriendly media.
|
||||
*
|
||||
* @param dev The QIODevice to save to.
|
||||
* @param base64 If true, the drawing is written encoded with base64.
|
||||
*/
|
||||
|
||||
void InkCanvas::save( QIODevice &dev, bool base64 )
|
||||
{
|
||||
dev.write( Isf::Stream::writer( *drawing_, base64 ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the canvas colour (i.e., the background colour of the InkCanvas control)
|
||||
*
|
||||
* Note: this is not saved with the Ink data. This is only used for colouring the canvas.
|
||||
*
|
||||
* @param newColor The new canvas color.
|
||||
*/
|
||||
void InkCanvas::setCanvasColor( QColor newColor )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Setting new canvas color:" << newColor.name();
|
||||
#endif
|
||||
canvasColor_ = newColor;
|
||||
|
||||
QPalette p = palette();
|
||||
p.setColor( QPalette::Window, newColor );
|
||||
setPalette( p );
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Changes the currently displayed Ink to the one supplied.
|
||||
*
|
||||
* Note: InkCanvas does not take ownership of Isf::Drawing instances supplied here.
|
||||
* You are still responsible of ensuring they are deleted appropriately.
|
||||
*
|
||||
* @param drawing The new Ink drawing to display.
|
||||
*/
|
||||
void InkCanvas::setDrawing( Isf::Drawing* drawing )
|
||||
{
|
||||
drawing_ = drawing;
|
||||
|
||||
// try to resize of the widget to accommodate the
|
||||
// drawing.
|
||||
QRect boundingRect = drawing_->boundingRect();
|
||||
boundingRect.setTopLeft( QPoint( 0, 0 ) );
|
||||
|
||||
resize( boundingRect.width(), boundingRect.height() );
|
||||
|
||||
drawingDirty_ = true;
|
||||
|
||||
updateGeometry();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change the color of the pen used to draw on the Ink canvas.
|
||||
*
|
||||
* @param newColor The new color for the pen.
|
||||
*/
|
||||
void InkCanvas::setPenColor( QColor newColor )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Setting new pen color:" << newColor.name();
|
||||
#endif
|
||||
|
||||
color_ = newColor;
|
||||
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change the size of the pen. This value is in pixels.
|
||||
*
|
||||
* @param pixels The size of the pen, in pixels
|
||||
*/
|
||||
void InkCanvas::setPenSize( int pixels )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Setting new pen size:" << pixels;
|
||||
#endif
|
||||
penSize_ = pixels;
|
||||
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change the pen type.
|
||||
*
|
||||
* Currently, only PenType::EraserPen and PenType::DrawingPen are supported. See the PenType
|
||||
* enum for more information.
|
||||
*
|
||||
* @see PenType
|
||||
* @param type The new pen type
|
||||
*/
|
||||
void InkCanvas::setPenType( PenType type )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Setting new pen type:" << type;
|
||||
#endif
|
||||
|
||||
penType_ = type;
|
||||
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the suggested size for the canvas.
|
||||
*
|
||||
* The actual size hint will depend on the size of the drawing on the canvas. By default,
|
||||
* the size hint is 300x300 pixels.
|
||||
*/
|
||||
QSize InkCanvas::sizeHint() const
|
||||
{
|
||||
if ( drawing_->isNull() )
|
||||
{
|
||||
return QSize( 300, 300 );
|
||||
}
|
||||
else
|
||||
{
|
||||
QRect boundingRect = drawing_->boundingRect();
|
||||
boundingRect.setTopLeft( QPoint( 0, 0 ) );
|
||||
return boundingRect.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a QCursor displayed when the mouse pointer moves over the widget.
|
||||
*
|
||||
* The cursor becomes a point, drawn with the current stroke colour and pen size.
|
||||
*/
|
||||
void InkCanvas::updateCursor()
|
||||
{
|
||||
if ( cursorPixmap_.isNull() )
|
||||
{
|
||||
cursorPixmap_ = QPixmap( QSize( 32, 32 ) );
|
||||
}
|
||||
|
||||
if ( penType_ == EraserPen )
|
||||
{
|
||||
cursorPixmap_ = QPixmap( ":pics/draw-eraser.png" );
|
||||
cursor_ = QCursor( cursorPixmap_, 0, cursorPixmap_.height() );
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorPixmap_.fill( Qt::transparent );
|
||||
|
||||
QPainter painter( &cursorPixmap_ );
|
||||
|
||||
painter.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform, true );
|
||||
painter.setPen( QPen( color_, penSize_, Qt::SolidLine, Qt::RoundCap,
|
||||
Qt::RoundJoin ) );
|
||||
|
||||
// now draw a point.
|
||||
painter.drawPoint( QPoint( cursorPixmap_.size().width() / 2, cursorPixmap_.size().height() / 2 ) );
|
||||
|
||||
cursor_ = QCursor( cursorPixmap_ );
|
||||
}
|
||||
|
||||
// create our cursor.
|
||||
setCursor( cursor_ );
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef ISFQT_INTERNAL_H
|
||||
#define ISFQT_INTERNAL_H
|
||||
|
||||
#include "isfqtconfig.h"
|
||||
|
||||
#include <IsfQt>
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
|
||||
/**
|
||||
* Uncomment this define to enable very verbose debugging output
|
||||
*/
|
||||
#ifdef ISFQT_DEBUG
|
||||
#define ISFQT_DEBUG_VERBOSE
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
// Forward declarations
|
||||
class Stroke;
|
||||
namespace Compress
|
||||
{
|
||||
class DataSource;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A list of all known ISF tag indexes into the GUID
|
||||
* table
|
||||
*/
|
||||
enum DataTag
|
||||
{
|
||||
TAG_INK_SPACE_RECT = 0
|
||||
, TAG_GUID_TABLE
|
||||
, TAG_DRAW_ATTRS_TABLE
|
||||
, TAG_DRAW_ATTRS_BLOCK
|
||||
, TAG_STROKE_DESC_TABLE
|
||||
, TAG_STROKE_DESC_BLOCK
|
||||
, TAG_BUTTONS
|
||||
, TAG_NO_X
|
||||
, TAG_NO_Y
|
||||
, TAG_DIDX
|
||||
, TAG_STROKE // = 10
|
||||
, TAG_STROKE_PROPERTY_LIST
|
||||
, TAG_POINT_PROPERTY
|
||||
, TAG_SIDX
|
||||
, TAG_COMPRESSION_HEADER
|
||||
, TAG_TRANSFORM_TABLE
|
||||
, TAG_TRANSFORM
|
||||
, TAG_TRANSFORM_ISOTROPIC_SCALE
|
||||
, TAG_TRANSFORM_ANISOTROPIC_SCALE
|
||||
, TAG_TRANSFORM_ROTATE
|
||||
, TAG_TRANSFORM_TRANSLATE // = 20
|
||||
, TAG_TRANSFORM_SCALE_AND_TRANSLATE
|
||||
, TAG_TRANSFORM_QUAD
|
||||
, TAG_TIDX
|
||||
, TAG_METRIC_TABLE
|
||||
, TAG_METRIC_BLOCK
|
||||
, TAG_MIDX
|
||||
, TAG_MANTISSA
|
||||
, TAG_PERSISTENT_FORMAT
|
||||
, TAG_HIMETRIC_SIZE
|
||||
, TAG_STROKE_IDS // = 30
|
||||
, DEFAULT_TAGS_NUMBER = 100
|
||||
, FIRST_CUSTOM_TAG_ID = 100
|
||||
};
|
||||
Q_DECLARE_FLAGS( DataTags, DataTag )
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( DataTags )
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ISF parser state machine states
|
||||
*/
|
||||
enum ParserState
|
||||
{
|
||||
ISF_PARSER_START = 0, ///< The parser has not read anything from the data yet
|
||||
ISF_PARSER_FINISH, ///< The parser is done
|
||||
ISF_PARSER_STREAMSIZE, ///< The parser is reading the ISF stream size
|
||||
ISF_PARSER_TAG ///< The parser is reading tags from the stream
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Persistent Format version
|
||||
*
|
||||
* @see TagsParser::parsePersistentFormat() for more info.
|
||||
*/
|
||||
const uint ISF_PERSISTENT_FORMAT_VERSION = 65536;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Drawing attributes for points.
|
||||
*/
|
||||
struct AttributeSet
|
||||
{
|
||||
/// Constructor
|
||||
AttributeSet()
|
||||
: color( Qt::black )
|
||||
, flags( 0x10 ) // Meaning unknown
|
||||
, penSize( 8.f, 8.f ) // Default pen is 8.0 pixels wide
|
||||
{
|
||||
}
|
||||
/// Quick comparison operator
|
||||
bool operator ==( const AttributeSet& other ) const
|
||||
{
|
||||
return color == other.color
|
||||
&& flags == other.flags
|
||||
&& penSize == other.penSize;
|
||||
}
|
||||
/// Quick comparison operator
|
||||
bool operator !=( const AttributeSet& other ) const
|
||||
{
|
||||
return color != other.color
|
||||
|| flags != other.flags
|
||||
|| penSize != other.penSize;
|
||||
}
|
||||
|
||||
/// The stroke color, optionally with alpha channel
|
||||
QColor color;
|
||||
/// Mask of StrokeFlags, @see StrokeFlags
|
||||
StrokeFlags flags;
|
||||
/// Dimensions of the pencil in pixels
|
||||
QSizeF penSize;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Drawing attributes for strokes.
|
||||
*/
|
||||
struct StrokeInfo
|
||||
{
|
||||
/// Constructor
|
||||
StrokeInfo()
|
||||
: hasPressureData( false )
|
||||
, hasXData( true )
|
||||
, hasYData( true )
|
||||
{
|
||||
}
|
||||
|
||||
/// Whether the stroke contains pressure info or not
|
||||
bool hasPressureData;
|
||||
/// Whether the stroke contains X coordinates or not
|
||||
bool hasXData;
|
||||
/// Whether the stroke contains Y coordinates or not
|
||||
bool hasYData;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Internal parser data
|
||||
*/
|
||||
struct StreamData
|
||||
{
|
||||
StreamData()
|
||||
: currentAttributeSetIndex( 0 )
|
||||
, currentMetricsIndex( 0 )
|
||||
, currentStrokeInfoIndex( 0 )
|
||||
, currentTransformsIndex( 0 )
|
||||
, dataSource( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
/// List of attributes of the points in the drawing
|
||||
QList<AttributeSet> attributeSets;
|
||||
/// Drawing's bounding rectangle
|
||||
QRect boundingRect;
|
||||
/// Current attribute set
|
||||
quint64 currentAttributeSetIndex;
|
||||
/// Current metrics set
|
||||
quint64 currentMetricsIndex;
|
||||
/// Current stroke info
|
||||
quint64 currentStrokeInfoIndex;
|
||||
/// Current transform
|
||||
quint64 currentTransformsIndex;
|
||||
/// Raw bytes source used to read and write streams
|
||||
Compress::DataSource* dataSource;
|
||||
/// List of metrics used in the drawing
|
||||
QList<Metrics*> metrics;
|
||||
/// List of stroke info
|
||||
QList<StrokeInfo*> strokeInfos;
|
||||
/// Transformation matrices
|
||||
QList<QMatrix*> transforms;
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,719 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include "data/datasource.h"
|
||||
#include "data/multibytecoding.h"
|
||||
#include "tagsparser.h"
|
||||
#include "tagswriter.h"
|
||||
|
||||
#include <IsfQtDrawing>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
|
||||
#if ISFQT_GIF_ENABLED == 1
|
||||
#include "gif-support.h"
|
||||
#endif
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
using namespace Compress;
|
||||
|
||||
|
||||
/// Supported ISF version number
|
||||
#define SUPPORTED_ISF_VERSION 0
|
||||
|
||||
|
||||
// Initialization of static properties
|
||||
StreamData* Stream::streamData_( 0 );
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a raw ISF data stream into a drawing.
|
||||
*
|
||||
* If the ISF data is invalid, a null Drawing is returned.
|
||||
*
|
||||
* @param rawData Source byte array with an ISF stream
|
||||
* @param decodeFromBase64 Whether the bytes are in the Base64 format and
|
||||
* need to be decoded first
|
||||
* @return an Isf::Drawing, with null contents on error
|
||||
*/
|
||||
Drawing& Stream::reader( const QByteArray& rawData, bool decodeFromBase64 )
|
||||
{
|
||||
// Create a new drawing on the heap to ensure it will keep
|
||||
// living after this method returns
|
||||
Drawing* drawing = new Drawing();
|
||||
|
||||
ParserState state = ISF_PARSER_START;
|
||||
|
||||
streamData_ = new StreamData();
|
||||
streamData_->dataSource = new DataSource( decodeFromBase64
|
||||
? QByteArray::fromBase64( rawData )
|
||||
: rawData );
|
||||
|
||||
int size = streamData_->dataSource->size();
|
||||
if( size == 0 )
|
||||
{
|
||||
state = ISF_PARSER_FINISH;
|
||||
drawing->error_ = ISF_ERROR_BAD_STREAMSIZE;
|
||||
}
|
||||
|
||||
while( state != ISF_PARSER_FINISH )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
case ISF_PARSER_START:
|
||||
{
|
||||
// step 1: read ISF version.
|
||||
quint8 version = decodeUInt( streamData_->dataSource );
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Version:" << version;
|
||||
#endif
|
||||
if ( version != SUPPORTED_ISF_VERSION )
|
||||
{
|
||||
drawing->error_ = ISF_ERROR_BAD_VERSION;
|
||||
drawing->isNull_ = true;
|
||||
state = ISF_PARSER_FINISH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// version is OK. find ISF stream size next.
|
||||
state = ISF_PARSER_STREAMSIZE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ISF_PARSER_STREAMSIZE:
|
||||
{
|
||||
// read ISF stream size.
|
||||
// check it matches the length of the data array.
|
||||
quint64 streamSize = decodeUInt( streamData_->dataSource );
|
||||
|
||||
if ( streamSize != (quint64)( streamData_->dataSource->size() - streamData_->dataSource->pos() ) )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Invalid stream size" << streamSize
|
||||
<< ", expected" << ( streamData_->dataSource->size() - streamData_->dataSource->pos() );
|
||||
#endif
|
||||
// streamsize is bad.
|
||||
drawing->error_ = ISF_ERROR_BAD_STREAMSIZE;
|
||||
state = ISF_PARSER_FINISH;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Reading ISF stream of size:" << streamSize << "...";
|
||||
#endif
|
||||
// Validate the drawing
|
||||
drawing->isNull_ = false;
|
||||
|
||||
// start looking for ISF tags.
|
||||
state = ISF_PARSER_TAG;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ISF_PARSER_TAG:
|
||||
{
|
||||
if( streamData_->dataSource->atEnd() )
|
||||
{
|
||||
state = ISF_PARSER_FINISH;
|
||||
break;
|
||||
}
|
||||
|
||||
// Read the next tag from the stream
|
||||
drawing->error_ = TagsParser::nextTag( streamData_, drawing );
|
||||
|
||||
if( drawing->error_ != ISF_ERROR_NONE )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qWarning() << "Error in last operation, stopping.";
|
||||
#endif
|
||||
state = ISF_PARSER_FINISH;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Should never arrive here! It's here only to avoid compiler warnings.
|
||||
case ISF_PARSER_FINISH:
|
||||
break;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Finished with" << ( drawing->error_ == ISF_ERROR_NONE ? "success" : "error" );
|
||||
qDebug();
|
||||
#endif
|
||||
|
||||
delete streamData_->dataSource;
|
||||
|
||||
if( drawing->error_ != ISF_ERROR_NONE )
|
||||
{
|
||||
delete streamData_;
|
||||
streamData_ = 0;
|
||||
return *drawing;
|
||||
}
|
||||
|
||||
// Perform the last operations on the drawing
|
||||
|
||||
// Adjust the bounding rectangle to include the strokes borders
|
||||
QSize penSize( drawing->maxPenSize_.toSize() );
|
||||
streamData_->boundingRect.adjust( -penSize.width() - 1, -penSize.height() - 1,
|
||||
+penSize.width() + 1, +penSize.height() + 1 );
|
||||
drawing->setBoundingRect( streamData_->boundingRect );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Drawing bounding rectangle:" << drawing->boundingRect();
|
||||
qDebug() << "Maximum thickness:" << drawing->maxPenSize_;
|
||||
#endif
|
||||
|
||||
delete streamData_;
|
||||
streamData_ = 0;
|
||||
return *drawing;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a Fortified-GIF image into a drawing.
|
||||
*
|
||||
* If the GIF image or the ISF data within it are invalid, or if the GIF did not
|
||||
* have any ISF stream within, then a null Drawing is returned.
|
||||
*
|
||||
* Please note that this method does nothing if Isf-Qt is compiled
|
||||
* withous GIF support. Use Stream::supportsGif() to verify whether
|
||||
* GIF was compiled in or not.
|
||||
*
|
||||
* @see supportsGif()
|
||||
* @param gifRawBytes Source byte array with a Fortified GIF image
|
||||
* @param decodeFromBase64 True if the bytes are in the Base64 format and
|
||||
* need to be decoded first
|
||||
* @return an Isf::Drawing, with null contents on error
|
||||
*/
|
||||
Drawing& Stream::readerGif( const QByteArray& gifRawBytes, bool decodeFromBase64 )
|
||||
{
|
||||
QByteArray isfData;
|
||||
|
||||
#if ISFQT_GIF_ENABLED == 1
|
||||
|
||||
QByteArray gifBytes( decodeFromBase64
|
||||
? QByteArray::fromBase64( gifRawBytes )
|
||||
: gifRawBytes );
|
||||
|
||||
/**
|
||||
* With the commented code below, it would all have been so easy, but no!
|
||||
* DGifGetComment is NOT PRESENT IN THE LIBRARY despite being in the giflib
|
||||
* header file. And EGifPutComment is present!
|
||||
* It doesn't work, but hey, at least it's there. (see below)
|
||||
*/
|
||||
/*
|
||||
QBuffer gifData( &gifBytes );
|
||||
gifData.open( QIODevice::ReadOnly );
|
||||
|
||||
// Open the gif file
|
||||
GifFileType* gifImage = DGifOpen( (void*)&gifData, GifReadFromByteArray );
|
||||
if( gifImage != 0 )
|
||||
{
|
||||
DGifGetComment( gifImage, data?? );
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "Couldn't initialize GIF library!";
|
||||
}
|
||||
|
||||
DGifCloseFile( gifImage );
|
||||
gifData.close();
|
||||
*/
|
||||
|
||||
// Find the last 'comment' type tag: it should be the last thing in the file...
|
||||
qint32 size = 0;
|
||||
qint32 position = 0;
|
||||
qint32 maxDataPosition = gifBytes.size() - 2; // comment and gif stream ending bytes
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Searching for a stream. Last valid position:" << maxDataPosition;
|
||||
#endif
|
||||
|
||||
while( size == 0 && ( position = gifBytes.lastIndexOf( COMMENT_EXT_FUNC_CODE, position - 1 ) ) >= 0 )
|
||||
{
|
||||
// Skip the comment tag, to have the size byte as current char
|
||||
qint32 lastPosition = position + 1;
|
||||
|
||||
// The next character after the tag can't be an ISF stream start, skip
|
||||
if( gifBytes[ position + 2 ] != '\0' )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to read the stream
|
||||
quint8 sizeByte;
|
||||
do
|
||||
{
|
||||
sizeByte = gifBytes[ lastPosition ];
|
||||
|
||||
// Skip the size byte
|
||||
lastPosition++;
|
||||
|
||||
isfData.append( gifBytes.mid( lastPosition, sizeByte ) );
|
||||
lastPosition += sizeByte;
|
||||
}
|
||||
while( sizeByte == MAX_GIF_BYTE && ( lastPosition <= maxDataPosition ) );
|
||||
|
||||
// We found the ISF stream!
|
||||
if( lastPosition == maxDataPosition )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Found an ISF stream of size" << isfData.size();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Stream not found at position:" << position
|
||||
<< "size:" << ( lastPosition - position );
|
||||
#endif
|
||||
isfData.clear();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ISFQT_GIF_ENABLED == 1
|
||||
|
||||
return reader( isfData );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a Fortified-PNG image into a drawing.
|
||||
*
|
||||
* If the PNG image or the ISF data within it are invalid, or if the PNG did not
|
||||
* have any ISF stream within, then a null Drawing is returned.
|
||||
*
|
||||
* @param gifRawBytes Source byte array with a Fortified PNG image
|
||||
* @param decodeFromBase64 True if the bytes are in the Base64 format and
|
||||
* need to be decoded first
|
||||
* @return an Isf::Drawing, with null contents on error
|
||||
*/
|
||||
Drawing& Stream::readerPng( const QByteArray& pngRawBytes, bool decodeFromBase64 )
|
||||
{
|
||||
QByteArray isfData;
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Reading a PNG-Fortified file";
|
||||
#endif
|
||||
|
||||
QByteArray pngBytes( decodeFromBase64
|
||||
? QByteArray::fromBase64( pngRawBytes )
|
||||
: pngRawBytes );
|
||||
|
||||
QImage imageData( QImage::fromData( pngBytes, "PNG" ) );
|
||||
if( ! imageData.isNull() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Picture data is valid: checking for the ISF data tag...";
|
||||
#endif
|
||||
isfData = imageData.text( "application/x-ms-ink" ).toLatin1();
|
||||
|
||||
if( ! isfData.isEmpty() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "ISF data found! Decoding from Base64 and parsing it...";
|
||||
#endif
|
||||
isfData = QByteArray::fromBase64( isfData );
|
||||
}
|
||||
}
|
||||
|
||||
return reader( isfData );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return whether the library was built with Fortified GIF support or not.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool Stream::supportsGif()
|
||||
{
|
||||
return ( ISFQT_GIF_ENABLED == true );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a drawing into a raw ISF data stream.
|
||||
*
|
||||
* The resulting byte array will be empty if the drawing is not valid.
|
||||
*
|
||||
* @param drawing Source drawing
|
||||
* @param encodeToBase64 Whether the converted ISF stream should be
|
||||
* encoded with Base64 or not
|
||||
* @return Byte array with an ISF data stream
|
||||
*/
|
||||
QByteArray Stream::writer( const Drawing& drawing, bool encodeToBase64 )
|
||||
{
|
||||
if( drawing.isNull() || drawing.error() != ISF_ERROR_NONE )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "The drawing was not valid!";
|
||||
#endif
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
streamData_ = new StreamData();
|
||||
streamData_->dataSource = new DataSource();
|
||||
DataSource* dataSource = streamData_->dataSource;
|
||||
|
||||
// Add the initial data
|
||||
TagsWriter::prepare( streamData_, &drawing );
|
||||
|
||||
// Write the persistent format tag
|
||||
TagsWriter::addPersistentFormat( streamData_, &drawing );
|
||||
|
||||
// Write the drawing size
|
||||
TagsWriter::addHiMetricSize( streamData_, &drawing );
|
||||
|
||||
// Write the attributes
|
||||
TagsWriter::addAttributeTable( streamData_, &drawing );
|
||||
|
||||
// Write the metrics
|
||||
TagsWriter::addMetricsTable( streamData_, &drawing );
|
||||
|
||||
// Write the transforms
|
||||
TagsWriter::addTransformationTable( streamData_, &drawing );
|
||||
|
||||
// Write the strokes
|
||||
TagsWriter::addStrokes( streamData_, &drawing );
|
||||
|
||||
// Write the stream size (at the start of the stream)
|
||||
encodeUInt( dataSource, dataSource->size(), true/*prepend*/ );
|
||||
|
||||
// Write the version number (at the start of the stream)
|
||||
encodeUInt( dataSource, SUPPORTED_ISF_VERSION, true/*prepend*/ );
|
||||
|
||||
QByteArray data( dataSource->data() );
|
||||
|
||||
delete streamData_->dataSource;
|
||||
delete streamData_;
|
||||
streamData_ = 0;
|
||||
|
||||
// Convert to Base64 if needed
|
||||
if( encodeToBase64 )
|
||||
{
|
||||
return data.toBase64();
|
||||
}
|
||||
else
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a drawing into a Fortified-GIF image.
|
||||
*
|
||||
* The resulting byte array will be empty if the drawing is not valid.
|
||||
*
|
||||
* Please note that this method does nothing if Isf-Qt is compiled
|
||||
* withous GIF support. Use Stream::supportsGif() to verify whether
|
||||
* GIF was compiled in or not.
|
||||
*
|
||||
* The Fortified-GIF format is nothing more than a GIF image with the original
|
||||
* ISF drawing added as a GIF Comment field.
|
||||
*
|
||||
* @see supportsGif()
|
||||
* @param drawing Source drawing
|
||||
* @param encodeToBase64 Whether the converted GIF should be
|
||||
* encoded with Base64 or not
|
||||
* @return Byte array with a GIF data stream (optionally encoded with Base64)
|
||||
*/
|
||||
QByteArray Stream::writerGif( const Drawing& drawing, bool encodeToBase64 )
|
||||
{
|
||||
QByteArray imageBytes;
|
||||
|
||||
#if ISFQT_GIF_ENABLED == 1
|
||||
Drawing source( drawing );
|
||||
|
||||
// Get the ISF data stream
|
||||
QByteArray isfData( writer( source ) );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "GIF-Fortifying an ISF stream of size" << isfData.size();
|
||||
#endif
|
||||
|
||||
// Get the ISF pixmap and copy the pixels to an 8bpp image
|
||||
QImage isfImage( source.pixmap().toImage()
|
||||
.convertToFormat( QImage::Format_Indexed8,
|
||||
Qt::ThresholdDither ) );
|
||||
|
||||
// Initialise the gif variables
|
||||
QBuffer gifData;
|
||||
GifFileType* gifImage = NULL;
|
||||
ColorMapObject* cmap = NULL;
|
||||
int height = isfImage.height();
|
||||
int width = isfImage.width();
|
||||
int colorCount = 0;
|
||||
int numColors = 0;
|
||||
int gifErrorCode = 0;
|
||||
bool gifError = true;
|
||||
|
||||
// Convert the image to GIF using libgif
|
||||
|
||||
// Open the gif file
|
||||
gifData.open( QIODevice::WriteOnly );
|
||||
gifImage = EGifOpen( (void*)&gifData, GifWriteToByteArray, &gifErrorCode );
|
||||
if( gifImage == 0 )
|
||||
{
|
||||
qWarning() << "Couldn't initialize gif library!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
// Create the color map
|
||||
colorCount = isfImage.colorCount();
|
||||
numColors = 2;
|
||||
while( numColors < colorCount && numColors < 256 )
|
||||
{
|
||||
numColors <<= 1;
|
||||
}
|
||||
|
||||
cmap = GifMakeMapObject( numColors, NULL );
|
||||
if( cmap == 0 && colorCount > 1 )
|
||||
{
|
||||
qWarning() << "Couldn't create map object for gif conversion (colors:" << colorCount << ")!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
// Fill in the color map with the colors in the image color table
|
||||
for( int i = 0; i < colorCount && i < numColors; ++i )
|
||||
{
|
||||
const QRgb &color( isfImage.color( i ) );
|
||||
cmap->Colors[i].Red = qRed ( color );
|
||||
cmap->Colors[i].Green = qGreen( color );
|
||||
cmap->Colors[i].Blue = qBlue ( color );
|
||||
}
|
||||
|
||||
// Save the file properties
|
||||
if( EGifPutScreenDesc( gifImage, width, height, 8, 0, cmap ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutScreenDesc() failed!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
// Save the image format
|
||||
if( EGifPutImageDesc( gifImage, 0, 0, width, height, 0, NULL ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutImageDesc() failed!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FIXME: If to write the scanlines you use
|
||||
* EGifPutLine( gifImage, isfImage.bits(), isfImage.width() * isfImage.height() )
|
||||
* i.e. convert the complete image in one call, then the resulting image is mangled.
|
||||
* Something is wrong with the width or so, it seems to be off by about two pixels.
|
||||
*/
|
||||
// Write every scanline
|
||||
for( int line = 0; line < height; ++line )
|
||||
{
|
||||
if( EGifPutLine( gifImage, reinterpret_cast<GifPixelType*>( isfImage.scanLine( line ) ), width ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutLine failed at scanline" << line
|
||||
<< "(height:" << isfImage.height()
|
||||
<< ", width:" << isfImage.width()
|
||||
<< ", bytesperline:" << isfImage.bytesPerLine() << ")";
|
||||
goto writeError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Completing the funny theater that is giflib, EGifPutComment() doesn't
|
||||
* work, or I've overlooked something Extremely Obvious(tm).
|
||||
* Googling didn't help: I rewrote it (from the giflib source) with
|
||||
* Qt.
|
||||
*/
|
||||
/*
|
||||
if( EGifPutComment( gifImage, isfData.constData() ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutComment has failed!";
|
||||
goto writeError;
|
||||
}
|
||||
*/
|
||||
|
||||
// Write the ISF stream into the Comment Extension field
|
||||
if( isfData.size() < MAX_GIF_BYTE )
|
||||
{
|
||||
EGifPutExtension( gifImage, COMMENT_EXT_FUNC_CODE, isfData.size(), isfData.constData() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write the extension
|
||||
if( EGifPutExtensionLeader( gifImage, COMMENT_EXT_FUNC_CODE ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutExtensionLeader failed!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
if( EGifPutExtensionBlock( gifImage, MAX_GIF_BYTE, isfData.left( MAX_GIF_BYTE ).constData() ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutExtensionBlock failed!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
// The first MAX_GIF_BYTE bytes have been written already
|
||||
quint32 pos = MAX_GIF_BYTE;
|
||||
|
||||
quint32 length = ( isfData.size() - pos );
|
||||
|
||||
// Write all the full data blocks
|
||||
while( length >= MAX_GIF_BYTE )
|
||||
{
|
||||
if( EGifPutExtensionBlock( gifImage, MAX_GIF_BYTE, isfData.mid( pos, MAX_GIF_BYTE ).constData() ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutExtensionBlock failed!";
|
||||
goto writeError;
|
||||
}
|
||||
|
||||
pos += MAX_GIF_BYTE;
|
||||
length -= MAX_GIF_BYTE;
|
||||
}
|
||||
|
||||
// Write the last block
|
||||
if( length > 0 )
|
||||
{
|
||||
if( EGifPutExtensionBlock( gifImage, length, isfData.mid( pos, MAX_GIF_BYTE ).constData() ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutExtensionBlock (tail) failed!";
|
||||
goto writeError;
|
||||
}
|
||||
}
|
||||
|
||||
if( EGifPutExtensionTrailer( gifImage ) == GIF_ERROR )
|
||||
{
|
||||
qWarning() << "EGifPutExtensionTrailer failed!";
|
||||
goto writeError;
|
||||
}
|
||||
}
|
||||
|
||||
gifError = false;
|
||||
|
||||
writeError:
|
||||
// Clean up the GIF converter etc
|
||||
if( gifImage != 0 && EGifCloseFile( gifImage, &gifErrorCode ) == GIF_ERROR )
|
||||
{
|
||||
gifError = true;
|
||||
}
|
||||
if( cmap != 0 )
|
||||
{
|
||||
GifFreeMapObject( cmap );
|
||||
}
|
||||
gifData.close();
|
||||
|
||||
if( gifError )
|
||||
{
|
||||
qWarning() << "GIF error code:" << gifErrorCode << GifErrorString( gifErrorCode );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the GIF data
|
||||
imageBytes = gifData.data();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Converted a" << isfData.size() << "bytes Ink to GIF:" << isfImage.height() << "x" << isfImage.width() << "->" << imageBytes.size() << "bytes";
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // ISFQT_GIF_ENABLED == 1
|
||||
|
||||
|
||||
// Convert to Base64 if needed
|
||||
if( encodeToBase64 )
|
||||
{
|
||||
return imageBytes.toBase64();
|
||||
}
|
||||
else
|
||||
{
|
||||
return imageBytes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert a drawing into a Fortified-PNG image.
|
||||
*
|
||||
* The resulting byte array will be empty if the drawing is not valid.
|
||||
*
|
||||
* The Fortified-PNG format is nothing more than a PNG image with the original
|
||||
* ISF drawing added as a PNG text field.
|
||||
*
|
||||
* @param drawing Source drawing
|
||||
* @param encodeToBase64 Whether the converted ISF stream should be
|
||||
* encoded with Base64 or not
|
||||
* @return Byte array with a PNG data stream (optionally encoded with Base64)
|
||||
*/
|
||||
QByteArray Stream::writerPng( const Drawing& drawing, bool encodeToBase64 )
|
||||
{
|
||||
Drawing source( drawing );
|
||||
|
||||
// Get the ISF data stream
|
||||
QByteArray isfData( writer( source ) );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "PNG-Fortifying an ISF stream of size" << isfData.size();
|
||||
#endif
|
||||
|
||||
// Get the actual image
|
||||
QImage isfImage( source.pixmap().toImage() );
|
||||
|
||||
// Add to it the Base64 version of the ISF drawing, as a comment and in Base64 form
|
||||
// (that's because the PNG text fields are only meant to hold text)
|
||||
isfImage.setText( "application/x-ms-ink", isfData.toBase64() );
|
||||
|
||||
// Save it as a PNG image
|
||||
QBuffer imageBytes;
|
||||
imageBytes.open( QIODevice::WriteOnly );
|
||||
isfImage.save( &imageBytes, "PNG" );
|
||||
imageBytes.close();
|
||||
|
||||
// Convert to Base64 if needed
|
||||
if( encodeToBase64 )
|
||||
{
|
||||
return imageBytes.data().toBase64();
|
||||
}
|
||||
else
|
||||
{
|
||||
return imageBytes.data();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,729 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include "data/datasource.h"
|
||||
#include "data/multibytecoding.h"
|
||||
#include "tagsparser.h"
|
||||
|
||||
#include <IsfQtDrawing>
|
||||
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
using namespace Compress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct a new empty (null) Drawing instance.
|
||||
*
|
||||
* As soon as you add data, the instance becomes non-NULL.
|
||||
*/
|
||||
Drawing::Drawing()
|
||||
: error_( ISF_ERROR_NONE )
|
||||
, hasXData_( true )
|
||||
, hasYData_( true )
|
||||
, isNull_( true )
|
||||
, maxGuid_( 0 )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "** Created new ISF drawing:" << this << "**";
|
||||
#endif
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Construct a copy of an existing Drawing instance.
|
||||
*
|
||||
* @param other The instance to duplicate.
|
||||
*/
|
||||
Drawing::Drawing( const Drawing& other )
|
||||
: boundingRect_( other.boundingRect_ )
|
||||
, canvas_( other.canvas_ )
|
||||
, error_( other.error_ )
|
||||
, guids_( other.guids_ )
|
||||
, hasXData_( other.hasXData_ )
|
||||
, hasYData_( other.hasYData_ )
|
||||
, isNull_( other.isNull_ )
|
||||
, maxGuid_( other.maxGuid_ )
|
||||
, maxPenSize_( other.maxPenSize_ )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "** Copying ISF drawing:" << (void*)&other << "into new:" << this << "**";
|
||||
#endif
|
||||
|
||||
// Clone the stroke objects
|
||||
foreach( Stroke* stroke, other.strokes_ )
|
||||
{
|
||||
Stroke* newStroke = new Stroke( *stroke );
|
||||
strokes_.append( newStroke );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
Drawing::~Drawing()
|
||||
{
|
||||
qDeleteAll( strokes_ );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "** Destroyed ISF drawing:" << this << "**";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a new stroke to the drawing.
|
||||
*
|
||||
* @param newStroke The stroke to add
|
||||
* @return Index of the new stroke or -1 on failure
|
||||
*/
|
||||
qint32 Drawing::addStroke( PointList points )
|
||||
{
|
||||
Stroke* newStroke = new Stroke();
|
||||
|
||||
newStroke->addPoints( points );
|
||||
newStroke->finalize();
|
||||
|
||||
return addStroke( newStroke );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a new stroke to the drawing.
|
||||
*
|
||||
* @param newStroke The stroke to add
|
||||
* @return Index of the new stroke or -1 on failure
|
||||
*/
|
||||
qint32 Drawing::addStroke( Stroke* newStroke )
|
||||
{
|
||||
if( newStroke == 0 )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
isNull_ = false;
|
||||
strokes_.append( newStroke );
|
||||
maxPenSize_ = maxPenSize_.width() < newStroke->penSize().width() ? newStroke->penSize() : maxPenSize_;
|
||||
|
||||
// This stroke needs to be painted
|
||||
changedStrokes_.append( newStroke );
|
||||
|
||||
dirty_ = true;
|
||||
|
||||
updateBoundingRect();
|
||||
|
||||
return ( strokes_.count() - 1 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the current bounding rectangle of the drawing.
|
||||
*
|
||||
* The bounding rectangle (or bounding box) is a QRect large enough (and as
|
||||
* small as) to hold all of the strokes in this Drawing instance.
|
||||
*
|
||||
* @return The current bounding box.
|
||||
*/
|
||||
QRect Drawing::boundingRect() const
|
||||
{
|
||||
return boundingRect_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clean up the drawing.
|
||||
*
|
||||
* This restores the drawing to its initial state: an empty (null) drawing.
|
||||
*/
|
||||
void Drawing::clear()
|
||||
{
|
||||
// Clean up the internal property lists
|
||||
qDeleteAll( strokes_ );
|
||||
strokes_ .clear();
|
||||
changedStrokes_.clear();
|
||||
|
||||
// Nullify the other properties
|
||||
boundingRect_ = QRect();
|
||||
canvas_ = QRect();
|
||||
cacheRect_ = QRect();
|
||||
error_ = ISF_ERROR_NONE;
|
||||
hasXData_ = true;
|
||||
hasYData_ = true;
|
||||
isNull_ = true;
|
||||
maxGuid_ = 0;
|
||||
maxPenSize_ = QSizeF();
|
||||
dirty_ = false;
|
||||
cachePixmap_ = QPixmap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Remove a stroke from the drawing.
|
||||
*
|
||||
* Delete a stroke object from the drawing.
|
||||
* @return bool
|
||||
*/
|
||||
bool Drawing::deleteStroke( quint32 index )
|
||||
{
|
||||
if( (qint64)index >= strokes_.count() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Stroke* victim = strokes_.takeAt( index );
|
||||
|
||||
// make sure this goes from the changedStrokes_ list too.
|
||||
changedStrokes_.removeAll( victim );
|
||||
|
||||
delete victim;
|
||||
|
||||
dirty_ = true;
|
||||
|
||||
updateBoundingRect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Delete a stroke object from the drawing.
|
||||
*
|
||||
* @param stroke The stroke to remove.
|
||||
* @return bool
|
||||
*/
|
||||
bool Drawing::deleteStroke( Stroke* stroke )
|
||||
{
|
||||
if ( ! strokes_.contains( stroke ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return deleteStroke( strokes_.indexOf( stroke ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the last error that has occurred.
|
||||
*
|
||||
* If nothing went wrong, this returns ISF_ERROR_NONE.
|
||||
*
|
||||
* @return The last ISF error status
|
||||
*/
|
||||
IsfError Drawing::error() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the index of a certain stroke.
|
||||
*
|
||||
* @param stroke Stroke to search
|
||||
* @return Index or -1 if not found
|
||||
*/
|
||||
qint32 Drawing::indexOfStroke( const Stroke* stroke ) const
|
||||
{
|
||||
return strokes_.indexOf( const_cast<Stroke*>( stroke ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return whether this drawing is empty.
|
||||
*
|
||||
* A Drawing is empty when there are no strokes in it.
|
||||
*
|
||||
* @return True if this is an empty Drawing, false otherwise.
|
||||
*/
|
||||
bool Drawing::isEmpty() const
|
||||
{
|
||||
return strokes_.empty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return whether this drawing is null.
|
||||
*
|
||||
* A Drawing is null when there are no strokes, attributes or anything in it,
|
||||
* for example when it has just been initialized, cleared, or when a copy from
|
||||
* another drawing fails.
|
||||
*
|
||||
* @return True if this is an null Drawing, false otherwise.
|
||||
*/
|
||||
bool Drawing::isNull() const
|
||||
{
|
||||
return isNull_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Render the drawing into an image.
|
||||
*
|
||||
* If the drawing is empty, the pixmap will be null.
|
||||
* Also note that the maximum renderable size is 2000x2000 pixels: if the strokes
|
||||
* exceed by even one dimension, the pixmap will not be rendered.
|
||||
* This is a security measure, since the pixmaps are allocated by the graphics
|
||||
* system (X on *nix, for example) and a machine could be DoSed with a very large
|
||||
* drawing.
|
||||
*
|
||||
* @param backgroundColor The color used as background in the returned image.
|
||||
* Default is transparent.
|
||||
* @return The rendered drawing, or a null one on error.
|
||||
*/
|
||||
QPixmap Drawing::pixmap( const QColor backgroundColor )
|
||||
{
|
||||
if( isNull() )
|
||||
{
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
if( ! dirty_ && ! cachePixmap_.isNull() )
|
||||
{
|
||||
return cachePixmap_;
|
||||
}
|
||||
|
||||
QSize drawingSize( size() );
|
||||
|
||||
if( drawingSize.width() > 2000 || drawingSize.height() > 2000 )
|
||||
{
|
||||
qWarning() << "Cannot render a drawing so big!";
|
||||
qDebug() << "[Information - Size:" << drawingSize << "pixels]";
|
||||
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
// is the cache null, or are we repainting everything? if so, create a new pixmap.
|
||||
if( cachePixmap_.isNull() || changedStrokes_.isEmpty() )
|
||||
{
|
||||
cachePixmap_ = QPixmap( drawingSize );
|
||||
cachePixmap_.fill( backgroundColor );
|
||||
cacheRect_ = boundingRect_;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise, resize and repaint the cache.
|
||||
|
||||
QRect newRect = boundingRect_;
|
||||
|
||||
// has the size of the drawing changed? if so, resize the cachePixmap_.
|
||||
if( cacheRect_.size() != newRect.size() )
|
||||
{
|
||||
// qDebug() << "Cache pixmap needs resizing to" << drawingSize;
|
||||
// qDebug() << "Cache rect:" << cacheRect_;
|
||||
// qDebug() << "New rect:" << newRect;
|
||||
|
||||
QPixmap pixmap( drawingSize );
|
||||
pixmap.fill( backgroundColor );
|
||||
QPainter painter( &pixmap );
|
||||
|
||||
int xOffset = ( newRect.x() - cacheRect_.x() ) * -1;
|
||||
int yOffset = ( newRect.y() - cacheRect_.y() ) * -1;
|
||||
|
||||
// qDebug() << "x-offset:"<<xOffset<<", y-offset:"<<yOffset;
|
||||
painter.drawPixmap( xOffset, yOffset, cachePixmap_ );
|
||||
|
||||
cachePixmap_ = pixmap;
|
||||
cacheRect_ = newRect;
|
||||
}
|
||||
}
|
||||
|
||||
// if we're told specifically what strokes to paint, only paint those ones.
|
||||
// otherwise, paint all of them.
|
||||
|
||||
QList<Stroke*> strokes = ( changedStrokes_.isEmpty() ? strokes_ : changedStrokes_ );
|
||||
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Rendering a drawing of size" << drawingSize;
|
||||
#endif
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Rendering" << strokes.count() << "out of" << strokes_.count() << "strokes in the drawing.";
|
||||
#endif
|
||||
|
||||
// if there are no strokes there's no point going
|
||||
// through the rest of this logic.
|
||||
if ( strokes.count() == 0 )
|
||||
{
|
||||
return cachePixmap_;
|
||||
}
|
||||
|
||||
QPainter painter( &cachePixmap_ );
|
||||
|
||||
painter.setWindow( boundingRect_ );
|
||||
painter.setWorldMatrixEnabled( true );
|
||||
painter.setRenderHints( QPainter::Antialiasing
|
||||
| QPainter::SmoothPixmapTransform
|
||||
| QPainter::TextAntialiasing,
|
||||
true );
|
||||
|
||||
QPen pen;
|
||||
pen.setStyle ( Qt::SolidLine );
|
||||
pen.setCapStyle ( Qt::RoundCap );
|
||||
pen.setJoinStyle( Qt::RoundJoin );
|
||||
|
||||
// Keep record of the last used properties, to avoid re-setting them for each stroke
|
||||
AttributeSet currentAttributes;
|
||||
Metrics* currentMetrics = 0;
|
||||
QMatrix* currentTransform = 0;
|
||||
|
||||
int index = 0;
|
||||
foreach( Stroke* stroke, strokes )
|
||||
{
|
||||
if( currentAttributes.color != stroke->color()
|
||||
|| currentAttributes.flags != stroke->flags()
|
||||
|| currentAttributes.penSize != stroke->penSize() )
|
||||
{
|
||||
currentAttributes.color = stroke->color();
|
||||
currentAttributes.flags = stroke->flags();
|
||||
currentAttributes.penSize = stroke->penSize();
|
||||
|
||||
pen.setColor( stroke->color() );
|
||||
pen.setWidthF( stroke->penSize().width() );
|
||||
painter.setPen( pen );
|
||||
}
|
||||
if( stroke->metrics() && currentMetrics != stroke->metrics() )
|
||||
{
|
||||
currentMetrics = stroke->metrics();
|
||||
// TODO need to convert all units somehow?
|
||||
// painter.setSomething( currentMetrics );
|
||||
}
|
||||
if( stroke->transform() && currentTransform != stroke->transform() )
|
||||
{
|
||||
currentTransform = stroke->transform();
|
||||
painter.setWorldTransform( QTransform( *currentTransform ), false );
|
||||
|
||||
// the problem with setting the world transform is that it will scale the pen size too.
|
||||
// we don't want that. so we have to artificially beef up the pen size.
|
||||
QPen pen = painter.pen();
|
||||
pen.setWidthF( stroke->penSize().width() / currentTransform->m22() );
|
||||
|
||||
/*
|
||||
// FIXME Ignoring pressure data - need to find out how pressure must be applied
|
||||
pen.setWidth( pen.widthF() + points??.pressureLevel );
|
||||
*/
|
||||
|
||||
painter.setPen( pen );
|
||||
painter.setBrush( Qt::transparent );
|
||||
}
|
||||
|
||||
const PointList& points = stroke->points();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Rendering stroke" << index << "containing" << stroke->points().count() << "points";
|
||||
qDebug() << "- Stroke color:" << stroke->color().name() << "Pen size:" << pen.widthF();
|
||||
#endif
|
||||
|
||||
++index;
|
||||
|
||||
if( points.count() == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: pressure data.
|
||||
if( points.count() > 1 )
|
||||
{
|
||||
painter.drawPath( stroke->painterPath() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Point point = stroke->points().first();
|
||||
|
||||
// qDebug() << "Point:" << point.position;
|
||||
painter.drawPoint( point.position );
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
// Draw the stroke number next to each one, for debugging purposes
|
||||
pen.setColor( QColor( Qt::red ) );
|
||||
painter.setPen( pen );
|
||||
painter.drawText( stroke.points.first().position, QString::number( index ) );
|
||||
pen.setColor( currentAttributeSet_->color );
|
||||
painter.setPen( pen );
|
||||
#endif
|
||||
*/
|
||||
}
|
||||
|
||||
painter.end();
|
||||
|
||||
changedStrokes_.clear();
|
||||
|
||||
#ifdef ISFQT_DEBUG
|
||||
qDebug() << "Rendering complete.";
|
||||
#endif
|
||||
|
||||
dirty_ = false;
|
||||
|
||||
return cachePixmap_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change the bounding rectangle of the drawing.
|
||||
*
|
||||
* Adding, removing or changing the strokes will change it back to the real rectangle.
|
||||
*
|
||||
* @param newRect the new bounding rectangle
|
||||
*/
|
||||
void Drawing::setBoundingRect( QRect newRect )
|
||||
{
|
||||
boundingRect_ = newRect;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the size of this drawing.
|
||||
*
|
||||
* @return Size of the drawing, in pixels.
|
||||
*/
|
||||
QSize Drawing::size() const
|
||||
{
|
||||
return boundingRect_.size();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a stroke to manipulate.
|
||||
*
|
||||
* @param index Index of the stroke to get
|
||||
* @return Stroke or 0 if not found
|
||||
*/
|
||||
Stroke* Drawing::stroke( quint32 index )
|
||||
{
|
||||
if( (qint64)index >= strokes_.count() )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strokes_.at( index );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the Stroke under a certain point.
|
||||
*
|
||||
* Given a QPoint, return the Stroke object that passes through there.
|
||||
* If no stroke passes through that point, returns NULL.
|
||||
*
|
||||
* If multiple Strokes pass through that point the most recently drawn stroke
|
||||
* will be returned.
|
||||
*
|
||||
* @param point Point to check
|
||||
* @return A Stroke instance or NULL if no Stroke passes through that point.
|
||||
*/
|
||||
Stroke* Drawing::strokeAtPoint( const QPoint& point )
|
||||
{
|
||||
/*
|
||||
Here's how this algorithm works:
|
||||
|
||||
1) Iterate through strokes in reverse order.
|
||||
2) For each stroke, check if the bounding rectangle contains the point where the cursor is.
|
||||
If not, continue to the next stroke. Prevents us checking all the strokes.
|
||||
3) Next, iterate through each pair of points in the stroke. If both of the points fall outside
|
||||
of the searching rectangle around the cursor, skip. Saves us checking hundreds of points when
|
||||
only a few will do.
|
||||
4) a) For each pair of strokes, form a triangle whose points are defined by the two points for the
|
||||
line segment, plus the cursor position.
|
||||
b) Calculate the height of this triangle. To do so, use Heron's Formula plus the formula for the
|
||||
area of any triangle.
|
||||
c) If the cursor is touching the drawn stroke, the height should be less than or equal to the
|
||||
half-width of the pen that drew the stroke.
|
||||
|
||||
For reference (triangle sides a, b, c, height h)
|
||||
Heron's Formula: area = sqrt(s * (s - a) * (s - b) * (s - c) ), where s = semiperimeter = 0.5*(a+b+c)
|
||||
area = 0.5*base*height;
|
||||
thus height = ( 2 * area ) / base.
|
||||
*/
|
||||
|
||||
QListIterator<Stroke* > i( strokes_ );
|
||||
i.toBack();
|
||||
|
||||
while( i.hasPrevious() )
|
||||
{
|
||||
Stroke* s = i.previous();
|
||||
|
||||
// skip strokes where we're not near.
|
||||
if ( ! s->boundingRect().contains( point ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// what's the pen size of this stroke? That way we have a "fudge factor"
|
||||
float penSize = s->penSize().width();
|
||||
float penHalfSize = penSize / 2;
|
||||
float penHalfSizeFixed = penHalfSize * 1.25;
|
||||
|
||||
// only want points that fall near the cursor. prevents searching unnecessary points.
|
||||
QRect searchRect;
|
||||
|
||||
// search rect must accommodate pen size.
|
||||
// the large the pen size, the bigger our search area has to be.
|
||||
//
|
||||
// minimum search area of 10x10 pixels.
|
||||
if ( penHalfSize > 5 )
|
||||
{
|
||||
// 25% extra room to move.
|
||||
searchRect = QRect( point.x() - penHalfSizeFixed,
|
||||
point.y() - penHalfSizeFixed,
|
||||
penHalfSizeFixed, penHalfSizeFixed );
|
||||
}
|
||||
else
|
||||
{
|
||||
searchRect = QRect( point.x() - 5, point.y() - 5, 10, 10 );
|
||||
}
|
||||
|
||||
// special case: a single point (sometimes it'll appear as a single point but
|
||||
// be made up of two).
|
||||
const PointList& points( s->points() );
|
||||
if( points.size() == 1 || points.size() == 2 )
|
||||
{
|
||||
QLineF dist = QLineF( QPointF( points.at(0).position ), QPointF( point ) );
|
||||
if( dist.length() <= penHalfSizeFixed )
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// multiple points.
|
||||
for( int j = 0; j < points.size() - 1; j++)
|
||||
{
|
||||
QPoint p1 = points.at(j).position;
|
||||
QPoint p2 = points.at(j+1).position;
|
||||
|
||||
if( ! searchRect.contains( p1 ) && ! searchRect.contains( p2 ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
QPointF cursorPos = QPointF( point );
|
||||
QLineF base = QLineF( QPointF( p1 ), QPointF( p2 ) );
|
||||
QLineF lineA = QLineF( QPointF( p1 ), cursorPos );
|
||||
QLineF lineC = QLineF( QPointF( p2 ), cursorPos );
|
||||
|
||||
// picture a triangle made up of the two points for the line segment, plus the
|
||||
// cursor position. The height of the triangle is the distance from the cursor point
|
||||
// to the line segment. If the cursor lies on the line, then the height should be less than
|
||||
// or equal to the half-width of the pen that drew the line.
|
||||
//
|
||||
// so, use Heron's Formula to get the area, plus A=0.5*base*height, re-arrange to get height.
|
||||
//
|
||||
// easy!
|
||||
float sp = 0.5 * ( lineA.length() + base.length() + lineC.length() );
|
||||
float a = lineA.length();
|
||||
float b = base.length();
|
||||
float c = lineC.length();
|
||||
float area = sqrt( sp * (sp - a) * (sp - b) * (sp - c) );
|
||||
|
||||
float height = ( 2 * area ) / b;
|
||||
|
||||
if ( height <= penHalfSizeFixed )
|
||||
{
|
||||
// got one
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the strokes.
|
||||
*
|
||||
* @return The list of existing strokes
|
||||
*/
|
||||
const QList<Stroke*> Drawing::strokes()
|
||||
{
|
||||
return strokes_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Update the bounding rectangle of the drawing.
|
||||
*/
|
||||
void Drawing::updateBoundingRect()
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
QRect oldRect( boundingRect_ );
|
||||
#endif
|
||||
|
||||
boundingRect_ = QRect();
|
||||
foreach( Stroke* stroke, strokes_ )
|
||||
{
|
||||
const QRect rect( stroke->boundingRect() );
|
||||
QMatrix* transform = stroke->transform();
|
||||
if( transform != 0 )
|
||||
{
|
||||
boundingRect_ = boundingRect_.united( transform->mapRect( rect ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingRect_ = boundingRect_.united( rect );
|
||||
}
|
||||
|
||||
maxPenSize_ = maxPenSize_.width() < stroke->penSize().width() ? stroke->penSize() : maxPenSize_;
|
||||
}
|
||||
|
||||
const QSize penSize( maxPenSize_.toSize() );
|
||||
boundingRect_.adjust( -penSize.width() - 1, -penSize.height() - 1,
|
||||
+penSize.width() + 1, +penSize.height() + 1 );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "Bounding rectangle updated: from" << oldRect << "to" << boundingRect_;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,516 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2010 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2010 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "isfqtstroke.h"
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include <QPainterPath>
|
||||
#include <QVector>
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Stroke::Stroke()
|
||||
: finalized_( true )
|
||||
, metrics_( 0 )
|
||||
, transform_( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param other The object to clone
|
||||
*/
|
||||
Stroke::Stroke( const Stroke& other )
|
||||
{
|
||||
bezierControlPoints1_ = other.bezierControlPoints1_;
|
||||
bezierControlPoints2_ = other.bezierControlPoints2_;
|
||||
bezierKnots_ = other.bezierKnots_;
|
||||
|
||||
boundingRect_ = other.boundingRect_;
|
||||
color_ = other.color_;
|
||||
finalized_ = other.finalized_;
|
||||
flags_ = other.flags_;
|
||||
hasPressureData_ = other.hasPressureData_;
|
||||
metrics_ = other.metrics_;
|
||||
penSize_ = other.penSize_;
|
||||
points_ = other.points_;
|
||||
transform_ = other.transform_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
Stroke::~Stroke()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Stroke::addPoint( Point point )
|
||||
{
|
||||
// Avoid splitting up the logic
|
||||
addPoints( PointList() << point );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Stroke::addPoints( PointList points )
|
||||
{
|
||||
points_.append( points );
|
||||
|
||||
// Hunt for pressure info
|
||||
if( ! hasPressureData_ )
|
||||
{
|
||||
foreach( Point point, points )
|
||||
{
|
||||
if( point.pressureLevel != 0 )
|
||||
{
|
||||
hasPressureData_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finalized_ = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Given a series of known (knot) points, knots, calculates the control points (c1, c2) to approximate a series of
|
||||
* bezier curves passing smoothly through the knot points.
|
||||
*/
|
||||
void Stroke::bezierCalculateControlPoints()
|
||||
{
|
||||
// bezierKnots_.clear();
|
||||
// bezierControlPoints1_.clear();
|
||||
// bezierControlPoints2_.clear();
|
||||
|
||||
int numPoints = points_.size();
|
||||
|
||||
// For a better curve, don't pass through all of points:
|
||||
// skip about 70% of them
|
||||
int toSkip = 0.70 * numPoints;
|
||||
int step = numPoints / ( numPoints - toSkip );
|
||||
step = ( step < 1 ) ? 1 : step; // Sanity check
|
||||
|
||||
for( int i = 0; i < numPoints; i += step )
|
||||
{
|
||||
bezierKnots_.append( points_.at(i).position );
|
||||
}
|
||||
|
||||
// always pass through the last point.
|
||||
bezierKnots_.append( points_.last().position );
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if( bezierKnots_.size() == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int n = bezierKnots_.size() - 1;
|
||||
if( n < 1 )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG
|
||||
qWarning() << "Require at least two knot points to generate Bezier control points; ignoring.";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// special case: Bezier curve should be a straight line.
|
||||
if( n == 1 )
|
||||
{
|
||||
QPointF cp1, cp2;
|
||||
QPointF k1 = bezierKnots_.at(0); // knot point 1
|
||||
QPointF k2 = bezierKnots_.at(1); // knot point 2
|
||||
|
||||
cp1.setX( ( 2 * k1.x() + k2.x() ) / 3.0 );
|
||||
cp1.setY( ( 2 * k1.y() + k2.y() ) / 3.0 );
|
||||
|
||||
cp2.setX( ( 2 * cp1.x() - k1.x() ) );
|
||||
cp2.setY( ( 2 * cp1.y() - k1.y() ) );
|
||||
|
||||
bezierControlPoints1_.append( cp1 );
|
||||
bezierControlPoints2_.append( cp2 );
|
||||
|
||||
return; // done!
|
||||
}
|
||||
|
||||
// right hand side vector.
|
||||
QVector<double> rhs( n );
|
||||
|
||||
// set RHS x values
|
||||
for( int i = 1; i < n-1; i++ )
|
||||
{
|
||||
rhs[i] = 4 * bezierKnots_[i].x() + 2 * bezierKnots_[i+1].x();
|
||||
}
|
||||
|
||||
rhs[0] = bezierKnots_[0].x() + 2 * bezierKnots_[1].x();
|
||||
rhs[n-1] = ( 8 * bezierKnots_[n-1].x() + bezierKnots_[n].x() ) / 2.0;
|
||||
|
||||
// get the first ctl points x values.
|
||||
QVector<double> x( n );
|
||||
bezierGetFirstControlPoints( rhs.data(), x.data(), n );
|
||||
|
||||
|
||||
// now set RHS y-values.
|
||||
for(int i = 1; i < n-1; i++ )
|
||||
{
|
||||
rhs[i] = 4 * bezierKnots_[i].y() + 2 * bezierKnots_[i+1].y();
|
||||
}
|
||||
|
||||
rhs[0] = bezierKnots_[0].y() + 2 * bezierKnots_[1].y();
|
||||
rhs[n-1] = ( 8 * bezierKnots_[n-1].y() + bezierKnots_[n].y() ) / 2.0;
|
||||
|
||||
QVector<double> y( n );
|
||||
bezierGetFirstControlPoints( rhs.data(), y.data(), n );
|
||||
|
||||
// now fill the output QList.
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
QPointF cp1( x[i], y[i] );
|
||||
QPointF cp2;
|
||||
|
||||
// second ctl point
|
||||
if ( i < n-1 )
|
||||
{
|
||||
cp2 = QPointF( 2 * bezierKnots_[i+1].x() - x[i+1],
|
||||
2 * bezierKnots_[i+1].y() - y[i+1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
cp2 = QPointF( ( bezierKnots_[n].x() + x[n-1] ) / 2,
|
||||
( bezierKnots_[n].y() + y[n-1] ) / 2 );
|
||||
}
|
||||
|
||||
bezierControlPoints1_.append( cp1 );
|
||||
bezierControlPoints2_.append( cp2 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Solves the system for the first control points.
|
||||
void Stroke::bezierGetFirstControlPoints( double rhs[], double* xOut, int n )
|
||||
{
|
||||
double* x = xOut; // solution vector.
|
||||
QVector<double> tmp( n ); // temp workspace.
|
||||
|
||||
double b = 2.0;
|
||||
x[0] = rhs[0] / b;
|
||||
for( int i = 1; i < n; i++ ) // decomposition and forward substitution
|
||||
{
|
||||
tmp[i] = 1 / b;
|
||||
b = ( i < n - 1 ? 4.0 : 3.5 ) - tmp[i];
|
||||
x[i] = (rhs[i] - x[i-1]) / b;
|
||||
}
|
||||
|
||||
for( int i = 1; i < n; i++ )
|
||||
{
|
||||
x[n-i-1] -= tmp[n-i] * x[n-i]; // back substitution.
|
||||
}
|
||||
|
||||
// results are in xOut.
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the rectangle which contains this stroke.
|
||||
*
|
||||
* @return Rectangle
|
||||
*/
|
||||
QRect Stroke::boundingRect() const
|
||||
{
|
||||
return boundingRect_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QColor Stroke::color() const
|
||||
{
|
||||
return color_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Apply the changes to the stroke.
|
||||
*
|
||||
* Calculating the stroke bounds is complex, so a lazy approach was
|
||||
* chosen instead of recalculating them for every added point.
|
||||
*/
|
||||
void Stroke::finalize()
|
||||
{
|
||||
if( finalized_ )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// A polygon is used to determine the stroke's bounding rect
|
||||
quint64 numPoints = points_.count();
|
||||
QPolygon polygon( numPoints );
|
||||
for( quint64 index = 0; index < numPoints; ++index )
|
||||
{
|
||||
polygon.setPoint( index, points_.at( index ).position );
|
||||
}
|
||||
|
||||
// Transform the bounding rect with the current transformation
|
||||
if( transform_ )
|
||||
{
|
||||
polygon = transform_->map( polygon );
|
||||
}
|
||||
|
||||
// Set the bounding rectangle, expanded it to also accommodate pen size
|
||||
float halfPenSize = penSize_.width() / 2;
|
||||
boundingRect_ = polygon.boundingRect().adjusted( -( halfPenSize ), -( halfPenSize ),
|
||||
halfPenSize, halfPenSize );
|
||||
|
||||
// Finally, pre-calculate the stroke paths
|
||||
painterPath();
|
||||
|
||||
finalized_ = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the stroke drawing flags.
|
||||
*
|
||||
* @return Stroke flags
|
||||
*/
|
||||
StrokeFlags Stroke::flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get whether the stroke contains pressure information.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
bool Stroke::hasPressureData() const
|
||||
{
|
||||
return hasPressureData_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the stroke metrics.
|
||||
*
|
||||
* @return Metrics*
|
||||
*/
|
||||
Metrics* Stroke::metrics()
|
||||
{
|
||||
return metrics_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get a painter path to draw this stroke.
|
||||
*
|
||||
* The path is adjusted according to the drawing flags.
|
||||
* Given a list of knot points, generates a QPainterPath that describes the stroke.
|
||||
*
|
||||
* If the FitToCurve flag is present, the stroke path is generated using bezier curves
|
||||
* to approximate the stroke, giving a much smoother appearance.
|
||||
*
|
||||
* @see calculateControlPoints()
|
||||
* @return QPainterPath
|
||||
*/
|
||||
QPainterPath Stroke::painterPath()
|
||||
{
|
||||
int numPoints = points_.size();
|
||||
|
||||
if( numPoints == 0 )
|
||||
{
|
||||
return QPainterPath();
|
||||
}
|
||||
|
||||
QPointF startPos( points_.first().position );
|
||||
|
||||
QPainterPath path( startPos );
|
||||
|
||||
if( ( flags_ & FitToCurve ) == false )
|
||||
{
|
||||
foreach( Point point, points_ )
|
||||
{
|
||||
path.lineTo( point.position );
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// don't calculate control points if they've
|
||||
// already been calculated.
|
||||
if( bezierKnots_.isEmpty() )
|
||||
{
|
||||
bezierCalculateControlPoints();
|
||||
}
|
||||
|
||||
for( int i = 0; i < bezierControlPoints1_.size(); i++ )
|
||||
{
|
||||
// draw the bezier curve!
|
||||
path.cubicTo( bezierControlPoints1_[ i ], bezierControlPoints2_[ i ], bezierKnots_[ i + 1 ] );
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current pen size
|
||||
*
|
||||
* @return QSizeF of the stroke pen
|
||||
*/
|
||||
QSizeF Stroke::penSize() const
|
||||
{
|
||||
return penSize_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of points
|
||||
*
|
||||
* @return List of stroke points
|
||||
*/
|
||||
PointList& Stroke::points()
|
||||
{
|
||||
return points_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change pen color
|
||||
*
|
||||
* @param newColor new pen color
|
||||
*/
|
||||
void Stroke::setColor( QColor newColor )
|
||||
{
|
||||
color_ = newColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change a flag
|
||||
*
|
||||
* @param flag The flag to alter
|
||||
* @param set If true, the flag will be set. If false, it will be unset.
|
||||
*/
|
||||
void Stroke::setFlag( StrokeFlag flag, bool set )
|
||||
{
|
||||
if( set )
|
||||
{
|
||||
flags_ |= flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
flags_ ^= flag;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Change stroke flags all at once.
|
||||
*
|
||||
* @param newFlags Flags bitfield
|
||||
*/
|
||||
void Stroke::setFlags( StrokeFlags newFlags )
|
||||
{
|
||||
flags_ = newFlags;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current pen size
|
||||
*
|
||||
* @param newMetrics The new metrics object
|
||||
*/
|
||||
void Stroke::setMetrics( Metrics* newMetrics )
|
||||
{
|
||||
metrics_ = newMetrics;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the current pen size
|
||||
*
|
||||
* @return QSizeF of the stroke pen
|
||||
*/
|
||||
void Stroke::setPenSize( QSizeF newSize )
|
||||
{
|
||||
penSize_ = newSize;
|
||||
|
||||
// The bounding box changes with pen size
|
||||
finalized_ = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Apply a transformation to the stroke.
|
||||
*
|
||||
* The stroke doesn't take ownership of the transformation.
|
||||
*
|
||||
* @param newTransform Transformation to apply
|
||||
*/
|
||||
void Stroke::setTransform( QMatrix* newTransform )
|
||||
{
|
||||
transform_ = newTransform;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the stroke transformation, if any
|
||||
*
|
||||
* @return QMatrix, possibly null
|
||||
*/
|
||||
QMatrix* Stroke::transform()
|
||||
{
|
||||
return transform_;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGSPARSER_H
|
||||
#define TAGSPARSER_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QMatrix>
|
||||
#include <QString>
|
||||
|
||||
#include <IsfQt>
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
|
||||
|
||||
// Forward declarations
|
||||
class StreamData;
|
||||
class Drawing;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The methods in this class can parse the ISF tags data.
|
||||
*
|
||||
* @author Valerio Pilo (valerio@kmess.org)
|
||||
*/
|
||||
class TagsParser
|
||||
{
|
||||
|
||||
public: // Static public methods
|
||||
static IsfError nextTag( StreamData* streamData, Drawing* drawing );
|
||||
|
||||
static IsfError parseCustomTag( StreamData* streamData, Drawing* drawing, quint64 tagIndex );
|
||||
static IsfError parseGuidTable( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseHiMetricSize( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseInkSpaceRectangle( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseAttributeBlock( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseAttributeTable( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parsePersistentFormat( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseMetricBlock( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseMetricTable( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseTransformation( StreamData* streamData, Drawing* drawing, quint64 transformType );
|
||||
static IsfError parseTransformationTable( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseStroke( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseStrokeDescBlock( StreamData* streamData, Drawing* drawing );
|
||||
static IsfError parseStrokeDescTable( StreamData* streamData, Drawing* drawing );
|
||||
|
||||
public: // Static public debugging methods
|
||||
static IsfError parseUnsupported( StreamData* streamData, const QString& tagName );
|
||||
|
||||
private: // Static private debugging methods
|
||||
static QByteArray analyzePayload( StreamData* streamData, const QString& tagName );
|
||||
static QByteArray analyzePayload( StreamData* streamData, const quint64 payloadSize, const QString& message = QString() );
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,680 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tagswriter.h"
|
||||
|
||||
#include "isfqt-internal.h"
|
||||
|
||||
#include "data/compression.h"
|
||||
#include "data/datasource.h"
|
||||
#include "data/multibytecoding.h"
|
||||
|
||||
#include <IsfQtDrawing>
|
||||
|
||||
|
||||
using namespace Isf;
|
||||
using namespace Isf::Compress;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write the persistent format tag.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::addPersistentFormat( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
Q_UNUSED( drawing );
|
||||
|
||||
QByteArray tagContents;
|
||||
|
||||
tagContents.append( encodeUInt( ISF_PERSISTENT_FORMAT_VERSION ) );
|
||||
|
||||
tagContents.prepend( encodeUInt( tagContents.size() ) );
|
||||
tagContents.prepend( encodeUInt( TAG_PERSISTENT_FORMAT ) );
|
||||
|
||||
streamData->dataSource->append( tagContents );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Persistent Format version";
|
||||
#endif
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write the drawing dimensions.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::addHiMetricSize( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
QByteArray tagContents;
|
||||
|
||||
QPoint size( drawing->boundingRect().bottomRight() );
|
||||
tagContents.append( encodeInt( size.x() ) );
|
||||
tagContents.append( encodeInt( size.y() ) );
|
||||
|
||||
encodeUInt( streamData->dataSource, TAG_HIMETRIC_SIZE );
|
||||
encodeUInt( streamData->dataSource, tagContents.size() );
|
||||
streamData->dataSource->append( tagContents );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Added drawing dimensions:" << size;
|
||||
#endif
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write a table (or a block only) of points attributes.
|
||||
*
|
||||
* If there is only one block, no table is outputted into the stream, just
|
||||
* that block.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::addAttributeTable( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
Q_UNUSED( drawing );
|
||||
|
||||
QByteArray blockData;
|
||||
QByteArray tagContents;
|
||||
AttributeSet defaultAttributeSet;
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
quint8 counter = 0;
|
||||
#endif
|
||||
|
||||
// Add default attributes if the drawing didn't contain any
|
||||
if( streamData->attributeSets.isEmpty() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Adding a default attribute set...";
|
||||
#endif
|
||||
AttributeSet set;
|
||||
streamData->attributeSets.append( set );
|
||||
}
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
else
|
||||
{
|
||||
qDebug() << "- Adding" << streamData->attributeSets.count() << "attributes...";
|
||||
}
|
||||
#endif
|
||||
|
||||
foreach( const AttributeSet set, streamData->attributeSets )
|
||||
{
|
||||
// Add the color to the attribute block
|
||||
if( set.color != defaultAttributeSet.color )
|
||||
{
|
||||
blockData.append( encodeUInt( GUID_COLORREF ) );
|
||||
|
||||
// Prepare the color value, it needs to be stored in BGR format,
|
||||
// in the 24 least significant bits
|
||||
quint64 value = ( set.color.blue () << 16 )
|
||||
| ( set.color.green() << 8 )
|
||||
| ( set.color.red () << 0 );
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Color:" << set.color.name();
|
||||
#endif
|
||||
blockData.append( encodeUInt( value ) );
|
||||
|
||||
// Add the transparency if needed
|
||||
if( set.color.alpha() < 255 )
|
||||
{
|
||||
blockData.append( encodeUInt( GUID_TRANSPARENCY ) );
|
||||
blockData.append( encodeUInt( set.color.alpha() ) );
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Alpha:" << set.color.alpha();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Add the pen size
|
||||
if( set.penSize != defaultAttributeSet.penSize )
|
||||
{
|
||||
blockData.append( encodeUInt( GUID_PEN_WIDTH ) );
|
||||
blockData.append( encodeUInt( set.penSize.width() * HiMetricToPixel ) );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Pen width:" << ( set.penSize.width() * HiMetricToPixel );
|
||||
#endif
|
||||
if( set.penSize.width() != set.penSize.height() )
|
||||
{
|
||||
blockData.append( encodeUInt( GUID_PEN_HEIGHT ) );
|
||||
blockData.append( encodeUInt( set.penSize.height() * HiMetricToPixel ) );
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Pen height:" << ( set.penSize.height() * HiMetricToPixel );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Add the other drawing flags
|
||||
if( set.flags != defaultAttributeSet.flags )
|
||||
{
|
||||
StrokeFlags flags = set.flags;
|
||||
if( flags & IsRectangle )
|
||||
{
|
||||
blockData.append( encodeUInt( GUID_PEN_TIP ) );
|
||||
blockData.append( encodeUInt( 0 ) ); // Value unknown, is the tag enough?
|
||||
flags ^= IsRectangle;
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - isRectangle flag";
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
if( flags & IsHighlighter )
|
||||
{
|
||||
blockData.append( encodeUInt( GUID_ROP ) );
|
||||
blockData.append( QByteArray( 4, '\0' ) ); // Value unknown
|
||||
flags ^= IsHighlighter;
|
||||
}
|
||||
*/
|
||||
|
||||
// Copy the other flags as they are
|
||||
blockData.append( encodeUInt( GUID_DRAWING_FLAGS ) );
|
||||
blockData.append( encodeUInt( flags ) );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Flags bitfield:" << flags;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
blockData.prepend( encodeUInt( blockData.size() ) );
|
||||
tagContents.append( blockData );
|
||||
blockData.clear();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Added attribute block #" << ++counter;
|
||||
#endif
|
||||
}
|
||||
|
||||
if( streamData->attributeSets.count() > 1 )
|
||||
{
|
||||
tagContents.prepend( encodeUInt( tagContents.size() ) );
|
||||
tagContents.prepend( encodeUInt( TAG_DRAW_ATTRS_TABLE ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
tagContents.prepend( encodeUInt( TAG_DRAW_ATTRS_BLOCK ) );
|
||||
}
|
||||
|
||||
streamData->dataSource->append( tagContents );
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write a table (or a block only) of metrics.
|
||||
*
|
||||
* If there is only one block, no table is outputted into the stream, just
|
||||
* that block.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::addMetricsTable( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
Q_UNUSED( drawing );
|
||||
|
||||
QByteArray metricData;
|
||||
QByteArray metricBlockData;
|
||||
QByteArray tagData;
|
||||
Metrics defaultMetrics;
|
||||
Metric* defaultMetric;
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Adding" << streamData->metrics.count() << "metrics...";
|
||||
quint8 counter = 0;
|
||||
#endif
|
||||
|
||||
foreach( const Metrics* metrics, streamData->metrics )
|
||||
{
|
||||
QMapIterator<int,Metric> it( metrics->items );
|
||||
while( it.hasNext() )
|
||||
{
|
||||
it.next();
|
||||
const quint64& property = it.key ();
|
||||
const Metric& metric = it.value();
|
||||
|
||||
// Skip metrics which are set to the default
|
||||
defaultMetric = &defaultMetrics.items[ property ];
|
||||
if( defaultMetric->min == metric.min
|
||||
&& defaultMetric->max == metric.max
|
||||
&& defaultMetric->units == metric.units
|
||||
&& defaultMetric->resolution == metric.resolution )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Write the metric data
|
||||
metricData.append( encodeInt( metric.min ) );
|
||||
metricData.append( encodeInt( metric.max ) );
|
||||
metricData.append( (uchar) metric.units );
|
||||
metricData.append( encodeFloat( metric.resolution ) );
|
||||
|
||||
// Write how much data is there
|
||||
metricData.prepend( encodeUInt( metricData.size() ) );
|
||||
|
||||
// Write the property ID
|
||||
metricData.prepend( encodeUInt( property ) );
|
||||
|
||||
// Flush the metric
|
||||
metricBlockData.append( metricData );
|
||||
metricData.clear();
|
||||
}
|
||||
|
||||
// Flush the metrics block
|
||||
tagData.append( encodeUInt( metricBlockData.size() ) );
|
||||
tagData.append( metricBlockData );
|
||||
metricBlockData.clear();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Added metrics block #" << ++counter;
|
||||
#endif
|
||||
}
|
||||
|
||||
if( streamData->metrics.count() > 1 )
|
||||
{
|
||||
tagData.prepend( encodeUInt( TAG_METRIC_TABLE ) );
|
||||
}
|
||||
else if ( streamData->metrics.count() == 1 )
|
||||
{
|
||||
tagData.prepend( encodeUInt( TAG_METRIC_BLOCK ) );
|
||||
}
|
||||
// else: don't do anything.
|
||||
|
||||
streamData->dataSource->append( tagData );
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write a table (or a block only) of transformation matrices.
|
||||
*
|
||||
* If there is only one block, no table is outputted into the stream, just
|
||||
* that block.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::addTransformationTable( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
Q_UNUSED( drawing );
|
||||
|
||||
QByteArray blockData;
|
||||
QByteArray tagContents;
|
||||
quint64 transformTag;
|
||||
|
||||
/*
|
||||
* QTransform is too complex for the 2D transformations we need to do;
|
||||
* but QMatrix is too simple: it doesn't 'remember' rotations. So we
|
||||
* save rotations as generic transformations.
|
||||
* Possible solutions:
|
||||
* 1) Create a struct containing the transform matrix data and use
|
||||
* that instead of QMatrices;
|
||||
* 2) Switch (again!) to QTransform and fix the inconsistencies.
|
||||
*/
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Adding" << streamData->transforms.count() << "transformations...";
|
||||
quint8 counter = 0;
|
||||
#endif
|
||||
|
||||
foreach( const QMatrix* trans, streamData->transforms )
|
||||
{
|
||||
/*
|
||||
* All transforms are written backwards because they're stored my most
|
||||
* significant value first.
|
||||
*/
|
||||
|
||||
// Detect translation
|
||||
if( trans->dx() || trans->dy() )
|
||||
{
|
||||
// Detect scaling
|
||||
if( trans->m11() || trans->m22() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Transform: TAG_TRANSFORM_SCALE_AND_TRANSLATE";
|
||||
qDebug() << " - Transformation details - "
|
||||
<< "Scale X:" << trans->m11()
|
||||
<< "Scale Y:" << trans->m22()
|
||||
<< "Translate X:" << trans->dx()
|
||||
<< "Translate Y:" << trans->dy();
|
||||
#endif
|
||||
transformTag = TAG_TRANSFORM_SCALE_AND_TRANSLATE;
|
||||
blockData.append( encodeFloat( trans->m11() * HiMetricToPixel ) );
|
||||
blockData.append( encodeFloat( trans->m22() * HiMetricToPixel ) );
|
||||
blockData.append( encodeFloat( trans->dx() / trans->m11() ) );
|
||||
blockData.append( encodeFloat( trans->dy() / trans->m22() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Transform: TAG_TRANSFORM_TRANSLATE";
|
||||
qDebug() << " - Transformation details - "
|
||||
<< "Translate X:" << trans->dx()
|
||||
<< "Translate Y:" << trans->dy();
|
||||
#endif
|
||||
transformTag = TAG_TRANSFORM_TRANSLATE;
|
||||
blockData.append( encodeFloat( trans->dx() ) );
|
||||
blockData.append( encodeFloat( trans->dy() ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
// Detect scaling
|
||||
if( trans->m11() || trans->m22() )
|
||||
{
|
||||
if( trans->m11() == trans->m22() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Transform: TAG_TRANSFORM_ISOTROPIC_SCALE";
|
||||
qDebug() << " - Transformation details - "
|
||||
<< "Scale:" << (trans->m11() * HiMetricToPixel);
|
||||
#endif
|
||||
transformTag = TAG_TRANSFORM_ISOTROPIC_SCALE;
|
||||
blockData.append( encodeFloat( trans->m11() * HiMetricToPixel ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Transform: TAG_TRANSFORM_ANISOTROPIC_SCALE";
|
||||
qDebug() << " - Transformation details - "
|
||||
<< "Scale X:" << trans->m11()
|
||||
<< "Scale Y:" << trans->m22();
|
||||
#endif
|
||||
transformTag = TAG_TRANSFORM_ANISOTROPIC_SCALE;
|
||||
blockData.append( encodeFloat( trans->m11() * HiMetricToPixel ) );
|
||||
blockData.append( encodeFloat( trans->m22() * HiMetricToPixel ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Transform: TAG_TRANSFORM";
|
||||
#endif
|
||||
transformTag = TAG_TRANSFORM;
|
||||
blockData.append( encodeFloat( trans->m11() * HiMetricToPixel ) );
|
||||
blockData.append( encodeFloat( .0f ) );
|
||||
blockData.append( encodeFloat( .0f ) );
|
||||
blockData.append( encodeFloat( trans->m22() * HiMetricToPixel ) );
|
||||
blockData.append( encodeFloat( trans->dx () / trans->m11() ) );
|
||||
blockData.append( encodeFloat( trans->dy () / trans->m22() ) );
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Transformation details - "
|
||||
<< "Scale X:" << trans->m11()
|
||||
<< "Shear X:" << trans->m21()
|
||||
<< "Scale Y:" << trans->m22()
|
||||
<< "Shear Y:" << trans->m12()
|
||||
<< "Translate X:" << trans->dx()
|
||||
<< "Translate Y:" << trans->dy();
|
||||
#endif
|
||||
}
|
||||
|
||||
blockData.prepend( encodeUInt( transformTag ) );
|
||||
|
||||
tagContents.append( blockData );
|
||||
blockData.clear();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Added transform #" << ++counter;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
if ( streamData->transforms.size() == 0 )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Added default transformation";
|
||||
#endif
|
||||
|
||||
// Write the default transform
|
||||
QMatrix defaultTransform;
|
||||
defaultTransform.scale( 1.f, 1.f );
|
||||
defaultTransform.translate( .0f, .0f );
|
||||
|
||||
tagContents.append( TAG_TRANSFORM_ISOTROPIC_SCALE );
|
||||
tagContents.append( encodeFloat( defaultTransform.m11() * HiMetricToPixel ) );
|
||||
}
|
||||
else if ( streamData->transforms.size() > 1 )
|
||||
{
|
||||
tagContents.prepend( encodeUInt( tagContents.size() ) );
|
||||
tagContents.prepend( encodeUInt( TAG_TRANSFORM_TABLE ) );
|
||||
}
|
||||
|
||||
streamData->dataSource->append( tagContents );
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write the strokes.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::addStrokes( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
QByteArray blockData;
|
||||
QByteArray tagContents;
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Adding" << drawing->strokes_.count() << "strokes...";
|
||||
quint8 counter = 0;
|
||||
#endif
|
||||
|
||||
// Last set of attibutes applied to a stroke
|
||||
AttributeSet currentAttributeSet;
|
||||
const Metrics* currentMetrics = 0;
|
||||
const QMatrix* currentTransform = 0;
|
||||
|
||||
foreach( Stroke* stroke, drawing->strokes_ )
|
||||
{
|
||||
// There is more than one set of metrics, assign each stroke to its own
|
||||
if( streamData->metrics.count() > 1 )
|
||||
{
|
||||
// Make sure that the first strokes use the first metrics list (write a MIDX only
|
||||
// when needed)
|
||||
if( currentMetrics == 0 )
|
||||
{
|
||||
currentMetrics = streamData->metrics.first();
|
||||
}
|
||||
|
||||
// Only write a MIDX if this stroke needs different metrics than the last stroke
|
||||
Metrics* metrics = stroke->metrics();
|
||||
if( metrics && currentMetrics != metrics )
|
||||
{
|
||||
currentMetrics = metrics;
|
||||
blockData.append( encodeUInt( TAG_MIDX ) );
|
||||
blockData.append( encodeUInt( streamData->metrics.indexOf( metrics ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// There is more than one set of attributes, assign each stroke to its own
|
||||
if( streamData->attributeSets.count() > 1 )
|
||||
{
|
||||
// Only write a DIDX if this stroke needs a different attribute set than the last stroke
|
||||
if( currentAttributeSet.color != stroke->color()
|
||||
|| currentAttributeSet.flags != stroke->flags()
|
||||
|| currentAttributeSet.penSize != stroke->penSize() )
|
||||
{
|
||||
currentAttributeSet.color = stroke->color();
|
||||
currentAttributeSet.flags = stroke->flags();
|
||||
currentAttributeSet.penSize = stroke->penSize();
|
||||
|
||||
blockData.append( encodeUInt( TAG_DIDX ) );
|
||||
blockData.append( encodeUInt( streamData->attributeSets.indexOf( currentAttributeSet ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that the first strokes use the first transform (write a TIDX only
|
||||
// when needed)
|
||||
if ( streamData->transforms.count() > 0 )
|
||||
{
|
||||
if( currentTransform == 0 )
|
||||
{
|
||||
currentTransform = streamData->transforms.first();
|
||||
}
|
||||
|
||||
// Only write a TIDX if this stroke needs a different transform than the last stroke
|
||||
if( currentTransform != stroke->transform() && stroke->transform() != 0 )
|
||||
{
|
||||
currentTransform = stroke->transform();
|
||||
blockData.append( encodeUInt( TAG_TIDX ) );
|
||||
blockData.append( encodeUInt( streamData->transforms.indexOf( stroke->transform() ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Flush the index tags
|
||||
if( ! blockData.isEmpty() )
|
||||
{
|
||||
tagContents.append( blockData );
|
||||
blockData.clear();
|
||||
}
|
||||
|
||||
// Write this stroke in the stream
|
||||
|
||||
QList<qint64> xPoints, yPoints;
|
||||
foreach( const Point& point, stroke->points() )
|
||||
{
|
||||
xPoints.append( point.position.x() );
|
||||
yPoints.append( point.position.y() );
|
||||
}
|
||||
|
||||
deflatePacketData( blockData, xPoints );
|
||||
deflatePacketData( blockData, yPoints );
|
||||
|
||||
// The stroke is made by tag, then payload size, then number of points, then
|
||||
// the compressed points data
|
||||
blockData.prepend( encodeUInt( stroke->points().count() ) );
|
||||
blockData.prepend( encodeUInt( blockData.size() ) );
|
||||
blockData.prepend( encodeUInt( TAG_STROKE ) );
|
||||
|
||||
tagContents.append( blockData );
|
||||
blockData.clear();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Added stroke #" << ++counter;
|
||||
#endif
|
||||
}
|
||||
|
||||
streamData->dataSource->append( tagContents );
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the stream data for writing.
|
||||
*
|
||||
* @param source Data Source where to write bytes to
|
||||
* @param drawing Drawing from which to obtain the data to write
|
||||
* @return IsfError
|
||||
*/
|
||||
IsfError TagsWriter::prepare( StreamData* streamData, const Drawing* drawing )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Optimizing data...";
|
||||
#endif
|
||||
|
||||
streamData->attributeSets.clear();
|
||||
streamData->metrics.clear();
|
||||
streamData->transforms.clear();
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - " << drawing->strokes_.count() << "strokes to optimize";
|
||||
#endif
|
||||
|
||||
// Prepare the list of attributes
|
||||
foreach( Stroke* stroke, drawing->strokes_ )
|
||||
{
|
||||
AttributeSet set;
|
||||
set.color = stroke->color();
|
||||
set.flags = stroke->flags();
|
||||
set.penSize = stroke->penSize();
|
||||
|
||||
Metrics* metrics = stroke->metrics();
|
||||
QMatrix* transform = stroke->transform();
|
||||
|
||||
if( ! streamData->attributeSets.contains( set ) )
|
||||
{
|
||||
streamData->attributeSets.append( set );
|
||||
}
|
||||
if( metrics && ! streamData->metrics.contains( metrics ) )
|
||||
{
|
||||
streamData->metrics.append( metrics );
|
||||
}
|
||||
if( transform && ! streamData->transforms.contains( transform ) )
|
||||
{
|
||||
streamData->transforms.append( transform );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
int count = 0;
|
||||
#endif
|
||||
|
||||
// Add default elements if the drawing didn't contain any
|
||||
if( streamData->attributeSets.isEmpty() )
|
||||
{
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Added " << count << "default elements";
|
||||
#endif
|
||||
AttributeSet set;
|
||||
streamData->attributeSets.append( set );
|
||||
}
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << " - Added " << count << "default elements";
|
||||
#endif
|
||||
|
||||
#ifdef ISFQT_DEBUG_VERBOSE
|
||||
qDebug() << "- Optimization phase complete";
|
||||
#endif
|
||||
|
||||
return ISF_ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGSWRITER_H
|
||||
#define TAGSWRITER_H
|
||||
|
||||
#include <IsfQt>
|
||||
|
||||
|
||||
|
||||
namespace Isf
|
||||
{
|
||||
|
||||
|
||||
// Forward declarations
|
||||
namespace Compress
|
||||
{
|
||||
class DataSource;
|
||||
}
|
||||
using Compress::DataSource;
|
||||
class Drawing;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The methods in this class can convert data to ISF tags.
|
||||
*
|
||||
* @author Valerio Pilo (valerio@kmess.org)
|
||||
*/
|
||||
class TagsWriter
|
||||
{
|
||||
public: // Static public methods
|
||||
static IsfError addPersistentFormat( StreamData* streamData, const Drawing* drawing );
|
||||
static IsfError addHiMetricSize( StreamData* streamData, const Drawing* drawing );
|
||||
static IsfError addAttributeTable( StreamData* streamData, const Drawing* drawing );
|
||||
static IsfError addMetricsTable( StreamData* streamData, const Drawing* drawing );
|
||||
static IsfError addTransformationTable( StreamData* streamData, const Drawing* drawing );
|
||||
static IsfError addStrokes( StreamData* streamData, const Drawing* drawing );
|
||||
static IsfError prepare( StreamData* streamData, const Drawing* drawing );
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
INCLUDE_DIRECTORIES( ${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
SET( CMAKE_EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
|
||||
MACRO( add_multiple_tests )
|
||||
FOREACH( test ${ARGN} )
|
||||
QT4_AUTOMOC( test_${test}.cpp )
|
||||
ADD_EXECUTABLE( test_${test} test_${test}.cpp )
|
||||
ADD_TEST( libisf-${test} test_${test} )
|
||||
TARGET_LINK_LIBRARIES( test_${test} ${QT_QTTEST_LIBRARY} ${QT_QTGUI_LIBRARY} isf-qt )
|
||||
ENDFOREACH( test )
|
||||
ENDMACRO( add_multiple_tests )
|
||||
|
||||
ADD_MULTIPLE_TESTS(
|
||||
isfdrawing
|
||||
multibyte_coding
|
||||
algorithms
|
||||
png_fortification
|
||||
)
|
||||
|
||||
|
||||
# Compile the other testing applications
|
||||
ADD_SUBDIRECTORY( decode )
|
||||
ADD_SUBDIRECTORY( inkedit )
|
||||
@@ -0,0 +1,33 @@
|
||||
set( CMAKE_BUILD_TYPE debugfull )
|
||||
|
||||
if( CMAKE_COMPILER_IS_GNUCXX )
|
||||
set( CMAKE_CXX_FLAGS_DEBUGFULL "-O0 -g3 -fno-inline -Wall -Woverloaded-virtual -Wsign-compare -Wundef -fvisibility=default" )
|
||||
endif( CMAKE_COMPILER_IS_GNUCXX )
|
||||
|
||||
|
||||
set( TEST_DECODE_SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set( TEST_DECODE_MOC_HEADERS
|
||||
# testdecode.h
|
||||
)
|
||||
|
||||
set( TEST_DECODE_UI
|
||||
testdecode.ui
|
||||
)
|
||||
|
||||
find_package( Qt4 REQUIRED )
|
||||
include( ${QT_USE_FILE} )
|
||||
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
|
||||
qt4_wrap_ui( TEST_DECODE_UI_HEADERS ${TEST_DECODE_UI} )
|
||||
qt4_wrap_cpp( TEST_DECODE_MOC_SOURCES ${TEST_DECODE_MOC_HEADERS} )
|
||||
|
||||
add_executable( test_decode
|
||||
${TEST_DECODE_MOC_SOURCES}
|
||||
${TEST_DECODE_SOURCES}
|
||||
${TEST_DECODE_UI_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries( test_decode ${QT_QTGUI_LIBRARY} isf-qt )
|
||||
@@ -0,0 +1,192 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Valerio Pilo *
|
||||
* valerio@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include <IsfQtDrawing>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "ui_testdecode.h"
|
||||
|
||||
|
||||
|
||||
class TestDecode : public QMainWindow, private Ui::TestDecode
|
||||
{
|
||||
public:
|
||||
|
||||
void test()
|
||||
{
|
||||
bool rewrite = false;
|
||||
QStringList arguments( qApp->arguments() );
|
||||
arguments.removeAt( 0 );
|
||||
|
||||
if( arguments.count() < 1 )
|
||||
{
|
||||
label_->setText( "You must specify the name of an ISF file to test!" );
|
||||
return;
|
||||
}
|
||||
|
||||
int rewriteIndex = arguments.indexOf( "--rewrite" );
|
||||
if( rewriteIndex >= 0 )
|
||||
{
|
||||
rewrite = true;
|
||||
arguments.removeAt( rewriteIndex );
|
||||
}
|
||||
|
||||
const QString fileName( arguments.at( 0 ) );
|
||||
const QByteArray rawData( readTestIsfData( fileName ) );
|
||||
Isf::Drawing drawing;
|
||||
|
||||
// Extract the ISF data from the GIF image
|
||||
if( fileName.contains( ".gif" ) )
|
||||
{
|
||||
qDebug() << "------------------------- Creating drawing from GIF -------------------------";
|
||||
drawing = Isf::Stream::readerGif( rawData );
|
||||
}
|
||||
// Extract the ISF data from the ISF file
|
||||
else
|
||||
{
|
||||
qDebug() << "------------------------- Creating drawing from ISF -------------------------";
|
||||
drawing = Isf::Stream::reader( rawData );
|
||||
}
|
||||
|
||||
if( rewrite )
|
||||
{
|
||||
// change to test GIF or regular streams
|
||||
bool testGif = false;
|
||||
|
||||
if( testGif )
|
||||
{
|
||||
// Test Fortified-GIF r/w
|
||||
qDebug() << "------------------------- Writing drawing to GIF file -------------------------";
|
||||
QByteArray data( Isf::Stream::writerGif( drawing ) );
|
||||
|
||||
qDebug() << "------------------------- Reading GIF back -------------------------";
|
||||
drawing = Isf::Stream::readerGif( data );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Test regular streams r/w
|
||||
qDebug() << "------------------------- Writing drawing to ISF file -------------------------";
|
||||
QByteArray data2( Isf::Stream::writer( drawing ) );
|
||||
|
||||
qDebug() << "------------------------- Reading ISF back -------------------------";
|
||||
drawing = Isf::Stream::reader( data2 );
|
||||
}
|
||||
}
|
||||
|
||||
if( drawing.isNull() )
|
||||
{
|
||||
label_->setText( "Invalid file contents!" );
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "------------------------- Showing it -------------------------";
|
||||
label_->setPixmap( drawing.pixmap( Qt::transparent ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TestDecode()
|
||||
{
|
||||
QWidget *mainWidget = new QWidget( this );
|
||||
setupUi( mainWidget );
|
||||
setCentralWidget( mainWidget );
|
||||
setWindowTitle( "ISF decoding test" );
|
||||
|
||||
connect( closeButton_, SIGNAL( clicked(QAbstractButton*) ),
|
||||
this, SLOT ( close() ) );
|
||||
|
||||
show();
|
||||
|
||||
test();
|
||||
}
|
||||
|
||||
~TestDecode()
|
||||
{}
|
||||
|
||||
|
||||
|
||||
// read some test raw ISF data from a file on the filesystem and
|
||||
// return it as a QByteArray.
|
||||
QByteArray readTestIsfData( const QString& filename )
|
||||
{
|
||||
QFile file( filename );
|
||||
if ( !file.exists() )
|
||||
{
|
||||
qWarning() << "Test ISF file" << filename << "does not exist.";
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
// read file and convert to qbytearray.
|
||||
if ( !file.open( QIODevice::ReadOnly ) )
|
||||
{
|
||||
qWarning() << "Failed to open ISF data file" << filename;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// read some test raw ISF data from a file on the filesystem and
|
||||
// return it as a QByteArray.
|
||||
void saveTestIsfData( const QByteArray& data )
|
||||
{
|
||||
static int number = 1;
|
||||
QString fileName( "test" + QString::number( number ) + "." );
|
||||
|
||||
// autodetect if it's a gif or an isf
|
||||
if( data[0] == '\0' )
|
||||
{
|
||||
fileName.append( "isf" );
|
||||
}
|
||||
else
|
||||
{
|
||||
fileName.append( "gif" );
|
||||
}
|
||||
|
||||
QFile file( fileName );
|
||||
file.open( QIODevice::WriteOnly );
|
||||
file.write( data );
|
||||
file.close();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
QApplication app( argc, argv );
|
||||
|
||||
TestDecode test;
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TestDecode</class>
|
||||
<widget class="QWidget" name="TestDecode">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>314</width>
|
||||
<height>123</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="closeButton_">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,33 @@
|
||||
set( CMAKE_BUILD_TYPE debugfull )
|
||||
|
||||
if( CMAKE_COMPILER_IS_GNUCXX )
|
||||
set( CMAKE_CXX_FLAGS_DEBUGFULL "-O0 -g3 -fno-inline -Wall -Woverloaded-virtual -Wsign-compare -Wundef -fvisibility=default" )
|
||||
endif( CMAKE_COMPILER_IS_GNUCXX )
|
||||
|
||||
|
||||
set( TEST_INKEDIT_SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set( TEST_INKEDIT_MOC_HEADERS
|
||||
main.h
|
||||
)
|
||||
|
||||
set( TEST_INKEDIT_UI
|
||||
testinkedit.ui
|
||||
)
|
||||
|
||||
find_package( Qt4 REQUIRED )
|
||||
include( ${QT_USE_FILE} )
|
||||
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
|
||||
|
||||
qt4_wrap_cpp( TEST_INKEDIT_MOC_SOURCES ${TEST_INKEDIT_MOC_HEADERS} )
|
||||
qt4_wrap_ui( TEST_INKEDIT_UI_HEADERS ${TEST_INKEDIT_UI} )
|
||||
|
||||
add_executable( test_inkedit
|
||||
${TEST_INKEDIT_MOC_SOURCES}
|
||||
${TEST_INKEDIT_SOURCES}
|
||||
${TEST_INKEDIT_UI_HEADERS}
|
||||
)
|
||||
|
||||
target_link_libraries( test_inkedit ${QT_QTGUI_LIBRARY} isf-qt )
|
||||
@@ -0,0 +1,204 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include <IsfQtDrawing>
|
||||
|
||||
#include <QColorDialog>
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QDataStream>
|
||||
#include <QTextStream>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
|
||||
#include <IsfInkCanvas>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
using namespace Isf;
|
||||
|
||||
TestInkEdit::TestInkEdit()
|
||||
{
|
||||
setupUi( this );
|
||||
connect( cmdSave_, SIGNAL(clicked()), this, SLOT( saveInk() ) );
|
||||
connect( cmdLoad_, SIGNAL(clicked()), this, SLOT( loadInk() ) );
|
||||
connect( cmdClear_, SIGNAL(clicked()), this, SLOT( clearInk() ) );
|
||||
|
||||
connect( cmdStrokeColor_, SIGNAL(clicked()), this, SLOT( chooseColor() ) );
|
||||
connect( cmdCanvasColor_, SIGNAL(clicked()), this, SLOT( chooseColor() ) );
|
||||
|
||||
connect( editor_, SIGNAL(inkChanged()), this, SLOT( inkChanged() ) );
|
||||
|
||||
QButtonGroup *grp = new QButtonGroup( this );
|
||||
grp->addButton( rbDrawing_ );
|
||||
grp->addButton( rbEraser_ );
|
||||
|
||||
connect( grp, SIGNAL( buttonClicked( QAbstractButton * ) ), this, SLOT( penTypeChanged( QAbstractButton * ) ) );
|
||||
|
||||
connect( spinWidth_, SIGNAL( valueChanged( int ) ), editor_, SLOT( setPenSize( int ) ) );
|
||||
|
||||
editor_->setPenSize( spinWidth_->value() );
|
||||
/*
|
||||
setWindowTitle("Ink Edit Test");
|
||||
|
||||
editor_ = new Isf::InkEdit();
|
||||
editor_->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) );
|
||||
|
||||
sayLabel_ = new QLabel();
|
||||
sayLabel_->setText( "Draw something!" );
|
||||
sayLabel_->setSizePolicy(QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
|
||||
|
||||
cmdSave_ = new QPushButton();
|
||||
cmdSave_->setText( "Save Ink" );
|
||||
connect( cmdSave_, SIGNAL(clicked()), this, SLOT( saveInk() ) );
|
||||
|
||||
cmdLoad_ = new QPushButton();
|
||||
cmdLoad_->setText( "Load Ink" );
|
||||
connect( cmdLoad_, SIGNAL(clicked()), this, SLOT( loadInk() ) );
|
||||
|
||||
QPushButton *cmdClear_ = new QPushButton();
|
||||
cmdClear_->setText( "Clear Ink" );
|
||||
connect( cmdClear_, SIGNAL(clicked()), this, SLOT( clearInk() ) );
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
QHBoxLayout *strokeBtnLayout = new QHBoxLayout();
|
||||
|
||||
layout->addWidget( sayLabel_ );
|
||||
|
||||
layout->addWidget( editor_ );
|
||||
|
||||
QHBoxLayout *ctlBtnLayout = new QHBoxLayout();
|
||||
ctlBtnLayout->addWidget(cmdSave_);
|
||||
ctlBtnLayout->addWidget(cmdLoad_);
|
||||
ctlBtnLayout->addWidget(cmdClear_);
|
||||
|
||||
layout->addLayout( ctlBtnLayout );
|
||||
|
||||
setLayout( layout );
|
||||
*/
|
||||
}
|
||||
|
||||
TestInkEdit::~TestInkEdit()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void TestInkEdit::chooseColor()
|
||||
{
|
||||
QColor color = QColorDialog::getColor();
|
||||
if ( sender() == cmdStrokeColor_ )
|
||||
{
|
||||
editor_->setPenColor( color );
|
||||
}
|
||||
else
|
||||
{
|
||||
editor_->setCanvasColor( color );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestInkEdit::penTypeChanged( QAbstractButton *button )
|
||||
{
|
||||
if( button == rbDrawing_ )
|
||||
{
|
||||
editor_->setPenType( InkCanvas::DrawingPen );
|
||||
}
|
||||
else
|
||||
{
|
||||
editor_->setPenType( InkCanvas::EraserPen );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestInkEdit::saveInk()
|
||||
{
|
||||
QString filter;
|
||||
QString saveFile = QFileDialog::getSaveFileName( this, "Save Ink", QString(), "Raw ISF (*.isf);;base64-encoded ISF (*.isf64)", &filter );
|
||||
if ( saveFile != QString() )
|
||||
{
|
||||
QFile file(saveFile);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
|
||||
if ( filter.contains("isf64") )
|
||||
{
|
||||
editor_->save( file, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
editor_->save( file );
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
QMessageBox::information(0, "Save complete", "Saved to " + saveFile );
|
||||
}
|
||||
}
|
||||
|
||||
void TestInkEdit::loadInk()
|
||||
{
|
||||
QString filter;
|
||||
QString filename = QFileDialog::getOpenFileName( this, "Open Ink", QString(), "Raw ISF (*.isf);;base64-encoded ISF (*.isf64)", &filter );
|
||||
if ( ! filename.isEmpty() )
|
||||
{
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
|
||||
QByteArray data = filter.contains("*.isf64") ? QByteArray::fromBase64( file.readAll() ) : file.readAll();
|
||||
|
||||
file.close();
|
||||
|
||||
// got some ink.
|
||||
Isf::Drawing *drawing = &Isf::Stream::reader(data);
|
||||
|
||||
editor_->setDrawing( drawing );
|
||||
|
||||
editor_->updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void TestInkEdit::clearInk()
|
||||
{
|
||||
editor_->clear();
|
||||
}
|
||||
|
||||
void TestInkEdit::inkChanged()
|
||||
{
|
||||
emptyDrawingCheckbox_->setChecked( editor_->isEmpty() );
|
||||
|
||||
statusLabel_->setText( "Drawing changed!" );
|
||||
QTimer::singleShot( 3000, statusLabel_, SLOT(clear()) );
|
||||
}
|
||||
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
QApplication app( argc, argv );
|
||||
|
||||
TestInkEdit edit;
|
||||
edit.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2009 by Adam Goossens *
|
||||
* adam@kmess.org *
|
||||
* *
|
||||
* This program 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 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 Lesser General Public *
|
||||
* License along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include "ui_testinkedit.h"
|
||||
|
||||
#include <IsfQtDrawing>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QVBoxLayout>
|
||||
#include <QAbstractButton>
|
||||
|
||||
#include <IsfInkCanvas>
|
||||
|
||||
|
||||
class TestInkEdit : public QWidget, public Ui::TestInkEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
TestInkEdit();
|
||||
~TestInkEdit();
|
||||
|
||||
public slots:
|
||||
|
||||
void saveInk();
|
||||
void loadInk();
|
||||
void clearInk();
|
||||
void penTypeChanged( QAbstractButton *button );
|
||||
void chooseColor();
|
||||
void inkChanged();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,234 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TestInkEdit</class>
|
||||
<widget class="QWidget" name="TestInkEdit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>495</width>
|
||||
<height>459</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Ink Editor</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Draw Something!</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="Isf::InkCanvas" name="editor_" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdStrokeColor_">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stroke Colour</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdCanvasColor_">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Canvas Colour</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Stroke Width </p>
|
||||
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(Pixels)</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinWidth_">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> pixels</string>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>4</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="penType_">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Pen Type</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbDrawing_">
|
||||
<property name="text">
|
||||
<string>Drawing</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbEraser_">
|
||||
<property name="text">
|
||||
<string>Eraser</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="emptyDrawingCheckbox_">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Is empty?</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="statusLabel_"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdSave_">
|
||||
<property name="text">
|
||||
<string>Save Ink</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdLoad_">
|
||||
<property name="text">
|
||||
<string>Load Ink</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cmdClear_">
|
||||
<property name="text">
|
||||
<string>Clear Ink</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Isf::InkCanvas</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">IsfInkCanvas</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user