[pulseview] 03/06: New upstream version 0.3.0
Zoltan Gyarmati
zgyarmati-guest at moszumanska.debian.org
Wed Mar 29 21:43:19 UTC 2017
This is an automated email from the git hooks/post-receive script.
zgyarmati-guest pushed a commit to branch master
in repository pulseview.
commit 2d67d35fbb14ee3551208775f1e978fcca53a4c8
Author: Zoltan Gyarmati <mr.zoltan.gyarmati at gmail.com>
Date: Wed Mar 1 02:14:46 2017 +0100
New upstream version 0.3.0
---
CMake/GetGitRevisionDescription.cmake | 130 +
CMake/GetGitRevisionDescription.cmake.in | 41 +
CMake/cotire.cmake | 3185 --------------------
CMakeLists.txt | 288 +-
Doxyfile | 234 ++
HACKING | 2 +-
INSTALL | 22 +-
NEWS | 157 +
README | 10 +-
android/AndroidManifest.xml | 68 +
android/assetreader.cpp | 82 +
android/assetreader.hpp | 41 +
android/bundled_libs.xml.in | 24 +
android/custom_rules.xml | 123 +
android/loghandler.cpp | 103 +
android/loghandler.hpp | 39 +
android/res/layout/splash.xml | 20 +
android/res/values/libs.xml | 49 +
android/res/values/strings-pv.xml | 22 +
.../org/sigrok/pulseview/PulseViewActivity.java | 51 +
.../org/sigrok/pulseview/PulseViewApplication.java | 38 +
config.h.in | 1 +
...{pulseview_cross.nsi => pulseview_cross.nsi.in} | 51 +-
doc/pulseview.1 | 63 +-
extdef.h | 2 +-
icons/add-decoder.svg | 7 +
icons/channels.svg | 26 +
icons/decoder-hidden.svg | 41 +-
icons/decoder-shown.svg | 60 +-
icons/menu.svg | 21 +
icons/probes.svg | 72 -
icons/show-cursors.svg | 7 +
icons/sigrok-logo-notext.ico | Bin 9662 -> 9662 bytes
icons/sigrok-logo-notext.svg | 234 ++
icons/status-green.svg | 20 +-
icons/status-grey.svg | 20 +-
icons/status-red.svg | 20 +-
icons/trigger-change.svg | 87 +-
icons/trigger-falling.svg | 46 +-
icons/trigger-high.svg | 46 +-
icons/trigger-low.svg | 46 +-
...rigger-rising.svg => trigger-marker-change.svg} | 12 +-
...trigger-none.svg => trigger-marker-falling.svg} | 15 +-
.../{trigger-none.svg => trigger-marker-high.svg} | 15 +-
.../{trigger-rising.svg => trigger-marker-low.svg} | 11 +-
...rigger-rising.svg => trigger-marker-rising.svg} | 8 +-
icons/trigger-none.svg | 43 +-
icons/trigger-rising.svg | 46 +-
main.cpp | 105 +-
pulseview.qrc | 12 +-
pv/{data/signaldata.cpp => application.cpp} | 41 +-
pv/{view/marginwidget.cpp => application.hpp} | 23 +-
pv/{prop => }/binding/binding.cpp | 35 +-
pv/{prop/binding/binding.h => binding/binding.hpp} | 22 +-
.../decoderoptions.cpp => binding/decoder.cpp} | 99 +-
.../decoderoptions.h => binding/decoder.hpp} | 30 +-
pv/binding/device.cpp | 196 ++
pv/binding/device.hpp | 72 +
pv/binding/inputoutput.cpp | 119 +
pv/binding/inputoutput.hpp | 81 +
pv/data/analog.cpp | 29 +-
pv/data/{analog.h => analog.hpp} | 26 +-
pv/data/{analogsnapshot.cpp => analogsegment.cpp} | 98 +-
pv/data/{analogsnapshot.h => analogsegment.hpp} | 22 +-
pv/data/decode/annotation.cpp | 21 +-
pv/data/decode/{annotation.h => annotation.hpp} | 14 +-
pv/data/decode/decoder.cpp | 71 +-
pv/data/decode/{decoder.h => decoder.hpp} | 31 +-
pv/data/decode/row.cpp | 32 +-
pv/data/decode/{row.h => row.hpp} | 14 +-
pv/data/decode/rowdata.cpp | 11 +-
pv/data/decode/{rowdata.h => rowdata.hpp} | 10 +-
pv/data/decoderstack.cpp | 272 +-
pv/data/{decoderstack.h => decoderstack.hpp} | 78 +-
pv/data/logic.cpp | 41 +-
pv/data/{logic.h => logic.hpp} | 31 +-
pv/data/{logicsnapshot.cpp => logicsegment.cpp} | 180 +-
pv/data/{logicsnapshot.h => logicsegment.hpp} | 54 +-
pv/data/segment.cpp | 110 +
pv/data/{snapshot.h => segment.hpp} | 47 +-
pv/data/signaldata.cpp | 22 +-
pv/data/{signaldata.h => signaldata.hpp} | 24 +-
pv/data/snapshot.cpp | 95 -
pv/device/device.cpp | 104 -
pv/device/devinst.cpp | 139 -
pv/device/devinst.h | 95 -
pv/device/file.cpp | 66 -
pv/device/inputfile.cpp | 143 -
pv/device/inputfile.h | 68 -
pv/device/sessionfile.cpp | 75 -
pv/device/sessionfile.h | 47 -
pv/devicemanager.cpp | 205 +-
pv/devicemanager.h | 75 -
pv/devicemanager.hpp | 83 +
pv/devices/device.cpp | 99 +
pv/devices/device.hpp | 85 +
pv/{data/signaldata.cpp => devices/file.cpp} | 29 +-
pv/{device/file.h => devices/file.hpp} | 33 +-
pv/devices/hardwaredevice.cpp | 133 +
pv/devices/hardwaredevice.hpp | 68 +
pv/devices/inputfile.cpp | 106 +
pv/{view/viewport.h => devices/inputfile.hpp} | 57 +-
.../signaldata.cpp => devices/sessionfile.cpp} | 36 +-
pv/{dialogs/about.h => devices/sessionfile.hpp} | 37 +-
pv/dialogs/about.cpp | 40 +-
pv/dialogs/{about.h => about.hpp} | 14 +-
pv/dialogs/about.ui | 2 +-
pv/dialogs/connect.cpp | 242 +-
pv/dialogs/{connect.h => connect.hpp} | 57 +-
pv/dialogs/{about.h => inputoutputoptions.cpp} | 54 +-
pv/dialogs/inputoutputoptions.hpp | 71 +
pv/dialogs/storeprogress.cpp | 45 +-
pv/dialogs/{storeprogress.h => storeprogress.hpp} | 23 +-
pv/mainwindow.cpp | 825 +++--
pv/mainwindow.h | 122 -
pv/mainwindow.hpp | 212 ++
pv/popups/channels.cpp | 260 ++
pv/popups/{probes.h => channels.hpp} | 65 +-
pv/popups/deviceoptions.cpp | 29 +-
pv/popups/{deviceoptions.h => deviceoptions.hpp} | 24 +-
pv/popups/probes.cpp | 258 --
pv/prop/binding/deviceoptions.cpp | 192 --
pv/prop/binding/deviceoptions.h | 72 -
pv/prop/bool.cpp | 38 +-
pv/prop/{bool.h => bool.hpp} | 12 +-
pv/prop/double.cpp | 55 +-
pv/prop/{double.h => double.hpp} | 20 +-
pv/prop/enum.cpp | 57 +-
pv/prop/{enum.h => enum.hpp} | 20 +-
pv/prop/int.cpp | 125 +-
pv/prop/{int.h => int.hpp} | 18 +-
pv/prop/property.cpp | 10 +-
pv/prop/{property.h => property.hpp} | 23 +-
pv/prop/string.cpp | 42 +-
pv/prop/{string.h => string.hpp} | 12 +-
pv/session.cpp | 682 +++++
pv/session.hpp | 200 ++
pv/sigsession.cpp | 650 ----
pv/sigsession.h | 204 --
pv/storesession.cpp | 224 +-
pv/{storesession.h => storesession.hpp} | 58 +-
pv/toolbars/mainbar.cpp | 565 ++++
pv/toolbars/{samplingbar.h => mainbar.hpp} | 89 +-
pv/toolbars/samplingbar.cpp | 450 ---
pv/util.cpp | 239 ++
pv/util.hpp | 130 +
pv/view/analogsignal.cpp | 158 +-
pv/view/{analogsignal.h => analogsignal.hpp} | 74 +-
pv/view/cursor.cpp | 142 +-
pv/view/{cursor.h => cursor.hpp} | 43 +-
pv/view/cursorpair.cpp | 143 +-
pv/view/cursorpair.hpp | 111 +
pv/view/decodetrace.cpp | 626 ++--
pv/view/decodetrace.h | 191 --
pv/view/decodetrace.hpp | 217 ++
pv/view/flag.cpp | 112 +
pv/view/{cursorpair.h => flag.hpp} | 60 +-
pv/view/header.cpp | 342 +--
pv/view/{header.h => header.hpp} | 70 +-
pv/view/logicsignal.cpp | 465 ++-
pv/view/logicsignal.h | 119 -
pv/view/logicsignal.hpp | 155 +
pv/view/marginwidget.cpp | 51 +-
pv/view/{selectableitem.h => marginwidget.hpp} | 61 +-
pv/view/{marginwidget.cpp => rowitem.cpp} | 10 +-
pv/view/{marginwidget.h => rowitem.hpp} | 25 +-
pv/view/ruler.cpp | 354 +--
pv/view/ruler.h | 84 -
pv/view/ruler.hpp | 180 ++
pv/view/selectableitem.cpp | 66 -
pv/view/signal.cpp | 94 +-
pv/view/{signal.h => signal.hpp} | 67 +-
pv/view/signalscalehandle.cpp | 107 +
pv/view/signalscalehandle.hpp | 93 +
pv/view/{marginwidget.cpp => timeitem.cpp} | 17 +-
pv/view/{marginwidget.h => timeitem.hpp} | 39 +-
pv/view/timemarker.cpp | 163 +-
pv/view/timemarker.h | 113 -
pv/view/timemarker.hpp | 133 +
pv/view/trace.cpp | 213 +-
pv/view/trace.h | 203 --
pv/view/trace.hpp | 145 +
pv/view/tracegroup.cpp | 226 ++
pv/view/tracegroup.hpp | 133 +
pv/view/tracepalette.cpp | 2 +-
pv/view/{tracepalette.h => tracepalette.hpp} | 6 +-
pv/view/tracetreeitem.cpp | 147 +
pv/view/tracetreeitem.hpp | 142 +
pv/view/tracetreeitemowner.cpp | 130 +
pv/view/tracetreeitemowner.hpp | 117 +
pv/view/triggermarker.cpp | 82 +
pv/view/triggermarker.hpp | 86 +
pv/view/view.cpp | 1102 +++++--
pv/view/view.h | 214 --
pv/view/view.hpp | 404 +++
pv/view/viewitem.cpp | 139 +
pv/view/viewitem.hpp | 178 ++
pv/view/viewitemiterator.hpp | 129 +
.../decode/rowdata.cpp => view/viewitemowner.cpp} | 44 +-
pv/view/viewitemowner.hpp | 96 +
.../device.h => view/viewitempaintparams.cpp} | 46 +-
pv/view/viewitempaintparams.hpp | 92 +
pv/view/viewport.cpp | 236 +-
pv/view/viewport.hpp | 110 +
pv/view/viewwidget.cpp | 301 ++
pv/view/viewwidget.hpp | 152 +
pv/widgets/colourbutton.cpp | 40 +-
pv/widgets/{colourbutton.h => colourbutton.hpp} | 16 +-
pv/widgets/colourpopup.cpp | 18 +-
pv/widgets/{colourpopup.h => colourpopup.hpp} | 20 +-
pv/widgets/decodergroupbox.cpp | 46 +-
.../{decodergroupbox.h => decodergroupbox.hpp} | 15 +-
pv/widgets/decodermenu.cpp | 19 +-
pv/widgets/{decodermenu.h => decodermenu.hpp} | 12 +-
pv/widgets/devicetoolbutton.cpp | 153 +
pv/widgets/devicetoolbutton.hpp | 105 +
pv/widgets/exportmenu.cpp | 99 +
pv/widgets/{decodermenu.h => exportmenu.hpp} | 33 +-
.../signaldata.cpp => widgets/hidingmenubar.cpp} | 35 +-
pv/widgets/{colourpopup.h => hidingmenubar.hpp} | 49 +-
pv/widgets/importmenu.cpp | 89 +
pv/widgets/{decodermenu.h => importmenu.hpp} | 33 +-
pv/widgets/popup.cpp | 101 +-
pv/widgets/{popup.h => popup.hpp} | 16 +-
pv/widgets/popuptoolbutton.cpp | 14 +-
.../{popuptoolbutton.h => popuptoolbutton.hpp} | 12 +-
pv/widgets/sweeptimingwidget.cpp | 81 +-
.../{sweeptimingwidget.h => sweeptimingwidget.hpp} | 20 +-
pv/widgets/timestampspinbox.cpp | 121 +
pv/widgets/timestampspinbox.hpp | 93 +
pv/widgets/wellarray.cpp | 50 +-
pv/widgets/{wellarray.h => wellarray.hpp} | 18 +-
signalhandler.cpp | 32 +-
signalhandler.h => signalhandler.hpp | 16 +-
test/CMakeLists.txt | 180 +-
.../data/{analogsnapshot.cpp => analogsegment.cpp} | 48 +-
test/data/decoderstack.cpp | 24 +-
test/data/{logicsnapshot.cpp => logicsegment.cpp} | 208 +-
test/test.cpp | 6 +
test/{test.cpp => test.hpp} | 12 +-
test/util.cpp | 240 ++
test/view/ruler.cpp | 172 ++
242 files changed, 16354 insertions(+), 11682 deletions(-)
diff --git a/CMake/GetGitRevisionDescription.cmake b/CMake/GetGitRevisionDescription.cmake
new file mode 100644
index 0000000..c8d27f2
--- /dev/null
+++ b/CMake/GetGitRevisionDescription.cmake
@@ -0,0 +1,130 @@
+# - Returns a version string from Git
+#
+# These functions force a re-configure on each git commit so that you can
+# trust the values of the variables in your build system.
+#
+# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git describe> ...])
+#
+# Returns the refspec and sha hash of the current head revision
+#
+# git_describe(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe on the source tree, and adjusting
+# the output so that it tests false if an error occurs.
+#
+# git_get_exact_tag(<var> [<additional arguments to git describe> ...])
+#
+# Returns the results of git describe --exact-match on the source tree,
+# and adjusting the output so that it tests false if there was no exact
+# matching tag.
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+if(__get_git_revision_description)
+ return()
+endif()
+set(__get_git_revision_description YES)
+
+# We must run the following at "include" time, not at function call time,
+# to find the path to this module rather than the path to a calling list file
+get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH)
+
+function(get_git_head_revision _refspecvar _hashvar)
+ set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+ while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories
+ set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}")
+ get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH)
+ if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT)
+ # We have reached the root directory, we are not in git
+ set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+ set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+ set(GIT_DIR "${GIT_PARENT_DIR}/.git")
+ endwhile()
+ # check if this is a submodule
+ if(NOT IS_DIRECTORY ${GIT_DIR})
+ file(READ ${GIT_DIR} submodule)
+ string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule})
+ get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH)
+ get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE)
+ endif()
+ set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data")
+ if(NOT EXISTS "${GIT_DATA}")
+ file(MAKE_DIRECTORY "${GIT_DATA}")
+ endif()
+
+ if(NOT EXISTS "${GIT_DIR}/HEAD")
+ return()
+ endif()
+ set(HEAD_FILE "${GIT_DATA}/HEAD")
+ configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY)
+
+ configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in"
+ "${GIT_DATA}/grabRef.cmake"
+ @ONLY)
+ include("${GIT_DATA}/grabRef.cmake")
+
+ set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE)
+ set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE)
+endfunction()
+
+function(git_describe _var)
+ if(NOT GIT_FOUND)
+ find_package(Git QUIET)
+ endif()
+ get_git_head_revision(refspec hash)
+ if(NOT GIT_FOUND)
+ set(${_var} "GIT-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+ if(NOT hash)
+ set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE)
+ return()
+ endif()
+
+ # TODO sanitize
+ #if((${ARGN}" MATCHES "&&") OR
+ # (ARGN MATCHES "||") OR
+ # (ARGN MATCHES "\\;"))
+ # message("Please report the following error to the project!")
+ # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}")
+ #endif()
+
+ #message(STATUS "Arguments to execute_process: ${ARGN}")
+
+ execute_process(COMMAND
+ "${GIT_EXECUTABLE}"
+ describe
+ ${hash}
+ ${ARGN}
+ WORKING_DIRECTORY
+ "${CMAKE_SOURCE_DIR}"
+ RESULT_VARIABLE
+ res
+ OUTPUT_VARIABLE
+ out
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(NOT res EQUAL 0)
+ set(out "${out}-${res}-NOTFOUND")
+ endif()
+
+ set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
+
+function(git_get_exact_tag _var)
+ git_describe(out --exact-match ${ARGN})
+ set(${_var} "${out}" PARENT_SCOPE)
+endfunction()
diff --git a/CMake/GetGitRevisionDescription.cmake.in b/CMake/GetGitRevisionDescription.cmake.in
new file mode 100644
index 0000000..6d8b708
--- /dev/null
+++ b/CMake/GetGitRevisionDescription.cmake.in
@@ -0,0 +1,41 @@
+#
+# Internal file for GetGitRevisionDescription.cmake
+#
+# Requires CMake 2.6 or newer (uses the 'function' command)
+#
+# Original Author:
+# 2009-2010 Ryan Pavlik <rpavlik at iastate.edu> <abiryan at ryand.net>
+# http://academic.cleardefinition.com
+# Iowa State University HCI Graduate Program/VRAC
+#
+# Copyright Iowa State University 2009-2010.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+set(HEAD_HASH)
+
+file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024)
+
+string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS)
+if(HEAD_CONTENTS MATCHES "ref")
+ # named branch
+ string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}")
+ if(EXISTS "@GIT_DIR@/${HEAD_REF}")
+ configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY)
+ else()
+ configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY)
+ file(READ "@GIT_DATA@/packed-refs" PACKED_REFS)
+ if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}")
+ set(HEAD_HASH "${CMAKE_MATCH_1}")
+ endif()
+ endif()
+else()
+ # detached HEAD
+ configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY)
+endif()
+
+if(NOT HEAD_HASH)
+ file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024)
+ string(STRIP "${HEAD_HASH}" HEAD_HASH)
+endif()
diff --git a/CMake/cotire.cmake b/CMake/cotire.cmake
deleted file mode 100644
index a6e3141..0000000
--- a/CMake/cotire.cmake
+++ /dev/null
@@ -1,3185 +0,0 @@
-# - cotire (compile time reducer)
-#
-# See the cotire manual for usage hints.
-#
-#=============================================================================
-# Copyright 2012-2013 Sascha Kratky
-#
-# Permission is hereby granted, free of charge, to any person
-# obtaining a copy of this software and associated documentation
-# files (the "Software"), to deal in the Software without
-# restriction, including without limitation the rights to use,
-# copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following
-# conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-# OTHER DEALINGS IN THE SOFTWARE.
-#=============================================================================
-
-if(__COTIRE_INCLUDED)
- return()
-endif()
-set(__COTIRE_INCLUDED TRUE)
-
-# call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode
-# cmake_minimum_required also sets the policy version as a side effect, which we have to avoid
-if (NOT CMAKE_SCRIPT_MODE_FILE)
- cmake_policy(PUSH)
-endif()
-# we need the CMake variables CMAKE_SCRIPT_MODE_FILE and CMAKE_ARGV available since 2.8.5
-# we need APPEND_STRING option for set_property available since 2.8.6
-cmake_minimum_required(VERSION 2.8.6)
-if (NOT CMAKE_SCRIPT_MODE_FILE)
- cmake_policy(POP)
-endif()
-
-set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}")
-set (COTIRE_CMAKE_MODULE_VERSION "1.4.1")
-
-include(CMakeParseArguments)
-include(ProcessorCount)
-
-function (cotire_determine_compiler_version _language _versionPrefix)
- if (NOT ${_versionPrefix}_VERSION)
- # use CMake's predefined compiler version variable (available since CMake 2.8.8)
- if (DEFINED CMAKE_${_language}_COMPILER_VERSION)
- set (${_versionPrefix}_VERSION "${CMAKE_${_language}_COMPILER_VERSION}")
- elseif (WIN32)
- # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
- unset (ENV{VS_UNICODE_OUTPUT})
- string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1)
- execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1}
- ERROR_VARIABLE _versionLine OUTPUT_QUIET TIMEOUT 10)
- string (REGEX REPLACE ".*Version *([0-9]+(\\.[0-9]+)*).*" "\\1" ${_versionPrefix}_VERSION "${_versionLine}")
- else()
- # assume GCC like command line interface
- string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1)
- execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} "-dumpversion"
- OUTPUT_VARIABLE ${_versionPrefix}_VERSION
- RESULT_VARIABLE _result
- OUTPUT_STRIP_TRAILING_WHITESPACE TIMEOUT 10)
- if (_result)
- set (${_versionPrefix}_VERSION "")
- endif()
- endif()
- if (${_versionPrefix}_VERSION)
- set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" CACHE INTERNAL "${_language} compiler version")
- endif()
- set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" PARENT_SCOPE)
- if (COTIRE_DEBUG)
- message (STATUS "${CMAKE_${_language}_COMPILER} version ${${_versionPrefix}_VERSION}")
- endif()
- endif()
-endfunction()
-
-function (cotire_get_source_file_extension _sourceFile _extVar)
- # get_filename_component returns extension from first occurrence of . in file name
- # this function computes the extension from last occurrence of . in file name
- string (FIND "${_sourceFile}" "." _index REVERSE)
- if (_index GREATER -1)
- math (EXPR _index "${_index} + 1")
- string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt)
- else()
- set (_sourceExt "")
- endif()
- set (${_extVar} "${_sourceExt}" PARENT_SCOPE)
-endfunction()
-
-macro (cotire_check_is_path_relative_to _path _isRelativeVar)
- set (${_isRelativeVar} FALSE)
- if (IS_ABSOLUTE "${_path}")
- foreach (_dir ${ARGN})
- file (RELATIVE_PATH _relPath "${_dir}" "${_path}")
- if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\."))
- set (${_isRelativeVar} TRUE)
- break()
- endif()
- endforeach()
- endif()
-endmacro()
-
-function (cotire_filter_language_source_files _language _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar)
- set (_sourceFiles "")
- set (_excludedSourceFiles "")
- set (_cotiredSourceFiles "")
- if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS)
- set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}")
- else()
- set (_languageExtensions "")
- endif()
- if (CMAKE_${_language}_IGNORE_EXTENSIONS)
- set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}")
- else()
- set (_ignoreExtensions "")
- endif()
- if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS)
- set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}")
- else()
- set (_excludeExtensions "")
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "${_language} source file extensions: ${_languageExtensions}")
- message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}")
- message (STATUS "${_language} exclude extensions: ${_excludeExtensions}")
- endif()
- foreach (_sourceFile ${ARGN})
- get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY)
- get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT)
- get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC)
- get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE)
- set (_sourceIsFiltered FALSE)
- if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic)
- cotire_get_source_file_extension("${_sourceFile}" _sourceExt)
- if (_sourceExt)
- list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex)
- if (_ignoreIndex LESS 0)
- list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex)
- if (_excludeIndex GREATER -1)
- list (APPEND _excludedSourceFiles "${_sourceFile}")
- else()
- list (FIND _languageExtensions "${_sourceExt}" _sourceIndex)
- if (_sourceIndex GREATER -1)
- set (_sourceIsFiltered TRUE)
- elseif ("${_sourceLanguage}" STREQUAL "${_language}")
- # add to excluded sources, if file is not ignored and has correct language without having the correct extension
- list (APPEND _excludedSourceFiles "${_sourceFile}")
- endif()
- endif()
- endif()
- endif()
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "${_sourceFile} filtered=${_sourceIsFiltered} language=${_sourceLanguage} header=${_sourceIsHeaderOnly}")
- endif()
- if (_sourceIsFiltered)
- get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED)
- get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET)
- get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS)
- if (COTIRE_DEBUG)
- message (STATUS "${_sourceFile} excluded=${_sourceIsExcluded} cotired=${_sourceIsCotired}")
- endif()
- if (_sourceIsCotired)
- list (APPEND _cotiredSourceFiles "${_sourceFile}")
- elseif (_sourceIsExcluded OR _sourceCompileFlags)
- list (APPEND _excludedSourceFiles "${_sourceFile}")
- else()
- list (APPEND _sourceFiles "${_sourceFile}")
- endif()
- endif()
- endforeach()
- if (COTIRE_DEBUG)
- message (STATUS "All: ${ARGN}")
- message (STATUS "${_language}: ${_sourceFiles}")
- message (STATUS "Excluded: ${_excludedSourceFiles}")
- message (STATUS "Cotired: ${_cotiredSourceFiles}")
- endif()
- set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE)
- set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE)
- set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type)
- set (_filteredObjects "")
- foreach (_object ${ARGN})
- get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET)
- if (_isSet)
- get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
- if (_propertyValue)
- list (APPEND _filteredObjects "${_object}")
- endif()
- endif()
- endforeach()
- set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type)
- set (_filteredObjects "")
- foreach (_object ${ARGN})
- get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET)
- if (_isSet)
- get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
- if (NOT _propertyValue)
- list (APPEND _filteredObjects "${_object}")
- endif()
- endif()
- endforeach()
- set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_source_file_property_values _valuesVar _property)
- set (_values "")
- foreach (_sourceFile ${ARGN})
- get_source_file_property(_propertyValue "${_sourceFile}" ${_property})
- if (_propertyValue)
- list (APPEND _values "${_propertyValue}")
- endif()
- endforeach()
- set (${_valuesVar} ${_values} PARENT_SCOPE)
-endfunction()
-
-function (cotrie_resolve_config_properites _configurations _propertiesVar)
- set (_properties "")
- foreach (_property ${ARGN})
- if ("${_property}" MATCHES "<CONFIG>")
- foreach (_config ${_configurations})
- string (TOUPPER "${_config}" _upperConfig)
- string (REPLACE "<CONFIG>" "${_upperConfig}" _configProperty "${_property}")
- list (APPEND _properties ${_configProperty})
- endforeach()
- else()
- list (APPEND _properties ${_property})
- endif()
- endforeach()
- set (${_propertiesVar} ${_properties} PARENT_SCOPE)
-endfunction()
-
-function (cotrie_copy_set_properites _configurations _type _source _target)
- cotrie_resolve_config_properites("${_configurations}" _properties ${ARGN})
- foreach (_property ${_properties})
- get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET)
- if (_isSet)
- get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property})
- set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}")
- endif()
- endforeach()
-endfunction()
-
-function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar)
- if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- set (_flagPrefix "[/-]")
- else()
- set (_flagPrefix "--?")
- endif()
- set (_optionFlag "")
- set (_matchedOptions "")
- set (_unmatchedOptions "")
- foreach (_compileFlag ${ARGN})
- if (_compileFlag)
- if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}")
- # option with separate argument
- list (APPEND _matchedOptions "${_compileFlag}")
- set (_optionFlag "")
- elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$")
- # remember option
- set (_optionFlag "${CMAKE_MATCH_2}")
- elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$")
- # option with joined argument
- list (APPEND _matchedOptions "${CMAKE_MATCH_3}")
- set (_optionFlag "")
- else()
- # flush remembered option
- if (_optionFlag)
- list (APPEND _matchedOptions "${_optionFlag}")
- set (_optionFlag "")
- endif()
- # add to unfiltered options
- list (APPEND _unmatchedOptions "${_compileFlag}")
- endif()
- endif()
- endforeach()
- if (_optionFlag)
- list (APPEND _matchedOptions "${_optionFlag}")
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "Filter ${_flagFilter}")
- if (_matchedOptions)
- message (STATUS "Matched ${_matchedOptions}")
- endif()
- if (_unmatchedOptions)
- message (STATUS "Unmatched ${_unmatchedOptions}")
- endif()
- endif()
- set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE)
- set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_target_compile_flags _config _language _directory _target _flagsVar)
- string (TOUPPER "${_config}" _upperConfig)
- # collect options from CMake language variables
- set (_compileFlags "")
- if (CMAKE_${_language}_FLAGS)
- set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}")
- endif()
- if (CMAKE_${_language}_FLAGS_${_upperConfig})
- set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}")
- endif()
- if (_target)
- # add option from CMake target type variable
- get_target_property(_targetType ${_target} TYPE)
- if (POLICY CMP0018)
- # handle POSITION_INDEPENDENT_CODE property introduced with CMake 2.8.9 if policy CMP0018 is turned on
- cmake_policy(GET CMP0018 _PIC_Policy)
- else()
- # default to old behavior
- set (_PIC_Policy "OLD")
- endif()
- if (COTIRE_DEBUG)
- message(STATUS "CMP0018=${_PIC_Policy}")
- endif()
- if (_PIC_Policy STREQUAL "NEW")
- # NEW behavior: honor the POSITION_INDEPENDENT_CODE target property
- get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE)
- if (_targetPIC)
- if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE)
- set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIE}")
- elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC)
- set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIC}")
- endif()
- endif()
- else()
- # OLD behavior or policy not set: use the value of CMAKE_SHARED_LIBRARY_<Lang>_FLAGS
- if (_targetType STREQUAL "MODULE_LIBRARY")
- # flags variable for module library uses different name SHARED_MODULE
- # (e.g., CMAKE_SHARED_MODULE_C_FLAGS)
- set (_targetType SHARED_MODULE)
- endif()
- if (CMAKE_${_targetType}_${_language}_FLAGS)
- set (_compileFlags "${_compileFlags} ${CMAKE_${_targetType}_${_language}_FLAGS}")
- endif()
- endif()
- endif()
- if (_directory)
- # add_definitions may have been used to add flags to the compiler command
- get_directory_property(_dirDefinitions DIRECTORY "${_directory}" DEFINITIONS)
- if (_dirDefinitions)
- set (_compileFlags "${_compileFlags} ${_dirDefinitions}")
- endif()
- endif()
- if (_target)
- # add target compile options
- get_target_property(_targetflags ${_target} COMPILE_FLAGS)
- if (_targetflags)
- set (_compileFlags "${_compileFlags} ${_targetflags}")
- endif()
- endif()
- if (UNIX)
- separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}")
- elseif(WIN32)
- separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}")
- else()
- separate_arguments(_compileFlags)
- endif()
- # platform specific flags
- if (APPLE)
- get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig})
- if (NOT _architectures)
- get_target_property(_architectures ${_target} OSX_ARCHITECTURES)
- endif()
- foreach (_arch ${_architectures})
- list (APPEND _compileFlags "-arch" "${_arch}")
- endforeach()
- if (CMAKE_OSX_SYSROOT AND CMAKE_OSX_SYSROOT_DEFAULT AND CMAKE_${_language}_HAS_ISYSROOT)
- if (NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "${CMAKE_OSX_SYSROOT_DEFAULT}")
- list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}")
- endif()
- endif()
- if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG)
- list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}")
- endif()
- endif()
- if (COTIRE_DEBUG AND _compileFlags)
- message (STATUS "Target ${_target} compile flags ${_compileFlags}")
- endif()
- set (${_flagsVar} ${_compileFlags} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_target_include_directories _config _language _targetSourceDir _targetBinaryDir _target _includeDirsVar)
- set (_includeDirs "")
- # default include dirs
- if (CMAKE_INCLUDE_CURRENT_DIR)
- list (APPEND _includeDirs "${_targetBinaryDir}")
- list (APPEND _includeDirs "${_targetSourceDir}")
- endif()
- # parse additional include directories from target compile flags
- set (_targetFlags "")
- cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags)
- cotire_filter_compile_flags("${_language}" "I" _dirs _ignore ${_targetFlags})
- if (_dirs)
- list (APPEND _includeDirs ${_dirs})
- endif()
- # target include directories
- get_directory_property(_dirs DIRECTORY "${_targetSourceDir}" INCLUDE_DIRECTORIES)
- if (_target)
- get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES)
- if (_targetDirs)
- list (APPEND _dirs ${_targetDirs})
- list (REMOVE_DUPLICATES _dirs)
- endif()
- endif()
- list (LENGTH _includeDirs _projectInsertIndex)
- foreach (_dir ${_dirs})
- if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE)
- cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}")
- if (_isRelative)
- list (LENGTH _includeDirs _len)
- if (_len EQUAL _projectInsertIndex)
- list (APPEND _includeDirs "${_dir}")
- else()
- list (INSERT _includeDirs _projectInsertIndex "${_dir}")
- endif()
- math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1")
- else()
- list (APPEND _includeDirs "${_dir}")
- endif()
- else()
- list (APPEND _includeDirs "${_dir}")
- endif()
- endforeach()
- list (REMOVE_DUPLICATES _includeDirs)
- if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES)
- list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES})
- endif()
- if (COTIRE_DEBUG AND _includeDirs)
- message (STATUS "Target ${_target} include dirs ${_includeDirs}")
- endif()
- set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE)
-endfunction()
-
-macro (cotire_make_C_identifier _identifierVar _str)
- # mimic CMake SystemTools::MakeCindentifier behavior
- if ("${_str}" MATCHES "^[0-9].+$")
- set (_str "_${str}")
- endif()
- string (REGEX REPLACE "[^a-zA-Z0-9]" "_" ${_identifierVar} "${_str}")
-endmacro()
-
-function (cotire_get_target_export_symbol _target _exportSymbolVar)
- set (_exportSymbol "")
- get_target_property(_targetType ${_target} TYPE)
- get_target_property(_enableExports ${_target} ENABLE_EXPORTS)
- if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR
- (_targetType STREQUAL "EXECUTABLE" AND _enableExports))
- get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL)
- if (NOT _exportSymbol)
- set (_exportSymbol "${_target}_EXPORTS")
- endif()
- cotire_make_C_identifier(_exportSymbol "${_exportSymbol}")
- endif()
- set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_target_compile_definitions _config _language _directory _target _definitionsVar)
- string (TOUPPER "${_config}" _upperConfig)
- set (_configDefinitions "")
- # CMAKE_INTDIR for multi-configuration build systems
- if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".")
- list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"")
- endif()
- # target export define symbol
- cotire_get_target_export_symbol("${_target}" _defineSymbol)
- if (_defineSymbol)
- list (APPEND _configDefinitions "${_defineSymbol}")
- endif()
- # directory compile definitions
- get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS)
- if (_definitions)
- list (APPEND _configDefinitions ${_definitions})
- endif()
- get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS_${_upperConfig})
- if (_definitions)
- list (APPEND _configDefinitions ${_definitions})
- endif()
- # target compile definitions
- get_target_property(_definitions ${_target} COMPILE_DEFINITIONS)
- if (_definitions)
- list (APPEND _configDefinitions ${_definitions})
- endif()
- get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig})
- if (_definitions)
- list (APPEND _configDefinitions ${_definitions})
- endif()
- # parse additional compile definitions from target compile flags
- # and don't look at directory compile definitions, which we already handled
- set (_targetFlags "")
- cotire_get_target_compile_flags("${_config}" "${_language}" "" "${_target}" _targetFlags)
- cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags})
- if (_definitions)
- list (APPEND _configDefinitions ${_definitions})
- endif()
- list (REMOVE_DUPLICATES _configDefinitions)
- if (COTIRE_DEBUG AND _configDefinitions)
- message (STATUS "Target ${_target} compile definitions ${_configDefinitions}")
- endif()
- set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_target_compiler_flags _config _language _directory _target _compilerFlagsVar)
- # parse target compile flags omitting compile definitions and include directives
- set (_targetFlags "")
- cotire_get_target_compile_flags("${_config}" "${_language}" "${_directory}" "${_target}" _targetFlags)
- set (_compilerFlags "")
- cotire_filter_compile_flags("${_language}" "[ID]" _ignore _compilerFlags ${_targetFlags})
- if (COTIRE_DEBUG AND _compilerFlags)
- message (STATUS "Target ${_target} compiler flags ${_compilerFlags}")
- endif()
- set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE)
-endfunction()
-
-function (cotire_add_sys_root_paths _pathsVar)
- if (APPLE)
- if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT)
- foreach (_path IN LISTS ${_pathsVar})
- if (IS_ABSOLUTE "${_path}")
- get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE)
- if (EXISTS "${_path}")
- list (APPEND ${_pathsVar} "${_path}")
- endif()
- endif()
- endforeach()
- endif()
- endif()
- set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE)
- if (COTIRE_DEBUG)
- message (STATUS "${_pathsVar}=${${_pathsVar}}")
- endif()
-endfunction()
-
-function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar)
- set (_extraProperties ${ARGN})
- set (_result "")
- if (_extraProperties)
- list (FIND _extraProperties "${_sourceFile}" _index)
- if (_index GREATER -1)
- math (EXPR _index "${_index} + 1")
- list (LENGTH _extraProperties _len)
- math (EXPR _len "${_len} - 1")
- foreach (_index RANGE ${_index} ${_len})
- list (GET _extraProperties ${_index} _value)
- if ("${_value}" MATCHES "${_pattern}")
- list (APPEND _result "${_value}")
- else()
- break()
- endif()
- endforeach()
- endif()
- endif()
- set (${_resultVar} ${_result} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar)
- set (_compileDefinitions "")
- if (NOT CMAKE_SCRIPT_MODE_FILE)
- string (TOUPPER "${_config}" _upperConfig)
- get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS)
- if (_definitions)
- list (APPEND _compileDefinitions ${_definitions})
- endif()
- get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig})
- if (_definitions)
- list (APPEND _compileDefinitions ${_definitions})
- endif()
- endif()
- cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN})
- if (_definitions)
- list (APPEND _compileDefinitions ${_definitions})
- endif()
- if (COTIRE_DEBUG AND _compileDefinitions)
- message (STATUS "Source ${_sourceFile} compile definitions ${_compileDefinitions}")
- endif()
- set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_source_files_compile_definitions _config _language _definitionsVar)
- set (_configDefinitions "")
- foreach (_sourceFile ${ARGN})
- cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions)
- if (_sourceDefinitions)
- list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-")
- endif()
- endforeach()
- set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar)
- set (_sourceUndefs "")
- if (NOT CMAKE_SCRIPT_MODE_FILE)
- get_source_file_property(_undefs "${_sourceFile}" ${_property})
- if (_undefs)
- list (APPEND _sourceUndefs ${_undefs})
- endif()
- endif()
- cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN})
- if (_undefs)
- list (APPEND _sourceUndefs ${_undefs})
- endif()
- if (COTIRE_DEBUG AND _sourceUndefs)
- message (STATUS "Source ${_sourceFile} ${_property} undefs ${_sourceUndefs}")
- endif()
- set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_source_files_undefs _property _sourceUndefsVar)
- set (_sourceUndefs "")
- foreach (_sourceFile ${ARGN})
- cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs)
- if (_undefs)
- list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-")
- endif()
- endforeach()
- set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE)
-endfunction()
-
-macro (cotire_set_cmd_to_prologue _cmdVar)
- set (${_cmdVar} "${CMAKE_COMMAND}")
- if (COTIRE_DEBUG)
- list (APPEND ${_cmdVar} "--warn-uninitialized")
- endif()
- list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$<CONFIGURATION>")
- if (COTIRE_VERBOSE)
- list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON")
- elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles")
- list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)")
- endif()
-endmacro()
-
-function (cotire_init_compile_cmd _cmdVar _language _compilerExe _compilerArg1)
- if (NOT _compilerExe)
- set (_compilerExe "${CMAKE_${_language}_COMPILER}")
- endif()
- if (NOT _compilerArg1)
- set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1})
- endif()
- string (STRIP "${_compilerArg1}" _compilerArg1)
- set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE)
-endfunction()
-
-macro (cotire_add_definitions_to_cmd _cmdVar _language)
- foreach (_definition ${ARGN})
- if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- list (APPEND ${_cmdVar} "/D${_definition}")
- else()
- list (APPEND ${_cmdVar} "-D${_definition}")
- endif()
- endforeach()
-endmacro()
-
-macro (cotire_add_includes_to_cmd _cmdVar _language)
- foreach (_include ${ARGN})
- if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- file (TO_NATIVE_PATH "${_include}" _include)
- list (APPEND ${_cmdVar} "/I${_include}")
- else()
- list (APPEND ${_cmdVar} "-I${_include}")
- endif()
- endforeach()
-endmacro()
-
-macro (cotire_add_compile_flags_to_cmd _cmdVar)
- foreach (_flag ${ARGN})
- list (APPEND ${_cmdVar} "${_flag}")
- endforeach()
-endmacro()
-
-function (cotire_check_file_up_to_date _fileIsUpToDateVar _file)
- set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE)
- set (_triggerFile "")
- foreach (_dependencyFile ${ARGN})
- if (EXISTS "${_dependencyFile}" AND "${_dependencyFile}" IS_NEWER_THAN "${_file}")
- set (_triggerFile "${_dependencyFile}")
- break()
- endif()
- endforeach()
- get_filename_component(_fileName "${_file}" NAME)
- if (EXISTS "${_file}")
- if (_triggerFile)
- if (COTIRE_VERBOSE)
- message (STATUS "${_fileName} update triggered by ${_triggerFile} change.")
- endif()
- else()
- if (COTIRE_VERBOSE)
- message (STATUS "${_fileName} is up-to-date.")
- endif()
- set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE)
- endif()
- else()
- if (COTIRE_VERBOSE)
- message (STATUS "${_fileName} does not exist yet.")
- endif()
- endif()
-endfunction()
-
-macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar)
- set (${_relPathVar} "")
- foreach (_includeDir ${_includeDirs})
- if (IS_DIRECTORY "${_includeDir}")
- file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}")
- if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")
- string (LENGTH "${${_relPathVar}}" _closestLen)
- string (LENGTH "${_relPath}" _relLen)
- if (_closestLen EQUAL 0 OR _relLen LESS _closestLen)
- set (${_relPathVar} "${_relPath}")
- endif()
- endif()
- elseif ("${_includeDir}" STREQUAL "${_headerFile}")
- # if path matches exactly, return short non-empty string
- set (${_relPathVar} "1")
- break()
- endif()
- endforeach()
-endmacro()
-
-macro (cotire_check_header_file_location _headerFile _insideIncudeDirs _outsideIncudeDirs _headerIsInside)
- # check header path against ignored and honored include directories
- cotire_find_closest_relative_path("${_headerFile}" "${_insideIncudeDirs}" _insideRelPath)
- if (_insideRelPath)
- # header is inside, but could be become outside if there is a shorter outside match
- cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncudeDirs}" _outsideRelPath)
- if (_outsideRelPath)
- string (LENGTH "${_insideRelPath}" _insideRelPathLen)
- string (LENGTH "${_outsideRelPath}" _outsideRelPathLen)
- if (_outsideRelPathLen LESS _insideRelPathLen)
- set (${_headerIsInside} FALSE)
- else()
- set (${_headerIsInside} TRUE)
- endif()
- else()
- set (${_headerIsInside} TRUE)
- endif()
- else()
- # header is outside
- set (${_headerIsInside} FALSE)
- endif()
-endmacro()
-
-macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar)
- if (NOT EXISTS "${_headerFile}")
- set (${_headerIsIgnoredVar} TRUE)
- elseif (IS_DIRECTORY "${_headerFile}")
- set (${_headerIsIgnoredVar} TRUE)
- elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$")
- # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path
- # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation
- # with the error message "error: no include path in which to search for header.h"
- set (${_headerIsIgnoredVar} TRUE)
- else()
- set (${_headerIsIgnoredVar} FALSE)
- endif()
-endmacro()
-
-macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar)
- # check header file extension
- cotire_get_source_file_extension("${_headerFile}" _headerFileExt)
- set (${_headerIsIgnoredVar} FALSE)
- if (_headerFileExt)
- list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index)
- if (_index GREATER -1)
- set (${_headerIsIgnoredVar} TRUE)
- endif()
- endif()
-endmacro()
-
-macro (cotire_parse_line _line _headerFileVar _headerDepthVar)
- if (MSVC)
- # cl.exe /showIncludes output looks different depending on the language pack used, e.g.:
- # English: "Note: including file: C:\directory\file"
- # German: "Hinweis: Einlesen der Datei: C:\directory\file"
- # We use a very general regular expression, relying on the presence of the : characters
- if ("${_line}" MATCHES ":( +)([^:]+:[^:]+)$")
- # Visual Studio compiler output
- string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar})
- get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE)
- else()
- set (${_headerFileVar} "")
- set (${_headerDepthVar} 0)
- endif()
- else()
- if ("${_line}" MATCHES "^(\\.+) (.*)$")
- # GCC like output
- string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar})
- if (IS_ABSOLUTE "${CMAKE_MATCH_2}")
- set (${_headerFileVar} "${CMAKE_MATCH_2}")
- else()
- get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH)
- endif()
- else()
- set (${_headerFileVar} "")
- set (${_headerDepthVar} 0)
- endif()
- endif()
-endmacro()
-
-function (cotire_parse_includes _language _scanOutput _ignoredIncudeDirs _honoredIncudeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar)
- if (WIN32)
- # prevent CMake macro invocation errors due to backslash characters in Windows paths
- string (REPLACE "\\" "/" _scanOutput "${_scanOutput}")
- endif()
- # canonize slashes
- string (REPLACE "//" "/" _scanOutput "${_scanOutput}")
- # prevent semicolon from being interpreted as a line separator
- string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}")
- # then separate lines
- string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}")
- list (LENGTH _scanOutput _len)
- # remove duplicate lines to speed up parsing
- list (REMOVE_DUPLICATES _scanOutput)
- list (LENGTH _scanOutput _uniqueLen)
- if (COTIRE_VERBOSE)
- message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes")
- if (_ignoredExtensions)
- message (STATUS "Ignored extensions: ${_ignoredExtensions}")
- endif()
- if (_ignoredIncudeDirs)
- message (STATUS "Ignored paths: ${_ignoredIncudeDirs}")
- endif()
- if (_honoredIncudeDirs)
- message (STATUS "Included paths: ${_honoredIncudeDirs}")
- endif()
- endif()
- set (_sourceFiles ${ARGN})
- set (_selectedIncludes "")
- set (_unparsedLines "")
- # stack keeps track of inside/outside project status of processed header files
- set (_headerIsInsideStack "")
- foreach (_line IN LISTS _scanOutput)
- if (_line)
- cotire_parse_line("${_line}" _headerFile _headerDepth)
- if (_headerFile)
- cotire_check_header_file_location("${_headerFile}" "${_ignoredIncudeDirs}" "${_honoredIncudeDirs}" _headerIsInside)
- if (COTIRE_DEBUG)
- message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}")
- endif()
- # update stack
- list (LENGTH _headerIsInsideStack _stackLen)
- if (_headerDepth GREATER _stackLen)
- math (EXPR _stackLen "${_stackLen} + 1")
- foreach (_index RANGE ${_stackLen} ${_headerDepth})
- list (APPEND _headerIsInsideStack ${_headerIsInside})
- endforeach()
- else()
- foreach (_index RANGE ${_headerDepth} ${_stackLen})
- list (REMOVE_AT _headerIsInsideStack -1)
- endforeach()
- list (APPEND _headerIsInsideStack ${_headerIsInside})
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "${_headerIsInsideStack}")
- endif()
- # header is a candidate if it is outside project
- if (NOT _headerIsInside)
- # get parent header file's inside/outside status
- if (_headerDepth GREATER 1)
- math (EXPR _index "${_headerDepth} - 2")
- list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside)
- else()
- set (_parentHeaderIsInside TRUE)
- endif()
- # select header file if parent header file is inside project
- # (e.g., a project header file that includes a standard header file)
- if (_parentHeaderIsInside)
- cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored)
- if (NOT _headerIsIgnored)
- cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored)
- if (NOT _headerIsIgnored)
- list (APPEND _selectedIncludes "${_headerFile}")
- else()
- # fix header's inside status on stack, it is ignored by extension now
- list (REMOVE_AT _headerIsInsideStack -1)
- list (APPEND _headerIsInsideStack TRUE)
- endif()
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}")
- endif()
- endif()
- endif()
- else()
- if (MSVC)
- # for cl.exe do not keep unparsed lines which solely consist of a source file name
- string (FIND "${_sourceFiles}" "${_line}" _index)
- if (_index LESS 0)
- list (APPEND _unparsedLines "${_line}")
- endif()
- else()
- list (APPEND _unparsedLines "${_line}")
- endif()
- endif()
- endif()
- endforeach()
- list (REMOVE_DUPLICATES _selectedIncludes)
- set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE)
- set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE)
-endfunction()
-
-function (cotire_scan_includes _includesVar)
- set(_options "")
- set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_VERSION LANGUAGE UNPARSED_LINES)
- set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS)
- cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
- set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
- if (NOT _option_LANGUAGE)
- set (_option_LANGUAGE "CXX")
- endif()
- if (NOT _option_COMPILER_ID)
- set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
- endif()
- set (_cmd "${_option_COMPILER_EXECUTABLE}" ${_option_COMPILER_ARG1})
- cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}")
- cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS})
- cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS})
- cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES})
- cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd)
- # only consider existing source files for scanning
- set (_existingSourceFiles "")
- foreach (_sourceFile ${_sourceFiles})
- if (EXISTS "${_sourceFile}")
- list (APPEND _existingSourceFiles "${_sourceFile}")
- endif()
- endforeach()
- if (NOT _existingSourceFiles)
- set (${_includesVar} "" PARENT_SCOPE)
- return()
- endif()
- list (APPEND _cmd ${_existingSourceFiles})
- if (COTIRE_VERBOSE)
- message (STATUS "execute_process: ${_cmd}")
- endif()
- if (_option_COMPILER_ID MATCHES "MSVC")
- if (COTIRE_DEBUG)
- message (STATUS "clearing VS_UNICODE_OUTPUT")
- endif()
- # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
- unset (ENV{VS_UNICODE_OUTPUT})
- endif()
- execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
- RESULT_VARIABLE _result OUTPUT_QUIET ERROR_VARIABLE _output)
- if (_result)
- message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.")
- endif()
- cotire_parse_includes(
- "${_option_LANGUAGE}" "${_output}"
- "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}"
- "${_option_IGNORE_EXTENSIONS}"
- _includes _unparsedLines
- ${_sourceFiles})
- set (${_includesVar} ${_includes} PARENT_SCOPE)
- if (_option_UNPARSED_LINES)
- set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE)
- endif()
-endfunction()
-
-macro (cotire_append_undefs _contentsVar)
- set (_undefs ${ARGN})
- if (_undefs)
- list (REMOVE_DUPLICATES _undefs)
- foreach (_definition ${_undefs})
- list (APPEND ${_contentsVar} "#undef ${_definition}")
- endforeach()
- endif()
-endmacro()
-
-macro (cotire_comment_str _language _commentText _commentVar)
- if ("${_language}" STREQUAL "CMAKE")
- set (${_commentVar} "# ${_commentText}")
- else()
- set (${_commentVar} "/* ${_commentText} */")
- endif()
-endmacro()
-
-function (cotire_write_file _language _file _contents _force)
- get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME)
- cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1)
- cotire_comment_str("${_language}" "${_file}" _header2)
- set (_contents "${_header1}\n${_header2}\n${_contents}")
- if (COTIRE_DEBUG)
- message (STATUS "${_contents}")
- endif()
- if (_force OR NOT EXISTS "${_file}")
- file (WRITE "${_file}" "${_contents}")
- else()
- file (READ "${_file}" _oldContents)
- if (NOT "${_oldContents}" STREQUAL "${_contents}")
- file (WRITE "${_file}" "${_contents}")
- else()
- if (COTIRE_DEBUG)
- message (STATUS "${_file} unchanged")
- endif()
- endif()
- endif()
-endfunction()
-
-function (cotire_generate_unity_source _unityFile)
- set(_options "")
- set(_oneValueArgs LANGUAGE)
- set(_multiValueArgs
- DEPENDS SOURCES_COMPILE_DEFINITIONS
- PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE)
- cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
- if (_option_DEPENDS)
- cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS})
- if (_unityFileIsUpToDate)
- return()
- endif()
- endif()
- set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
- if (NOT _option_PRE_UNDEFS)
- set (_option_PRE_UNDEFS "")
- endif()
- if (NOT _option_SOURCES_PRE_UNDEFS)
- set (_option_SOURCES_PRE_UNDEFS "")
- endif()
- if (NOT _option_POST_UNDEFS)
- set (_option_POST_UNDEFS "")
- endif()
- if (NOT _option_SOURCES_POST_UNDEFS)
- set (_option_SOURCES_POST_UNDEFS "")
- endif()
- set (_contents "")
- if (_option_PROLOGUE)
- list (APPEND _contents ${_option_PROLOGUE})
- endif()
- if (_option_LANGUAGE AND _sourceFiles)
- if ("${_option_LANGUAGE}" STREQUAL "CXX")
- list (APPEND _contents "#ifdef __cplusplus")
- elseif ("${_option_LANGUAGE}" STREQUAL "C")
- list (APPEND _contents "#ifndef __cplusplus")
- endif()
- endif()
- set (_compileUndefinitions "")
- foreach (_sourceFile ${_sourceFiles})
- cotire_get_source_compile_definitions(
- "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions
- ${_option_SOURCES_COMPILE_DEFINITIONS})
- cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS})
- cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS})
- if (_option_PRE_UNDEFS)
- list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS})
- endif()
- if (_sourcePreUndefs)
- list (APPEND _compileUndefinitions ${_sourcePreUndefs})
- endif()
- if (_compileUndefinitions)
- cotire_append_undefs(_contents ${_compileUndefinitions})
- set (_compileUndefinitions "")
- endif()
- if (_sourcePostUndefs)
- list (APPEND _compileUndefinitions ${_sourcePostUndefs})
- endif()
- if (_option_POST_UNDEFS)
- list (APPEND _compileUndefinitions ${_option_POST_UNDEFS})
- endif()
- foreach (_definition ${_compileDefinitions})
- if ("${_definition}" MATCHES "^([a-zA-Z0-9_]+)=(.+)$")
- list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}")
- list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}")
- else()
- list (APPEND _contents "#define ${_definition}")
- list (INSERT _compileUndefinitions 0 "${_definition}")
- endif()
- endforeach()
- get_filename_component(_sourceFile "${_sourceFile}" ABSOLUTE)
- if (WIN32)
- file (TO_NATIVE_PATH "${_sourceFile}" _sourceFile)
- endif()
- list (APPEND _contents "#include \"${_sourceFile}\"")
- endforeach()
- if (_compileUndefinitions)
- cotire_append_undefs(_contents ${_compileUndefinitions})
- set (_compileUndefinitions "")
- endif()
- if (_option_LANGUAGE AND _sourceFiles)
- list (APPEND _contents "#endif")
- endif()
- if (_option_EPILOGUE)
- list (APPEND _contents ${_option_EPILOGUE})
- endif()
- list (APPEND _contents "")
- string (REPLACE ";" "\n" _contents "${_contents}")
- if (COTIRE_VERBOSE)
- message ("${_contents}")
- endif()
- cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE)
-endfunction()
-
-function (cotire_generate_prefix_header _prefixFile)
- set(_options "")
- set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION)
- set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS
- INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS)
- cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
- if (_option_DEPENDS)
- cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS})
- if (_prefixFileIsUpToDate)
- return()
- endif()
- endif()
- set (_epilogue "")
- if (_option_COMPILER_ID MATCHES "Intel")
- # Intel compiler requires hdrstop pragma to stop generating PCH file
- set (_epilogue "#pragma hdrstop")
- endif()
- set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
- cotire_scan_includes(_selectedHeaders ${_sourceFiles}
- LANGUAGE "${_option_LANGUAGE}"
- COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}"
- COMPILER_ID "${_option_COMPILER_ID}"
- COMPILER_VERSION "${_option_COMPILER_VERSION}"
- COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS}
- COMPILE_FLAGS ${_option_COMPILE_FLAGS}
- INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES}
- IGNORE_PATH ${_option_IGNORE_PATH}
- INCLUDE_PATH ${_option_INCLUDE_PATH}
- IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS}
- UNPARSED_LINES _unparsedLines)
- cotire_generate_unity_source("${_prefixFile}" EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders})
- set (_unparsedLinesFile "${_prefixFile}.log")
- if (_unparsedLines)
- if (COTIRE_VERBOSE OR NOT _selectedHeaders)
- list (LENGTH _unparsedLines _skippedLineCount)
- file (RELATIVE_PATH _unparsedLinesFileRelPath "${CMAKE_BINARY_DIR}" "${_unparsedLinesFile}")
- message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFileRelPath}")
- endif()
- string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}")
- endif()
- file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}\n")
-endfunction()
-
-function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar)
- set (_flags ${${_flagsVar}})
- if (_compilerID MATCHES "MSVC")
- # cl.exe options used
- # /nologo suppresses display of sign-on banner
- # /TC treat all files named on the command line as C source files
- # /TP treat all files named on the command line as C++ source files
- # /EP preprocess to stdout without #line directives
- # /showIncludes list include files
- set (_sourceFileTypeC "/TC")
- set (_sourceFileTypeCXX "/TP")
- if (_flags)
- # append to list
- list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes)
- else()
- # return as a flag string
- set (_flags "${_sourceFileType${_language}} /EP /showIncludes")
- endif()
- elseif (_compilerID MATCHES "GNU")
- # GCC options used
- # -H print the name of each header file used
- # -E invoke preprocessor
- # -fdirectives-only do not expand macros, requires GCC >= 4.3
- if (_flags)
- # append to list
- list (APPEND _flags -H -E)
- if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0")
- list (APPEND _flags "-fdirectives-only")
- endif()
- else()
- # return as a flag string
- set (_flags "-H -E")
- if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0")
- set (_flags "${_flags} -fdirectives-only")
- endif()
- endif()
- elseif (_compilerID MATCHES "Clang")
- # Clang options used
- # -H print the name of each header file used
- # -E invoke preprocessor
- if (_flags)
- # append to list
- list (APPEND _flags -H -E)
- else()
- # return as a flag string
- set (_flags "-H -E")
- endif()
- elseif (_compilerID MATCHES "Intel")
- if (WIN32)
- # Windows Intel options used
- # /nologo do not display compiler version information
- # /QH display the include file order
- # /EP preprocess to stdout, omitting #line directives
- # /TC process all source or unrecognized file types as C source files
- # /TP process all source or unrecognized file types as C++ source files
- set (_sourceFileTypeC "/TC")
- set (_sourceFileTypeCXX "/TP")
- if (_flags)
- # append to list
- list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH)
- else()
- # return as a flag string
- set (_flags "${_sourceFileType${_language}} /EP /QH")
- endif()
- else()
- # Linux / Mac OS X Intel options used
- # -H print the name of each header file used
- # -EP preprocess to stdout, omitting #line directives
- # -Kc++ process all source or unrecognized file types as C++ source files
- if (_flags)
- # append to list
- if ("${_language}" STREQUAL "CXX")
- list (APPEND _flags -Kc++)
- endif()
- list (APPEND _flags -H -EP)
- else()
- # return as a flag string
- if ("${_language}" STREQUAL "CXX")
- set (_flags "-Kc++ ")
- endif()
- set (_flags "${_flags}-H -EP")
- endif()
- endif()
- else()
- message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
- endif()
- set (${_flagsVar} ${_flags} PARENT_SCOPE)
-endfunction()
-
-function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar)
- set (_flags ${${_flagsVar}})
- if (_compilerID MATCHES "MSVC")
- file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
- file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
- file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative)
- # cl.exe options used
- # /Yc creates a precompiled header file
- # /Fp specifies precompiled header binary file name
- # /FI forces inclusion of file
- # /TC treat all files named on the command line as C source files
- # /TP treat all files named on the command line as C++ source files
- # /Zs syntax check only
- set (_sourceFileTypeC "/TC")
- set (_sourceFileTypeCXX "/TP")
- if (_flags)
- # append to list
- list (APPEND _flags /nologo "${_sourceFileType${_language}}"
- "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}")
- else()
- # return as a flag string
- set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
- endif()
- elseif (_compilerID MATCHES "GNU|Clang")
- # GCC / Clang options used
- # -x specify the source language
- # -c compile but do not link
- # -o place output in file
- set (_xLanguage_C "c-header")
- set (_xLanguage_CXX "c++-header")
- if (_flags)
- # append to list
- list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}")
- else()
- # return as a flag string
- set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"")
- endif()
- elseif (_compilerID MATCHES "Intel")
- if (WIN32)
- file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
- file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
- file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative)
- # Windows Intel options used
- # /nologo do not display compiler version information
- # /Yc create a precompiled header (PCH) file
- # /Fp specify a path or file name for precompiled header files
- # /FI tells the preprocessor to include a specified file name as the header file
- # /TC process all source or unrecognized file types as C source files
- # /TP process all source or unrecognized file types as C++ source files
- # /Zs syntax check only
- # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
- set (_sourceFileTypeC "/TC")
- set (_sourceFileTypeCXX "/TP")
- if (_flags)
- # append to list
- list (APPEND _flags /nologo "${_sourceFileType${_language}}"
- "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- list (APPEND _flags "/Wpch-messages")
- endif()
- else()
- # return as a flag string
- set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- set (_flags "${_flags} /Wpch-messages")
- endif()
- endif()
- else()
- # Linux / Mac OS X Intel options used
- # -pch-dir location for precompiled header files
- # -pch-create name of the precompiled header (PCH) to create
- # -Kc++ process all source or unrecognized file types as C++ source files
- # -fsyntax-only check only for correct syntax
- # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
- get_filename_component(_pchDir "${_pchFile}" PATH)
- get_filename_component(_pchName "${_pchFile}" NAME)
- set (_xLanguage_C "c-header")
- set (_xLanguage_CXX "c++-header")
- if (_flags)
- # append to list
- if ("${_language}" STREQUAL "CXX")
- list (APPEND _flags -Kc++)
- endif()
- list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-create" "${_pchName}" "-fsyntax-only" "${_hostFile}")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- list (APPEND _flags "-Wpch-messages")
- endif()
- else()
- # return as a flag string
- set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- set (_flags "${_flags} -Wpch-messages")
- endif()
- endif()
- endif()
- else()
- message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
- endif()
- set (${_flagsVar} ${_flags} PARENT_SCOPE)
-endfunction()
-
-function (cotire_add_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar)
- set (_flags ${${_flagsVar}})
- if (_compilerID MATCHES "MSVC")
- file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
- file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
- # cl.exe options used
- # /Yu uses a precompiled header file during build
- # /Fp specifies precompiled header binary file name
- # /FI forces inclusion of file
- if (_flags)
- # append to list
- list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}")
- else()
- # return as a flag string
- set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
- endif()
- elseif (_compilerID MATCHES "GNU")
- # GCC options used
- # -include process include file as the first line of the primary source file
- # -Winvalid-pch warns if precompiled header is found but cannot be used
- if (_flags)
- # append to list
- list (APPEND _flags "-include" "${_prefixFile}" "-Winvalid-pch")
- else()
- # return as a flag string
- set (_flags "-include \"${_prefixFile}\" -Winvalid-pch")
- endif()
- elseif (_compilerID MATCHES "Clang")
- # Clang options used
- # -include process include file as the first line of the primary source file
- # -Qunused-arguments don't emit warning for unused driver arguments
- if (_flags)
- # append to list
- list (APPEND _flags "-include" "${_prefixFile}" "-Qunused-arguments")
- else()
- # return as a flag string
- set (_flags "-include \"${_prefixFile}\" -Qunused-arguments")
- endif()
- elseif (_compilerID MATCHES "Intel")
- if (WIN32)
- file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
- file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
- # Windows Intel options used
- # /Yu use a precompiled header (PCH) file
- # /Fp specify a path or file name for precompiled header files
- # /FI tells the preprocessor to include a specified file name as the header file
- # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
- if (_flags)
- # append to list
- list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- list (APPEND _flags "/Wpch-messages")
- endif()
- else()
- # return as a flag string
- set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- set (_flags "${_flags} /Wpch-messages")
- endif()
- endif()
- else()
- # Linux / Mac OS X Intel options used
- # -pch-dir location for precompiled header files
- # -pch-use name of the precompiled header (PCH) to use
- # -include process include file as the first line of the primary source file
- # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
- get_filename_component(_pchDir "${_pchFile}" PATH)
- get_filename_component(_pchName "${_pchFile}" NAME)
- if (_flags)
- # append to list
- list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-use" "${_pchName}")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- list (APPEND _flags "-Wpch-messages")
- endif()
- else()
- # return as a flag string
- set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"")
- if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
- set (_flags "${_flags} -Wpch-messages")
- endif()
- endif()
- endif()
- else()
- message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
- endif()
- set (${_flagsVar} ${_flags} PARENT_SCOPE)
-endfunction()
-
-function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile)
- set(_options "")
- set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION LANGUAGE)
- set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES)
- cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
- if (NOT _option_LANGUAGE)
- set (_option_LANGUAGE "CXX")
- endif()
- if (NOT _option_COMPILER_ID)
- set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
- endif()
- cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}")
- cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS})
- cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS})
- cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES})
- cotire_add_pch_compilation_flags(
- "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}"
- "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd)
- if (COTIRE_VERBOSE)
- message (STATUS "execute_process: ${_cmd}")
- endif()
- if (_option_COMPILER_ID MATCHES "MSVC")
- if (COTIRE_DEBUG)
- message (STATUS "clearing VS_UNICODE_OUTPUT")
- endif()
- # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
- unset (ENV{VS_UNICODE_OUTPUT})
- endif()
- execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _result)
- if (_result)
- message (FATAL_ERROR "Error ${_result} precompiling ${_prefixFile}.")
- endif()
-endfunction()
-
-function (cotire_check_precompiled_header_support _language _targetSourceDir _target _msgVar)
- set (_unsupportedCompiler
- "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}")
- if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC")
- # supported since Visual Studio C++ 6.0
- # and CMake does not support an earlier version
- set (${_msgVar} "" PARENT_SCOPE)
- elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU")
- # GCC PCH support requires version >= 3.4
- cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
- if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND
- "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0")
- set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE)
- else()
- set (${_msgVar} "" PARENT_SCOPE)
- endif()
- elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang")
- # all Clang versions have PCH support
- set (${_msgVar} "" PARENT_SCOPE)
- elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel")
- # Intel PCH support requires version >= 8.0.0
- cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
- if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND
- "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0")
- set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE)
- else()
- set (${_msgVar} "" PARENT_SCOPE)
- endif()
- else()
- set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE)
- endif()
- if (APPLE)
- # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64)
- if (CMAKE_CONFIGURATION_TYPES)
- set (_configs ${CMAKE_CONFIGURATION_TYPES})
- elseif (CMAKE_BUILD_TYPE)
- set (_configs ${CMAKE_BUILD_TYPE})
- else()
- set (_configs "None")
- endif()
- foreach (_config ${_configs})
- set (_targetFlags "")
- cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags)
- cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags})
- list (LENGTH _architectures _numberOfArchitectures)
- if (_numberOfArchitectures GREATER 1)
- string (REPLACE ";" ", " _architectureStr "${_architectures}")
- set (${_msgVar}
- "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})."
- PARENT_SCOPE)
- break()
- endif()
- endforeach()
- endif()
-endfunction()
-
-macro (cotire_get_intermediate_dir _cotireDir)
- get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE)
-endmacro()
-
-macro (cotire_setup_file_extension_variables)
- set (_unityFileExt_C ".c")
- set (_unityFileExt_CXX ".cxx")
- set (_prefixFileExt_C ".h")
- set (_prefixFileExt_CXX ".hxx")
-endmacro()
-
-function (cotire_make_single_unity_source_file_path _language _target _unityFileVar)
- cotire_setup_file_extension_variables()
- if (NOT DEFINED _unityFileExt_${_language})
- set (${_unityFileVar} "" PARENT_SCOPE)
- return()
- endif()
- set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
- set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}")
- cotire_get_intermediate_dir(_baseDir)
- set (_unityFile "${_baseDir}/${_unityFileName}")
- set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE)
- if (COTIRE_DEBUG)
- message(STATUS "${_unityFile}")
- endif()
-endfunction()
-
-function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar)
- cotire_setup_file_extension_variables()
- if (NOT DEFINED _unityFileExt_${_language})
- set (${_unityFileVar} "" PARENT_SCOPE)
- return()
- endif()
- set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
- cotire_get_intermediate_dir(_baseDir)
- set (_startIndex 0)
- set (_index 0)
- set (_unityFiles "")
- set (_sourceFiles ${ARGN})
- foreach (_sourceFile ${_sourceFiles})
- get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE)
- math (EXPR _unityFileCount "${_index} - ${_startIndex}")
- if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes))
- if (_index GREATER 0)
- # start new unity file segment
- math (EXPR _endIndex "${_index} - 1")
- set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}")
- list (APPEND _unityFiles "${_baseDir}/${_unityFileName}")
- endif()
- set (_startIndex ${_index})
- endif()
- math (EXPR _index "${_index} + 1")
- endforeach()
- list (LENGTH _sourceFiles _numberOfSources)
- if (_startIndex EQUAL 0)
- # there is only a single unity file
- cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles)
- elseif (_startIndex LESS _numberOfSources)
- # end with final unity file segment
- math (EXPR _endIndex "${_index} - 1")
- set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}")
- list (APPEND _unityFiles "${_baseDir}/${_unityFileName}")
- endif()
- set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE)
- if (COTIRE_DEBUG)
- message(STATUS "${_unityFiles}")
- endif()
-endfunction()
-
-function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar)
- cotire_setup_file_extension_variables()
- if (NOT DEFINED _unityFileExt_${_language})
- set (${_prefixFileVar} "" PARENT_SCOPE)
- return()
- endif()
- set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
- set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
- string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}")
- string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}")
- set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE)
-endfunction()
-
-function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar)
- cotire_setup_file_extension_variables()
- if (NOT _language)
- set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
- set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}")
- elseif (DEFINED _prefixFileExt_${_language})
- set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
- set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}")
- else()
- set (_prefixFileBaseName "")
- set (_prefixFileName "")
- endif()
- set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE)
- set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE)
-endfunction()
-
-function (cotire_make_prefix_file_path _language _target _prefixFileVar)
- cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName)
- set (${_prefixFileVar} "" PARENT_SCOPE)
- if (_prefixFileName)
- if (NOT _language)
- set (_language "C")
- endif()
- if (MSVC OR CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel")
- cotire_get_intermediate_dir(_baseDir)
- set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE)
- endif()
- endif()
-endfunction()
-
-function (cotire_make_pch_file_path _language _targetSourceDir _target _pchFileVar)
- cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName)
- set (${_pchFileVar} "" PARENT_SCOPE)
- if (_prefixFileBaseName AND _prefixFileName)
- cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _msg)
- if (NOT _msg)
- if (XCODE)
- # For Xcode, we completely hand off the compilation of the prefix header to the IDE
- return()
- endif()
- cotire_get_intermediate_dir(_baseDir)
- if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC")
- # MSVC uses the extension .pch added to the prefix header base name
- set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE)
- elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang")
- # GCC / Clang look for a precompiled header corresponding to the prefix header with the extension .gch appended
- set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE)
- elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel")
- # Intel uses the extension .pchi added to the prefix header base name
- set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE)
- endif()
- endif()
- endif()
-endfunction()
-
-function (cotire_select_unity_source_files _unityFile _sourcesVar)
- set (_sourceFiles ${ARGN})
- if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)")
- set (_startIndex ${CMAKE_MATCH_1})
- set (_endIndex ${CMAKE_MATCH_2})
- list (LENGTH _sourceFiles _numberOfSources)
- if (NOT _startIndex LESS _numberOfSources)
- math (EXPR _startIndex "${_numberOfSources} - 1")
- endif()
- if (NOT _endIndex LESS _numberOfSources)
- math (EXPR _endIndex "${_numberOfSources} - 1")
- endif()
- set (_files "")
- foreach (_index RANGE ${_startIndex} ${_endIndex})
- list (GET _sourceFiles ${_index} _file)
- list (APPEND _files "${_file}")
- endforeach()
- else()
- set (_files ${_sourceFiles})
- endif()
- set (${_sourcesVar} ${_files} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar)
- set (_dependencySources "")
- # depend on target's generated source files
- cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${ARGN})
- if (_generatedSources)
- # but omit all generated source files that have the COTIRE_EXCLUDED property set to true
- cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources})
- if (_excludedGeneratedSources)
- list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources})
- endif()
- # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly
- cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources})
- if (_excludedNonDependencySources)
- list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources})
- endif()
- if (_generatedSources)
- list (APPEND _dependencySources ${_generatedSources})
- endif()
- endif()
- if (COTIRE_DEBUG AND _dependencySources)
- message (STATUS "${_language} ${_target} unity source depends on ${_dependencySources}")
- endif()
- set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE)
-endfunction()
-
-function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar)
- # depend on target source files marked with custom COTIRE_DEPENDENCY property
- set (_dependencySources "")
- cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${ARGN})
- if (COTIRE_DEBUG AND _dependencySources)
- message (STATUS "${_language} ${_target} prefix header DEPENDS ${_dependencySources}")
- endif()
- set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE)
-endfunction()
-
-function (cotire_generate_target_script _language _configurations _targetSourceDir _targetBinaryDir _target _targetScriptVar)
- set (COTIRE_TARGET_SOURCES ${ARGN})
- get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME)
- set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}")
- cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${COTIRE_TARGET_SOURCES})
- cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${COTIRE_TARGET_SOURCES})
- # set up variables to be configured
- set (COTIRE_TARGET_LANGUAGE "${_language}")
- cotire_determine_compiler_version("${COTIRE_TARGET_LANGUAGE}" COTIRE_${_language}_COMPILER)
- get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH)
- cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH)
- get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH)
- cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH)
- get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS)
- get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS)
- get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES)
- cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${COTIRE_TARGET_SOURCES})
- cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${COTIRE_TARGET_SOURCES})
- set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}")
- foreach (_config ${_configurations})
- string (TOUPPER "${_config}" _upperConfig)
- cotire_get_target_include_directories(
- "${_config}" "${_language}" "${_targetSourceDir}" "${_targetBinaryDir}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig})
- cotire_get_target_compile_definitions(
- "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig})
- cotire_get_target_compiler_flags(
- "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig})
- cotire_get_source_files_compile_definitions(
- "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${COTIRE_TARGET_SOURCES})
- endforeach()
- get_cmake_property(_vars VARIABLES)
- string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}")
- # remove COTIRE_VERBOSE which is passed as a CMake define on command line
- list (REMOVE_ITEM _matchVars COTIRE_VERBOSE)
- set (_contents "")
- foreach (_var IN LISTS _matchVars ITEMS
- MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES
- CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1
- CMAKE_${_language}_SOURCE_FILE_EXTENSIONS)
- if (DEFINED ${_var})
- string (REPLACE "\"" "\\\"" _value "${${_var}}")
- set (_contents "${_contents}set (${_var} \"${_value}\")\n")
- endif()
- endforeach()
- cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE)
- set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE)
-endfunction()
-
-function (cotire_setup_pch_file_compilation _language _targetBinaryDir _targetScript _prefixFile _pchFile)
- set (_sourceFiles ${ARGN})
- if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- # for Visual Studio and Intel, we attach the precompiled header compilation to the first source file
- # the remaining files include the precompiled header, see cotire_setup_prefix_file_inclusion
- if (_sourceFiles)
- file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
- file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
- list (GET _sourceFiles 0 _hostFile)
- set (_flags "")
- cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
- cotire_add_pch_compilation_flags(
- "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
- "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags)
- set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
- set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}")
- # make first source file depend on prefix header
- set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}")
- endif()
- elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
- # for makefile based generator, we add a custom command to precompile the prefix header
- if (_targetScript)
- cotire_set_cmd_to_prologue(_cmds)
- list (GET _sourceFiles 0 _hostFile)
- list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}")
- file (RELATIVE_PATH _pchFileRelPath "${CMAKE_BINARY_DIR}" "${_pchFile}")
- if (COTIRE_DEBUG)
- message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} IMPLICIT_DEPENDS ${_language} ${_prefixFile}")
- endif()
- set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE)
- add_custom_command(OUTPUT "${_pchFile}"
- COMMAND ${_cmds}
- DEPENDS "${_prefixFile}"
- IMPLICIT_DEPENDS ${_language} "${_prefixFile}"
- WORKING_DIRECTORY "${_targetSourceDir}"
- COMMENT "Building ${_language} precompiled header ${_pchFileRelPath}" VERBATIM)
- endif()
- endif()
-endfunction()
-
-function (cotire_setup_prefix_file_inclusion _language _target _wholeTarget _prefixFile _pchFile)
- set (_sourceFiles ${ARGN})
- if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- # for Visual Studio and Intel, we include the precompiled header in all but the first source file
- # the first source file does the precompiled header compilation, see cotire_setup_pch_file_compilation
- list (LENGTH _sourceFiles _numberOfSourceFiles)
- if (_numberOfSourceFiles GREATER 1)
- # mark sources as cotired to prevent them from being used in another cotired target
- set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
- list (REMOVE_AT _sourceFiles 0)
- set (_flags "")
- cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
- cotire_add_pch_inclusion_flags(
- "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
- "${_prefixFile}" "${_pchFile}" _flags)
- set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
- # make source files depend on precompiled header
- set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}")
- endif()
- elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
- if (NOT _wholeTarget)
- # for makefile based generator, we force the inclusion of the prefix header for a subset
- # of the source files, if this is a multi-language target or has excluded files
- set (_flags "")
- cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
- cotire_add_pch_inclusion_flags(
- "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
- "${_prefixFile}" "${_pchFile}" _flags)
- set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
- # mark sources as cotired to prevent them from being used in another cotired target
- set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
- endif()
- # make source files depend on precompiled header
- set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}")
- endif()
-endfunction()
-
-function (cotire_get_first_set_property_value _propertyValueVar _type _object)
- set (_properties ${ARGN})
- foreach (_property ${_properties})
- get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
- if (_propertyValue)
- set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE)
- return()
- endif()
- endforeach()
- set (${_propertyValueVar} "" PARENT_SCOPE)
-endfunction()
-
-function (cotire_setup_combine_command _language _sourceDir _targetScript _joinedFile _cmdsVar)
- set (_files ${ARGN})
- set (_filesPaths "")
- foreach (_file ${_files})
- if (IS_ABSOLUTE "${_file}")
- set (_filePath "${_file}")
- else()
- get_filename_component(_filePath "${_sourceDir}/${_file}" ABSOLUTE)
- endif()
- file (RELATIVE_PATH _fileRelPath "${_sourceDir}" "${_filePath}")
- if (NOT IS_ABSOLUTE "${_fileRelPath}" AND NOT "${_fileRelPath}" MATCHES "^\\.\\.")
- list (APPEND _filesPaths "${_fileRelPath}")
- else()
- list (APPEND _filesPaths "${_filePath}")
- endif()
- endforeach()
- cotire_set_cmd_to_prologue(_prefixCmd)
- list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine")
- if (_targetScript)
- list (APPEND _prefixCmd "${_targetScript}")
- endif()
- list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths})
- if (COTIRE_DEBUG)
- message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}")
- endif()
- set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE)
- file (RELATIVE_PATH _joinedFileRelPath "${CMAKE_BINARY_DIR}" "${_joinedFile}")
- get_filename_component(_joinedFileName "${_joinedFileRelPath}" NAME_WE)
- if (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$")
- set (_comment "Generating ${_language} unity source ${_joinedFileRelPath}")
- elseif (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$")
- set (_comment "Generating ${_language} prefix header ${_joinedFileRelPath}")
- else()
- set (_comment "Generating ${_joinedFileRelPath}")
- endif()
- add_custom_command(
- OUTPUT "${_joinedFile}"
- COMMAND ${_prefixCmd}
- DEPENDS ${_files}
- COMMENT "${_comment}"
- WORKING_DIRECTORY "${_sourceDir}" VERBATIM)
- list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd})
- set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
-endfunction()
-
-function (cotire_setup_target_pch_usage _languages _targetSourceDir _target _wholeTarget)
- if (XCODE)
- # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers
- # if necessary, we also generate a single prefix header which includes all language specific prefix headers
- set (_prefixFiles "")
- foreach (_language ${_languages})
- get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
- if (_prefixFile)
- list (APPEND _prefixFiles "${_prefixFile}")
- endif()
- endforeach()
- set (_cmds ${ARGN})
- list (LENGTH _prefixFiles _numberOfPrefixFiles)
- if (_numberOfPrefixFiles GREATER 1)
- cotire_make_prefix_file_path("" ${_target} _prefixHeader)
- cotire_setup_combine_command("" "${_targetSourceDir}" "" "${_prefixHeader}" _cmds ${_prefixFiles})
- else()
- set (_prefixHeader "${_prefixFiles}")
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}")
- endif()
- add_custom_command(TARGET "${_target}"
- PRE_BUILD ${_cmds}
- WORKING_DIRECTORY "${_targetSourceDir}"
- COMMENT "Updating target ${_target} prefix headers" VERBATIM)
- # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++
- set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES")
- set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}")
- elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
- # for makefile based generator, we force inclusion of the prefix header for all target source files
- # if this is a single-language target without any excluded files
- if (_wholeTarget)
- set (_language "${_languages}")
- # for Visual Studio and Intel, precompiled header inclusion is always done on the source file level
- # see cotire_setup_prefix_file_inclusion
- if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
- get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER)
- set (_flags "")
- cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
- cotire_add_pch_inclusion_flags(
- "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
- "${_prefixFile}" "${_pchFile}" _flags)
- set_property (TARGET ${_target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
- endif()
- endif()
- endif()
-endfunction()
-
-function (cotire_setup_unity_generation_commands _language _targetSourceDir _target _targetScript _unityFiles _cmdsVar)
- set (_dependencySources "")
- cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN})
- foreach (_unityFile ${_unityFiles})
- file (RELATIVE_PATH _unityFileRelPath "${CMAKE_BINARY_DIR}" "${_unityFile}")
- set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE)
- # set up compiled unity source dependencies
- # this ensures that missing source files are generated before the unity file is compiled
- if (COTIRE_DEBUG AND _dependencySources)
- message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}")
- endif()
- if (_dependencySources)
- set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_dependencySources})
- endif()
- if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC and Windows Intel
- set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj")
- endif()
- cotire_set_cmd_to_prologue(_unityCmd)
- list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetScript}" "${_unityFile}")
- if (COTIRE_DEBUG)
- message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_targetScript}")
- endif()
- add_custom_command(
- OUTPUT "${_unityFile}"
- COMMAND ${_unityCmd}
- DEPENDS "${_targetScript}"
- COMMENT "Generating ${_language} unity source ${_unityFileRelPath}"
- WORKING_DIRECTORY "${_targetSourceDir}" VERBATIM)
- list (APPEND ${_cmdsVar} COMMAND ${_unityCmd})
- endforeach()
- list (LENGTH _unityFiles _numberOfUnityFiles)
- if (_numberOfUnityFiles GREATER 1)
- # create a joint unity file from all unity file segments
- cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile)
- cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_unityFile}" ${_cmdsVar} ${_unityFiles})
- endif()
- set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
-endfunction()
-
-function (cotire_setup_single_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFile _cmdsVar)
- set (_sourceFiles ${ARGN})
- set (_dependencySources "")
- cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles})
- cotire_set_cmd_to_prologue(_prefixCmd)
- list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" "${_unityFile}")
- set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE)
- if (COTIRE_DEBUG)
- message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_targetScript} ${_unityFile} ${_dependencySources}")
- endif()
- file (RELATIVE_PATH _prefixFileRelPath "${CMAKE_BINARY_DIR}" "${_prefixFile}")
- add_custom_command(
- OUTPUT "${_prefixFile}" "${_prefixFile}.log"
- COMMAND ${_prefixCmd}
- DEPENDS "${_targetScript}" "${_unityFile}" ${_dependencySources}
- COMMENT "Generating ${_language} prefix header ${_prefixFileRelPath}"
- WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM)
- list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd})
- set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
-endfunction()
-
-function (cotire_setup_multi_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFiles _cmdsVar)
- set (_sourceFiles ${ARGN})
- list (LENGTH _unityFiles _numberOfUnityFiles)
- if (_numberOfUnityFiles GREATER 1)
- cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile)
- cotire_setup_single_prefix_generation_command(
- ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}"
- "${_prefixFile}" "${_unityFile}" ${_cmdsVar} ${_sourceFiles})
- else()
- cotire_setup_single_prefix_generation_command(
- ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}"
- "${_prefixFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles})
- endif()
- set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
-endfunction()
-
-function (cotire_init_cotire_target_properties _target)
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE)
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE)
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE)
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}")
- cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}")
- if (NOT _isRelative)
- set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}")
- endif()
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "")
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "")
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET)
- if (NOT _isSet)
- set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "")
- endif()
- get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET)
- if (NOT _isSet)
- if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES)
- set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}")
- else()
- set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "")
- endif()
- endif()
-endfunction()
-
-function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar)
- get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
- get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
- string (REPLACE ";" " " _languagesStr "${_languages}")
- string (REPLACE ";" ", " _excludedStr "${ARGN}")
- set (_targetMsg "")
- if (NOT _languages)
- set (_targetMsg "Target ${_target} cannot be cotired.")
- if (_disableMsg)
- set (_targetMsg "${_targetMsg} ${_disableMsg}")
- endif()
- elseif (NOT _targetUsePCH AND NOT _targetAddSCU)
- set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.")
- if (_disableMsg)
- set (_targetMsg "${_targetMsg} ${_disableMsg}")
- endif()
- elseif (NOT _targetUsePCH)
- if (_allExcludedSourceFiles)
- set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without precompiled header.")
- else()
- set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.")
- endif()
- if (_disableMsg)
- set (_targetMsg "${_targetMsg} ${_disableMsg}")
- endif()
- elseif (NOT _targetAddSCU)
- if (_allExcludedSourceFiles)
- set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without unity build.")
- else()
- set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.")
- endif()
- else()
- if (_allExcludedSourceFiles)
- set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr}.")
- else()
- set (_targetMsg "${_languagesStr} target ${_target} cotired.")
- endif()
- endif()
- set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE)
-endfunction()
-
-function (cotire_choose_target_languages _targetSourceDir _target _targetLanguagesVar)
- set (_languages ${ARGN})
- set (_allSourceFiles "")
- set (_allExcludedSourceFiles "")
- set (_allCotiredSourceFiles "")
- set (_targetLanguages "")
- get_target_property(_targetType ${_target} TYPE)
- get_target_property(_targetSourceFiles ${_target} SOURCES)
- get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
- get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
- set (_disableMsg "")
- foreach (_language ${_languages})
- get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER)
- get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE)
- if (_prefixHeader OR _unityBuildFile)
- message (WARNING "Target ${_target} has already been cotired.")
- set (${_targetLanguagesVar} "" PARENT_SCOPE)
- return()
- endif()
- if (_targetUsePCH AND "${_language}" STREQUAL "C" OR "${_language}" STREQUAL "CXX")
- cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _disableMsg)
- if (_disableMsg)
- set (_targetUsePCH FALSE)
- endif()
- endif()
- set (_sourceFiles "")
- set (_excludedSources "")
- set (_cotiredSources "")
- cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
- if (_sourceFiles OR _excludedSources OR _cotiredSources)
- list (APPEND _targetLanguages ${_language})
- endif()
- if (_sourceFiles)
- list (APPEND _allSourceFiles ${_sourceFiles})
- endif()
- if (_excludedSources)
- list (APPEND _allExcludedSourceFiles ${_excludedSources})
- endif()
- if (_cotiredSources)
- list (APPEND _allCotiredSourceFiles ${_cotiredSources})
- endif()
- endforeach()
- set (_targetMsgLevel STATUS)
- if (NOT _targetLanguages)
- string (REPLACE ";" " or " _languagesStr "${_languages}")
- set (_disableMsg "No ${_languagesStr} source files.")
- set (_targetUsePCH FALSE)
- set (_targetAddSCU FALSE)
- endif()
- if (_targetUsePCH)
- list (LENGTH _allSourceFiles _numberOfSources)
- if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
- set (_disableMsg "Too few applicable sources.")
- set (_targetUsePCH FALSE)
- elseif (_allCotiredSourceFiles)
- cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles})
- list (REMOVE_DUPLICATES _cotireTargets)
- string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}")
- set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.")
- set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},")
- set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.")
- set (_targetMsgLevel SEND_ERROR)
- set (_targetUsePCH FALSE)
- elseif (XCODE AND _allExcludedSourceFiles)
- # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target
- set (_disableMsg "Exclusion of source files not supported for generator Xcode.")
- set (_targetUsePCH FALSE)
- elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY")
- # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target
- set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.")
- set (_targetUsePCH FALSE)
- endif()
- endif()
- set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH})
- set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU})
- cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles})
- if (_targetMsg)
- if (NOT DEFINED COTIREMSG_${_target})
- set (COTIREMSG_${_target} "")
- endif()
- if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR
- NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}")
- # cache message to avoid redundant messages on re-configure
- set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.")
- message (${_targetMsgLevel} "${_targetMsg}")
- endif()
- endif()
- set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE)
-endfunction()
-
-function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar)
- set (_sourceFiles ${ARGN})
- get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES)
- if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)")
- set (_numberOfThreads "${CMAKE_MATCH_2}")
- if (NOT _numberOfThreads)
- # use all available cores
- ProcessorCount(_numberOfThreads)
- endif()
- list (LENGTH _sourceFiles _numberOfSources)
- math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}")
- # a unity source segment must not contain less than COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES files
- if (_maxIncludes LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
- set (_maxIncludes ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
- endif()
- elseif (NOT _maxIncludes MATCHES "[0-9]+")
- set (_maxIncludes 0)
- endif()
- if (COTIRE_DEBUG)
- message (STATUS "${_target} unity source max includes = ${_maxIncludes}")
- endif()
- set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE)
-endfunction()
-
-function (cotire_process_target_language _language _configurations _targetSourceDir _targetBinaryDir _target _wholeTargetVar _cmdsVar)
- set (${_cmdsVar} "" PARENT_SCOPE)
- get_target_property(_targetSourceFiles ${_target} SOURCES)
- set (_sourceFiles "")
- set (_excludedSources "")
- set (_cotiredSources "")
- cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
- if (NOT _sourceFiles AND NOT _cotiredSources)
- return()
- endif()
- set (_wholeTarget ${${_wholeTargetVar}})
- set (_cmds "")
- # check for user provided unity source file list
- get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT)
- if (NOT _unitySourceFiles)
- set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources})
- endif()
- cotire_generate_target_script(
- ${_language} "${_configurations}" "${_targetSourceDir}" "${_targetBinaryDir}" ${_target} _targetScript ${_unitySourceFiles})
- cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles})
- cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles})
- if (NOT _unityFiles)
- return()
- endif()
- cotire_setup_unity_generation_commands(
- ${_language} "${_targetSourceDir}" ${_target} "${_targetScript}" "${_unityFiles}" _cmds ${_unitySourceFiles})
- cotire_make_prefix_file_path(${_language} ${_target} _prefixFile)
- if (_prefixFile)
- # check for user provided prefix header files
- get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT)
- if (_prefixHeaderFiles)
- cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles})
- else()
- cotire_setup_multi_prefix_generation_command(
- ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFiles}" _cmds ${_unitySourceFiles})
- endif()
- get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
- if (_targetUsePCH)
- cotire_make_pch_file_path(${_language} "${_targetSourceDir}" ${_target} _pchFile)
- if (_pchFile)
- cotire_setup_pch_file_compilation(
- ${_language} "${_targetBinaryDir}" "${_targetScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles})
- if (_excludedSources)
- set (_wholeTarget FALSE)
- endif()
- cotire_setup_prefix_file_inclusion(
- ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles})
- endif()
- endif()
- endif()
- # mark target as cotired for language
- set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}")
- if (_prefixFile)
- set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}")
- if (_targetUsePCH AND _pchFile)
- set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}")
- endif()
- endif()
- set (${_wholeTargetVar} ${_wholeTarget} PARENT_SCOPE)
- set (${_cmdsVar} ${_cmds} PARENT_SCOPE)
-endfunction()
-
-function (cotire_setup_clean_target _target)
- set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}")
- if (NOT TARGET "${_cleanTargetName}")
- cotire_set_cmd_to_prologue(_cmds)
- get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE)
- list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}")
- add_custom_target(${_cleanTargetName} COMMAND ${_cmds} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
- COMMENT "Cleaning up target ${_target} cotire generated files" VERBATIM)
- cotire_init_target("${_cleanTargetName}")
- endif()
-endfunction()
-
-function (cotire_setup_pch_target _languages _configurations _target)
- if ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
- # for makefile based generators, we add a custom target to trigger the generation of the cotire related files
- set (_dependsFiles "")
- foreach (_language ${_languages})
- set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE)
- if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
- # Visual Studio and Intel only create precompiled header as a side effect
- list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER)
- endif()
- cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props})
- if (_dependsFile)
- list (APPEND _dependsFiles "${_dependsFile}")
- endif()
- endforeach()
- if (_dependsFiles)
- set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}")
- add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles})
- cotire_init_target("${_pchTargetName}")
- cotire_add_to_pch_all_target(${_pchTargetName})
- endif()
- else()
- # for other generators, we add the "clean all" target to clean up the precompiled header
- cotire_setup_clean_all_target()
- endif()
-endfunction()
-
-function (cotire_setup_unity_build_target _languages _configurations _targetSourceDir _target)
- get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME)
- if (NOT _unityTargetName)
- set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}")
- endif()
- # determine unity target sub type
- get_target_property(_targetType ${_target} TYPE)
- if ("${_targetType}" STREQUAL "EXECUTABLE")
- get_target_property(_isWin32 ${_target} WIN32_EXECUTABLE)
- get_target_property(_isMacOSX_Bundle ${_target} MACOSX_BUNDLE)
- if (_isWin32)
- set (_unityTargetSubType WIN32)
- elseif (_isMacOSX_Bundle)
- set (_unityTargetSubType MACOSX_BUNDLE)
- else()
- set (_unityTargetSubType "")
- endif()
- elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY")
- set (_unityTargetSubType "${CMAKE_MATCH_1}")
- else()
- message (WARNING "Unknown target type ${_targetType}.")
- return()
- endif()
- # determine unity target sources
- get_target_property(_targetSourceFiles ${_target} SOURCES)
- set (_unityTargetSources ${_targetSourceFiles})
- foreach (_language ${_languages})
- get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE)
- if (_unityFiles)
- # remove source files that are included in the unity source
- set (_sourceFiles "")
- set (_excludedSources "")
- set (_cotiredSources "")
- cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
- if (_sourceFiles OR _cotiredSources)
- list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources})
- endif()
- # if cotire is applied to a target which has not been added in the current source dir,
- # non-existing files cannot be referenced from the unity build target (this is a CMake restriction)
- if (NOT "${_targetSourceDir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
- set (_nonExistingFiles "")
- foreach (_file ${_unityTargetSources})
- if (NOT EXISTS "${_file}")
- list (APPEND _nonExistingFiles "${_file}")
- endif()
- endforeach()
- if (_nonExistingFiles)
- if (COTIRE_VERBOSE)
- message (STATUS "removing non-existing ${_nonExistingFiles} from ${_unityTargetName}")
- endif()
- list (REMOVE_ITEM _unityTargetSources ${_nonExistingFiles})
- endif()
- endif()
- # add unity source files instead
- list (APPEND _unityTargetSources ${_unityFiles})
- endif()
- endforeach()
- if (COTIRE_DEBUG)
- message (STATUS "add ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}")
- endif()
- # generate unity target
- if ("${_targetType}" STREQUAL "EXECUTABLE")
- add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources})
- else()
- add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources})
- endif()
- set (_outputDirProperties
- ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
- LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
- RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_<CONFIG>)
- # copy output location properties
- if (COTIRE_UNITY_OUTPUT_DIRECTORY)
- set (_setDefaultOutputDir TRUE)
- if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}")
- set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}")
- else()
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties})
- cotrie_resolve_config_properites("${_configurations}" _properties ${_outputDirProperties})
- foreach (_property ${_properties})
- get_property(_outputDir TARGET ${_target} PROPERTY ${_property})
- if (_outputDir)
- get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE)
- set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}")
- set (_setDefaultOutputDir FALSE)
- endif()
- endforeach()
- if (_setDefaultOutputDir)
- get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE)
- endif()
- endif()
- if (_setDefaultOutputDir)
- set_target_properties(${_unityTargetName} PROPERTIES
- ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}"
- LIBRARY_OUTPUT_DIRECTORY "${_outputDir}"
- RUNTIME_OUTPUT_DIRECTORY "${_outputDir}")
- endif()
- else()
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties})
- endif()
- # copy output name
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_<CONFIG>
- LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_<CONFIG>
- OUTPUT_NAME OUTPUT_NAME_<CONFIG>
- RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_<CONFIG>
- PREFIX <CONFIG>_POSTFIX SUFFIX)
- # copy compile stuff
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- COMPILE_DEFINITIONS COMPILE_DEFINITIONS_<CONFIG>
- COMPILE_FLAGS Fortran_FORMAT
- INCLUDE_DIRECTORIES
- INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
- POSITION_INDEPENDENT_CODE)
- # copy link stuff
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH
- LINKER_LANGUAGE LINK_DEPENDS
- LINK_FLAGS LINK_FLAGS_<CONFIG>
- LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_<CONFIG>
- LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_<CONFIG>
- LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC
- STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_<CONFIG>
- NO_SONAME SOVERSION VERSION)
- # copy Qt stuff
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- AUTOMOC AUTOMOC_MOC_OPTIONS)
- # copy cmake stuff
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK)
- # copy platform stuff
- if (APPLE)
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- BUNDLE BUNDLE_EXTENSION FRAMEWORK INSTALL_NAME_DIR MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST
- OSX_ARCHITECTURES OSX_ARCHITECTURES_<CONFIG> PRIVATE_HEADER PUBLIC_HEADER RESOURCE)
- elseif (WIN32)
- cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
- GNUtoMS
- PDB_NAME PDB_NAME_<CONFIG> PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_<CONFIG>
- VS_DOTNET_REFERENCES VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_KEYWORD
- VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER
- VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES)
- endif()
- # use output name from original target
- get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME)
- if (NOT _targetOutputName)
- set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}")
- endif()
- # use export symbol from original target
- cotire_get_target_export_symbol("${_target}" _defineSymbol)
- if (_defineSymbol)
- set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}")
- if ("${_targetType}" STREQUAL "EXECUTABLE")
- set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE)
- endif()
- endif()
- cotire_init_target(${_unityTargetName})
- cotire_add_to_unity_all_target(${_unityTargetName})
- set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}")
-endfunction(cotire_setup_unity_build_target)
-
-function (cotire_target _target)
- set(_options "")
- set(_oneValueArgs SOURCE_DIR BINARY_DIR)
- set(_multiValueArgs LANGUAGES CONFIGURATIONS)
- cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
- if (NOT _option_SOURCE_DIR)
- set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
- endif()
- if (NOT _option_BINARY_DIR)
- set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
- endif()
- if (NOT _option_LANGUAGES)
- get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
- endif()
- if (NOT _option_CONFIGURATIONS)
- if (CMAKE_CONFIGURATION_TYPES)
- set (_option_CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES})
- elseif (CMAKE_BUILD_TYPE)
- set (_option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}")
- else()
- set (_option_CONFIGURATIONS "None")
- endif()
- endif()
- # trivial checks
- get_target_property(_imported ${_target} IMPORTED)
- if (_imported)
- message (WARNING "Imported target ${_target} cannot be cotired.")
- return()
- endif()
- # check if target needs to be cotired for build type
- # when using configuration types, the test is performed at build time
- cotire_init_cotire_target_properties(${_target})
- if (NOT CMAKE_CONFIGURATION_TYPES)
- if (CMAKE_BUILD_TYPE)
- list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index)
- else()
- list (FIND _option_CONFIGURATIONS "None" _index)
- endif()
- if (_index EQUAL -1)
- if (COTIRE_DEBUG)
- message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})")
- endif()
- return()
- endif()
- endif()
- # choose languages that apply to the target
- cotire_choose_target_languages("${_option_SOURCE_DIR}" "${_target}" _targetLanguages ${_option_LANGUAGES})
- if (NOT _targetLanguages)
- return()
- endif()
- list (LENGTH _targetLanguages _numberOfLanguages)
- if (_numberOfLanguages GREATER 1)
- set (_wholeTarget FALSE)
- else()
- set (_wholeTarget TRUE)
- endif()
- set (_cmds "")
- foreach (_language ${_targetLanguages})
- cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}"
- "${_option_SOURCE_DIR}" "${_option_BINARY_DIR}" ${_target} _wholeTarget _cmd)
- if (_cmd)
- list (APPEND _cmds ${_cmd})
- endif()
- endforeach()
- get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
- if (_targetAddSCU)
- cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" "${_option_SOURCE_DIR}" ${_target})
- endif()
- get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
- if (_targetUsePCH)
- cotire_setup_target_pch_usage("${_targetLanguages}" "${_option_SOURCE_DIR}" ${_target} ${_wholeTarget} ${_cmds})
- cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target})
- endif()
- get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN)
- if (_targetAddCleanTarget)
- cotire_setup_clean_target(${_target})
- endif()
-endfunction()
-
-function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName)
- if (_targetName)
- file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*")
- else()
- file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*")
- endif()
- # filter files in intermediate directory
- set (_filesToRemove "")
- foreach (_file ${_cotireFiles})
- get_filename_component(_dir "${_file}" PATH)
- get_filename_component(_dirName "${_dir}" NAME)
- if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}")
- list (APPEND _filesToRemove "${_file}")
- endif()
- endforeach()
- if (_filesToRemove)
- if (COTIRE_VERBOSE)
- message (STATUS "removing ${_filesToRemove}")
- endif()
- file (REMOVE ${_filesToRemove})
- endif()
-endfunction()
-
-function (cotire_init_target _targetName)
- if (COTIRE_TARGETS_FOLDER)
- set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}")
- endif()
- if (MSVC_IDE)
- set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE)
- endif()
-endfunction()
-
-function (cotire_add_to_pch_all_target _pchTargetName)
- set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}")
- if (NOT TARGET "${_targetName}")
- add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM)
- cotire_init_target("${_targetName}")
- endif()
- cotire_setup_clean_all_target()
- add_dependencies(${_targetName} ${_pchTargetName})
-endfunction()
-
-function (cotire_add_to_unity_all_target _unityTargetName)
- set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}")
- if (NOT TARGET "${_targetName}")
- add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM)
- cotire_init_target("${_targetName}")
- endif()
- cotire_setup_clean_all_target()
- add_dependencies(${_targetName} ${_unityTargetName})
-endfunction()
-
-function (cotire_setup_clean_all_target)
- set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}")
- if (NOT TARGET "${_targetName}")
- cotire_set_cmd_to_prologue(_cmds)
- list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}")
- add_custom_target(${_targetName} COMMAND ${_cmds}
- WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Cleaning up all cotire generated files" VERBATIM)
- cotire_init_target("${_targetName}")
- endif()
-endfunction()
-
-function (cotire)
- set(_options "")
- set(_oneValueArgs SOURCE_DIR BINARY_DIR)
- set(_multiValueArgs LANGUAGES CONFIGURATIONS)
- cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
- set (_targets ${_option_UNPARSED_ARGUMENTS})
- if (NOT _option_SOURCE_DIR)
- set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
- endif()
- if (NOT _option_BINARY_DIR)
- set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
- endif()
- foreach (_target ${_targets})
- if (TARGET ${_target})
- cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS}
- SOURCE_DIR "${_option_SOURCE_DIR}" BINARY_DIR "${_option_BINARY_DIR}")
- else()
- message (WARNING "${_target} is not a target")
- endif()
- endforeach()
-endfunction()
-
-if (CMAKE_SCRIPT_MODE_FILE)
-
- # cotire is being run in script mode
- # locate -P on command args
- set (COTIRE_ARGC -1)
- foreach (_index RANGE ${CMAKE_ARGC})
- if (COTIRE_ARGC GREATER -1)
- set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}")
- math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1")
- elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P")
- set (COTIRE_ARGC 0)
- endif()
- endforeach()
-
- # include target script if available
- if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$")
- # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES)
- include("${COTIRE_ARGV2}")
- endif()
-
- if (COTIRE_DEBUG)
- message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}")
- endif()
-
- if (WIN32)
- # for MSVC, compiler IDs may not always be set correctly
- if (MSVC)
- set (CMAKE_C_COMPILER_ID "MSVC")
- set (CMAKE_CXX_COMPILER_ID "MSVC")
- endif()
- endif()
-
- if (NOT COTIRE_BUILD_TYPE)
- set (COTIRE_BUILD_TYPE "None")
- endif()
- string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig)
- set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}})
- set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}})
- set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}})
- # check if target has been cotired for actual build type COTIRE_BUILD_TYPE
- list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index)
- if (_index GREATER -1)
- set (_sources ${COTIRE_TARGET_SOURCES})
- set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}})
- else()
- if (COTIRE_DEBUG)
- message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})")
- endif()
- set (_sources "")
- set (_sourcesDefinitions "")
- endif()
- set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS})
- set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS})
- set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS})
- set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS})
-
- if ("${COTIRE_ARGV1}" STREQUAL "unity")
-
- cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources})
- cotire_generate_unity_source(
- "${COTIRE_ARGV3}" ${_sources}
- LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
- DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV2}"
- SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions}
- PRE_UNDEFS ${_targetPreUndefs}
- POST_UNDEFS ${_targetPostUndefs}
- SOURCES_PRE_UNDEFS ${_sourcesPreUndefs}
- SOURCES_POST_UNDEFS ${_sourcesPostUndefs})
-
- elseif ("${COTIRE_ARGV1}" STREQUAL "prefix")
-
- set (_files "")
- foreach (_index RANGE 4 ${COTIRE_ARGC})
- if (COTIRE_ARGV${_index})
- list (APPEND _files "${COTIRE_ARGV${_index}}")
- endif()
- endforeach()
-
- cotire_generate_prefix_header(
- "${COTIRE_ARGV3}" ${_files}
- COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}"
- COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1}
- COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}"
- COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}"
- LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
- DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS}
- IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}"
- INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH}
- IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}"
- INCLUDE_DIRECTORIES ${_includeDirs}
- COMPILE_DEFINITIONS ${_compileDefinitions}
- COMPILE_FLAGS ${_compileFlags})
-
- elseif ("${COTIRE_ARGV1}" STREQUAL "precompile")
-
- set (_files "")
- foreach (_index RANGE 5 ${COTIRE_ARGC})
- if (COTIRE_ARGV${_index})
- list (APPEND _files "${COTIRE_ARGV${_index}}")
- endif()
- endforeach()
-
- cotire_precompile_prefix_header(
- "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}"
- COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}"
- COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1}
- COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}"
- COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}"
- LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
- INCLUDE_DIRECTORIES ${_includeDirs}
- COMPILE_DEFINITIONS ${_compileDefinitions}
- COMPILE_FLAGS ${_compileFlags})
-
- elseif ("${COTIRE_ARGV1}" STREQUAL "combine")
-
- if (COTIRE_TARGET_LANGUAGE)
- set (_startIndex 3)
- else()
- set (_startIndex 2)
- endif()
- set (_files "")
- foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC})
- if (COTIRE_ARGV${_index})
- list (APPEND _files "${COTIRE_ARGV${_index}}")
- endif()
- endforeach()
- if (COTIRE_TARGET_LANGUAGE)
- cotire_generate_unity_source(${_files} LANGUAGE "${COTIRE_TARGET_LANGUAGE}")
- else()
- cotire_generate_unity_source(${_files})
- endif()
-
- elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup")
-
- cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}")
-
- else()
- message (FATAL_ERROR "Unknown cotire command \"${COTIRE_ARGV1}\".")
- endif()
-
-else()
-
- # cotire is being run in include mode
- # set up all variable and property definitions
-
- unset (COTIRE_C_COMPILER_VERSION CACHE)
- unset (COTIRE_CXX_COMPILER_VERSION CACHE)
-
- if (NOT DEFINED COTIRE_DEBUG_INIT)
- if (DEFINED COTIRE_DEBUG)
- set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG})
- else()
- set (COTIRE_DEBUG_INIT FALSE)
- endif()
- endif()
- option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT})
-
- if (NOT DEFINED COTIRE_VERBOSE_INIT)
- if (DEFINED COTIRE_VERBOSE)
- set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE})
- else()
- set (COTIRE_VERBOSE_INIT FALSE)
- endif()
- endif()
- option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT})
-
- set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING
- "Ignore headers with the listed file extensions from the generated prefix header.")
-
- set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING
- "Ignore headers from these directories when generating the prefix header.")
-
- set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING
- "Ignore sources with the listed file extensions from the generated unity source.")
-
- set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING
- "Minimum number of sources in target required to enable use of precompiled header.")
-
- if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT)
- if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES)
- set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES})
- elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio")
- # enable parallelization for generators that run multiple jobs by default
- set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j")
- else()
- set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0")
- endif()
- endif()
- set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING
- "Maximum number of source files to include in a single unity source file.")
-
- if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX)
- set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix")
- endif()
- if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX)
- set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity")
- endif()
- if (NOT COTIRE_INTDIR)
- set (COTIRE_INTDIR "cotire")
- endif()
- if (NOT COTIRE_PCH_ALL_TARGET_NAME)
- set (COTIRE_PCH_ALL_TARGET_NAME "all_pch")
- endif()
- if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME)
- set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity")
- endif()
- if (NOT COTIRE_CLEAN_ALL_TARGET_NAME)
- set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire")
- endif()
- if (NOT COTIRE_CLEAN_TARGET_SUFFIX)
- set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire")
- endif()
- if (NOT COTIRE_PCH_TARGET_SUFFIX)
- set (COTIRE_PCH_TARGET_SUFFIX "_pch")
- endif()
- if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX)
- set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity")
- endif()
- if (NOT DEFINED COTIRE_TARGETS_FOLDER)
- set (COTIRE_TARGETS_FOLDER "cotire")
- endif()
- if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY)
- if ("${CMAKE_GENERATOR}" MATCHES "Ninja")
- # generated Ninja build files do not work if the unity target produces the same output file as the cotired target
- set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity")
- else()
- set (COTIRE_UNITY_OUTPUT_DIRECTORY "")
- endif()
- endif()
-
- # define cotire cache variables
-
- define_property(
- CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH"
- BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
- FULL_DOCS
- "The variable can be set to a semicolon separated list of include directories."
- "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header."
- "If not defined, defaults to empty list."
- )
-
- define_property(
- CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS"
- BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header."
- FULL_DOCS
- "The variable can be set to a semicolon separated list of file extensions."
- "If a header file extension matches one in the list, it will be excluded from the generated prefix header."
- "Includes with an extension in CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS are always ignored."
- "If not defined, defaults to inc;inl;ipp."
- )
-
- define_property(
- CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS"
- BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source."
- FULL_DOCS
- "The variable can be set to a semicolon separated list of file extensions."
- "If a source file extension matches one in the list, it will be excluded from the generated unity source file."
- "Source files with an extension in CMAKE_<LANG>_IGNORE_EXTENSIONS are always excluded."
- "If not defined, defaults to m;mm."
- )
-
- define_property(
- CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES"
- BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header."
- FULL_DOCS
- "The variable can be set to an integer > 0."
- "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target."
- "If not defined, defaults to 3."
- )
-
- define_property(
- CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES"
- BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
- FULL_DOCS
- "This may be set to an integer >= 0."
- "If 0, cotire will only create a single unity source file."
- "If a target contains more than that number of source files, cotire will create multiple unity source files for it."
- "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores."
- "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs."
- "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES."
- "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise."
- )
-
- # define cotire directory properties
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER"
- BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header."
- FULL_DOCS
- "See target property COTIRE_ENABLE_PRECOMPILED_HEADER."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD"
- BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory."
- FULL_DOCS
- "See target property COTIRE_ADD_UNITY_BUILD."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_ADD_CLEAN"
- BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory."
- FULL_DOCS
- "See target property COTIRE_ADD_CLEAN."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH"
- BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
- FULL_DOCS
- "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH"
- BRIEF_DOCS "Honor headers from these directories when generating the prefix header."
- FULL_DOCS
- "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS"
- BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file."
- FULL_DOCS
- "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS"
- BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file."
- FULL_DOCS
- "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS."
- )
-
- define_property(
- DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES"
- BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
- FULL_DOCS
- "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES."
- )
-
- # define cotire target properties
-
- define_property(
- TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED
- BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header."
- FULL_DOCS
- "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header."
- "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target."
- "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header."
- "The target name will be set to this target's name with the suffix _pch appended."
- "Inherited from directory."
- "Defaults to TRUE."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED
- BRIEF_DOCS "Add a new target that performs a unity build for this target."
- FULL_DOCS
- "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources."
- "Most of the relevant target properties will be copied from this target to the new unity build target."
- "Target dependencies and linked libraries have to be manually set up for the new unity build target."
- "The unity target name will be set to this target's name with the suffix _unity appended."
- "Inherited from directory."
- "Defaults to TRUE."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED
- BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target."
- FULL_DOCS
- "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)."
- "The clean target name will be set to this target's name with the suffix _clean_cotire appended."
- "Inherited from directory."
- "Defaults to FALSE."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED
- BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
- FULL_DOCS
- "The property can be set to a list of directories."
- "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header."
- "Inherited from directory."
- "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED
- BRIEF_DOCS "Honor headers from these directories when generating the prefix header."
- FULL_DOCS
- "The property can be set to a list of directories."
- "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header."
- "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH,"
- "the option which yields the closer relative path match wins."
- "Inherited from directory."
- "If not set, this property is initialized to the empty list."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED
- BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file."
- FULL_DOCS
- "This may be set to a semicolon-separated list of preprocessor symbols."
- "cotire will add corresponding #undef directives to the generated unit source file before each target source file."
- "Inherited from directory."
- "Defaults to empty string."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED
- BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file."
- FULL_DOCS
- "This may be set to a semicolon-separated list of preprocessor symbols."
- "cotire will add corresponding #undef directives to the generated unit source file after each target source file."
- "Inherited from directory."
- "Defaults to empty string."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED
- BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
- FULL_DOCS
- "This may be set to an integer > 0."
- "If a target contains more than that number of source files, cotire will create multiple unity build files for it."
- "If not set, cotire will only create a single unity source file."
- "Inherited from directory."
- "Defaults to empty."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE_INIT"
- BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one."
- FULL_DOCS
- "If set, cotire will only add the given file(s) to the generated unity source file."
- "If not set, cotire will add all the target source files to the generated unity source file."
- "The property can be set to a user provided unity source file."
- "Defaults to empty."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER_INIT"
- BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one."
- FULL_DOCS
- "If set, cotire will add the given header file(s) to the generated prefix header file."
- "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file."
- "The property can be set to a user provided prefix header file (e.g., stdafx.h)."
- "Defaults to empty."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE"
- BRIEF_DOCS "Read-only property. The generated <LANG> unity source file(s)."
- FULL_DOCS
- "cotire sets this property to the path of the generated <LANG> single computation unit source file for the target."
- "Defaults to empty string."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER"
- BRIEF_DOCS "Read-only property. The generated <LANG> prefix header file."
- FULL_DOCS
- "cotire sets this property to the full path of the generated <LANG> language prefix header for the target."
- "Defaults to empty string."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_<LANG>_PRECOMPILED_HEADER"
- BRIEF_DOCS "Read-only property. The generated <LANG> precompiled header file."
- FULL_DOCS
- "cotire sets this property to the full path of the generated <LANG> language precompiled header binary for the target."
- "Defaults to empty string."
- )
-
- define_property(
- TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME"
- BRIEF_DOCS "The name of the generated unity build target corresponding to this target."
- FULL_DOCS
- "This property can be set to the desired name of the unity target that will be created by cotire."
- "If not set, the unity target name will be set to this target's name with the suffix _unity appended."
- "After this target has been processed by cotire, the property is set to the actual name of the generated unity target."
- "Defaults to empty string."
- )
-
- # define cotire source properties
-
- define_property(
- SOURCE PROPERTY "COTIRE_EXCLUDED"
- BRIEF_DOCS "Do not modify source file's build command."
- FULL_DOCS
- "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header."
- "The source file will also be excluded from the generated unity source file."
- "Source files that have their COMPILE_FLAGS property set will be excluded by default."
- "Defaults to FALSE."
- )
-
- define_property(
- SOURCE PROPERTY "COTIRE_DEPENDENCY"
- BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file."
- FULL_DOCS
- "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file."
- "If the file is modified, cotire will re-generate the prefix header source upon build."
- "Defaults to FALSE."
- )
-
- define_property(
- SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS"
- BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file."
- FULL_DOCS
- "This may be set to a semicolon-separated list of preprocessor symbols."
- "cotire will add corresponding #undef directives to the generated unit source file before this file is included."
- "Defaults to empty string."
- )
-
- define_property(
- SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS"
- BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file."
- FULL_DOCS
- "This may be set to a semicolon-separated list of preprocessor symbols."
- "cotire will add corresponding #undef directives to the generated unit source file after this file is included."
- "Defaults to empty string."
- )
-
- define_property(
- SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE"
- BRIEF_DOCS "Start a new unity source file which includes this source file as the first one."
- FULL_DOCS
- "If this property is set to TRUE, cotire will complete the current unity file and start a new one."
- "The new unity source file will include this source file as the first one."
- "This property essentially works as a separator for unity source files."
- "Defaults to FALSE."
- )
-
- define_property(
- SOURCE PROPERTY "COTIRE_TARGET"
- BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target."
- FULL_DOCS
- "cotire sets this property to the name of target, that the source file's build command has been altered for."
- "Defaults to empty string."
- )
-
- message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.")
-
-endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 50dde49..e21ace6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -20,13 +20,12 @@
cmake_minimum_required(VERSION 2.8.6)
-include(FindPkgConfig)
include(GNUInstallDirs)
-set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake")
-
project(pulseview)
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake")
+
#===============================================================================
#= User Options
#-------------------------------------------------------------------------------
@@ -34,9 +33,9 @@ project(pulseview)
option(DISABLE_WERROR "Build without -Werror" FALSE)
option(ENABLE_SIGNALS "Build with UNIX signals" TRUE)
option(ENABLE_DECODE "Build with libsigrokdecode" TRUE)
-option(ENABLE_COTIRE "Enable cotire" FALSE)
-option(ENABLE_TESTS "Enable unit tests" FALSE)
+option(ENABLE_TESTS "Enable unit tests" TRUE)
option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE)
+option(FORCE_QT4 "Force use of Qt4 even if Qt5 is available" FALSE)
if(WIN32)
# On Windows/MinGW we need to statically link to libraries.
@@ -47,44 +46,67 @@ if(WIN32)
set(Boost_USE_STATIC_LIBS ON)
add_definitions(-DBOOST_THREAD_USE_LIB)
+ # On Windows/MinGW we need to use 'thread_win32' instead of 'thread'.
+ # The library is named libboost_thread_win32* (not libboost_thread*).
+ set(Boost_THREADAPI win32)
+
# Windows does not support UNIX signals.
set(ENABLE_SIGNALS FALSE)
endif()
if(NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
- "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
- FORCE)
+ set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
+ "Choose the type of build (None, Debug, Release, RelWithDebInfo, MinSizeRel)."
+ FORCE)
endif()
#===============================================================================
#= Dependencies
#-------------------------------------------------------------------------------
-list(APPEND PKGDEPS libsigrok>=0.3.0)
+list(APPEND PKGDEPS libsigrokcxx>=0.4.0)
if(ENABLE_DECODE)
- list(APPEND PKGDEPS libsigrokdecode>=0.3.0)
+ list(APPEND PKGDEPS libsigrokdecode>=0.4.0)
+endif()
+
+if(ANDROID)
+ list(APPEND PKGDEPS libsigrokandroidutils>=0.1.0)
endif()
find_package(PkgConfig)
pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS})
-find_program(QT_QMAKE_EXECUTABLE NAMES qmake4 qmake-qt4 qmake-mac)
-find_package(Qt4 REQUIRED)
+if(FORCE_QT4)
+ set(Qt5Core_FOUND FALSE)
+else()
+ find_package(Qt5Core QUIET)
+endif()
-# Find the platform's thread library (needed for boost-thread).
-# This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value.
-find_package(Threads)
+if(Qt5Core_FOUND)
+ message("-- Using Qt5")
+ find_package(Qt5Widgets REQUIRED)
+ find_package(Qt5Gui REQUIRED)
+ find_package(Qt5Svg REQUIRED)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
+ set(QT_INCLUDE_DIRS ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS})
+ set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg)
+ add_definitions(${Qt5Gui_DEFINITIONS} ${Qt5Widgets_DEFINITIONS})
+else()
+ find_program(QT_QMAKE_EXECUTABLE NAMES qmake4 qmake-qt4 qmake-mac)
+ find_package(Qt4 REQUIRED QtCore QtGui QtSvg)
+endif()
-if(WIN32)
- # On Windows/MinGW we need to use 'thread_win32' instead of 'thread'.
- # The library is named libboost_thread_win32* (not libboost_thread*).
- find_package(Boost 1.42 COMPONENTS filesystem system thread_win32 REQUIRED)
+if(ENABLE_TESTS)
+ find_package(Boost 1.53 COMPONENTS filesystem system thread unit_test_framework REQUIRED)
else()
- find_package(Boost 1.42 COMPONENTS filesystem system thread REQUIRED)
+ find_package(Boost 1.53 COMPONENTS filesystem system thread REQUIRED)
endif()
+# Find the platform's thread library (needed for C++11 threads).
+# This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value.
+find_package(Threads REQUIRED)
+
#===============================================================================
#= System Introspection
#-------------------------------------------------------------------------------
@@ -98,13 +120,28 @@ memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS)
set(PV_TITLE PulseView)
set(PV_DESCRIPTION "A GUI for sigrok")
+set(PV_VERSION_STRING "0.3.0")
+
+include(GetGitRevisionDescription)
+
+# Append the revision hash unless we are exactly on a tagged release.
+git_describe(PV_TAG_VERSION_STRING --match "pulseview-${PV_VERSION_STRING}" --exact-match)
+if(NOT PV_TAG_VERSION_STRING)
+ get_git_head_revision(PV_REVSPEC PV_HASH)
+ if(PV_HASH)
+ string(SUBSTRING "${PV_HASH}" 0 7 PV_SHORTHASH)
+ set(PV_VERSION_STRING "${PV_VERSION_STRING}-git-${PV_SHORTHASH}")
+ endif()
+endif()
-set(PV_VERSION_MAJOR 0)
-set(PV_VERSION_MINOR 2)
-set(PV_VERSION_MICRO 0)
-set(PV_VERSION_STRING
- ${PV_VERSION_MAJOR}.${PV_VERSION_MINOR}.${PV_VERSION_MICRO}
-)
+if(PV_VERSION_STRING MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[-0-9a-z]*)?$")
+ set(PV_VERSION_MAJOR ${CMAKE_MATCH_1})
+ set(PV_VERSION_MINOR ${CMAKE_MATCH_2})
+ set(PV_VERSION_MICRO ${CMAKE_MATCH_3})
+ set(PV_VERSION_SUFFIX ${CMAKE_MATCH_4})
+endif()
+
+message("-- ${PV_TITLE} version: ${PV_VERSION_STRING}")
configure_file (
${PROJECT_SOURCE_DIR}/config.h.in
@@ -117,92 +154,126 @@ configure_file (
set(pulseview_SOURCES
main.cpp
+ pv/application.cpp
pv/devicemanager.cpp
pv/mainwindow.cpp
- pv/sigsession.cpp
+ pv/session.cpp
pv/storesession.cpp
+ pv/util.cpp
+ pv/binding/binding.cpp
+ pv/binding/inputoutput.cpp
+ pv/binding/device.cpp
pv/data/analog.cpp
- pv/data/analogsnapshot.cpp
+ pv/data/analogsegment.cpp
pv/data/logic.cpp
- pv/data/logicsnapshot.cpp
+ pv/data/logicsegment.cpp
pv/data/signaldata.cpp
- pv/data/snapshot.cpp
- pv/device/device.cpp
- pv/device/file.cpp
- pv/device/devinst.cpp
- pv/device/inputfile.cpp
- pv/device/sessionfile.cpp
+ pv/data/segment.cpp
+ pv/devices/device.cpp
+ pv/devices/file.cpp
+ pv/devices/hardwaredevice.cpp
+ pv/devices/inputfile.cpp
+ pv/devices/sessionfile.cpp
pv/dialogs/about.cpp
pv/dialogs/connect.cpp
+ pv/dialogs/inputoutputoptions.cpp
pv/dialogs/storeprogress.cpp
pv/popups/deviceoptions.cpp
- pv/popups/probes.cpp
+ pv/popups/channels.cpp
pv/prop/bool.cpp
pv/prop/double.cpp
pv/prop/enum.cpp
pv/prop/int.cpp
pv/prop/property.cpp
pv/prop/string.cpp
- pv/prop/binding/binding.cpp
- pv/prop/binding/deviceoptions.cpp
- pv/toolbars/samplingbar.cpp
+ pv/toolbars/mainbar.cpp
pv/view/analogsignal.cpp
pv/view/cursor.cpp
pv/view/cursorpair.cpp
+ pv/view/flag.cpp
pv/view/header.cpp
pv/view/marginwidget.cpp
pv/view/logicsignal.cpp
+ pv/view/rowitem.cpp
pv/view/ruler.cpp
- pv/view/selectableitem.cpp
pv/view/signal.cpp
+ pv/view/signalscalehandle.cpp
+ pv/view/timeitem.cpp
pv/view/timemarker.cpp
pv/view/trace.cpp
+ pv/view/tracegroup.cpp
pv/view/tracepalette.cpp
+ pv/view/tracetreeitem.cpp
+ pv/view/tracetreeitemowner.cpp
+ pv/view/triggermarker.cpp
pv/view/view.cpp
+ pv/view/viewitem.cpp
+ pv/view/viewitemowner.cpp
+ pv/view/viewitempaintparams.cpp
pv/view/viewport.cpp
+ pv/view/viewwidget.cpp
pv/widgets/colourbutton.cpp
pv/widgets/colourpopup.cpp
+ pv/widgets/devicetoolbutton.cpp
+ pv/widgets/exportmenu.cpp
+ pv/widgets/hidingmenubar.cpp
+ pv/widgets/importmenu.cpp
pv/widgets/popup.cpp
pv/widgets/popuptoolbutton.cpp
pv/widgets/sweeptimingwidget.cpp
+ pv/widgets/timestampspinbox.cpp
pv/widgets/wellarray.cpp
)
# This list includes only QObject derived class headers.
set(pulseview_HEADERS
- pv/mainwindow.h
- pv/sigsession.h
- pv/storesession.h
- pv/device/devinst.h
- pv/dialogs/about.h
- pv/dialogs/connect.h
- pv/dialogs/storeprogress.h
- pv/popups/probes.h
- pv/popups/deviceoptions.h
- pv/prop/bool.h
- pv/prop/double.h
- pv/prop/enum.h
- pv/prop/int.h
- pv/prop/property.h
- pv/prop/string.h
- pv/toolbars/samplingbar.h
- pv/view/cursor.h
- pv/view/header.h
- pv/view/logicsignal.h
- pv/view/marginwidget.h
- pv/view/ruler.h
- pv/view/selectableitem.h
- pv/view/signal.h
- pv/view/timemarker.h
- pv/view/trace.h
- pv/view/view.h
- pv/view/viewport.h
- pv/widgets/colourbutton.h
- pv/widgets/colourpopup.h
- pv/widgets/popup.h
- pv/widgets/popuptoolbutton.h
- pv/widgets/sweeptimingwidget.h
- pv/widgets/wellarray.h
+ pv/mainwindow.hpp
+ pv/session.hpp
+ pv/storesession.hpp
+ pv/binding/device.hpp
+ pv/dialogs/about.hpp
+ pv/dialogs/connect.hpp
+ pv/dialogs/inputoutputoptions.hpp
+ pv/dialogs/storeprogress.hpp
+ pv/popups/channels.hpp
+ pv/popups/deviceoptions.hpp
+ pv/prop/bool.hpp
+ pv/prop/double.hpp
+ pv/prop/enum.hpp
+ pv/prop/int.hpp
+ pv/prop/property.hpp
+ pv/prop/string.hpp
+ pv/toolbars/mainbar.hpp
+ pv/view/cursor.hpp
+ pv/view/flag.hpp
+ pv/view/header.hpp
+ pv/view/logicsignal.hpp
+ pv/view/marginwidget.hpp
+ pv/view/rowitem.hpp
+ pv/view/ruler.hpp
+ pv/view/signal.hpp
+ pv/view/signalscalehandle.hpp
+ pv/view/timeitem.hpp
+ pv/view/timemarker.hpp
+ pv/view/trace.hpp
+ pv/view/tracegroup.hpp
+ pv/view/tracetreeitem.hpp
+ pv/view/triggermarker.hpp
+ pv/view/view.hpp
+ pv/view/viewitem.hpp
+ pv/view/viewport.hpp
+ pv/view/viewwidget.hpp
+ pv/widgets/colourbutton.hpp
+ pv/widgets/colourpopup.hpp
+ pv/widgets/devicetoolbutton.hpp
+ pv/widgets/exportmenu.hpp
+ pv/widgets/hidingmenubar.hpp
+ pv/widgets/importmenu.hpp
+ pv/widgets/popup.hpp
+ pv/widgets/popuptoolbutton.hpp
+ pv/widgets/sweeptimingwidget.hpp
+ pv/widgets/timestampspinbox.hpp
+ pv/widgets/wellarray.hpp
)
set(pulseview_FORMS
@@ -215,27 +286,27 @@ set(pulseview_RESOURCES
if(ENABLE_SIGNALS)
list(APPEND pulseview_SOURCES signalhandler.cpp)
- list(APPEND pulseview_HEADERS signalhandler.h)
+ list(APPEND pulseview_HEADERS signalhandler.hpp)
endif()
if(ENABLE_DECODE)
list(APPEND pulseview_SOURCES
+ pv/binding/decoder.cpp
pv/data/decoderstack.cpp
pv/data/decode/annotation.cpp
pv/data/decode/decoder.cpp
pv/data/decode/row.cpp
pv/data/decode/rowdata.cpp
- pv/prop/binding/decoderoptions.cpp
pv/view/decodetrace.cpp
pv/widgets/decodergroupbox.cpp
pv/widgets/decodermenu.cpp
)
list(APPEND pulseview_HEADERS
- pv/data/decoderstack.h
- pv/view/decodetrace.h
- pv/widgets/decodergroupbox.h
- pv/widgets/decodermenu.h
+ pv/data/decoderstack.hpp
+ pv/view/decodetrace.hpp
+ pv/widgets/decodergroupbox.hpp
+ pv/widgets/decodermenu.hpp
)
endif()
@@ -246,19 +317,35 @@ if(WIN32)
list(APPEND pulseview_SOURCES pulseviewico.rc)
endif()
-qt4_wrap_cpp(pulseview_HEADERS_MOC ${pulseview_HEADERS})
-qt4_wrap_ui(pulseview_FORMS_HEADERS ${pulseview_FORMS})
-qt4_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+if(ANDROID)
+ list(APPEND pulseview_SOURCES
+ android/assetreader.cpp
+ android/loghandler.cpp
+ )
+endif()
-include(${QT_USE_FILE})
+if(Qt5Core_FOUND)
+ qt5_wrap_cpp(pulseview_HEADERS_MOC ${pulseview_HEADERS})
+ qt5_wrap_ui(pulseview_FORMS_HEADERS ${pulseview_FORMS})
+ qt5_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+else()
+ # Workaround for QTBUG-22829: -DBOOST_NEXT_PRIOR_HPP_INCLUDED.
+ # https://bugreports.qt.io/browse/QTBUG-22829
+ qt4_wrap_cpp(pulseview_HEADERS_MOC ${pulseview_HEADERS}
+ OPTIONS -DBOOST_NEXT_PRIOR_HPP_INCLUDED)
+ qt4_wrap_ui(pulseview_FORMS_HEADERS ${pulseview_FORMS})
+ qt4_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+ include(${QT_USE_FILE})
+endif()
#===============================================================================
#= Global Definitions
#-------------------------------------------------------------------------------
-add_definitions(${QT_DEFINITIONS})
+add_definitions(${QT_DEFINITIONS} -DQT_NO_KEYWORDS)
add_definitions(-D__STDC_LIMIT_MACROS)
add_definitions(-Wall -Wextra)
+add_definitions(-std=c++11)
if(ENABLE_DECODE)
add_definitions(-DENABLE_DECODE)
@@ -268,6 +355,10 @@ if(NOT DISABLE_WERROR)
add_definitions(-Werror)
endif()
+if(ENABLE_SIGNALS)
+ add_definitions(-DENABLE_SIGNALS)
+endif()
+
#===============================================================================
#= Global Include Directories
#-------------------------------------------------------------------------------
@@ -276,6 +367,7 @@ include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${Boost_INCLUDE_DIRS}
+ ${QT_INCLUDE_DIRS}
)
if(STATIC_PKGDEPS_LIBS)
@@ -292,8 +384,8 @@ link_directories(${Boost_LIBRARY_DIRS})
set(PULSEVIEW_LINK_LIBS
${Boost_LIBRARIES}
- ${CMAKE_THREAD_LIBS_INIT}
${QT_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT}
)
if(STATIC_PKGDEPS_LIBS)
@@ -313,16 +405,29 @@ if(WIN32)
# plugin (and the QtSvg component) for SVG graphics/icons to work.
add_definitions(-DQT_STATICPLUGIN)
link_directories("${QT_PLUGINS_DIR}/imageformats")
- list(APPEND PULSEVIEW_LINK_LIBS ${QT_QTSVG_LIBRARY})
list(APPEND PULSEVIEW_LINK_LIBS "-lqsvg")
+ list(APPEND PULSEVIEW_LINK_LIBS ${QT_QTSVG_LIBRARY})
endif()
+if(ANDROID)
+ list(APPEND PULSEVIEW_LINK_LIBS "-llog")
+endif()
+
+if(ANDROID)
+add_library(${PROJECT_NAME} SHARED
+ ${pulseview_SOURCES}
+ ${pulseview_HEADERS_MOC}
+ ${pulseview_FORMS_HEADERS}
+ ${pulseview_RESOURCES_RCC}
+)
+else()
add_executable(${PROJECT_NAME}
${pulseview_SOURCES}
${pulseview_HEADERS_MOC}
${pulseview_FORMS_HEADERS}
${pulseview_RESOURCES_RCC}
)
+endif()
target_link_libraries(${PROJECT_NAME} ${PULSEVIEW_LINK_LIBS})
@@ -331,11 +436,6 @@ if(WIN32)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-mwindows")
endif()
-if(ENABLE_COTIRE)
- include(cotire)
- cotire(${PROJECT_NAME})
-endif()
-
#===============================================================================
#= Installation
#-------------------------------------------------------------------------------
@@ -346,6 +446,9 @@ install(TARGETS ${PROJECT_NAME} DESTINATION bin/)
# Install the manpage.
install(FILES doc/pulseview.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc)
+# Generate Windows installer script.
+configure_file(contrib/pulseview_cross.nsi.in contrib/pulseview_cross.nsi @ONLY)
+
#===============================================================================
#= Packaging (handled by CPack)
#-------------------------------------------------------------------------------
@@ -356,8 +459,7 @@ set(CPACK_PACKAGE_VERSION_PATCH ${PV_VERSION_MICRO})
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/README)
set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/COPYING)
set(CPACK_SOURCE_IGNORE_FILES ${CMAKE_CURRENT_BINARY_DIR} ".gitignore" ".git")
-set(CPACK_SOURCE_PACKAGE_FILE_NAME
- "${CMAKE_PROJECT_NAME}-${PV_VERSION_MAJOR}.${PV_VERSION_MINOR}.${PV_VERSION_MICRO}")
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PV_VERSION_STRING}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..a7cb281
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,234 @@
+# Doxyfile 1.8.6
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "PulseView"
+PROJECT_NUMBER = "unreleased development snapshot"
+PROJECT_BRIEF = "A Qt-based sigrok GUI"
+PROJECT_LOGO = icons/sigrok-logo-notext.png
+OUTPUT_DIRECTORY = doxy
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF =
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 4
+ALIASES =
+MARKDOWN_SUPPORT = YES
+AUTOLINK_SUPPORT = YES
+BUILTIN_STL_SUPPORT = YES
+SUBGROUPING = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS = YES
+TYPEDEF_HIDES_STRUCT = NO
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+EXTRACT_ALL = YES
+EXTRACT_PRIVATE = YES
+EXTRACT_PACKAGE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = YES
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = YES
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+SHOW_GROUPED_MEMB_INC = NO
+FORCE_LOCAL_INCLUDES = NO
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_MEMBERS_CTORS_1ST = YES
+SORT_GROUP_NAMES = NO
+SORT_BY_SCOPE_NAME = NO
+STRICT_PROTO_MATCHING = NO
+GENERATE_TODOLIST = NO
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = NO
+GENERATE_DEPRECATEDLIST= NO
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_FILES = YES
+SHOW_NAMESPACES = YES
+FILE_VERSION_FILTER =
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+INPUT = .
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS =
+RECURSIVE = YES
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS = moc_*.cxx*
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+GENERATE_HTML = YES
+HTML_OUTPUT = html-api
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_EXTRA_STYLESHEET =
+HTML_EXTRA_FILES =
+HTML_COLORSTYLE_HUE = 220
+HTML_COLORSTYLE_SAT = 100
+HTML_COLORSTYLE_GAMMA = 80
+HTML_TIMESTAMP = YES
+HTML_DYNAMIC_SECTIONS = NO
+HTML_INDEX_NUM_ENTRIES = 100
+DISABLE_INDEX = NO
+GENERATE_TREEVIEW = YES
+ENUM_VALUES_PER_LINE = 4
+TREEVIEW_WIDTH = 250
+EXT_LINKS_IN_WINDOW = NO
+FORMULA_FONTSIZE = 10
+FORMULA_TRANSPARENT = YES
+SEARCHENGINE = YES
+SERVER_BASED_SEARCH = NO
+EXTERNAL_SEARCH = NO
+SEARCHENGINE_URL =
+SEARCHDATA_FILE = searchdata.xml
+EXTERNAL_SEARCH_ID =
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Latex output
+#---------------------------------------------------------------------------
+
+GENERATE_LATEX = NO
+
+#---------------------------------------------------------------------------
+# 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 options related to external references
+#---------------------------------------------------------------------------
+
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+EXTERNAL_PAGES = YES
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+CLASS_DIAGRAMS = YES
+MSCGEN_PATH =
+DIA_PATH =
+HIDE_UNDOC_RELATIONS = NO
+HAVE_DOT = YES
+DOT_NUM_THREADS = 0
+DOT_FONTNAME = Helvetica
+DOT_FONTSIZE = 10
+DOT_FONTPATH =
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = YES
+UML_LIMIT_NUM_FIELDS = 10
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = YES
+CALLER_GRAPH = YES
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = svg
+INTERACTIVE_SVG = YES
+DOT_PATH =
+DOTFILE_DIRS =
+MSCFILE_DIRS =
+DIAFILE_DIRS =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 0
+DOT_TRANSPARENT = YES
+DOT_MULTI_TARGETS = YES
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
diff --git a/HACKING b/HACKING
index d1edc87..17183ed 100644
--- a/HACKING
+++ b/HACKING
@@ -38,7 +38,7 @@ Random notes
glib's g_try_malloc()/g_try_malloc0() was used, use g_free() to free the
memory. Otherwise use standard free(). Never use the wrong function!
- - Never use g_malloc() or g_malloc0(). These functions do not return NULL
+ - Never use g_malloc() or g_malloc0(). These functions do not return nullptr
if not enough memory is available but rather lead to an exit() or segfault
instead. This behaviour is not acceptable.
Use g_try_malloc()/g_try_malloc0() instead and check the return value.
diff --git a/INSTALL b/INSTALL
index 4a93e08..b532339 100644
--- a/INSTALL
+++ b/INSTALL
@@ -5,21 +5,27 @@ INSTALL
Requirements
------------
- - git
- - g++
+ - git (only needed when building from git)
+ - A C++ compiler with C++11 support (-std=c++11 option), e.g.
+ - g++ (>= 4.7)
+ - clang++ (>= 3.1)
- make
- - libtool
+ - libtool (only needed when building from git)
- pkg-config >= 0.22
- cmake >= 2.8.6
- libglib >= 2.28.0
- - Qt >= 4.5
- - libboost >= 1.42 (including the following libs):
+ - glibmm-2.4 (>= 2.28.0)
+ - Qt4 >= 4.5 or Qt5 (including the following components):
+ - Qt4: QtCore, QtGui, QtSvg
+ - Qt5: Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg
+ - libboost >= 1.53 (including the following libs):
- libboost-system
- - libboost-thread
- libboost-filesystem
+ - libboost-thread
- libboost-test (optional, only needed to run the unit tests)
- - libsigrok >= 0.3.0
- - libsigrokdecode >= 0.3.0
+ - libsigrokcxx >= 0.4.0 (libsigrok C++ bindings)
+ - libsigrokdecode >= 0.4.0
+ - libsigrokandroidutils >= 0.1.0 (optional, only needed on Android)
Building and installing
diff --git a/NEWS b/NEWS
index 696f13a..5ffbfe8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,160 @@
+0.3.0 (2016-01-29)
+------------------
+
+ * PulseView now uses libsigrokcxx (the libsigrok C++ bindings library).
+ * Update to use the new APIs of libsigrokcxx 0.4.x and libsigrokdecode 0.4.x.
+ * Add support for vertical scaling of analog and logic traces.
+ * Add a "Save selection range as..." feature. This allows the user to select
+ a subset of the data (using the cursors) and save that into a file in the
+ libsigrok session format (*.sr).
+ * Remember the directory of the last file that was opened/saved.
+ * Automatically save and restore (upon PulseView shutdown/startup):
+ - the window state (size, position)
+ - the most recently used device
+ * Implement pinch-zoom support (useful e.g. on tablets).
+ * Implement an "always zoom-to-fit" feature.
+ * Implement a "sticky scrolling" feature, using hotkey 's' (bug #121).
+ * Show icons for active triggers on the right-hand side of the window.
+ * Cursors: Also show the frequency in addition to distance/time (bug #502).
+ * List available serial ports (for serial-based devices) in a drop-down.
+ * Add support for trace groups. An arbitrary number of traces can be grouped
+ together, which allows for dragging them around as a group and re-ordering
+ traces within the group. Hotkeys: group (CTRL+g), ungroup (CTRL+u).
+ * Use either alternating gray values as trace background color, or the
+ trace's own color (slightly modified). This behaviour is configurable
+ via the menu or the 'b' hotkey.
+ * Show a vertical marker at the trigger point (bug #685).
+ * Sampling bar: Show the total sampling time in a tooltip.
+ * Trace/Decoder popups: Close the popup when the ENTER/RETURN key is pressed.
+ * Improve horizontal arrow-key scrolling step size.
+ * The viewport can now be dragged vertically as well.
+ * Add support for placing arbitrary markers (double-click at the desired
+ position in the ruler area) with arbitrary name/content. Markers can be
+ removed via the delete key (or the right-click context menu).
+ * Show full device names in the device drop-down.
+ * The device selector combobox is now a split button. Clicking on a device
+ name will open the "Connect to Device" dialog which allows for manual
+ device selection. Clicking on the small arrow will open a drop-down with
+ all autodetected devices.
+ * The main menu is now hidden by default, pressing ALT will make it appear,
+ pressing ALT or ESC will hide it again.
+ * Add an Export menu item, which can export the data in various formats.
+ * Add an Import menu item, which allows data import of various formats.
+ * Support specifying input files + formats on the command-line (-i and -I).
+ * Update the possible samplerates/samplecounts widgets when needed.
+ * Various UI fixes and improvements (colors, font sizes, clipping, etc).
+ * Add support for the SR_CONF_{POWER_OFF,PROBE_FACTOR} config keys.
+ * Include the device / filename in the window title (bug #244).
+ * Keyboard shortcuts:
+ - Add space as a shortcut to start/stop an acquisition.
+ - Add shortcuts for the open/save menu items (CTRL-o, CTRL-s).
+ - Add trace group shortcuts: group (CTRL+g), ungroup (CTRL+u).
+ - Add 's' as shortcut for enabling/disabling sticky scrolling.
+ - Add 'b' as shortcut for colored / alternating-gray trace backgrounds.
+ * Add a few missing toolbar buttons.
+ * Provide tooltips for decoder annotations (which show the "full" annotation
+ text regardless of zoom-level).
+ * Improvements to work (better) with Qt5 (Qt4 remains supported as well).
+ * The whole code-base has been converted to C++11 (dropping the use
+ of various Boost functionality in favor of std:: equivalents).
+ * Build system:
+ - Always build with -std=c++11.
+ - Auto-detect Qt4 or Qt5. If both are available, Qt5 will be selected
+ unless the cmake option FORCE_QT4 is set to TRUE.
+ - Don't use Qt-defined keywords (can cause issues with other headers).
+ - Fix an issue with QtSvg linking (bug #369).
+ - Fix a build issue related to Qt4 MOC.
+ - Add "-git-<hash>" suffix to development version numbers (bug #609).
+ - Fix the build for older glibmm versions (bug #548).
+ * Updated build requirements:
+ - A C++ compiler with C++11 support (g++ >= 4.7 or clang++ >= 3.1)
+ - glibmm-2.4 (>= 2.28.0)
+ - Qt4 >= 4.5 or Qt5 (including the following components):
+ - Qt4: QtCore, QtGui, QtSvg
+ - Qt5: Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg
+ - Boost >= 1.53 (bugs #722, #593).
+ - libsigrokcxx >= 0.4.0 (libsigrok C++ bindings)
+ - libsigrokdecode >= 0.4.0
+ - libsigrokandroidutils >= 0.1.0 (optional, only needed on Android)
+ * Dropped build requirements:
+ - libsigrok (PulseView now uses libsigrokcxx instead).
+ * manpage:
+ - Various fixes and updates.
+ - Document all keyboard shortcuts.
+ * Android:
+ - Add basic Android support and support for building a PulseView APK.
+ - Install logging callbacks for Android.
+ - Fix a rendering issue by disabling the system background.
+ - Fix an issue due to a missing libintl.so in the APK (bug #575).
+ - Add asset reader functionality, e.g. for firmware files.
+ - Include firmware files (from sigrok-firmware) in the APK (bug #400).
+ * Windows:
+ - Fix an issue related to CMAKE_MODULE_PATH usage.
+ - Fix multiple Boost- and thread-related issues.
+ - Fix a build issue due to windows.h namespace pollution (bug #517).
+ - Fix an issue when saving .sr files (bug #615).
+ * Mac OS X:
+ - Fix an issue related to Glib::Variant types.
+ - Fix a build failure on Mac OS X 10.10 (bug #621).
+ * NSIS:
+ - Drop libusb0.dll, we use libusb-1.0 everywhere now.
+ - Add start menu entries for Zadig (bug #542).
+ - Support out-of-tree builds.
+ - Don't hardcode the MXE install location.
+ * README: Drop reference to obsolete sigrok-commits mailing list.
+ * Add a Doxygen file for auto-generated code documentation.
+ * Populate signal popup combo box with signal names (not probe names).
+ * Fix various thread related issues.
+ * Fix various compiler warnings and compiler portability issues.
+ * Adapt PulseView to use the new libsigrokcxx trigger API (bugs #448, #452).
+ * Reimplement file save using the "srzip" output module (bug #451).
+ * Check whether config keys are available before use (bug #487).
+ * Markers: Fix display of negative values in popups (bug #460).
+ * Properly handle device selection failure (bug #455).
+ * Fix a progressbar issue when saving files (bug #451).
+ * Fix incorrect channel names in .sr files (bug #490).
+ * Save and load signal names as UTF-8 strings (bug #498).
+ * Add a workaround for QTBUG-22829 (bug #532).
+ * Allow vertical scrolling via CTRL + mouse-wheel (bug #497).
+ * Fix an isnan() related compiler error (bug #531).
+ * Gracefully handle the case of a failing SAMPLERATE query (bug #529).
+ * Show less device info in the device dropdown (bug #285).
+ * Always show full device info in device selection tooltips (bug #489).
+ * Fix a segfault due to a missing Capability::LIST check (bug #453).
+ * Allow PulseView to be killed via CTRL-C from a terminal (bug #368).
+ * Support having no selected device (bug #488, #392).
+ * Add menu actions to the main window too (bug #590).
+ * Avoid confusing autocompletion in the channel name popups (bug #501).
+ * Fix an issue with channel ordering being reset incorrectly (bug #536).
+ * Fix an issue related to a disappearing samplerate dropdown (bug #594).
+ * Fix an issue with incorrect samplenumbers when running decoders.
+ * Suppress warnings from glibmm about deprecated auto_ptr (bug #654).
+ * Add a workaround for a corrupted timescale issue (bug #627).
+ * Fix a libzip-related issue resulting in errors writing .sr files (bug #570).
+ * Fix an issue that caused incorrect .sr files being saved (bug #599).
+ * When clicking a channel name, place the cursor in the text field (bug #298).
+ * Fix a double-free issue/segfault when trying to open an .sr file (bug #405).
+ * Fix an issue with incorrect placement of PD annotation tooltips (bug #477).
+ * Allow drag-moving both cursors (left + right) at the same time (bug #514).
+ * Improve the step-size of the vertical scroll bar click-movement (bug #513).
+ * Fix a too small display area for devices with many channels (bug #515).
+ * Fix a Qt5 QWellArray related static linking issue (bug #525).
+ * Fix broken session saving for devices with more than one channel (bug #404).
+ * Fix incorrect ruler units of kilo-/mega-/giga-seconds (bug #371).
+ * Fix an issue with segment sizes wasting huge amounts of memory (bug #622).
+ * Avoid a crash when running out of memory, show an error instead (bug #626).
+ * Improve behaviour when the last PD is removed from the stack (bug #510).
+ * Disable device options GUI elements after an acquisition start (bug #597).
+ * Fix an issue with the samplerate input format option (bug #595).
+ * Fix a segfault when switching from a file to a device and back (bug #596).
+ * Fix a segfault when loading very large .sr files (bug #592).
+ * Fix a segfault when selecting a device twice (bug #605).
+ * Fix an incorrect decode trace background color (bug #718).
+ * Fix incorrect annotation row background color behaviour (bug #719).
+ * Fix an issue with lingering decoder traces (bug #687).
+ * Fix non-intuitive channel name editing behaviour (bug #717).
+ * Speed up annotation drawing by quite a bit (bug #325).
+
0.2.0 (2014-05-06)
------------------
diff --git a/README b/README
index e4aa3c1..ccc7d05 100644
--- a/README
+++ b/README
@@ -14,9 +14,6 @@ Status
PulseView is in a usable state and has had official tarball releases.
-However, it is still work in progress. Some basic functionality
-is available and working, but other things are still on the TODO list.
-
Copyright and license
---------------------
@@ -47,13 +44,10 @@ is to be interpreted as
Copyright (C) 2010,2011,2012,2013 Contributor Name
-Mailing lists
--------------
-
-There are two mailing lists for sigrok/PulseView:
+Mailing list
+------------
https://lists.sourceforge.net/lists/listinfo/sigrok-devel
- https://lists.sourceforge.net/lists/listinfo/sigrok-commits
IRC
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
new file mode 100644
index 0000000..eacc002
--- /dev/null
+++ b/android/AndroidManifest.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ This file is part of the PulseView project.
+
+ Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.sigrok.pulseview"
+ android:versionName="0.2.0"
+ android:versionCode="20"
+ android:installLocation="auto">
+ <application android:hardwareAccelerated="true"
+ android:name="org.sigrok.pulseview.PulseViewApplication"
+ android:label="@string/pv_app_name"
+ android:icon="@drawable/logo">
+ <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation"
+ android:name="org.sigrok.pulseview.PulseViewActivity"
+ android:label="@string/pv_app_name"
+ android:screenOrientation="unspecified"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <meta-data android:name="android.app.lib_name" android:value="pulseview"/>
+ <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
+ <meta-data android:name="android.app.repository" android:value="default"/>
+ <meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
+ <meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
+ <!-- Deploy Qt libs as part of package -->
+ <meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
+ <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
+ <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
+ <!-- Run with local libs -->
+ <meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
+ <meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
+ <meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so"/>
+ <meta-data android:name="android.app.load_local_jars" android:value="jar/QtAndroid.jar:jar/QtAndroidAccessibility.jar:jar/QtAndroid-bundled.jar:jar/QtAndroidAccessibility-bundled.jar"/>
+ <meta-data android:name="android.app.static_init_classes" android:value=""/>
+ <!-- Messages maps -->
+ <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
+ <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
+ <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
+ <!-- Messages maps -->
+ <!-- Splash screen -->
+ <!--
+ <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
+ -->
+ <!-- Splash screen -->
+ </activity>
+ </application>
+ <uses-sdk android:minSdkVersion="12" android:targetSdkVersion="14"/>
+ <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+</manifest>
diff --git a/android/assetreader.cpp b/android/assetreader.cpp
new file mode 100644
index 0000000..f14e7b6
--- /dev/null
+++ b/android/assetreader.cpp
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "assetreader.hpp"
+#include <libsigrok/libsigrok.h>
+#include <memory>
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QStandardPaths>
+
+using namespace pv;
+
+AndroidAssetReader::~AndroidAssetReader()
+{}
+
+void AndroidAssetReader::open(struct sr_resource *res, std::string name)
+{
+ if (res->type == SR_RESOURCE_FIRMWARE) {
+ auto path = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
+ QString::fromStdString("sigrok-firmware/" + name));
+ if (path.isEmpty())
+ path = QString::fromStdString("assets:/sigrok-firmware/" + name);
+
+ std::unique_ptr<QFile> file {new QFile{path}};
+
+ if (!file->open(QIODevice::ReadOnly))
+ throw sigrok::Error{SR_ERR};
+
+ const auto size = file->size();
+ if (size < 0)
+ throw sigrok::Error{SR_ERR};
+
+ res->size = size;
+ res->handle = file.release();
+ } else {
+ qWarning() << "AndroidAssetReader: Unknown resource type" << res->type;
+ throw sigrok::Error{SR_ERR};
+ }
+}
+
+void AndroidAssetReader::close(struct sr_resource *res)
+{
+ if (!res->handle) {
+ qCritical("AndroidAssetReader: Invalid handle");
+ throw sigrok::Error{SR_ERR_ARG};
+ }
+ const std::unique_ptr<QFile> file {static_cast<QFile*>(res->handle)};
+ res->handle = nullptr;
+
+ file->close();
+}
+
+size_t AndroidAssetReader::read(const struct sr_resource *res, void *buf, size_t count)
+{
+ if (!res->handle) {
+ qCritical("AndroidAssetReader: Invalid handle");
+ throw sigrok::Error{SR_ERR_ARG};
+ }
+ auto *const file = static_cast<QFile*>(res->handle);
+
+ const auto n_read = file->read(static_cast<char*>(buf), count);
+ if (n_read < 0)
+ throw sigrok::Error{SR_ERR};
+
+ return n_read;
+}
diff --git a/android/assetreader.hpp b/android/assetreader.hpp
new file mode 100644
index 0000000..74d4999
--- /dev/null
+++ b/android/assetreader.hpp
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Daniel Elstner <daniel.kitta at gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_ANDROID_ASSETREADER_HPP
+#define PULSEVIEW_ANDROID_ASSETREADER_HPP
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+namespace pv {
+
+class AndroidAssetReader : public sigrok::ResourceReader
+{
+public:
+ AndroidAssetReader() {}
+ virtual ~AndroidAssetReader();
+
+private:
+ void open(struct sr_resource *res, std::string name) override;
+ void close(struct sr_resource *res) override;
+ size_t read(const struct sr_resource *res, void *buf, size_t count) override;
+};
+
+} // namespace pv
+
+#endif // !PULSEVIEW_ANDROID_ASSETREADER_HPP
diff --git a/android/bundled_libs.xml.in b/android/bundled_libs.xml.in
new file mode 100644
index 0000000..d308390
--- /dev/null
+++ b/android/bundled_libs.xml.in
@@ -0,0 +1,24 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ This file is part of the PulseView project.
+
+ Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<resources>
+ <array name="bundled_libs">
+ @bundled_libs@
+ </array>
+</resources>
diff --git a/android/custom_rules.xml b/android/custom_rules.xml
new file mode 100644
index 0000000..0800575
--- /dev/null
+++ b/android/custom_rules.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ This file is part of the PulseView project.
+
+ Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<project>
+ <property name="pulseview-source-path" value="${basedir}/.."/>
+ <property name="pulseview-build-path" value="${pulseview-source-path}"/>
+ <property name="cmake-cache" value="${pulseview-build-path}/CMakeCache.txt"/>
+ <target name="-get-prefix">
+ <loadproperties srcFile="${cmake-cache}" prefix="cmake">
+ <filterchain>
+ <replaceregex pattern=":[A-Z]*=" replace="="/>
+ </filterchain>
+ </loadproperties>
+ <property name="prefix" value="${cmake.CMAKE_INSTALL_PREFIX}"/>
+ <property name="android.abi" value="${cmake.ANDROID_ABI}"/>
+ </target>
+ <target name="-declare-anttasks" depends="-get-prefix">
+ <taskdef resource="anttasks.properties"
+ classpath="${prefix}/jar/sigrok-androidutils-anttasks.jar"/>
+ </target>
+ <target name="-pre-build" depends="-get-prefix, -declare-anttasks">
+ <copylibs todir="${native.libs.absolute.dir}" property="bundled_libs">
+ <fileset dir="${pulseview-build-path}/libs"/>
+ <include name="libpulseview.so"/>
+ <exclude name="libQt5*.so"/>
+ <exclude name="libc.so"/>
+ <exclude name="libm.so"/>
+ <exclude name="libdl.so"/>
+ <exclude name="liblog.so"/>
+ <exclude name="libstdc++.so"/>
+ <exclude name="libz.so"/>
+ </copylibs>
+ <copy todir="${native.libs.absolute.dir}">
+ <fileset dir="${prefix}/jar">
+ <include name="QtAndroid-bundled.jar" />
+ <include name="QtAndroidAccessibility-bundled.jar" />
+ </fileset>
+ </copy>
+ <copy todir="${native.libs.absolute.dir}/${android.abi}">
+ <fileset dir="${prefix}/lib">
+ <include name="libQt5Core.so" />
+ <include name="libQt5Gui.so" />
+ <include name="libQt5Widgets.so" />
+ <include name="libQt5Svg.so" />
+ </fileset>
+ </copy>
+ <copy todir="${native.libs.absolute.dir}/${android.abi}">
+ <fileset dir="${prefix}/plugins">
+ <include name="platforms/android/libqtforandroid.so"/>
+ <include name="platforms/libqeglfs.so"/>
+ <include name="platforms/libqminimal.so"/>
+ <include name="platforms/libqminimalegl.so"/>
+ <include name="platforms/libqoffscreen.so"/>
+ <include name="generic/libqevdevkeyboardplugin.so"/>
+ <include name="generic/libqevdevmouseplugin.so"/>
+ <include name="generic/libqevdevtabletplugin.so"/>
+ <include name="generic/libqevdevtouchplugin.so"/>
+ <include name="imageformats/libqsvg.so"/>
+ <include name="iconengines/libqsvgicon.so"/>
+ </fileset>
+ <filtermapper>
+ <replacestring from="/" to="_" />
+ <prefixlines prefix="libplugins_" />
+ </filtermapper>
+ </copy>
+ <copy file="${prefix}/jar/sigrok-androidutils.jar"
+ tofile="${jar.libs.absolute.dir}/sigrok-androidutils.jar"/>
+ <copy file="${prefix}/share/sigrok-androidutils/device_filter.xml"
+ tofile="${resource.absolute.dir}/xml/device_filter.xml"/>
+ <copy file="bundled_libs.xml.in"
+ tofile="${resource.absolute.dir}/values/bundled_libs.xml">
+ <filterset>
+ <filter token="bundled_libs" value="${bundled_libs}"/>
+ </filterset>
+ </copy>
+ <copy file="${pulseview-source-path}/icons/sigrok-logo-notext.png"
+ tofile="${resource.absolute.dir}/drawable/logo.png"/>
+ <copy todir="${source.absolute.dir}">
+ <fileset dir="${prefix}/src/android/java/src">
+ <include name="org/qtproject/qt5/android/bindings/**"/>
+ <include name="org/kde/necessitas/ministro/**"/>
+ </fileset>
+ </copy>
+ <copy todir="${resource.absolute.dir}">
+ <fileset dir="${prefix}/src/android/java/res">
+ <include name="**/strings.xml"/>
+ </fileset>
+ </copy>
+ <copy todir="${asset.absolute.dir}/libsigrokdecode">
+ <fileset dir="${prefix}/share/libsigrokdecode"/>
+ </copy>
+ <copy todir="${asset.absolute.dir}/python3.3">
+ <fileset dir="${prefix}/lib/python3.3">
+ <include name="**/*.py"/>
+ <exclude name="**/test/**"/>
+ <exclude name="**/tests/**"/>
+ <exclude name="**/tkinter/**"/>
+ <exclude name="**/turtledemo/**"/>
+ <exclude name="**/turtle.py"/>
+ <exclude name="**/idlelib/**"/>
+ </fileset>
+ </copy>
+ <copy todir="${asset.absolute.dir}/sigrok-firmware">
+ <fileset dir="${prefix}/share/sigrok-firmware"/>
+ </copy>
+ </target>
+</project>
diff --git a/android/loghandler.cpp b/android/loghandler.cpp
new file mode 100644
index 0000000..2c6674c
--- /dev/null
+++ b/android/loghandler.cpp
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef ENABLE_DECODE
+#include <libsigrokdecode/libsigrokdecode.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */
+#endif
+
+#include <android/log.h>
+
+#include <stdint.h>
+#include <libsigrok/libsigrok.h>
+
+#include "android/loghandler.hpp"
+
+namespace pv {
+
+int AndroidLogHandler::sr_callback(void *cb_data, int loglevel, const char *format, va_list args)
+{
+ static const int prio[] = {
+ [SR_LOG_NONE] = ANDROID_LOG_SILENT,
+ [SR_LOG_ERR] = ANDROID_LOG_ERROR,
+ [SR_LOG_WARN] = ANDROID_LOG_WARN,
+ [SR_LOG_INFO] = ANDROID_LOG_INFO,
+ [SR_LOG_DBG] = ANDROID_LOG_DEBUG,
+ [SR_LOG_SPEW] = ANDROID_LOG_VERBOSE,
+ };
+ int ret;
+
+ /* This specific log callback doesn't need the void pointer data. */
+ (void)cb_data;
+
+ /* Only output messages of at least the selected loglevel(s). */
+ if (loglevel > sr_log_loglevel_get())
+ return SR_OK;
+
+ if (loglevel < SR_LOG_NONE)
+ loglevel = SR_LOG_NONE;
+ else if (loglevel > SR_LOG_SPEW)
+ loglevel = SR_LOG_SPEW;
+
+ ret = __android_log_vprint(prio[loglevel], "sr", format, args);
+
+ return ret;
+}
+
+int AndroidLogHandler::srd_callback(void *cb_data, int loglevel, const char *format, va_list args)
+{
+#ifdef ENABLE_DECODE
+ static const int prio[] = {
+ [SRD_LOG_NONE] = ANDROID_LOG_SILENT,
+ [SRD_LOG_ERR] = ANDROID_LOG_ERROR,
+ [SRD_LOG_WARN] = ANDROID_LOG_WARN,
+ [SRD_LOG_INFO] = ANDROID_LOG_INFO,
+ [SRD_LOG_DBG] = ANDROID_LOG_DEBUG,
+ [SRD_LOG_SPEW] = ANDROID_LOG_VERBOSE,
+ };
+ int ret;
+
+ /* This specific log callback doesn't need the void pointer data. */
+ (void)cb_data;
+
+ /* Only output messages of at least the selected loglevel(s). */
+ if (loglevel > srd_log_loglevel_get())
+ return SRD_OK;
+
+ if (loglevel < SRD_LOG_NONE)
+ loglevel = SRD_LOG_NONE;
+ else if (loglevel > SRD_LOG_SPEW)
+ loglevel = SRD_LOG_SPEW;
+
+ ret = __android_log_vprint(prio[loglevel], "srd", format, args);
+
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+void AndroidLogHandler::install_callbacks()
+{
+ sr_log_callback_set(sr_callback, nullptr);
+#ifdef ENABLE_DECODE
+ srd_log_callback_set(srd_callback, nullptr);
+#endif
+}
+
+} // namespace pv
diff --git a/android/loghandler.hpp b/android/loghandler.hpp
new file mode 100644
index 0000000..b12a36e
--- /dev/null
+++ b/android/loghandler.hpp
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSEVIEW_ANDROID_LOGHANDLER_HPP
+#define PULSEVIEW_ANDROID_LOGHANDLER_HPP
+
+#include <stdarg.h>
+
+namespace pv {
+
+class AndroidLogHandler
+{
+private:
+ static int sr_callback(void *cb_data, int loglevel, const char *format, va_list args);
+ static int srd_callback(void *cb_data, int loglevel, const char *format, va_list args);
+
+public:
+ static void install_callbacks();
+};
+
+} // namespace pv
+
+#endif // PULSEVIEW_ANDROID_LOGHANDLER_HPP
diff --git a/android/res/layout/splash.xml b/android/res/layout/splash.xml
new file mode 100644
index 0000000..eb93afe
--- /dev/null
+++ b/android/res/layout/splash.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ This file is part of the PulseView project.
+
+ Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/android/res/values/libs.xml b/android/res/values/libs.xml
new file mode 100644
index 0000000..4447e9f
--- /dev/null
+++ b/android/res/values/libs.xml
@@ -0,0 +1,49 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ This file is part of the PulseView project.
+
+ Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<resources>
+ <array name="qt_sources">
+ <item>https://download.qt-project.org/ministro/android/qt5/qt-5.3</item>
+ </array>
+ <array name="qt_libs">
+ <!-- %%INSERT_QT_LIBS%% -->
+ <item>gnustl_shared</item>
+ <item>Qt5Core</item>
+ <item>Qt5Gui</item>
+ <item>Qt5Widgets</item>
+ <item>Qt5Svg</item>
+ </array>
+ <array name="bundled_in_lib">
+ <!-- %%INSERT_BUNDLED_IN_LIB%% -->
+ <item>libplugins_platforms_android_libqtforandroid.so:plugins/platforms/android/libqtforandroid.so</item>
+ <item>libplugins_platforms_libqeglfs.so:plugins/platforms/libqeglfs.so</item>
+ <item>libplugins_platforms_libqminimal.so:plugins/platforms/libqminimal.so</item>
+ <item>libplugins_platforms_libqminimalegl.so:plugins/platforms/libqminimalegl.so</item>
+ <item>libplugins_platforms_libqoffscreen.so:plugins/platforms/libqoffscreen.so</item>
+ <item>libplugins_generic_libqevdevkeyboardplugin.so:plugins/generic/libqevdevkeyboardplugin.so</item>
+ <item>libplugins_generic_libqevdevmouseplugin.so:plugins/generic/libqevdevmouseplugin.so</item>
+ <item>libplugins_generic_libqevdevtabletplugin.so:plugins/generic/libqevdevtabletplugin.so</item>
+ <item>libplugins_generic_libqevdevtouchplugin.so:plugins/generic/libqevdevtouchplugin.so</item>
+ <item>libplugins_imageformats_libqsvg.so:plugins/imageformats/libqsvg.so</item>
+ <item>libplugins_iconengines_libqsvgicon.so:plugins/iconengines/libqsvgicon.so</item>
+ </array>
+ <array name="bundled_in_assets">
+ <!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
+ </array>
+</resources>
diff --git a/android/res/values/strings-pv.xml b/android/res/values/strings-pv.xml
new file mode 100644
index 0000000..4606c21
--- /dev/null
+++ b/android/res/values/strings-pv.xml
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!--
+ This file is part of the PulseView project.
+
+ Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<resources>
+ <string name="pv_app_name">PulseView</string>
+</resources>
diff --git a/android/src/org/sigrok/pulseview/PulseViewActivity.java b/android/src/org/sigrok/pulseview/PulseViewActivity.java
new file mode 100644
index 0000000..cdd9149
--- /dev/null
+++ b/android/src/org/sigrok/pulseview/PulseViewActivity.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sigrok.pulseview;
+
+import org.qtproject.qt5.android.bindings.QtActivity;
+import org.sigrok.androidutils.UsbSupplicant;
+
+import android.os.Bundle;
+
+public class PulseViewActivity extends QtActivity
+{
+ private UsbSupplicant supplicant;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ supplicant = new UsbSupplicant(getApplicationContext(), R.xml.device_filter);
+ }
+
+ @Override
+ protected void onStart()
+ {
+ super.onStart();
+ supplicant.start();
+ }
+
+ @Override
+ protected void onStop()
+ {
+ supplicant.stop();
+ super.onStop();
+ }
+}
diff --git a/android/src/org/sigrok/pulseview/PulseViewApplication.java b/android/src/org/sigrok/pulseview/PulseViewApplication.java
new file mode 100644
index 0000000..55d00d9
--- /dev/null
+++ b/android/src/org/sigrok/pulseview/PulseViewApplication.java
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Marcus Comstedt <marcus at mc.pp.se>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sigrok.pulseview;
+
+import org.qtproject.qt5.android.bindings.QtApplication;
+import org.sigrok.androidutils.Environment;
+import org.sigrok.androidutils.UsbHelper;
+
+import java.io.File;
+import java.io.IOException;
+
+public class PulseViewApplication extends QtApplication
+{
+ @Override
+ public void onCreate()
+ {
+ Environment.initEnvironment(getApplicationInfo().sourceDir);
+ UsbHelper.setContext(getApplicationContext());
+ super.onCreate();
+ }
+}
diff --git a/config.h.in b/config.h.in
index aa6062d..8da6daf 100644
--- a/config.h.in
+++ b/config.h.in
@@ -29,6 +29,7 @@
#define PV_VERSION_MAJOR @PV_VERSION_MAJOR@
#define PV_VERSION_MINOR @PV_VERSION_MINOR@
#define PV_VERSION_MICRO @PV_VERSION_MICRO@
+#define PV_VERSION_SUFFIX @PV_VERSION_SUFFIX@
#define PV_VERSION_STRING "@PV_VERSION_STRING@"
/* Platform properties */
diff --git a/contrib/pulseview_cross.nsi b/contrib/pulseview_cross.nsi.in
similarity index 85%
rename from contrib/pulseview_cross.nsi
rename to contrib/pulseview_cross.nsi.in
index 3c39351..6c0475c 100644
--- a/contrib/pulseview_cross.nsi
+++ b/contrib/pulseview_cross.nsi.in
@@ -38,7 +38,7 @@
Name "PulseView"
# Filename of the installer executable.
-OutFile "pulseview-0.2.0-installer.exe"
+OutFile "pulseview- at PV_VERSION_STRING@-installer.exe"
# Where to install the application.
InstallDir "$PROGRAMFILES\sigrok\PulseView"
@@ -54,7 +54,7 @@ RequestExecutionLevel admin
# --- MUI interface configuration ---------------------------------------------
# Use the following icon for the installer EXE file.
-!define MUI_ICON "../icons/sigrok-logo-notext.ico"
+!define MUI_ICON "@PROJECT_SOURCE_DIR@/icons/sigrok-logo-notext.ico"
# Show a nice image at the top of each installer page.
!define MUI_HEADERIMAGE
@@ -72,11 +72,7 @@ RequestExecutionLevel admin
# Path where the cross-compiled sigrok tools and libraries are located.
# Change this to where-ever you installed libsigrok.a and so on.
-!define CROSS "$%HOME%/sr_mingw"
-
-# Path where the cross-compiled MXE tools and libraries are located.
-# Change this to where-ever you installed MXE (and the files it built).
-!define MXE "$%HOME%/mxe-git/usr/i686-pc-mingw32"
+!define CROSS "@CMAKE_INSTALL_PREFIX@"
# --- MUI pages ---------------------------------------------------------------
@@ -85,7 +81,7 @@ RequestExecutionLevel admin
!insertmacro MUI_PAGE_WELCOME
# Show the license of the project.
-!insertmacro MUI_PAGE_LICENSE "../COPYING"
+!insertmacro MUI_PAGE_LICENSE "@PROJECT_SOURCE_DIR@/COPYING"
# Show a screen which allows the user to select which components to install.
!insertmacro MUI_PAGE_COMPONENTS
@@ -123,15 +119,12 @@ Section "PulseView (required)" Section1
SetOutPath "$INSTDIR"
# License file.
- File "../COPYING"
+ File "@PROJECT_SOURCE_DIR@/COPYING"
# PulseView (statically linked, includes all libs).
File "${CROSS}/bin/pulseview.exe"
- # libusb0.dll (needed for libusb-0.1).
- File "${CROSS}/libusb0.dll"
-
- # Zadig (used for installing libusb-win32 and WinUSB drivers).
+ # Zadig (used for installing WinUSB drivers).
File "${CROSS}/zadig.exe"
File "${CROSS}/zadig_xp.exe"
@@ -139,13 +132,13 @@ Section "PulseView (required)" Section1
File "${CROSS}/python32.dll"
File "${CROSS}/python32.zip"
+ SetOutPath "$INSTDIR\share"
+
# Protocol decoders.
- SetOutPath "$INSTDIR\decoders"
- File /r /x "__pycache__" "${CROSS}/share/libsigrokdecode/decoders/*"
+ File /r /x "__pycache__" /x "*.pyc" "${CROSS}/share/libsigrokdecode"
# Firmware files.
- SetOutPath "$INSTDIR\firmware"
- File /r "${CROSS}/share/sigrok-firmware/*"
+ File /r "${CROSS}/share/sigrok-firmware"
# Example *.sr files.
SetOutPath "$INSTDIR\examples"
@@ -170,6 +163,16 @@ Section "PulseView (required)" Section1
"$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 \
SW_SHOWNORMAL "" "Uninstall PulseView"
+ # Create a shortcut for the Zadig executable.
+ CreateShortCut "$SMPROGRAMS\sigrok\PulseView\Zadig.lnk" \
+ "$INSTDIR\zadig.exe" "" "$INSTDIR\zadig.exe" 0 \
+ SW_SHOWNORMAL "" "Zadig"
+
+ # Create a shortcut for the Zadig executable (for Win XP).
+ CreateShortCut "$SMPROGRAMS\sigrok\PulseView\Zadig (Win XP).lnk" \
+ "$INSTDIR\zadig_xp.exe" "" "$INSTDIR\zadig_xp.exe" 0 \
+ SW_SHOWNORMAL "" "Zadig (Win XP)"
+
# Create registry keys for "Add/remove programs" in the control panel.
WriteRegStr HKLM "${REGSTR}" "DisplayName" "PulseView"
WriteRegStr HKLM "${REGSTR}" "UninstallString" \
@@ -183,7 +186,7 @@ Section "PulseView (required)" Section1
WriteRegStr HKLM "${REGSTR}" "URLUpdateInfo" \
"http://sigrok.org/wiki/Downloads"
WriteRegStr HKLM "${REGSTR}" "URLInfoAbout" "http://sigrok.org"
- WriteRegStr HKLM "${REGSTR}" "DisplayVersion" "0.2.0"
+ WriteRegStr HKLM "${REGSTR}" "DisplayVersion" "@PV_VERSION_STRING@"
WriteRegStr HKLM "${REGSTR}" "Contact" \
"sigrok-devel at lists.sourceforge.org"
WriteRegStr HKLM "${REGSTR}" "Comments" \
@@ -206,31 +209,31 @@ Section "Uninstall"
# Delete the application, the application data, and related libs.
Delete "$INSTDIR\COPYING"
Delete "$INSTDIR\pulseview.exe"
- Delete "$INSTDIR\libusb0.dll"
Delete "$INSTDIR\zadig.exe"
Delete "$INSTDIR\zadig_xp.exe"
Delete "$INSTDIR\python32.dll"
Delete "$INSTDIR\python32.zip"
- # Delete all decoders and everything else in decoders/.
+ # Delete all decoders and everything else in libsigrokdecode/.
# There could be *.pyc files or __pycache__ subdirs and so on.
- RMDir /r "$INSTDIR\decoders\*"
+ RMDir /r "$INSTDIR\share\libsigrokdecode"
# Delete the firmware files.
- RMDir /r "$INSTDIR\firmware\*"
+ RMDir /r "$INSTDIR\share\sigrok-firmware"
# Delete the example *.sr files.
RMDir /r "$INSTDIR\examples\*"
# Delete the install directory and its sub-directories.
- RMDir "$INSTDIR\decoders"
- RMDir "$INSTDIR\firmware"
+ RMDir "$INSTDIR\share"
RMDir "$INSTDIR\examples"
RMDir "$INSTDIR"
# Delete the links from the start menu.
Delete "$SMPROGRAMS\sigrok\PulseView\PulseView.lnk"
Delete "$SMPROGRAMS\sigrok\PulseView\Uninstall.lnk"
+ Delete "$SMPROGRAMS\sigrok\PulseView\Zadig.lnk"
+ Delete "$SMPROGRAMS\sigrok\PulseView\Zadig (Win XP).lnk"
# Delete the sub-directory in the start menu.
RMDir "$SMPROGRAMS\sigrok\PulseView"
diff --git a/doc/pulseview.1 b/doc/pulseview.1
index b830f08..eb294e1 100644
--- a/doc/pulseview.1
+++ b/doc/pulseview.1
@@ -1,8 +1,8 @@
-.TH PULSEVIEW 1 "May 4, 2013"
+.TH PULSEVIEW 1 "December 16, 2015"
.SH "NAME"
PulseView \- Qt-based LA/scope/MSO GUI for sigrok
.SH "SYNOPSIS"
-.B pulseview \fR[\fB\-lh?V\fR] [\fB\-l\fR|\fB\-\-loglevel\fR] [\fB\-h\fR|\fB\-?\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fBfile.sr\fR]
+.B pulseview \fR[\fBOPTIONS\fR] [\fBfile.sr\fR]
.SH "DESCRIPTION"
.B PulseView
is a cross-platform Qt-based GUI for the
@@ -39,6 +39,65 @@ Show a help text and exit.
.TP
.B "\-V, \-\-version"
Show version information and exit.
+.TP
+.BR "\-i, \-\-input\-file " <filename>
+Load input from a file. If the
+.B \-\-input\-format
+option is not supplied, PulseView attempts to load the file as a sigrok session
+file.
+.TP
+.BR "\-I, \-\-input\-format " <format>
+Specifies the format of the input file to be loaded.
+.SH "KEYBOARD SHORTCUTS"
+.TP
+.B "f"
+Zoom-to-fit.
+.TP
+.B "o"
+Zoom 1:1.
+.TP
+.B "s"
+Enable / disable sticky scrolling.
+.TP
+.B "c"
+Show / hide cursors.
+.TP
+.B "b"
+Toggle between coloured trace backgrounds and alternating light/dark
+gray trace backgrounds.
+.TP
+.B "SPACE"
+Start / stop an acquisition.
+.TP
+.B "ALT"
+Show / hide the menu.
+.TP
+.B "Arrow keys"
+Scroll up/down/left/right.
+.TP
+.B "CTRL+o"
+Open file.
+.TP
+.B "CTRL+s"
+Save as...
+.TP
+.B "CTRL+r"
+Save selected range as...
+.TP
+.B "CTRL+g"
+Group all currently selected traces into a trace group.
+.TP
+.B "CTRL+u"
+Ungroup the traces in the currently selected trace group.
+.TP
+.B "CTRL++"
+Zoom in.
+.TP
+.B "CTRL+-"
+Zoom out.
+.TP
+.B "CTRL+q"
+Quit, i.e. shutdown PulseView.
.SH "EXIT STATUS"
.B PulseView
exits with 0 on success, 1 on most failures.
diff --git a/extdef.h b/extdef.h
index 8af615c..0e92865 100644
--- a/extdef.h
+++ b/extdef.h
@@ -21,7 +21,7 @@
#ifndef PULSEVIEW_EXTDEF_H
#define PULSEVIEW_EXTDEF_H
-#define countof(x) (sizeof(x)/sizeof(x[0]))
+#define countof(x) (sizeof(x) / sizeof(x[0]))
#define begin_element(x) (&x[0])
#define end_element(x) (&x[countof(x)])
diff --git a/icons/add-decoder.svg b/icons/add-decoder.svg
new file mode 100644
index 0000000..58167ba
--- /dev/null
+++ b/icons/add-decoder.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path id="path2989" d="m1.5 12.5h21" stroke="#888a85" stroke-linecap="square" stroke-width="1px" fill="none"/>
+ <path id="path2991" d="m3.5 12.5 2-5h2l2 5-2 5h-2z" stroke="#967b00" stroke-width="1px" fill="#edd400"/>
+ <path id="path3012" d="m9.5 12.5 2-5 7 4e-7 2 5-2 5-7-0.000005z" stroke="#4e9a06" stroke-width="1px" fill="#73d216"/>
+</svg>
diff --git a/icons/channels.svg b/icons/channels.svg
new file mode 100644
index 0000000..42e3cca
--- /dev/null
+++ b/icons/channels.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata27">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs id="defs4">
+ <radialGradient id="radialGradient3098" gradientUnits="userSpaceOnUse" cy="20.5" cx="15" gradientTransform="matrix(1.8793 3.9617e-8 -.0000050558 .34168 -17.189 14.996)" r="5.8534">
+ <stop id="stop3930" stop-color="#888a85" stop-opacity=".52995" offset="0"/>
+ <stop id="stop3932" stop-color="#888a85" stop-opacity="0" offset="1"/>
+ </radialGradient>
+ </defs>
+ <g id="g3091" transform="translate(1.0007 -1)">
+ <path id="path3926" fill-rule="evenodd" fill="url(#radialGradient3098)" d="m21.999 22a11 2 0 0 1 -22 0 11 2 0 1 1 22 0z"/>
+ <path id="path3806" d="m10 10 1.5-1.5 3 3l-1.5 1.5z" stroke="#a40000" stroke-width="1px" fill="#c00"/>
+ <path id="path3808" d="m1.5 21.5 2-2" stroke="#888a85" stroke-linecap="round" stroke-width="1px" fill="none"/>
+ <path id="path3804" stroke-linejoin="round" d="m9.5 9.5 0.5 2.5-6 6-2 2.5 0.5 0.5 2.5-2 6-6l2.5 0.5z" stroke="#a40000" stroke-width="1px" fill="#c00"/>
+ <path id="path3793" stroke-linejoin="round" d="m16.5 2.5-0.955 0.9548-0.045 1.0452-0.5 0.5-0.567-0.4332-3.433 3.4332 4 4 3.5-3.5-0.5-0.5l0.5-0.5h1l1-1c-1.5-0.5-3.5-2.5-4-4z" stroke="#a40000" stroke-width="1px" fill="#c00"/>
+ </g>
+</svg>
diff --git a/icons/decoder-hidden.svg b/icons/decoder-hidden.svg
index d462200..43678af 100644
--- a/icons/decoder-hidden.svg
+++ b/icons/decoder-hidden.svg
@@ -1,33 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <path
- d="m 19.426489,11.070664 a 11,11 0 0 1 -16.8529782,-1e-6"
- transform="matrix(1.1867339,0,0,1.1867339,-2.0540724,-2.1379314)"
- id="path3053"
- style="fill:none;stroke:#000000;stroke-width:1.26397336" />
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <path id="path3053" d="m22 12c-2.4408 2.9089-6.2027 4.6631-10 4.6631s-7.5592-1.7542-10-4.6631" stroke="#000" stroke-width="1.5" fill="none"/>
</svg>
diff --git a/icons/decoder-shown.svg b/icons/decoder-shown.svg
index 6050bdc..a634cf4 100644
--- a/icons/decoder-shown.svg
+++ b/icons/decoder-shown.svg
@@ -1,47 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <path
- d="M 13.390812,-9.3707772 A 5.87888,5.87888 0 0 1 14.476134,0.74106762"
- transform="matrix(0.89442417,0,0,0.89442417,1.1613345,14.644558)"
- id="path2989"
- style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
- <path
- d="m 12,-4 a 1,1 0 1 1 -2,0 1,1 0 1 1 2,0 z"
- transform="matrix(1.3065319,0,0,1.3065319,-3.3718505,16.219638)"
- id="path2995"
- style="fill:#000000;stroke:none" />
- <path
- d="M 10.875,6.34375 C 7.9493854,6.36765 5.0099958,7.3787791 2.59375,9.40625 2.0116039,9.8947285 1.4884785,10.417854 1,11 1.4884785,11.582146 2.0116039,12.105271 2.59375,12.59375 8.1165975,17.227969 16.365781,16.522848 21,11 18.393252,7.8933983 14.636505,6.3130214 10.875,6.34375 z"
- id="path3053"
- style="fill:none;stroke:#000000;stroke-width:1.5" />
- <path
- d="M 7.5357022,0.74972325 A 5.87888,5.87888 0 0 1 8.1948438,-9.1664619"
- transform="matrix(0.89442417,0,0,0.89442417,1.1613345,14.644558)"
- id="path3057"
- style="fill:none;stroke:#000000;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="g3321" transform="translate(1 -.99889)">
+ <path id="path2989" stroke-linejoin="round" d="m13.138 8.2631a5.2582 5.2582 0 0 1 0.97074 9.0443" stroke="#000" stroke-linecap="round" stroke-width=".44721" fill="none"/>
+ <path id="path2995" d="m12.307 12.994a1.3065 1.3065 0 0 1 -2.6131 0 1.3065 1.3065 0 1 1 2.6131 0z"/>
+ <path id="path3053" d="m10.875 8.3438c-2.9256 0.0238-5.865 1.035-8.2812 3.0622-0.5822 0.489-1.1053 1.012-1.5938 1.594 0.4885 0.582 1.0116 1.105 1.5938 1.594 5.5228 4.634 13.772 3.929 18.406-1.594-2.607-3.1066-6.363-4.687-10.125-4.6562z" stroke="#000" stroke-width="1.5" fill="none"/>
+ <path id="path3057" stroke-linejoin="round" d="m7.9014 17.315a5.2582 5.2582 0 0 1 0.5896 -8.8691" stroke="#000" stroke-linecap="round" stroke-width=".44721" fill="none"/>
+ </g>
</svg>
diff --git a/icons/menu.svg b/icons/menu.svg
new file mode 100644
index 0000000..2d1492d
--- /dev/null
+++ b/icons/menu.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg id="svg2991" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs3001">
+ <linearGradient id="linearGradient2999" y2="8.5" gradientUnits="userSpaceOnUse" x2="12.5" y1="7" x1="12.5">
+ <stop id="stop2995" stop-color="#2e3436" offset="0"/>
+ <stop id="stop2997" stop-color="#888a85" offset="1"/>
+ </linearGradient>
+ </defs>
+ <g id="g3001-4" fill-rule="evenodd" transform="translate(0,5)">
+ <rect id="rect2993-6" rx="1.5" ry="1.5" height="3" width="18" y="6" x="3" fill="#2e3436"/>
+ <rect id="rect2991-0" rx="1" ry="1" height="2" width="16" y="6.5" x="4" fill="url(#linearGradient2999)"/>
+ </g>
+ <g id="g3001-8" fill-rule="evenodd" transform="translate(0,10)">
+ <rect id="rect2993-9" rx="1.5" ry="1.5" height="3" width="18" y="6" x="3" fill="#2e3436"/>
+ <rect id="rect2991-8" rx="1" ry="1" height="2" width="16" y="6.5" x="4" fill="url(#linearGradient2999)"/>
+ </g>
+ <g id="g3001" fill-rule="evenodd">
+ <rect id="rect2993" rx="1.5" ry="1.5" height="3" width="18" y="6" x="3" fill="#2e3436"/>
+ <rect id="rect2991" rx="1" ry="1" height="2" width="16" y="6.5" x="4" fill="url(#linearGradient2999)"/>
+ </g>
+</svg>
diff --git a/icons/probes.svg b/icons/probes.svg
deleted file mode 100644
index 8e7e604..0000000
--- a/icons/probes.svg
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:xlink="http://www.w3.org/1999/xlink"
- version="1.1"
- width="22"
- height="22"
- id="svg2">
- <metadata
- id="metadata27">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs4">
- <radialGradient
- cx="15"
- cy="20.5"
- r="5.8534002"
- id="radialGradient3934"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.0000001,4.9521507e-8,-2.6903182e-6,0.42710347,6.4579919e-5,11.744378)">
- <stop
- id="stop3930"
- style="stop-color:#888a85;stop-opacity:0.5299539"
- offset="0" />
- <stop
- id="stop3932"
- style="stop-color:#888a85;stop-opacity:0"
- offset="1" />
- </radialGradient>
- <radialGradient
- cx="15"
- cy="20.5"
- r="5.8534002"
- id="radialGradient3009"
- xlink:href="#radialGradient3934"
- gradientUnits="userSpaceOnUse"
- gradientTransform="matrix(1.8792554,3.9617209e-8,-5.0557945e-6,0.34168281,-17.188707,12.995502)" />
- </defs>
- <path
- d="M 21.999281,20 A 11.00022,2.0000344 0 0 1 -0.00115994,20 11.00022,2.0000344 0 1 1 21.999281,20 z"
- id="path3926"
- style="fill:url(#radialGradient3009);fill-rule:evenodd" />
- <path
- d="m 10,8 1.5,-1.5 3,3 L 13,11 z"
- id="path3806"
- style="fill:#cc0000;stroke:#a40000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="m 1.5,19.5 2,-2"
- id="path3808"
- style="fill:none;stroke:#888a85;stroke-width:1px;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- d="M 9.5,7.5 10,10 3.9999998,16 2,18.5 2.5,19 4.9999998,17 11,11 l 2.5,0.5 z"
- id="path3804"
- style="fill:#cc0000;stroke:#a40000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
- <path
- d="M 16.5,0.5 15.545182,1.4548178 15.5,2.5 15,3 14.433209,2.5667912 11,6 15,10 18.5,6.5 18,6 l 0.5,-0.5 1,0 1,-1 C 19,4 17,2 16.5,0.5 z"
- id="path3793"
- style="fill:#cc0000;stroke:#a40000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
-</svg>
diff --git a/icons/show-cursors.svg b/icons/show-cursors.svg
new file mode 100644
index 0000000..1cbf440
--- /dev/null
+++ b/icons/show-cursors.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path id="path2993" d="m1.5 3.5v5h4l3 3v-8z" stroke="#204a87" stroke-width="1px" fill="#3465a4"/>
+ <path id="path2995" d="m8.5 20.5v-16" stroke="#204a87" stroke-linecap="square" stroke-width="1px" fill="none"/>
+ <path id="path2993-2" d="m22.5 3.5v5h-4l-3 3v-8z" stroke="#204a87" stroke-width="1px" fill="#3465a4"/>
+ <path id="path2995-2" d="m15.5 20.5v-16" stroke="#204a87" stroke-linecap="square" stroke-width="1px" fill="none"/>
+</svg>
diff --git a/icons/sigrok-logo-notext.ico b/icons/sigrok-logo-notext.ico
index 8f1228c..d8b50c6 100644
Binary files a/icons/sigrok-logo-notext.ico and b/icons/sigrok-logo-notext.ico differ
diff --git a/icons/sigrok-logo-notext.svg b/icons/sigrok-logo-notext.svg
new file mode 100644
index 0000000..620028d
--- /dev/null
+++ b/icons/sigrok-logo-notext.svg
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg2" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="48" width="48" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <defs id="defs4">
+ <filter id="filter5288" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur5290" stdDeviation="1.2304688"/>
+ </filter>
+ <filter id="filter5288-0" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur5290-1" stdDeviation="1.2304688"/>
+ </filter>
+ <filter id="filter5354" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur5356" stdDeviation="1.2575"/>
+ </filter>
+ <filter id="filter5354-5" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur5356-2" stdDeviation="1.2575"/>
+ </filter>
+ <filter id="filter5412" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur5414" stdDeviation="1.311875"/>
+ </filter>
+ <filter id="filter5412-5" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur5414-6" stdDeviation="1.311875"/>
+ </filter>
+ <filter id="filter5470" height="1.0266" width="1.2422" color-interpolation-filters="sRGB" y="-.013320" x="-.12110">
+ <feGaussianBlur id="feGaussianBlur5472" stdDeviation="1.30875"/>
+ </filter>
+ <filter id="filter5470-6" height="1.0266" width="1.2422" color-interpolation-filters="sRGB" y="-.013320" x="-.12110">
+ <feGaussianBlur id="feGaussianBlur5472-1" stdDeviation="1.30875"/>
+ </filter>
+ <filter id="filter5536" height="1.0246" width="1.9962" color-interpolation-filters="sRGB" y="-.012296" x="-.49812">
+ <feGaussianBlur id="feGaussianBlur5538" stdDeviation="1.2453125"/>
+ </filter>
+ <filter id="filter5703" height="1" width="1" color-interpolation-filters="sRGB" y="0" x="0">
+ <feGaussianBlur id="feGaussianBlur5705" stdDeviation="1.709984544049459" result="result8"/>
+ <feTurbulence id="feTurbulence5707" baseFrequency="0.0080370942812983005 0.027820710973724884" seed="56" result="result7" numOctaves="2" type="turbulence"/>
+ <feComposite id="feComposite5709" operator="in" result="result6" in2="result8" in="SourceGraphic"/>
+ <feComposite id="feComposite5711" in="result6" in2="result7" k3="1" k2="0" k1="0" result="result2" k4="0" operator="arithmetic"/>
+ <feComposite id="feComposite5713" operator="in" result="fbSourceGraphic" in2="result6" in="result2"/>
+ <feComposite id="feComposite5715" in="fbSourceGraphic" in2="fbSourceGraphic" k3="0" k2="2.5" k1="0" result="fbSourceGraphic" k4="0" operator="arithmetic"/>
+ <feColorMatrix id="feColorMatrix5805" result="fbSourceGraphicAlpha" values="0" type="saturate" in="fbSourceGraphic"/>
+ <feColorMatrix id="feColorMatrix5807" values="1" type="saturate" result="result2" in="fbSourceGraphic"/>
+ <feFlood id="feFlood5809" flood-color="rgb(113,79,56)" result="result1"/>
+ <feBlend id="feBlend5811" result="result3" mode="multiply" in2="result2" in="result1"/>
+ <feComposite id="feComposite5813" operator="in" result="result4" in2="fbSourceGraphic"/>
+ </filter>
+ <filter id="filter4766-3" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur4768-8" stdDeviation="0.25453125"/>
+ </filter>
+ <filter id="filter4654-6" height="1.0252" width="1.5121" color-interpolation-filters="sRGB" y="-.012590" x="-.25603">
+ <feGaussianBlur id="feGaussianBlur4656-4" stdDeviation="0.4267185"/>
+ </filter>
+ <filter id="filter4596-6" height="1.2771" width="1.0125" color-interpolation-filters="sRGB" y="-.13854" x="-.0062716">
+ <feGaussianBlur id="feGaussianBlur4598-4" stdDeviation="0.48343725"/>
+ </filter>
+ <filter id="filter4536-1" height="1.0263" width="1.2736" color-interpolation-filters="sRGB" y="-.013154" x="-.13680">
+ <feGaussianBlur id="feGaussianBlur4538-3" stdDeviation="0.45059085"/>
+ </filter>
+ <linearGradient id="linearGradient4358-7">
+ <stop id="stop4360-7" stop-color="#aca592" offset="0"/>
+ <stop id="stop4362-1" stop-color="#aca592" stop-opacity="0" offset="1"/>
+ </linearGradient>
+ <filter id="filter6293" height="1.4486" width="1.1034" color-interpolation-filters="sRGB" y="-.22428" x="-.051678">
+ <feGaussianBlur id="feGaussianBlur6295" stdDeviation="4.5525501"/>
+ </filter>
+ <filter id="filter6293-3" height="1.4486" width="1.1034" color-interpolation-filters="sRGB" y="-.22428" x="-.051678">
+ <feGaussianBlur id="feGaussianBlur6295-5" stdDeviation="4.5525501"/>
+ </filter>
+ <filter id="filter7222" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur7224" stdDeviation="0.93122891"/>
+ </filter>
+ <filter id="filter7410" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur7412" stdDeviation="0.28402344"/>
+ </filter>
+ <filter id="filter7414" color-interpolation-filters="sRGB">
+ <feGaussianBlur id="feGaussianBlur7416" stdDeviation="0.28402344"/>
+ </filter>
+ <linearGradient id="linearGradient4024" y2="837.09" gradientUnits="userSpaceOnUse" x2="112.48" gradientTransform="translate(279.37)" y1="900.59" x1="-60.266">
+ <stop id="stop4324-0" stop-color="#6c6753" stop-opacity=".50862" offset="0"/>
+ <stop id="stop4332-7" stop-color="#a39e88" stop-opacity="0" offset=".45474"/>
+ <stop id="stop4330-01" stop-color="#a39e88" stop-opacity="0" offset=".77666"/>
+ <stop id="stop4326-8" stop-color="#6c6753" stop-opacity=".70690" offset="1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient4026" y2="805.75" xlink:href="#linearGradient4358-7" gradientUnits="userSpaceOnUse" x2="1.7663" gradientTransform="translate(279.37)" y1="787.42" x1="1.1134"/>
+ <linearGradient id="linearGradient4028" y2="716.49" gradientUnits="userSpaceOnUse" x2="-133.64" gradientTransform="translate(279.37)" y1="715.43" x1="-134">
+ <stop id="stop4408-14" offset="0"/>
+ <stop id="stop4410-2" stop-opacity="0" offset="1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient4030" y2="806.65" gradientUnits="userSpaceOnUse" x2="-69.701" gradientTransform="translate(279.37)" y1="839.88" x1="-69.701">
+ <stop id="stop4542-9" stop-color="#989078" offset="0"/>
+ <stop id="stop4544-5" stop-color="#aca592" stop-opacity="0" offset="1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient4032" y2="820.96" xlink:href="#linearGradient4358-7" gradientUnits="userSpaceOnUse" x2="-8.9303" gradientTransform="translate(279.37)" y1="829.56" x1="-8.9303"/>
+ <linearGradient id="linearGradient4034" y2="801.74" gradientUnits="userSpaceOnUse" x2="25.605" gradientTransform="translate(279.37)" y1="911.18" x1="25.605">
+ <stop id="stop4704-1" stop-color="#6c6753" stop-opacity=".50862" offset="0"/>
+ <stop id="stop4706-4" stop-color="#a39e88" stop-opacity="0" offset=".60980"/>
+ <stop id="stop4708-5" stop-color="#a39e88" stop-opacity="0" offset=".77666"/>
+ <stop id="stop4710-4" stop-color="#6c6753" stop-opacity=".70690" offset="1"/>
+ </linearGradient>
+ <radialGradient id="radialGradient4036" gradientUnits="userSpaceOnUse" cy="356.19" cx="300" gradientTransform="matrix(1.3455 -.0000010547 2.0946e-7 .10026 -103.64 317.01)" r="55.264">
+ <stop id="stop6329" offset="0"/>
+ <stop id="stop6333" stop-color="#464646" offset=".60223"/>
+ <stop id="stop6331" stop-color="#aca592" stop-opacity="0" offset="1"/>
+ </radialGradient>
+ <radialGradient id="radialGradient4038" gradientUnits="userSpaceOnUse" cy="38.204" cx="301.23" gradientTransform="matrix(1 0 0 13.333 0 -471.18)" r="2.1213">
+ <stop id="stop4661" stop-color="#fff" stop-opacity=".33621" offset="0"/>
+ <stop id="stop4663" stop-color="#fff" stop-opacity="0" offset="1"/>
+ </radialGradient>
+ <radialGradient id="radialGradient4040" gradientUnits="userSpaceOnUse" cy="67.522" cx="302.84" gradientTransform="matrix(.74160 0 0 3.0648 78.253 -107.79)" r="7.0144">
+ <stop id="stop4691" stop-color="#fff" stop-opacity=".23276" offset="0"/>
+ <stop id="stop4693" stop-color="#fff" stop-opacity="0" offset="1"/>
+ </radialGradient>
+ <linearGradient id="linearGradient4074" y2="333.77" gradientUnits="userSpaceOnUse" x2="291.22" gradientTransform="matrix(1.0518 0 0 1.0574 -15.542 -15.976)" y1="330.77" x1="291.22">
+ <stop id="stop5572" stop-color="#fff" stop-opacity=".36207" offset="0"/>
+ <stop id="stop5574" stop-opacity="0" offset="1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient4076" y2="339.48" gradientUnits="userSpaceOnUse" x2="337.09" gradientTransform="matrix(1.0417 0 0 1.2372 -12.207 -75.58)" y1="339.48" x1="343.43">
+ <stop id="stop5572-3" stop-color="#fff" stop-opacity=".36207" offset="0"/>
+ <stop id="stop5574-2" stop-opacity="0" offset="1"/>
+ </linearGradient>
+ <linearGradient id="linearGradient4078" y2="304.68" gradientUnits="userSpaceOnUse" x2="300.31" gradientTransform="translate(0,4)" y1="331.2" x1="300.31">
+ <stop id="stop5656" stop-opacity=".30172" offset="0"/>
+ <stop id="stop5658" stop-opacity="0" offset="1"/>
+ </linearGradient>
+ </defs>
+ <metadata id="metadata7">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(-93.213 -870.88)">
+ <path id="path1648" d="m1319.8 222.81c105.62-29.815 164.49-18.697 304.84-6.9484 82.527 6.9082 180.31-11.422 237.14-8.5433 95.114 4.8165 189.98 28.25 321.13 20.545" display="none" stroke="#fff" stroke-width="1px" fill="none"/>
+ </g>
+ <g id="layer2" transform="translate(-93.213 -418.52)">
+ <g id="g3938" transform="matrix(.10491 0 0 .10491 85.726 417.58)">
+ <path id="rect10305-9-6-8-1-4-6" style="color:#000000" d="m403.34 894.39 1.1978 14.787-3.5969 6.1085-5.8543 2.6695-6.7717 1.1769-12.988-1.6568-14.183 0.0522-9.733 0.82833-9.6449 1.6044-9.7661-1.2906-9.033-0.36618-15.992 0.46239-12.627 1.1944-11.95-3.3136-8.446 0.80213-11.891 1.6831-16.155-1.6219-12.442 1.6743-9.5288 0.11012-7.6552-3.0431-11.662-3.9917-4.2875-7.8841-4.7091-13.569-2.5128-14.622 5.9409-4.9408 199.41 1.7589 4.5302 6.1849 0.35355 15.203z" fill-opacity=".27273" transform="m [...]
+ <path id="rect10305-9-6-8-1-4-6-9" style="color:#000000" d="m403.34 894.39 1.1978 14.787-3.5969 6.1085-5.8543 2.6695-6.7717 1.1769-12.988-1.6568-14.183 0.0522-9.733 0.82833-9.6449 1.6044-9.7661-1.2906-9.033-0.36618-15.992 0.46239-12.627 1.1944-11.95-3.3136-8.446 0.80213-11.891 1.6831-16.155-1.6219-12.442 1.6743-9.5288 0.11012-7.6552-3.0431-11.662-3.9917-4.2875-7.8841-4.7091-13.569-2.5128-14.622 5.9409-4.9408 199.41 1.7589 4.5302 6.1849 0.35355 15.203z" fill-opacity=".60766" transform= [...]
+ <path id="rect10305-9-6-8-1-4" style="color:#000000" d="m399.81 876.33 0.13139 28.312-3.4465 5.2698-5.6095 2.303-6.4886 1.0153-12.445-1.4293-13.59 0.045-9.3261 0.7146-9.2417 1.3841-9.3578-1.1134-8.6554-0.3159-15.323 0.3989-12.099 1.0304-11.451-2.8586-8.0929 0.692-11.394 1.452-15.48-1.3992-11.922 1.4444-9.1305 0.095-7.3352-2.6253-11.175-3.4437-4.1083-6.8016-4.5122-11.706-2.4078-12.614 202.46 0.15063z" fill-opacity=".41627" transform="matrix(.97921 0 0 .79971 6.2101 -277.03)" filter="ur [...]
+ <g id="g6187" transform="translate(0 -456.36)">
+ <path id="rect10307-2-2" style="color:#000000" d="m302 787.43-10.188 1.625-7.4375-0.6875-4.1875 1.875-10.969-0.65625-15.906-0.4375-12.375-0.71875-8.7188 3.375-9.125 0.3125-3.1562 2.7812-5.5312-0.46875-5.125 3.625-0.0312 0.0312v0.0312l-5.1562 6.4062-2.875 14.812-2.4375 16.688 0.0312 14.625-0.65625 24.844 2.9062 11.156 2.4688 4.75 1.5625 4.125 2.5312 3.3438v0.0312l1.625 2.7812 3.7812 2.8125v-0.0625l7.4375 4.5938 10.156-0.125 9.0312 0.0937 11.812 1.4375 15.312-1.375 11.281 1.4375 8 0.68 [...]
+ <path id="rect10307-2-93" style="color:#000000" d="m302 787.42-10.188 1.625-7.4375-0.6875-4.1875 1.875-10.969-0.65625-15.906-0.4375-12.375-0.71875-8.7188 3.375-9.125 0.3125-3.1562 2.7812-5.5312-0.46875-5.125 3.625-0.0312 0.0312v0.0312l-5.1562 6.4062-2.875 14.812-2.4375 16.688 0.0312 14.625-0.65625 24.844 2.9062 11.156 2.4688 4.75 1.5625 4.125 2.5312 3.3438v0.031l1.625 2.7812 3.7812 2.8125v-0.062l7.4375 4.5938 10.156-0.125 9.0312 0.094 11.812 1.4375 15.312-1.375 11.281 1.4375 8 0.6875 [...]
+ <path id="rect10307-2-40" opacity=".85446" style="color:#000000" d="m240.93 788.44-8.722 3.3744-9.1332 0.29289-3.159 2.7813-5.513-0.4688-5.6193 4.9375-1.552 5.9778-0.28856 7.9063 5.6345 0.1875 2.8704 4.625 3.4762-0.65782 5.4177 1.2203 3.1183 0.18775 2.5158-1.0469 5.7268 1.7656 4.223-1.0781 4.6232 0.43755 6.4071 4.1406 6.4091-0.3437 2.0368-0.39063 13.819-0.45317 11.132-0.5937 5.4025-0.66994 5.913 1.2891 9.9355 0.18187 5.9864-2.5133 14.125 1.1596 19.16-1.0722 5.2521 1.7751 5.1057-0.337 [...]
+ <path id="path4520-4-5" opacity=".85446" style="color:#000000" d="m295.67 821.86-5.9301-1.3438-5.388 0.7188-11.132 0.5937-13.732 0.46483 20.46 0.78517 5.103 3.0313 4.5562 0.4062 5.7864-1 5.8775 1.5h6.485l3.3108-2.9375 7.943-0.5 22.887-1.0312 4.8725-0.67192 4.3831 1.1485 4.1244 2.1796 2.3844-0.1875 9.5528-1.9375 2.8859 0.8125 2.9916-0.7812 3.3564 0.75-7.1606-2.9493-14.056 0.1368-5.0615 0.33764-5.2963-1.7751-19.513 0.7187-13.74-0.875-5.9984 2.5625-9.9516-0.1562zm-55.761-3.4688-4.1917 1 [...]
+ <path id="rect10305-9-5" opacity=".85446" style="color:#000000" d="m215.44 818 2.6312 4.8729 7.0328-2.6372 15.87 4.8498 3.7519-0.7846 5.6678 1.7952 6.0397-2.0213 2.9938-1.7933 20.527 0.7826 5.1115 3.032 4.548 0.42551 5.7911-1.0106 5.878 1.516h6.487l3.3018-2.9522 7.9493-0.5053 22.891-1.0107 4.872-0.68284 4.3742 1.143 4.1299 2.1786 2.3796-0.1734 9.55-1.9601 2.893 0.82619 2.9862-0.77539 10.993 2.4759 10.526-2.5267 5.7016 11.871-0.31026 10.481 1.8466 14.246-1.231 24.242-1.1703 16.219-2.4 [...]
+ <path id="rect10309-8-0" opacity=".85446" style="color:#000000" d="m208.41 896.27-0.005-52.06-1.4772-19.14-1.524-9.5383-1.5111-3.6499 0.26834-4.6763 5.1214-9.1629-5.202 6.4739-2.8718 14.825-2.4333 16.683 0.0321 14.616-0.6476 24.862 2.9029 11.135 2.4543 4.7714 1.5812 4.11 2.5721 3.4418 0.73955-2.6911h0.00002z" fill="#aca793"/>
+ <path id="path4517-1-0" opacity=".85446" style="color:#000000" d="m214.44 794.41-5.2056 3.6638-5.066 9.134-0.27706 4.7026 1.5072 3.61 1.5339 9.5625 1.4732 19.125v41.156l4.3284-54.281 2.6578-12.812 0.0607-0.2185-2.8704-4.625-5.6345-0.1875 0.28856-7.9063 1.5868-5.9587 5.6169-4.9643z" fill="#e3e2db"/>
+ <path id="path4522-1-19" opacity=".85446" d="m208.31 885.27-0.18225 3.4688 0.27338-3.375-0.0911-0.094zm-0.19743 3.9375-0.50119 9.6875 1.6402 2.7813 3.7665 2.8125v-0.062l-0.18225-0.125-2.5211-5.625-2.2022-9.4688z" fill-rule="evenodd" fill="#797463"/>
+ <path id="rect10305-9-6-1" style="color:#000000" d="m215.44 818 2.6312 4.8729 7.0328-2.6372 15.87 4.8498 3.7519-0.7846 5.6678 1.7952 6.0397-2.0213 2.9938-1.7933 20.527 0.7826 5.1115 3.032 4.548 0.42551 5.7911-1.0106 5.878 1.516h6.487l3.3018-2.9522 7.9493-0.5053 22.891-1.0107 4.872-0.68284 4.3742 1.143 4.1299 2.1786 2.3796-0.1734 9.55-1.9601 2.893 0.82619 2.9862-0.77539 10.993 2.4759 10.526-2.5267 5.7016 11.871-0.31026 10.481 1.8466 14.246-1.231 24.242-1.1703 16.219-2.412 7.7302-4.068 [...]
+ <path id="rect10307-2-9-5" style="color:#000000" d="m240.93 788.44-8.722 3.3744-9.1332 0.29289-3.159 2.7813-5.513-0.4688-5.6193 4.9375-1.552 5.9778-0.28856 7.9063 5.6345 0.1875 2.8704 4.625 3.4762-0.65782 5.4177 1.2203 3.1496 0.12525 2.4689-0.96875 5.7893 1.6875 4.1605-1.0938 4.5763 0.4688 6.4696 4.1874 6.4091-0.3437 2.0368-0.39063 13.819-0.45317 11.132-0.5937 5.388-0.75005 5.8364 1.3438 10.077 0.12495 5.9672-2.4688 14.094 1.1973 19.16-1.0722 5.2575 1.7343 5.1003-0.29682 14.045-0.156 [...]
+ <path id="rect10309-8-4-6" style="color:#000000" d="m208.41 896.27-0.005-52.06-1.4772-19.14-1.524-9.5383-1.5111-3.6499 0.26834-4.6763 5.1214-9.1629-5.202 6.4739-2.8718 14.825-2.4333 16.683 0.0321 14.616-0.6476 24.862 2.9029 11.135 2.4543 4.7714 1.5812 4.11 2.5721 3.4418 0.73955-2.6911h0.00002z" fill="url(#linearGradient4028)"/>
+ <path id="path4517-1-6-7" style="color:#000000" d="m214.44 794.41-5.2056 3.6638-5.066 9.134-0.27706 4.7026 1.5072 3.61 1.5339 9.5625 1.4732 19.125v41.156l4.3284-54.281 2.6578-12.812 0.0607-0.2185-2.8704-4.625-5.6345-0.1875 0.28856-7.9063 1.5868-5.9587 5.6169-4.9643z" fill="url(#linearGradient4030)"/>
+ <path id="path4520-4-2-3" style="color:#000000" d="m295.7 821.86-5.9926-1.3438-5.3568 0.7188-11.132 0.5937-13.732 0.46483 20.46 0.78517 5.103 3.0313 4.5562 0.4062 5.7864-1 5.8775 1.5h6.485l3.3108-2.9375 7.943-0.5 22.887-1.0312 4.8725-0.67192 4.3831 1.1485 4.1244 2.1796 2.3844-0.1875 9.5528-1.9375 2.8859 0.8125 2.9916-0.7812 3.3564 0.75-7.1245-2.9375-14.092 0.125-5.0066 0.31245-5.3512-1.75-19.513 0.7187-13.803-0.875-5.8734 2.5312-9.9829-0.12495zm-55.824-3.4688-4.1605 1-5.7268-1.75-2.5 [...]
+ <path id="rect10305-9-6-2-0" style="color:#000000" d="m213.3 828.62-2.5971 22.13-3.1409 39.489 3.8661 12.473-2.0177-12.023 1.0763-18.998 1.913-21.72 0.90036-21.35z" filter="url(#filter4536-1)" fill="#7f7a66"/>
+ <path id="path4520-4-2-2-6" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m397.96 818.25-3.875 0.1875h-0.0625-0.0625l-16.719 5.1562 0.28125 0.96875 16.625-5.125h0.0312l4.0625-0.1875h0.0312v-0.5l0.375 0.28125 0.75-1.9375-1.4374 1.1562zm-179.06-1.375-0.125 0.0312-3.4375 0.65625 0.1875 0.96875 3.3438-0.625 5.375 1.1875h0.0312 0.0625l3.0938 0.125h0.125l0.0937-0.0625 2.3438-0.96875 5.5938 1.6875 0.125 0.0312 0.125-0.0312 4-0.96875h0.0312l4.4375 0.46875 6. [...]
+ <path id="rect10309-8-4-2-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m203.83 811.86 1.0771 3.7672 1.5312 9.4688v0.0625l1.4688 19.062v0.0312l-0.35355 51.943 0.32322 2.2981 0.32322-2.9168 0.70711-51.355v-0.0312-0.0312l-1.4688-19.125v-0.0312l-1.5312-9.5625-2.0771-3.5798z" filter="url(#filter4654-6)" fill="#888470"/>
+ <path id="rect10305-9-6-8-1" style="color:#000000" d="m215.44 818 2.6312 4.8729 7.0328-2.6372 15.87 4.8498 3.7519-0.7846 5.6678 1.7952 6.0397-2.0213 2.9938-1.7933 20.527 0.7826 5.1115 3.032 4.548 0.42551 5.7911-1.0106 5.878 1.516h6.487l3.3018-2.9522 7.9493-0.5053 22.891-1.0107 4.872-0.68284 4.3742 1.143 4.1299 2.1786 2.3796-0.1734 9.55-1.9601 2.893 0.82619 2.9862-0.77539 10.993 2.4759 10.526-2.5267 5.7016 11.871-0.31026 10.481 1.8466 14.246-1.231 24.242-1.1703 16.219-2.412 7.7302-4.0 [...]
+ <path id="path4517-1-6-8-9" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m209.01 798.41-0.125 0.0937-0.0312 0.15625-1.8589 6.0259v0.0625 0.0625l-0.3125 7.9062v0.5l0.5 0.0312 5.2424 0.36428 2.7263 4.0107 0.52275 0.49686-2.429-5.4656-0.125-0.21875h-0.28125l-5.125-0.15625 0.28125-7.4375 1.5312-5.6875 4.7487-4.3005-5.2648 3.5559z" filter="url(#filter4766-3)" fill="#e3e2db"/>
+ </g>
+ <path id="path7353" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m240.5 829.86v61h-22.719v5h27.719v-61h59v61h30v-61h16v61h28.5v-5h-23.5v-61h-26v61h-20v-61h-69z" fill-opacity=".40670" transform="translate(0 -452.36)" fill="#f2f1ef"/>
+ <path id="path7353-5" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m240.5 377.5v61h-22.719v5h1v-3h22.719v-61h68v-2h-69zm89 0v61h-19v2h20v-61h25v-2h-26zm-25 7v59h1v-59h-1zm46 0v59h1v-59h-1zm6 54v2h22.5v-2h-22.5z" filter="url(#filter7410)" fill="#4b473c"/>
+ <path id="path7353-8" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m308.5 829.86v60h1v-60h-1zm46 0v60h1v-60h-1zm-110 4v61h-26.719v1h27.719v-61h58v-1h-59zm89 0v61h-29v1h30v-61h15v-1h-16zm44.5 57v4h-27.5v1h28.5v-5h-1z" transform="translate(0 -452.36)" filter="url(#filter7414)" fill="#e2e0da"/>
+ <path id="path4332" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m295 109.34v151.53 91.531h10v-91.531-151.53h-10z" fill="#d4aa00"/>
+ <path id="path4332-4" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m297 109.34v243.06h6v-243.06h-6z" fill-opacity=".54440" filter="url(#filter5536)" fill="#fcff4c"/>
+ <path id="path4332-3" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m274.94 116.59-9.875 1.5c7.92 51.61 19.94 109.69 19.94 162.79v71.531h10v-71.531c0-54.598-12.202-113.07-20.062-164.28z" fill="#d45500"/>
+ <path id="path4332-3-9" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m272.94 116.59-5.875 1.5c7.92 51.61 19.94 109.69 19.94 162.79v71.531h6v-71.531c0-54.598-12.202-113.07-20.062-164.28z" fill-opacity=".40927" filter="url(#filter5470)" fill="#fca"/>
+ <path id="path4332-3-5" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m244.75 133.78-9.5 3.125c19.28 58.02 39.75 96.67 39.75 183.98v31.531h10v-31.531c0-88.882-21.224-129.84-40.25-187.09z" fill="#a00"/>
+ <path id="path4332-3-5-8" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m242.75 133.78-3.5 3.125c19.277 58.01 38.25 96.664 38.25 183.97v31.531h5.5v-31.531c0-88.882-21.224-129.84-40.25-187.09z" fill-opacity=".47490" filter="url(#filter5412)" fill="#faa"/>
+ <path id="path4332-3-5-7" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m214.19 156.62-8.375 5.4688c28.45 43.57 59.18 77.47 59.18 158.79v31.531h10v-31.531c0-83.882-32.86-121.45-60.812-164.25z" fill="#784421"/>
+ <path id="path4332-3-5-7-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m215.19 162.62-4.375-0.53125c28.45 43.57 57.68 77.47 57.68 158.79v31.531h4v-31.531c0-83.882-29.36-115.45-57.312-158.25z" fill-opacity=".37838" filter="url(#filter5354)" fill="#deaa87"/>
+ <path id="path4332-3-5-7-3" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m183.16 185.47-6.3125 7.75c28.98 23.48 78.15 58.28 78.15 127.66v31.531h10v-31.531c0-74.139-53.403-112.37-81.844-135.41z" fill="#1a1a1a"/>
+ <path id="path4332-3-5-7-3-15" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m183.16 189.47-4.3125 1.75c28.98 23.48 79.65 53.27 79.65 129.66v31.531h4v-31.531c0-71.639-50.903-108.37-79.344-131.41z" fill-opacity=".31274" filter="url(#filter5288)" fill="#fff"/>
+ <path id="path4332-3-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m325.06 116.59c-7.86 51.21-20.06 109.69-20.06 164.29v71.531h10v-71.531c0-53.099 12.017-111.18 19.938-162.78l-9.875-1.5z" fill="#008000"/>
+ <path id="path4332-3-9-6" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m272.94 112.59-5.875 1.5c7.92 51.61 19.94 109.69 19.94 162.79v71.531h6v-71.531c0-54.598-12.202-113.07-20.062-164.28z" fill-opacity=".40927" transform="matrix(-1 0 0 1 600.06 4)" filter="url(#filter5470-6)" fill="#cfa"/>
+ <path id="path4332-3-5-91" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m355.25 133.78c-19.03 57.26-40.25 98.21-40.25 187.1v31.531h10v-31.531c0-87.304 20.473-125.96 39.75-183.97l-9.5-3.125z" fill="#04a"/>
+ <path id="path4332-3-5-8-6" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m242.75 129.78-3.5 3.125c19.277 58.01 38.25 96.664 38.25 183.97v31.531h5.5v-31.531c0-88.882-21.224-129.84-40.25-187.09z" fill-opacity=".47490" transform="matrix(-1,0,0,1,600.25,4)" filter="url(#filter5412-5)" fill="#acf"/>
+ <path id="path4332-3-5-7-1" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m385.81 156.62c-27.95 42.81-60.81 80.37-60.81 164.26v31.531h10v-31.531c0-81.315 30.735-115.21 59.188-158.78l-8.375-5.4688z" fill="#aa00d4"/>
+ <path id="path4332-3-5-7-0-9" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m275.19 158.62-4.375-0.53125c28.45 43.57 57.68 77.47 57.68 158.79v31.531h4v-31.531c0-83.882-29.36-115.45-57.312-158.25z" fill-opacity=".48263" transform="matrix(-1 0 0 1 660.81 4)" filter="url(#filter5354-5)" fill="#eaf"/>
+ <path id="path4332-3-5-7-3-0" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m416.84 185.47c-28.44 23.03-81.84 61.27-81.84 135.41v31.531h10v-31.531c0-69.374 49.168-104.18 78.156-127.66l-6.3125-7.75z" fill="#6f6f91"/>
+ <path id="path4332-3-5-7-3-15-1" style="block-progression:tb;text-indent:0;color:#000000;text-transform:none" d="m183.16 185.47-4.3125 1.75c28.98 23.48 78.15 55.27 78.15 129.65v31.531h5v-31.531c0-69.13-50.4-108.37-78.84-131.4z" fill-opacity=".42857" transform="matrix(-1 0 0 1 598.84 4)" filter="url(#filter5288-0)" fill="#dbdbe3"/>
+ <path id="rect4622-0" style="color:#000000" d="m225.64 343.5h148.71v16.777c-38.37 1.6959-81.343 2.6948-148.71 0v-16.777z" fill="url(#radialGradient4036)"/>
+ <path id="rect4622" style="color:#000000" d="m253.93 333h92.143v20.357c-23.774 2.0577-50.401 3.2698-92.143 0v-20.357z" fill="#1a1a1a"/>
+ <g id="g4720" transform="translate(0,8)">
+ <path id="rect4625" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-2" transform="matrix(.98787 -.15526 .15526 .98787 -42.271 63.417)">
+ <path id="rect4625-0" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-9" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-0" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-6" transform="matrix(.94451 -.32848 .32848 .94451 -79.478 131.23)">
+ <path id="rect4625-93" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-5" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-6" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-0" transform="matrix(.84411 -.53617 .53617 .84411 -103.8 224.17)">
+ <path id="rect4625-6" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-2" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-1" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-4" transform="matrix(.63008 -.77653 .77653 .63008 -96.161 351.53)">
+ <path id="rect4625-05" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-0" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-2" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-2-1" transform="matrix(-.98787 -.15526 -.15526 .98787 642.52 63.417)">
+ <path id="rect4625-0-8" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-9-4" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-0-8" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-6-1" transform="matrix(-.94451 -.32848 -.32848 .94451 679.72 131.23)">
+ <path id="rect4625-93-8" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-5-1" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-6-7" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-0-8" transform="matrix(-.84411 -.53617 -.53617 .84411 704.05 224.17)">
+ <path id="rect4625-6-8" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-2-3" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-1-5" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <g id="g4720-4-5" transform="matrix(-.63008 -.77653 -.77653 .63008 696.41 351.53)">
+ <path id="rect4625-05-6" style="color:#000000" d="m300.03 0.9989c-1.1099 0.0045-2.2085 0.96259-2.3617 2.8125-3.4868 42.092-1.1379 61.943-1.3171 72.469-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126h12.532v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.17845-1.8678-1.2972-2.817-2.4071-2.8125l-0.00015-0.0001z" fill="#1a1a1a"/>
+ <path id="path4657-0-4" style="color:#000000" d="m303.35 38.204a2.1213 28.284 0 1 1 -4.2426 0 2.1213 28.284 0 1 1 4.2426 0z" transform="translate(-2.1213 8.4853)" fill="url(#radialGradient4038)"/>
+ <path id="rect4625-9-2-6" style="color:#000000" d="m298.41 7.3114c-3.4868 42.092 0.11212 56.693-0.0671 67.219-0.1512 8.8808-4.6309 14.242-4.3146 14.969 1.5637 3.5938 1.7792 8.6526 1.7792 11.905v17.126l10.532 1.75v-17.225c0-3.4204 0.19044-8.2459 1.6756-11.806 0.41457-0.9939-4.3147-6.0874-4.3147-14.969 0-10.271 2.7656-30.213-1.2717-72.469-0.67872-3.8375-3.0216 0.39801-4.019 3.4999z" fill="url(#radialGradient4040)"/>
+ </g>
+ <path id="rect4622-8" style="color:#000000" d="m254.32 333.77h91.362v2.5144c-23.573 1.6946-49.974 2.6928-91.362 0v-2.5144z" fill="url(#linearGradient4074)"/>
+ <path id="rect4622-8-0" style="color:#000000" d="m338.82 333.63h6.7367v19.615c-3.3472-0.0173-4.745 0.15065-6.7367 0v-19.615z" fill="url(#linearGradient4076)"/>
+ <path id="rect4622-8-0-4" style="color:#000000" d="m255.07 308.68h90.487v24.565c-23.347 1.9827-49.495 3.1506-90.487 0v-24.565z" fill="url(#linearGradient4078)"/>
+ </g>
+ </g>
+</svg>
diff --git a/icons/status-green.svg b/icons/status-green.svg
index e9ba2e8..9c76cd8 100644
--- a/icons/status-green.svg
+++ b/icons/status-green.svg
@@ -1,12 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg id="svg4376" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="22" width="22" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg4376" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata3015">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs id="defs3">
- <radialGradient id="radialGradient3774" gradientUnits="userSpaceOnUse" cy="6" cx="7.5" gradientTransform="matrix(1.0799,0,0,1.0799,-2.4045,-1.3652)" r="7.5">
+ <radialGradient id="radialGradient3051" gradientUnits="userSpaceOnUse" cy="6" cx="7.5" gradientTransform="matrix(1.0799 0 0 1.0799 .0955 4.1348)" r="7.5">
<stop id="stop3292" stop-color="#c1f093" offset="0"/>
<stop id="stop3294" stop-color="#8eea34" offset=".64486"/>
<stop id="stop3296" stop-color="#73d216" offset="1"/>
</radialGradient>
</defs>
- <path id="path2996" stroke-linejoin="round" d="m17 7.5a8.5 8.5 0 1 1 -17 0 8.5 8.5 0 1 1 17 0z" transform="translate(2.5,3.5)" stroke="#3b7404" stroke-linecap="round" fill="url(#radialGradient3774)"/>
- <path id="path3766" stroke-linejoin="round" d="m16 7.5a7.5 7.5 0 1 1 -15 0 7.5 7.5 0 1 1 15 0z" stroke-opacity=".64338" transform="translate(2.5,3.5)" stroke="#fff" stroke-linecap="round" fill="none"/>
+ <g id="g3047" stroke-linejoin="round" stroke-linecap="round" transform="translate(1,-1)">
+ <path id="path2996" d="m19.5 13a8.5 8.5 0 0 1 -17 0 8.5 8.5 0 1 1 17 0z" stroke="#3b7404" fill="url(#radialGradient3051)"/>
+ <path id="path3766" d="m18.5 13a7.5 7.5 0 0 1 -15 0 7.5 7.5 0 1 1 15 0z" stroke-opacity=".64338" stroke="#fff" fill="none"/>
+ </g>
</svg>
diff --git a/icons/status-grey.svg b/icons/status-grey.svg
index 33f75d8..516c7b6 100644
--- a/icons/status-grey.svg
+++ b/icons/status-grey.svg
@@ -1,12 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg id="svg4376" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="22" width="22" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg4376" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata3027">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs id="defs3">
- <radialGradient id="radialGradient3774" gradientUnits="userSpaceOnUse" cy="6" cx="7.5" gradientTransform="matrix(1.0799,0,0,1.0799,-2.4045,-1.3652)" r="7.5">
+ <radialGradient id="radialGradient3125" gradientUnits="userSpaceOnUse" cy="6" cx="7.5" gradientTransform="matrix(1.0799 0 0 1.0799 .0955 4.1348)" r="7.5">
<stop id="stop3778" stop-color="#d3d7cf" offset="0"/>
<stop id="stop3780" stop-color="#babdb6" offset=".64486"/>
<stop id="stop3782" stop-color="#888a85" offset="1"/>
</radialGradient>
</defs>
- <path id="path2996" stroke-linejoin="round" d="m17 7.5a8.5 8.5 0 1 1 -17 0 8.5 8.5 0 1 1 17 0z" transform="translate(2.5,3.5)" stroke="#555753" stroke-linecap="round" fill="url(#radialGradient3774)"/>
- <path id="path3766" stroke-linejoin="round" d="m16 7.5a7.5 7.5 0 1 1 -15 0 7.5 7.5 0 1 1 15 0z" stroke-opacity=".64338" transform="translate(2.5,3.5)" stroke="#fff" stroke-linecap="round" fill="none"/>
+ <g id="g3121" stroke-linejoin="round" stroke-linecap="round" transform="translate(1,-1)">
+ <path id="path2996" d="m19.5 13a8.5 8.5 0 0 1 -17 0 8.5 8.5 0 1 1 17 0z" stroke="#555753" fill="url(#radialGradient3125)"/>
+ <path id="path3766" d="m18.5 13a7.5 7.5 0 0 1 -15 0 7.5 7.5 0 1 1 15 0z" stroke-opacity=".64338" stroke="#fff" fill="none"/>
+ </g>
</svg>
diff --git a/icons/status-red.svg b/icons/status-red.svg
index 593b307..9a48fd5 100644
--- a/icons/status-red.svg
+++ b/icons/status-red.svg
@@ -1,12 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg id="svg4376" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="22" width="22" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg id="svg4376" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata3039">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
<defs id="defs3">
- <radialGradient id="radialGradient3774" gradientUnits="userSpaceOnUse" cy="6" cx="7.5" gradientTransform="matrix(1.0799,0,0,1.0799,-2.4045,-1.3652)" r="7.5">
+ <radialGradient id="radialGradient3045" gradientUnits="userSpaceOnUse" cy="6" cx="7.5" gradientTransform="matrix(1.0799 0 0 1.0799 .0955 4.1348)" r="7.5">
<stop id="stop3292" stop-color="#f24e4e" offset="0"/>
<stop id="stop3294" stop-color="#c00" offset=".64486"/>
<stop id="stop3296" stop-color="#a40000" offset="1"/>
</radialGradient>
</defs>
- <path id="path2996" stroke-linejoin="round" d="m17 7.5a8.5 8.5 0 1 1 -17 0 8.5 8.5 0 1 1 17 0z" transform="translate(2.5,3.5)" stroke="#780000" stroke-linecap="round" fill="url(#radialGradient3774)"/>
- <path id="path3766" stroke-linejoin="round" d="m16 7.5a7.5 7.5 0 1 1 -15 0 7.5 7.5 0 1 1 15 0z" stroke-opacity=".64338" transform="translate(2.5,3.5)" stroke="#fff" stroke-linecap="round" fill="none"/>
+ <g id="g3041" stroke-linejoin="round" stroke-linecap="round" transform="translate(1,-1)">
+ <path id="path2996" d="m19.5 13a8.5 8.5 0 0 1 -17 0 8.5 8.5 0 1 1 17 0z" stroke="#780000" fill="url(#radialGradient3045)"/>
+ <path id="path3766" d="m18.5 13a7.5 7.5 0 0 1 -15 0 7.5 7.5 0 1 1 15 0z" stroke-opacity=".64338" stroke="#fff" fill="none"/>
+ </g>
</svg>
diff --git a/icons/trigger-change.svg b/icons/trigger-change.svg
index 1786d4b..be20eb4 100644
--- a/icons/trigger-change.svg
+++ b/icons/trigger-change.svg
@@ -1,76 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- version="1.1"
- width="22"
- height="22"
- id="svg2989"
- inkscape:version="0.48.3.1 r9886"
- sodipodi:docname="trigger-rising.svg">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1680"
- inkscape:window-height="995"
- id="namedview7"
- showgrid="true"
- inkscape:zoom="36.791193"
- inkscape:cx="7.942203"
- inkscape:cy="10.877688"
- inkscape:window-x="1278"
- inkscape:window-y="-3"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg2989">
- <inkscape:grid
- type="xygrid"
- id="grid2984"
- empspacing="2"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true" />
- </sodipodi:namedview>
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(0,-1030.3622)"
- id="layer1">
- <path
- d="m 4,1048.3622 5,0 4,-14 5,0"
- id="path3007"
- style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- <path
- sodipodi:nodetypes="cccc"
- inkscape:connector-curvature="0"
- style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1"
- id="path3012"
- d="m 4,1034.3622 5,0 4,14 5,0" />
- </g>
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(1 -1029.4)" stroke="#000" stroke-linecap="square" stroke-width="2" fill="none">
+ <path id="path3007" d="m4 1048.4h5l4-14h5"/>
+ <path id="path3012" d="m4 1034.4h5l4 14h5"/>
+ </g>
</svg>
diff --git a/icons/trigger-falling.svg b/icons/trigger-falling.svg
index 24bd4f6..ed72f2c 100644
--- a/icons/trigger-falling.svg
+++ b/icons/trigger-falling.svg
@@ -1,36 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(0,-1030.3622)"
- id="layer1">
- <path
- d="m 18,1048.3622 -5,0 -4,-14 -5,0"
- id="path3007"
- style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
- </g>
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(1 -1029.4)">
+ <path id="path3007" d="m18 1048.4h-5l-4-14h-5" stroke="#000" stroke-linecap="square" stroke-width="2" fill="none"/>
+ </g>
</svg>
diff --git a/icons/trigger-high.svg b/icons/trigger-high.svg
index d2150d8..c9ef3c4 100644
--- a/icons/trigger-high.svg
+++ b/icons/trigger-high.svg
@@ -1,36 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(0,-1030.3622)"
- id="layer1">
- <path
- d="m 4,1034.3622 14,0"
- id="path3007"
- style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
- </g>
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(1 -1028.4)">
+ <path id="path3007" d="m4 1033.4h14" stroke="#000" stroke-linecap="square" stroke-width="2" fill="none"/>
+ </g>
</svg>
diff --git a/icons/trigger-low.svg b/icons/trigger-low.svg
index aa71a98..aec158e 100644
--- a/icons/trigger-low.svg
+++ b/icons/trigger-low.svg
@@ -1,36 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(0,-1030.3622)"
- id="layer1">
- <path
- d="m 4,1048.3622 14,0"
- id="path3007"
- style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
- </g>
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(1.1398 -1029.2)">
+ <path id="path3007" d="m3.8602 1048.2h14" stroke="#000" stroke-linecap="square" stroke-width="2" fill="none"/>
+ </g>
</svg>
diff --git a/icons/trigger-rising.svg b/icons/trigger-marker-change.svg
similarity index 75%
copy from icons/trigger-rising.svg
copy to icons/trigger-marker-change.svg
index ac1550f..b454c91 100644
--- a/icons/trigger-rising.svg
+++ b/icons/trigger-marker-change.svg
@@ -8,8 +8,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
- width="22"
- height="22"
+ width="16"
+ height="16"
id="svg2989">
<defs
id="defs2991" />
@@ -26,11 +26,15 @@
</rdf:RDF>
</metadata>
<g
- transform="translate(0,-1030.3622)"
+ transform="translate(0,-1036.3622)"
id="layer1">
<path
- d="m 4,1048.3622 5,0 4,-14 5,0"
+ d="m 2,1049.3622 4,0 4,-10 4,0"
id="path3007"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ d="m 2,1039.3622 4,0 4,10 4,0"
+ id="path3012"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>
diff --git a/icons/trigger-none.svg b/icons/trigger-marker-falling.svg
similarity index 78%
copy from icons/trigger-none.svg
copy to icons/trigger-marker-falling.svg
index 196264d..ec92877 100644
--- a/icons/trigger-none.svg
+++ b/icons/trigger-marker-falling.svg
@@ -8,8 +8,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
- width="22"
- height="22"
+ width="16"
+ height="16"
id="svg2989">
<defs
id="defs2991" />
@@ -25,11 +25,8 @@
</cc:Work>
</rdf:RDF>
</metadata>
- <rect
- width="4"
- height="4"
- x="9"
- y="9"
- id="rect3033"
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ <path
+ d="m 2,3 4,0 4,10 4,0"
+ id="path3012"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
</svg>
diff --git a/icons/trigger-none.svg b/icons/trigger-marker-high.svg
similarity index 78%
copy from icons/trigger-none.svg
copy to icons/trigger-marker-high.svg
index 196264d..b7a8825 100644
--- a/icons/trigger-none.svg
+++ b/icons/trigger-marker-high.svg
@@ -8,8 +8,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
- width="22"
- height="22"
+ width="16"
+ height="16"
id="svg2989">
<defs
id="defs2991" />
@@ -25,11 +25,8 @@
</cc:Work>
</rdf:RDF>
</metadata>
- <rect
- width="4"
- height="4"
- x="9"
- y="9"
- id="rect3033"
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+ <path
+ d="M 2,3 14,3"
+ id="path3066"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
</svg>
diff --git a/icons/trigger-rising.svg b/icons/trigger-marker-low.svg
similarity index 84%
copy from icons/trigger-rising.svg
copy to icons/trigger-marker-low.svg
index ac1550f..8513561 100644
--- a/icons/trigger-rising.svg
+++ b/icons/trigger-marker-low.svg
@@ -8,8 +8,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
- width="22"
- height="22"
+ width="16"
+ height="16"
id="svg2989">
<defs
id="defs2991" />
@@ -26,11 +26,12 @@
</rdf:RDF>
</metadata>
<g
- transform="translate(0,-1030.3622)"
+ transform="translate(0,-1036.3622)"
id="layer1">
<path
- d="m 4,1048.3622 5,0 4,-14 5,0"
- id="path3007"
+ d="m 2,13 12,0"
+ transform="translate(0,1036.3622)"
+ id="path3906"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>
diff --git a/icons/trigger-rising.svg b/icons/trigger-marker-rising.svg
similarity index 89%
copy from icons/trigger-rising.svg
copy to icons/trigger-marker-rising.svg
index ac1550f..ddebe8d 100644
--- a/icons/trigger-rising.svg
+++ b/icons/trigger-marker-rising.svg
@@ -8,8 +8,8 @@
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
- width="22"
- height="22"
+ width="16"
+ height="16"
id="svg2989">
<defs
id="defs2991" />
@@ -26,10 +26,10 @@
</rdf:RDF>
</metadata>
<g
- transform="translate(0,-1030.3622)"
+ transform="translate(0,-1036.3622)"
id="layer1">
<path
- d="m 4,1048.3622 5,0 4,-14 5,0"
+ d="m 2,1049.3622 4,0 4,-10 4,0"
id="path3007"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
</g>
diff --git a/icons/trigger-none.svg b/icons/trigger-none.svg
index 196264d..631b681 100644
--- a/icons/trigger-none.svg
+++ b/icons/trigger-none.svg
@@ -1,35 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <rect
- width="4"
- height="4"
- x="9"
- y="9"
- id="rect3033"
- style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none" />
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect id="rect3033" fill-rule="evenodd" height="4" width="4" y="10" x="10"/>
</svg>
diff --git a/icons/trigger-rising.svg b/icons/trigger-rising.svg
index ac1550f..730aa72 100644
--- a/icons/trigger-rising.svg
+++ b/icons/trigger-rising.svg
@@ -1,36 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- version="1.1"
- width="22"
- height="22"
- id="svg2989">
- <defs
- id="defs2991" />
- <metadata
- id="metadata2994">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(0,-1030.3622)"
- id="layer1">
- <path
- d="m 4,1048.3622 5,0 4,-14 5,0"
- id="path3007"
- style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:1" />
- </g>
+<svg id="svg2989" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="24" width="24" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <metadata id="metadata2994">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(1 -1029.4)">
+ <path id="path3007" d="m4 1048.4h5l4-14h5" stroke="#000" stroke-linecap="square" stroke-width="2" fill="none"/>
+ </g>
</svg>
diff --git a/main.cpp b/main.cpp
index 2c6a59e..3e00479 100644
--- a/main.cpp
+++ b/main.cpp
@@ -23,19 +23,24 @@
#endif
#include <stdint.h>
-#include <libsigrok/libsigrok.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
#include <getopt.h>
-#include <QtGui/QApplication>
#include <QDebug>
#ifdef ENABLE_SIGNALS
-#include "signalhandler.h"
+#include "signalhandler.hpp"
#endif
-#include "pv/devicemanager.h"
-#include "pv/mainwindow.h"
+#include "pv/application.hpp"
+#include "pv/devicemanager.hpp"
+#include "pv/mainwindow.hpp"
+#ifdef ANDROID
+#include <libsigrokandroidutils/libsigrokandroidutils.h>
+#include "android/assetreader.hpp"
+#include "android/loghandler.hpp"
+#endif
#include "config.h"
@@ -49,47 +54,64 @@ void usage()
{
fprintf(stdout,
"Usage:\n"
- " %s [OPTION…] [FILE] — %s\n"
+ " %s [OPTION…] — %s\n"
"\n"
"Help Options:\n"
- " -l, --loglevel Set libsigrok/libsigrokdecode loglevel\n"
- " -V, --version Show release version\n"
" -h, -?, --help Show help option\n"
+ "\n"
+ "Application Options:\n"
+ " -V, --version Show release version\n"
+ " -l, --loglevel Set libsigrok/libsigrokdecode loglevel\n"
+ " -i, --input-file Load input from file\n"
+ " -I, --input-format Input format\n"
"\n", PV_BIN_NAME, PV_DESCRIPTION);
}
int main(int argc, char *argv[])
{
int ret = 0;
- struct sr_context *sr_ctx = NULL;
- const char *open_file = NULL;
+ std::shared_ptr<sigrok::Context> context;
+ std::string open_file, open_file_format;
- QApplication a(argc, argv);
+ Application a(argc, argv);
- // Set some application metadata
- QApplication::setApplicationVersion(PV_VERSION_STRING);
- QApplication::setApplicationName("PulseView");
- QApplication::setOrganizationDomain("http://www.sigrok.org");
+#ifdef ANDROID
+ srau_init_environment();
+ pv::AndroidLogHandler::install_callbacks();
+ pv::AndroidAssetReader asset_reader;
+#endif
// Parse arguments
while (1) {
static const struct option long_options[] = {
- {"loglevel", required_argument, 0, 'l'},
- {"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'V'},
+ {"loglevel", required_argument, 0, 'l'},
+ {"input-file", required_argument, 0, 'i'},
+ {"input-format", required_argument, 0, 'I'},
{0, 0, 0, 0}
};
const int c = getopt_long(argc, argv,
- "l:Vh?", long_options, NULL);
+ "l:Vh?i:I:", long_options, nullptr);
if (c == -1)
break;
switch (c) {
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+
+ case 'V':
+ // Print version info
+ fprintf(stdout, "%s %s\n", PV_TITLE, PV_VERSION_STRING);
+ return 0;
+
case 'l':
{
const int loglevel = atoi(optarg);
- sr_log_loglevel_set(loglevel);
+ context->set_log_level(sigrok::LogLevel::get(loglevel));
#ifdef ENABLE_DECODE
srd_log_loglevel_set(loglevel);
@@ -98,35 +120,33 @@ int main(int argc, char *argv[])
break;
}
- case 'V':
- // Print version info
- fprintf(stdout, "%s %s\n", PV_TITLE, PV_VERSION_STRING);
- return 0;
+ case 'i':
+ open_file = optarg;
+ break;
- case 'h':
- case '?':
- usage();
- return 0;
+ case 'I':
+ open_file_format = optarg;
+ break;
}
}
if (argc - optind > 1) {
- fprintf(stderr, "Only one file can be openened.\n");
+ fprintf(stderr, "Only one file can be opened.\n");
return 1;
- } else if (argc - optind == 1)
+ } else if (argc - optind == 1) {
open_file = argv[argc - 1];
-
- // Initialise libsigrok
- if (sr_init(&sr_ctx) != SR_OK) {
- qDebug() << "ERROR: libsigrok init failed.";
- return 1;
}
+ // Initialise libsigrok
+ context = sigrok::Context::create();
+#ifdef ANDROID
+ context->set_resource_reader(&asset_reader);
+#endif
do {
#ifdef ENABLE_DECODE
// Initialise libsigrokdecode
- if (srd_init(NULL) != SRD_OK) {
+ if (srd_init(nullptr) != SRD_OK) {
qDebug() << "ERROR: libsigrokdecode init failed.";
break;
}
@@ -137,14 +157,15 @@ int main(int argc, char *argv[])
try {
// Create the device manager, initialise the drivers
- pv::DeviceManager device_manager(sr_ctx);
+ pv::DeviceManager device_manager(context);
// Initialise the main window
- pv::MainWindow w(device_manager, open_file);
+ pv::MainWindow w(device_manager,
+ open_file, open_file_format);
w.show();
#ifdef ENABLE_SIGNALS
- if(SignalHandler::prepare_signals()) {
+ if (SignalHandler::prepare_signals()) {
SignalHandler *const handler =
new SignalHandler(&w);
QObject::connect(handler,
@@ -153,7 +174,7 @@ int main(int argc, char *argv[])
QObject::connect(handler,
SIGNAL(term_received()),
&w, SLOT(close()));
- } else {
+ } else {
qWarning() <<
"Could not prepare signal handler.";
}
@@ -162,7 +183,7 @@ int main(int argc, char *argv[])
// Run the application
ret = a.exec();
- } catch(std::exception e) {
+ } catch (std::exception e) {
qDebug() << e.what();
}
@@ -173,9 +194,5 @@ int main(int argc, char *argv[])
} while (0);
- // Destroy libsigrok
- if (sr_ctx)
- sr_exit(sr_ctx);
-
return ret;
}
diff --git a/pulseview.qrc b/pulseview.qrc
index 98edc39..05cf878 100644
--- a/pulseview.qrc
+++ b/pulseview.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/" >
+ <file>icons/add-decoder.svg</file>
<file>icons/application-exit.png</file>
<file>icons/configure.png</file>
<file>icons/decoder-delete.svg</file>
@@ -7,15 +8,22 @@
<file>icons/decoder-shown.svg</file>
<file>icons/document-open.png</file>
<file>icons/document-save-as.png</file>
- <file>icons/probes.svg</file>
- <file>icons/sigrok-logo-notext.png</file>
+ <file>icons/channels.svg</file>
+ <file>icons/menu.svg</file>
+ <file>icons/sigrok-logo-notext.svg</file>
<file>icons/status-green.svg</file>
<file>icons/status-grey.svg</file>
<file>icons/status-red.svg</file>
+ <file>icons/show-cursors.svg</file>
<file>icons/trigger-change.svg</file>
<file>icons/trigger-falling.svg</file>
<file>icons/trigger-high.svg</file>
<file>icons/trigger-low.svg</file>
+ <file>icons/trigger-marker-change.svg</file>
+ <file>icons/trigger-marker-falling.svg</file>
+ <file>icons/trigger-marker-high.svg</file>
+ <file>icons/trigger-marker-low.svg</file>
+ <file>icons/trigger-marker-rising.svg</file>
<file>icons/trigger-none.svg</file>
<file>icons/trigger-rising.svg</file>
<file>icons/zoom-fit.png</file>
diff --git a/pv/data/signaldata.cpp b/pv/application.cpp
similarity index 57%
copy from pv/data/signaldata.cpp
copy to pv/application.cpp
index 04f1d3f..41f00f8 100644
--- a/pv/data/signaldata.cpp
+++ b/pv/application.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2014 Martin Ling <martin-sigrok at earth.li>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,32 +18,27 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "signaldata.h"
+#include "application.hpp"
+#include "config.h"
-namespace pv {
-namespace data {
+#include <iostream>
-SignalData::SignalData() :
- _start_time(0),
- _samplerate(0)
+Application::Application(int &argc, char* argv[]) :
+ QApplication(argc, argv)
{
+ setApplicationVersion(PV_VERSION_STRING);
+ setApplicationName("PulseView");
+ setOrganizationName("sigrok");
+ setOrganizationDomain("sigrok.org");
}
-double SignalData::samplerate() const
+bool Application::notify(QObject *receiver, QEvent *event)
{
- return _samplerate;
+ try {
+ return QApplication::notify(receiver, event);
+ } catch (std::exception& e) {
+ std::cerr << "Caught exception: " << e.what() << std::endl;
+ exit(1);
+ return false;
+ }
}
-
-void SignalData::set_samplerate(double samplerate)
-{
- _samplerate = samplerate;
- clear();
-}
-
-double SignalData::get_start_time() const
-{
- return _start_time;
-}
-
-} // namespace data
-} // namespace pv
diff --git a/pv/view/marginwidget.cpp b/pv/application.hpp
similarity index 69%
copy from pv/view/marginwidget.cpp
copy to pv/application.hpp
index 539551d..8d0ebd4 100644
--- a/pv/view/marginwidget.cpp
+++ b/pv/application.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2014 Martin Ling <martin-sigrok at earth.li>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,18 +18,17 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "view.h"
+#ifndef PULSEVIEW_PV_APPLICATION_HPP
+#define PULSEVIEW_PV_APPLICATION_HPP
-#include "marginwidget.h"
+#include <QApplication>
-namespace pv {
-namespace view {
-
-MarginWidget::MarginWidget(View &parent) :
- QWidget(&parent),
- _view(parent)
+class Application : public QApplication
{
-}
+public:
+ Application(int &argc, char* argv[]);
+private:
+ bool notify(QObject *receiver, QEvent *event);
+};
-} // namespace view
-} // namespace pv
+#endif // PULSEVIEW_PV_APPLICATION_HPP
diff --git a/pv/prop/binding/binding.cpp b/pv/binding/binding.cpp
similarity index 71%
rename from pv/prop/binding/binding.cpp
rename to pv/binding/binding.cpp
index 3a6c73f..f2a28d3 100644
--- a/pv/prop/binding/binding.cpp
+++ b/pv/binding/binding.cpp
@@ -18,28 +18,27 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <boost/foreach.hpp>
+#include <cassert>
#include <QFormLayout>
-#include <pv/prop/property.h>
+#include <pv/prop/property.hpp>
-#include "binding.h"
+#include "binding.hpp"
-using boost::shared_ptr;
+using std::shared_ptr;
namespace pv {
-namespace prop {
namespace binding {
-const std::vector< boost::shared_ptr<Property> >& Binding::properties()
+const std::vector< std::shared_ptr<prop::Property> >& Binding::properties()
{
- return _properties;
+ return properties_;
}
void Binding::commit()
{
- BOOST_FOREACH(shared_ptr<pv::prop::Property> p, _properties) {
+ for (shared_ptr<pv::prop::Property> p : properties_) {
assert(p);
p->commit();
}
@@ -50,8 +49,7 @@ void Binding::add_properties_to_form(QFormLayout *layout,
{
assert(layout);
- BOOST_FOREACH(shared_ptr<pv::prop::Property> p, _properties)
- {
+ for (shared_ptr<pv::prop::Property> p : properties_) {
assert(p);
QWidget *const widget = p->get_widget(layout->parentWidget(),
@@ -73,22 +71,21 @@ QWidget* Binding::get_property_form(QWidget *parent,
return form;
}
-QString Binding::print_gvariant(GVariant *const gvar)
+QString Binding::print_gvariant(Glib::VariantBase gvar)
{
QString s;
- if (g_variant_is_of_type(gvar, G_VARIANT_TYPE("s")))
- s = QString::fromUtf8(g_variant_get_string(gvar, NULL));
+ if (!gvar.gobj())
+ s = QString::fromStdString("(null)");
+ else if (gvar.is_of_type(Glib::VariantType("s")))
+ s = QString::fromStdString(
+ Glib::VariantBase::cast_dynamic<Glib::Variant<std::string>>(
+ gvar).get());
else
- {
- gchar *const text = g_variant_print(gvar, FALSE);
- s = QString::fromUtf8(text);
- g_free(text);
- }
+ s = QString::fromStdString(gvar.print());
return s;
}
} // binding
-} // prop
} // pv
diff --git a/pv/prop/binding/binding.h b/pv/binding/binding.hpp
similarity index 71%
rename from pv/prop/binding/binding.h
rename to pv/binding/binding.hpp
index 89c1acb..4854bd6 100644
--- a/pv/prop/binding/binding.h
+++ b/pv/binding/binding.hpp
@@ -18,13 +18,17 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_BINDING_BINDING_H
-#define PULSEVIEW_PV_PROP_BINDING_BINDING_H
+#ifndef PULSEVIEW_PV_BINDING_BINDING_HPP
+#define PULSEVIEW_PV_BINDING_BINDING_HPP
#include <glib.h>
+// Suppress warnings due to use of deprecated std::auto_ptr<> by glibmm.
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <glibmm.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
#include <vector>
-#include <boost/shared_ptr.hpp>
+#include <memory>
#include <QString>
@@ -32,16 +36,17 @@ class QFormLayout;
class QWidget;
namespace pv {
-namespace prop {
+namespace prop {
class Property;
+}
namespace binding {
class Binding
{
public:
- const std::vector< boost::shared_ptr<Property> >& properties();
+ const std::vector< std::shared_ptr<prop::Property> >& properties();
void commit();
@@ -51,14 +56,13 @@ public:
QWidget* get_property_form(QWidget *parent,
bool auto_commit = false) const;
- static QString print_gvariant(GVariant *const gvar);
+ static QString print_gvariant(Glib::VariantBase gvar);
protected:
- std::vector< boost::shared_ptr<Property> > _properties;
+ std::vector< std::shared_ptr<prop::Property> > properties_;
};
} // binding
-} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_BINDING_BINDING_H
+#endif // PULSEVIEW_PV_BINDING_BINDING_HPP
diff --git a/pv/prop/binding/decoderoptions.cpp b/pv/binding/decoder.cpp
similarity index 59%
rename from pv/prop/binding/decoderoptions.cpp
rename to pv/binding/decoder.cpp
index 063daf8..7d29e2f 100644
--- a/pv/prop/binding/decoderoptions.cpp
+++ b/pv/binding/decoder.cpp
@@ -20,108 +20,106 @@
#include <libsigrokdecode/libsigrokdecode.h>
-#include "decoderoptions.h"
+#include "decoder.hpp"
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
#include <boost/none_t.hpp>
-#include <pv/data/decoderstack.h>
-#include <pv/data/decode/decoder.h>
-#include <pv/prop/double.h>
-#include <pv/prop/enum.h>
-#include <pv/prop/int.h>
-#include <pv/prop/string.h>
+#include <pv/data/decoderstack.hpp>
+#include <pv/data/decode/decoder.hpp>
+#include <pv/prop/double.hpp>
+#include <pv/prop/enum.hpp>
+#include <pv/prop/int.hpp>
+#include <pv/prop/string.hpp>
-using boost::bind;
using boost::none;
-using boost::shared_ptr;
using std::make_pair;
using std::map;
using std::pair;
+using std::shared_ptr;
using std::string;
using std::vector;
+using pv::prop::Double;
+using pv::prop::Enum;
+using pv::prop::Int;
+using pv::prop::Property;
+using pv::prop::String;
+
namespace pv {
-namespace prop {
namespace binding {
-DecoderOptions::DecoderOptions(
+Decoder::Decoder(
shared_ptr<pv::data::DecoderStack> decoder_stack,
shared_ptr<data::decode::Decoder> decoder) :
- _decoder_stack(decoder_stack),
- _decoder(decoder)
+ decoder_stack_(decoder_stack),
+ decoder_(decoder)
{
- assert(_decoder);
+ assert(decoder_);
- const srd_decoder *const dec = _decoder->decoder();
+ const srd_decoder *const dec = decoder_->decoder();
assert(dec);
- for (GSList *l = dec->options; l; l = l->next)
- {
+ for (GSList *l = dec->options; l; l = l->next) {
const srd_decoder_option *const opt =
(srd_decoder_option*)l->data;
const QString name = QString::fromUtf8(opt->desc);
- const Property::Getter getter = bind(
- &DecoderOptions::getter, this, opt->id);
- const Property::Setter setter = bind(
- &DecoderOptions::setter, this, opt->id, _1);
+ const Property::Getter get = [&, opt]() {
+ return getter(opt->id); };
+ const Property::Setter set = [&, opt](Glib::VariantBase value) {
+ setter(opt->id, value); };
shared_ptr<Property> prop;
if (opt->values)
- prop = bind_enum(name, opt, getter, setter);
+ prop = bind_enum(name, opt, get, set);
else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d")))
prop = shared_ptr<Property>(new Double(name, 2, "",
- none, none, getter, setter));
+ none, none, get, set));
else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("x")))
prop = shared_ptr<Property>(
- new Int(name, "", none, getter, setter));
+ new Int(name, "", none, get, set));
else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("s")))
prop = shared_ptr<Property>(
- new String(name, getter, setter));
+ new String(name, get, set));
else
continue;
- _properties.push_back(prop);
+ properties_.push_back(prop);
}
}
-shared_ptr<Property> DecoderOptions::bind_enum(
+shared_ptr<Property> Decoder::bind_enum(
const QString &name, const srd_decoder_option *option,
Property::Getter getter, Property::Setter setter)
{
- vector< pair<GVariant*, QString> > values;
+ vector< pair<Glib::VariantBase, QString> > values;
for (GSList *l = option->values; l; l = l->next) {
- GVariant *const var = (GVariant*)l->data;
- assert(var);
+ Glib::VariantBase var = Glib::VariantBase((GVariant*)l->data, true);
values.push_back(make_pair(var, print_gvariant(var)));
}
return shared_ptr<Property>(new Enum(name, values, getter, setter));
}
-GVariant* DecoderOptions::getter(const char *id)
+Glib::VariantBase Decoder::getter(const char *id)
{
- GVariant *val = NULL;
+ GVariant *val = nullptr;
- assert(_decoder);
+ assert(decoder_);
// Get the value from the hash table if it is already present
- const map<string, GVariant*>& options = _decoder->options();
- map<string, GVariant*>::const_iterator iter = options.find(id);
+ const map<string, GVariant*>& options = decoder_->options();
+ const auto iter = options.find(id);
if (iter != options.end())
val = (*iter).second;
- else
- {
- assert(_decoder->decoder());
+ else {
+ assert(decoder_->decoder());
// Get the default value if not
- for (GSList *l = _decoder->decoder()->options; l; l = l->next)
- {
+ for (GSList *l = decoder_->decoder()->options; l; l = l->next) {
const srd_decoder_option *const opt =
(srd_decoder_option*)l->data;
if (strcmp(opt->id, id) == 0) {
@@ -132,20 +130,19 @@ GVariant* DecoderOptions::getter(const char *id)
}
if (val)
- g_variant_ref(val);
-
- return val;
+ return Glib::VariantBase(val, true);
+ else
+ return Glib::VariantBase();
}
-void DecoderOptions::setter(const char *id, GVariant *value)
+void Decoder::setter(const char *id, Glib::VariantBase value)
{
- assert(_decoder);
- _decoder->set_option(id, value);
+ assert(decoder_);
+ decoder_->set_option(id, value.gobj());
- assert(_decoder_stack);
- _decoder_stack->begin_decode();
+ assert(decoder_stack_);
+ decoder_stack_->begin_decode();
}
} // binding
-} // prop
} // pv
diff --git a/pv/prop/binding/decoderoptions.h b/pv/binding/decoder.hpp
similarity index 60%
rename from pv/prop/binding/decoderoptions.h
rename to pv/binding/decoder.hpp
index 6dec9c2..107ac15 100644
--- a/pv/prop/binding/decoderoptions.h
+++ b/pv/binding/decoder.hpp
@@ -18,12 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_BINDING_DECODEROPTIONS_H
-#define PULSEVIEW_PV_PROP_BINDING_DECODEROPTIONS_H
+#ifndef PULSEVIEW_PV_BINDING_DECODER_HPP
+#define PULSEVIEW_PV_BINDING_DECODER_HPP
-#include "binding.h"
+#include "binding.hpp"
-#include <pv/prop/property.h>
+#include <pv/prop/property.hpp>
struct srd_decoder_option;
@@ -36,31 +36,29 @@ class Decoder;
}
}
-namespace prop {
namespace binding {
-class DecoderOptions : public Binding
+class Decoder : public Binding
{
public:
- DecoderOptions(boost::shared_ptr<pv::data::DecoderStack> decoder_stack,
- boost::shared_ptr<pv::data::decode::Decoder> decoder);
+ Decoder(std::shared_ptr<pv::data::DecoderStack> decoder_stack,
+ std::shared_ptr<pv::data::decode::Decoder> decoder);
private:
- static boost::shared_ptr<Property> bind_enum(const QString &name,
+ static std::shared_ptr<prop::Property> bind_enum(const QString &name,
const srd_decoder_option *option,
- Property::Getter getter, Property::Setter setter);
+ prop::Property::Getter getter, prop::Property::Setter setter);
- GVariant* getter(const char *id);
+ Glib::VariantBase getter(const char *id);
- void setter(const char *id, GVariant *value);
+ void setter(const char *id, Glib::VariantBase value);
private:
- boost::shared_ptr<pv::data::DecoderStack> _decoder_stack;
- boost::shared_ptr<pv::data::decode::Decoder> _decoder;
+ std::shared_ptr<pv::data::DecoderStack> decoder_stack_;
+ std::shared_ptr<pv::data::decode::Decoder> decoder_;
};
} // binding
-} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_BINDING_DECODEROPTIONS_H
+#endif // PULSEVIEW_PV_BINDING_DECODER_HPP
diff --git a/pv/binding/device.cpp b/pv/binding/device.cpp
new file mode 100644
index 0000000..026ca16
--- /dev/null
+++ b/pv/binding/device.cpp
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include <QDebug>
+
+#include "device.hpp"
+
+#include <pv/prop/bool.hpp>
+#include <pv/prop/double.hpp>
+#include <pv/prop/enum.hpp>
+#include <pv/prop/int.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using boost::optional;
+using std::function;
+using std::make_pair;
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+using sigrok::Capability;
+using sigrok::Configurable;
+using sigrok::ConfigKey;
+using sigrok::Error;
+
+using pv::prop::Bool;
+using pv::prop::Double;
+using pv::prop::Enum;
+using pv::prop::Int;
+using pv::prop::Property;
+
+namespace pv {
+namespace binding {
+
+Device::Device(shared_ptr<sigrok::Configurable> configurable) :
+ configurable_(configurable)
+{
+
+ auto keys = configurable->config_keys();
+
+ for (auto key : keys) {
+
+ auto capabilities = configurable->config_capabilities(key);
+
+ if (!capabilities.count(Capability::GET) ||
+ !capabilities.count(Capability::SET))
+ continue;
+
+ string name_str;
+ try {
+ name_str = key->description();
+ } catch (Error e) {
+ name_str = key->name();
+ }
+
+ const QString name = QString::fromStdString(name_str);
+
+ const Property::Getter get = [&, key]() {
+ return configurable_->config_get(key); };
+ const Property::Setter set = [&, key](Glib::VariantBase value) {
+ configurable_->config_set(key, value);
+ config_changed();
+ };
+
+ switch (key->id()) {
+ case SR_CONF_SAMPLERATE:
+ // Sample rate values are not bound because they are shown
+ // in the MainBar
+ break;
+
+ case SR_CONF_CAPTURE_RATIO:
+ bind_int(name, "%", pair<int64_t, int64_t>(0, 100),
+ get, set);
+ break;
+
+ case SR_CONF_PATTERN_MODE:
+ case SR_CONF_BUFFERSIZE:
+ case SR_CONF_TRIGGER_SOURCE:
+ case SR_CONF_TRIGGER_SLOPE:
+ case SR_CONF_FILTER:
+ case SR_CONF_COUPLING:
+ case SR_CONF_CLOCK_EDGE:
+ bind_enum(name, key, capabilities, get, set);
+ break;
+
+ case SR_CONF_EXTERNAL_CLOCK:
+ case SR_CONF_RLE:
+ case SR_CONF_POWER_OFF:
+ bind_bool(name, get, set);
+ break;
+
+ case SR_CONF_TIMEBASE:
+ bind_enum(name, key, capabilities, get, set, print_timebase);
+ break;
+
+ case SR_CONF_VDIV:
+ bind_enum(name, key, capabilities, get, set, print_vdiv);
+ break;
+
+ case SR_CONF_VOLTAGE_THRESHOLD:
+ bind_enum(name, key, capabilities, get, set, print_voltage_threshold);
+ break;
+
+ case SR_CONF_PROBE_FACTOR:
+ bind_int(name, "", pair<int64_t, int64_t>(1, 500), get, set);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void Device::bind_bool(const QString &name,
+ Property::Getter getter, Property::Setter setter)
+{
+ assert(configurable_);
+ properties_.push_back(shared_ptr<Property>(new Bool(
+ name, getter, setter)));
+}
+
+void Device::bind_enum(const QString &name,
+ const ConfigKey *key, std::set<const Capability *> capabilities,
+ Property::Getter getter,
+ Property::Setter setter, function<QString (Glib::VariantBase)> printer)
+{
+ Glib::VariantBase gvar;
+ vector< pair<Glib::VariantBase, QString> > values;
+
+ assert(configurable_);
+
+ if (!capabilities.count(Capability::LIST))
+ return;
+
+ Glib::VariantIter iter(configurable_->config_list(key));
+ while ((iter.next_value(gvar)))
+ values.push_back(make_pair(gvar, printer(gvar)));
+
+ properties_.push_back(shared_ptr<Property>(new Enum(name, values,
+ getter, setter)));
+}
+
+void Device::bind_int(const QString &name, QString suffix,
+ optional< std::pair<int64_t, int64_t> > range,
+ Property::Getter getter, Property::Setter setter)
+{
+ assert(configurable_);
+
+ properties_.push_back(shared_ptr<Property>(new Int(name, suffix, range,
+ getter, setter)));
+}
+
+QString Device::print_timebase(Glib::VariantBase gvar)
+{
+ uint64_t p, q;
+ g_variant_get(gvar.gobj(), "(tt)", &p, &q);
+ return QString::fromUtf8(sr_period_string(p * q));
+}
+
+QString Device::print_vdiv(Glib::VariantBase gvar)
+{
+ uint64_t p, q;
+ g_variant_get(gvar.gobj(), "(tt)", &p, &q);
+ return QString::fromUtf8(sr_voltage_string(p, q));
+}
+
+QString Device::print_voltage_threshold(Glib::VariantBase gvar)
+{
+ gdouble lo, hi;
+ g_variant_get(gvar.gobj(), "(dd)", &lo, &hi);
+ return QString("L<%1V H>%2V").arg(lo, 0, 'f', 1).arg(hi, 0, 'f', 1);
+}
+
+} // binding
+} // pv
diff --git a/pv/binding/device.hpp b/pv/binding/device.hpp
new file mode 100644
index 0000000..563132a
--- /dev/null
+++ b/pv/binding/device.hpp
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_BINDING_DEVICE_HPP
+#define PULSEVIEW_PV_BINDING_DEVICE_HPP
+
+#include <boost/optional.hpp>
+
+#include <QObject>
+#include <QString>
+
+#include "binding.hpp"
+
+#include <pv/prop/property.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+namespace pv {
+
+namespace binding {
+
+class Device : public QObject, public Binding
+{
+ Q_OBJECT
+
+public:
+ Device(std::shared_ptr<sigrok::Configurable> configurable);
+
+Q_SIGNALS:
+ void config_changed();
+
+private:
+ void bind_bool(const QString &name,
+ prop::Property::Getter getter, prop::Property::Setter setter);
+ void bind_enum(const QString &name,
+ const sigrok::ConfigKey *key,
+ std::set<const sigrok::Capability *> capabilities,
+ prop::Property::Getter getter, prop::Property::Setter setter,
+ std::function<QString (Glib::VariantBase)> printer = print_gvariant);
+ void bind_int(const QString &name, QString suffix,
+ boost::optional< std::pair<int64_t, int64_t> > range,
+ prop::Property::Getter getter, prop::Property::Setter setter);
+
+ static QString print_timebase(Glib::VariantBase gvar);
+ static QString print_vdiv(Glib::VariantBase gvar);
+ static QString print_voltage_threshold(Glib::VariantBase gvar);
+
+protected:
+ std::shared_ptr<sigrok::Configurable> configurable_;
+};
+
+} // binding
+} // pv
+
+#endif // PULSEVIEW_PV_BINDING_DEVICE_HPP
diff --git a/pv/binding/inputoutput.cpp b/pv/binding/inputoutput.cpp
new file mode 100644
index 0000000..30548a8
--- /dev/null
+++ b/pv/binding/inputoutput.cpp
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cassert>
+#include <iostream>
+#include <utility>
+
+#include <boost/none_t.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <pv/prop/bool.hpp>
+#include <pv/prop/double.hpp>
+#include <pv/prop/enum.hpp>
+#include <pv/prop/int.hpp>
+#include <pv/prop/string.hpp>
+
+#include "inputoutput.hpp"
+
+using boost::none;
+
+using std::make_pair;
+using std::map;
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+using Glib::VariantBase;
+using Glib::VariantType;
+
+using sigrok::Option;
+
+using pv::prop::Bool;
+using pv::prop::Double;
+using pv::prop::Enum;
+using pv::prop::Int;
+using pv::prop::Property;
+using pv::prop::String;
+
+namespace pv {
+namespace binding {
+
+InputOutput::InputOutput(
+ const map<string, shared_ptr<Option>> &options)
+{
+ for (pair<string, shared_ptr<Option>> o : options) {
+ const shared_ptr<Option> &opt = o.second;
+ assert(opt);
+
+ const QString name = QString::fromStdString(opt->name());
+ const VariantBase def_val = opt->default_value();
+ const vector<VariantBase> values = opt->values();
+
+ options_[opt->id()] = def_val;
+
+ const Property::Getter get = [&, opt]() {
+ return options_[opt->id()]; };
+ const Property::Setter set = [&, opt](VariantBase value) {
+ options_[opt->id()] = value; };
+
+ shared_ptr<Property> prop;
+
+ if (!opt->values().empty())
+ prop = bind_enum(name, values, get, set);
+ else if (def_val.is_of_type(VariantType("b")))
+ prop = shared_ptr<Property>(new Bool(name, get, set));
+ else if (def_val.is_of_type(VariantType("d")))
+ prop = shared_ptr<Property>(new Double(name, 2, "",
+ none, none, get, set));
+ else if (def_val.is_of_type(VariantType("i")) ||
+ def_val.is_of_type(VariantType("t")) ||
+ def_val.is_of_type(VariantType("u")))
+ prop = shared_ptr<Property>(
+ new Int(name, "", none, get, set));
+ else if (def_val.is_of_type(VariantType("s")))
+ prop = shared_ptr<Property>(
+ new String(name, get, set));
+ else
+ continue;
+
+ properties_.push_back(prop);
+ }
+}
+
+const map<string, VariantBase>& InputOutput::options() const
+{
+ return options_;
+}
+
+shared_ptr<Property> InputOutput::bind_enum(
+ const QString &name, const vector<VariantBase> &values,
+ Property::Getter getter, Property::Setter setter)
+{
+ vector< pair<VariantBase, QString> > enum_vals;
+ for (VariantBase var : values)
+ enum_vals.push_back(make_pair(var, print_gvariant(var)));
+ return shared_ptr<Property>(new Enum(name, enum_vals, getter, setter));
+}
+
+} // namespace binding
+} // namespace pv
diff --git a/pv/binding/inputoutput.hpp b/pv/binding/inputoutput.hpp
new file mode 100644
index 0000000..628b1c4
--- /dev/null
+++ b/pv/binding/inputoutput.hpp
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_BINDING_INPUTOUTPUT_HPP
+#define PULSEVIEW_PV_BINDING_INPUTOUTPUT_HPP
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "binding.hpp"
+
+#include <pv/prop/property.hpp>
+
+namespace sigrok {
+class Option;
+}
+
+namespace pv {
+namespace binding {
+
+/**
+ * A binding of glibmm variants for sigrok input and output options.
+ */
+class InputOutput : public Binding
+{
+public:
+ /**
+ * Constructs a new @c InputOutput binding.
+ * @param options the map of options to use as a template.
+ */
+ InputOutput(
+ const std::map<std::string, std::shared_ptr<sigrok::Option>>
+ &options);
+
+ /**
+ * Gets the map of selected options.
+ * @return the options.
+ */
+ const std::map<std::string, Glib::VariantBase>& options() const;
+
+private:
+ /**
+ * A helper function to bind an option list to and enum property.
+ * @param name the name of the property.
+ * @param values the list of values.
+ * @param getter the getter that will read the values out of the map.
+ * @param setter the setter that will set the values into the map.
+ */
+ std::shared_ptr<prop::Property> bind_enum(const QString &name,
+ const std::vector<Glib::VariantBase> &values,
+ prop::Property::Getter getter, prop::Property::Setter setter);
+
+private:
+ /**
+ * The current map of options.
+ */
+ std::map<std::string, Glib::VariantBase> options_;
+};
+
+} // binding
+} // pv
+
+#endif // PULSEVIEW_PV_BINDING_INPUTOUTPUT_H
diff --git a/pv/data/analog.cpp b/pv/data/analog.cpp
index 21ddb5f..ca2659b 100644
--- a/pv/data/analog.cpp
+++ b/pv/data/analog.cpp
@@ -18,14 +18,15 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <boost/foreach.hpp>
+#include <cassert>
-#include "analog.h"
-#include "analogsnapshot.h"
+#include "analog.hpp"
+#include "analogsegment.hpp"
-using boost::shared_ptr;
using std::deque;
using std::max;
+using std::shared_ptr;
+using std::vector;
namespace pv {
namespace data {
@@ -35,25 +36,31 @@ Analog::Analog() :
{
}
-void Analog::push_snapshot(shared_ptr<AnalogSnapshot> &snapshot)
+void Analog::push_segment(shared_ptr<AnalogSegment> &segment)
{
- _snapshots.push_front(snapshot);
+ segments_.push_front(segment);
}
-deque< shared_ptr<AnalogSnapshot> >& Analog::get_snapshots()
+const deque< shared_ptr<AnalogSegment> >& Analog::analog_segments() const
{
- return _snapshots;
+ return segments_;
+}
+
+vector< shared_ptr<Segment> > Analog::segments() const
+{
+ return vector< shared_ptr<Segment> >(
+ segments_.begin(), segments_.end());
}
void Analog::clear()
{
- _snapshots.clear();
+ segments_.clear();
}
-uint64_t Analog::get_max_sample_count() const
+uint64_t Analog::max_sample_count() const
{
uint64_t l = 0;
- BOOST_FOREACH(const boost::shared_ptr<AnalogSnapshot> s, _snapshots) {
+ for (const std::shared_ptr<AnalogSegment> s : segments_) {
assert(s);
l = max(l, s->get_sample_count());
}
diff --git a/pv/data/analog.h b/pv/data/analog.hpp
similarity index 68%
rename from pv/data/analog.h
rename to pv/data/analog.hpp
index 42e3167..58f5f3f 100644
--- a/pv/data/analog.h
+++ b/pv/data/analog.hpp
@@ -18,39 +18,41 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_ANALOG_H
-#define PULSEVIEW_PV_DATA_ANALOG_H
+#ifndef PULSEVIEW_PV_DATA_ANALOG_HPP
+#define PULSEVIEW_PV_DATA_ANALOG_HPP
-#include "signaldata.h"
+#include "signaldata.hpp"
-#include <boost/shared_ptr.hpp>
#include <deque>
+#include <memory>
namespace pv {
namespace data {
-class AnalogSnapshot;
+class AnalogSegment;
class Analog : public SignalData
{
public:
Analog();
- void push_snapshot(
- boost::shared_ptr<AnalogSnapshot> &snapshot);
+ void push_segment(
+ std::shared_ptr<AnalogSegment> &segment);
- std::deque< boost::shared_ptr<AnalogSnapshot> >&
- get_snapshots();
+ const std::deque< std::shared_ptr<AnalogSegment> >&
+ analog_segments() const;
+
+ std::vector< std::shared_ptr<Segment> > segments() const;
void clear();
- uint64_t get_max_sample_count() const;
+ uint64_t max_sample_count() const;
private:
- std::deque< boost::shared_ptr<AnalogSnapshot> > _snapshots;
+ std::deque< std::shared_ptr<AnalogSegment> > segments_;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_ANALOG_H
+#endif // PULSEVIEW_PV_DATA_ANALOG_HPP
diff --git a/pv/data/analogsnapshot.cpp b/pv/data/analogsegment.cpp
similarity index 64%
rename from pv/data/analogsnapshot.cpp
rename to pv/data/analogsegment.cpp
index 4d4f5d6..cd4384d 100644
--- a/pv/data/analogsnapshot.cpp
+++ b/pv/data/analogsegment.cpp
@@ -23,16 +23,14 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
-#include <math.h>
+#include <cmath>
#include <algorithm>
-#include <boost/foreach.hpp>
+#include "analogsegment.hpp"
-#include "analogsnapshot.h"
-
-using boost::lock_guard;
-using boost::recursive_mutex;
+using std::lock_guard;
+using std::recursive_mutex;
using std::max;
using std::max_element;
using std::min;
@@ -41,76 +39,77 @@ using std::min_element;
namespace pv {
namespace data {
-const int AnalogSnapshot::EnvelopeScalePower = 4;
-const int AnalogSnapshot::EnvelopeScaleFactor = 1 << EnvelopeScalePower;
-const float AnalogSnapshot::LogEnvelopeScaleFactor =
+const int AnalogSegment::EnvelopeScalePower = 4;
+const int AnalogSegment::EnvelopeScaleFactor = 1 << EnvelopeScalePower;
+const float AnalogSegment::LogEnvelopeScaleFactor =
logf(EnvelopeScaleFactor);
-const uint64_t AnalogSnapshot::EnvelopeDataUnit = 64*1024; // bytes
+const uint64_t AnalogSegment::EnvelopeDataUnit = 64*1024; // bytes
-AnalogSnapshot::AnalogSnapshot(const uint64_t expected_num_samples) :
- Snapshot(sizeof(float))
+AnalogSegment::AnalogSegment(
+ uint64_t samplerate, const uint64_t expected_num_samples) :
+ Segment(samplerate, sizeof(float))
{
set_capacity(expected_num_samples);
- lock_guard<recursive_mutex> lock(_mutex);
- memset(_envelope_levels, 0, sizeof(_envelope_levels));
+ lock_guard<recursive_mutex> lock(mutex_);
+ memset(envelope_levels_, 0, sizeof(envelope_levels_));
}
-AnalogSnapshot::~AnalogSnapshot()
+AnalogSegment::~AnalogSegment()
{
- lock_guard<recursive_mutex> lock(_mutex);
- BOOST_FOREACH(Envelope &e, _envelope_levels)
+ lock_guard<recursive_mutex> lock(mutex_);
+ for (Envelope &e : envelope_levels_)
free(e.samples);
}
-void AnalogSnapshot::append_interleaved_samples(const float *data,
+void AnalogSegment::append_interleaved_samples(const float *data,
size_t sample_count, size_t stride)
{
- assert(_unit_size == sizeof(float));
+ assert(unit_size_ == sizeof(float));
- lock_guard<recursive_mutex> lock(_mutex);
+ lock_guard<recursive_mutex> lock(mutex_);
- _data = realloc(_data, (_sample_count + sample_count) * sizeof(float));
+ // If we're out of memory, this will throw std::bad_alloc
+ data_.resize((sample_count_ + sample_count) * sizeof(float));
- float *dst = (float*)_data + _sample_count;
+ float *dst = (float*)data_.data() + sample_count_;
const float *dst_end = dst + sample_count;
- while (dst != dst_end)
- {
+ while (dst != dst_end) {
*dst++ = *data;
data += stride;
}
- _sample_count += sample_count;
+ sample_count_ += sample_count;
// Generate the first mip-map from the data
append_payload_to_envelope_levels();
}
-const float* AnalogSnapshot::get_samples(
+const float* AnalogSegment::get_samples(
int64_t start_sample, int64_t end_sample) const
{
assert(start_sample >= 0);
- assert(start_sample < (int64_t)_sample_count);
+ assert(start_sample < (int64_t)sample_count_);
assert(end_sample >= 0);
- assert(end_sample < (int64_t)_sample_count);
+ assert(end_sample < (int64_t)sample_count_);
assert(start_sample <= end_sample);
- lock_guard<recursive_mutex> lock(_mutex);
+ lock_guard<recursive_mutex> lock(mutex_);
float *const data = new float[end_sample - start_sample];
- memcpy(data, (float*)_data + start_sample, sizeof(float) *
+ memcpy(data, (float*)data_.data() + start_sample, sizeof(float) *
(end_sample - start_sample));
return data;
}
-void AnalogSnapshot::get_envelope_section(EnvelopeSection &s,
+void AnalogSegment::get_envelope_section(EnvelopeSection &s,
uint64_t start, uint64_t end, float min_length) const
{
assert(end <= get_sample_count());
assert(start <= end);
assert(min_length > 0);
- lock_guard<recursive_mutex> lock(_mutex);
+ lock_guard<recursive_mutex> lock(mutex_);
const unsigned int min_level = max((int)floorf(logf(min_length) /
LogEnvelopeScaleFactor) - 1, 0);
@@ -123,31 +122,30 @@ void AnalogSnapshot::get_envelope_section(EnvelopeSection &s,
s.scale = 1 << scale_power;
s.length = end - start;
s.samples = new EnvelopeSample[s.length];
- memcpy(s.samples, _envelope_levels[min_level].samples + start,
+ memcpy(s.samples, envelope_levels_[min_level].samples + start,
s.length * sizeof(EnvelopeSample));
}
-void AnalogSnapshot::reallocate_envelope(Envelope &e)
+void AnalogSegment::reallocate_envelope(Envelope &e)
{
const uint64_t new_data_length = ((e.length + EnvelopeDataUnit - 1) /
EnvelopeDataUnit) * EnvelopeDataUnit;
- if (new_data_length > e.data_length)
- {
+ if (new_data_length > e.data_length) {
e.data_length = new_data_length;
e.samples = (EnvelopeSample*)realloc(e.samples,
new_data_length * sizeof(EnvelopeSample));
}
}
-void AnalogSnapshot::append_payload_to_envelope_levels()
+void AnalogSegment::append_payload_to_envelope_levels()
{
- Envelope &e0 = _envelope_levels[0];
+ Envelope &e0 = envelope_levels_[0];
uint64_t prev_length;
EnvelopeSample *dest_ptr;
// Expand the data buffer to fit the new samples
prev_length = e0.length;
- e0.length = _sample_count / EnvelopeScaleFactor;
+ e0.length = sample_count_ / EnvelopeScaleFactor;
// Break off if there are no new samples to compute
if (e0.length == prev_length)
@@ -158,12 +156,11 @@ void AnalogSnapshot::append_payload_to_envelope_levels()
dest_ptr = e0.samples + prev_length;
// Iterate through the samples to populate the first level mipmap
- const float *const end_src_ptr = (float*)_data +
+ const float *const end_src_ptr = (float*)data_.data() +
e0.length * EnvelopeScaleFactor;
- for (const float *src_ptr = (float*)_data +
- prev_length * EnvelopeScaleFactor;
- src_ptr < end_src_ptr; src_ptr += EnvelopeScaleFactor)
- {
+ for (const float *src_ptr = (float*)data_.data() +
+ prev_length * EnvelopeScaleFactor;
+ src_ptr < end_src_ptr; src_ptr += EnvelopeScaleFactor) {
const EnvelopeSample sub_sample = {
*min_element(src_ptr, src_ptr + EnvelopeScaleFactor),
*max_element(src_ptr, src_ptr + EnvelopeScaleFactor),
@@ -173,10 +170,9 @@ void AnalogSnapshot::append_payload_to_envelope_levels()
}
// Compute higher level mipmaps
- for (unsigned int level = 1; level < ScaleStepCount; level++)
- {
- Envelope &e = _envelope_levels[level];
- const Envelope &el = _envelope_levels[level-1];
+ for (unsigned int level = 1; level < ScaleStepCount; level++) {
+ Envelope &e = envelope_levels_[level];
+ const Envelope &el = envelope_levels_[level-1];
// Expand the data buffer to fit the new samples
prev_length = e.length;
@@ -193,14 +189,12 @@ void AnalogSnapshot::append_payload_to_envelope_levels()
el.samples + prev_length * EnvelopeScaleFactor;
const EnvelopeSample *const end_dest_ptr = e.samples + e.length;
for (dest_ptr = e.samples + prev_length;
- dest_ptr < end_dest_ptr; dest_ptr++)
- {
+ dest_ptr < end_dest_ptr; dest_ptr++) {
const EnvelopeSample *const end_src_ptr =
src_ptr + EnvelopeScaleFactor;
EnvelopeSample sub_sample = *src_ptr++;
- while (src_ptr < end_src_ptr)
- {
+ while (src_ptr < end_src_ptr) {
sub_sample.min = min(sub_sample.min, src_ptr->min);
sub_sample.max = max(sub_sample.max, src_ptr->max);
src_ptr++;
diff --git a/pv/data/analogsnapshot.h b/pv/data/analogsegment.hpp
similarity index 81%
rename from pv/data/analogsnapshot.h
rename to pv/data/analogsegment.hpp
index b60c2ce..1a91b8b 100644
--- a/pv/data/analogsnapshot.h
+++ b/pv/data/analogsegment.hpp
@@ -18,22 +18,22 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_ANALOGSNAPSHOT_H
-#define PULSEVIEW_PV_DATA_ANALOGSNAPSHOT_H
+#ifndef PULSEVIEW_PV_DATA_ANALOGSEGMENT_HPP
+#define PULSEVIEW_PV_DATA_ANALOGSEGMENT_HPP
-#include "snapshot.h"
+#include "segment.hpp"
#include <utility>
#include <vector>
-namespace AnalogSnapshotTest {
-class Basic;
+namespace AnalogSegmentTest {
+struct Basic;
}
namespace pv {
namespace data {
-class AnalogSnapshot : public Snapshot
+class AnalogSegment : public Segment
{
public:
struct EnvelopeSample
@@ -66,9 +66,9 @@ private:
static const uint64_t EnvelopeDataUnit;
public:
- AnalogSnapshot(uint64_t expected_num_samples = 0);
+ AnalogSegment(uint64_t samplerate, uint64_t expected_num_samples = 0);
- virtual ~AnalogSnapshot();
+ virtual ~AnalogSegment();
void append_interleaved_samples(const float *data,
size_t sample_count, size_t stride);
@@ -85,12 +85,12 @@ private:
void append_payload_to_envelope_levels();
private:
- struct Envelope _envelope_levels[ScaleStepCount];
+ struct Envelope envelope_levels_[ScaleStepCount];
- friend class AnalogSnapshotTest::Basic;
+ friend struct AnalogSegmentTest::Basic;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_ANALOGSNAPSHOT_H
+#endif // PULSEVIEW_PV_DATA_ANALOGSEGMENT_HPP
diff --git a/pv/data/decode/annotation.cpp b/pv/data/decode/annotation.cpp
index b0517ae..9a16580 100644
--- a/pv/data/decode/annotation.cpp
+++ b/pv/data/decode/annotation.cpp
@@ -22,50 +22,51 @@ extern "C" {
#include <libsigrokdecode/libsigrokdecode.h>
}
+#include <cassert>
#include <vector>
-#include "annotation.h"
+#include "annotation.hpp"
namespace pv {
namespace data {
namespace decode {
Annotation::Annotation(const srd_proto_data *const pdata) :
- _start_sample(pdata->start_sample),
- _end_sample(pdata->end_sample)
+ start_sample_(pdata->start_sample),
+ end_sample_(pdata->end_sample)
{
assert(pdata);
const srd_proto_data_annotation *const pda =
(const srd_proto_data_annotation*)pdata->data;
assert(pda);
- _format = pda->ann_format;
+ format_ = pda->ann_class;
const char *const *annotations = (char**)pda->ann_text;
- while(*annotations) {
- _annotations.push_back(QString::fromUtf8(*annotations));
+ while (*annotations) {
+ annotations_.push_back(QString::fromUtf8(*annotations));
annotations++;
}
}
uint64_t Annotation::start_sample() const
{
- return _start_sample;
+ return start_sample_;
}
uint64_t Annotation::end_sample() const
{
- return _end_sample;
+ return end_sample_;
}
int Annotation::format() const
{
- return _format;
+ return format_;
}
const std::vector<QString>& Annotation::annotations() const
{
- return _annotations;
+ return annotations_;
}
} // namespace decode
diff --git a/pv/data/decode/annotation.h b/pv/data/decode/annotation.hpp
similarity index 83%
rename from pv/data/decode/annotation.h
rename to pv/data/decode/annotation.hpp
index 0d7fd5d..071f292 100644
--- a/pv/data/decode/annotation.h
+++ b/pv/data/decode/annotation.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_H
-#define PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_H
+#ifndef PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_HPP
+#define PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_HPP
#include <stdint.h>
@@ -42,14 +42,14 @@ public:
const std::vector<QString>& annotations() const;
private:
- uint64_t _start_sample;
- uint64_t _end_sample;
- int _format;
- std::vector<QString> _annotations;
+ uint64_t start_sample_;
+ uint64_t end_sample_;
+ int format_;
+ std::vector<QString> annotations_;
};
} // namespace decode
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_H
+#endif // PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_HPP
diff --git a/pv/data/decode/decoder.cpp b/pv/data/decode/decoder.cpp
index 44e474b..03f4f0e 100644
--- a/pv/data/decode/decoder.cpp
+++ b/pv/data/decode/decoder.cpp
@@ -18,16 +18,18 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <libsigrok/libsigrok.h>
+#include <cassert>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
#include <libsigrokdecode/libsigrokdecode.h>
-#include "decoder.h"
+#include "decoder.hpp"
-#include <pv/view/logicsignal.h>
+#include <pv/view/logicsignal.hpp>
-using boost::shared_ptr;
using std::set;
using std::map;
+using std::shared_ptr;
using std::string;
namespace pv {
@@ -35,63 +37,62 @@ namespace data {
namespace decode {
Decoder::Decoder(const srd_decoder *const dec) :
- _decoder(dec),
- _shown(true)
+ decoder_(dec),
+ shown_(true)
{
}
Decoder::~Decoder()
{
- for (map<string, GVariant*>::const_iterator i = _options.begin();
- i != _options.end(); i++)
+ for (auto i = options_.begin(); i != options_.end(); i++)
g_variant_unref((*i).second);
}
const srd_decoder* Decoder::decoder() const
{
- return _decoder;
+ return decoder_;
}
bool Decoder::shown() const
{
- return _shown;
+ return shown_;
}
void Decoder::show(bool show)
{
- _shown = show;
+ shown_ = show;
}
const map<const srd_channel*, shared_ptr<view::LogicSignal> >&
Decoder::channels() const
{
- return _probes;
+ return channels_;
}
-void Decoder::set_probes(std::map<const srd_channel*,
- boost::shared_ptr<view::LogicSignal> > probes)
+void Decoder::set_channels(std::map<const srd_channel*,
+ std::shared_ptr<view::LogicSignal> > channels)
{
- _probes = probes;
+ channels_ = channels;
}
const std::map<std::string, GVariant*>& Decoder::options() const
{
- return _options;
+ return options_;
}
void Decoder::set_option(const char *id, GVariant *value)
{
assert(value);
g_variant_ref(value);
- _options[id] = value;
+ options_[id] = value;
}
-bool Decoder::have_required_probes() const
+bool Decoder::have_required_channels() const
{
- for (GSList *l = _decoder->channels; l; l = l->next) {
+ for (GSList *l = decoder_->channels; l; l = l->next) {
const srd_channel *const pdch = (const srd_channel*)l->data;
assert(pdch);
- if (_probes.find(pdch) == _probes.end())
+ if (channels_.find(pdch) == channels_.end())
return false;
}
@@ -101,10 +102,7 @@ bool Decoder::have_required_probes() const
set< shared_ptr<pv::data::Logic> > Decoder::get_data()
{
set< shared_ptr<pv::data::Logic> > data;
- for(map<const srd_channel*, shared_ptr<view::LogicSignal> >::
- const_iterator i = _probes.begin();
- i != _probes.end(); i++)
- {
+ for (auto i = channels_.cbegin(); i != channels_.cend(); i++) {
shared_ptr<view::LogicSignal> signal((*i).second);
assert(signal);
data.insert(signal->logic_data());
@@ -113,13 +111,12 @@ set< shared_ptr<pv::data::Logic> > Decoder::get_data()
return data;
}
-srd_decoder_inst* Decoder::create_decoder_inst(srd_session *session, int unit_size) const
+srd_decoder_inst* Decoder::create_decoder_inst(srd_session *session) const
{
GHashTable *const opt_hash = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
- for (map<string, GVariant*>::const_iterator i = _options.begin();
- i != _options.end(); i++)
+ for (auto i = options_.cbegin(); i != options_.cend(); i++)
{
GVariant *const value = (*i).second;
g_variant_ref(value);
@@ -128,28 +125,26 @@ srd_decoder_inst* Decoder::create_decoder_inst(srd_session *session, int unit_si
}
srd_decoder_inst *const decoder_inst = srd_inst_new(
- session, _decoder->id, opt_hash);
+ session, decoder_->id, opt_hash);
g_hash_table_destroy(opt_hash);
- if(!decoder_inst)
- return NULL;
+ if (!decoder_inst)
+ return nullptr;
- // Setup the probes
- GHashTable *const probes = g_hash_table_new_full(g_str_hash,
+ // Setup the channels
+ GHashTable *const channels = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, (GDestroyNotify)g_variant_unref);
- for(map<const srd_channel*, shared_ptr<view::LogicSignal> >::
- const_iterator i = _probes.begin();
- i != _probes.end(); i++)
+ for (auto i = channels_.cbegin(); i != channels_.cend(); i++)
{
shared_ptr<view::LogicSignal> signal((*i).second);
GVariant *const gvar = g_variant_new_int32(
- signal->probe()->index);
+ signal->channel()->index());
g_variant_ref_sink(gvar);
- g_hash_table_insert(probes, (*i).first->id, gvar);
+ g_hash_table_insert(channels, (*i).first->id, gvar);
}
- srd_inst_channel_set_all(decoder_inst, probes, unit_size);
+ srd_inst_channel_set_all(decoder_inst, channels);
return decoder_inst;
}
diff --git a/pv/data/decode/decoder.h b/pv/data/decode/decoder.hpp
similarity index 70%
rename from pv/data/decode/decoder.h
rename to pv/data/decode/decoder.hpp
index dffefab..121286f 100644
--- a/pv/data/decode/decoder.h
+++ b/pv/data/decode/decoder.hpp
@@ -18,14 +18,13 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_DECODE_DECODER_H
-#define PULSEVIEW_PV_DATA_DECODE_DECODER_H
+#ifndef PULSEVIEW_PV_DATA_DECODE_DECODER_HPP
+#define PULSEVIEW_PV_DATA_DECODE_DECODER_HPP
#include <map>
+#include <memory>
#include <set>
-#include <boost/shared_ptr.hpp>
-
#include <glib.h>
struct srd_decoder;
@@ -58,33 +57,33 @@ public:
void show(bool show = true);
const std::map<const srd_channel*,
- boost::shared_ptr<view::LogicSignal> >& channels() const;
- void set_probes(std::map<const srd_channel*,
- boost::shared_ptr<view::LogicSignal> > probes);
+ std::shared_ptr<view::LogicSignal> >& channels() const;
+ void set_channels(std::map<const srd_channel*,
+ std::shared_ptr<view::LogicSignal> > channels);
const std::map<std::string, GVariant*>& options() const;
void set_option(const char *id, GVariant *value);
- bool have_required_probes() const;
+ bool have_required_channels() const;
srd_decoder_inst* create_decoder_inst(
- srd_session *session, int unit_size) const;
+ srd_session *session) const;
- std::set< boost::shared_ptr<pv::data::Logic> > get_data();
+ std::set< std::shared_ptr<pv::data::Logic> > get_data();
private:
- const srd_decoder *const _decoder;
+ const srd_decoder *const decoder_;
- bool _shown;
+ bool shown_;
- std::map<const srd_channel*, boost::shared_ptr<pv::view::LogicSignal> >
- _probes;
- std::map<std::string, GVariant*> _options;
+ std::map<const srd_channel*, std::shared_ptr<pv::view::LogicSignal> >
+ channels_;
+ std::map<std::string, GVariant*> options_;
};
} // namespace decode
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_DECODE_DECODER_H
+#endif // PULSEVIEW_PV_DATA_DECODE_DECODER_HPP
diff --git a/pv/data/decode/row.cpp b/pv/data/decode/row.cpp
index 2aabf0f..70a0609 100644
--- a/pv/data/decode/row.cpp
+++ b/pv/data/decode/row.cpp
@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "row.h"
+#include "row.hpp"
#include <libsigrokdecode/libsigrokdecode.h>
@@ -27,44 +27,44 @@ namespace data {
namespace decode {
Row::Row() :
- _decoder(NULL),
- _row(NULL)
+ decoder_(nullptr),
+ row_(nullptr)
{
}
Row::Row(const srd_decoder *decoder, const srd_decoder_annotation_row *row) :
- _decoder(decoder),
- _row(row)
+ decoder_(decoder),
+ row_(row)
{
}
const srd_decoder* Row::decoder() const
{
- return _decoder;
+ return decoder_;
}
const srd_decoder_annotation_row* Row::row() const
{
- return _row;
+ return row_;
}
const QString Row::title() const
{
- if (_decoder && _decoder->name && _row && _row->desc)
+ if (decoder_ && decoder_->name && row_ && row_->desc)
return QString("%1: %2")
- .arg(QString::fromUtf8(_decoder->name))
- .arg(QString::fromUtf8(_row->desc));
- if (_decoder && _decoder->name)
- return QString::fromUtf8(_decoder->name);
- if (_row && _row->desc)
- return QString::fromUtf8(_row->desc);
+ .arg(QString::fromUtf8(decoder_->name))
+ .arg(QString::fromUtf8(row_->desc));
+ if (decoder_ && decoder_->name)
+ return QString::fromUtf8(decoder_->name);
+ if (row_ && row_->desc)
+ return QString::fromUtf8(row_->desc);
return QString();
}
bool Row::operator<(const Row &other) const
{
- return (_decoder < other._decoder) ||
- (_decoder == other._decoder && _row < other._row);
+ return (decoder_ < other.decoder_) ||
+ (decoder_ == other.decoder_ && row_ < other.row_);
}
} // decode
diff --git a/pv/data/decode/row.h b/pv/data/decode/row.hpp
similarity index 81%
rename from pv/data/decode/row.h
rename to pv/data/decode/row.hpp
index 6c05ac4..9c81b87 100644
--- a/pv/data/decode/row.h
+++ b/pv/data/decode/row.hpp
@@ -18,12 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_DECODE_ROW_H
-#define PULSEVIEW_PV_DATA_DECODE_ROW_H
+#ifndef PULSEVIEW_PV_DATA_DECODE_ROW_HPP
+#define PULSEVIEW_PV_DATA_DECODE_ROW_HPP
#include <vector>
-#include "annotation.h"
+#include "annotation.hpp"
struct srd_decoder;
struct srd_decoder_annotation_row;
@@ -38,7 +38,7 @@ public:
Row();
Row(const srd_decoder *decoder,
- const srd_decoder_annotation_row *row = NULL);
+ const srd_decoder_annotation_row *row = nullptr);
const srd_decoder* decoder() const;
const srd_decoder_annotation_row* row() const;
@@ -48,12 +48,12 @@ public:
bool operator<(const Row &other) const;
private:
- const srd_decoder *_decoder;
- const srd_decoder_annotation_row *_row;
+ const srd_decoder *decoder_;
+ const srd_decoder_annotation_row *row_;
};
} // decode
} // data
} // pv
-#endif // PULSEVIEW_PV_DATA_DECODE_ROW_H
+#endif // PULSEVIEW_PV_DATA_DECODE_ROW_HPP
diff --git a/pv/data/decode/rowdata.cpp b/pv/data/decode/rowdata.cpp
index 87da27c..9402e70 100644
--- a/pv/data/decode/rowdata.cpp
+++ b/pv/data/decode/rowdata.cpp
@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "rowdata.h"
+#include "rowdata.hpp"
using std::vector;
@@ -32,17 +32,16 @@ RowData::RowData()
uint64_t RowData::get_max_sample() const
{
- if (_annotations.empty())
+ if (annotations_.empty())
return 0;
- return _annotations.back().end_sample();
+ return annotations_.back().end_sample();
}
void RowData::get_annotation_subset(
vector<pv::data::decode::Annotation> &dest,
uint64_t start_sample, uint64_t end_sample) const
{
- for (vector<Annotation>::const_iterator i = _annotations.begin();
- i != _annotations.end(); i++)
+ for (auto i = annotations_.cbegin(); i != annotations_.cend(); i++)
if ((*i).end_sample() > start_sample &&
(*i).start_sample() <= end_sample)
dest.push_back(*i);
@@ -50,7 +49,7 @@ void RowData::get_annotation_subset(
void RowData::push_annotation(const Annotation &a)
{
- _annotations.push_back(a);
+ annotations_.push_back(a);
}
} // decode
diff --git a/pv/data/decode/rowdata.h b/pv/data/decode/rowdata.hpp
similarity index 86%
rename from pv/data/decode/rowdata.h
rename to pv/data/decode/rowdata.hpp
index 01c65b6..820959d 100644
--- a/pv/data/decode/rowdata.h
+++ b/pv/data/decode/rowdata.hpp
@@ -18,12 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_DECODE_ROWDATA_H
-#define PULSEVIEW_PV_DATA_DECODE_ROWDATA_H
+#ifndef PULSEVIEW_PV_DATA_DECODE_ROWDATA_HPP
+#define PULSEVIEW_PV_DATA_DECODE_ROWDATA_HPP
#include <vector>
-#include "annotation.h"
+#include "annotation.hpp"
namespace pv {
namespace data {
@@ -47,11 +47,11 @@ public:
void push_annotation(const Annotation &a);
private:
- std::vector<Annotation> _annotations;
+ std::vector<Annotation> annotations_;
};
}
} // data
} // pv
-#endif // PULSEVIEW_PV_DATA_DECODE_ROWDATA_H
+#endif // PULSEVIEW_PV_DATA_DECODE_ROWDATA_HPP
diff --git a/pv/data/decoderstack.cpp b/pv/data/decoderstack.cpp
index c3bf26d..2668df2 100644
--- a/pv/data/decoderstack.cpp
+++ b/pv/data/decoderstack.cpp
@@ -20,27 +20,23 @@
#include <libsigrokdecode/libsigrokdecode.h>
-#include <boost/foreach.hpp>
-#include <boost/thread/thread.hpp>
-
#include <stdexcept>
#include <QDebug>
-#include "decoderstack.h"
+#include "decoderstack.hpp"
-#include <pv/data/logic.h>
-#include <pv/data/logicsnapshot.h>
-#include <pv/data/decode/decoder.h>
-#include <pv/data/decode/annotation.h>
-#include <pv/sigsession.h>
-#include <pv/view/logicsignal.h>
+#include <pv/data/logic.hpp>
+#include <pv/data/logicsegment.hpp>
+#include <pv/data/decode/decoder.hpp>
+#include <pv/data/decode/annotation.hpp>
+#include <pv/session.hpp>
+#include <pv/view/logicsignal.hpp>
-using boost::lock_guard;
-using boost::mutex;
+using std::lock_guard;
+using std::mutex;
using boost::optional;
-using boost::shared_ptr;
-using boost::unique_lock;
+using std::unique_lock;
using std::deque;
using std::make_pair;
using std::max;
@@ -48,6 +44,7 @@ using std::min;
using std::list;
using std::map;
using std::pair;
+using std::shared_ptr;
using std::vector;
using namespace pv::data::decode;
@@ -60,74 +57,86 @@ const double DecoderStack::DecodeThreshold = 0.2;
const int64_t DecoderStack::DecodeChunkLength = 4096;
const unsigned int DecoderStack::DecodeNotifyPeriod = 65536;
-mutex DecoderStack::_global_decode_mutex;
+mutex DecoderStack::global_decode_mutex_;
-DecoderStack::DecoderStack(pv::SigSession &session,
+DecoderStack::DecoderStack(pv::Session &session,
const srd_decoder *const dec) :
- _session(session),
- _sample_count(0),
- _frame_complete(false),
- _samples_decoded(0)
+ session_(session),
+ start_time_(0),
+ samplerate_(0),
+ sample_count_(0),
+ frame_complete_(false),
+ samples_decoded_(0)
{
- connect(&_session, SIGNAL(frame_began()),
+ connect(&session_, SIGNAL(frame_began()),
this, SLOT(on_new_frame()));
- connect(&_session, SIGNAL(data_received()),
+ connect(&session_, SIGNAL(data_received()),
this, SLOT(on_data_received()));
- connect(&_session, SIGNAL(frame_ended()),
+ connect(&session_, SIGNAL(frame_ended()),
this, SLOT(on_frame_ended()));
- _stack.push_back(shared_ptr<decode::Decoder>(
+ stack_.push_back(shared_ptr<decode::Decoder>(
new decode::Decoder(dec)));
}
DecoderStack::~DecoderStack()
{
- if (_decode_thread.joinable()) {
- _decode_thread.interrupt();
- _decode_thread.join();
+ if (decode_thread_.joinable()) {
+ interrupt_ = true;
+ input_cond_.notify_one();
+ decode_thread_.join();
}
}
-const std::list< boost::shared_ptr<decode::Decoder> >&
+const std::list< std::shared_ptr<decode::Decoder> >&
DecoderStack::stack() const
{
- return _stack;
+ return stack_;
}
-void DecoderStack::push(boost::shared_ptr<decode::Decoder> decoder)
+void DecoderStack::push(std::shared_ptr<decode::Decoder> decoder)
{
assert(decoder);
- _stack.push_back(decoder);
+ stack_.push_back(decoder);
}
void DecoderStack::remove(int index)
{
assert(index >= 0);
- assert(index < (int)_stack.size());
+ assert(index < (int)stack_.size());
// Find the decoder in the stack
- list< shared_ptr<Decoder> >::iterator iter = _stack.begin();
- for(int i = 0; i < index; i++, iter++)
- assert(iter != _stack.end());
+ auto iter = stack_.begin();
+ for (int i = 0; i < index; i++, iter++)
+ assert(iter != stack_.end());
// Delete the element
- _stack.erase(iter);
+ stack_.erase(iter);
+}
+
+double DecoderStack::samplerate() const
+{
+ return samplerate_;
+}
+
+const pv::util::Timestamp& DecoderStack::start_time() const
+{
+ return start_time_;
}
int64_t DecoderStack::samples_decoded() const
{
- lock_guard<mutex> decode_lock(_output_mutex);
- return _samples_decoded;
+ lock_guard<mutex> decode_lock(output_mutex_);
+ return samples_decoded_;
}
std::vector<Row> DecoderStack::get_visible_rows() const
{
- lock_guard<mutex> lock(_output_mutex);
+ lock_guard<mutex> lock(output_mutex_);
vector<Row> rows;
- BOOST_FOREACH (const shared_ptr<decode::Decoder> &dec, _stack)
- {
+ for (const shared_ptr<decode::Decoder> &dec : stack_) {
assert(dec);
if (!dec->shown())
continue;
@@ -140,8 +149,7 @@ std::vector<Row> DecoderStack::get_visible_rows() const
rows.push_back(Row(decc));
// Add the decoder rows
- for (const GSList *l = decc->annotation_rows; l; l = l->next)
- {
+ for (const GSList *l = decc->annotation_rows; l; l = l->next) {
const srd_decoder_annotation_row *const ann_row =
(srd_decoder_annotation_row *)l->data;
assert(ann_row);
@@ -157,29 +165,28 @@ void DecoderStack::get_annotation_subset(
const Row &row, uint64_t start_sample,
uint64_t end_sample) const
{
- lock_guard<mutex> lock(_output_mutex);
+ lock_guard<mutex> lock(output_mutex_);
- std::map<const Row, decode::RowData>::const_iterator iter =
- _rows.find(row);
- if (iter != _rows.end())
+ const auto iter = rows_.find(row);
+ if (iter != rows_.end())
(*iter).second.get_annotation_subset(dest,
start_sample, end_sample);
}
QString DecoderStack::error_message()
{
- lock_guard<mutex> lock(_output_mutex);
- return _error_message;
+ lock_guard<mutex> lock(output_mutex_);
+ return error_message_;
}
void DecoderStack::clear()
{
- _sample_count = 0;
- _frame_complete = false;
- _samples_decoded = 0;
- _error_message = QString();
- _rows.clear();
- _class_rows.clear();
+ sample_count_ = 0;
+ frame_complete_ = false;
+ samples_decoded_ = 0;
+ error_message_ = QString();
+ rows_.clear();
+ class_rows_.clear();
}
void DecoderStack::begin_decode()
@@ -187,35 +194,34 @@ void DecoderStack::begin_decode()
shared_ptr<pv::view::LogicSignal> logic_signal;
shared_ptr<pv::data::Logic> data;
- if (_decode_thread.joinable()) {
- _decode_thread.interrupt();
- _decode_thread.join();
+ if (decode_thread_.joinable()) {
+ interrupt_ = true;
+ input_cond_.notify_one();
+ decode_thread_.join();
}
clear();
// Check that all decoders have the required channels
- BOOST_FOREACH(const shared_ptr<decode::Decoder> &dec, _stack)
- if (!dec->have_required_probes()) {
- _error_message = tr("One or more required channels "
+ for (const shared_ptr<decode::Decoder> &dec : stack_)
+ if (!dec->have_required_channels()) {
+ error_message_ = tr("One or more required channels "
"have not been specified");
return;
}
// Add classes
- BOOST_FOREACH (const shared_ptr<decode::Decoder> &dec, _stack)
- {
+ for (const shared_ptr<decode::Decoder> &dec : stack_) {
assert(dec);
const srd_decoder *const decc = dec->decoder();
assert(dec->decoder());
// Add a row for the decoder if it doesn't have a row list
if (!decc->annotation_rows)
- _rows[Row(decc)] = decode::RowData();
+ rows_[Row(decc)] = decode::RowData();
// Add the decoder rows
- for (const GSList *l = decc->annotation_rows; l; l = l->next)
- {
+ for (const GSList *l = decc->annotation_rows; l; l = l->next) {
const srd_decoder_annotation_row *const ann_row =
(srd_decoder_annotation_row *)l->data;
assert(ann_row);
@@ -223,20 +229,20 @@ void DecoderStack::begin_decode()
const Row row(decc, ann_row);
// Add a new empty row data object
- _rows[row] = decode::RowData();
+ rows_[row] = decode::RowData();
// Map out all the classes
for (const GSList *ll = ann_row->ann_classes;
ll; ll = ll->next)
- _class_rows[make_pair(decc,
+ class_rows_[make_pair(decc,
GPOINTER_TO_INT(ll->data))] = row;
}
}
// We get the logic data of the first channel in the list.
// This works because we are currently assuming all
- // LogicSignals have the same data/snapshot
- BOOST_FOREACH (const shared_ptr<decode::Decoder> &dec, _stack)
+ // LogicSignals have the same data/segment
+ for (const shared_ptr<decode::Decoder> &dec : stack_)
if (dec && !dec->channels().empty() &&
((logic_signal = (*dec->channels().begin()).second)) &&
((data = logic_signal->logic_data())))
@@ -245,28 +251,28 @@ void DecoderStack::begin_decode()
if (!data)
return;
- // Check we have a snapshot of data
- const deque< shared_ptr<pv::data::LogicSnapshot> > &snapshots =
- data->get_snapshots();
- if (snapshots.empty())
+ // Check we have a segment of data
+ const deque< shared_ptr<pv::data::LogicSegment> > &segments =
+ data->logic_segments();
+ if (segments.empty())
return;
- _snapshot = snapshots.front();
+ segment_ = segments.front();
// Get the samplerate and start time
- _start_time = data->get_start_time();
- _samplerate = data->samplerate();
- if (_samplerate == 0.0)
- _samplerate = 1.0;
+ start_time_ = segment_->start_time();
+ samplerate_ = segment_->samplerate();
+ if (samplerate_ == 0.0)
+ samplerate_ = 1.0;
- _decode_thread = boost::thread(&DecoderStack::decode_proc, this);
+ interrupt_ = false;
+ decode_thread_ = std::thread(&DecoderStack::decode_proc, this);
}
-uint64_t DecoderStack::get_max_sample_count() const
+uint64_t DecoderStack::max_sample_count() const
{
uint64_t max_sample_count = 0;
- for (map<const Row, RowData>::const_iterator i = _rows.begin();
- i != _rows.end(); i++)
+ for (auto i = rows_.cbegin(); i != rows_.end(); i++)
max_sample_count = max(max_sample_count,
(*i).second.get_max_sample());
@@ -275,14 +281,13 @@ uint64_t DecoderStack::get_max_sample_count() const
optional<int64_t> DecoderStack::wait_for_data() const
{
- unique_lock<mutex> input_lock(_input_mutex);
- while(!boost::this_thread::interruption_requested() &&
- !_frame_complete && _samples_decoded >= _sample_count)
- _input_cond.wait(input_lock);
- return boost::make_optional(
- !boost::this_thread::interruption_requested() &&
- (_samples_decoded < _sample_count || !_frame_complete),
- _sample_count);
+ unique_lock<mutex> input_lock(input_mutex_);
+ while (!interrupt_ && !frame_complete_ &&
+ samples_decoded_ >= sample_count_)
+ input_cond_.wait(input_lock);
+ return boost::make_optional(!interrupt_ &&
+ (samples_decoded_ < sample_count_ || !frame_complete_),
+ sample_count_);
}
void DecoderStack::decode_data(
@@ -292,28 +297,25 @@ void DecoderStack::decode_data(
uint8_t chunk[DecodeChunkLength];
const unsigned int chunk_sample_count =
- DecodeChunkLength / _snapshot->unit_size();
+ DecodeChunkLength / segment_->unit_size();
- for (int64_t i = 0;
- !boost::this_thread::interruption_requested() &&
- i < sample_count;
- i += chunk_sample_count)
- {
- lock_guard<mutex> decode_lock(_global_decode_mutex);
+ for (int64_t i = 0; !interrupt_ && i < sample_count;
+ i += chunk_sample_count) {
+ lock_guard<mutex> decode_lock(global_decode_mutex_);
const int64_t chunk_end = min(
i + chunk_sample_count, sample_count);
- _snapshot->get_samples(chunk, i, chunk_end);
+ segment_->get_samples(chunk, i, chunk_end);
- if (srd_session_send(session, i, i + sample_count, chunk,
- (chunk_end - i) * unit_size) != SRD_OK) {
- _error_message = tr("Decoder reported an error");
+ if (srd_session_send(session, i, chunk_end, chunk,
+ (chunk_end - i) * unit_size, unit_size) != SRD_OK) {
+ error_message_ = tr("Decoder reported an error");
break;
}
{
- lock_guard<mutex> lock(_output_mutex);
- _samples_decoded = chunk_end;
+ lock_guard<mutex> lock(output_mutex_);
+ samples_decoded_ = chunk_end;
}
if (i % DecodeNotifyPeriod == 0)
@@ -327,24 +329,22 @@ void DecoderStack::decode_proc()
{
optional<int64_t> sample_count;
srd_session *session;
- srd_decoder_inst *prev_di = NULL;
+ srd_decoder_inst *prev_di = nullptr;
- assert(_snapshot);
+ assert(segment_);
// Create the session
srd_session_new(&session);
assert(session);
// Create the decoders
- const unsigned int unit_size = _snapshot->unit_size();
+ const unsigned int unit_size = segment_->unit_size();
- BOOST_FOREACH(const shared_ptr<decode::Decoder> &dec, _stack)
- {
- srd_decoder_inst *const di = dec->create_decoder_inst(session, unit_size);
+ for (const shared_ptr<decode::Decoder> &dec : stack_) {
+ srd_decoder_inst *const di = dec->create_decoder_inst(session);
- if (!di)
- {
- _error_message = tr("Failed to create decoder instance");
+ if (!di) {
+ error_message_ = tr("Failed to create decoder instance");
srd_session_destroy(session);
return;
}
@@ -357,13 +357,13 @@ void DecoderStack::decode_proc()
// Get the intial sample count
{
- unique_lock<mutex> input_lock(_input_mutex);
- sample_count = _sample_count = _snapshot->get_sample_count();
+ unique_lock<mutex> input_lock(input_mutex_);
+ sample_count = sample_count_ = segment_->get_sample_count();
}
// Start the session
srd_session_metadata_set(session, SRD_CONF_SAMPLERATE,
- g_variant_new_uint64((uint64_t)_samplerate));
+ g_variant_new_uint64((uint64_t)samplerate_));
srd_pd_output_callback_add(session, SRD_OUTPUT_ANN,
DecoderStack::annotation_callback, this);
@@ -372,7 +372,7 @@ void DecoderStack::decode_proc()
do {
decode_data(*sample_count, unit_size, session);
- } while(_error_message.isEmpty() && (sample_count = wait_for_data()));
+ } while (error_message_.isEmpty() && (sample_count = wait_for_data()));
// Destroy the session
srd_session_destroy(session);
@@ -386,7 +386,7 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder)
DecoderStack *const d = (DecoderStack*)decoder;
assert(d);
- lock_guard<mutex> lock(d->_output_mutex);
+ lock_guard<mutex> lock(d->output_mutex_);
const Annotation a(pdata);
@@ -396,21 +396,19 @@ void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder)
const srd_decoder *const decc = pdata->pdo->di->decoder;
assert(decc);
- map<const Row, decode::RowData>::iterator row_iter = d->_rows.end();
-
+ auto row_iter = d->rows_.end();
+
// Try looking up the sub-row of this class
- const map<pair<const srd_decoder*, int>, Row>::const_iterator r =
- d->_class_rows.find(make_pair(decc, a.format()));
- if (r != d->_class_rows.end())
- row_iter = d->_rows.find((*r).second);
- else
- {
+ const auto r = d->class_rows_.find(make_pair(decc, a.format()));
+ if (r != d->class_rows_.end())
+ row_iter = d->rows_.find((*r).second);
+ else {
// Failing that, use the decoder as a key
- row_iter = d->_rows.find(Row(decc));
+ row_iter = d->rows_.find(Row(decc));
}
- assert(row_iter != d->_rows.end());
- if (row_iter == d->_rows.end()) {
+ assert(row_iter != d->rows_.end());
+ if (row_iter == d->rows_.end()) {
qDebug() << "Unexpected annotation: decoder = " << decc <<
", format = " << a.format();
assert(0);
@@ -429,21 +427,21 @@ void DecoderStack::on_new_frame()
void DecoderStack::on_data_received()
{
{
- unique_lock<mutex> lock(_input_mutex);
- if (_snapshot)
- _sample_count = _snapshot->get_sample_count();
+ unique_lock<mutex> lock(input_mutex_);
+ if (segment_)
+ sample_count_ = segment_->get_sample_count();
}
- _input_cond.notify_one();
+ input_cond_.notify_one();
}
void DecoderStack::on_frame_ended()
{
{
- unique_lock<mutex> lock(_input_mutex);
- if (_snapshot)
- _frame_complete = true;
+ unique_lock<mutex> lock(input_mutex_);
+ if (segment_)
+ frame_complete_ = true;
}
- _input_cond.notify_one();
+ input_cond_.notify_one();
}
} // namespace data
diff --git a/pv/data/decoderstack.h b/pv/data/decoderstack.hpp
similarity index 63%
rename from pv/data/decoderstack.h
rename to pv/data/decoderstack.hpp
index 2eeaf1c..64ce13b 100644
--- a/pv/data/decoderstack.h
+++ b/pv/data/decoderstack.hpp
@@ -18,22 +18,26 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_DECODERSTACK_H
-#define PULSEVIEW_PV_DATA_DECODERSTACK_H
+#ifndef PULSEVIEW_PV_DATA_DECODERSTACK_HPP
+#define PULSEVIEW_PV_DATA_DECODERSTACK_HPP
-#include "signaldata.h"
+#include "signaldata.hpp"
+#include <atomic>
+#include <condition_variable>
#include <list>
+#include <map>
+#include <memory>
+#include <thread>
#include <boost/optional.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/thread.hpp>
#include <QObject>
#include <QString>
-#include <pv/data/decode/row.h>
-#include <pv/data/decode/rowdata.h>
+#include <pv/data/decode/row.hpp>
+#include <pv/data/decode/rowdata.hpp>
+#include <pv/util.hpp>
struct srd_decoder;
struct srd_decoder_annotation_row;
@@ -42,12 +46,12 @@ struct srd_proto_data;
struct srd_session;
namespace DecoderStackTest {
-class TwoDecoderStack;
+struct TwoDecoderStack;
}
namespace pv {
-class SigSession;
+class Session;
namespace view {
class LogicSignal;
@@ -55,7 +59,7 @@ class LogicSignal;
namespace data {
-class LogicSnapshot;
+class LogicSegment;
namespace decode {
class Annotation;
@@ -64,7 +68,7 @@ class Decoder;
class Logic;
-class DecoderStack : public QObject, public SignalData
+class DecoderStack : public QObject
{
Q_OBJECT
@@ -75,15 +79,19 @@ private:
static const unsigned int DecodeNotifyPeriod;
public:
- DecoderStack(pv::SigSession &_session,
+ DecoderStack(pv::Session &session_,
const srd_decoder *const decoder);
virtual ~DecoderStack();
- const std::list< boost::shared_ptr<decode::Decoder> >& stack() const;
- void push(boost::shared_ptr<decode::Decoder> decoder);
+ const std::list< std::shared_ptr<decode::Decoder> >& stack() const;
+ void push(std::shared_ptr<decode::Decoder> decoder);
void remove(int index);
+ double samplerate() const;
+
+ const pv::util::Timestamp& start_time() const;
+
int64_t samples_decoded() const;
std::vector<decode::Row> get_visible_rows() const;
@@ -100,7 +108,7 @@ public:
void clear();
- uint64_t get_max_sample_count() const;
+ uint64_t max_sample_count() const;
void begin_decode();
@@ -115,18 +123,21 @@ private:
static void annotation_callback(srd_proto_data *pdata,
void *decoder);
-private slots:
+private Q_SLOTS:
void on_new_frame();
void on_data_received();
void on_frame_ended();
-signals:
+Q_SIGNALS:
void new_decode_data();
private:
- pv::SigSession &_session;
+ pv::Session &session_;
+
+ pv::util::Timestamp start_time_;
+ double samplerate_;
/**
* This mutex prevents more than one decode operation occuring
@@ -134,32 +145,33 @@ private:
* @todo A proper solution should be implemented to allow multiple
* decode operations.
*/
- static boost::mutex _global_decode_mutex;
+ static std::mutex global_decode_mutex_;
- std::list< boost::shared_ptr<decode::Decoder> > _stack;
+ std::list< std::shared_ptr<decode::Decoder> > stack_;
- boost::shared_ptr<pv::data::LogicSnapshot> _snapshot;
+ std::shared_ptr<pv::data::LogicSegment> segment_;
- mutable boost::mutex _input_mutex;
- mutable boost::condition_variable _input_cond;
- int64_t _sample_count;
- bool _frame_complete;
+ mutable std::mutex input_mutex_;
+ mutable std::condition_variable input_cond_;
+ int64_t sample_count_;
+ bool frame_complete_;
- mutable boost::mutex _output_mutex;
- int64_t _samples_decoded;
+ mutable std::mutex output_mutex_;
+ int64_t samples_decoded_;
- std::map<const decode::Row, decode::RowData> _rows;
+ std::map<const decode::Row, decode::RowData> rows_;
- std::map<std::pair<const srd_decoder*, int>, decode::Row> _class_rows;
+ std::map<std::pair<const srd_decoder*, int>, decode::Row> class_rows_;
- QString _error_message;
+ QString error_message_;
- boost::thread _decode_thread;
+ std::thread decode_thread_;
+ std::atomic<bool> interrupt_;
- friend class DecoderStackTest::TwoDecoderStack;
+ friend struct DecoderStackTest::TwoDecoderStack;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_DECODERSTACK_H
+#endif // PULSEVIEW_PV_DATA_DECODERSTACK_HPP
diff --git a/pv/data/logic.cpp b/pv/data/logic.cpp
index 167c79b..b346353 100644
--- a/pv/data/logic.cpp
+++ b/pv/data/logic.cpp
@@ -18,50 +18,57 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <boost/foreach.hpp>
+#include <cassert>
-#include "logic.h"
-#include "logicsnapshot.h"
+#include "logic.hpp"
+#include "logicsegment.hpp"
-using boost::shared_ptr;
using std::deque;
using std::max;
+using std::shared_ptr;
+using std::vector;
namespace pv {
namespace data {
-Logic::Logic(unsigned int num_probes) :
+Logic::Logic(unsigned int num_channels) :
SignalData(),
- _num_probes(num_probes)
+ num_channels_(num_channels)
{
- assert(_num_probes > 0);
+ assert(num_channels_ > 0);
}
-int Logic::get_num_probes() const
+unsigned int Logic::num_channels() const
{
- return _num_probes;
+ return num_channels_;
}
-void Logic::push_snapshot(
- shared_ptr<LogicSnapshot> &snapshot)
+void Logic::push_segment(
+ shared_ptr<LogicSegment> &segment)
{
- _snapshots.push_front(snapshot);
+ segments_.push_front(segment);
}
-deque< shared_ptr<LogicSnapshot> >& Logic::get_snapshots()
+const deque< shared_ptr<LogicSegment> >& Logic::logic_segments() const
{
- return _snapshots;
+ return segments_;
+}
+
+vector< shared_ptr<Segment> > Logic::segments() const
+{
+ return vector< shared_ptr<Segment> >(
+ segments_.begin(), segments_.end());
}
void Logic::clear()
{
- _snapshots.clear();
+ segments_.clear();
}
-uint64_t Logic::get_max_sample_count() const
+uint64_t Logic::max_sample_count() const
{
uint64_t l = 0;
- BOOST_FOREACH(boost::shared_ptr<LogicSnapshot> s, _snapshots) {
+ for (std::shared_ptr<LogicSegment> s : segments_) {
assert(s);
l = max(l, s->get_sample_count());
}
diff --git a/pv/data/logic.h b/pv/data/logic.hpp
similarity index 64%
rename from pv/data/logic.h
rename to pv/data/logic.hpp
index 3d83394..a597027 100644
--- a/pv/data/logic.h
+++ b/pv/data/logic.hpp
@@ -18,42 +18,43 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_LOGIC_H
-#define PULSEVIEW_PV_DATA_LOGIC_H
+#ifndef PULSEVIEW_PV_DATA_LOGIC_HPP
+#define PULSEVIEW_PV_DATA_LOGIC_HPP
-#include "signaldata.h"
+#include "signaldata.hpp"
-#include <boost/shared_ptr.hpp>
#include <deque>
namespace pv {
namespace data {
-class LogicSnapshot;
+class LogicSegment;
class Logic : public SignalData
{
public:
- Logic(unsigned int num_probes);
+ Logic(unsigned int num_channels);
- int get_num_probes() const;
+ unsigned int num_channels() const;
- void push_snapshot(
- boost::shared_ptr<LogicSnapshot> &snapshot);
+ void push_segment(
+ std::shared_ptr<LogicSegment> &segment);
- std::deque< boost::shared_ptr<LogicSnapshot> >&
- get_snapshots();
+ const std::deque< std::shared_ptr<LogicSegment> >&
+ logic_segments() const;
+
+ std::vector< std::shared_ptr<Segment> > segments() const;
void clear();
- uint64_t get_max_sample_count() const;
+ uint64_t max_sample_count() const;
private:
- const unsigned int _num_probes;
- std::deque< boost::shared_ptr<LogicSnapshot> > _snapshots;
+ const unsigned int num_channels_;
+ std::deque< std::shared_ptr<LogicSegment> > segments_;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_LOGIC_H
+#endif // PULSEVIEW_PV_DATA_LOGIC_HPP
diff --git a/pv/data/logicsnapshot.cpp b/pv/data/logicsegment.cpp
similarity index 69%
rename from pv/data/logicsnapshot.cpp
rename to pv/data/logicsegment.cpp
index 797a00b..3e18b85 100644
--- a/pv/data/logicsnapshot.cpp
+++ b/pv/data/logicsegment.cpp
@@ -23,53 +23,55 @@
#include <assert.h>
#include <string.h>
#include <stdlib.h>
-#include <math.h>
+#include <cmath>
-#include <boost/foreach.hpp>
+#include "logicsegment.hpp"
-#include "config.h"
-#include "logicsnapshot.h"
+#include <libsigrokcxx/libsigrokcxx.hpp>
-using boost::lock_guard;
-using boost::recursive_mutex;
+using std::lock_guard;
+using std::recursive_mutex;
using std::max;
using std::min;
using std::pair;
+using std::shared_ptr;
+
+using sigrok::Logic;
namespace pv {
namespace data {
-const int LogicSnapshot::MipMapScalePower = 4;
-const int LogicSnapshot::MipMapScaleFactor = 1 << MipMapScalePower;
-const float LogicSnapshot::LogMipMapScaleFactor = logf(MipMapScaleFactor);
-const uint64_t LogicSnapshot::MipMapDataUnit = 64*1024; // bytes
+const int LogicSegment::MipMapScalePower = 4;
+const int LogicSegment::MipMapScaleFactor = 1 << MipMapScalePower;
+const float LogicSegment::LogMipMapScaleFactor = logf(MipMapScaleFactor);
+const uint64_t LogicSegment::MipMapDataUnit = 64*1024; // bytes
-LogicSnapshot::LogicSnapshot(const sr_datafeed_logic &logic,
- const uint64_t expected_num_samples) :
- Snapshot(logic.unitsize),
- _last_append_sample(0)
+LogicSegment::LogicSegment(shared_ptr<Logic> logic, uint64_t samplerate,
+ const uint64_t expected_num_samples) :
+ Segment(samplerate, logic->unit_size()),
+ last_append_sample_(0)
{
set_capacity(expected_num_samples);
- lock_guard<recursive_mutex> lock(_mutex);
- memset(_mip_map, 0, sizeof(_mip_map));
+ lock_guard<recursive_mutex> lock(mutex_);
+ memset(mip_map_, 0, sizeof(mip_map_));
append_payload(logic);
}
-LogicSnapshot::~LogicSnapshot()
+LogicSegment::~LogicSegment()
{
- lock_guard<recursive_mutex> lock(_mutex);
- BOOST_FOREACH(MipMapLevel &l, _mip_map)
+ lock_guard<recursive_mutex> lock(mutex_);
+ for (MipMapLevel &l : mip_map_)
free(l.data);
}
-uint64_t LogicSnapshot::unpack_sample(const uint8_t *ptr) const
+uint64_t LogicSegment::unpack_sample(const uint8_t *ptr) const
{
#ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
return *(uint64_t*)ptr;
#else
uint64_t value = 0;
- switch(_unit_size) {
+ switch (unit_size_) {
default:
value |= ((uint64_t)ptr[7]) << 56;
/* FALLTHRU */
@@ -101,12 +103,12 @@ uint64_t LogicSnapshot::unpack_sample(const uint8_t *ptr) const
#endif
}
-void LogicSnapshot::pack_sample(uint8_t *ptr, uint64_t value)
+void LogicSegment::pack_sample(uint8_t *ptr, uint64_t value)
{
#ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
*(uint64_t*)ptr = value;
#else
- switch(_unit_size) {
+ switch (unit_size_) {
default:
ptr[7] = value >> 56;
/* FALLTHRU */
@@ -137,53 +139,52 @@ void LogicSnapshot::pack_sample(uint8_t *ptr, uint64_t value)
#endif
}
-void LogicSnapshot::append_payload(
- const sr_datafeed_logic &logic)
+void LogicSegment::append_payload(shared_ptr<Logic> logic)
{
- assert(_unit_size == logic.unitsize);
- assert((logic.length % _unit_size) == 0);
+ assert(unit_size_ == logic->unit_size());
+ assert((logic->data_length() % unit_size_) == 0);
- lock_guard<recursive_mutex> lock(_mutex);
+ lock_guard<recursive_mutex> lock(mutex_);
- append_data(logic.data, logic.length / _unit_size);
+ append_data(logic->data_pointer(),
+ logic->data_length() / unit_size_);
// Generate the first mip-map from the data
append_payload_to_mipmap();
}
-void LogicSnapshot::get_samples(uint8_t *const data,
+void LogicSegment::get_samples(uint8_t *const data,
int64_t start_sample, int64_t end_sample) const
{
assert(data);
assert(start_sample >= 0);
- assert(start_sample <= (int64_t)_sample_count);
+ assert(start_sample <= (int64_t)sample_count_);
assert(end_sample >= 0);
- assert(end_sample <= (int64_t)_sample_count);
+ assert(end_sample <= (int64_t)sample_count_);
assert(start_sample <= end_sample);
- lock_guard<recursive_mutex> lock(_mutex);
+ lock_guard<recursive_mutex> lock(mutex_);
- const size_t size = (end_sample - start_sample) * _unit_size;
- memcpy(data, (const uint8_t*)_data + start_sample * _unit_size, size);
+ const size_t size = (end_sample - start_sample) * unit_size_;
+ memcpy(data, (const uint8_t*)data_.data() + start_sample * unit_size_, size);
}
-void LogicSnapshot::reallocate_mipmap_level(MipMapLevel &m)
+void LogicSegment::reallocate_mipmap_level(MipMapLevel &m)
{
const uint64_t new_data_length = ((m.length + MipMapDataUnit - 1) /
MipMapDataUnit) * MipMapDataUnit;
- if (new_data_length > m.data_length)
- {
+ if (new_data_length > m.data_length) {
m.data_length = new_data_length;
// Padding is added to allow for the uint64_t write word
- m.data = realloc(m.data, new_data_length * _unit_size +
+ m.data = realloc(m.data, new_data_length * unit_size_ +
sizeof(uint64_t));
}
}
-void LogicSnapshot::append_payload_to_mipmap()
+void LogicSegment::append_payload_to_mipmap()
{
- MipMapLevel &m0 = _mip_map[0];
+ MipMapLevel &m0 = mip_map_[0];
uint64_t prev_length;
const uint8_t *src_ptr;
uint8_t *dest_ptr;
@@ -192,7 +193,7 @@ void LogicSnapshot::append_payload_to_mipmap()
// Expand the data buffer to fit the new samples
prev_length = m0.length;
- m0.length = _sample_count / MipMapScaleFactor;
+ m0.length = sample_count_ / MipMapScaleFactor;
// Break off if there are no new samples to compute
if (m0.length == prev_length)
@@ -200,35 +201,32 @@ void LogicSnapshot::append_payload_to_mipmap()
reallocate_mipmap_level(m0);
- dest_ptr = (uint8_t*)m0.data + prev_length * _unit_size;
+ dest_ptr = (uint8_t*)m0.data + prev_length * unit_size_;
// Iterate through the samples to populate the first level mipmap
- const uint8_t *const end_src_ptr = (uint8_t*)_data +
- m0.length * _unit_size * MipMapScaleFactor;
- for (src_ptr = (uint8_t*)_data +
- prev_length * _unit_size * MipMapScaleFactor;
- src_ptr < end_src_ptr;)
- {
+ const uint8_t *const end_src_ptr = (uint8_t*)data_.data() +
+ m0.length * unit_size_ * MipMapScaleFactor;
+ for (src_ptr = (uint8_t*)data_.data() +
+ prev_length * unit_size_ * MipMapScaleFactor;
+ src_ptr < end_src_ptr;) {
// Accumulate transitions which have occurred in this sample
accumulator = 0;
diff_counter = MipMapScaleFactor;
- while (diff_counter-- > 0)
- {
+ while (diff_counter-- > 0) {
const uint64_t sample = unpack_sample(src_ptr);
- accumulator |= _last_append_sample ^ sample;
- _last_append_sample = sample;
- src_ptr += _unit_size;
+ accumulator |= last_append_sample_ ^ sample;
+ last_append_sample_ = sample;
+ src_ptr += unit_size_;
}
pack_sample(dest_ptr, accumulator);
- dest_ptr += _unit_size;
+ dest_ptr += unit_size_;
}
// Compute higher level mipmaps
- for (unsigned int level = 1; level < ScaleStepCount; level++)
- {
- MipMapLevel &m = _mip_map[level];
- const MipMapLevel &ml = _mip_map[level-1];
+ for (unsigned int level = 1; level < ScaleStepCount; level++) {
+ MipMapLevel &m = mip_map_[level];
+ const MipMapLevel &ml = mip_map_[level-1];
// Expand the data buffer to fit the new samples
prev_length = m.length;
@@ -242,20 +240,18 @@ void LogicSnapshot::append_payload_to_mipmap()
// Subsample the level lower level
src_ptr = (uint8_t*)ml.data +
- _unit_size * prev_length * MipMapScaleFactor;
+ unit_size_ * prev_length * MipMapScaleFactor;
const uint8_t *const end_dest_ptr =
- (uint8_t*)m.data + _unit_size * m.length;
+ (uint8_t*)m.data + unit_size_ * m.length;
for (dest_ptr = (uint8_t*)m.data +
- _unit_size * prev_length;
- dest_ptr < end_dest_ptr;
- dest_ptr += _unit_size)
- {
+ unit_size_ * prev_length;
+ dest_ptr < end_dest_ptr;
+ dest_ptr += unit_size_) {
accumulator = 0;
diff_counter = MipMapScaleFactor;
- while (diff_counter-- > 0)
- {
+ while (diff_counter-- > 0) {
accumulator |= unpack_sample(src_ptr);
- src_ptr += _unit_size;
+ src_ptr += unit_size_;
}
pack_sample(dest_ptr, accumulator);
@@ -263,15 +259,14 @@ void LogicSnapshot::append_payload_to_mipmap()
}
}
-uint64_t LogicSnapshot::get_sample(uint64_t index) const
+uint64_t LogicSegment::get_sample(uint64_t index) const
{
- assert(_data);
- assert(index < _sample_count);
+ assert(index < sample_count_);
- return unpack_sample((uint8_t*)_data + index * _unit_size);
+ return unpack_sample((uint8_t*)data_.data() + index * unit_size_);
}
-void LogicSnapshot::get_subsampled_edges(
+void LogicSegment::get_subsampled_edges(
std::vector<EdgePair> &edges,
uint64_t start, uint64_t end,
float min_length, int sig_index)
@@ -287,7 +282,7 @@ void LogicSnapshot::get_subsampled_edges(
assert(sig_index >= 0);
assert(sig_index < 64);
- lock_guard<recursive_mutex> lock(_mutex);
+ lock_guard<recursive_mutex> lock(mutex_);
const uint64_t block_length = (uint64_t)max(min_length, 1.0f);
const unsigned int min_level = max((int)floorf(logf(min_length) /
@@ -298,26 +293,23 @@ void LogicSnapshot::get_subsampled_edges(
last_sample = (get_sample(start) & sig_mask) != 0;
edges.push_back(pair<int64_t, bool>(index++, last_sample));
- while (index + block_length <= end)
- {
+ while (index + block_length <= end) {
//----- Continue to search -----//
level = min_level;
// We cannot fast-forward if there is no mip-map data at
// at the minimum level.
- fast_forward = (_mip_map[level].data != NULL);
+ fast_forward = (mip_map_[level].data != nullptr);
- if (min_length < MipMapScaleFactor)
- {
+ if (min_length < MipMapScaleFactor) {
// Search individual samples up to the beginning of
// the next first level mip map block
const uint64_t final_index = min(end,
pow2_ceil(index, MipMapScalePower));
for (; index < final_index &&
- (index & ~(~0 << MipMapScalePower)) != 0;
- index++)
- {
+ (index & ~(~0 << MipMapScalePower)) != 0;
+ index++) {
const bool sample =
(get_sample(index) & sig_mask) != 0;
@@ -327,9 +319,7 @@ void LogicSnapshot::get_subsampled_edges(
break;
}
}
- }
- else
- {
+ } else {
// If resolution is less than a mip map block,
// round up to the beginning of the mip-map block
// for this level of detail
@@ -363,7 +353,7 @@ void LogicSnapshot::get_subsampled_edges(
// Check if we reached the last block at this
// level, or if there was a change in this block
- if (offset >= _mip_map[level].length ||
+ if (offset >= mip_map_[level].length ||
(get_subsample(level, offset) &
sig_mask))
break;
@@ -373,7 +363,7 @@ void LogicSnapshot::get_subsampled_edges(
// higher level mip-map block ascend one
// level
if (level + 1 >= ScaleStepCount ||
- !_mip_map[level + 1].data)
+ !mip_map_[level + 1].data)
break;
level++;
@@ -388,7 +378,7 @@ void LogicSnapshot::get_subsampled_edges(
// Zoom in, and slide right until we encounter a change,
// and repeat until we reach min_level
while (1) {
- assert(_mip_map[level].data);
+ assert(mip_map_[level].data);
const int level_scale_power =
(level + 1) * MipMapScalePower;
@@ -397,8 +387,8 @@ void LogicSnapshot::get_subsampled_edges(
// Check if we reached the last block at this
// level, or if there was a change in this block
- if (offset >= _mip_map[level].length ||
- (get_subsample(level, offset) &
+ if (offset >= mip_map_[level].length ||
+ (get_subsample(level, offset) &
sig_mask)) {
// Zoom in unless we reached the minimum
// zoom
@@ -450,15 +440,15 @@ void LogicSnapshot::get_subsampled_edges(
edges.push_back(pair<int64_t, bool>(end + 1, end_sample));
}
-uint64_t LogicSnapshot::get_subsample(int level, uint64_t offset) const
+uint64_t LogicSegment::get_subsample(int level, uint64_t offset) const
{
assert(level >= 0);
- assert(_mip_map[level].data);
- return unpack_sample((uint8_t*)_mip_map[level].data +
- _unit_size * offset);
+ assert(mip_map_[level].data);
+ return unpack_sample((uint8_t*)mip_map_[level].data +
+ unit_size_ * offset);
}
-uint64_t LogicSnapshot::pow2_ceil(uint64_t x, unsigned int power)
+uint64_t LogicSegment::pow2_ceil(uint64_t x, unsigned int power)
{
const uint64_t p = 1 << power;
return (x + p - 1) / p * p;
diff --git a/pv/data/logicsnapshot.h b/pv/data/logicsegment.hpp
similarity index 72%
rename from pv/data/logicsnapshot.h
rename to pv/data/logicsegment.hpp
index 0f6f410..c9e8559 100644
--- a/pv/data/logicsnapshot.h
+++ b/pv/data/logicsegment.hpp
@@ -18,26 +18,30 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_LOGICSNAPSHOT_H
-#define PULSEVIEW_PV_DATA_LOGICSNAPSHOT_H
+#ifndef PULSEVIEW_PV_DATA_LOGICSEGMENT_HPP
+#define PULSEVIEW_PV_DATA_LOGICSEGMENT_HPP
-#include "snapshot.h"
+#include "segment.hpp"
#include <utility>
#include <vector>
-namespace LogicSnapshotTest {
-class Pow2;
-class Basic;
-class LargeData;
-class Pulses;
-class LongPulses;
+namespace sigrok {
+ class Logic;
+}
+
+namespace LogicSegmentTest {
+struct Pow2;
+struct Basic;
+struct LargeData;
+struct Pulses;
+struct LongPulses;
}
namespace pv {
namespace data {
-class LogicSnapshot : public Snapshot
+class LogicSegment : public Segment
{
private:
struct MipMapLevel
@@ -58,12 +62,12 @@ public:
typedef std::pair<int64_t, bool> EdgePair;
public:
- LogicSnapshot(const sr_datafeed_logic &logic,
- uint64_t expected_num_samples = 0);
+ LogicSegment(std::shared_ptr<sigrok::Logic> logic,
+ uint64_t samplerate, uint64_t expected_num_samples = 0);
- virtual ~LogicSnapshot();
+ virtual ~LogicSegment();
- void append_payload(const sr_datafeed_logic &logic);
+ void append_payload(std::shared_ptr<sigrok::Logic> logic);
void get_samples(uint8_t *const data,
int64_t start_sample, int64_t end_sample) const;
@@ -80,7 +84,7 @@ private:
public:
/**
- * Parses a logic data snapshot to generate a list of transitions
+ * Parses a logic data segment to generate a list of transitions
* in a time interval to a given level of detail.
* @param[out] edges The vector to place the edges into.
* @param[in] start The start sample index.
@@ -88,7 +92,7 @@ public:
* @param[in] min_length The minimum number of samples that
* can be resolved at this level of detail.
* @param[in] sig_index The index of the signal.
- **/
+ */
void get_subsampled_edges(std::vector<EdgePair> &edges,
uint64_t start, uint64_t end,
float min_length, int sig_index);
@@ -99,17 +103,17 @@ private:
static uint64_t pow2_ceil(uint64_t x, unsigned int power);
private:
- struct MipMapLevel _mip_map[ScaleStepCount];
- uint64_t _last_append_sample;
-
- friend class LogicSnapshotTest::Pow2;
- friend class LogicSnapshotTest::Basic;
- friend class LogicSnapshotTest::LargeData;
- friend class LogicSnapshotTest::Pulses;
- friend class LogicSnapshotTest::LongPulses;
+ struct MipMapLevel mip_map_[ScaleStepCount];
+ uint64_t last_append_sample_;
+
+ friend struct LogicSegmentTest::Pow2;
+ friend struct LogicSegmentTest::Basic;
+ friend struct LogicSegmentTest::LargeData;
+ friend struct LogicSegmentTest::Pulses;
+ friend struct LogicSegmentTest::LongPulses;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_LOGICSNAPSHOT_H
+#endif // PULSEVIEW_PV_DATA_LOGICSEGMENT_HPP
diff --git a/pv/data/segment.cpp b/pv/data/segment.cpp
new file mode 100644
index 0000000..754e300
--- /dev/null
+++ b/pv/data/segment.cpp
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "segment.hpp"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+using std::lock_guard;
+using std::recursive_mutex;
+
+namespace pv {
+namespace data {
+
+Segment::Segment(uint64_t samplerate, unsigned int unit_size) :
+ sample_count_(0),
+ start_time_(0),
+ samplerate_(samplerate),
+ capacity_(0),
+ unit_size_(unit_size)
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+ assert(unit_size_ > 0);
+}
+
+Segment::~Segment()
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+}
+
+uint64_t Segment::get_sample_count() const
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+ return sample_count_;
+}
+
+const pv::util::Timestamp& Segment::start_time() const
+{
+ return start_time_;
+}
+
+double Segment::samplerate() const
+{
+ return samplerate_;
+}
+
+void Segment::set_samplerate(double samplerate)
+{
+ samplerate_ = samplerate;
+}
+
+unsigned int Segment::unit_size() const
+{
+ return unit_size_;
+}
+
+void Segment::set_capacity(const uint64_t new_capacity)
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+
+ assert(capacity_ >= sample_count_);
+ if (new_capacity > capacity_) {
+ // If we're out of memory, this will throw std::bad_alloc
+ data_.resize((new_capacity * unit_size_) + sizeof(uint64_t));
+ capacity_ = new_capacity;
+ }
+}
+
+uint64_t Segment::capacity() const
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+ return data_.size();
+}
+
+void Segment::append_data(void *data, uint64_t samples)
+{
+ lock_guard<recursive_mutex> lock(mutex_);
+
+ assert(capacity_ >= sample_count_);
+
+ // Ensure there's enough capacity to copy.
+ const uint64_t free_space = capacity_ - sample_count_;
+ if (free_space < samples)
+ set_capacity(sample_count_ + samples);
+
+ memcpy((uint8_t*)data_.data() + sample_count_ * unit_size_,
+ data, samples * unit_size_);
+ sample_count_ += samples;
+}
+
+} // namespace data
+} // namespace pv
diff --git a/pv/data/snapshot.h b/pv/data/segment.hpp
similarity index 64%
rename from pv/data/snapshot.h
rename to pv/data/segment.hpp
index 1f1ca3c..5d0a242 100644
--- a/pv/data/snapshot.h
+++ b/pv/data/segment.hpp
@@ -18,51 +18,58 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_SNAPSHOT_H
-#define PULSEVIEW_PV_DATA_SNAPSHOT_H
+#ifndef PULSEVIEW_PV_DATA_SEGMENT_HPP
+#define PULSEVIEW_PV_DATA_SEGMENT_HPP
-#include <libsigrok/libsigrok.h>
+#include "pv/util.hpp"
-#include <boost/thread.hpp>
+#include <thread>
+#include <mutex>
+#include <vector>
namespace pv {
namespace data {
-class Snapshot
+class Segment
{
public:
- Snapshot(int unit_size);
+ Segment(uint64_t samplerate, unsigned int unit_size);
- virtual ~Snapshot();
+ virtual ~Segment();
uint64_t get_sample_count() const;
- int unit_size() const;
+ const pv::util::Timestamp& start_time() const;
+
+ double samplerate() const;
+ void set_samplerate(double samplerate);
+
+ unsigned int unit_size() const;
/**
- * @brief Increase the capacity of the snapshot.
+ * @brief Increase the capacity of the segment.
*
* Increasing the capacity allows samples to be appended without needing
* to reallocate memory.
*
* For the best efficiency @c set_capacity() should be called once before
- * @c append_data() is called to set up the snapshot with the expected number
+ * @c append_data() is called to set up the segment with the expected number
* of samples that will be appended in total.
*
* @note The capacity will automatically be increased when @c append_data()
* is called if there is not enough capacity in the buffer to store the samples.
*
- * @param[in] new_capacity The new capacity of the snapshot. If this value is
+ * @param[in] new_capacity The new capacity of the segment. If this value is
* smaller or equal than the current capacity then the method has no effect.
*/
void set_capacity(uint64_t new_capacity);
/**
- * @brief Get the current capacity of the snapshot.
+ * @brief Get the current capacity of the segment.
*
* The capacity can be increased by calling @c set_capacity().
*
- * @return The current capacity of the snapshot.
+ * @return The current capacity of the segment.
*/
uint64_t capacity() const;
@@ -70,14 +77,16 @@ protected:
void append_data(void *data, uint64_t samples);
protected:
- mutable boost::recursive_mutex _mutex;
- void *_data;
- uint64_t _sample_count;
- uint64_t _capacity;
- int _unit_size;
+ mutable std::recursive_mutex mutex_;
+ std::vector<uint8_t> data_;
+ uint64_t sample_count_;
+ pv::util::Timestamp start_time_;
+ double samplerate_;
+ uint64_t capacity_;
+ unsigned int unit_size_;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_SNAPSHOT_H
+#endif // PULSEVIEW_PV_DATA_SEGMENT_HPP
diff --git a/pv/data/signaldata.cpp b/pv/data/signaldata.cpp
index 04f1d3f..6a4c6d9 100644
--- a/pv/data/signaldata.cpp
+++ b/pv/data/signaldata.cpp
@@ -18,32 +18,14 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "signaldata.h"
+#include "signaldata.hpp"
namespace pv {
namespace data {
-SignalData::SignalData() :
- _start_time(0),
- _samplerate(0)
+SignalData::SignalData()
{
}
-double SignalData::samplerate() const
-{
- return _samplerate;
-}
-
-void SignalData::set_samplerate(double samplerate)
-{
- _samplerate = samplerate;
- clear();
-}
-
-double SignalData::get_start_time() const
-{
- return _start_time;
-}
-
} // namespace data
} // namespace pv
diff --git a/pv/data/signaldata.h b/pv/data/signaldata.hpp
similarity index 74%
rename from pv/data/signaldata.h
rename to pv/data/signaldata.hpp
index 0aa3b34..ff30cf1 100644
--- a/pv/data/signaldata.h
+++ b/pv/data/signaldata.hpp
@@ -18,35 +18,33 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DATA_SIGNALDATA_H
-#define PULSEVIEW_PV_DATA_SIGNALDATA_H
+#ifndef PULSEVIEW_PV_DATA_SIGNALDATA_HPP
+#define PULSEVIEW_PV_DATA_SIGNALDATA_HPP
-#include <stdint.h>
+#include <cstdint>
+#include <memory>
+#include <vector>
namespace pv {
namespace data {
+class Segment;
+
class SignalData
{
public:
SignalData();
+ virtual ~SignalData() {}
public:
- double samplerate() const;
- void set_samplerate(double samplerate);
-
- double get_start_time() const;
+ virtual std::vector< std::shared_ptr<Segment> > segments() const = 0;
virtual void clear() = 0;
- virtual uint64_t get_max_sample_count() const = 0;
-
-protected:
- double _start_time;
- double _samplerate;
+ virtual uint64_t max_sample_count() const = 0;
};
} // namespace data
} // namespace pv
-#endif // PULSEVIEW_PV_DATA_SIGNALDATA_H
+#endif // PULSEVIEW_PV_DATA_SIGNALDATA_HPP
diff --git a/pv/data/snapshot.cpp b/pv/data/snapshot.cpp
deleted file mode 100644
index 6ba39d2..0000000
--- a/pv/data/snapshot.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "snapshot.h"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-using boost::lock_guard;
-using boost::recursive_mutex;
-
-namespace pv {
-namespace data {
-
-Snapshot::Snapshot(int unit_size) :
- _data(NULL),
- _sample_count(0),
- _capacity(0),
- _unit_size(unit_size)
-{
- lock_guard<recursive_mutex> lock(_mutex);
- assert(_unit_size > 0);
-}
-
-Snapshot::~Snapshot()
-{
- lock_guard<recursive_mutex> lock(_mutex);
- free(_data);
-}
-
-uint64_t Snapshot::get_sample_count() const
-{
- lock_guard<recursive_mutex> lock(_mutex);
- return _sample_count;
-}
-
-int Snapshot::unit_size() const
-{
- return _unit_size;
-}
-
-void Snapshot::set_capacity(const uint64_t new_capacity)
-{
- lock_guard<recursive_mutex> lock(_mutex);
-
- assert(_capacity >= _sample_count);
- if (new_capacity > _capacity) {
- _capacity = new_capacity;
- _data = realloc(_data, (new_capacity * _unit_size) + sizeof(uint64_t));
- }
-}
-
-uint64_t Snapshot::capacity() const
-{
- lock_guard<recursive_mutex> lock(_mutex);
- return _capacity;
-}
-
-void Snapshot::append_data(void *data, uint64_t samples)
-{
- lock_guard<recursive_mutex> lock(_mutex);
-
- assert(_capacity >= _sample_count);
-
- // Ensure there's enough capacity to copy.
- const uint64_t free_space = _capacity - _sample_count;
- if (free_space < samples) {
- set_capacity(_sample_count + samples);
- }
-
- memcpy((uint8_t*)_data + _sample_count * _unit_size,
- data, samples * _unit_size);
- _sample_count += samples;
-}
-
-} // namespace data
-} // namespace pv
diff --git a/pv/device/device.cpp b/pv/device/device.cpp
deleted file mode 100644
index 4f9a779..0000000
--- a/pv/device/device.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <sstream>
-
-#include <libsigrok/libsigrok.h>
-
-#include "device.h"
-
-using std::ostringstream;
-using std::string;
-
-namespace pv {
-namespace device {
-
-Device::Device(sr_dev_inst *sdi) :
- _sdi(sdi)
-{
- assert(_sdi);
-}
-
-sr_dev_inst* Device::dev_inst() const
-{
- return _sdi;
-}
-
-void Device::use(SigSession *owner) throw(QString)
-{
- DevInst::use(owner);
-
- sr_session_new();
-
- assert(_sdi);
- sr_dev_open(_sdi);
- if (sr_session_dev_add(_sdi) != SR_OK)
- throw QString(tr("Failed to use device."));
-}
-
-void Device::release()
-{
- if (_owner) {
- DevInst::release();
- sr_session_destroy();
- }
-
- sr_dev_close(_sdi);
-}
-
-std::string Device::format_device_title() const
-{
- ostringstream s;
-
- assert(_sdi);
-
- if (_sdi->vendor && _sdi->vendor[0]) {
- s << _sdi->vendor;
- if ((_sdi->model && _sdi->model[0]) ||
- (_sdi->version && _sdi->version[0]))
- s << ' ';
- }
-
- if (_sdi->model && _sdi->model[0]) {
- s << _sdi->model;
- if (_sdi->version && _sdi->version[0])
- s << ' ';
- }
-
- if (_sdi->version && _sdi->version[0])
- s << _sdi->version;
-
- return s.str();
-}
-
-bool Device::is_trigger_enabled() const
-{
- assert(_sdi);
- for (const GSList *l = _sdi->channels; l; l = l->next) {
- const sr_channel *const p = (const sr_channel *)l->data;
- assert(p);
- if (p->trigger && p->trigger[0] != '\0')
- return true;
- }
- return false;
-}
-
-} // device
-} // pv
diff --git a/pv/device/devinst.cpp b/pv/device/devinst.cpp
deleted file mode 100644
index 4543e5c..0000000
--- a/pv/device/devinst.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <cassert>
-
-#include <QDebug>
-
-#include <libsigrok/libsigrok.h>
-
-#include "devinst.h"
-
-#include <pv/sigsession.h>
-
-namespace pv {
-namespace device {
-
-DevInst::DevInst() :
- _owner(NULL)
-{
-}
-
-void DevInst::use(SigSession *owner) throw(QString)
-{
- assert(owner);
- assert(!_owner);
- _owner = owner;
-}
-
-void DevInst::release()
-{
- if (_owner) {
- _owner->release_device(this);
- _owner = NULL;
- }
-}
-
-SigSession* DevInst::owner() const
-{
- return _owner;
-}
-
-GVariant* DevInst::get_config(const sr_channel_group *group, int key)
-{
- GVariant *data = NULL;
- assert(_owner);
- sr_dev_inst *const sdi = dev_inst();
- assert(sdi);
- if (sr_config_get(sdi->driver, sdi, group, key, &data) != SR_OK)
- return NULL;
- return data;
-}
-
-bool DevInst::set_config(const sr_channel_group *group, int key, GVariant *data)
-{
- assert(_owner);
- sr_dev_inst *const sdi = dev_inst();
- assert(sdi);
- if(sr_config_set(sdi, group, key, data) == SR_OK) {
- config_changed();
- return true;
- }
- return false;
-}
-
-GVariant* DevInst::list_config(const sr_channel_group *group, int key)
-{
- GVariant *data = NULL;
- assert(_owner);
- sr_dev_inst *const sdi = dev_inst();
- assert(sdi);
- if (sr_config_list(sdi->driver, sdi, group, key, &data) != SR_OK)
- return NULL;
- return data;
-}
-
-void DevInst::enable_probe(const sr_channel *probe, bool enable)
-{
- assert(_owner);
- sr_dev_inst *const sdi = dev_inst();
- assert(sdi);
- for (const GSList *p = sdi->channels; p; p = p->next)
- if (probe == p->data) {
- const_cast<sr_channel*>(probe)->enabled = enable;
- config_changed();
- return;
- }
-
- // Probe was not found in the device
- assert(0);
-}
-
-uint64_t DevInst::get_sample_limit()
-{
- uint64_t sample_limit;
- GVariant* gvar = get_config(NULL, SR_CONF_LIMIT_SAMPLES);
- if (gvar != NULL) {
- sample_limit = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- } else {
- sample_limit = 0U;
- }
- return sample_limit;
-}
-
-bool DevInst::is_trigger_enabled() const
-{
- return false;
-}
-
-void DevInst::start()
-{
- if (sr_session_start() != SR_OK)
- throw tr("Failed to start session.");
-}
-
-void DevInst::run()
-{
- sr_session_run();
-}
-
-} // device
-} // pv
diff --git a/pv/device/devinst.h b/pv/device/devinst.h
deleted file mode 100644
index e6a5140..0000000
--- a/pv/device/devinst.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_DEVICE_DEVINST_H
-#define PULSEVIEW_PV_DEVICE_DEVINST_H
-
-#include <string>
-
-#include <boost/shared_ptr.hpp>
-
-#include <QObject>
-
-#include <glib.h>
-
-#include <stdint.h>
-
-struct sr_dev_inst;
-struct sr_channel;
-struct sr_channel_group;
-
-namespace pv {
-
-class SigSession;
-
-namespace device {
-
-class DevInst : public QObject
-{
- Q_OBJECT
-
-protected:
- DevInst();
-
-public:
- virtual sr_dev_inst* dev_inst() const = 0;
-
- virtual void use(SigSession *owner) throw(QString);
-
- virtual void release();
-
- SigSession* owner() const;
-
- virtual std::string format_device_title() const = 0;
-
- GVariant* get_config(const sr_channel_group *group, int key);
-
- bool set_config(const sr_channel_group *group, int key, GVariant *data);
-
- GVariant* list_config(const sr_channel_group *group, int key);
-
- void enable_probe(const sr_channel *probe, bool enable = true);
-
- /**
- * @brief Gets the sample limit from the driver.
- *
- * @return The returned sample limit from the driver, or 0 if the
- * sample limit could not be read.
- */
- uint64_t get_sample_limit();
-
- virtual bool is_trigger_enabled() const;
-
-public:
- virtual void start();
-
- virtual void run();
-
-signals:
- void config_changed();
-
-protected:
- SigSession *_owner;
-};
-
-} // device
-} // pv
-
-#endif // PULSEVIEW_PV_DEVICE_DEVINST_H
diff --git a/pv/device/file.cpp b/pv/device/file.cpp
deleted file mode 100644
index e33be85..0000000
--- a/pv/device/file.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "file.h"
-#include "inputfile.h"
-#include "sessionfile.h"
-
-#include <boost/filesystem.hpp>
-
-#include <libsigrok/libsigrok.h>
-
-using std::string;
-
-namespace pv {
-namespace device {
-
-File::File(const std::string path) :
- _path(path)
-{
-}
-
-std::string File::format_device_title() const
-{
- return boost::filesystem::path(_path).filename().string();
-}
-
-File* File::create(const string &name)
-{
- if (sr_session_load(name.c_str()) == SR_OK) {
- GSList *devlist = NULL;
- sr_session_dev_list(&devlist);
- sr_session_destroy();
-
- if (devlist) {
- sr_dev_inst *const sdi = (sr_dev_inst*)devlist->data;
- g_slist_free(devlist);
- if (sdi) {
- sr_dev_close(sdi);
- sr_dev_clear(sdi->driver);
- return new SessionFile(name);
- }
- }
- }
-
- return new InputFile(name);
-}
-
-} // device
-} // pv
diff --git a/pv/device/inputfile.cpp b/pv/device/inputfile.cpp
deleted file mode 100644
index 55a0fa5..0000000
--- a/pv/device/inputfile.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <cassert>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "inputfile.h"
-
-#include <libsigrok/libsigrok.h>
-
-using std::string;
-
-namespace pv {
-namespace device {
-
-InputFile::InputFile(const std::string &path) :
- File(path),
- _input(NULL)
-{
-}
-
-sr_dev_inst* InputFile::dev_inst() const
-{
- assert(_input);
- return _input->sdi;
-}
-
-void InputFile::use(SigSession *owner) throw(QString)
-{
- assert(!_input);
-
- _input = load_input_file_format(_path, NULL);
- File::use(owner);
-
- sr_session_new();
-
- if (sr_session_dev_add(_input->sdi) != SR_OK)
- throw tr("Failed to add session device.");
-}
-
-void InputFile::release()
-{
- if (!_owner)
- return;
-
- assert(_input);
- File::release();
- sr_dev_close(_input->sdi);
- sr_session_destroy();
- _input = NULL;
-}
-
-sr_input_format* InputFile::determine_input_file_format(
- const string &filename)
-{
- int i;
-
- /* If there are no input formats, return NULL right away. */
- sr_input_format *const *const inputs = sr_input_list();
- if (!inputs) {
- g_critical("No supported input formats available.");
- return NULL;
- }
-
- /* Otherwise, try to find an input module that can handle this file. */
- for (i = 0; inputs[i]; i++) {
- if (inputs[i]->format_match(filename.c_str()))
- break;
- }
-
- /* Return NULL if no input module wanted to touch this. */
- if (!inputs[i]) {
- g_critical("Error: no matching input module found.");
- return NULL;
- }
-
- return inputs[i];
-}
-
-sr_input* InputFile::load_input_file_format(const string &filename,
- sr_input_format *format)
-{
- struct stat st;
- sr_input *in;
-
- if (!format && !(format =
- determine_input_file_format(filename.c_str()))) {
- /* The exact cause was already logged. */
- throw tr("Failed to load file");
- }
-
- if (stat(filename.c_str(), &st) == -1)
- throw tr("Failed to load file");
-
- /* Initialize the input module. */
- if (!(in = new sr_input)) {
- throw tr("Failed to allocate input module.");
- }
-
- in->format = format;
- in->param = NULL;
- if (in->format->init &&
- in->format->init(in, filename.c_str()) != SR_OK) {
- throw tr("Failed to load file");
- }
-
- return in;
-}
-
-void InputFile::start()
-{
-}
-
-void InputFile::run()
-{
- assert(_input);
- assert(_input->format);
- assert(_input->format->loadfile);
- _input->format->loadfile(_input, _path.c_str());
-}
-
-} // device
-} // pv
diff --git a/pv/device/inputfile.h b/pv/device/inputfile.h
deleted file mode 100644
index 150418d..0000000
--- a/pv/device/inputfile.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_DEVICE_INPUTFILE_H
-#define PULSEVIEW_PV_DEVICE_INPUTFILE_H
-
-#include "file.h"
-
-#include <string>
-
-struct sr_input;
-struct sr_input_format;
-
-namespace pv {
-namespace device {
-
-class InputFile : public File
-{
-public:
- InputFile(const std::string &path);
-
- sr_dev_inst* dev_inst() const;
-
- virtual void use(SigSession *owner) throw(QString);
-
- virtual void release();
-
- virtual void start();
-
- virtual void run();
-
-private:
- /**
- * Attempts to autodetect the format. Failing that
- * @param filename The filename of the input file.
- * @return A pointer to the 'struct sr_input_format' that should be used,
- * or NULL if no input format was selected or auto-detected.
- */
- static sr_input_format* determine_input_file_format(
- const std::string &filename);
-
- static sr_input* load_input_file_format(const std::string &filename,
- sr_input_format *format);
-private:
- sr_input *_input;
-};
-
-} // device
-} // pv
-
-#endif // PULSEVIEW_PV_DEVICE_INPUTFILE_H
diff --git a/pv/device/sessionfile.cpp b/pv/device/sessionfile.cpp
deleted file mode 100644
index ffdeb67..0000000
--- a/pv/device/sessionfile.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "sessionfile.h"
-
-#include <libsigrok/libsigrok.h>
-
-namespace pv {
-namespace device {
-
-SessionFile::SessionFile(const std::string &path) :
- File(path),
- _sdi(NULL)
-{
-}
-
-sr_dev_inst* SessionFile::dev_inst() const
-{
- return _sdi;
-}
-
-void SessionFile::use(SigSession *owner) throw(QString)
-{
- assert(!_sdi);
-
- if (sr_session_load(_path.c_str()) != SR_OK)
- throw tr("Failed to open file.\n");
-
- GSList *devlist = NULL;
- sr_session_dev_list(&devlist);
-
- if (!devlist || !devlist->data) {
- if (devlist)
- g_slist_free(devlist);
- throw tr("Failed to start session.");
- }
-
- _sdi = (sr_dev_inst*)devlist->data;
- g_slist_free(devlist);
-
- File::use(owner);
-}
-
-void SessionFile::release()
-{
- if (!_owner)
- return;
-
- assert(_sdi);
- File::release();
- sr_dev_close(_sdi);
- sr_dev_clear(_sdi->driver);
- sr_session_destroy();
- _sdi = NULL;
-}
-
-} // device
-} // pv
diff --git a/pv/device/sessionfile.h b/pv/device/sessionfile.h
deleted file mode 100644
index c0ed958..0000000
--- a/pv/device/sessionfile.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_DEVICE_SESSIONFILE_H
-#define PULSEVIEW_PV_DEVICE_SESSIONFILE_H
-
-#include "file.h"
-
-namespace pv {
-namespace device {
-
-class SessionFile : public File
-{
-public:
- SessionFile(const std::string &path);
-
- sr_dev_inst* dev_inst() const;
-
- virtual void use(SigSession *owner) throw(QString);
-
- virtual void release();
-
-private:
- sr_dev_inst *_sdi;
-};
-
-} // device
-} // pv
-
-#endif // PULSEVIEW_PV_DEVICE_SESSIONFILE_H
diff --git a/pv/devicemanager.cpp b/pv/devicemanager.cpp
index fb487a8..b11e60c 100644
--- a/pv/devicemanager.cpp
+++ b/pv/devicemanager.cpp
@@ -18,133 +18,184 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "devicemanager.h"
-#include "device/device.h"
-#include "sigsession.h"
+#include "devicemanager.hpp"
+#include "session.hpp"
#include <cassert>
+#include <functional>
#include <stdexcept>
+#include <sstream>
#include <string>
+#include <vector>
-#include <boost/foreach.hpp>
+#include <libsigrokcxx/libsigrokcxx.hpp>
-#include <libsigrok/libsigrok.h>
+#include <boost/algorithm/string/join.hpp>
+#include <boost/filesystem.hpp>
-using boost::shared_ptr;
+#include <pv/devices/hardwaredevice.hpp>
+
+using boost::algorithm::join;
+
+using std::bind;
+using std::dynamic_pointer_cast;
using std::list;
using std::map;
-using std::ostringstream;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::remove_if;
using std::runtime_error;
+using std::shared_ptr;
using std::string;
+using std::vector;
+
+using Glib::VariantBase;
+
+using sigrok::ConfigKey;
+using sigrok::Context;
+using sigrok::Driver;
+using sigrok::SessionDevice;
namespace pv {
-DeviceManager::DeviceManager(struct sr_context *sr_ctx) :
- _sr_ctx(sr_ctx)
+DeviceManager::DeviceManager(shared_ptr<Context> context) :
+ context_(context)
{
- init_drivers();
- scan_all_drivers();
+ for (auto entry : context->drivers())
+ driver_scan(entry.second, map<const ConfigKey *, VariantBase>());
}
DeviceManager::~DeviceManager()
{
- release_devices();
}
-const list< shared_ptr<pv::device::Device> >& DeviceManager::devices() const
+const std::shared_ptr<sigrok::Context>& DeviceManager::context() const
+{
+ return context_;
+}
+
+shared_ptr<Context> DeviceManager::context()
+{
+ return context_;
+}
+
+const list< shared_ptr<devices::HardwareDevice> >&
+DeviceManager::devices() const
{
- return _devices;
+ return devices_;
}
-list< shared_ptr<device::Device> > DeviceManager::driver_scan(
- struct sr_dev_driver *const driver, GSList *const drvopts)
+list< shared_ptr<devices::HardwareDevice> >
+DeviceManager::driver_scan(
+ shared_ptr<Driver> driver, map<const ConfigKey *, VariantBase> drvopts)
{
- list< shared_ptr<device::Device> > driver_devices;
+ list< shared_ptr<devices::HardwareDevice> > driver_devices;
assert(driver);
// Remove any device instances from this driver from the device
// list. They will not be valid after the scan.
- list< shared_ptr<device::Device> >::iterator i = _devices.begin();
- while (i != _devices.end()) {
- if ((*i)->dev_inst()->driver == driver)
- i = _devices.erase(i);
- else
- i++;
- }
-
- // Release this driver and all it's attached devices
- release_driver(driver);
+ devices_.remove_if([&](shared_ptr<devices::HardwareDevice> device) {
+ return device->hardware_device()->driver() == driver; });
// Do the scan
- GSList *const devices = sr_driver_scan(driver, drvopts);
- for (GSList *l = devices; l; l = l->next)
- driver_devices.push_back(shared_ptr<device::Device>(
- new device::Device((sr_dev_inst*)l->data)));
- g_slist_free(devices);
- driver_devices.sort(compare_devices);
-
- // Add the scanned devices to the main list
- _devices.insert(_devices.end(), driver_devices.begin(),
- driver_devices.end());
- _devices.sort(compare_devices);
-
- return driver_devices;
-}
+ auto devices = driver->scan(drvopts);
-void DeviceManager::init_drivers()
-{
- // Initialise all libsigrok drivers
- sr_dev_driver **const drivers = sr_driver_list();
- for (sr_dev_driver **driver = drivers; *driver; driver++) {
- if (sr_driver_init(_sr_ctx, *driver) != SR_OK) {
- throw runtime_error(
- string("Failed to initialize driver ") +
- string((*driver)->name));
- }
+ // Add the scanned devices to the main list, set display names and sort.
+ for (shared_ptr<sigrok::HardwareDevice> device : devices) {
+ const shared_ptr<devices::HardwareDevice> d(
+ new devices::HardwareDevice(context_, device));
+ driver_devices.push_back(d);
}
-}
-void DeviceManager::release_devices()
-{
- // Release all the used devices
- BOOST_FOREACH(shared_ptr<device::Device> dev, _devices) {
- assert(dev);
- dev->release();
- }
+ devices_.insert(devices_.end(), driver_devices.begin(),
+ driver_devices.end());
+ devices_.sort(bind(&DeviceManager::compare_devices, this, _1, _2));
+ driver_devices.sort(bind(
+ &DeviceManager::compare_devices, this, _1, _2));
- // Clear all the drivers
- sr_dev_driver **const drivers = sr_driver_list();
- for (sr_dev_driver **driver = drivers; *driver; driver++)
- sr_dev_clear(*driver);
+ return driver_devices;
}
-void DeviceManager::scan_all_drivers()
+const map<string, string> DeviceManager::get_device_info(
+ shared_ptr<devices::Device> device)
{
- // Scan all drivers for all devices.
- struct sr_dev_driver **const drivers = sr_driver_list();
- for (struct sr_dev_driver **driver = drivers; *driver; driver++)
- driver_scan(*driver);
+ map<string, string> result;
+
+ assert(device);
+
+ const shared_ptr<sigrok::Device> sr_dev = device->device();
+ if (sr_dev->vendor().length() > 0)
+ result["vendor"] = sr_dev->vendor();
+ if (sr_dev->model().length() > 0)
+ result["model"] = sr_dev->model();
+ if (sr_dev->version().length() > 0)
+ result["version"] = sr_dev->version();
+ if (sr_dev->serial_number().length() > 0)
+ result["serial_num"] = sr_dev->serial_number();
+ if (sr_dev->connection_id().length() > 0)
+ result["connection_id"] = sr_dev->connection_id();
+
+ return result;
}
-void DeviceManager::release_driver(struct sr_dev_driver *const driver)
+const shared_ptr<devices::HardwareDevice> DeviceManager::find_device_from_info(
+ const map<string, string> search_info)
{
- BOOST_FOREACH(shared_ptr<device::Device> dev, _devices) {
+ shared_ptr<devices::HardwareDevice> last_resort_dev;
+ map<string, string> dev_info;
+
+ for (shared_ptr<devices::HardwareDevice> dev : devices_) {
assert(dev);
- if(dev->dev_inst()->driver == driver)
- dev->release();
+ dev_info = get_device_info(dev);
+
+ // If present, vendor and model always have to match.
+ if (dev_info.count("vendor") > 0 && search_info.count("vendor") > 0)
+ if (dev_info.at("vendor") != search_info.at("vendor"))
+ continue;
+
+ if (dev_info.count("model") > 0 && search_info.count("model") > 0)
+ if (dev_info.at("model") != search_info.at("model"))
+ continue;
+
+ // Most unique match: vendor/model/serial_num (but don't match a S/N of 0)
+ if ((dev_info.count("serial_num") > 0) && (dev_info.at("serial_num") != "0")
+ && search_info.count("serial_num") > 0)
+ if (dev_info.at("serial_num") == search_info.at("serial_num") &&
+ dev_info.at("serial_num") != "0")
+ return dev;
+
+ // Second best match: vendor/model/connection_id
+ if (dev_info.count("connection_id") > 0 &&
+ search_info.count("connection_id") > 0)
+ if (dev_info.at("connection_id") == search_info.at("connection_id"))
+ return dev;
+
+ // Last resort: vendor/model/version
+ if (dev_info.count("version") > 0 &&
+ search_info.count("version") > 0)
+ if (dev_info.at("version") == search_info.at("version") &&
+ dev_info.at("version") != "0")
+ return dev;
+
+ // For this device, we merely have a vendor/model match.
+ last_resort_dev = dev;
}
- // Clear all the old device instances from this driver
- sr_dev_clear(driver);
+ // If there wasn't even a vendor/model/version match, we end up here.
+ // This is usually the case for devices with only vendor/model data.
+ // The selected device may be wrong with multiple such devices attached
+ // but it is the best we can do at this point. After all, there may be
+ // only one such device and we do want to select it in this case.
+ return last_resort_dev;
}
-bool DeviceManager::compare_devices(shared_ptr<device::Device> a,
- shared_ptr<device::Device> b)
+bool DeviceManager::compare_devices(shared_ptr<devices::Device> a,
+ shared_ptr<devices::Device> b)
{
assert(a);
assert(b);
- return a->format_device_title().compare(b->format_device_title()) < 0;
+ return a->display_name(*this).compare(b->display_name(*this)) < 0;
}
} // namespace pv
diff --git a/pv/devicemanager.h b/pv/devicemanager.h
deleted file mode 100644
index acef8a4..0000000
--- a/pv/devicemanager.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_DEVICEMANAGER_H
-#define PULSEVIEW_PV_DEVICEMANAGER_H
-
-#include <glib.h>
-
-#include <list>
-#include <string>
-
-#include <boost/shared_ptr.hpp>
-
-struct sr_context;
-struct sr_dev_driver;
-
-namespace pv {
-
-class SigSession;
-
-namespace device {
-class Device;
-}
-
-class DeviceManager
-{
-public:
- DeviceManager(struct sr_context *sr_ctx);
-
- ~DeviceManager();
-
- const std::list< boost::shared_ptr<pv::device::Device> >&
- devices() const;
-
- std::list< boost::shared_ptr<pv::device::Device> > driver_scan(
- struct sr_dev_driver *const driver,
- GSList *const drvopts = NULL);
-
-private:
- void init_drivers();
-
- void release_devices();
-
- void scan_all_drivers();
-
- void release_driver(struct sr_dev_driver *const driver);
-
- static bool compare_devices(boost::shared_ptr<device::Device> a,
- boost::shared_ptr<device::Device> b);
-
-private:
- struct sr_context *const _sr_ctx;
- std::list< boost::shared_ptr<pv::device::Device> > _devices;
-};
-
-} // namespace pv
-
-#endif // PULSEVIEW_PV_DEVICEMANAGER_H
diff --git a/pv/devicemanager.hpp b/pv/devicemanager.hpp
new file mode 100644
index 0000000..33e784d
--- /dev/null
+++ b/pv/devicemanager.hpp
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_DEVICEMANAGER_HPP
+#define PULSEVIEW_PV_DEVICEMANAGER_HPP
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+
+namespace Glib {
+class VariantBase;
+}
+
+namespace sigrok {
+class ConfigKey;
+class Context;
+class Driver;
+}
+
+namespace pv {
+
+namespace devices {
+class Device;
+class HardwareDevice;
+}
+
+class Session;
+
+class DeviceManager
+{
+public:
+ DeviceManager(std::shared_ptr<sigrok::Context> context);
+
+ ~DeviceManager();
+
+ const std::shared_ptr<sigrok::Context>& context() const;
+
+ std::shared_ptr<sigrok::Context> context();
+
+ const std::list< std::shared_ptr<devices::HardwareDevice> >&
+ devices() const;
+
+ std::list< std::shared_ptr<devices::HardwareDevice> > driver_scan(
+ std::shared_ptr<sigrok::Driver> driver,
+ std::map<const sigrok::ConfigKey *, Glib::VariantBase> drvopts);
+
+ const std::map<std::string, std::string> get_device_info(
+ const std::shared_ptr<devices::Device> device);
+
+ const std::shared_ptr<devices::HardwareDevice> find_device_from_info(
+ const std::map<std::string, std::string> search_info);
+
+private:
+ bool compare_devices(std::shared_ptr<devices::Device> a,
+ std::shared_ptr<devices::Device> b);
+
+protected:
+ std::shared_ptr<sigrok::Context> context_;
+ std::list< std::shared_ptr<devices::HardwareDevice> > devices_;
+};
+
+} // namespace pv
+
+#endif // PULSEVIEW_PV_DEVICEMANAGER_HPP
diff --git a/pv/devices/device.cpp b/pv/devices/device.cpp
new file mode 100644
index 0000000..403acc4
--- /dev/null
+++ b/pv/devices/device.cpp
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cassert>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include "device.hpp"
+
+using std::map;
+using std::set;
+
+using sigrok::ConfigKey;
+using sigrok::Capability;
+using sigrok::Error;
+
+using Glib::VariantBase;
+using Glib::Variant;
+
+namespace pv {
+namespace devices {
+
+Device::Device()
+{
+}
+
+Device::~Device()
+{
+ if (session_)
+ session_->remove_datafeed_callbacks();
+}
+
+std::shared_ptr<sigrok::Session> Device::session() const
+{
+ return session_;
+}
+
+std::shared_ptr<sigrok::Device> Device::device() const
+{
+ return device_;
+}
+
+template
+uint64_t Device::read_config(const sigrok::ConfigKey*,
+ const uint64_t);
+
+template<typename T>
+T Device::read_config(const ConfigKey *key, const T default_value)
+{
+ assert(key);
+
+ if (!device_)
+ return default_value;
+
+ if (!device_->config_check(key, Capability::GET))
+ return default_value;
+
+ return VariantBase::cast_dynamic<Glib::Variant<guint64>>(
+ device_->config_get(ConfigKey::SAMPLERATE)).get();
+}
+
+void Device::start()
+{
+ assert(session_);
+ session_->start();
+}
+
+void Device::run()
+{
+ assert(device_);
+ assert(session_);
+ session_->run();
+}
+
+void Device::stop()
+{
+ assert(session_);
+ session_->stop();
+}
+
+} // namespace devices
+} // namespace pv
diff --git a/pv/devices/device.hpp b/pv/devices/device.hpp
new file mode 100644
index 0000000..acc772e
--- /dev/null
+++ b/pv/devices/device.hpp
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_DEVICES_DEVICE_HPP
+#define PULSEVIEW_PV_DEVICES_DEVICE_HPP
+
+#include <memory>
+#include <string>
+
+namespace sigrok {
+class ConfigKey;
+class Device;
+class Session;
+} // namespace sigrok
+
+namespace pv {
+
+class DeviceManager;
+
+namespace devices {
+
+class Device
+{
+protected:
+ Device();
+
+public:
+ virtual ~Device();
+
+ std::shared_ptr<sigrok::Session> session() const;
+
+ std::shared_ptr<sigrok::Device> device() const;
+
+ template<typename T>
+ T read_config(const sigrok::ConfigKey *key, const T default_value = 0);
+
+ /**
+ * Builds the full name. It only contains all the fields.
+ */
+ virtual std::string full_name() const = 0;
+
+ /**
+ * Builds the display name. It only contains fields as required.
+ * @param device_manager a reference to the device manager is needed
+ * so that other similarly titled devices can be detected.
+ */
+ virtual std::string display_name(
+ const DeviceManager &device_manager) const = 0;
+
+ virtual void open() = 0;
+
+ virtual void close() = 0;
+
+ virtual void start();
+
+ virtual void run();
+
+ virtual void stop();
+
+protected:
+ std::shared_ptr<sigrok::Session> session_;
+ std::shared_ptr<sigrok::Device> device_;
+};
+
+} // namespace devices
+} // namespace pv
+
+#endif // PULSEVIEW_PV_DEVICES_DEVICE_HPP
diff --git a/pv/data/signaldata.cpp b/pv/devices/file.cpp
similarity index 66%
copy from pv/data/signaldata.cpp
copy to pv/devices/file.cpp
index 04f1d3f..5e1cb85 100644
--- a/pv/data/signaldata.cpp
+++ b/pv/devices/file.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,32 +18,27 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "signaldata.h"
+#include <boost/filesystem.hpp>
-namespace pv {
-namespace data {
+#include "file.hpp"
-SignalData::SignalData() :
- _start_time(0),
- _samplerate(0)
-{
-}
+namespace pv {
+namespace devices {
-double SignalData::samplerate() const
+File::File(const std::string &file_name) :
+ file_name_(file_name)
{
- return _samplerate;
}
-void SignalData::set_samplerate(double samplerate)
+std::string File::full_name() const
{
- _samplerate = samplerate;
- clear();
+ return boost::filesystem::path(file_name_).filename().string();
}
-double SignalData::get_start_time() const
+std::string File::display_name(const DeviceManager&) const
{
- return _start_time;
+ return File::full_name();
}
-} // namespace data
+} // namespace devices
} // namespace pv
diff --git a/pv/device/file.h b/pv/devices/file.hpp
similarity index 59%
rename from pv/device/file.h
rename to pv/devices/file.hpp
index 4a45a82..8a8c870 100644
--- a/pv/device/file.h
+++ b/pv/devices/file.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,32 +18,37 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DEVICE_FILE_H
-#define PULSEVIEW_PV_DEVICE_FILE_H
+#ifndef PULSEVIEW_PV_DEVICES_FILE_HPP
+#define PULSEVIEW_PV_DEVICES_FILE_HPP
#include <string>
-#include "devinst.h"
+#include "device.hpp"
namespace pv {
-namespace device {
+namespace devices {
-class File : public DevInst
+class File : public Device
{
protected:
- File(const std::string path);
+ File(const std::string &file_name);
public:
- static File* create(const std::string &name);
+ /**
+ * Builds the full name. It only contains all the fields.
+ */
+ std::string full_name() const;
-public:
- std::string format_device_title() const;
+ /**
+ * Builds the display name. It only contains fields as required.
+ */
+ std::string display_name(const DeviceManager&) const;
protected:
- const std::string _path;
+ const std::string file_name_;
};
-} // device
-} // pv
+} // namespace devices
+} // namespace pv
-#endif // PULSEVIEW_PV_DEVICE_FILE_H
+#endif // PULSEVIEW_PV_DEVICES_FILE_HPP
diff --git a/pv/devices/hardwaredevice.cpp b/pv/devices/hardwaredevice.cpp
new file mode 100644
index 0000000..318ce1c
--- /dev/null
+++ b/pv/devices/hardwaredevice.cpp
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <boost/algorithm/string/join.hpp>
+
+#include <QString>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <pv/devicemanager.hpp>
+
+#include "hardwaredevice.hpp"
+
+using std::dynamic_pointer_cast;
+using std::shared_ptr;
+using std::static_pointer_cast;
+using std::string;
+using std::vector;
+
+using boost::algorithm::join;
+
+using sigrok::HardwareDevice;
+
+namespace pv {
+namespace devices {
+
+HardwareDevice::HardwareDevice(const std::shared_ptr<sigrok::Context> &context,
+ std::shared_ptr<sigrok::HardwareDevice> device) :
+ context_(context),
+ device_open_(false)
+{
+ device_ = device;
+}
+
+HardwareDevice::~HardwareDevice()
+{
+ close();
+}
+
+string HardwareDevice::full_name() const
+{
+ vector<string> parts = {device_->vendor(), device_->model(),
+ device_->version(), device_->serial_number()};
+ if (device_->connection_id().length() > 0)
+ parts.push_back("(" + device_->connection_id() + ")");
+ return join(parts, " ");
+}
+
+shared_ptr<sigrok::HardwareDevice> HardwareDevice::hardware_device() const
+{
+ return static_pointer_cast<sigrok::HardwareDevice>(device_);
+}
+
+string HardwareDevice::display_name(
+ const DeviceManager &device_manager) const
+{
+ const auto hw_dev = hardware_device();
+
+ // If we can find another device with the same model/vendor then
+ // we have at least two such devices and need to distinguish them.
+ const auto &devices = device_manager.devices();
+ const bool multiple_dev = hw_dev && any_of(
+ devices.begin(), devices.end(),
+ [&](shared_ptr<devices::HardwareDevice> dev) {
+ return dev->hardware_device()->vendor() ==
+ hw_dev->vendor() &&
+ dev->hardware_device()->model() ==
+ hw_dev->model() &&
+ dev->device_ != device_;
+ });
+
+ vector<string> parts = {device_->vendor(), device_->model()};
+
+ if (multiple_dev) {
+ parts.push_back(device_->version());
+ parts.push_back(device_->serial_number());
+
+ if ((device_->serial_number().length() == 0) &&
+ (device_->connection_id().length() > 0))
+ parts.push_back("(" + device_->connection_id() + ")");
+ }
+
+ return join(parts, " ");
+}
+
+void HardwareDevice::open()
+{
+ if (device_open_)
+ close();
+
+ try {
+ device_->open();
+ } catch (const sigrok::Error &e) {
+ throw QString(e.what());
+ }
+
+ device_open_ = true;
+
+ // Set up the session
+ session_ = context_->create_session();
+ session_->add_device(device_);
+}
+
+void HardwareDevice::close()
+{
+ if (device_open_)
+ device_->close();
+
+ if (session_)
+ session_->remove_devices();
+
+ device_open_ = false;
+}
+
+} // namespace devices
+} // namespace pv
diff --git a/pv/devices/hardwaredevice.hpp b/pv/devices/hardwaredevice.hpp
new file mode 100644
index 0000000..bc8e47a
--- /dev/null
+++ b/pv/devices/hardwaredevice.hpp
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_DEVICES_HARDWAREDEVICE_HPP
+#define PULSEVIEW_PV_DEVICES_HARDWAREDEVICE_HPP
+
+#include "device.hpp"
+
+namespace sigrok {
+class Context;
+class HardwareDevice;
+} // sigrok
+
+namespace pv {
+namespace devices {
+
+class HardwareDevice final : public Device
+{
+public:
+ HardwareDevice(const std::shared_ptr<sigrok::Context> &context,
+ std::shared_ptr<sigrok::HardwareDevice> device);
+
+ ~HardwareDevice();
+
+ std::shared_ptr<sigrok::HardwareDevice> hardware_device() const;
+
+ /**
+ * Builds the full name. It only contains all the fields.
+ */
+ std::string full_name() const;
+
+ /**
+ * Builds the display name. It only contains fields as required.
+ * @param device_manager a reference to the device manager is needed
+ * so that other similarly titled devices can be detected.
+ */
+ std::string display_name(const DeviceManager &device_manager) const;
+
+ void open();
+
+ void close();
+
+private:
+ const std::shared_ptr<sigrok::Context> context_;
+ bool device_open_;
+};
+
+} // namespace devices
+} // namespace pv
+
+#endif // PULSEVIEW_PV_DEVICES_HARDWAREDEVICE_HPP
diff --git a/pv/devices/inputfile.cpp b/pv/devices/inputfile.cpp
new file mode 100644
index 0000000..3ddc8ae
--- /dev/null
+++ b/pv/devices/inputfile.cpp
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cassert>
+#include <fstream>
+
+#include <QString>
+
+#include "inputfile.hpp"
+
+namespace pv {
+namespace devices {
+
+const std::streamsize InputFile::BufferSize = 16384;
+
+InputFile::InputFile(const std::shared_ptr<sigrok::Context> &context,
+ const std::string &file_name,
+ std::shared_ptr<sigrok::InputFormat> format,
+ const std::map<std::string, Glib::VariantBase> &options) :
+ File(file_name),
+ context_(context),
+ input_(format->create_input(options)),
+ interrupt_(false)
+{
+ if (!input_)
+ throw QString("Failed to create input");
+}
+
+void InputFile::open()
+{
+ if (session_)
+ close();
+
+ session_ = context_->create_session();
+}
+
+void InputFile::close()
+{
+ if (session_)
+ session_->remove_devices();
+}
+
+void InputFile::start()
+{
+}
+
+void InputFile::run()
+{
+ char buffer[BufferSize];
+ bool need_device = true;
+
+ assert(session_);
+ assert(input_);
+
+ interrupt_ = false;
+ std::ifstream f(file_name_);
+ while (!interrupt_ && f) {
+ f.read(buffer, BufferSize);
+ const std::streamsize size = f.gcount();
+ if (size == 0)
+ break;
+
+ input_->send(buffer, size);
+
+ if (need_device) {
+ try {
+ device_ = input_->device();
+ } catch (sigrok::Error) {
+ break;
+ }
+
+ session_->add_device(device_);
+ need_device = false;
+ }
+
+ if (size != BufferSize)
+ break;
+ }
+
+ input_->end();
+}
+
+void InputFile::stop()
+{
+ interrupt_ = true;
+}
+
+} // namespace devices
+} // namespace pv
diff --git a/pv/view/viewport.h b/pv/devices/inputfile.hpp
similarity index 51%
rename from pv/view/viewport.h
rename to pv/devices/inputfile.hpp
index 0474b7e..9b9aee8 100644
--- a/pv/view/viewport.h
+++ b/pv/devices/inputfile.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,51 +18,48 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_VIEWPORT_H
-#define PULSEVIEW_PV_VIEW_VIEWPORT_H
+#ifndef PULSEVIEW_PV_DEVICE_INPUTFILE_HPP
+#define PULSEVIEW_PV_DEVICE_INPUTFILE_HPP
-#include <QTimer>
-#include <QWidget>
+#include <atomic>
-class QPainter;
-class QPaintEvent;
-class SigSession;
+#include <libsigrokcxx/libsigrokcxx.hpp>
-namespace pv {
-namespace view {
+#include "file.hpp"
-class View;
+namespace pv {
+namespace devices {
-class Viewport : public QWidget
+class InputFile final : public File
{
- Q_OBJECT
+private:
+ static const std::streamsize BufferSize;
public:
- explicit Viewport(View &parent);
+ InputFile(const std::shared_ptr<sigrok::Context> &context,
+ const std::string &file_name,
+ std::shared_ptr<sigrok::InputFormat> format,
+ const std::map<std::string, Glib::VariantBase> &options);
- int get_total_height() const;
+ void open();
-protected:
- void paintEvent(QPaintEvent *event);
+ void close();
-private:
- void mousePressEvent(QMouseEvent *event);
- void mouseMoveEvent(QMouseEvent *event);
- void mouseDoubleClickEvent(QMouseEvent * event);
- void wheelEvent(QWheelEvent *event);
+ void start();
+
+ void run();
-private slots:
- void on_signals_changed();
- void on_signals_moved();
+ void stop();
private:
- View &_view;
+ const std::shared_ptr<sigrok::Context> context_;
+ const std::shared_ptr<sigrok::Input> input_;
- QPoint _mouse_down_point;
- double _mouse_down_offset;
+ std::atomic<bool> interrupt_;
};
-} // namespace view
+} // namespace devices
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_VIEWPORT_H
+#endif // PULSEVIEW_PV_SESSIONS_INPUTFILE_HPP
+
diff --git a/pv/data/signaldata.cpp b/pv/devices/sessionfile.cpp
similarity index 59%
copy from pv/data/signaldata.cpp
copy to pv/devices/sessionfile.cpp
index 04f1d3f..8a583ee 100644
--- a/pv/data/signaldata.cpp
+++ b/pv/devices/sessionfile.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,32 +18,36 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "signaldata.h"
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <boost/filesystem.hpp>
+
+#include "sessionfile.hpp"
namespace pv {
-namespace data {
+namespace devices {
-SignalData::SignalData() :
- _start_time(0),
- _samplerate(0)
+SessionFile::SessionFile(const std::shared_ptr<sigrok::Context> context,
+ const std::string &file_name) :
+ File(file_name),
+ context_(context)
{
}
-double SignalData::samplerate() const
+void SessionFile::open()
{
- return _samplerate;
-}
+ if (session_)
+ close();
-void SignalData::set_samplerate(double samplerate)
-{
- _samplerate = samplerate;
- clear();
+ session_ = context_->load_session(file_name_);
+ device_ = session_->devices()[0];
}
-double SignalData::get_start_time() const
+void SessionFile::close()
{
- return _start_time;
+ if (session_)
+ session_->remove_devices();
}
-} // namespace data
+} // namespace devices
} // namespace pv
diff --git a/pv/dialogs/about.h b/pv/devices/sessionfile.hpp
similarity index 62%
copy from pv/dialogs/about.h
copy to pv/devices/sessionfile.hpp
index cc153ad..9494571 100644
--- a/pv/dialogs/about.h
+++ b/pv/devices/sessionfile.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,36 +18,35 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_ABOUT_H
-#define PULSEVIEW_PV_ABOUT_H
-
-#include <QDialog>
+#ifndef PULSEVIEW_PV_DEVICES_SESSIONFILE_HPP
+#define PULSEVIEW_PV_DEVICES_SESSIONFILE_HPP
#include <memory>
-class QTextDocument;
+#include "file.hpp"
-namespace Ui {
-class About;
-}
+namespace sigrok {
+class Context;
+} // sigrok
namespace pv {
-namespace dialogs {
+namespace devices {
-class About : public QDialog
+class SessionFile final : public File
{
- Q_OBJECT
-
public:
- explicit About(QWidget *parent = 0);
- ~About();
+ SessionFile(const std::shared_ptr<sigrok::Context> context,
+ const std::string &file_name);
+
+ void open();
+
+ void close();
private:
- Ui::About *ui;
- std::auto_ptr<QTextDocument> supportedDoc;
+ const std::shared_ptr<sigrok::Context> context_;
};
-} // namespace dialogs
+} // namespace devices
} // namespace pv
-#endif // PULSEVIEW_PV_ABOUT_H
+#endif // PULSEVIEW_PV_DEVICES_SESSIONFILE_HPP
diff --git a/pv/dialogs/about.cpp b/pv/dialogs/about.cpp
index fe0f16a..20a0299 100644
--- a/pv/dialogs/about.cpp
+++ b/pv/dialogs/about.cpp
@@ -24,26 +24,21 @@
#include <QTextDocument>
-#include "about.h"
+#include "about.hpp"
#include <ui_about.h>
-/* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */
-#define __STDC_FORMAT_MACROS
-#include <glib.h>
-#include <libsigrok/libsigrok.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
+using std::shared_ptr;
+using sigrok::Context;
namespace pv {
namespace dialogs {
-About::About(QWidget *parent) :
+About::About(shared_ptr<Context> context, QWidget *parent) :
QDialog(parent),
ui(new Ui::About)
{
- struct sr_dev_driver **drivers;
- struct sr_input_format **inputs;
- struct sr_output_format **outputs;
-
#ifdef ENABLE_DECODE
struct srd_decoder *dec;
#endif
@@ -66,31 +61,28 @@ About::About(QWidget *parent) :
s.append("<tr><td colspan=\"2\"><b>" +
tr("Supported hardware drivers:") +
"</b></td></tr>");
- drivers = sr_driver_list();
- for (int i = 0; drivers[i]; ++i) {
+ for (auto entry : context->drivers()) {
s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
- .arg(QString::fromUtf8(drivers[i]->name))
- .arg(QString::fromUtf8(drivers[i]->longname)));
+ .arg(QString::fromUtf8(entry.first.c_str()))
+ .arg(QString::fromUtf8(entry.second->long_name().c_str())));
}
s.append("<tr><td colspan=\"2\"><b>" +
tr("Supported input formats:") +
"</b></td></tr>");
- inputs = sr_input_list();
- for (int i = 0; inputs[i]; ++i) {
+ for (auto entry : context->input_formats()) {
s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
- .arg(QString::fromUtf8(inputs[i]->id))
- .arg(QString::fromUtf8(inputs[i]->description)));
+ .arg(QString::fromUtf8(entry.first.c_str()))
+ .arg(QString::fromUtf8(entry.second->description().c_str())));
}
s.append("<tr><td colspan=\"2\"><b>" +
tr("Supported output formats:") +
"</b></td></tr>");
- outputs = sr_output_list();
- for (int i = 0; outputs[i]; ++i) {
+ for (auto entry : context->output_formats()) {
s.append(QString("<tr><td><i>%1</i></td><td>%2</td></tr>")
- .arg(QString::fromUtf8(outputs[i]->id))
- .arg(QString::fromUtf8(outputs[i]->description)));
+ .arg(QString::fromUtf8(entry.first.c_str()))
+ .arg(QString::fromUtf8(entry.second->description().c_str())));
}
#ifdef ENABLE_DECODE
@@ -107,9 +99,9 @@ About::About(QWidget *parent) :
s.append("</table>");
- supportedDoc.reset(new QTextDocument(this));
+ supportedDoc = new QTextDocument(this);
supportedDoc->setHtml(s);
- ui->supportList->setDocument(supportedDoc.get());
+ ui->supportList->setDocument(supportedDoc);
}
About::~About()
diff --git a/pv/dialogs/about.h b/pv/dialogs/about.hpp
similarity index 81%
copy from pv/dialogs/about.h
copy to pv/dialogs/about.hpp
index cc153ad..1baa603 100644
--- a/pv/dialogs/about.h
+++ b/pv/dialogs/about.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_ABOUT_H
-#define PULSEVIEW_PV_ABOUT_H
+#ifndef PULSEVIEW_PV_ABOUT_HPP
+#define PULSEVIEW_PV_ABOUT_HPP
#include <QDialog>
@@ -27,6 +27,10 @@
class QTextDocument;
+namespace sigrok {
+ class Context;
+}
+
namespace Ui {
class About;
}
@@ -39,15 +43,15 @@ class About : public QDialog
Q_OBJECT
public:
- explicit About(QWidget *parent = 0);
+ explicit About(std::shared_ptr<sigrok::Context> context, QWidget *parent = 0);
~About();
private:
Ui::About *ui;
- std::auto_ptr<QTextDocument> supportedDoc;
+ QTextDocument *supportedDoc;
};
} // namespace dialogs
} // namespace pv
-#endif // PULSEVIEW_PV_ABOUT_H
+#endif // PULSEVIEW_PV_ABOUT_HPP
diff --git a/pv/dialogs/about.ui b/pv/dialogs/about.ui
index 10163f9..11450f7 100644
--- a/pv/dialogs/about.ui
+++ b/pv/dialogs/about.ui
@@ -28,7 +28,7 @@
<string/>
</property>
<property name="pixmap">
- <pixmap resource="pulseview.qrc">:/icons/sigrok-logo-notext.png</pixmap>
+ <pixmap resource="pulseview.qrc">:/icons/sigrok-logo-notext.svg</pixmap>
</property>
</widget>
</item>
diff --git a/pv/dialogs/connect.cpp b/pv/dialogs/connect.cpp
index d5ab9c4..4d33cc9 100644
--- a/pv/dialogs/connect.cpp
+++ b/pv/dialogs/connect.cpp
@@ -18,95 +18,87 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <boost/foreach.hpp>
+#include <cassert>
-#include <libsigrok/libsigrok.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
-#include "connect.h"
+#include "connect.hpp"
-#include "pv/devicemanager.h"
-#include "pv/device/device.h"
+#include <pv/devicemanager.hpp>
+#include <pv/devices/hardwaredevice.hpp>
-extern "C" {
-/* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */
-#define __STDC_FORMAT_MACROS
-#include <glib.h>
-#include <libsigrok/libsigrok.h>
-}
-
-using boost::shared_ptr;
using std::list;
+using std::map;
+using std::shared_ptr;
using std::string;
-extern sr_context *sr_ctx;
+using Glib::ustring;
+using Glib::Variant;
+using Glib::VariantBase;
+
+using sigrok::ConfigKey;
+using sigrok::Driver;
+using sigrok::Error;
+
+using pv::devices::HardwareDevice;
namespace pv {
namespace dialogs {
Connect::Connect(QWidget *parent, pv::DeviceManager &device_manager) :
QDialog(parent),
- _device_manager(device_manager),
- _layout(this),
- _form(this),
- _form_layout(&_form),
- _drivers(&_form),
- _serial_device(&_form),
- _scan_button(tr("Scan for Devices"), this),
- _device_list(this),
- _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
+ device_manager_(device_manager),
+ layout_(this),
+ form_(this),
+ form_layout_(&form_),
+ drivers_(&form_),
+ serial_devices_(&form_),
+ scan_button_(tr("&Scan for Devices"), this),
+ device_list_(this),
+ button_box_(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
Qt::Horizontal, this)
{
setWindowTitle(tr("Connect to Device"));
- connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept()));
- connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject()));
+ connect(&button_box_, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(&button_box_, SIGNAL(rejected()), this, SLOT(reject()));
populate_drivers();
- connect(&_drivers, SIGNAL(activated(int)),
+ connect(&drivers_, SIGNAL(activated(int)),
this, SLOT(device_selected(int)));
- _form.setLayout(&_form_layout);
- _form_layout.addRow(tr("Driver"), &_drivers);
+ form_.setLayout(&form_layout_);
+ form_layout_.addRow(tr("&Driver"), &drivers_);
- _form_layout.addRow(tr("Serial Port"), &_serial_device);
+ form_layout_.addRow(tr("Serial &Port"), &serial_devices_);
+ serial_devices_.setEditable(true);
unset_connection();
- connect(&_scan_button, SIGNAL(pressed()),
+ connect(&scan_button_, SIGNAL(pressed()),
this, SLOT(scan_pressed()));
- setLayout(&_layout);
- _layout.addWidget(&_form);
- _layout.addWidget(&_scan_button);
- _layout.addWidget(&_device_list);
- _layout.addWidget(&_button_box);
+ setLayout(&layout_);
+ layout_.addWidget(&form_);
+ layout_.addWidget(&scan_button_);
+ layout_.addWidget(&device_list_);
+ layout_.addWidget(&button_box_);
}
-shared_ptr<device::Device> Connect::get_selected_device() const
+shared_ptr<HardwareDevice> Connect::get_selected_device() const
{
- const QListWidgetItem *const item = _device_list.currentItem();
+ const QListWidgetItem *const item = device_list_.currentItem();
if (!item)
- return shared_ptr<device::Device>();
-
- const sr_dev_inst *const sdi = (sr_dev_inst*)item->data(
- Qt::UserRole).value<void*>();
- assert(sdi);
-
- std::map<const sr_dev_inst*, boost::shared_ptr<pv::device::Device> >::
- const_iterator iter = _device_map.find(sdi);
- assert(iter != _device_map.end());
+ return shared_ptr<HardwareDevice>();
- return (*iter).second;
+ return item->data(Qt::UserRole).value<shared_ptr<HardwareDevice>>();
}
void Connect::populate_drivers()
{
- gsize num_opts = 0;
- const int32_t *hwopts;
- struct sr_dev_driver **drivers = sr_driver_list();
- GVariant *gvar_opts;
-
- for (int i = 0; drivers[i]; ++i) {
+ for (auto entry : device_manager_.context()->drivers()) {
+ auto name = entry.first;
+ auto driver = entry.second;
/**
* We currently only support devices that can deliver
* samples at a fixed samplerate i.e. oscilloscopes and
@@ -114,127 +106,99 @@ void Connect::populate_drivers()
* @todo Add support for non-monotonic devices i.e. DMMs
* and sensors.
*/
- bool supported_device = false;
- if ((sr_config_list(drivers[i], NULL, NULL,
- SR_CONF_DEVICE_OPTIONS, &gvar_opts) == SR_OK)) {
- hwopts = (const int32_t *)g_variant_get_fixed_array(gvar_opts,
- &num_opts, sizeof(int32_t));
- for (unsigned int j = 0; j < num_opts; j++)
- if (hwopts[j] == SR_CONF_SAMPLERATE) {
- supported_device = true;
- break;
- }
- }
+ const auto keys = driver->config_keys();
+
+ bool supported_device = keys.count(ConfigKey::LOGIC_ANALYZER) |
+ keys.count(ConfigKey::OSCILLOSCOPE);
if (supported_device)
- _drivers.addItem(QString("%1 (%2)").arg(
- drivers[i]->longname).arg(drivers[i]->name),
- qVariantFromValue((void*)drivers[i]));
+ drivers_.addItem(QString("%1 (%2)").arg(
+ driver->long_name().c_str()).arg(name.c_str()),
+ qVariantFromValue(driver));
}
}
+void Connect::populate_serials(shared_ptr<Driver> driver)
+{
+ serial_devices_.clear();
+ for (auto serial : device_manager_.context()->serials(driver))
+ serial_devices_.addItem(QString("%1 (%2)").arg(
+ serial.first.c_str()).arg(serial.second.c_str()),
+ QString::fromStdString(serial.first));
+}
+
void Connect::unset_connection()
{
- _device_list.clear();
- _device_map.clear();
- _serial_device.hide();
- _form_layout.labelForField(&_serial_device)->hide();
- _button_box.button(QDialogButtonBox::Ok)->setDisabled(true);
+ device_list_.clear();
+ serial_devices_.hide();
+ form_layout_.labelForField(&serial_devices_)->hide();
+ button_box_.button(QDialogButtonBox::Ok)->setDisabled(true);
}
-void Connect::set_serial_connection()
+void Connect::set_serial_connection(shared_ptr<Driver> driver)
{
- _serial_device.show();
- _form_layout.labelForField(&_serial_device)->show();
+ populate_serials(driver);
+ serial_devices_.show();
+ form_layout_.labelForField(&serial_devices_)->show();
}
void Connect::scan_pressed()
{
- _device_list.clear();
- _device_map.clear();
+ device_list_.clear();
- const int index = _drivers.currentIndex();
+ const int index = drivers_.currentIndex();
if (index == -1)
return;
- sr_dev_driver *const driver = (sr_dev_driver*)_drivers.itemData(
- index).value<void*>();
+ shared_ptr<Driver> driver =
+ drivers_.itemData(index).value<shared_ptr<Driver>>();
- GSList *drvopts = NULL;
+ assert(driver);
- if (_serial_device.isVisible()) {
- sr_config *const src = (sr_config*)g_try_malloc(sizeof(sr_config));
- src->key = SR_CONF_CONN;
- const QByteArray byteArray = _serial_device.text().toUtf8();
- src->data = g_variant_new_string((const gchar*)byteArray.constData());
- drvopts = g_slist_append(drvopts, src);
- }
-
- const list< shared_ptr<device::Device> > devices =
- _device_manager.driver_scan(driver, drvopts);
+ map<const ConfigKey *, VariantBase> drvopts;
- g_slist_free_full(drvopts, (GDestroyNotify)free_drvopts);
+ if (serial_devices_.isVisible()) {
+ QString serial;
+ const int index = serial_devices_.currentIndex();
+ if (index >= 0 && index < serial_devices_.count() &&
+ serial_devices_.currentText() == serial_devices_.itemText(index))
+ serial = serial_devices_.itemData(index).value<QString>();
+ else
+ serial = serial_devices_.currentText();
+ drvopts[ConfigKey::CONN] = Variant<ustring>::create(
+ serial.toUtf8().constData());
+ }
- BOOST_FOREACH(shared_ptr<device::Device> dev_inst, devices)
- {
- assert(dev_inst);
- const sr_dev_inst *const sdi = dev_inst->dev_inst();
- assert(sdi);
+ const list< shared_ptr<HardwareDevice> > devices =
+ device_manager_.driver_scan(driver, drvopts);
- const string title = dev_inst->format_device_title();
- QString text = QString::fromUtf8(title.c_str());
+ for (shared_ptr<HardwareDevice> device : devices) {
+ assert(device);
- if (sdi->channels) {
- text += QString(" with %1 channels").arg(
- g_slist_length(sdi->channels));
- }
+ QString text = QString::fromStdString(
+ device->display_name(device_manager_));
+ text += QString(" with %1 channels").arg(
+ device->device()->channels().size());
QListWidgetItem *const item = new QListWidgetItem(text,
- &_device_list);
- item->setData(Qt::UserRole, qVariantFromValue((void*)sdi));
- _device_list.addItem(item);
- _device_map[sdi] = dev_inst;
+ &device_list_);
+ item->setData(Qt::UserRole, qVariantFromValue(device));
+ device_list_.addItem(item);
}
- _device_list.setCurrentRow(0);
- _button_box.button(QDialogButtonBox::Ok)->setDisabled(_device_list.count() == 0);
+ device_list_.setCurrentRow(0);
+ button_box_.button(QDialogButtonBox::Ok)->setDisabled(device_list_.count() == 0);
}
void Connect::device_selected(int index)
{
- gsize num_opts = 0;
- const int32_t *hwopts;
- GVariant *gvar_list;
- sr_dev_driver *const driver = (sr_dev_driver*)_drivers.itemData(
- index).value<void*>();
+ shared_ptr<Driver> driver =
+ drivers_.itemData(index).value<shared_ptr<Driver>>();
unset_connection();
- if ((sr_config_list(driver, NULL, NULL,
- SR_CONF_SCAN_OPTIONS, &gvar_list) == SR_OK)) {
- hwopts = (const int32_t *)g_variant_get_fixed_array(gvar_list,
- &num_opts, sizeof(int32_t));
-
- for (unsigned int i = 0; i < num_opts; i++) {
- switch(hwopts[i]) {
- case SR_CONF_SERIALCOMM:
- set_serial_connection();
- break;
-
- default:
- continue;
- }
-
- break;
- }
- g_variant_unref(gvar_list);
- }
-}
-
-void Connect::free_drvopts(struct sr_config *src)
-{
- g_variant_unref(src->data);
- g_free(src);
+ if (driver->scan_options().count(ConfigKey::SERIALCOMM))
+ set_serial_connection(driver);
}
} // namespace dialogs
diff --git a/pv/dialogs/connect.h b/pv/dialogs/connect.hpp
similarity index 63%
rename from pv/dialogs/connect.h
rename to pv/dialogs/connect.hpp
index 1de0ed8..a8f793a 100644
--- a/pv/dialogs/connect.h
+++ b/pv/dialogs/connect.hpp
@@ -18,10 +18,10 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_CONNECT_H
-#define PULSEVIEW_PV_CONNECT_H
+#ifndef PULSEVIEW_PV_CONNECT_HPP
+#define PULSEVIEW_PV_CONNECT_HPP
-#include <boost/shared_ptr.hpp>
+#include <memory>
#include <QComboBox>
#include <QDialog>
@@ -32,16 +32,22 @@
#include <QPushButton>
#include <QVBoxLayout>
-struct sr_config;
-struct sr_dev_inst;
+namespace sigrok {
+class Driver;
+}
namespace pv {
+namespace devices {
+class HardwareDevice;
+}
+}
-class DeviceManager;
+Q_DECLARE_METATYPE(std::shared_ptr<sigrok::Driver>);
+Q_DECLARE_METATYPE(std::shared_ptr<pv::devices::HardwareDevice>);
-namespace device {
-class Device;
-}
+namespace pv {
+
+class DeviceManager;
namespace dialogs {
@@ -52,44 +58,41 @@ class Connect : public QDialog
public:
Connect(QWidget *parent, pv::DeviceManager &device_manager);
- boost::shared_ptr<device::Device> get_selected_device() const;
+ std::shared_ptr<devices::HardwareDevice> get_selected_device() const;
private:
void populate_drivers();
+ void populate_serials(std::shared_ptr<sigrok::Driver> driver);
+
void unset_connection();
- void set_serial_connection();
+ void set_serial_connection(std::shared_ptr<sigrok::Driver> driver);
-private slots:
+private Q_SLOTS:
void device_selected(int index);
void scan_pressed();
private:
- static void free_drvopts(sr_config *src);
-
-private:
- pv::DeviceManager &_device_manager;
+ pv::DeviceManager &device_manager_;
- QVBoxLayout _layout;
+ QVBoxLayout layout_;
- QWidget _form;
- QFormLayout _form_layout;
+ QWidget form_;
+ QFormLayout form_layout_;
- QComboBox _drivers;
+ QComboBox drivers_;
- QLineEdit _serial_device;
+ QComboBox serial_devices_;
- QPushButton _scan_button;
- QListWidget _device_list;
- std::map<const sr_dev_inst*, boost::shared_ptr<pv::device::Device> >
- _device_map;
+ QPushButton scan_button_;
+ QListWidget device_list_;
- QDialogButtonBox _button_box;
+ QDialogButtonBox button_box_;
};
} // namespace dialogs
} // namespace pv
-#endif // PULSEVIEW_PV_CONNECT_H
+#endif // PULSEVIEW_PV_CONNECT_HPP
diff --git a/pv/dialogs/about.h b/pv/dialogs/inputoutputoptions.cpp
similarity index 50%
rename from pv/dialogs/about.h
rename to pv/dialogs/inputoutputoptions.cpp
index cc153ad..8f013a2 100644
--- a/pv/dialogs/about.h
+++ b/pv/dialogs/inputoutputoptions.cpp
@@ -18,36 +18,52 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_ABOUT_H
-#define PULSEVIEW_PV_ABOUT_H
+#include "inputoutputoptions.hpp"
-#include <QDialog>
+#include <pv/prop/property.hpp>
-#include <memory>
+using std::map;
+using std::shared_ptr;
+using std::string;
-class QTextDocument;
+using Glib::VariantBase;
-namespace Ui {
-class About;
-}
+using sigrok::Option;
namespace pv {
namespace dialogs {
-class About : public QDialog
+InputOutputOptions::InputOutputOptions(const QString &title,
+ const map<string, shared_ptr<Option>> &options, QWidget *parent) :
+ QDialog(parent),
+ layout_(this),
+ button_box_(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
+ Qt::Horizontal, this),
+ binding_(options)
{
- Q_OBJECT
+ setWindowTitle(title);
+
+ connect(&button_box_, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(&button_box_, SIGNAL(rejected()), this, SLOT(reject()));
+
+ setLayout(&layout_);
-public:
- explicit About(QWidget *parent = 0);
- ~About();
+ layout_.addWidget(binding_.get_property_form(this));
+ layout_.addWidget(&button_box_);
+}
-private:
- Ui::About *ui;
- std::auto_ptr<QTextDocument> supportedDoc;
-};
+const map<string, VariantBase>& InputOutputOptions::options() const
+{
+ return binding_.options();
+}
+
+void InputOutputOptions::accept()
+{
+ QDialog::accept();
+
+ // Commit the properties
+ binding_.commit();
+}
} // namespace dialogs
} // namespace pv
-
-#endif // PULSEVIEW_PV_ABOUT_H
diff --git a/pv/dialogs/inputoutputoptions.hpp b/pv/dialogs/inputoutputoptions.hpp
new file mode 100644
index 0000000..cde778c
--- /dev/null
+++ b/pv/dialogs/inputoutputoptions.hpp
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_DIALOGS_INPUTOUTPUTOPTIONS_HPP
+#define PULSEVIEW_PV_DIALOGS_INPUTOUTPUTOPTIONS_HPP
+
+#include <QDialog>
+#include <QDialogButtonBox>
+#include <QGroupBox>
+#include <QVBoxLayout>
+
+#include <pv/binding/inputoutput.hpp>
+
+namespace pv {
+namespace dialogs {
+
+/**
+ * Presents the selection of inpout/output options to the user.
+ */
+class InputOutputOptions : public QDialog
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor.
+ * @param title the title of the dialog.
+ * @param options the map of options to use as a template.
+ * @param parent the parent widget of the dialog.
+ */
+ InputOutputOptions(const QString &title,
+ const std::map<std::string, std::shared_ptr<sigrok::Option>>
+ &options,
+ QWidget *parent);
+
+ /**
+ * Gets the map of selected options.
+ * @return the options.
+ */
+ const std::map<std::string, Glib::VariantBase>& options() const;
+
+protected:
+ void accept();
+
+private:
+ QVBoxLayout layout_;
+ QDialogButtonBox button_box_;
+ pv::binding::InputOutput binding_;
+};
+
+} // namespace dialogs
+} // namespace pv
+
+#endif // PULSEVIEW_PV_INPUTOUTPUTOPTIONS_HPP
diff --git a/pv/dialogs/storeprogress.cpp b/pv/dialogs/storeprogress.cpp
index 7853173..5aeafd1 100644
--- a/pv/dialogs/storeprogress.cpp
+++ b/pv/dialogs/storeprogress.cpp
@@ -18,30 +18,41 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "storeprogress.h"
+#include <cassert>
#include <QMessageBox>
+#include "storeprogress.hpp"
+
+using std::map;
+using std::string;
+
+using Glib::VariantBase;
+
namespace pv {
namespace dialogs {
StoreProgress::StoreProgress(const QString &file_name,
- const SigSession &session, QWidget *parent) :
+ const std::shared_ptr<sigrok::OutputFormat> output_format,
+ const map<string, VariantBase> &options,
+ const std::pair<uint64_t, uint64_t> sample_range,
+ const Session &session, QWidget *parent) :
QProgressDialog(tr("Saving..."), tr("Cancel"), 0, 0, parent),
- _session(file_name.toStdString(), session)
+ session_(file_name.toStdString(), output_format, options, sample_range,
+ session)
{
- connect(&_session, SIGNAL(progress_updated()),
+ connect(&session_, SIGNAL(progress_updated()),
this, SLOT(on_progress_updated()));
}
StoreProgress::~StoreProgress()
{
- _session.wait();
+ session_.wait();
}
void StoreProgress::run()
{
- if (_session.start())
+ if (session_.start())
show();
else
show_error();
@@ -51,7 +62,7 @@ void StoreProgress::show_error()
{
QMessageBox msg(parentWidget());
msg.setText(tr("Failed to save session."));
- msg.setInformativeText(_session.error());
+ msg.setInformativeText(session_.error());
msg.setStandardButtons(QMessageBox::Ok);
msg.setIcon(QMessageBox::Warning);
msg.exec();
@@ -59,25 +70,23 @@ void StoreProgress::show_error()
void StoreProgress::closeEvent(QCloseEvent*)
{
- _session.cancel();
+ session_.cancel();
}
void StoreProgress::on_progress_updated()
{
- const std::pair<uint64_t, uint64_t> p = _session.progress();
+ const std::pair<int, int> p = session_.progress();
assert(p.first <= p.second);
- setValue(p.first);
- setMaximum(p.second);
-
- const QString err = _session.error();
- if (!err.isEmpty()) {
- show_error();
+ if (p.second) {
+ setValue(p.first);
+ setMaximum(p.second);
+ } else {
+ const QString err = session_.error();
+ if (!err.isEmpty())
+ show_error();
close();
}
-
- if (p.first == p.second)
- close();
}
} // dialogs
diff --git a/pv/dialogs/storeprogress.h b/pv/dialogs/storeprogress.hpp
similarity index 70%
rename from pv/dialogs/storeprogress.h
rename to pv/dialogs/storeprogress.hpp
index 61d08d5..3791642 100644
--- a/pv/dialogs/storeprogress.h
+++ b/pv/dialogs/storeprogress.hpp
@@ -18,20 +18,19 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H
-#define PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H
+#ifndef PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_HPP
+#define PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_HPP
+#include <memory>
#include <set>
-#include <boost/shared_ptr.hpp>
-
#include <QProgressDialog>
-#include <pv/storesession.h>
+#include <pv/storesession.hpp>
namespace pv {
-class SigSession;
+class Session;
namespace dialogs {
@@ -40,7 +39,11 @@ class StoreProgress : public QProgressDialog
Q_OBJECT
public:
- StoreProgress(const QString &file_name, const SigSession &session,
+ StoreProgress(const QString &file_name,
+ const std::shared_ptr<sigrok::OutputFormat> output_format,
+ const std::map<std::string, Glib::VariantBase> &options,
+ const std::pair<uint64_t, uint64_t> sample_range,
+ const Session &session,
QWidget *parent = 0);
virtual ~StoreProgress();
@@ -52,14 +55,14 @@ private:
void closeEvent(QCloseEvent*);
-private slots:
+private Q_SLOTS:
void on_progress_updated();
private:
- pv::StoreSession _session;
+ pv::StoreSession session_;
};
} // dialogs
} // pv
-#endif // PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H
+#endif // PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_HPP
diff --git a/pv/mainwindow.cpp b/pv/mainwindow.cpp
index 0089b11..912ed60 100644
--- a/pv/mainwindow.cpp
+++ b/pv/mainwindow.cpp
@@ -18,227 +18,508 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <cassert>
+
#ifdef ENABLE_DECODE
#include <libsigrokdecode/libsigrokdecode.h>
#endif
-#include <boost/bind.hpp>
-#include <boost/foreach.hpp>
-
#include <algorithm>
#include <iterator>
+#include <boost/algorithm/string/join.hpp>
+
#include <QAction>
#include <QApplication>
#include <QButtonGroup>
+#include <QCloseEvent>
#include <QFileDialog>
#include <QMessageBox>
#include <QMenu>
#include <QMenuBar>
+#include <QSettings>
#include <QStatusBar>
#include <QVBoxLayout>
#include <QWidget>
-#include "mainwindow.h"
-
-#include "devicemanager.h"
-#include "device/device.h"
-#include "dialogs/about.h"
-#include "dialogs/connect.h"
-#include "dialogs/storeprogress.h"
-#include "toolbars/samplingbar.h"
-#include "view/logicsignal.h"
-#include "view/view.h"
+#include "mainwindow.hpp"
+
+#include "devicemanager.hpp"
+#include "util.hpp"
+#include "data/segment.hpp"
+#include "devices/hardwaredevice.hpp"
+#include "devices/inputfile.hpp"
+#include "devices/sessionfile.hpp"
+#include "dialogs/about.hpp"
+#include "dialogs/connect.hpp"
+#include "dialogs/inputoutputoptions.hpp"
+#include "dialogs/storeprogress.hpp"
+#include "toolbars/mainbar.hpp"
+#include "view/logicsignal.hpp"
+#include "view/view.hpp"
+#include "widgets/exportmenu.hpp"
+#include "widgets/importmenu.hpp"
#ifdef ENABLE_DECODE
-#include "widgets/decodermenu.h"
+#include "widgets/decodermenu.hpp"
#endif
+#include "widgets/hidingmenubar.hpp"
-/* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */
-#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <stdarg.h>
#include <glib.h>
-#include <libsigrok/libsigrok.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
-using boost::shared_ptr;
+using std::cerr;
+using std::endl;
using std::list;
+using std::map;
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+using boost::algorithm::join;
+
+using sigrok::Error;
+using sigrok::OutputFormat;
+using sigrok::InputFormat;
namespace pv {
namespace view {
-class SelectableItem;
+class ViewItem;
}
+const char *MainWindow::SettingOpenDirectory = "MainWindow/OpenDirectory";
+const char *MainWindow::SettingSaveDirectory = "MainWindow/SaveDirectory";
+
MainWindow::MainWindow(DeviceManager &device_manager,
- const char *open_file_name,
+ string open_file_name, string open_file_format,
QWidget *parent) :
QMainWindow(parent),
- _device_manager(device_manager),
- _session(device_manager)
+ device_manager_(device_manager),
+ session_(device_manager),
+ action_open_(new QAction(this)),
+ action_save_as_(new QAction(this)),
+ action_save_selection_as_(new QAction(this)),
+ action_connect_(new QAction(this)),
+ action_quit_(new QAction(this)),
+ action_view_zoom_in_(new QAction(this)),
+ action_view_zoom_out_(new QAction(this)),
+ action_view_zoom_fit_(new QAction(this)),
+ action_view_zoom_one_to_one_(new QAction(this)),
+ action_view_sticky_scrolling_(new QAction(this)),
+ action_view_coloured_bg_(new QAction(this)),
+ action_view_show_cursors_(new QAction(this)),
+ action_about_(new QAction(this))
+#ifdef ENABLE_DECODE
+ , menu_decoders_add_(new pv::widgets::DecoderMenu(this, true))
+#endif
{
+ qRegisterMetaType<util::Timestamp>("util::Timestamp");
+
setup_ui();
- if (open_file_name) {
- const QString s(QString::fromUtf8(open_file_name));
- QMetaObject::invokeMethod(this, "load_file",
- Qt::QueuedConnection,
- Q_ARG(QString, s));
+ restore_ui_settings();
+ if (open_file_name.empty())
+ select_init_device();
+ else
+ load_init_file(open_file_name, open_file_format);
+}
+
+QAction* MainWindow::action_open() const
+{
+ return action_open_;
+}
+
+QAction* MainWindow::action_save_as() const
+{
+ return action_save_as_;
+}
+
+QAction* MainWindow::action_save_selection_as() const
+{
+ return action_save_selection_as_;
+}
+
+QAction* MainWindow::action_connect() const
+{
+ return action_connect_;
+}
+
+QAction* MainWindow::action_quit() const
+{
+ return action_quit_;
+}
+
+QAction* MainWindow::action_view_zoom_in() const
+{
+ return action_view_zoom_in_;
+}
+
+QAction* MainWindow::action_view_zoom_out() const
+{
+ return action_view_zoom_out_;
+}
+
+QAction* MainWindow::action_view_zoom_fit() const
+{
+ return action_view_zoom_fit_;
+}
+
+QAction* MainWindow::action_view_zoom_one_to_one() const
+{
+ return action_view_zoom_one_to_one_;
+}
+
+QAction* MainWindow::action_view_sticky_scrolling() const
+{
+ return action_view_sticky_scrolling_;
+}
+
+QAction* MainWindow::action_view_coloured_bg() const
+{
+ return action_view_coloured_bg_;
+}
+
+QAction* MainWindow::action_view_show_cursors() const
+{
+ return action_view_show_cursors_;
+}
+
+QAction* MainWindow::action_about() const
+{
+ return action_about_;
+}
+
+#ifdef ENABLE_DECODE
+QMenu* MainWindow::menu_decoder_add() const
+{
+ return menu_decoders_add_;
+}
+#endif
+
+void MainWindow::run_stop()
+{
+ switch (session_.get_capture_state()) {
+ case Session::Stopped:
+ session_.start_capture([&](QString message) {
+ session_error("Capture failed", message); });
+ break;
+ case Session::AwaitingTrigger:
+ case Session::Running:
+ session_.stop_capture();
+ break;
+ }
+}
+
+void MainWindow::select_device(shared_ptr<devices::Device> device)
+{
+ try {
+ if (device)
+ session_.set_device(device);
+ else
+ session_.set_default_device();
+ } catch (const QString &e) {
+ QMessageBox msg(this);
+ msg.setText(e);
+ msg.setInformativeText(tr("Failed to Select Device"));
+ msg.setStandardButtons(QMessageBox::Ok);
+ msg.setIcon(QMessageBox::Warning);
+ msg.exec();
+ }
+}
+
+void MainWindow::export_file(shared_ptr<OutputFormat> format,
+ bool selection_only)
+{
+ using pv::dialogs::StoreProgress;
+
+ // Stop any currently running capture session
+ session_.stop_capture();
+
+ QSettings settings;
+ const QString dir = settings.value(SettingSaveDirectory).toString();
+
+ std::pair<uint64_t, uint64_t> sample_range;
+
+ // Selection only? Verify that the cursors are active and fetch their values
+ if (selection_only) {
+ if (!view_->cursors()->enabled()) {
+ show_session_error(tr("Missing Cursors"), tr("You need to set the " \
+ "cursors before you can save the data enclosed by them " \
+ "to a session file (e.g. using ALT-V - Show Cursors)."));
+ return;
+ }
+
+ const double samplerate = session_.get_samplerate();
+
+ const pv::util::Timestamp& start_time = view_->cursors()->first()->time();
+ const pv::util::Timestamp& end_time = view_->cursors()->second()->time();
+
+ const uint64_t start_sample = start_time.convert_to<double>() * samplerate;
+ const uint64_t end_sample = end_time.convert_to<double>() * samplerate;
+
+ sample_range = std::make_pair(start_sample, end_sample);
+ } else {
+ sample_range = std::make_pair(0, 0);
+ }
+
+ // Construct the filter
+ const vector<string> exts = format->extensions();
+ QString filter = tr("%1 files ").arg(
+ QString::fromStdString(format->description()));
+
+ if (exts.empty())
+ filter += "(*.*)";
+ else
+ filter += QString("(*.%1);;%2 (*.*)").arg(
+ QString::fromStdString(join(exts, ", *."))).arg(
+ tr("All Files"));
+
+ // Show the file dialog
+ const QString file_name = QFileDialog::getSaveFileName(
+ this, tr("Save File"), dir, filter);
+
+ if (file_name.isEmpty())
+ return;
+
+ const QString abs_path = QFileInfo(file_name).absolutePath();
+ settings.setValue(SettingSaveDirectory, abs_path);
+
+ // Show the options dialog
+ map<string, Glib::VariantBase> options;
+ if (!format->options().empty()) {
+ dialogs::InputOutputOptions dlg(
+ tr("Export %1").arg(QString::fromStdString(
+ format->description())),
+ format->options(), this);
+ if (!dlg.exec())
+ return;
+ options = dlg.options();
+ }
+
+ StoreProgress *dlg = new StoreProgress(file_name, format, options,
+ sample_range, session_, this);
+ dlg->run();
+}
+
+void MainWindow::import_file(shared_ptr<InputFormat> format)
+{
+ assert(format);
+
+ QSettings settings;
+ const QString dir = settings.value(SettingOpenDirectory).toString();
+
+ // Construct the filter
+ const vector<string> exts = format->extensions();
+ const QString filter = exts.empty() ? "" :
+ tr("%1 files (*.%2)").arg(
+ QString::fromStdString(format->description())).arg(
+ QString::fromStdString(join(exts, ", *.")));
+
+ // Show the file dialog
+ const QString file_name = QFileDialog::getOpenFileName(
+ this, tr("Import File"), dir, tr(
+ "%1 files (*.*);;All Files (*.*)").arg(
+ QString::fromStdString(format->description())));
+
+ if (file_name.isEmpty())
+ return;
+
+ // Show the options dialog
+ map<string, Glib::VariantBase> options;
+ if (!format->options().empty()) {
+ dialogs::InputOutputOptions dlg(
+ tr("Import %1").arg(QString::fromStdString(
+ format->description())),
+ format->options(), this);
+ if (!dlg.exec())
+ return;
+ options = dlg.options();
}
+
+ load_file(file_name, format, options);
+
+ const QString abs_path = QFileInfo(file_name).absolutePath();
+ settings.setValue(SettingOpenDirectory, abs_path);
}
void MainWindow::setup_ui()
{
setObjectName(QString::fromUtf8("MainWindow"));
- resize(1024, 768);
-
// Set the window icon
QIcon icon;
- icon.addFile(QString::fromUtf8(":/icons/sigrok-logo-notext.png"),
- QSize(), QIcon::Normal, QIcon::Off);
+ icon.addFile(QString(":/icons/sigrok-logo-notext.svg"));
setWindowIcon(icon);
// Setup the central widget
- _central_widget = new QWidget(this);
- _vertical_layout = new QVBoxLayout(_central_widget);
- _vertical_layout->setSpacing(6);
- _vertical_layout->setContentsMargins(0, 0, 0, 0);
- setCentralWidget(_central_widget);
+ central_widget_ = new QWidget(this);
+ vertical_layout_ = new QVBoxLayout(central_widget_);
+ vertical_layout_->setSpacing(6);
+ vertical_layout_->setContentsMargins(0, 0, 0, 0);
+ setCentralWidget(central_widget_);
- _view = new pv::view::View(_session, this);
+ view_ = new pv::view::View(session_, this);
- _vertical_layout->addWidget(_view);
+ vertical_layout_->addWidget(view_);
// Setup the menu bar
- QMenuBar *const menu_bar = new QMenuBar(this);
- menu_bar->setGeometry(QRect(0, 0, 400, 25));
+ pv::widgets::HidingMenuBar *const menu_bar =
+ new pv::widgets::HidingMenuBar(this);
// File Menu
QMenu *const menu_file = new QMenu;
- menu_file->setTitle(QApplication::translate(
- "MainWindow", "&File", 0, QApplication::UnicodeUTF8));
+ menu_file->setTitle(tr("&File"));
- QAction *const action_open = new QAction(this);
- action_open->setText(QApplication::translate(
- "MainWindow", "&Open...", 0, QApplication::UnicodeUTF8));
- action_open->setIcon(QIcon::fromTheme("document-open",
+ action_open_->setText(tr("&Open..."));
+ action_open_->setIcon(QIcon::fromTheme("document-open",
QIcon(":/icons/document-open.png")));
- action_open->setObjectName(QString::fromUtf8("actionOpen"));
- menu_file->addAction(action_open);
+ action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
+ action_open_->setObjectName(QString::fromUtf8("actionOpen"));
+ menu_file->addAction(action_open_);
- QAction *const action_save_as = new QAction(this);
- action_save_as->setText(QApplication::translate(
- "MainWindow", "&Save As...", 0, QApplication::UnicodeUTF8));
- action_save_as->setIcon(QIcon::fromTheme("document-save-as",
+ action_save_as_->setText(tr("&Save As..."));
+ action_save_as_->setIcon(QIcon::fromTheme("document-save-as",
QIcon(":/icons/document-save-as.png")));
- action_save_as->setObjectName(QString::fromUtf8("actionSaveAs"));
- menu_file->addAction(action_save_as);
+ action_save_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+ action_save_as_->setObjectName(QString::fromUtf8("actionSaveAs"));
+ menu_file->addAction(action_save_as_);
+
+ action_save_selection_as_->setText(tr("Save Selected &Range As..."));
+ action_save_selection_as_->setIcon(QIcon::fromTheme("document-save-as",
+ QIcon(":/icons/document-save-as.png")));
+ action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
+ action_save_selection_as_->setObjectName(QString::fromUtf8("actionSaveSelectionAs"));
+ menu_file->addAction(action_save_selection_as_);
+
+ menu_file->addSeparator();
+
+ widgets::ExportMenu *menu_file_export = new widgets::ExportMenu(this,
+ device_manager_.context());
+ menu_file_export->setTitle(tr("&Export"));
+ connect(menu_file_export,
+ SIGNAL(format_selected(std::shared_ptr<sigrok::OutputFormat>)),
+ this, SLOT(export_file(std::shared_ptr<sigrok::OutputFormat>)));
+ menu_file->addAction(menu_file_export->menuAction());
+
+ widgets::ImportMenu *menu_file_import = new widgets::ImportMenu(this,
+ device_manager_.context());
+ menu_file_import->setTitle(tr("&Import"));
+ connect(menu_file_import,
+ SIGNAL(format_selected(std::shared_ptr<sigrok::InputFormat>)),
+ this, SLOT(import_file(std::shared_ptr<sigrok::InputFormat>)));
+ menu_file->addAction(menu_file_import->menuAction());
menu_file->addSeparator();
- QAction *const action_connect = new QAction(this);
- action_connect->setText(QApplication::translate(
- "MainWindow", "&Connect to Device...", 0,
- QApplication::UnicodeUTF8));
- action_connect->setObjectName(QString::fromUtf8("actionConnect"));
- menu_file->addAction(action_connect);
+ action_connect_->setText(tr("&Connect to Device..."));
+ action_connect_->setObjectName(QString::fromUtf8("actionConnect"));
+ menu_file->addAction(action_connect_);
menu_file->addSeparator();
- QAction *action_quit = new QAction(this);
- action_quit->setText(QApplication::translate(
- "MainWindow", "&Quit", 0, QApplication::UnicodeUTF8));
- action_quit->setIcon(QIcon::fromTheme("application-exit",
+ action_quit_->setText(tr("&Quit"));
+ action_quit_->setIcon(QIcon::fromTheme("application-exit",
QIcon(":/icons/application-exit.png")));
- action_quit->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
- action_quit->setObjectName(QString::fromUtf8("actionQuit"));
- menu_file->addAction(action_quit);
+ action_quit_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q));
+ action_quit_->setObjectName(QString::fromUtf8("actionQuit"));
+ menu_file->addAction(action_quit_);
// View Menu
QMenu *menu_view = new QMenu;
- menu_view->setTitle(QApplication::translate(
- "MainWindow", "&View", 0, QApplication::UnicodeUTF8));
+ menu_view->setTitle(tr("&View"));
- QAction *const action_view_zoom_in = new QAction(this);
- action_view_zoom_in->setText(QApplication::translate(
- "MainWindow", "Zoom &In", 0, QApplication::UnicodeUTF8));
- action_view_zoom_in->setIcon(QIcon::fromTheme("zoom-in",
+ action_view_zoom_in_->setText(tr("Zoom &In"));
+ action_view_zoom_in_->setIcon(QIcon::fromTheme("zoom-in",
QIcon(":/icons/zoom-in.png")));
// simply using Qt::Key_Plus shows no + in the menu
- action_view_zoom_in->setShortcut(QKeySequence::ZoomIn);
- action_view_zoom_in->setObjectName(
+ action_view_zoom_in_->setShortcut(QKeySequence::ZoomIn);
+ action_view_zoom_in_->setObjectName(
QString::fromUtf8("actionViewZoomIn"));
- menu_view->addAction(action_view_zoom_in);
+ menu_view->addAction(action_view_zoom_in_);
- QAction *const action_view_zoom_out = new QAction(this);
- action_view_zoom_out->setText(QApplication::translate(
- "MainWindow", "Zoom &Out", 0, QApplication::UnicodeUTF8));
- action_view_zoom_out->setIcon(QIcon::fromTheme("zoom-out",
+ action_view_zoom_out_->setText(tr("Zoom &Out"));
+ action_view_zoom_out_->setIcon(QIcon::fromTheme("zoom-out",
QIcon(":/icons/zoom-out.png")));
- action_view_zoom_out->setShortcut(QKeySequence::ZoomOut);
- action_view_zoom_out->setObjectName(
+ action_view_zoom_out_->setShortcut(QKeySequence::ZoomOut);
+ action_view_zoom_out_->setObjectName(
QString::fromUtf8("actionViewZoomOut"));
- menu_view->addAction(action_view_zoom_out);
+ menu_view->addAction(action_view_zoom_out_);
- QAction *const action_view_zoom_fit = new QAction(this);
- action_view_zoom_fit->setText(QApplication::translate(
- "MainWindow", "Zoom to &Fit", 0, QApplication::UnicodeUTF8));
- action_view_zoom_fit->setIcon(QIcon::fromTheme("zoom-fit",
+ action_view_zoom_fit_->setCheckable(true);
+ action_view_zoom_fit_->setText(tr("Zoom to &Fit"));
+ action_view_zoom_fit_->setIcon(QIcon::fromTheme("zoom-fit",
QIcon(":/icons/zoom-fit.png")));
- action_view_zoom_fit->setShortcut(QKeySequence(Qt::Key_F));
- action_view_zoom_fit->setObjectName(
+ action_view_zoom_fit_->setShortcut(QKeySequence(Qt::Key_F));
+ action_view_zoom_fit_->setObjectName(
QString::fromUtf8("actionViewZoomFit"));
- menu_view->addAction(action_view_zoom_fit);
+ menu_view->addAction(action_view_zoom_fit_);
- QAction *const action_view_zoom_one_to_one = new QAction(this);
- action_view_zoom_one_to_one->setText(QApplication::translate(
- "MainWindow", "Zoom to &One-to-One", 0,
- QApplication::UnicodeUTF8));
- action_view_zoom_one_to_one->setIcon(QIcon::fromTheme("zoom-original",
+ action_view_zoom_one_to_one_->setText(tr("Zoom to O&ne-to-One"));
+ action_view_zoom_one_to_one_->setIcon(QIcon::fromTheme("zoom-original",
QIcon(":/icons/zoom-original.png")));
- action_view_zoom_one_to_one->setShortcut(QKeySequence(Qt::Key_O));
- action_view_zoom_one_to_one->setObjectName(
+ action_view_zoom_one_to_one_->setShortcut(QKeySequence(Qt::Key_O));
+ action_view_zoom_one_to_one_->setObjectName(
QString::fromUtf8("actionViewZoomOneToOne"));
- menu_view->addAction(action_view_zoom_one_to_one);
+ menu_view->addAction(action_view_zoom_one_to_one_);
+
+ menu_view->addSeparator();
+
+ action_view_sticky_scrolling_->setCheckable(true);
+ action_view_sticky_scrolling_->setChecked(true);
+ action_view_sticky_scrolling_->setShortcut(QKeySequence(Qt::Key_S));
+ action_view_sticky_scrolling_->setObjectName(
+ QString::fromUtf8("actionViewStickyScrolling"));
+ action_view_sticky_scrolling_->setText(tr("&Sticky Scrolling"));
+ menu_view->addAction(action_view_sticky_scrolling_);
+
+ view_->enable_sticky_scrolling(action_view_sticky_scrolling_->isChecked());
menu_view->addSeparator();
- QAction *action_view_show_cursors = new QAction(this);
- action_view_show_cursors->setCheckable(true);
- action_view_show_cursors->setChecked(_view->cursors_shown());
- action_view_show_cursors->setShortcut(QKeySequence(Qt::Key_C));
- action_view_show_cursors->setObjectName(
+ action_view_coloured_bg_->setCheckable(true);
+ action_view_coloured_bg_->setChecked(true);
+ action_view_coloured_bg_->setShortcut(QKeySequence(Qt::Key_B));
+ action_view_coloured_bg_->setObjectName(
+ QString::fromUtf8("actionViewColouredBg"));
+ action_view_coloured_bg_->setText(tr("Use &coloured backgrounds"));
+ menu_view->addAction(action_view_coloured_bg_);
+
+ view_->enable_coloured_bg(action_view_coloured_bg_->isChecked());
+
+ menu_view->addSeparator();
+
+ action_view_show_cursors_->setCheckable(true);
+ action_view_show_cursors_->setChecked(view_->cursors_shown());
+ action_view_show_cursors_->setIcon(QIcon::fromTheme("show-cursors",
+ QIcon(":/icons/show-cursors.svg")));
+ action_view_show_cursors_->setShortcut(QKeySequence(Qt::Key_C));
+ action_view_show_cursors_->setObjectName(
QString::fromUtf8("actionViewShowCursors"));
- action_view_show_cursors->setText(QApplication::translate(
- "MainWindow", "Show &Cursors", 0, QApplication::UnicodeUTF8));
- menu_view->addAction(action_view_show_cursors);
+ action_view_show_cursors_->setText(tr("Show &Cursors"));
+ menu_view->addAction(action_view_show_cursors_);
// Decoders Menu
#ifdef ENABLE_DECODE
QMenu *const menu_decoders = new QMenu;
- menu_decoders->setTitle(QApplication::translate(
- "MainWindow", "&Decoders", 0, QApplication::UnicodeUTF8));
-
- pv::widgets::DecoderMenu *const menu_decoders_add =
- new pv::widgets::DecoderMenu(menu_decoders, true);
- menu_decoders_add->setTitle(QApplication::translate(
- "MainWindow", "&Add", 0, QApplication::UnicodeUTF8));
- connect(menu_decoders_add, SIGNAL(decoder_selected(srd_decoder*)),
+ menu_decoders->setTitle(tr("&Decoders"));
+
+ menu_decoders_add_->setTitle(tr("&Add"));
+ connect(menu_decoders_add_, SIGNAL(decoder_selected(srd_decoder*)),
this, SLOT(add_decoder(srd_decoder*)));
- menu_decoders->addMenu(menu_decoders_add);
+ menu_decoders->addMenu(menu_decoders_add_);
#endif
// Help Menu
QMenu *const menu_help = new QMenu;
- menu_help->setTitle(QApplication::translate(
- "MainWindow", "&Help", 0, QApplication::UnicodeUTF8));
+ menu_help->setTitle(tr("&Help"));
- QAction *const action_about = new QAction(this);
- action_about->setObjectName(QString::fromUtf8("actionAbout"));
- action_about->setText(QApplication::translate(
- "MainWindow", "&About...", 0, QApplication::UnicodeUTF8));
- menu_help->addAction(action_about);
+ action_about_->setObjectName(QString::fromUtf8("actionAbout"));
+ action_about_->setText(tr("&About..."));
+ menu_help->addAction(action_about_);
menu_bar->addAction(menu_file->menuAction());
menu_bar->addAction(menu_view->menuAction());
@@ -250,33 +531,140 @@ void MainWindow::setup_ui()
setMenuBar(menu_bar);
QMetaObject::connectSlotsByName(this);
- // Setup the toolbar
- QToolBar *const toolbar = new QToolBar(tr("Main Toolbar"), this);
- toolbar->addAction(action_open);
- toolbar->addSeparator();
- toolbar->addAction(action_view_zoom_in);
- toolbar->addAction(action_view_zoom_out);
- toolbar->addAction(action_view_zoom_fit);
- addToolBar(toolbar);
+ // Also add all actions to the main window for always-enabled hotkeys
+ for (QAction* action : menu_bar->actions())
+ this->addAction(action);
- // Setup the sampling bar
- _sampling_bar = new toolbars::SamplingBar(_session, this);
+ // Setup the toolbar
+ main_bar_ = new toolbars::MainBar(session_, *this);
// Populate the device list and select the initially selected device
update_device_list();
- connect(_sampling_bar, SIGNAL(run_stop()), this,
- SLOT(run_stop()));
- addToolBar(_sampling_bar);
+ addToolBar(main_bar_);
// Set the title
- setWindowTitle(QApplication::translate("MainWindow", "PulseView", 0,
- QApplication::UnicodeUTF8));
+ setWindowTitle(tr("PulseView"));
- // Setup _session events
- connect(&_session, SIGNAL(capture_state_changed(int)), this,
+ // Setup session_ events
+ connect(&session_, SIGNAL(capture_state_changed(int)), this,
SLOT(capture_state_changed(int)));
+ connect(&session_, SIGNAL(device_selected()), this,
+ SLOT(device_selected()));
+ connect(&session_, SIGNAL(trigger_event(util::Timestamp)), view_,
+ SLOT(trigger_event(util::Timestamp)));
+
+ // Setup view_ events
+ connect(view_, SIGNAL(sticky_scrolling_changed(bool)), this,
+ SLOT(sticky_scrolling_changed(bool)));
+ connect(view_, SIGNAL(always_zoom_to_fit_changed(bool)), this,
+ SLOT(always_zoom_to_fit_changed(bool)));
+
+}
+
+void MainWindow::select_init_device()
+{
+ QSettings settings;
+ map<string, string> dev_info;
+ list<string> key_list;
+
+ // Re-select last used device if possible.
+ settings.beginGroup("Device");
+ key_list.push_back("vendor");
+ key_list.push_back("model");
+ key_list.push_back("version");
+ key_list.push_back("serial_num");
+ key_list.push_back("connection_id");
+
+ for (string key : key_list) {
+ const QString k = QString::fromStdString(key);
+ if (!settings.contains(k))
+ continue;
+
+ const string value = settings.value(k).toString().toStdString();
+ if (!value.empty())
+ dev_info.insert(std::make_pair(key, value));
+ }
+
+ const shared_ptr<devices::HardwareDevice> device =
+ device_manager_.find_device_from_info(dev_info);
+ select_device(device);
+ update_device_list();
+
+ settings.endGroup();
+}
+
+void MainWindow::load_init_file(const std::string &file_name,
+ const std::string &format)
+{
+ shared_ptr<InputFormat> input_format;
+
+ if (!format.empty()) {
+ const map<string, shared_ptr<InputFormat> > formats =
+ device_manager_.context()->input_formats();
+ const auto iter = find_if(formats.begin(), formats.end(),
+ [&](const pair<string, shared_ptr<InputFormat> > f) {
+ return f.first == format; });
+ if (iter == formats.end()) {
+ cerr << "Unexpected input format: " << format << endl;
+ return;
+ }
+
+ input_format = (*iter).second;
+ }
+
+ load_file(QString::fromStdString(file_name), input_format);
+}
+
+
+void MainWindow::save_ui_settings()
+{
+ QSettings settings;
+
+ map<string, string> dev_info;
+ list<string> key_list;
+
+ settings.beginGroup("MainWindow");
+ settings.setValue("state", saveState());
+ settings.setValue("geometry", saveGeometry());
+ settings.endGroup();
+
+ if (session_.device()) {
+ settings.beginGroup("Device");
+ key_list.push_back("vendor");
+ key_list.push_back("model");
+ key_list.push_back("version");
+ key_list.push_back("serial_num");
+ key_list.push_back("connection_id");
+
+ dev_info = device_manager_.get_device_info(
+ session_.device());
+
+ for (string key : key_list) {
+ if (dev_info.count(key))
+ settings.setValue(QString::fromUtf8(key.c_str()),
+ QString::fromUtf8(dev_info.at(key).c_str()));
+ else
+ settings.remove(QString::fromUtf8(key.c_str()));
+ }
+
+ settings.endGroup();
+ }
+}
+
+void MainWindow::restore_ui_settings()
+{
+ QSettings settings;
+
+ settings.beginGroup("MainWindow");
+ if (settings.contains("geometry")) {
+ restoreGeometry(settings.value("geometry").toByteArray());
+ restoreState(settings.value("state").toByteArray());
+ } else
+ resize(1000, 720);
+
+ settings.endGroup();
}
void MainWindow::session_error(
@@ -289,40 +677,54 @@ void MainWindow::session_error(
void MainWindow::update_device_list()
{
- assert(_sampling_bar);
-
- shared_ptr<pv::device::DevInst> selected_device = _session.get_device();
- list< shared_ptr<device::DevInst> > devices;
- std::copy(_device_manager.devices().begin(),
- _device_manager.devices().end(), std::back_inserter(devices));
-
- if (std::find(devices.begin(), devices.end(), selected_device) ==
- devices.end())
- devices.push_back(selected_device);
- assert(selected_device);
-
- _sampling_bar->set_device_list(devices, selected_device);
+ main_bar_->update_device_list();
}
-void MainWindow::load_file(QString file_name)
+void MainWindow::load_file(QString file_name,
+ std::shared_ptr<sigrok::InputFormat> format,
+ const std::map<std::string, Glib::VariantBase> &options)
{
const QString errorMessage(
QString("Failed to load file %1").arg(file_name));
- const QString infoMessage;
try {
- _session.set_file(file_name.toStdString());
- } catch(QString e) {
- show_session_error(tr("Failed to load ") + file_name, e);
- _session.set_default_device();
+ if (format)
+ session_.set_device(shared_ptr<devices::Device>(
+ new devices::InputFile(
+ device_manager_.context(),
+ file_name.toStdString(),
+ format, options)));
+ else
+ session_.set_device(shared_ptr<devices::Device>(
+ new devices::SessionFile(
+ device_manager_.context(),
+ file_name.toStdString())));
+ } catch (Error e) {
+ show_session_error(tr("Failed to load ") + file_name, e.what());
+ session_.set_default_device();
update_device_list();
return;
}
update_device_list();
- _session.start_capture(boost::bind(&MainWindow::session_error, this,
- errorMessage, infoMessage));
+ session_.start_capture([&, errorMessage](QString infoMessage) {
+ session_error(errorMessage, infoMessage); });
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ save_ui_settings();
+ event->accept();
+}
+
+void MainWindow::keyReleaseEvent(QKeyEvent *event)
+{
+ if (event->key() == Qt::Key_Alt) {
+ menuBar()->setHidden(!menuBar()->isHidden());
+ menuBar()->setFocus();
+ }
+ QMainWindow::keyReleaseEvent(event);
}
void MainWindow::show_session_error(
@@ -338,44 +740,44 @@ void MainWindow::show_session_error(
void MainWindow::on_actionOpen_triggered()
{
+ QSettings settings;
+ const QString dir = settings.value(SettingOpenDirectory).toString();
+
// Show the dialog
const QString file_name = QFileDialog::getOpenFileName(
- this, tr("Open File"), "", tr(
+ this, tr("Open File"), dir, tr(
"Sigrok Sessions (*.sr);;"
"All Files (*.*)"));
- if (!file_name.isEmpty())
+
+ if (!file_name.isEmpty()) {
load_file(file_name);
+
+ const QString abs_path = QFileInfo(file_name).absolutePath();
+ settings.setValue(SettingOpenDirectory, abs_path);
+ }
}
void MainWindow::on_actionSaveAs_triggered()
{
- using pv::dialogs::StoreProgress;
-
- // Stop any currently running capture session
- _session.stop_capture();
-
- // Show the dialog
- const QString file_name = QFileDialog::getSaveFileName(
- this, tr("Save File"), "", tr("Sigrok Sessions (*.sr)"));
-
- if (file_name.isEmpty())
- return;
+ export_file(device_manager_.context()->output_formats()["srzip"]);
+}
- StoreProgress *dlg = new StoreProgress(file_name, _session, this);
- dlg->run();
+void MainWindow::on_actionSaveSelectionAs_triggered()
+{
+ export_file(device_manager_.context()->output_formats()["srzip"], true);
}
void MainWindow::on_actionConnect_triggered()
{
// Stop any currently running capture session
- _session.stop_capture();
+ session_.stop_capture();
- dialogs::Connect dlg(this, _device_manager);
+ dialogs::Connect dlg(this, device_manager_);
// If the user selected a device, select it in the device list. Select the
// current device otherwise.
if (dlg.exec())
- _session.set_device(dlg.get_selected_device());
+ select_device(dlg.get_selected_device());
update_device_list();
}
@@ -387,70 +789,85 @@ void MainWindow::on_actionQuit_triggered()
void MainWindow::on_actionViewZoomIn_triggered()
{
- _view->zoom(1);
+ view_->zoom(1);
}
void MainWindow::on_actionViewZoomOut_triggered()
{
- _view->zoom(-1);
+ view_->zoom(-1);
}
void MainWindow::on_actionViewZoomFit_triggered()
{
- _view->zoom_fit();
+ view_->zoom_fit(action_view_zoom_fit_->isChecked());
}
void MainWindow::on_actionViewZoomOneToOne_triggered()
{
- _view->zoom_one_to_one();
+ view_->zoom_one_to_one();
+}
+
+void MainWindow::on_actionViewStickyScrolling_triggered()
+{
+ view_->enable_sticky_scrolling(action_view_sticky_scrolling_->isChecked());
+}
+
+void MainWindow::on_actionViewColouredBg_triggered()
+{
+ view_->enable_coloured_bg(action_view_coloured_bg_->isChecked());
}
void MainWindow::on_actionViewShowCursors_triggered()
{
- assert(_view);
+ assert(view_);
- const bool show = !_view->cursors_shown();
- if(show)
- _view->centre_cursors();
+ const bool show = !view_->cursors_shown();
+ if (show)
+ view_->centre_cursors();
- _view->show_cursors(show);
+ view_->show_cursors(show);
}
void MainWindow::on_actionAbout_triggered()
{
- dialogs::About dlg(this);
+ dialogs::About dlg(device_manager_.context(), this);
dlg.exec();
}
+void MainWindow::sticky_scrolling_changed(bool state)
+{
+ action_view_sticky_scrolling_->setChecked(state);
+}
+
+void MainWindow::always_zoom_to_fit_changed(bool state)
+{
+ action_view_zoom_fit_->setChecked(state);
+}
+
void MainWindow::add_decoder(srd_decoder *decoder)
{
#ifdef ENABLE_DECODE
assert(decoder);
- _session.add_decoder(decoder);
+ session_.add_decoder(decoder);
#else
(void)decoder;
#endif
}
-void MainWindow::run_stop()
+void MainWindow::capture_state_changed(int state)
{
- switch(_session.get_capture_state()) {
- case SigSession::Stopped:
- _session.start_capture(
- boost::bind(&MainWindow::session_error, this,
- QString("Capture failed"), _1));
- break;
-
- case SigSession::AwaitingTrigger:
- case SigSession::Running:
- _session.stop_capture();
- break;
- }
+ main_bar_->set_capture_state((pv::Session::capture_state)state);
}
-void MainWindow::capture_state_changed(int state)
+void MainWindow::device_selected()
{
- _sampling_bar->set_capture_state((pv::SigSession::capture_state)state);
+ // Set the title to include the device/file name
+ const shared_ptr<devices::Device> device = session_.device();
+ if (!device)
+ return;
+
+ const string display_name = device->display_name(device_manager_);
+ setWindowTitle(tr("%1 - PulseView").arg(display_name.c_str()));
}
} // namespace pv
diff --git a/pv/mainwindow.h b/pv/mainwindow.h
deleted file mode 100644
index 1f9dd52..0000000
--- a/pv/mainwindow.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_MAINWINDOW_H
-#define PULSEVIEW_PV_MAINWINDOW_H
-
-#include <list>
-
-#include <boost/weak_ptr.hpp>
-
-#include <QMainWindow>
-
-#include "sigsession.h"
-
-struct srd_decoder;
-
-class QVBoxLayout;
-
-namespace pv {
-
-class DeviceManager;
-
-namespace device {
-class DevInst;
-}
-
-namespace toolbars {
-class ContextBar;
-class SamplingBar;
-}
-
-namespace view {
-class View;
-}
-
-namespace widgets {
-class DecoderMenu;
-}
-
-class MainWindow : public QMainWindow
-{
- Q_OBJECT
-
-public:
- explicit MainWindow(DeviceManager &device_manager,
- const char *open_file_name = NULL,
- QWidget *parent = 0);
-
-private:
- void setup_ui();
-
- void session_error(const QString text, const QString info_text);
-
- /**
- * Updates the device list in the sampling bar
- */
- void update_device_list();
-
-private slots:
- void load_file(QString file_name);
-
-
- void show_session_error(
- const QString text, const QString info_text);
-
- void on_actionOpen_triggered();
- void on_actionSaveAs_triggered();
- void on_actionQuit_triggered();
-
- void on_actionConnect_triggered();
-
- void on_actionViewZoomIn_triggered();
-
- void on_actionViewZoomOut_triggered();
-
- void on_actionViewZoomFit_triggered();
-
- void on_actionViewZoomOneToOne_triggered();
-
- void on_actionViewShowCursors_triggered();
-
- void on_actionAbout_triggered();
-
- void add_decoder(srd_decoder *decoder);
-
- void run_stop();
-
- void capture_state_changed(int state);
-
-private:
- DeviceManager &_device_manager;
-
- SigSession _session;
-
- pv::view::View *_view;
-
- QWidget *_central_widget;
- QVBoxLayout *_vertical_layout;
-
- toolbars::SamplingBar *_sampling_bar;
-};
-
-} // namespace pv
-
-#endif // PULSEVIEW_PV_MAINWINDOW_H
diff --git a/pv/mainwindow.hpp b/pv/mainwindow.hpp
new file mode 100644
index 0000000..208cbe4
--- /dev/null
+++ b/pv/mainwindow.hpp
@@ -0,0 +1,212 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_MAINWINDOW_HPP
+#define PULSEVIEW_PV_MAINWINDOW_HPP
+
+#include <list>
+#include <map>
+#include <memory>
+
+#include <glibmm/variant.h>
+
+#include <QMainWindow>
+
+#include "session.hpp"
+
+struct srd_decoder;
+
+class QVBoxLayout;
+
+namespace sigrok {
+class InputFormat;
+class OutputFormat;
+}
+
+namespace pv {
+
+class DeviceManager;
+
+namespace toolbars {
+class ContextBar;
+class MainBar;
+}
+
+namespace view {
+class View;
+}
+
+namespace widgets {
+#ifdef ENABLE_DECODE
+class DecoderMenu;
+#endif
+}
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+private:
+ /**
+ * Name of the setting used to remember the directory
+ * containing the last file that was opened.
+ */
+ static const char *SettingOpenDirectory;
+
+ /**
+ * Name of the setting used to remember the directory
+ * containing the last file that was saved.
+ */
+ static const char *SettingSaveDirectory;
+
+public:
+ explicit MainWindow(DeviceManager &device_manager,
+ std::string open_file_name = std::string(),
+ std::string open_file_format = std::string(),
+ QWidget *parent = 0);
+
+ QAction* action_open() const;
+ QAction* action_save_as() const;
+ QAction* action_save_selection_as() const;
+ QAction* action_connect() const;
+ QAction* action_quit() const;
+ QAction* action_view_zoom_in() const;
+ QAction* action_view_zoom_out() const;
+ QAction* action_view_zoom_fit() const;
+ QAction* action_view_zoom_one_to_one() const;
+ QAction* action_view_sticky_scrolling() const;
+ QAction* action_view_coloured_bg() const;
+ QAction* action_view_show_cursors() const;
+ QAction* action_about() const;
+
+#ifdef ENABLE_DECODE
+ QMenu* menu_decoder_add() const;
+#endif
+
+ void run_stop();
+
+ void select_device(std::shared_ptr<devices::Device> device);
+
+public Q_SLOTS:
+ void export_file(std::shared_ptr<sigrok::OutputFormat> format,
+ bool selection_only = false);
+ void import_file(std::shared_ptr<sigrok::InputFormat> format);
+
+private:
+ void setup_ui();
+
+ void select_init_device();
+
+ void load_init_file(const std::string &file_name,
+ const std::string &format);
+
+ void save_ui_settings();
+
+ void restore_ui_settings();
+
+ void session_error(const QString text, const QString info_text);
+
+ /**
+ * Updates the device list in the toolbar
+ */
+ void update_device_list();
+
+ void load_file(QString file_name,
+ std::shared_ptr<sigrok::InputFormat> format = nullptr,
+ const std::map<std::string, Glib::VariantBase> &options =
+ std::map<std::string, Glib::VariantBase>());
+
+ void save_selection_to_file();
+
+private:
+ void closeEvent(QCloseEvent *event);
+
+ void keyReleaseEvent(QKeyEvent *event);
+
+private Q_SLOTS:
+ void show_session_error(
+ const QString text, const QString info_text);
+
+ void on_actionOpen_triggered();
+ void on_actionSaveAs_triggered();
+ void on_actionSaveSelectionAs_triggered();
+ void on_actionQuit_triggered();
+
+ void on_actionConnect_triggered();
+
+ void on_actionViewZoomIn_triggered();
+
+ void on_actionViewZoomOut_triggered();
+
+ void on_actionViewZoomFit_triggered();
+
+ void on_actionViewZoomOneToOne_triggered();
+
+ void on_actionViewStickyScrolling_triggered();
+
+ void on_actionViewColouredBg_triggered();
+
+ void on_actionViewShowCursors_triggered();
+
+ void on_actionAbout_triggered();
+
+ void add_decoder(srd_decoder *decoder);
+
+ void capture_state_changed(int state);
+ void device_selected();
+
+ void sticky_scrolling_changed(bool state);
+
+ void always_zoom_to_fit_changed(bool state);
+
+private:
+ DeviceManager &device_manager_;
+
+ Session session_;
+
+ pv::view::View *view_;
+
+ QWidget *central_widget_;
+ QVBoxLayout *vertical_layout_;
+
+ toolbars::MainBar *main_bar_;
+
+ QAction *const action_open_;
+ QAction *const action_save_as_;
+ QAction *const action_save_selection_as_;
+ QAction *const action_connect_;
+ QAction *const action_quit_;
+ QAction *const action_view_zoom_in_;
+ QAction *const action_view_zoom_out_;
+ QAction *const action_view_zoom_fit_;
+ QAction *const action_view_zoom_one_to_one_;
+ QAction *const action_view_sticky_scrolling_;
+ QAction *const action_view_coloured_bg_;
+ QAction *const action_view_show_cursors_;
+ QAction *const action_about_;
+
+#ifdef ENABLE_DECODE
+ QMenu *const menu_decoders_add_;
+#endif
+};
+
+} // namespace pv
+
+#endif // PULSEVIEW_PV_MAINWINDOW_HPP
diff --git a/pv/popups/channels.cpp b/pv/popups/channels.cpp
new file mode 100644
index 0000000..1a042da
--- /dev/null
+++ b/pv/popups/channels.cpp
@@ -0,0 +1,260 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <map>
+
+#ifdef _WIN32
+// Windows: Avoid boost/thread namespace pollution (which includes windows.h).
+#define NOGDI
+#define NORESOURCE
+#endif
+#include <boost/thread/locks.hpp>
+#include <boost/thread/shared_mutex.hpp>
+
+#include <QCheckBox>
+#include <QFormLayout>
+#include <QGridLayout>
+#include <QLabel>
+
+#include "channels.hpp"
+
+#include <pv/binding/device.hpp>
+#include <pv/devices/device.hpp>
+#include <pv/session.hpp>
+#include <pv/view/signal.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using namespace Qt;
+
+using boost::shared_lock;
+using boost::shared_mutex;
+using std::lock_guard;
+using std::map;
+using std::mutex;
+using std::set;
+using std::shared_ptr;
+using std::unordered_set;
+using std::vector;
+
+using sigrok::Channel;
+using sigrok::ChannelGroup;
+using sigrok::Device;
+
+using pv::view::Signal;
+
+namespace pv {
+namespace popups {
+
+Channels::Channels(Session &session, QWidget *parent) :
+ Popup(parent),
+ session_(session),
+ updating_channels_(false),
+ enable_all_channels_(tr("Enable All"), this),
+ disable_all_channels_(tr("Disable All"), this),
+ check_box_mapper_(this)
+{
+ // Create the layout
+ setLayout(&layout_);
+
+ const shared_ptr<sigrok::Device> device = session_.device()->device();
+ assert(device);
+
+ // Collect a set of signals
+ map<shared_ptr<Channel>, shared_ptr<Signal> > signal_map;
+
+ const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
+
+ for (const shared_ptr<Signal> &sig : sigs)
+ signal_map[sig->channel()] = sig;
+
+ // Populate channel groups
+ for (auto entry : device->channel_groups()) {
+ shared_ptr<ChannelGroup> group = entry.second;
+ // Make a set of signals, and removed this signals from the
+ // signal map.
+ vector< shared_ptr<Signal> > group_sigs;
+ for (auto channel : group->channels()) {
+ const auto iter = signal_map.find(channel);
+
+ if (iter == signal_map.end())
+ break;
+
+ group_sigs.push_back((*iter).second);
+ signal_map.erase(iter);
+ }
+
+ populate_group(group, group_sigs);
+ }
+
+ // Make a vector of the remaining channels
+ vector< shared_ptr<Signal> > global_sigs;
+ for (auto channel : device->channels()) {
+ const map<shared_ptr<Channel>, shared_ptr<Signal> >::
+ const_iterator iter = signal_map.find(channel);
+ if (iter != signal_map.end())
+ global_sigs.push_back((*iter).second);
+ }
+
+ // Create a group
+ populate_group(nullptr, global_sigs);
+
+ // Create the enable/disable all buttons
+ connect(&enable_all_channels_, SIGNAL(clicked()),
+ this, SLOT(enable_all_channels()));
+ connect(&disable_all_channels_, SIGNAL(clicked()),
+ this, SLOT(disable_all_channels()));
+
+ enable_all_channels_.setFlat(true);
+ disable_all_channels_.setFlat(true);
+
+ buttons_bar_.addWidget(&enable_all_channels_);
+ buttons_bar_.addWidget(&disable_all_channels_);
+ buttons_bar_.addStretch(1);
+
+ layout_.addRow(&buttons_bar_);
+
+ // Connect the check-box signal mapper
+ connect(&check_box_mapper_, SIGNAL(mapped(QWidget*)),
+ this, SLOT(on_channel_checked(QWidget*)));
+}
+
+void Channels::set_all_channels(bool set)
+{
+ updating_channels_ = true;
+
+ for (map<QCheckBox*, shared_ptr<Signal> >::const_iterator i =
+ check_box_signal_map_.begin();
+ i != check_box_signal_map_.end(); i++) {
+ const shared_ptr<Signal> sig = (*i).second;
+ assert(sig);
+
+ sig->enable(set);
+ (*i).first->setChecked(set);
+ }
+
+ updating_channels_ = false;
+}
+
+void Channels::populate_group(shared_ptr<ChannelGroup> group,
+ const vector< shared_ptr<pv::view::Signal> > sigs)
+{
+ using pv::binding::Device;
+
+ // Only bind options if this is a group. We don't do it for general
+ // options, because these properties are shown in the device config
+ // popup.
+ shared_ptr<Device> binding;
+ if (group)
+ binding = shared_ptr<Device>(new Device(group));
+
+ // Create a title if the group is going to have any content
+ if ((!sigs.empty() || (binding && !binding->properties().empty())) &&
+ group)
+ layout_.addRow(new QLabel(
+ QString("<h3>%1</h3>").arg(group->name().c_str())));
+
+ // Create the channel group grid
+ QGridLayout *const channel_grid =
+ create_channel_group_grid(sigs);
+ layout_.addRow(channel_grid);
+
+ // Create the channel group options
+ if (binding)
+ {
+ binding->add_properties_to_form(&layout_, true);
+ group_bindings_.push_back(binding);
+ }
+}
+
+QGridLayout* Channels::create_channel_group_grid(
+ const vector< shared_ptr<pv::view::Signal> > sigs)
+{
+ int row = 0, col = 0;
+ QGridLayout *const grid = new QGridLayout();
+
+ for (const shared_ptr<pv::view::Signal>& sig : sigs) {
+ assert(sig);
+
+ QCheckBox *const checkbox = new QCheckBox(sig->name());
+ check_box_mapper_.setMapping(checkbox, checkbox);
+ connect(checkbox, SIGNAL(toggled(bool)),
+ &check_box_mapper_, SLOT(map()));
+
+ grid->addWidget(checkbox, row, col);
+
+ check_box_signal_map_[checkbox] = sig;
+
+ if (++col >= 8)
+ col = 0, row++;
+ }
+
+ return grid;
+}
+
+void Channels::showEvent(QShowEvent *e)
+{
+ pv::widgets::Popup::showEvent(e);
+
+ updating_channels_ = true;
+
+ for (map<QCheckBox*, shared_ptr<Signal> >::const_iterator i =
+ check_box_signal_map_.begin();
+ i != check_box_signal_map_.end(); i++) {
+ const shared_ptr<Signal> sig = (*i).second;
+ assert(sig);
+
+ (*i).first->setChecked(sig->enabled());
+ }
+
+ updating_channels_ = false;
+}
+
+void Channels::on_channel_checked(QWidget *widget)
+{
+ if (updating_channels_)
+ return;
+
+ QCheckBox *const check_box = (QCheckBox*)widget;
+ assert(check_box);
+
+ // Look up the signal of this check-box
+ map< QCheckBox*, shared_ptr<Signal> >::const_iterator iter =
+ check_box_signal_map_.find((QCheckBox*)check_box);
+ assert(iter != check_box_signal_map_.end());
+
+ const shared_ptr<pv::view::Signal> s = (*iter).second;
+ assert(s);
+
+ s->enable(check_box->isChecked());
+}
+
+void Channels::enable_all_channels()
+{
+ set_all_channels(true);
+}
+
+void Channels::disable_all_channels()
+{
+ set_all_channels(false);
+}
+
+} // popups
+} // pv
diff --git a/pv/popups/probes.h b/pv/popups/channels.hpp
similarity index 54%
rename from pv/popups/probes.h
rename to pv/popups/channels.hpp
index dbf90cd..6ba8646 100644
--- a/pv/popups/probes.h
+++ b/pv/popups/channels.hpp
@@ -18,34 +18,33 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_POPUPS_PROBES_H
-#define PULSEVIEW_PV_POPUPS_PROBES_H
+#ifndef PULSEVIEW_PV_POPUPS_CHANNELS_HPP
+#define PULSEVIEW_PV_POPUPS_CHANNELS_HPP
#include <map>
+#include <memory>
#include <vector>
-#include <boost/shared_ptr.hpp>
-
#include <QFormLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QSignalMapper>
-#include <pv/widgets/popup.h>
-
-struct sr_channel_group;
+#include <pv/widgets/popup.hpp>
class QCheckBox;
class QGridLayout;
+namespace sigrok {
+ class ChannelGroup;
+}
+
namespace pv {
-class SigSession;
+class Session;
-namespace prop {
namespace binding {
-class DeviceOptions;
-}
+class Device;
}
namespace view {
@@ -54,51 +53,51 @@ class Signal;
namespace popups {
-class Probes : public pv::widgets::Popup
+class Channels : public pv::widgets::Popup
{
Q_OBJECT
public:
- Probes(SigSession &_session, QWidget *parent);
+ Channels(Session &session_, QWidget *parent);
private:
- void set_all_probes(bool set);
+ void set_all_channels(bool set);
- void populate_group(const sr_channel_group *group,
- const std::vector< boost::shared_ptr<pv::view::Signal> > sigs);
+ void populate_group(std::shared_ptr<sigrok::ChannelGroup> group,
+ const std::vector< std::shared_ptr<pv::view::Signal> > sigs);
QGridLayout* create_channel_group_grid(
- const std::vector< boost::shared_ptr<pv::view::Signal> > sigs);
+ const std::vector< std::shared_ptr<pv::view::Signal> > sigs);
private:
void showEvent(QShowEvent *e);
-private slots:
- void on_probe_checked(QWidget *widget);
+private Q_SLOTS:
+ void on_channel_checked(QWidget *widget);
- void enable_all_probes();
- void disable_all_probes();
+ void enable_all_channels();
+ void disable_all_channels();
private:
- pv::SigSession &_session;
+ pv::Session &session_;
- QFormLayout _layout;
+ QFormLayout layout_;
- bool _updating_probes;
+ bool updating_channels_;
- std::vector< boost::shared_ptr<pv::prop::binding::DeviceOptions> >
- _group_bindings;
- std::map< QCheckBox*, boost::shared_ptr<pv::view::Signal> >
- _check_box_signal_map;
+ std::vector< std::shared_ptr<pv::binding::Device> >
+ group_bindings_;
+ std::map< QCheckBox*, std::shared_ptr<pv::view::Signal> >
+ check_box_signal_map_;
- QHBoxLayout _buttons_bar;
- QPushButton _enable_all_probes;
- QPushButton _disable_all_probes;
+ QHBoxLayout buttons_bar_;
+ QPushButton enable_all_channels_;
+ QPushButton disable_all_channels_;
- QSignalMapper _check_box_mapper;
+ QSignalMapper check_box_mapper_;
};
} // popups
} // pv
-#endif // PULSEVIEW_PV_POPUPS_PROBES_H
+#endif // PULSEVIEW_PV_POPUPS_CHANNELS_HPP
diff --git a/pv/popups/deviceoptions.cpp b/pv/popups/deviceoptions.cpp
index 13055fc..fe79551 100644
--- a/pv/popups/deviceoptions.cpp
+++ b/pv/popups/deviceoptions.cpp
@@ -18,35 +18,36 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "deviceoptions.h"
-
-#include <boost/foreach.hpp>
+#include "deviceoptions.hpp"
#include <QFormLayout>
#include <QListWidget>
-#include <pv/prop/property.h>
+#include <pv/prop/property.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using std::shared_ptr;
-using boost::shared_ptr;
+using sigrok::Device;
namespace pv {
namespace popups {
-DeviceOptions::DeviceOptions(shared_ptr<device::DevInst> dev_inst,
- QWidget *parent) :
+DeviceOptions::DeviceOptions(shared_ptr<Device> device, QWidget *parent) :
Popup(parent),
- _dev_inst(dev_inst),
- _layout(this),
- _binding(dev_inst)
+ device_(device),
+ layout_(this),
+ binding_(device)
{
- setLayout(&_layout);
+ setLayout(&layout_);
- _layout.addWidget(_binding.get_property_form(this, true));
+ layout_.addWidget(binding_.get_property_form(this, true));
}
-pv::prop::binding::DeviceOptions& DeviceOptions::binding()
+pv::binding::Device& DeviceOptions::binding()
{
- return _binding;
+ return binding_;
}
} // namespace popups
diff --git a/pv/popups/deviceoptions.h b/pv/popups/deviceoptions.hpp
similarity index 71%
rename from pv/popups/deviceoptions.h
rename to pv/popups/deviceoptions.hpp
index 945959b..e358932 100644
--- a/pv/popups/deviceoptions.h
+++ b/pv/popups/deviceoptions.hpp
@@ -18,14 +18,18 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_H
-#define PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_H
+#ifndef PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_HPP
+#define PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_HPP
#include <QGroupBox>
#include <QVBoxLayout>
-#include <pv/prop/binding/deviceoptions.h>
-#include <pv/widgets/popup.h>
+#include <pv/binding/device.hpp>
+#include <pv/widgets/popup.hpp>
+
+namespace sigrok {
+ class Device;
+}
namespace pv {
namespace popups {
@@ -35,20 +39,20 @@ class DeviceOptions : public pv::widgets::Popup
Q_OBJECT
public:
- DeviceOptions(boost::shared_ptr<device::DevInst> dev_inst,
+ DeviceOptions(std::shared_ptr<sigrok::Device> device,
QWidget *parent);
- pv::prop::binding::DeviceOptions& binding();
+ pv::binding::Device& binding();
private:
- boost::shared_ptr<device::DevInst> _dev_inst;
+ std::shared_ptr<sigrok::Device> device_;
- QVBoxLayout _layout;
+ QVBoxLayout layout_;
- pv::prop::binding::DeviceOptions _binding;
+ pv::binding::Device binding_;
};
} // namespace popups
} // namespace pv
-#endif // PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_H
+#endif // PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_HPP
diff --git a/pv/popups/probes.cpp b/pv/popups/probes.cpp
deleted file mode 100644
index ca1a2fb..0000000
--- a/pv/popups/probes.cpp
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <map>
-
-#include <boost/foreach.hpp>
-
-#include <QCheckBox>
-#include <QFormLayout>
-#include <QGridLayout>
-#include <QLabel>
-
-#include "probes.h"
-
-#include <pv/device/devinst.h>
-#include <pv/prop/binding/deviceoptions.h>
-#include <pv/sigsession.h>
-#include <pv/view/signal.h>
-
-using namespace Qt;
-
-using boost::shared_ptr;
-using std::map;
-using std::set;
-using std::vector;
-
-using pv::view::Signal;
-
-namespace pv {
-namespace popups {
-
-Probes::Probes(SigSession &session, QWidget *parent) :
- Popup(parent),
- _session(session),
- _updating_probes(false),
- _enable_all_probes(tr("Enable All"), this),
- _disable_all_probes(tr("Disable All"), this),
- _check_box_mapper(this)
-{
- // Create the layout
- setLayout(&_layout);
-
- shared_ptr<device::DevInst> dev_inst = _session.get_device();
- assert(dev_inst);
- const sr_dev_inst *const sdi = dev_inst->dev_inst();
- assert(sdi);
-
- // Collect a set of signals
- map<const sr_channel*, shared_ptr<Signal> > signal_map;
- const vector< shared_ptr<Signal> > sigs = _session.get_signals();
- BOOST_FOREACH(const shared_ptr<Signal> &sig, sigs)
- signal_map[sig->probe()] = sig;
-
- // Populate channel groups
- for (const GSList *g = sdi->channel_groups; g; g = g->next)
- {
- const sr_channel_group *const group =
- (const sr_channel_group*)g->data;
- assert(group);
-
- // Make a set of signals, and removed this signals from the
- // signal map.
- vector< shared_ptr<Signal> > group_sigs;
- for (const GSList *p = group->channels; p; p = p->next)
- {
- const sr_channel *const probe = (const sr_channel*)p->data;
- assert(probe);
-
- const map<const sr_channel*, shared_ptr<Signal> >::
- iterator iter = signal_map.find(probe);
- assert(iter != signal_map.end());
-
- group_sigs.push_back((*iter).second);
- signal_map.erase(iter);
- }
-
- populate_group(group, group_sigs);
- }
-
- // Make a vector of the remaining probes
- vector< shared_ptr<Signal> > global_sigs;
- for (const GSList *p = sdi->channels; p; p = p->next)
- {
- const sr_channel *const probe = (const sr_channel*)p->data;
- assert(probe);
-
- const map<const sr_channel*, shared_ptr<Signal> >::
- const_iterator iter = signal_map.find(probe);
- if (iter != signal_map.end())
- global_sigs.push_back((*iter).second);
- }
-
- // Create a group
- populate_group(NULL, global_sigs);
-
- // Create the enable/disable all buttons
- connect(&_enable_all_probes, SIGNAL(clicked()),
- this, SLOT(enable_all_probes()));
- connect(&_disable_all_probes, SIGNAL(clicked()),
- this, SLOT(disable_all_probes()));
-
- _enable_all_probes.setFlat(true);
- _disable_all_probes.setFlat(true);
-
- _buttons_bar.addWidget(&_enable_all_probes);
- _buttons_bar.addWidget(&_disable_all_probes);
- _buttons_bar.addStretch(1);
-
- _layout.addRow(&_buttons_bar);
-
- // Connect the check-box signal mapper
- connect(&_check_box_mapper, SIGNAL(mapped(QWidget*)),
- this, SLOT(on_probe_checked(QWidget*)));
-}
-
-void Probes::set_all_probes(bool set)
-{
- _updating_probes = true;
-
- for (map<QCheckBox*, shared_ptr<Signal> >::const_iterator i =
- _check_box_signal_map.begin();
- i != _check_box_signal_map.end(); i++)
- {
- const shared_ptr<Signal> sig = (*i).second;
- assert(sig);
-
- sig->enable(set);
- (*i).first->setChecked(set);
- }
-
- _updating_probes = false;
-}
-
-void Probes::populate_group(const sr_channel_group *group,
- const vector< shared_ptr<pv::view::Signal> > sigs)
-{
- using pv::prop::binding::DeviceOptions;
-
- // Only bind options if this is a group. We don't do it for general
- // options, because these properties are shown in the device config
- // popup.
- shared_ptr<DeviceOptions> binding;
- if (group)
- binding = shared_ptr<DeviceOptions>(new DeviceOptions(
- _session.get_device(), group));
-
- // Create a title if the group is going to have any content
- if ((!sigs.empty() || (binding && !binding->properties().empty())) &&
- group && group->name)
- _layout.addRow(new QLabel(
- QString("<h3>%1</h3>").arg(group->name)));
-
- // Create the channel group grid
- QGridLayout *const probe_grid =
- create_channel_group_grid(sigs);
- _layout.addRow(probe_grid);
-
- // Create the channel group options
- if (binding)
- {
- binding->add_properties_to_form(&_layout, true);
- _group_bindings.push_back(binding);
- }
-}
-
-QGridLayout* Probes::create_channel_group_grid(
- const vector< shared_ptr<pv::view::Signal> > sigs)
-{
- int row = 0, col = 0;
- QGridLayout *const grid = new QGridLayout();
-
- BOOST_FOREACH(const shared_ptr<pv::view::Signal>& sig, sigs)
- {
- assert(sig);
-
- QCheckBox *const checkbox = new QCheckBox(sig->get_name());
- _check_box_mapper.setMapping(checkbox, checkbox);
- connect(checkbox, SIGNAL(toggled(bool)),
- &_check_box_mapper, SLOT(map()));
-
- grid->addWidget(checkbox, row, col);
-
- _check_box_signal_map[checkbox] = sig;
-
- if(++col >= 8)
- col = 0, row++;
- }
-
- return grid;
-}
-
-void Probes::showEvent(QShowEvent *e)
-{
- pv::widgets::Popup::showEvent(e);
-
- _updating_probes = true;
-
- for (map<QCheckBox*, shared_ptr<Signal> >::const_iterator i =
- _check_box_signal_map.begin();
- i != _check_box_signal_map.end(); i++)
- {
- const shared_ptr<Signal> sig = (*i).second;
- assert(sig);
-
- (*i).first->setChecked(sig->enabled());
- }
-
- _updating_probes = false;
-}
-
-void Probes::on_probe_checked(QWidget *widget)
-{
- if (_updating_probes)
- return;
-
- QCheckBox *const check_box = (QCheckBox*)widget;
- assert(check_box);
-
- // Look up the signal of this check-box
- map< QCheckBox*, shared_ptr<Signal> >::const_iterator iter =
- _check_box_signal_map.find((QCheckBox*)check_box);
- assert(iter != _check_box_signal_map.end());
-
- const shared_ptr<pv::view::Signal> s = (*iter).second;
- assert(s);
-
- s->enable(check_box->isChecked());
-}
-
-void Probes::enable_all_probes()
-{
- set_all_probes(true);
-}
-
-void Probes::disable_all_probes()
-{
- set_all_probes(false);
-}
-
-} // popups
-} // pv
diff --git a/pv/prop/binding/deviceoptions.cpp b/pv/prop/binding/deviceoptions.cpp
deleted file mode 100644
index 2dc237a..0000000
--- a/pv/prop/binding/deviceoptions.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <boost/bind.hpp>
-
-#include <stdint.h>
-
-#include <QDebug>
-
-#include "deviceoptions.h"
-
-#include <pv/device/devinst.h>
-#include <pv/prop/bool.h>
-#include <pv/prop/double.h>
-#include <pv/prop/enum.h>
-#include <pv/prop/int.h>
-
-#include <libsigrok/libsigrok.h>
-
-using boost::bind;
-using boost::function;
-using boost::optional;
-using boost::shared_ptr;
-using std::make_pair;
-using std::pair;
-using std::string;
-using std::vector;
-
-namespace pv {
-namespace prop {
-namespace binding {
-
-DeviceOptions::DeviceOptions(shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel_group *group) :
- _dev_inst(dev_inst),
- _group(group)
-{
- assert(dev_inst);
-
- GVariant *gvar_opts;
- gsize num_opts;
-
- if (!(gvar_opts = dev_inst->list_config(group, SR_CONF_DEVICE_OPTIONS)))
- /* Driver supports no device instance options. */
- return;
-
- const int *const options = (const int32_t *)g_variant_get_fixed_array(
- gvar_opts, &num_opts, sizeof(int32_t));
- for (unsigned int i = 0; i < num_opts; i++) {
- const struct sr_config_info *const info =
- sr_config_info_get(options[i]);
-
- if (!info)
- continue;
-
- const int key = info->key;
- GVariant *const gvar_list = dev_inst->list_config(group, key);
-
- const QString name = QString::fromUtf8(info->name);
-
- switch(key)
- {
- case SR_CONF_SAMPLERATE:
- // Sample rate values are not bound because they are shown
- // in the SamplingBar
- break;
-
- case SR_CONF_CAPTURE_RATIO:
- bind_int(name, key, "%", pair<int64_t, int64_t>(0, 100));
- break;
-
- case SR_CONF_PATTERN_MODE:
- case SR_CONF_BUFFERSIZE:
- case SR_CONF_TRIGGER_SOURCE:
- case SR_CONF_TRIGGER_SLOPE:
- case SR_CONF_FILTER:
- case SR_CONF_COUPLING:
- case SR_CONF_CLOCK_EDGE:
- bind_enum(name, key, gvar_list);
- break;
-
- case SR_CONF_EXTERNAL_CLOCK:
- case SR_CONF_RLE:
- bind_bool(name, key);
- break;
-
- case SR_CONF_TIMEBASE:
- bind_enum(name, key, gvar_list, print_timebase);
- break;
-
- case SR_CONF_VDIV:
- bind_enum(name, key, gvar_list, print_vdiv);
- break;
-
- case SR_CONF_VOLTAGE_THRESHOLD:
- bind_enum(name, key, gvar_list, print_voltage_threshold);
- break;
- }
-
- if (gvar_list)
- g_variant_unref(gvar_list);
- }
- g_variant_unref(gvar_opts);
-}
-
-void DeviceOptions::bind_bool(const QString &name, int key)
-{
- assert(_dev_inst);
- _properties.push_back(shared_ptr<Property>(new Bool(name,
- bind(&device::DevInst::get_config, _dev_inst, _group, key),
- bind(&device::DevInst::set_config, _dev_inst,
- _group, key, _1))));
-}
-
-void DeviceOptions::bind_enum(const QString &name, int key,
- GVariant *const gvar_list, function<QString (GVariant*)> printer)
-{
- GVariant *gvar;
- GVariantIter iter;
- vector< pair<GVariant*, QString> > values;
-
- assert(_dev_inst);
- if (!gvar_list) {
- qDebug() << "Config key " << key << " was listed, but no "
- "options were given";
- return;
- }
-
- g_variant_iter_init (&iter, gvar_list);
- while ((gvar = g_variant_iter_next_value (&iter)))
- values.push_back(make_pair(gvar, printer(gvar)));
-
- _properties.push_back(shared_ptr<Property>(new Enum(name, values,
- bind(&device::DevInst::get_config, _dev_inst, _group, key),
- bind(&device::DevInst::set_config, _dev_inst,
- _group, key, _1))));
-}
-
-void DeviceOptions::bind_int(const QString &name, int key, QString suffix,
- optional< std::pair<int64_t, int64_t> > range)
-{
- assert(_dev_inst);
-
- _properties.push_back(shared_ptr<Property>(new Int(name, suffix, range,
- bind(&device::DevInst::get_config, _dev_inst, _group, key),
- bind(&device::DevInst::set_config, _dev_inst, _group, key, _1))));
-}
-
-QString DeviceOptions::print_timebase(GVariant *const gvar)
-{
- uint64_t p, q;
- g_variant_get(gvar, "(tt)", &p, &q);
- return QString::fromUtf8(sr_period_string(p * q));
-}
-
-QString DeviceOptions::print_vdiv(GVariant *const gvar)
-{
- uint64_t p, q;
- g_variant_get(gvar, "(tt)", &p, &q);
- return QString::fromUtf8(sr_voltage_string(p, q));
-}
-
-QString DeviceOptions::print_voltage_threshold(GVariant *const gvar)
-{
- gdouble lo, hi;
- char buf[64];
- g_variant_get(gvar, "(dd)", &lo, &hi);
- snprintf(buf, sizeof(buf), "L<%.1fV H>%.1fV", lo, hi);
- return QString::fromUtf8(buf);
-}
-
-} // binding
-} // prop
-} // pv
-
diff --git a/pv/prop/binding/deviceoptions.h b/pv/prop/binding/deviceoptions.h
deleted file mode 100644
index 445361f..0000000
--- a/pv/prop/binding/deviceoptions.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_PROP_BINDING_DEVICEOPTIONS_H
-#define PULSEVIEW_PV_PROP_BINDING_DEVICEOPTIONS_H
-
-#include <boost/function.hpp>
-#include <boost/optional.hpp>
-
-#include <QString>
-
-#include "binding.h"
-
-#include <glib.h>
-
-struct sr_dev_inst;
-struct sr_channel_group;
-
-namespace pv {
-
-namespace device {
-class DevInst;
-}
-
-namespace prop {
-namespace binding {
-
-class DeviceOptions : public Binding
-{
-public:
- DeviceOptions(boost::shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel_group *group = NULL);
-
-private:
- void bind_bool(const QString &name, int key);
- void bind_enum(const QString &name, int key,
- GVariant *const gvar_list,
- boost::function<QString (GVariant*)> printer = print_gvariant);
- void bind_int(const QString &name, int key, QString suffix,
- boost::optional< std::pair<int64_t, int64_t> > range);
-
- static QString print_timebase(GVariant *const gvar);
- static QString print_vdiv(GVariant *const gvar);
- static QString print_voltage_threshold(GVariant *const gvar);
-
-protected:
- boost::shared_ptr<device::DevInst> _dev_inst;
- const sr_channel_group *const _group;
-};
-
-} // binding
-} // prop
-} // pv
-
-#endif // PULSEVIEW_PV_PROP_BINDING_DEVICEOPTIONS_H
diff --git a/pv/prop/bool.cpp b/pv/prop/bool.cpp
index e0e052f..3e201cc 100644
--- a/pv/prop/bool.cpp
+++ b/pv/prop/bool.cpp
@@ -22,14 +22,14 @@
#include <QCheckBox>
-#include "bool.h"
+#include "bool.hpp"
namespace pv {
namespace prop {
Bool::Bool(QString name, Getter getter, Setter setter) :
Property(name, getter, setter),
- _check_box(NULL)
+ check_box_(nullptr)
{
}
@@ -39,23 +39,27 @@ Bool::~Bool()
QWidget* Bool::get_widget(QWidget *parent, bool auto_commit)
{
- if (_check_box)
- return _check_box;
+ if (check_box_)
+ return check_box_;
- GVariant *const value = _getter ? _getter() : NULL;
- if (!value)
- return NULL;
+ if (!getter_)
+ return nullptr;
- _check_box = new QCheckBox(name(), parent);
- _check_box->setCheckState(g_variant_get_boolean(value) ?
- Qt::Checked : Qt::Unchecked);
- g_variant_unref(value);
+ Glib::VariantBase variant = getter_();
+ if (!variant.gobj())
+ return nullptr;
+
+ bool value = Glib::VariantBase::cast_dynamic<Glib::Variant<bool>>(
+ variant).get();
+
+ check_box_ = new QCheckBox(name(), parent);
+ check_box_->setCheckState(value ? Qt::Checked : Qt::Unchecked);
if (auto_commit)
- connect(_check_box, SIGNAL(stateChanged(int)),
+ connect(check_box_, SIGNAL(stateChanged(int)),
this, SLOT(on_state_changed(int)));
- return _check_box;
+ return check_box_;
}
bool Bool::labeled_widget() const
@@ -65,13 +69,13 @@ bool Bool::labeled_widget() const
void Bool::commit()
{
- assert(_setter);
+ assert(setter_);
- if (!_check_box)
+ if (!check_box_)
return;
- _setter(g_variant_new_boolean(
- _check_box->checkState() == Qt::Checked));
+ setter_(Glib::Variant<bool>::create(
+ check_box_->checkState() == Qt::Checked));
}
void Bool::on_state_changed(int)
diff --git a/pv/prop/bool.h b/pv/prop/bool.hpp
similarity index 87%
rename from pv/prop/bool.h
rename to pv/prop/bool.hpp
index 0d8e205..6d32da7 100644
--- a/pv/prop/bool.h
+++ b/pv/prop/bool.hpp
@@ -18,10 +18,10 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_BOOL_H
-#define PULSEVIEW_PV_PROP_BOOL_H
+#ifndef PULSEVIEW_PV_PROP_BOOL_HPP
+#define PULSEVIEW_PV_PROP_BOOL_HPP
-#include "property.h"
+#include "property.hpp"
class QCheckBox;
@@ -42,14 +42,14 @@ public:
void commit();
-private slots:
+private Q_SLOTS:
void on_state_changed(int);
private:
- QCheckBox *_check_box;
+ QCheckBox *check_box_;
};
} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_BOOL_H
+#endif // PULSEVIEW_PV_PROP_BOOL_HPP
diff --git a/pv/prop/double.cpp b/pv/prop/double.cpp
index 93b45d3..c5ab1db 100644
--- a/pv/prop/double.cpp
+++ b/pv/prop/double.cpp
@@ -22,7 +22,7 @@
#include <QDoubleSpinBox>
-#include "double.h"
+#include "double.hpp"
using boost::optional;
using std::pair;
@@ -38,11 +38,11 @@ Double::Double(QString name,
Getter getter,
Setter setter) :
Property(name, getter, setter),
- _decimals(decimals),
- _suffix(suffix),
- _range(range),
- _step(step),
- _spin_box(NULL)
+ decimals_(decimals),
+ suffix_(suffix),
+ range_(range),
+ step_(step),
+ spin_box_(nullptr)
{
}
@@ -52,39 +52,44 @@ Double::~Double()
QWidget* Double::get_widget(QWidget *parent, bool auto_commit)
{
- if (_spin_box)
- return _spin_box;
+ if (spin_box_)
+ return spin_box_;
- GVariant *const value = _getter ? _getter() : NULL;
- if (!value)
- return NULL;
+ if (!getter_)
+ return nullptr;
- _spin_box = new QDoubleSpinBox(parent);
- _spin_box->setDecimals(_decimals);
- _spin_box->setSuffix(_suffix);
- if (_range)
- _spin_box->setRange(_range->first, _range->second);
- if (_step)
- _spin_box->setSingleStep(*_step);
+ Glib::VariantBase variant = getter_();
+ if (!variant.gobj())
+ return nullptr;
- _spin_box->setValue(g_variant_get_double(value));
- g_variant_unref(value);
+ double value = Glib::VariantBase::cast_dynamic<Glib::Variant<double>>(
+ variant).get();
+
+ spin_box_ = new QDoubleSpinBox(parent);
+ spin_box_->setDecimals(decimals_);
+ spin_box_->setSuffix(suffix_);
+ if (range_)
+ spin_box_->setRange(range_->first, range_->second);
+ if (step_)
+ spin_box_->setSingleStep(*step_);
+
+ spin_box_->setValue(value);
if (auto_commit)
- connect(_spin_box, SIGNAL(valueChanged(double)),
+ connect(spin_box_, SIGNAL(valueChanged(double)),
this, SLOT(on_value_changed(double)));
- return _spin_box;
+ return spin_box_;
}
void Double::commit()
{
- assert(_setter);
+ assert(setter_);
- if (!_spin_box)
+ if (!spin_box_)
return;
- _setter(g_variant_new_double(_spin_box->value()));
+ setter_(Glib::Variant<double>::create(spin_box_->value()));
}
void Double::on_value_changed(double)
diff --git a/pv/prop/double.h b/pv/prop/double.hpp
similarity index 80%
rename from pv/prop/double.h
rename to pv/prop/double.hpp
index ba4c595..b62d1fa 100644
--- a/pv/prop/double.h
+++ b/pv/prop/double.hpp
@@ -18,14 +18,14 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_DOUBLE_H
-#define PULSEVIEW_PV_PROP_DOUBLE_H
+#ifndef PULSEVIEW_PV_PROP_DOUBLE_HPP
+#define PULSEVIEW_PV_PROP_DOUBLE_HPP
#include <utility>
#include <boost/optional.hpp>
-#include "property.h"
+#include "property.hpp"
class QDoubleSpinBox;
@@ -49,19 +49,19 @@ public:
void commit();
-private slots:
+private Q_SLOTS:
void on_value_changed(double);
private:
- const int _decimals;
- const QString _suffix;
- const boost::optional< std::pair<double, double> > _range;
- const boost::optional<double> _step;
+ const int decimals_;
+ const QString suffix_;
+ const boost::optional< std::pair<double, double> > range_;
+ const boost::optional<double> step_;
- QDoubleSpinBox *_spin_box;
+ QDoubleSpinBox *spin_box_;
};
} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_DOUBLE_H
+#endif // PULSEVIEW_PV_PROP_DOUBLE_HPP
diff --git a/pv/prop/enum.cpp b/pv/prop/enum.cpp
index 6439a72..06e4d76 100644
--- a/pv/prop/enum.cpp
+++ b/pv/prop/enum.cpp
@@ -22,7 +22,7 @@
#include <QComboBox>
-#include "enum.h"
+#include "enum.hpp"
using std::pair;
using std::vector;
@@ -31,62 +31,57 @@ namespace pv {
namespace prop {
Enum::Enum(QString name,
- vector<pair<GVariant*, QString> > values,
+ vector<pair<Glib::VariantBase, QString> > values,
Getter getter, Setter setter) :
Property(name, getter, setter),
- _values(values),
- _selector(NULL)
+ values_(values),
+ selector_(nullptr)
{
- for (vector< pair<GVariant*, QString> >::const_iterator i =
- _values.begin(); i != _values.end(); i++)
- g_variant_ref((*i).first);
}
Enum::~Enum()
{
- for (vector< pair<GVariant*, QString> >::const_iterator i =
- _values.begin(); i != _values.end(); i++)
- g_variant_unref((*i).first);
}
QWidget* Enum::get_widget(QWidget *parent, bool auto_commit)
{
- if (_selector)
- return _selector;
-
- GVariant *const value = _getter ? _getter() : NULL;
- if (!value)
- return NULL;
-
- _selector = new QComboBox(parent);
- for (unsigned int i = 0; i < _values.size(); i++) {
- const pair<GVariant*, QString> &v = _values[i];
- _selector->addItem(v.second, qVariantFromValue((void*)v.first));
- if (value && g_variant_equal(v.first, value))
- _selector->setCurrentIndex(i);
+ if (selector_)
+ return selector_;
+
+ if (!getter_)
+ return nullptr;
+
+ Glib::VariantBase variant = getter_();
+ if (!variant.gobj())
+ return nullptr;
+
+ selector_ = new QComboBox(parent);
+ for (unsigned int i = 0; i < values_.size(); i++) {
+ const pair<Glib::VariantBase, QString> &v = values_[i];
+ selector_->addItem(v.second, qVariantFromValue(v.first));
+ if (v.first.equal(variant))
+ selector_->setCurrentIndex(i);
}
- g_variant_unref(value);
-
if (auto_commit)
- connect(_selector, SIGNAL(currentIndexChanged(int)),
+ connect(selector_, SIGNAL(currentIndexChanged(int)),
this, SLOT(on_current_item_changed(int)));
- return _selector;
+ return selector_;
}
void Enum::commit()
{
- assert(_setter);
+ assert(setter_);
- if (!_selector)
+ if (!selector_)
return;
- const int index = _selector->currentIndex();
+ const int index = selector_->currentIndex();
if (index < 0)
return;
- _setter((GVariant*)_selector->itemData(index).value<void*>());
+ setter_(selector_->itemData(index).value<Glib::VariantBase>());
}
void Enum::on_current_item_changed(int)
diff --git a/pv/prop/enum.h b/pv/prop/enum.hpp
similarity index 75%
rename from pv/prop/enum.h
rename to pv/prop/enum.hpp
index a7e6ed8..63f78af 100644
--- a/pv/prop/enum.h
+++ b/pv/prop/enum.hpp
@@ -18,13 +18,17 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_ENUM_H
-#define PULSEVIEW_PV_PROP_ENUM_H
+#ifndef PULSEVIEW_PV_PROP_ENUM_HPP
+#define PULSEVIEW_PV_PROP_ENUM_HPP
#include <utility>
#include <vector>
-#include "property.h"
+#include "property.hpp"
+
+#include <QMetaType>
+
+Q_DECLARE_METATYPE(Glib::VariantBase);
class QComboBox;
@@ -36,7 +40,7 @@ class Enum : public Property
Q_OBJECT;
public:
- Enum(QString name, std::vector<std::pair<GVariant*, QString> > values,
+ Enum(QString name, std::vector<std::pair<Glib::VariantBase, QString> > values,
Getter getter, Setter setter);
virtual ~Enum();
@@ -45,16 +49,16 @@ public:
void commit();
-private slots:
+private Q_SLOTS:
void on_current_item_changed(int);
private:
- const std::vector< std::pair<GVariant*, QString> > _values;
+ const std::vector< std::pair<Glib::VariantBase, QString> > values_;
- QComboBox *_selector;
+ QComboBox *selector_;
};
} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_ENUM_H
+#endif // PULSEVIEW_PV_PROP_ENUM_HPP
diff --git a/pv/prop/int.cpp b/pv/prop/int.cpp
index 8124f9b..e750e9f 100644
--- a/pv/prop/int.cpp
+++ b/pv/prop/int.cpp
@@ -23,7 +23,7 @@
#include <QSpinBox>
-#include "int.h"
+#include "int.hpp"
using boost::optional;
using std::max;
@@ -39,76 +39,61 @@ Int::Int(QString name,
Getter getter,
Setter setter) :
Property(name, getter, setter),
- _suffix(suffix),
- _range(range),
- _value(NULL),
- _spin_box(NULL)
+ suffix_(suffix),
+ range_(range),
+ spin_box_(nullptr)
{
}
Int::~Int()
{
- if (_value)
- g_variant_unref(_value);
}
QWidget* Int::get_widget(QWidget *parent, bool auto_commit)
{
- int64_t int_val = 0, range_min = 0, range_max = 0;
+ int64_t int_val = 0, range_min = 0;
+ uint64_t range_max = 0;
- if (_spin_box)
- return _spin_box;
+ if (spin_box_)
+ return spin_box_;
- if (_value)
- g_variant_unref(_value);
+ if (!getter_)
+ return nullptr;
- _value = _getter ? _getter() : NULL;
- if (!_value)
- return NULL;
+ value_ = getter_();
- _spin_box = new QSpinBox(parent);
- _spin_box->setSuffix(_suffix);
+ GVariant *value = value_.gobj();
+ if (!value)
+ return nullptr;
- const GVariantType *const type = g_variant_get_type(_value);
+ spin_box_ = new QSpinBox(parent);
+ spin_box_->setSuffix(suffix_);
+
+ const GVariantType *const type = g_variant_get_type(value);
assert(type);
- if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE))
- {
- int_val = g_variant_get_byte(_value);
+ if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) {
+ int_val = g_variant_get_byte(value);
range_min = 0, range_max = UINT8_MAX;
- }
- else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16))
- {
- int_val = g_variant_get_int16(_value);
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) {
+ int_val = g_variant_get_int16(value);
range_min = INT16_MIN, range_max = INT16_MAX;
- }
- else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16))
- {
- int_val = g_variant_get_uint16(_value);
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) {
+ int_val = g_variant_get_uint16(value);
range_min = 0, range_max = UINT16_MAX;
- }
- else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32))
- {
- int_val = g_variant_get_int32(_value);
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) {
+ int_val = g_variant_get_int32(value);
range_min = INT32_MIN, range_max = INT32_MAX;
- }
- else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32))
- {
- int_val = g_variant_get_uint32(_value);
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) {
+ int_val = g_variant_get_uint32(value);
range_min = 0, range_max = UINT32_MAX;
- }
- else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64))
- {
- int_val = g_variant_get_int64(_value);
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) {
+ int_val = g_variant_get_int64(value);
range_min = INT64_MIN, range_max = INT64_MAX;
- }
- else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64))
- {
- int_val = g_variant_get_uint64(_value);
+ } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) {
+ int_val = g_variant_get_uint64(value);
range_min = 0, range_max = UINT64_MAX;
- }
- else
- {
+ } else {
// Unexpected value type.
assert(0);
}
@@ -119,49 +104,47 @@ QWidget* Int::get_widget(QWidget *parent, bool auto_commit)
// custom widget.
range_min = max(range_min, (int64_t)INT_MIN);
- range_max = min(range_max, (int64_t)INT_MAX);
+ range_max = min(range_max, (uint64_t)INT_MAX);
- if (_range)
- _spin_box->setRange((int)_range->first, (int)_range->second);
+ if (range_)
+ spin_box_->setRange((int)range_->first, (int)range_->second);
else
- _spin_box->setRange((int)range_min, (int)range_max);
+ spin_box_->setRange((int)range_min, (int)range_max);
- _spin_box->setValue((int)int_val);
+ spin_box_->setValue((int)int_val);
if (auto_commit)
- connect(_spin_box, SIGNAL(valueChanged(int)),
+ connect(spin_box_, SIGNAL(valueChanged(int)),
this, SLOT(on_value_changed(int)));
- return _spin_box;
+ return spin_box_;
}
void Int::commit()
{
- assert(_setter);
+ assert(setter_);
- if (!_spin_box)
+ if (!spin_box_)
return;
- assert(_value);
-
- GVariant *new_value = NULL;
- const GVariantType *const type = g_variant_get_type(_value);
+ GVariant *new_value = nullptr;
+ const GVariantType *const type = g_variant_get_type(value_.gobj());
assert(type);
if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE))
- new_value = g_variant_new_byte(_spin_box->value());
+ new_value = g_variant_new_byte(spin_box_->value());
else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16))
- new_value = g_variant_new_int16(_spin_box->value());
+ new_value = g_variant_new_int16(spin_box_->value());
else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16))
- new_value = g_variant_new_uint16(_spin_box->value());
+ new_value = g_variant_new_uint16(spin_box_->value());
else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32))
- new_value = g_variant_new_int32(_spin_box->value());
+ new_value = g_variant_new_int32(spin_box_->value());
else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32))
- new_value = g_variant_new_int32(_spin_box->value());
+ new_value = g_variant_new_uint32(spin_box_->value());
else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64))
- new_value = g_variant_new_int64(_spin_box->value());
+ new_value = g_variant_new_int64(spin_box_->value());
else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64))
- new_value = g_variant_new_uint64(_spin_box->value());
+ new_value = g_variant_new_uint64(spin_box_->value());
else
{
// Unexpected value type.
@@ -170,11 +153,9 @@ void Int::commit()
assert(new_value);
- g_variant_unref(_value);
- g_variant_ref(new_value);
- _value = new_value;
+ value_ = Glib::VariantBase(new_value);
- _setter(new_value);
+ setter_(value_);
}
void Int::on_value_changed(int)
diff --git a/pv/prop/int.h b/pv/prop/int.hpp
similarity index 81%
rename from pv/prop/int.h
rename to pv/prop/int.hpp
index 6d910cd..60c9d51 100644
--- a/pv/prop/int.h
+++ b/pv/prop/int.hpp
@@ -18,14 +18,14 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_INT_H
-#define PULSEVIEW_PV_PROP_INT_H
+#ifndef PULSEVIEW_PV_PROP_INT_HPP
+#define PULSEVIEW_PV_PROP_INT_HPP
#include <utility>
#include <boost/optional.hpp>
-#include "property.h"
+#include "property.hpp"
class QSpinBox;
@@ -47,18 +47,18 @@ public:
void commit();
-private slots:
+private Q_SLOTS:
void on_value_changed(int);
private:
- const QString _suffix;
- const boost::optional< std::pair<int64_t, int64_t> > _range;
+ const QString suffix_;
+ const boost::optional< std::pair<int64_t, int64_t> > range_;
- GVariant *_value;
- QSpinBox *_spin_box;
+ Glib::VariantBase value_;
+ QSpinBox *spin_box_;
};
} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_INT_H
+#endif // PULSEVIEW_PV_PROP_INT_HPP
diff --git a/pv/prop/property.cpp b/pv/prop/property.cpp
index 798b5f1..c513d51 100644
--- a/pv/prop/property.cpp
+++ b/pv/prop/property.cpp
@@ -18,21 +18,21 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "property.h"
+#include "property.hpp"
namespace pv {
namespace prop {
Property::Property(QString name, Getter getter, Setter setter) :
- _getter(getter),
- _setter(setter),
- _name(name)
+ getter_(getter),
+ setter_(setter),
+ name_(name)
{
}
const QString& Property::name() const
{
- return _name;
+ return name_;
}
bool Property::labeled_widget() const
diff --git a/pv/prop/property.h b/pv/prop/property.hpp
similarity index 73%
rename from pv/prop/property.h
rename to pv/prop/property.hpp
index 0b4bc7b..77eb9a6 100644
--- a/pv/prop/property.h
+++ b/pv/prop/property.hpp
@@ -18,13 +18,16 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_PROPERTY_H
-#define PULSEVIEW_PV_PROP_PROPERTY_H
+#ifndef PULSEVIEW_PV_PROP_PROPERTY_HPP
+#define PULSEVIEW_PV_PROP_PROPERTY_HPP
#include <glib.h>
+// Suppress warnings due to use of deprecated std::auto_ptr<> by glibmm.
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#include <glibmm.h>
+G_GNUC_END_IGNORE_DEPRECATIONS
-#include <boost/function.hpp>
-
+#include <functional>
#include <QString>
#include <QWidget>
@@ -38,8 +41,8 @@ class Property : public QObject
Q_OBJECT;
public:
- typedef boost::function<GVariant* ()> Getter;
- typedef boost::function<void (GVariant*)> Setter;
+ typedef std::function<Glib::VariantBase ()> Getter;
+ typedef std::function<void (Glib::VariantBase)> Setter;
protected:
Property(QString name, Getter getter, Setter setter);
@@ -54,14 +57,14 @@ public:
virtual void commit() = 0;
protected:
- const Getter _getter;
- const Setter _setter;
+ const Getter getter_;
+ const Setter setter_;
private:
- QString _name;
+ QString name_;
};
} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_PROPERTY_H
+#endif // PULSEVIEW_PV_PROP_PROPERTY_HPP
diff --git a/pv/prop/string.cpp b/pv/prop/string.cpp
index 7fcc656..e7af459 100644
--- a/pv/prop/string.cpp
+++ b/pv/prop/string.cpp
@@ -23,7 +23,11 @@
#include <QLineEdit>
#include <QSpinBox>
-#include "string.h"
+#include "string.hpp"
+
+using std::string;
+
+using Glib::ustring;
namespace pv {
namespace prop {
@@ -32,40 +36,44 @@ String::String(QString name,
Getter getter,
Setter setter) :
Property(name, getter, setter),
- _line_edit(NULL)
+ line_edit_(nullptr)
{
}
QWidget* String::get_widget(QWidget *parent, bool auto_commit)
{
- if (_line_edit)
- return _line_edit;
+ if (line_edit_)
+ return line_edit_;
+
+ if (!getter_)
+ return nullptr;
+
+ Glib::VariantBase variant = getter_();
+ if (!variant.gobj())
+ return nullptr;
- GVariant *const value = _getter ? _getter() : NULL;
- if (!value)
- return NULL;
+ string value = Glib::VariantBase::cast_dynamic<Glib::Variant<ustring>>(
+ variant).get();
- _line_edit = new QLineEdit(parent);
- _line_edit->setText(QString::fromUtf8(
- g_variant_get_string(value, NULL)));
- g_variant_unref(value);
+ line_edit_ = new QLineEdit(parent);
+ line_edit_->setText(QString::fromStdString(value));
if (auto_commit)
- connect(_line_edit, SIGNAL(textEdited(const QString&)),
+ connect(line_edit_, SIGNAL(textEdited(const QString&)),
this, SLOT(on_text_edited(const QString&)));
- return _line_edit;
+ return line_edit_;
}
void String::commit()
{
- assert(_setter);
+ assert(setter_);
- if (!_line_edit)
+ if (!line_edit_)
return;
- QByteArray ba = _line_edit->text().toLocal8Bit();
- _setter(g_variant_new_string(ba.data()));
+ QByteArray ba = line_edit_->text().toLocal8Bit();
+ setter_(Glib::Variant<ustring>::create(ba.data()));
}
void String::on_text_edited(const QString&)
diff --git a/pv/prop/string.h b/pv/prop/string.hpp
similarity index 86%
rename from pv/prop/string.h
rename to pv/prop/string.hpp
index d54635f..9cd9278 100644
--- a/pv/prop/string.h
+++ b/pv/prop/string.hpp
@@ -18,10 +18,10 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_PROP_STRING_H
-#define PULSEVIEW_PV_PROP_STRING_H
+#ifndef PULSEVIEW_PV_PROP_STRING_HPP
+#define PULSEVIEW_PV_PROP_STRING_HPP
-#include "property.h"
+#include "property.hpp"
class QLineEdit;
@@ -39,14 +39,14 @@ public:
void commit();
-private slots:
+private Q_SLOTS:
void on_text_edited(const QString&);
private:
- QLineEdit *_line_edit;
+ QLineEdit *line_edit_;
};
} // prop
} // pv
-#endif // PULSEVIEW_PV_PROP_STRING_H
+#endif // PULSEVIEW_PV_PROP_STRING_HPP
diff --git a/pv/session.cpp b/pv/session.cpp
new file mode 100644
index 0000000..9fd8fb3
--- /dev/null
+++ b/pv/session.cpp
@@ -0,0 +1,682 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012-14 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef _WIN32
+// Windows: Avoid boost/thread namespace pollution (which includes windows.h).
+#define NOGDI
+#define NORESOURCE
+#endif
+#include <boost/thread/locks.hpp>
+#include <boost/thread/shared_mutex.hpp>
+
+#ifdef ENABLE_DECODE
+#include <libsigrokdecode/libsigrokdecode.h>
+#endif
+
+#include "session.hpp"
+
+#include "devicemanager.hpp"
+
+#include "data/analog.hpp"
+#include "data/analogsegment.hpp"
+#include "data/decoderstack.hpp"
+#include "data/logic.hpp"
+#include "data/logicsegment.hpp"
+#include "data/decode/decoder.hpp"
+
+#include "devices/hardwaredevice.hpp"
+#include "devices/sessionfile.hpp"
+
+#include "view/analogsignal.hpp"
+#include "view/decodetrace.hpp"
+#include "view/logicsignal.hpp"
+
+#include <cassert>
+#include <mutex>
+#include <stdexcept>
+
+#include <sys/stat.h>
+
+#include <QDebug>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using boost::shared_lock;
+using boost::shared_mutex;
+using boost::unique_lock;
+
+using std::dynamic_pointer_cast;
+using std::function;
+using std::lock_guard;
+using std::list;
+using std::map;
+using std::mutex;
+using std::recursive_mutex;
+using std::set;
+using std::shared_ptr;
+using std::string;
+using std::unordered_set;
+using std::vector;
+
+using sigrok::Analog;
+using sigrok::Channel;
+using sigrok::ChannelType;
+using sigrok::ConfigKey;
+using sigrok::DatafeedCallbackFunction;
+using sigrok::Error;
+using sigrok::Header;
+using sigrok::Logic;
+using sigrok::Meta;
+using sigrok::Packet;
+using sigrok::PacketPayload;
+using sigrok::Session;
+using sigrok::SessionDevice;
+
+using Glib::VariantBase;
+using Glib::Variant;
+
+namespace pv {
+Session::Session(DeviceManager &device_manager) :
+ device_manager_(device_manager),
+ capture_state_(Stopped),
+ cur_samplerate_(0)
+{
+}
+
+Session::~Session()
+{
+ // Stop and join to the thread
+ stop_capture();
+}
+
+DeviceManager& Session::device_manager()
+{
+ return device_manager_;
+}
+
+const DeviceManager& Session::device_manager() const
+{
+ return device_manager_;
+}
+
+shared_ptr<sigrok::Session> Session::session() const
+{
+ if (!device_)
+ return shared_ptr<sigrok::Session>();
+ return device_->session();
+}
+
+shared_ptr<devices::Device> Session::device() const
+{
+ return device_;
+}
+
+void Session::set_device(shared_ptr<devices::Device> device)
+{
+ assert(device);
+
+ // Ensure we are not capturing before setting the device
+ stop_capture();
+
+ if (device_)
+ device_->close();
+
+ device_ = std::move(device);
+ device_->open();
+ device_->session()->add_datafeed_callback([=]
+ (shared_ptr<sigrok::Device> device, shared_ptr<Packet> packet) {
+ data_feed_in(device, packet);
+ });
+
+ decode_traces_.clear();
+
+ update_signals();
+ device_selected();
+}
+
+void Session::set_default_device()
+{
+ const list< shared_ptr<devices::HardwareDevice> > &devices =
+ device_manager_.devices();
+
+ if (devices.empty())
+ return;
+
+ // Try and find the demo device and select that by default
+ const auto iter = std::find_if(devices.begin(), devices.end(),
+ [] (const shared_ptr<devices::HardwareDevice> &d) {
+ return d->hardware_device()->driver()->name() ==
+ "demo"; });
+ set_device((iter == devices.end()) ? devices.front() : *iter);
+}
+
+Session::capture_state Session::get_capture_state() const
+{
+ lock_guard<mutex> lock(sampling_mutex_);
+ return capture_state_;
+}
+
+void Session::start_capture(function<void (const QString)> error_handler)
+{
+ stop_capture();
+
+ // Check that at least one channel is enabled
+ assert(device_);
+ const shared_ptr<sigrok::Device> sr_dev = device_->device();
+ if (sr_dev) {
+ const auto channels = sr_dev->channels();
+ if (!std::any_of(channels.begin(), channels.end(),
+ [](shared_ptr<Channel> channel) {
+ return channel->enabled(); })) {
+ error_handler(tr("No channels enabled."));
+ return;
+ }
+ }
+
+ // Clear signal data
+ for (const shared_ptr<data::SignalData> d : get_data())
+ d->clear();
+
+ // Begin the session
+ sampling_thread_ = std::thread(
+ &Session::sample_thread_proc, this, device_,
+ error_handler);
+}
+
+void Session::stop_capture()
+{
+ if (get_capture_state() != Stopped)
+ device_->stop();
+
+ // Check that sampling stopped
+ if (sampling_thread_.joinable())
+ sampling_thread_.join();
+}
+
+set< shared_ptr<data::SignalData> > Session::get_data() const
+{
+ shared_lock<shared_mutex> lock(signals_mutex_);
+ set< shared_ptr<data::SignalData> > data;
+ for (const shared_ptr<view::Signal> sig : signals_) {
+ assert(sig);
+ data.insert(sig->data());
+ }
+
+ return data;
+}
+
+double Session::get_samplerate() const
+{
+ double samplerate = 0.0;
+
+ for (const shared_ptr<pv::data::SignalData> d : get_data()) {
+ assert(d);
+ const vector< shared_ptr<pv::data::Segment> > segments =
+ d->segments();
+ for (const shared_ptr<pv::data::Segment> &s : segments)
+ samplerate = std::max(samplerate, s->samplerate());
+ }
+
+ // If there is no sample rate given we use samples as unit
+ if (samplerate == 0.0)
+ samplerate = 1.0;
+
+ return samplerate;
+}
+
+const unordered_set< shared_ptr<view::Signal> > Session::signals() const
+{
+ shared_lock<shared_mutex> lock(signals_mutex_);
+ return signals_;
+}
+
+#ifdef ENABLE_DECODE
+bool Session::add_decoder(srd_decoder *const dec)
+{
+ map<const srd_channel*, shared_ptr<view::LogicSignal> > channels;
+ shared_ptr<data::DecoderStack> decoder_stack;
+
+ try {
+ lock_guard<boost::shared_mutex> lock(signals_mutex_);
+
+ // Create the decoder
+ decoder_stack = shared_ptr<data::DecoderStack>(
+ new data::DecoderStack(*this, dec));
+
+ // Make a list of all the channels
+ std::vector<const srd_channel*> all_channels;
+ for (const GSList *i = dec->channels; i; i = i->next)
+ all_channels.push_back((const srd_channel*)i->data);
+ for (const GSList *i = dec->opt_channels; i; i = i->next)
+ all_channels.push_back((const srd_channel*)i->data);
+
+ // Auto select the initial channels
+ for (const srd_channel *pdch : all_channels)
+ for (shared_ptr<view::Signal> s : signals_) {
+ shared_ptr<view::LogicSignal> l =
+ dynamic_pointer_cast<view::LogicSignal>(s);
+ if (l && QString::fromUtf8(pdch->name).
+ toLower().contains(
+ l->name().toLower()))
+ channels[pdch] = l;
+ }
+
+ assert(decoder_stack);
+ assert(!decoder_stack->stack().empty());
+ assert(decoder_stack->stack().front());
+ decoder_stack->stack().front()->set_channels(channels);
+
+ // Create the decode signal
+ shared_ptr<view::DecodeTrace> d(
+ new view::DecodeTrace(*this, decoder_stack,
+ decode_traces_.size()));
+ decode_traces_.push_back(d);
+ } catch (std::runtime_error e) {
+ return false;
+ }
+
+ signals_changed();
+
+ // Do an initial decode
+ decoder_stack->begin_decode();
+
+ return true;
+}
+
+vector< shared_ptr<view::DecodeTrace> > Session::get_decode_signals() const
+{
+ shared_lock<shared_mutex> lock(signals_mutex_);
+ return decode_traces_;
+}
+
+void Session::remove_decode_signal(view::DecodeTrace *signal)
+{
+ for (auto i = decode_traces_.begin(); i != decode_traces_.end(); i++)
+ if ((*i).get() == signal) {
+ decode_traces_.erase(i);
+ signals_changed();
+ return;
+ }
+}
+#endif
+
+void Session::set_capture_state(capture_state state)
+{
+ bool changed;
+
+ {
+ lock_guard<mutex> lock(sampling_mutex_);
+ changed = capture_state_ != state;
+ capture_state_ = state;
+ }
+
+ if (changed)
+ capture_state_changed(state);
+}
+
+void Session::update_signals()
+{
+ assert(device_);
+
+ lock_guard<recursive_mutex> lock(data_mutex_);
+
+ const shared_ptr<sigrok::Device> sr_dev = device_->device();
+ if (!sr_dev) {
+ signals_.clear();
+ logic_data_.reset();
+ return;
+ }
+
+ // Detect what data types we will receive
+ auto channels = sr_dev->channels();
+ unsigned int logic_channel_count = std::count_if(
+ channels.begin(), channels.end(),
+ [] (shared_ptr<Channel> channel) {
+ return channel->type() == ChannelType::LOGIC; });
+
+ // Create data containers for the logic data segments
+ {
+ lock_guard<recursive_mutex> data_lock(data_mutex_);
+
+ if (logic_channel_count == 0) {
+ logic_data_.reset();
+ } else if (!logic_data_ ||
+ logic_data_->num_channels() != logic_channel_count) {
+ logic_data_.reset(new data::Logic(
+ logic_channel_count));
+ assert(logic_data_);
+ }
+ }
+
+ // Make the Signals list
+ {
+ unique_lock<shared_mutex> lock(signals_mutex_);
+
+ unordered_set< shared_ptr<view::Signal> > prev_sigs(signals_);
+ signals_.clear();
+
+ for (auto channel : sr_dev->channels()) {
+ shared_ptr<view::Signal> signal;
+
+ // Find the channel in the old signals
+ const auto iter = std::find_if(
+ prev_sigs.cbegin(), prev_sigs.cend(),
+ [&](const shared_ptr<view::Signal> &s) {
+ return s->channel() == channel;
+ });
+ if (iter != prev_sigs.end()) {
+ // Copy the signal from the old set to the new
+ signal = *iter;
+ auto logic_signal = dynamic_pointer_cast<
+ view::LogicSignal>(signal);
+ if (logic_signal)
+ logic_signal->set_logic_data(
+ logic_data_);
+ } else {
+ // Create a new signal
+ switch(channel->type()->id()) {
+ case SR_CHANNEL_LOGIC:
+ signal = shared_ptr<view::Signal>(
+ new view::LogicSignal(*this,
+ device_, channel,
+ logic_data_));
+ break;
+
+ case SR_CHANNEL_ANALOG:
+ {
+ shared_ptr<data::Analog> data(
+ new data::Analog());
+ signal = shared_ptr<view::Signal>(
+ new view::AnalogSignal(
+ *this, channel, data));
+ break;
+ }
+
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ assert(signal);
+ signals_.insert(signal);
+ }
+ }
+
+ signals_changed();
+}
+
+shared_ptr<view::Signal> Session::signal_from_channel(
+ shared_ptr<Channel> channel) const
+{
+ lock_guard<boost::shared_mutex> lock(signals_mutex_);
+ for (shared_ptr<view::Signal> sig : signals_) {
+ assert(sig);
+ if (sig->channel() == channel)
+ return sig;
+ }
+ return shared_ptr<view::Signal>();
+}
+
+void Session::sample_thread_proc(shared_ptr<devices::Device> device,
+ function<void (const QString)> error_handler)
+{
+ assert(device);
+ assert(error_handler);
+
+ (void)device;
+
+ cur_samplerate_ = device_->read_config<uint64_t>(ConfigKey::SAMPLERATE);
+
+ out_of_memory_ = false;
+
+ try {
+ device_->start();
+ } catch (Error e) {
+ error_handler(e.what());
+ return;
+ }
+
+ set_capture_state(device_->session()->trigger() ?
+ AwaitingTrigger : Running);
+
+ device_->run();
+ set_capture_state(Stopped);
+
+ // Confirm that SR_DF_END was received
+ if (cur_logic_segment_) {
+ qDebug("SR_DF_END was not received.");
+ assert(0);
+ }
+
+ if (out_of_memory_)
+ error_handler(tr("Out of memory, acquisition stopped."));
+}
+
+void Session::feed_in_header()
+{
+ cur_samplerate_ = device_->read_config<uint64_t>(ConfigKey::SAMPLERATE);
+}
+
+void Session::feed_in_meta(shared_ptr<Meta> meta)
+{
+ for (auto entry : meta->config()) {
+ switch (entry.first->id()) {
+ case SR_CONF_SAMPLERATE:
+ // We can't rely on the header to always contain the sample rate,
+ // so in case it's supplied via a meta packet, we use it.
+ if (!cur_samplerate_)
+ cur_samplerate_ = g_variant_get_uint64(entry.second.gobj());
+
+ /// @todo handle samplerate changes
+ break;
+ default:
+ // Unknown metadata is not an error.
+ break;
+ }
+ }
+
+ signals_changed();
+}
+
+void Session::feed_in_trigger()
+{
+ // The channel containing most samples should be most accurate
+ uint64_t sample_count = 0;
+
+ for (const shared_ptr<pv::data::SignalData> d : get_data()) {
+ assert(d);
+ uint64_t temp_count = 0;
+
+ const vector< shared_ptr<pv::data::Segment> > segments =
+ d->segments();
+ for (const shared_ptr<pv::data::Segment> &s : segments)
+ temp_count += s->get_sample_count();
+
+ if (temp_count > sample_count)
+ sample_count = temp_count;
+ }
+
+ trigger_event(sample_count / get_samplerate());
+}
+
+void Session::feed_in_frame_begin()
+{
+ if (cur_logic_segment_ || !cur_analog_segments_.empty())
+ frame_began();
+}
+
+void Session::feed_in_logic(shared_ptr<Logic> logic)
+{
+ lock_guard<recursive_mutex> lock(data_mutex_);
+
+ const size_t sample_count = logic->data_length() / logic->unit_size();
+
+ if (!logic_data_) {
+ // The only reason logic_data_ would not have been created is
+ // if it was not possible to determine the signals when the
+ // device was created.
+ update_signals();
+ }
+
+ if (!cur_logic_segment_) {
+ // This could be the first packet after a trigger
+ set_capture_state(Running);
+
+ // Create a new data segment
+ cur_logic_segment_ = shared_ptr<data::LogicSegment>(
+ new data::LogicSegment(
+ logic, cur_samplerate_, sample_count));
+ logic_data_->push_segment(cur_logic_segment_);
+
+ // @todo Putting this here means that only listeners querying
+ // for logic will be notified. Currently the only user of
+ // frame_began is DecoderStack, but in future we need to signal
+ // this after both analog and logic sweeps have begun.
+ frame_began();
+ } else {
+ // Append to the existing data segment
+ cur_logic_segment_->append_payload(logic);
+ }
+
+ data_received();
+}
+
+void Session::feed_in_analog(shared_ptr<Analog> analog)
+{
+ lock_guard<recursive_mutex> lock(data_mutex_);
+
+ const vector<shared_ptr<Channel>> channels = analog->channels();
+ const unsigned int channel_count = channels.size();
+ const size_t sample_count = analog->num_samples() / channel_count;
+ const float *data = static_cast<const float *>(analog->data_pointer());
+ bool sweep_beginning = false;
+
+ if (signals_.empty())
+ update_signals();
+
+ for (auto channel : channels) {
+ shared_ptr<data::AnalogSegment> segment;
+
+ // Try to get the segment of the channel
+ const map< shared_ptr<Channel>, shared_ptr<data::AnalogSegment> >::
+ iterator iter = cur_analog_segments_.find(channel);
+ if (iter != cur_analog_segments_.end())
+ segment = (*iter).second;
+ else {
+ // If no segment was found, this means we haven't
+ // created one yet. i.e. this is the first packet
+ // in the sweep containing this segment.
+ sweep_beginning = true;
+
+ // Create a segment, keep it in the maps of channels
+ segment = shared_ptr<data::AnalogSegment>(
+ new data::AnalogSegment(
+ cur_samplerate_, sample_count));
+ cur_analog_segments_[channel] = segment;
+
+ // Find the analog data associated with the channel
+ shared_ptr<view::AnalogSignal> sig =
+ dynamic_pointer_cast<view::AnalogSignal>(
+ signal_from_channel(channel));
+ assert(sig);
+
+ shared_ptr<data::Analog> data(sig->analog_data());
+ assert(data);
+
+ // Push the segment into the analog data.
+ data->push_segment(segment);
+ }
+
+ assert(segment);
+
+ // Append the samples in the segment
+ segment->append_interleaved_samples(data++, sample_count,
+ channel_count);
+ }
+
+ if (sweep_beginning) {
+ // This could be the first packet after a trigger
+ set_capture_state(Running);
+ }
+
+ data_received();
+}
+
+void Session::data_feed_in(shared_ptr<sigrok::Device> device,
+ shared_ptr<Packet> packet)
+{
+ (void)device;
+
+ assert(device);
+ assert(device == device_->device());
+ assert(packet);
+
+ switch (packet->type()->id()) {
+ case SR_DF_HEADER:
+ feed_in_header();
+ break;
+
+ case SR_DF_META:
+ feed_in_meta(dynamic_pointer_cast<Meta>(packet->payload()));
+ break;
+
+ case SR_DF_TRIGGER:
+ feed_in_trigger();
+ break;
+
+ case SR_DF_FRAME_BEGIN:
+ feed_in_frame_begin();
+ break;
+
+ case SR_DF_LOGIC:
+ try {
+ feed_in_logic(dynamic_pointer_cast<Logic>(packet->payload()));
+ } catch (std::bad_alloc) {
+ out_of_memory_ = true;
+ device_->stop();
+ }
+ break;
+
+ case SR_DF_ANALOG:
+ try {
+ feed_in_analog(dynamic_pointer_cast<Analog>(packet->payload()));
+ } catch (std::bad_alloc) {
+ out_of_memory_ = true;
+ device_->stop();
+ }
+ break;
+
+ case SR_DF_END:
+ {
+ {
+ lock_guard<recursive_mutex> lock(data_mutex_);
+ cur_logic_segment_.reset();
+ cur_analog_segments_.clear();
+ }
+ frame_ended();
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+} // namespace pv
diff --git a/pv/session.hpp b/pv/session.hpp
new file mode 100644
index 0000000..ac3d654
--- /dev/null
+++ b/pv/session.hpp
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012-14 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_SESSION_HPP
+#define PULSEVIEW_PV_SESSION_HPP
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <string>
+#include <thread>
+#include <unordered_set>
+#include <vector>
+
+#ifdef _WIN32
+// Windows: Avoid boost/thread namespace pollution (which includes windows.h).
+#define NOGDI
+#define NORESOURCE
+#endif
+#include <boost/thread/shared_mutex.hpp>
+
+#include <QObject>
+#include <QString>
+
+#include "util.hpp"
+
+struct srd_decoder;
+struct srd_channel;
+
+namespace sigrok {
+class Analog;
+class Channel;
+class Device;
+class Logic;
+class Meta;
+class Packet;
+class Session;
+}
+
+namespace pv {
+
+class DeviceManager;
+
+namespace data {
+class Analog;
+class AnalogSegment;
+class Logic;
+class LogicSegment;
+class SignalData;
+}
+
+namespace devices {
+class Device;
+}
+
+namespace view {
+class DecodeTrace;
+class LogicSignal;
+class Signal;
+}
+
+class Session : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum capture_state {
+ Stopped,
+ AwaitingTrigger,
+ Running
+ };
+
+public:
+ Session(DeviceManager &device_manager);
+
+ ~Session();
+
+ DeviceManager& device_manager();
+
+ const DeviceManager& device_manager() const;
+
+ std::shared_ptr<sigrok::Session> session() const;
+
+ std::shared_ptr<devices::Device> device() const;
+
+ /**
+ * Sets device instance that will be used in the next capture session.
+ */
+ void set_device(std::shared_ptr<devices::Device> device);
+
+ void set_default_device();
+
+ capture_state get_capture_state() const;
+
+ void start_capture(std::function<void (const QString)> error_handler);
+
+ void stop_capture();
+
+ std::set< std::shared_ptr<data::SignalData> > get_data() const;
+
+ double get_samplerate() const;
+
+ const std::unordered_set< std::shared_ptr<view::Signal> >
+ signals() const;
+
+#ifdef ENABLE_DECODE
+ bool add_decoder(srd_decoder *const dec);
+
+ std::vector< std::shared_ptr<view::DecodeTrace> >
+ get_decode_signals() const;
+
+ void remove_decode_signal(view::DecodeTrace *signal);
+#endif
+
+private:
+ void set_capture_state(capture_state state);
+
+ void update_signals();
+
+ std::shared_ptr<view::Signal> signal_from_channel(
+ std::shared_ptr<sigrok::Channel> channel) const;
+
+private:
+ void sample_thread_proc(std::shared_ptr<devices::Device> device,
+ std::function<void (const QString)> error_handler);
+
+ void feed_in_header();
+
+ void feed_in_meta(std::shared_ptr<sigrok::Meta> meta);
+
+ void feed_in_trigger();
+
+ void feed_in_frame_begin();
+
+ void feed_in_logic(std::shared_ptr<sigrok::Logic> logic);
+
+ void feed_in_analog(std::shared_ptr<sigrok::Analog> analog);
+
+ void data_feed_in(std::shared_ptr<sigrok::Device> device,
+ std::shared_ptr<sigrok::Packet> packet);
+
+private:
+ DeviceManager &device_manager_;
+ std::shared_ptr<devices::Device> device_;
+
+ std::vector< std::shared_ptr<view::DecodeTrace> > decode_traces_;
+
+ mutable std::mutex sampling_mutex_; //!< Protects access to capture_state_.
+ capture_state capture_state_;
+
+ mutable boost::shared_mutex signals_mutex_;
+ std::unordered_set< std::shared_ptr<view::Signal> > signals_;
+
+ mutable std::recursive_mutex data_mutex_;
+ std::shared_ptr<data::Logic> logic_data_;
+ uint64_t cur_samplerate_;
+ std::shared_ptr<data::LogicSegment> cur_logic_segment_;
+ std::map< std::shared_ptr<sigrok::Channel>, std::shared_ptr<data::AnalogSegment> >
+ cur_analog_segments_;
+
+ std::thread sampling_thread_;
+
+ bool out_of_memory_;
+
+Q_SIGNALS:
+ void capture_state_changed(int state);
+ void device_selected();
+
+ void signals_changed();
+
+ void trigger_event(util::Timestamp location);
+
+ void frame_began();
+
+ void data_received();
+
+ void frame_ended();
+};
+
+} // namespace pv
+
+#endif // PULSEVIEW_PV_SESSION_HPP
diff --git a/pv/sigsession.cpp b/pv/sigsession.cpp
deleted file mode 100644
index b441dab..0000000
--- a/pv/sigsession.cpp
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012-14 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifdef ENABLE_DECODE
-#include <libsigrokdecode/libsigrokdecode.h>
-#endif
-
-#include "sigsession.h"
-
-#include "devicemanager.h"
-#include "device/device.h"
-#include "device/file.h"
-
-#include "data/analog.h"
-#include "data/analogsnapshot.h"
-#include "data/decoderstack.h"
-#include "data/logic.h"
-#include "data/logicsnapshot.h"
-#include "data/decode/decoder.h"
-
-#include "view/analogsignal.h"
-#include "view/decodetrace.h"
-#include "view/logicsignal.h"
-
-#include <assert.h>
-
-#include <stdexcept>
-
-#include <boost/foreach.hpp>
-
-#include <sys/stat.h>
-
-#include <QDebug>
-
-using boost::dynamic_pointer_cast;
-using boost::function;
-using boost::lock_guard;
-using boost::mutex;
-using boost::shared_ptr;
-using std::list;
-using std::map;
-using std::set;
-using std::string;
-using std::vector;
-
-namespace pv {
-
-// TODO: This should not be necessary
-SigSession* SigSession::_session = NULL;
-
-SigSession::SigSession(DeviceManager &device_manager) :
- _device_manager(device_manager),
- _capture_state(Stopped)
-{
- // TODO: This should not be necessary
- _session = this;
-
- set_default_device();
-}
-
-SigSession::~SigSession()
-{
- using pv::device::Device;
-
- stop_capture();
-
- if (_sampling_thread.joinable())
- _sampling_thread.join();
-
- _dev_inst->release();
-
- // TODO: This should not be necessary
- _session = NULL;
-}
-
-shared_ptr<device::DevInst> SigSession::get_device() const
-{
- return _dev_inst;
-}
-
-void SigSession::set_device(
- shared_ptr<device::DevInst> dev_inst) throw(QString)
-{
- using pv::device::Device;
-
- // Ensure we are not capturing before setting the device
- stop_capture();
-
- if (_dev_inst) {
- sr_session_datafeed_callback_remove_all();
- _dev_inst->release();
- }
-
- _dev_inst = dev_inst;
- _decode_traces.clear();
-
- if (dev_inst) {
- dev_inst->use(this);
- sr_session_datafeed_callback_add(data_feed_in_proc, NULL);
- update_signals(dev_inst);
- }
-}
-
-void SigSession::set_file(const string &name) throw(QString)
-{
- // Deslect the old device, because file type detection in File::create
- // destorys the old session inside libsigrok.
- set_device(shared_ptr<device::DevInst>());
- set_device(shared_ptr<device::DevInst>(device::File::create(name)));
-}
-
-void SigSession::set_default_device()
-{
- shared_ptr<pv::device::DevInst> default_device;
- const list< shared_ptr<device::Device> > &devices =
- _device_manager.devices();
-
- if (!devices.empty()) {
- // Fall back to the first device in the list.
- default_device = devices.front();
-
- // Try and find the demo device and select that by default
- BOOST_FOREACH (shared_ptr<pv::device::Device> dev, devices)
- if (strcmp(dev->dev_inst()->driver->name,
- "demo") == 0) {
- default_device = dev;
- break;
- }
- }
-
- set_device(default_device);
-}
-
-void SigSession::release_device(device::DevInst *dev_inst)
-{
- (void)dev_inst;
- assert(_dev_inst.get() == dev_inst);
-
- assert(_capture_state == Stopped);
- _dev_inst = shared_ptr<device::DevInst>();
-}
-
-SigSession::capture_state SigSession::get_capture_state() const
-{
- lock_guard<mutex> lock(_sampling_mutex);
- return _capture_state;
-}
-
-void SigSession::start_capture(function<void (const QString)> error_handler)
-{
- stop_capture();
-
- // Check that a device instance has been selected.
- if (!_dev_inst) {
- qDebug() << "No device selected";
- return;
- }
-
- assert(_dev_inst->dev_inst());
-
- // Check that at least one probe is enabled
- const GSList *l;
- for (l = _dev_inst->dev_inst()->channels; l; l = l->next) {
- sr_channel *const probe = (sr_channel*)l->data;
- assert(probe);
- if (probe->enabled)
- break;
- }
-
- if (!l) {
- error_handler(tr("No channels enabled."));
- return;
- }
-
- // Begin the session
- _sampling_thread = boost::thread(
- &SigSession::sample_thread_proc, this, _dev_inst,
- error_handler);
-}
-
-void SigSession::stop_capture()
-{
- if (get_capture_state() == Stopped)
- return;
-
- sr_session_stop();
-
- // Check that sampling stopped
- if (_sampling_thread.joinable())
- _sampling_thread.join();
-}
-
-set< shared_ptr<data::SignalData> > SigSession::get_data() const
-{
- lock_guard<mutex> lock(_signals_mutex);
- set< shared_ptr<data::SignalData> > data;
- BOOST_FOREACH(const shared_ptr<view::Signal> sig, _signals) {
- assert(sig);
- data.insert(sig->data());
- }
-
- return data;
-}
-
-vector< shared_ptr<view::Signal> > SigSession::get_signals() const
-{
- lock_guard<mutex> lock(_signals_mutex);
- return _signals;
-}
-
-#ifdef ENABLE_DECODE
-bool SigSession::add_decoder(srd_decoder *const dec)
-{
- map<const srd_channel*, shared_ptr<view::LogicSignal> > probes;
- shared_ptr<data::DecoderStack> decoder_stack;
-
- try
- {
- lock_guard<mutex> lock(_signals_mutex);
-
- // Create the decoder
- decoder_stack = shared_ptr<data::DecoderStack>(
- new data::DecoderStack(*this, dec));
-
- // Make a list of all the probes
- std::vector<const srd_channel*> all_probes;
- for(const GSList *i = dec->channels; i; i = i->next)
- all_probes.push_back((const srd_channel*)i->data);
- for(const GSList *i = dec->opt_channels; i; i = i->next)
- all_probes.push_back((const srd_channel*)i->data);
-
- // Auto select the initial probes
- BOOST_FOREACH(const srd_channel *pdch, all_probes)
- BOOST_FOREACH(shared_ptr<view::Signal> s, _signals)
- {
- shared_ptr<view::LogicSignal> l =
- dynamic_pointer_cast<view::LogicSignal>(s);
- if (l && QString::fromUtf8(pdch->name).
- toLower().contains(
- l->get_name().toLower()))
- probes[pdch] = l;
- }
-
- assert(decoder_stack);
- assert(!decoder_stack->stack().empty());
- assert(decoder_stack->stack().front());
- decoder_stack->stack().front()->set_probes(probes);
-
- // Create the decode signal
- shared_ptr<view::DecodeTrace> d(
- new view::DecodeTrace(*this, decoder_stack,
- _decode_traces.size()));
- _decode_traces.push_back(d);
- }
- catch(std::runtime_error e)
- {
- return false;
- }
-
- signals_changed();
-
- // Do an initial decode
- decoder_stack->begin_decode();
-
- return true;
-}
-
-vector< shared_ptr<view::DecodeTrace> > SigSession::get_decode_signals() const
-{
- lock_guard<mutex> lock(_signals_mutex);
- return _decode_traces;
-}
-
-void SigSession::remove_decode_signal(view::DecodeTrace *signal)
-{
- for (vector< shared_ptr<view::DecodeTrace> >::iterator i =
- _decode_traces.begin();
- i != _decode_traces.end();
- i++)
- if ((*i).get() == signal)
- {
- _decode_traces.erase(i);
- signals_changed();
- return;
- }
-}
-#endif
-
-void SigSession::set_capture_state(capture_state state)
-{
- lock_guard<mutex> lock(_sampling_mutex);
- const bool changed = _capture_state != state;
- _capture_state = state;
- if(changed)
- capture_state_changed(state);
-}
-
-void SigSession::update_signals(shared_ptr<device::DevInst> dev_inst)
-{
- assert(dev_inst);
- assert(_capture_state == Stopped);
-
- unsigned int logic_probe_count = 0;
-
- // Clear the decode traces
- _decode_traces.clear();
-
- // Detect what data types we will receive
- if(dev_inst) {
- assert(dev_inst->dev_inst());
- for (const GSList *l = dev_inst->dev_inst()->channels;
- l; l = l->next) {
- const sr_channel *const probe = (const sr_channel *)l->data;
- if (!probe->enabled)
- continue;
-
- switch(probe->type) {
- case SR_CHANNEL_LOGIC:
- logic_probe_count++;
- break;
- }
- }
- }
-
- // Create data containers for the logic data snapshots
- {
- lock_guard<mutex> data_lock(_data_mutex);
-
- _logic_data.reset();
- if (logic_probe_count != 0) {
- _logic_data.reset(new data::Logic(
- logic_probe_count));
- assert(_logic_data);
- }
- }
-
- // Make the Signals list
- do {
- lock_guard<mutex> lock(_signals_mutex);
-
- _signals.clear();
-
- if(!dev_inst)
- break;
-
- assert(dev_inst->dev_inst());
- for (const GSList *l = dev_inst->dev_inst()->channels;
- l; l = l->next) {
- shared_ptr<view::Signal> signal;
- sr_channel *const probe = (sr_channel *)l->data;
- assert(probe);
-
- switch(probe->type) {
- case SR_CHANNEL_LOGIC:
- signal = shared_ptr<view::Signal>(
- new view::LogicSignal(dev_inst,
- probe, _logic_data));
- break;
-
- case SR_CHANNEL_ANALOG:
- {
- shared_ptr<data::Analog> data(
- new data::Analog());
- signal = shared_ptr<view::Signal>(
- new view::AnalogSignal(dev_inst,
- probe, data));
- break;
- }
-
- default:
- assert(0);
- break;
- }
-
- assert(signal);
- _signals.push_back(signal);
- }
-
- } while(0);
-
- signals_changed();
-}
-
-shared_ptr<view::Signal> SigSession::signal_from_probe(
- const sr_channel *probe) const
-{
- lock_guard<mutex> lock(_signals_mutex);
- BOOST_FOREACH(shared_ptr<view::Signal> sig, _signals) {
- assert(sig);
- if (sig->probe() == probe)
- return sig;
- }
- return shared_ptr<view::Signal>();
-}
-
-void SigSession::read_sample_rate(const sr_dev_inst *const sdi)
-{
- GVariant *gvar;
- uint64_t sample_rate = 0;
-
- // Read out the sample rate
- if(sdi->driver)
- {
- const int ret = sr_config_get(sdi->driver, sdi, NULL,
- SR_CONF_SAMPLERATE, &gvar);
- if (ret != SR_OK) {
- qDebug("Failed to get samplerate\n");
- return;
- }
-
- sample_rate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
- }
-
- // Set the sample rate of all data
- const set< shared_ptr<data::SignalData> > data_set = get_data();
- BOOST_FOREACH(shared_ptr<data::SignalData> data, data_set) {
- assert(data);
- data->set_samplerate(sample_rate);
- }
-}
-
-void SigSession::sample_thread_proc(shared_ptr<device::DevInst> dev_inst,
- function<void (const QString)> error_handler)
-{
- assert(dev_inst);
- assert(dev_inst->dev_inst());
- assert(error_handler);
-
- read_sample_rate(dev_inst->dev_inst());
-
- try {
- dev_inst->start();
- } catch(const QString e) {
- error_handler(e);
- return;
- }
-
- set_capture_state(dev_inst->is_trigger_enabled() ?
- AwaitingTrigger : Running);
-
- dev_inst->run();
- set_capture_state(Stopped);
-
- // Confirm that SR_DF_END was received
- if (_cur_logic_snapshot)
- {
- qDebug("SR_DF_END was not received.");
- assert(0);
- }
-}
-
-void SigSession::feed_in_header(const sr_dev_inst *sdi)
-{
- read_sample_rate(sdi);
-}
-
-void SigSession::feed_in_meta(const sr_dev_inst *sdi,
- const sr_datafeed_meta &meta)
-{
- (void)sdi;
-
- for (const GSList *l = meta.config; l; l = l->next) {
- const sr_config *const src = (const sr_config*)l->data;
- switch (src->key) {
- case SR_CONF_SAMPLERATE:
- /// @todo handle samplerate changes
- /// samplerate = (uint64_t *)src->value;
- break;
- default:
- // Unknown metadata is not an error.
- break;
- }
- }
-
- signals_changed();
-}
-
-void SigSession::feed_in_frame_begin()
-{
- if (_cur_logic_snapshot || !_cur_analog_snapshots.empty())
- frame_began();
-}
-
-void SigSession::feed_in_logic(const sr_datafeed_logic &logic)
-{
- lock_guard<mutex> lock(_data_mutex);
-
- if (!_logic_data)
- {
- qDebug() << "Unexpected logic packet";
- return;
- }
-
- if (!_cur_logic_snapshot)
- {
- // This could be the first packet after a trigger
- set_capture_state(Running);
-
- // Create a new data snapshot
- _cur_logic_snapshot = shared_ptr<data::LogicSnapshot>(
- new data::LogicSnapshot(logic, _dev_inst->get_sample_limit()));
- _logic_data->push_snapshot(_cur_logic_snapshot);
-
- // @todo Putting this here means that only listeners querying
- // for logic will be notified. Currently the only user of
- // frame_began is DecoderStack, but in future we need to signal
- // this after both analog and logic sweeps have begun.
- frame_began();
- }
- else
- {
- // Append to the existing data snapshot
- _cur_logic_snapshot->append_payload(logic);
- }
-
- data_received();
-}
-
-void SigSession::feed_in_analog(const sr_datafeed_analog &analog)
-{
- lock_guard<mutex> lock(_data_mutex);
-
- const unsigned int probe_count = g_slist_length(analog.channels);
- const size_t sample_count = analog.num_samples / probe_count;
- const float *data = analog.data;
- bool sweep_beginning = false;
-
- for (GSList *p = analog.channels; p; p = p->next)
- {
- shared_ptr<data::AnalogSnapshot> snapshot;
-
- sr_channel *const probe = (sr_channel*)p->data;
- assert(probe);
-
- // Try to get the snapshot of the probe
- const map< const sr_channel*, shared_ptr<data::AnalogSnapshot> >::
- iterator iter = _cur_analog_snapshots.find(probe);
- if (iter != _cur_analog_snapshots.end())
- snapshot = (*iter).second;
- else
- {
- // If no snapshot was found, this means we havn't
- // created one yet. i.e. this is the first packet
- // in the sweep containing this snapshot.
- sweep_beginning = true;
-
- // Create a snapshot, keep it in the maps of probes
- snapshot = shared_ptr<data::AnalogSnapshot>(
- new data::AnalogSnapshot(_dev_inst->get_sample_limit()));
- _cur_analog_snapshots[probe] = snapshot;
-
- // Find the annalog data associated with the probe
- shared_ptr<view::AnalogSignal> sig =
- dynamic_pointer_cast<view::AnalogSignal>(
- signal_from_probe(probe));
- assert(sig);
-
- shared_ptr<data::Analog> data(sig->analog_data());
- assert(data);
-
- // Push the snapshot into the analog data.
- data->push_snapshot(snapshot);
- }
-
- assert(snapshot);
-
- // Append the samples in the snapshot
- snapshot->append_interleaved_samples(data++, sample_count,
- probe_count);
- }
-
- if (sweep_beginning) {
- // This could be the first packet after a trigger
- set_capture_state(Running);
- }
-
- data_received();
-}
-
-void SigSession::data_feed_in(const struct sr_dev_inst *sdi,
- const struct sr_datafeed_packet *packet)
-{
- assert(sdi);
- assert(packet);
-
- switch (packet->type) {
- case SR_DF_HEADER:
- feed_in_header(sdi);
- break;
-
- case SR_DF_META:
- assert(packet->payload);
- feed_in_meta(sdi,
- *(const sr_datafeed_meta*)packet->payload);
- break;
-
- case SR_DF_FRAME_BEGIN:
- feed_in_frame_begin();
- break;
-
- case SR_DF_LOGIC:
- assert(packet->payload);
- feed_in_logic(*(const sr_datafeed_logic*)packet->payload);
- break;
-
- case SR_DF_ANALOG:
- assert(packet->payload);
- feed_in_analog(*(const sr_datafeed_analog*)packet->payload);
- break;
-
- case SR_DF_END:
- {
- {
- lock_guard<mutex> lock(_data_mutex);
- _cur_logic_snapshot.reset();
- _cur_analog_snapshots.clear();
- }
- frame_ended();
- break;
- }
- }
-}
-
-void SigSession::data_feed_in_proc(const struct sr_dev_inst *sdi,
- const struct sr_datafeed_packet *packet, void *cb_data)
-{
- (void) cb_data;
- assert(_session);
- _session->data_feed_in(sdi, packet);
-}
-
-} // namespace pv
diff --git a/pv/sigsession.h b/pv/sigsession.h
deleted file mode 100644
index 1221750..0000000
--- a/pv/sigsession.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012-14 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_SIGSESSION_H
-#define PULSEVIEW_PV_SIGSESSION_H
-
-#include <boost/function.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/thread.hpp>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <QObject>
-#include <QString>
-
-#include <libsigrok/libsigrok.h>
-
-struct srd_decoder;
-struct srd_channel;
-
-namespace pv {
-
-class DeviceManager;
-
-namespace data {
-class Analog;
-class AnalogSnapshot;
-class Logic;
-class LogicSnapshot;
-class SignalData;
-}
-
-namespace device {
-class DevInst;
-}
-
-namespace view {
-class DecodeTrace;
-class LogicSignal;
-class Signal;
-}
-
-class SigSession : public QObject
-{
- Q_OBJECT
-
-public:
- enum capture_state {
- Stopped,
- AwaitingTrigger,
- Running
- };
-
-public:
- SigSession(DeviceManager &device_manager);
-
- ~SigSession();
-
- boost::shared_ptr<device::DevInst> get_device() const;
-
- /**
- * Sets device instance that will be used in the next capture session.
- */
- void set_device(boost::shared_ptr<device::DevInst> dev_inst)
- throw(QString);
-
- void set_file(const std::string &name)
- throw(QString);
-
- void set_default_device();
-
- void release_device(device::DevInst *dev_inst);
-
- capture_state get_capture_state() const;
-
- void start_capture(boost::function<void (const QString)> error_handler);
-
- void stop_capture();
-
- std::set< boost::shared_ptr<data::SignalData> > get_data() const;
-
- std::vector< boost::shared_ptr<view::Signal> >
- get_signals() const;
-
-#ifdef ENABLE_DECODE
- bool add_decoder(srd_decoder *const dec);
-
- std::vector< boost::shared_ptr<view::DecodeTrace> >
- get_decode_signals() const;
-
- void remove_decode_signal(view::DecodeTrace *signal);
-#endif
-
-private:
- void set_capture_state(capture_state state);
-
- void update_signals(boost::shared_ptr<device::DevInst> dev_inst);
-
- boost::shared_ptr<view::Signal> signal_from_probe(
- const sr_channel *probe) const;
-
- void read_sample_rate(const sr_dev_inst *const sdi);
-
-private:
- /**
- * Attempts to autodetect the format. Failing that
- * @param filename The filename of the input file.
- * @return A pointer to the 'struct sr_input_format' that should be
- * used, or NULL if no input format was selected or
- * auto-detected.
- */
- static sr_input_format* determine_input_file_format(
- const std::string &filename);
-
- static sr_input* load_input_file_format(
- const std::string &filename,
- boost::function<void (const QString)> error_handler,
- sr_input_format *format = NULL);
-
- void sample_thread_proc(boost::shared_ptr<device::DevInst> dev_inst,
- boost::function<void (const QString)> error_handler);
-
- void feed_in_header(const sr_dev_inst *sdi);
-
- void feed_in_meta(const sr_dev_inst *sdi,
- const sr_datafeed_meta &meta);
-
- void feed_in_frame_begin();
-
- void feed_in_logic(const sr_datafeed_logic &logic);
-
- void feed_in_analog(const sr_datafeed_analog &analog);
-
- void data_feed_in(const struct sr_dev_inst *sdi,
- const struct sr_datafeed_packet *packet);
-
- static void data_feed_in_proc(const struct sr_dev_inst *sdi,
- const struct sr_datafeed_packet *packet, void *cb_data);
-
-private:
- DeviceManager &_device_manager;
-
- /**
- * The device instance that will be used in the next capture session.
- */
- boost::shared_ptr<device::DevInst> _dev_inst;
-
- std::vector< boost::shared_ptr<view::DecodeTrace> > _decode_traces;
-
- mutable boost::mutex _sampling_mutex;
- capture_state _capture_state;
-
- mutable boost::mutex _signals_mutex;
- std::vector< boost::shared_ptr<view::Signal> > _signals;
-
- mutable boost::mutex _data_mutex;
- boost::shared_ptr<data::Logic> _logic_data;
- boost::shared_ptr<data::LogicSnapshot> _cur_logic_snapshot;
- std::map< const sr_channel*, boost::shared_ptr<data::AnalogSnapshot> >
- _cur_analog_snapshots;
-
- boost::thread _sampling_thread;
-
-signals:
- void capture_state_changed(int state);
-
- void signals_changed();
-
- void frame_began();
-
- void data_received();
-
- void frame_ended();
-
-private:
- // TODO: This should not be necessary. Multiple concurrent
- // sessions should should be supported and it should be
- // possible to associate a pointer with a sr_session.
- static SigSession *_session;
-};
-
-} // namespace pv
-
-#endif // PULSEVIEW_PV_SIGSESSION_H
diff --git a/pv/storesession.cpp b/pv/storesession.cpp
index 3d0f12f..4642ed6 100644
--- a/pv/storesession.cpp
+++ b/pv/storesession.cpp
@@ -18,36 +18,70 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "storesession.h"
-
-#include <pv/sigsession.h>
-#include <pv/data/logic.h>
-#include <pv/data/logicsnapshot.h>
-#include <pv/view/signal.h>
-
-using boost::dynamic_pointer_cast;
-using boost::mutex;
-using boost::shared_ptr;
-using boost::thread;
-using boost::lock_guard;
+#include <cassert>
+
+#ifdef _WIN32
+// Windows: Avoid boost/thread namespace pollution (which includes windows.h).
+#define NOGDI
+#define NORESOURCE
+#endif
+#include <boost/thread/locks.hpp>
+#include <boost/thread/shared_mutex.hpp>
+
+#include "storesession.hpp"
+
+#include <pv/devicemanager.hpp>
+#include <pv/session.hpp>
+#include <pv/data/logic.hpp>
+#include <pv/data/logicsegment.hpp>
+#include <pv/devices/device.hpp>
+#include <pv/view/signal.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using boost::shared_lock;
+using boost::shared_mutex;
+
using std::deque;
+using std::dynamic_pointer_cast;
+using std::ios_base;
+using std::lock_guard;
using std::make_pair;
+using std::map;
using std::min;
+using std::mutex;
using std::pair;
using std::set;
+using std::shared_ptr;
using std::string;
+using std::thread;
+using std::unordered_set;
using std::vector;
+using Glib::VariantBase;
+
+using sigrok::ConfigKey;
+using sigrok::Error;
+using sigrok::OutputFormat;
+using sigrok::OutputFlag;
+
namespace pv {
const size_t StoreSession::BlockSize = 1024 * 1024;
StoreSession::StoreSession(const std::string &file_name,
- const SigSession &session) :
- _file_name(file_name),
- _session(session),
- _units_stored(0),
- _unit_count(0)
+ const shared_ptr<OutputFormat> &output_format,
+ const map<string, VariantBase> &options,
+ const std::pair<uint64_t, uint64_t> sample_range,
+ const Session &session) :
+ file_name_(file_name),
+ output_format_(output_format),
+ options_(options),
+ sample_range_(sample_range),
+ session_(session),
+ interrupt_(false),
+ units_stored_(0),
+ unit_count_(0)
{
}
@@ -56,140 +90,166 @@ StoreSession::~StoreSession()
wait();
}
-pair<uint64_t, uint64_t> StoreSession::progress() const
+pair<int, int> StoreSession::progress() const
{
- lock_guard<mutex> lock(_mutex);
- return make_pair(_units_stored, _unit_count);
+ return make_pair(units_stored_.load(), unit_count_.load());
}
const QString& StoreSession::error() const
{
- lock_guard<mutex> lock(_mutex);
- return _error;
+ lock_guard<mutex> lock(mutex_);
+ return error_;
}
bool StoreSession::start()
{
- set< shared_ptr<data::SignalData> > data_set =
- _session.get_data();
- const vector< shared_ptr<view::Signal> > sigs =
- _session.get_signals();
+ const unordered_set< shared_ptr<view::Signal> > sigs(session_.signals());
+
+ // Add enabled channels to the data set
+ set< shared_ptr<data::SignalData> > data_set;
+
+ for (shared_ptr<view::Signal> signal : sigs)
+ if (signal->enabled())
+ data_set.insert(signal->data());
// Check we have logic data
if (data_set.empty() || sigs.empty()) {
- _error = tr("No data to save.");
+ error_ = tr("No data to save.");
return false;
}
if (data_set.size() > 1) {
- _error = tr("PulseView currently only has support for "
+ error_ = tr("PulseView currently only has support for "
"storing a single data stream.");
return false;
}
// Get the logic data
- //shared_ptr<data::SignalData
shared_ptr<data::Logic> data;
if (!(data = dynamic_pointer_cast<data::Logic>(*data_set.begin()))) {
- _error = tr("PulseView currently only has support for "
- "storing a logic data.");
+ error_ = tr("PulseView currently only has support for "
+ "storing logic data.");
return false;
}
- // Get the snapshot
- const deque< shared_ptr<data::LogicSnapshot> > &snapshots =
- data->get_snapshots();
+ // Get the segment
+ const deque< shared_ptr<data::LogicSegment> > &segments =
+ data->logic_segments();
- if (snapshots.empty()) {
- _error = tr("No snapshots to save.");
+ if (segments.empty()) {
+ error_ = tr("No segments to save.");
return false;
}
- const shared_ptr<data::LogicSnapshot> snapshot(snapshots.front());
- assert(snapshot);
-
- // Make a list of probes
- char **const probes = new char*[sigs.size() + 1];
- for (size_t i = 0; i < sigs.size(); i++) {
- shared_ptr<view::Signal> sig(sigs[i]);
- assert(sig);
- probes[i] = strdup(sig->get_name().toUtf8().constData());
+ const shared_ptr<data::LogicSegment> segment(segments.front());
+ assert(segment);
+
+ // Check whether the user wants to export a certain sample range
+ if (sample_range_.first == sample_range_.second) {
+ start_sample_ = 0;
+ sample_count_ = segment->get_sample_count();
+ } else {
+ if (sample_range_.first > sample_range_.second) {
+ start_sample_ = sample_range_.second;
+ sample_count_ = sample_range_.first - sample_range_.second;
+ } else {
+ start_sample_ = sample_range_.first;
+ sample_count_ = sample_range_.second - sample_range_.first;
+ }
}
- probes[sigs.size()] = NULL;
// Begin storing
- if (sr_session_save_init(_file_name.c_str(),
- data->samplerate(), probes) != SR_OK) {
- _error = tr("Error while saving.");
+ try {
+ const auto context = session_.device_manager().context();
+ auto device = session_.device()->device();
+
+ map<string, Glib::VariantBase> options = options_;
+
+ if (!output_format_->test_flag(OutputFlag::INTERNAL_IO_HANDLING))
+ output_stream_.open(file_name_, ios_base::binary |
+ ios_base::trunc | ios_base::out);
+
+ output_ = output_format_->create_output(file_name_, device, options);
+ auto meta = context->create_meta_packet(
+ {{ConfigKey::SAMPLERATE, Glib::Variant<guint64>::create(
+ segment->samplerate())}});
+ output_->receive(meta);
+ } catch (Error error) {
+ error_ = tr("Error while saving.");
return false;
}
- // Delete the probes array
- for (size_t i = 0; i <= sigs.size(); i++)
- free(probes[i]);
- delete[] probes;
-
- _thread = boost::thread(&StoreSession::store_proc, this, snapshot);
+ thread_ = std::thread(&StoreSession::store_proc, this, segment);
return true;
}
void StoreSession::wait()
{
- if (_thread.joinable())
- _thread.join();
+ if (thread_.joinable())
+ thread_.join();
}
void StoreSession::cancel()
{
- _thread.interrupt();
+ interrupt_ = true;
}
-void StoreSession::store_proc(shared_ptr<data::LogicSnapshot> snapshot)
+void StoreSession::store_proc(shared_ptr<data::LogicSegment> segment)
{
- assert(snapshot);
+ assert(segment);
- uint64_t start_sample = 0;
+ unsigned progress_scale = 0;
/// TODO: Wrap this in a std::unique_ptr when we transition to C++11
uint8_t *const data = new uint8_t[BlockSize];
assert(data);
- const int unit_size = snapshot->unit_size();
+ const int unit_size = segment->unit_size();
assert(unit_size != 0);
- {
- lock_guard<mutex> lock(_mutex);
- _unit_count = snapshot->get_sample_count();
- }
+ // Qt needs the progress values to fit inside an int. If they would
+ // not, scale the current and max values down until they do.
+ while ((sample_count_ >> progress_scale) > INT_MAX)
+ progress_scale ++;
+
+ unit_count_ = sample_count_ >> progress_scale;
const unsigned int samples_per_block = BlockSize / unit_size;
- while (!boost::this_thread::interruption_requested() &&
- start_sample < _unit_count)
- {
+ while (!interrupt_ && sample_count_) {
progress_updated();
- const uint64_t end_sample = min(
- start_sample + samples_per_block, _unit_count);
- snapshot->get_samples(data, start_sample, end_sample);
+ const uint64_t packet_len =
+ std::min((uint64_t)samples_per_block, sample_count_);
- if(sr_session_append(_file_name.c_str(), data, unit_size,
- end_sample - start_sample) != SR_OK)
- {
- _error = tr("Error while saving.");
- break;
- }
+ segment->get_samples(data, start_sample_, start_sample_ + packet_len);
- start_sample = end_sample;
+ size_t length = packet_len * unit_size;
- {
- lock_guard<mutex> lock(_mutex);
- _units_stored = start_sample;
+ try {
+ const auto context = session_.device_manager().context();
+ auto logic = context->create_logic_packet(data, length, unit_size);
+ const string data = output_->receive(logic);
+ if (output_stream_.is_open())
+ output_stream_ << data;
+ } catch (Error error) {
+ error_ = tr("Error while saving.");
+ break;
}
+
+ sample_count_ -= packet_len;
+ start_sample_ += packet_len;
+ units_stored_ = unit_count_ - (sample_count_ >> progress_scale);
}
+ // Zeroing the progress variables indicates completion
+ units_stored_ = unit_count_ = 0;
+
progress_updated();
+ output_.reset();
+ output_stream_.close();
+
delete[] data;
}
diff --git a/pv/storesession.h b/pv/storesession.hpp
similarity index 52%
rename from pv/storesession.h
rename to pv/storesession.hpp
index 5ef92a3..6a08180 100644
--- a/pv/storesession.h
+++ b/pv/storesession.hpp
@@ -18,23 +18,34 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_STORESESSION_H
-#define PULSEVIEW_PV_STORESESSION_H
+#ifndef PULSEVIEW_PV_STORESESSION_HPP
+#define PULSEVIEW_PV_STORESESSION_HPP
#include <stdint.h>
+#include <atomic>
+#include <fstream>
+#include <map>
+#include <memory>
+#include <mutex>
#include <string>
+#include <thread>
-#include <boost/thread.hpp>
+#include <glibmm/variant.h>
#include <QObject>
+namespace sigrok {
+class Output;
+class OutputFormat;
+}
+
namespace pv {
-class SigSession;
+class Session;
namespace data {
-class LogicSnapshot;
+class LogicSegment;
}
class StoreSession : public QObject
@@ -46,11 +57,14 @@ private:
public:
StoreSession(const std::string &file_name,
- const SigSession &session);
+ const std::shared_ptr<sigrok::OutputFormat> &output_format,
+ const std::map<std::string, Glib::VariantBase> &options,
+ const std::pair<uint64_t, uint64_t> sample_range,
+ const Session &session);
~StoreSession();
- std::pair<uint64_t, uint64_t> progress() const;
+ std::pair<int, int> progress() const;
const QString& error() const;
@@ -61,23 +75,33 @@ public:
void cancel();
private:
- void store_proc(boost::shared_ptr<pv::data::LogicSnapshot> snapshot);
+ void store_proc(std::shared_ptr<pv::data::LogicSegment> segment);
-signals:
+Q_SIGNALS:
void progress_updated();
private:
- const std::string _file_name;
- const SigSession &_session;
+ const std::string file_name_;
+ const std::shared_ptr<sigrok::OutputFormat> output_format_;
+ const std::map<std::string, Glib::VariantBase> options_;
+ const std::pair<uint64_t, uint64_t> sample_range_;
+ const Session &session_;
+
+ std::shared_ptr<sigrok::Output> output_;
+ std::ofstream output_stream_;
+
+ std::thread thread_;
+
+ std::atomic<bool> interrupt_;
+
+ std::atomic<int> units_stored_, unit_count_;
- boost::thread _thread;
+ mutable std::mutex mutex_;
+ QString error_;
- mutable boost::mutex _mutex;
- uint64_t _units_stored;
- uint64_t _unit_count;
- QString _error;
+ uint64_t start_sample_, sample_count_;
};
} // pv
-#endif // PULSEVIEW_PV_STORESESSION_H
+#endif // PULSEVIEW_PV_STORESESSION_HPP
diff --git a/pv/toolbars/mainbar.cpp b/pv/toolbars/mainbar.cpp
new file mode 100644
index 0000000..8c0b063
--- /dev/null
+++ b/pv/toolbars/mainbar.cpp
@@ -0,0 +1,565 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012-2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <extdef.h>
+
+#include <algorithm>
+#include <cassert>
+
+#include <QAction>
+#include <QDebug>
+#include <QHelpEvent>
+#include <QMenu>
+#include <QToolTip>
+
+#include "mainbar.hpp"
+
+#include <pv/devicemanager.hpp>
+#include <pv/devices/hardwaredevice.hpp>
+#include <pv/mainwindow.hpp>
+#include <pv/popups/deviceoptions.hpp>
+#include <pv/popups/channels.hpp>
+#include <pv/util.hpp>
+#include <pv/widgets/exportmenu.hpp>
+#include <pv/widgets/importmenu.hpp>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+using std::back_inserter;
+using std::copy;
+using std::list;
+using std::map;
+using std::max;
+using std::min;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+using sigrok::Capability;
+using sigrok::ConfigKey;
+using sigrok::Error;
+using sigrok::InputFormat;
+
+namespace pv {
+namespace toolbars {
+
+const uint64_t MainBar::MinSampleCount = 100ULL;
+const uint64_t MainBar::MaxSampleCount = 1000000000000ULL;
+const uint64_t MainBar::DefaultSampleCount = 1000000;
+
+MainBar::MainBar(Session &session, MainWindow &main_window) :
+ QToolBar("Sampling Bar", &main_window),
+ session_(session),
+ main_window_(main_window),
+ device_selector_(this, session.device_manager(),
+ main_window.action_connect()),
+ configure_button_(this),
+ configure_button_action_(nullptr),
+ channels_button_(this),
+ channels_button_action_(nullptr),
+ sample_count_(" samples", this),
+ sample_rate_("Hz", this),
+ updating_sample_rate_(false),
+ updating_sample_count_(false),
+ sample_count_supported_(false),
+ icon_red_(":/icons/status-red.svg"),
+ icon_green_(":/icons/status-green.svg"),
+ icon_grey_(":/icons/status-grey.svg"),
+ run_stop_button_(this),
+ run_stop_button_action_(nullptr),
+ menu_button_(this)
+{
+ setObjectName(QString::fromUtf8("MainBar"));
+
+ setMovable(false);
+ setFloatable(false);
+ setContextMenuPolicy(Qt::PreventContextMenu);
+
+ // Open button
+ QToolButton *const open_button = new QToolButton(this);
+
+ widgets::ImportMenu *import_menu = new widgets::ImportMenu(this,
+ session.device_manager().context(),
+ main_window.action_open());
+ connect(import_menu,
+ SIGNAL(format_selected(std::shared_ptr<sigrok::InputFormat>)),
+ &main_window_,
+ SLOT(import_file(std::shared_ptr<sigrok::InputFormat>)));
+
+ open_button->setMenu(import_menu);
+ open_button->setDefaultAction(main_window.action_open());
+ open_button->setPopupMode(QToolButton::MenuButtonPopup);
+
+ // Save button
+ QToolButton *const save_button = new QToolButton(this);
+
+ vector<QAction *> open_actions;
+ open_actions.push_back(main_window.action_save_as());
+ open_actions.push_back(main_window.action_save_selection_as());
+
+ widgets::ExportMenu *export_menu = new widgets::ExportMenu(this,
+ session.device_manager().context(),
+ open_actions);
+ connect(export_menu,
+ SIGNAL(format_selected(std::shared_ptr<sigrok::OutputFormat>)),
+ &main_window_,
+ SLOT(export_file(std::shared_ptr<sigrok::OutputFormat>)));
+
+ save_button->setMenu(export_menu);
+ save_button->setDefaultAction(main_window.action_save_as());
+ save_button->setPopupMode(QToolButton::MenuButtonPopup);
+
+ // Device selector menu
+ connect(&device_selector_, SIGNAL(device_selected()),
+ this, SLOT(on_device_selected()));
+
+ // Setup the decoder button
+#ifdef ENABLE_DECODE
+ QToolButton *add_decoder_button = new QToolButton(this);
+ add_decoder_button->setIcon(QIcon::fromTheme("add-decoder",
+ QIcon(":/icons/add-decoder.svg")));
+ add_decoder_button->setPopupMode(QToolButton::InstantPopup);
+ add_decoder_button->setMenu(main_window_.menu_decoder_add());
+#endif
+
+ // Setup the burger menu
+ QMenu *const menu = new QMenu(this);
+
+ QMenu *const menu_view = new QMenu;
+ menu_view->setTitle(tr("&View"));
+ menu_view->addAction(main_window.action_view_sticky_scrolling());
+ menu_view->addSeparator();
+ menu_view->addAction(main_window.action_view_coloured_bg());
+
+ QMenu *const menu_help = new QMenu;
+ menu_help->setTitle(tr("&Help"));
+ menu_help->addAction(main_window.action_about());
+
+ menu->addAction(menu_view->menuAction());
+ menu->addSeparator();
+ menu->addAction(menu_help->menuAction());
+ menu->addSeparator();
+ menu->addAction(main_window.action_quit());
+
+ menu_button_.setMenu(menu);
+ menu_button_.setPopupMode(QToolButton::InstantPopup);
+ menu_button_.setIcon(QIcon::fromTheme("menu",
+ QIcon(":/icons/menu.svg")));
+
+ // Setup the toolbar
+ addWidget(open_button);
+ addWidget(save_button);
+ addSeparator();
+ addAction(main_window.action_view_zoom_in());
+ addAction(main_window.action_view_zoom_out());
+ addAction(main_window.action_view_zoom_fit());
+ addAction(main_window.action_view_zoom_one_to_one());
+ addSeparator();
+ addAction(main_window.action_view_show_cursors());
+ addSeparator();
+
+ connect(&run_stop_button_, SIGNAL(clicked()),
+ this, SLOT(on_run_stop()));
+ connect(&sample_count_, SIGNAL(value_changed()),
+ this, SLOT(on_sample_count_changed()));
+ connect(&sample_rate_, SIGNAL(value_changed()),
+ this, SLOT(on_sample_rate_changed()));
+
+ sample_count_.show_min_max_step(0, UINT64_MAX, 1);
+
+ set_capture_state(pv::Session::Stopped);
+
+ configure_button_.setIcon(QIcon::fromTheme("configure",
+ QIcon(":/icons/configure.png")));
+
+ channels_button_.setIcon(QIcon::fromTheme("channels",
+ QIcon(":/icons/channels.svg")));
+
+ run_stop_button_.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+
+ addWidget(&device_selector_);
+ configure_button_action_ = addWidget(&configure_button_);
+ channels_button_action_ = addWidget(&channels_button_);
+ addWidget(&sample_count_);
+ addWidget(&sample_rate_);
+ run_stop_button_action_ = addWidget(&run_stop_button_);
+#ifdef ENABLE_DECODE
+ addSeparator();
+ addWidget(add_decoder_button);
+#endif
+
+ QWidget *const spacer = new QWidget();
+ spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ addWidget(spacer);
+
+ addWidget(&menu_button_);
+
+ sample_count_.installEventFilter(this);
+ sample_rate_.installEventFilter(this);
+}
+
+void MainBar::update_device_list()
+{
+ DeviceManager &mgr = session_.device_manager();
+ shared_ptr<devices::Device> selected_device = session_.device();
+ list< shared_ptr<devices::Device> > devs;
+
+ copy(mgr.devices().begin(), mgr.devices().end(), back_inserter(devs));
+
+ if (std::find(devs.begin(), devs.end(), selected_device) == devs.end())
+ devs.push_back(selected_device);
+
+ device_selector_.set_device_list(devs, selected_device);
+ update_device_config_widgets();
+}
+
+
+void MainBar::set_capture_state(pv::Session::capture_state state)
+{
+ const QIcon *icons[] = {&icon_grey_, &icon_red_, &icon_green_};
+ run_stop_button_.setIcon(*icons[state]);
+ run_stop_button_.setText((state == pv::Session::Stopped) ?
+ tr("Run") : tr("Stop"));
+ run_stop_button_.setShortcut(QKeySequence(Qt::Key_Space));
+
+ bool ui_enabled = (state == pv::Session::Stopped) ? true : false;
+
+ device_selector_.setEnabled(ui_enabled);
+ configure_button_.setEnabled(ui_enabled);
+ channels_button_.setEnabled(ui_enabled);
+ sample_count_.setEnabled(ui_enabled);
+ sample_rate_.setEnabled(ui_enabled);
+}
+
+void MainBar::update_sample_rate_selector()
+{
+ Glib::VariantContainerBase gvar_dict;
+ GVariant *gvar_list;
+ const uint64_t *elements = nullptr;
+ gsize num_elements;
+ map< const ConfigKey*, std::set<Capability> > keys;
+
+ if (updating_sample_rate_) {
+ sample_rate_.show_none();
+ return;
+ }
+
+ const shared_ptr<devices::Device> device =
+ device_selector_.selected_device();
+ if (!device)
+ return;
+
+ assert(!updating_sample_rate_);
+ updating_sample_rate_ = true;
+
+ const shared_ptr<sigrok::Device> sr_dev = device->device();
+
+ if (sr_dev->config_check(ConfigKey::SAMPLERATE, Capability::LIST)) {
+ gvar_dict = sr_dev->config_list(ConfigKey::SAMPLERATE);
+ } else {
+ sample_rate_.show_none();
+ updating_sample_rate_ = false;
+ return;
+ }
+
+ if ((gvar_list = g_variant_lookup_value(gvar_dict.gobj(),
+ "samplerate-steps", G_VARIANT_TYPE("at")))) {
+ elements = (const uint64_t *)g_variant_get_fixed_array(
+ gvar_list, &num_elements, sizeof(uint64_t));
+
+ const uint64_t min = elements[0];
+ const uint64_t max = elements[1];
+ const uint64_t step = elements[2];
+
+ g_variant_unref(gvar_list);
+
+ assert(min > 0);
+ assert(max > 0);
+ assert(max > min);
+ assert(step > 0);
+
+ if (step == 1)
+ sample_rate_.show_125_list(min, max);
+ else {
+ // When the step is not 1, we cam't make a 1-2-5-10
+ // list of sample rates, because we may not be able to
+ // make round numbers. Therefore in this case, show a
+ // spin box.
+ sample_rate_.show_min_max_step(min, max, step);
+ }
+ } else if ((gvar_list = g_variant_lookup_value(gvar_dict.gobj(),
+ "samplerates", G_VARIANT_TYPE("at")))) {
+ elements = (const uint64_t *)g_variant_get_fixed_array(
+ gvar_list, &num_elements, sizeof(uint64_t));
+ sample_rate_.show_list(elements, num_elements);
+ g_variant_unref(gvar_list);
+ }
+ updating_sample_rate_ = false;
+
+ update_sample_rate_selector_value();
+}
+
+void MainBar::update_sample_rate_selector_value()
+{
+ if (updating_sample_rate_)
+ return;
+
+ const shared_ptr<devices::Device> device =
+ device_selector_.selected_device();
+ if (!device)
+ return;
+
+ try {
+ auto gvar = device->device()->config_get(ConfigKey::SAMPLERATE);
+ uint64_t samplerate =
+ Glib::VariantBase::cast_dynamic<Glib::Variant<guint64>>(gvar).get();
+ assert(!updating_sample_rate_);
+ updating_sample_rate_ = true;
+ sample_rate_.set_value(samplerate);
+ updating_sample_rate_ = false;
+ } catch (Error error) {
+ qDebug() << "WARNING: Failed to get value of sample rate";
+ return;
+ }
+}
+
+void MainBar::update_sample_count_selector()
+{
+ if (updating_sample_count_)
+ return;
+
+ const shared_ptr<devices::Device> device =
+ device_selector_.selected_device();
+ if (!device)
+ return;
+
+ const shared_ptr<sigrok::Device> sr_dev = device->device();
+
+ assert(!updating_sample_count_);
+ updating_sample_count_ = true;
+
+ if (!sample_count_supported_) {
+ sample_count_.show_none();
+ updating_sample_count_ = false;
+ return;
+ }
+
+ uint64_t sample_count = sample_count_.value();
+ uint64_t min_sample_count = 0;
+ uint64_t max_sample_count = MaxSampleCount;
+
+ if (sample_count == 0)
+ sample_count = DefaultSampleCount;
+
+ if (sr_dev->config_check(ConfigKey::LIMIT_SAMPLES, Capability::LIST)) {
+ auto gvar = sr_dev->config_list(ConfigKey::LIMIT_SAMPLES);
+ if (gvar.gobj())
+ g_variant_get(gvar.gobj(), "(tt)",
+ &min_sample_count, &max_sample_count);
+ }
+
+ min_sample_count = min(max(min_sample_count, MinSampleCount),
+ max_sample_count);
+
+ sample_count_.show_125_list(
+ min_sample_count, max_sample_count);
+
+ if (sr_dev->config_check(ConfigKey::LIMIT_SAMPLES, Capability::GET)) {
+ auto gvar = sr_dev->config_get(ConfigKey::LIMIT_SAMPLES);
+ sample_count = g_variant_get_uint64(gvar.gobj());
+ if (sample_count == 0)
+ sample_count = DefaultSampleCount;
+ sample_count = min(max(sample_count, MinSampleCount),
+ max_sample_count);
+ }
+
+ sample_count_.set_value(sample_count);
+
+ updating_sample_count_ = false;
+}
+
+void MainBar::update_device_config_widgets()
+{
+ using namespace pv::popups;
+
+ const shared_ptr<devices::Device> device =
+ device_selector_.selected_device();
+
+ // Hide the widgets if no device is selected
+ channels_button_action_->setVisible(!!device);
+ run_stop_button_action_->setVisible(!!device);
+ if (!device) {
+ configure_button_action_->setVisible(false);
+ sample_count_.show_none();
+ sample_rate_.show_none();
+ return;
+ }
+
+ const shared_ptr<sigrok::Device> sr_dev = device->device();
+ if (!sr_dev)
+ return;
+
+ // Update the configure popup
+ DeviceOptions *const opts = new DeviceOptions(sr_dev, this);
+ configure_button_action_->setVisible(
+ !opts->binding().properties().empty());
+ configure_button_.set_popup(opts);
+
+ // Update the channels popup
+ Channels *const channels = new Channels(session_, this);
+ channels_button_.set_popup(channels);
+
+ // Update supported options.
+ sample_count_supported_ = false;
+
+ if (sr_dev->config_check(ConfigKey::LIMIT_SAMPLES, Capability::SET))
+ sample_count_supported_ = true;
+
+ if (sr_dev->config_check(ConfigKey::LIMIT_FRAMES, Capability::SET)) {
+ sr_dev->config_set(ConfigKey::LIMIT_FRAMES,
+ Glib::Variant<guint64>::create(1));
+ on_config_changed();
+ }
+
+ // Add notification of reconfigure events
+ disconnect(this, SLOT(on_config_changed()));
+ connect(&opts->binding(), SIGNAL(config_changed()),
+ this, SLOT(on_config_changed()));
+
+ // Update sweep timing widgets.
+ update_sample_count_selector();
+ update_sample_rate_selector();
+}
+
+void MainBar::commit_sample_count()
+{
+ uint64_t sample_count = 0;
+
+ const shared_ptr<devices::Device> device =
+ device_selector_.selected_device();
+ if (!device)
+ return;
+
+ const shared_ptr<sigrok::Device> sr_dev = device->device();
+
+ sample_count = sample_count_.value();
+ if (sample_count_supported_) {
+ try {
+ sr_dev->config_set(ConfigKey::LIMIT_SAMPLES,
+ Glib::Variant<guint64>::create(sample_count));
+ update_sample_count_selector();
+ } catch (Error error) {
+ qDebug() << "Failed to configure sample count.";
+ return;
+ }
+ }
+
+ // Devices with built-in memory might impose limits on certain
+ // configurations, so let's check what sample rate the driver
+ // lets us use now.
+ update_sample_rate_selector();
+}
+
+void MainBar::commit_sample_rate()
+{
+ uint64_t sample_rate = 0;
+
+ const shared_ptr<devices::Device> device =
+ device_selector_.selected_device();
+ if (!device)
+ return;
+
+ const shared_ptr<sigrok::Device> sr_dev = device->device();
+
+ sample_rate = sample_rate_.value();
+ if (sample_rate == 0)
+ return;
+
+ try {
+ sr_dev->config_set(ConfigKey::SAMPLERATE,
+ Glib::Variant<guint64>::create(sample_rate));
+ update_sample_rate_selector();
+ } catch (Error error) {
+ qDebug() << "Failed to configure samplerate.";
+ return;
+ }
+
+ // Devices with built-in memory might impose limits on certain
+ // configurations, so let's check what sample count the driver
+ // lets us use now.
+ update_sample_count_selector();
+}
+
+void MainBar::on_device_selected()
+{
+ shared_ptr<devices::Device> device = device_selector_.selected_device();
+ if (!device)
+ return;
+
+ main_window_.select_device(device);
+
+ update_device_config_widgets();
+}
+
+void MainBar::on_sample_count_changed()
+{
+ if (!updating_sample_count_)
+ commit_sample_count();
+}
+
+void MainBar::on_sample_rate_changed()
+{
+ if (!updating_sample_rate_)
+ commit_sample_rate();
+}
+
+void MainBar::on_run_stop()
+{
+ commit_sample_count();
+ commit_sample_rate();
+ main_window_.run_stop();
+}
+
+void MainBar::on_config_changed()
+{
+ commit_sample_count();
+ commit_sample_rate();
+}
+
+bool MainBar::eventFilter(QObject *watched, QEvent *event)
+{
+ if (sample_count_supported_ && (watched == &sample_count_ ||
+ watched == &sample_rate_) &&
+ (event->type() == QEvent::ToolTip)) {
+ auto sec = pv::util::Timestamp(sample_count_.value()) / sample_rate_.value();
+ QHelpEvent *help_event = static_cast<QHelpEvent*>(event);
+
+ QString str = tr("Total sampling time: %1").arg(
+ pv::util::format_time_si(sec, pv::util::SIPrefix::unspecified, 0, "s", false));
+ QToolTip::showText(help_event->globalPos(), str);
+
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace toolbars
+} // namespace pv
diff --git a/pv/toolbars/samplingbar.h b/pv/toolbars/mainbar.hpp
similarity index 54%
rename from pv/toolbars/samplingbar.h
rename to pv/toolbars/mainbar.hpp
index e59d2f9..6e2a706 100644
--- a/pv/toolbars/samplingbar.h
+++ b/pv/toolbars/mainbar.hpp
@@ -18,38 +18,42 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_TOOLBARS_SAMPLINGBAR_H
-#define PULSEVIEW_PV_TOOLBARS_SAMPLINGBAR_H
+#ifndef PULSEVIEW_PV_TOOLBARS_MAINBAR_HPP
+#define PULSEVIEW_PV_TOOLBARS_MAINBAR_HPP
#include <stdint.h>
#include <list>
-#include <map>
-
-#include <boost/shared_ptr.hpp>
+#include <memory>
#include <QComboBox>
#include <QDoubleSpinBox>
+#include <QMenu>
#include <QToolBar>
#include <QToolButton>
-#include <pv/sigsession.h>
-#include <pv/widgets/popuptoolbutton.h>
-#include <pv/widgets/sweeptimingwidget.h>
+#include <pv/session.hpp>
+#include <pv/widgets/devicetoolbutton.hpp>
+#include <pv/widgets/popuptoolbutton.hpp>
+#include <pv/widgets/sweeptimingwidget.hpp>
+
+namespace sigrok {
+class Device;
+class InputFormat;
+}
+
+Q_DECLARE_METATYPE(std::shared_ptr<sigrok::Device>)
class QAction;
namespace pv {
-class SigSession;
-
-namespace device {
-class DevInst;
-}
+class MainWindow;
+class Session;
namespace toolbars {
-class SamplingBar : public QToolBar
+class MainBar : public QToolBar
{
Q_OBJECT
@@ -59,19 +63,11 @@ private:
static const uint64_t DefaultSampleCount;
public:
- SamplingBar(SigSession &session, QWidget *parent);
-
- void set_device_list(
- const std::list< boost::shared_ptr<pv::device::DevInst> >
- &devices,
- boost::shared_ptr<pv::device::DevInst> selected);
-
- boost::shared_ptr<pv::device::DevInst> get_selected_device() const;
+ MainBar(Session &session, pv::MainWindow &main_window);
- void set_capture_state(pv::SigSession::capture_state state);
+ void update_device_list();
-signals:
- void run_stop();
+ void set_capture_state(pv::Session::capture_state state);
private:
void update_sample_rate_selector();
@@ -81,7 +77,7 @@ private:
void commit_sample_rate();
void commit_sample_count();
-private slots:
+private Q_SLOTS:
void on_device_selected();
void on_sample_count_changed();
void on_sample_rate_changed();
@@ -89,33 +85,38 @@ private slots:
void on_config_changed();
+protected:
+ bool eventFilter(QObject *watched, QEvent *event);
+
private:
- SigSession &_session;
+ Session &session_;
+ MainWindow &main_window_;
+
+ pv::widgets::DeviceToolButton device_selector_;
- QComboBox _device_selector;
- std::map<const sr_dev_inst*, boost::weak_ptr<device::DevInst> >
- _device_selector_map;
- bool _updating_device_selector;
+ pv::widgets::PopupToolButton configure_button_;
+ QAction *configure_button_action_;
- pv::widgets::PopupToolButton _configure_button;
- QAction *_configure_button_action;
+ pv::widgets::PopupToolButton channels_button_;
+ QAction *channels_button_action_;
- pv::widgets::PopupToolButton _probes_button;
+ pv::widgets::SweepTimingWidget sample_count_;
+ pv::widgets::SweepTimingWidget sample_rate_;
+ bool updating_sample_rate_;
+ bool updating_sample_count_;
- pv::widgets::SweepTimingWidget _sample_count;
- pv::widgets::SweepTimingWidget _sample_rate;
- bool _updating_sample_rate;
- bool _updating_sample_count;
+ bool sample_count_supported_;
- bool _sample_count_supported;
+ QIcon icon_red_;
+ QIcon icon_green_;
+ QIcon icon_grey_;
+ QToolButton run_stop_button_;
+ QAction *run_stop_button_action_;
- QIcon _icon_red;
- QIcon _icon_green;
- QIcon _icon_grey;
- QToolButton _run_stop_button;
+ QToolButton menu_button_;
};
} // namespace toolbars
} // namespace pv
-#endif // PULSEVIEW_PV_TOOLBARS_SAMPLINGBAR_H
+#endif // PULSEVIEW_PV_TOOLBARS_MAINBAR_HPP
diff --git a/pv/toolbars/samplingbar.cpp b/pv/toolbars/samplingbar.cpp
deleted file mode 100644
index 91c4b07..0000000
--- a/pv/toolbars/samplingbar.cpp
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <extdef.h>
-
-#include <assert.h>
-
-#include <boost/foreach.hpp>
-
-#include <QAction>
-#include <QDebug>
-
-#include "samplingbar.h"
-
-#include <pv/devicemanager.h>
-#include <pv/device/devinst.h>
-#include <pv/popups/deviceoptions.h>
-#include <pv/popups/probes.h>
-
-using boost::shared_ptr;
-using std::map;
-using std::max;
-using std::min;
-using std::string;
-
-namespace pv {
-namespace toolbars {
-
-const uint64_t SamplingBar::MinSampleCount = 100ULL;
-const uint64_t SamplingBar::MaxSampleCount = 1000000000000ULL;
-const uint64_t SamplingBar::DefaultSampleCount = 1000000;
-
-SamplingBar::SamplingBar(SigSession &session, QWidget *parent) :
- QToolBar("Sampling Bar", parent),
- _session(session),
- _device_selector(this),
- _updating_device_selector(false),
- _configure_button(this),
- _configure_button_action(NULL),
- _probes_button(this),
- _sample_count(" samples", this),
- _sample_rate("Hz", this),
- _updating_sample_rate(false),
- _updating_sample_count(false),
- _sample_count_supported(false),
- _icon_red(":/icons/status-red.svg"),
- _icon_green(":/icons/status-green.svg"),
- _icon_grey(":/icons/status-grey.svg"),
- _run_stop_button(this)
-{
- connect(&_run_stop_button, SIGNAL(clicked()),
- this, SLOT(on_run_stop()));
- connect(&_device_selector, SIGNAL(currentIndexChanged (int)),
- this, SLOT(on_device_selected()));
- connect(&_sample_count, SIGNAL(value_changed()),
- this, SLOT(on_sample_count_changed()));
- connect(&_sample_rate, SIGNAL(value_changed()),
- this, SLOT(on_sample_rate_changed()));
-
- _sample_count.show_min_max_step(0, UINT64_MAX, 1);
-
- set_capture_state(pv::SigSession::Stopped);
-
- _configure_button.setIcon(QIcon::fromTheme("configure",
- QIcon(":/icons/configure.png")));
-
- _probes_button.setIcon(QIcon::fromTheme("probes",
- QIcon(":/icons/probes.svg")));
-
- _run_stop_button.setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
-
- addWidget(&_device_selector);
- _configure_button_action = addWidget(&_configure_button);
- addWidget(&_probes_button);
- addWidget(&_sample_count);
- addWidget(&_sample_rate);
-
- addWidget(&_run_stop_button);
-}
-
-void SamplingBar::set_device_list(
- const std::list< shared_ptr<pv::device::DevInst> > &devices,
- shared_ptr<pv::device::DevInst> selected)
-{
- int selected_index = -1;
-
- assert(selected);
-
- _updating_device_selector = true;
-
- _device_selector.clear();
- _device_selector_map.clear();
-
- BOOST_FOREACH (shared_ptr<pv::device::DevInst> dev_inst, devices) {
- assert(dev_inst);
- const string title = dev_inst->format_device_title();
- const sr_dev_inst *sdi = dev_inst->dev_inst();
- assert(sdi);
-
- if (selected == dev_inst)
- selected_index = _device_selector.count();
-
- _device_selector_map[sdi] = dev_inst;
- _device_selector.addItem(title.c_str(),
- qVariantFromValue((void*)sdi));
- }
-
- // The selected device should have been in the list
- assert(selected_index != -1);
- _device_selector.setCurrentIndex(selected_index);
-
- update_device_config_widgets();
-
- _updating_device_selector = false;
-}
-
-shared_ptr<pv::device::DevInst> SamplingBar::get_selected_device() const
-{
- const int index = _device_selector.currentIndex();
- if (index < 0)
- return shared_ptr<pv::device::DevInst>();
-
- const sr_dev_inst *const sdi =
- (const sr_dev_inst*)_device_selector.itemData(
- index).value<void*>();
- assert(sdi);
-
- map<const sr_dev_inst*, boost::weak_ptr<device::DevInst> >::
- const_iterator iter = _device_selector_map.find(sdi);
- if (iter == _device_selector_map.end())
- return shared_ptr<pv::device::DevInst>();
-
- return shared_ptr<pv::device::DevInst>((*iter).second);
-}
-
-void SamplingBar::set_capture_state(pv::SigSession::capture_state state)
-{
- const QIcon *icons[] = {&_icon_grey, &_icon_red, &_icon_green};
- _run_stop_button.setIcon(*icons[state]);
- _run_stop_button.setText((state == pv::SigSession::Stopped) ?
- tr("Run") : tr("Stop"));
-}
-
-void SamplingBar::update_sample_rate_selector()
-{
- GVariant *gvar_dict, *gvar_list;
- const uint64_t *elements = NULL;
- gsize num_elements;
-
- if (_updating_sample_rate)
- return;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- assert(!_updating_sample_rate);
- _updating_sample_rate = true;
-
- if (!(gvar_dict = dev_inst->list_config(NULL, SR_CONF_SAMPLERATE)))
- {
- _sample_rate.show_none();
- _updating_sample_rate = false;
- return;
- }
-
- if ((gvar_list = g_variant_lookup_value(gvar_dict,
- "samplerate-steps", G_VARIANT_TYPE("at"))))
- {
- elements = (const uint64_t *)g_variant_get_fixed_array(
- gvar_list, &num_elements, sizeof(uint64_t));
-
- const uint64_t min = elements[0];
- const uint64_t max = elements[1];
- const uint64_t step = elements[2];
-
- g_variant_unref(gvar_list);
-
- assert(min > 0);
- assert(max > 0);
- assert(max > min);
- assert(step > 0);
-
- if (step == 1)
- _sample_rate.show_125_list(min, max);
- else
- {
- // When the step is not 1, we cam't make a 1-2-5-10
- // list of sample rates, because we may not be able to
- // make round numbers. Therefore in this case, show a
- // spin box.
- _sample_rate.show_min_max_step(min, max, step);
- }
- }
- else if ((gvar_list = g_variant_lookup_value(gvar_dict,
- "samplerates", G_VARIANT_TYPE("at"))))
- {
- elements = (const uint64_t *)g_variant_get_fixed_array(
- gvar_list, &num_elements, sizeof(uint64_t));
- _sample_rate.show_list(elements, num_elements);
- g_variant_unref(gvar_list);
- }
- _updating_sample_rate = false;
-
- g_variant_unref(gvar_dict);
- update_sample_rate_selector_value();
-}
-
-void SamplingBar::update_sample_rate_selector_value()
-{
- GVariant *gvar;
- uint64_t samplerate;
-
- if (_updating_sample_rate)
- return;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- if (!(gvar = dev_inst->get_config(NULL, SR_CONF_SAMPLERATE))) {
- qDebug() << "WARNING: Failed to get value of sample rate";
- return;
- }
- samplerate = g_variant_get_uint64(gvar);
- g_variant_unref(gvar);
-
- assert(!_updating_sample_rate);
- _updating_sample_rate = true;
- _sample_rate.set_value(samplerate);
- _updating_sample_rate = false;
-}
-
-void SamplingBar::update_sample_count_selector()
-{
- GVariant *gvar;
-
- if (_updating_sample_count)
- return;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- assert(!_updating_sample_count);
- _updating_sample_count = true;
-
- if (_sample_count_supported)
- {
- uint64_t sample_count = _sample_count.value();
- uint64_t min_sample_count = 0;
- uint64_t max_sample_count = MaxSampleCount;
-
- if (sample_count == 0)
- sample_count = DefaultSampleCount;
-
- if ((gvar = dev_inst->list_config(NULL, SR_CONF_LIMIT_SAMPLES)))
- {
- g_variant_get(gvar, "(tt)",
- &min_sample_count, &max_sample_count);
- g_variant_unref(gvar);
- }
-
- min_sample_count = min(max(min_sample_count, MinSampleCount),
- max_sample_count);
-
- _sample_count.show_125_list(
- min_sample_count, max_sample_count);
-
- if ((gvar = dev_inst->get_config(NULL, SR_CONF_LIMIT_SAMPLES)))
- {
- sample_count = g_variant_get_uint64(gvar);
- if (sample_count == 0)
- sample_count = DefaultSampleCount;
- sample_count = min(max(sample_count, MinSampleCount),
- max_sample_count);
-
- g_variant_unref(gvar);
- }
-
- _sample_count.set_value(sample_count);
- }
- else
- _sample_count.show_none();
-
- _updating_sample_count = false;
-}
-
-void SamplingBar::update_device_config_widgets()
-{
- GVariant *gvar;
-
- using namespace pv::popups;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- // Update the configure popup
- DeviceOptions *const opts = new DeviceOptions(dev_inst, this);
- _configure_button_action->setVisible(
- !opts->binding().properties().empty());
- _configure_button.set_popup(opts);
-
- // Update the probes popup
- Probes *const probes = new Probes(_session, this);
- _probes_button.set_popup(probes);
-
- // Update supported options.
- _sample_count_supported = false;
-
- if ((gvar = dev_inst->list_config(NULL, SR_CONF_DEVICE_OPTIONS)))
- {
- gsize num_opts;
- const int *const options =
- (const int32_t *)g_variant_get_fixed_array(
- gvar, &num_opts, sizeof(int32_t));
- for (unsigned int i = 0; i < num_opts; i++)
- {
- switch (options[i]) {
- case SR_CONF_LIMIT_SAMPLES:
- _sample_count_supported = true;
- break;
- case SR_CONF_LIMIT_FRAMES:
- dev_inst->set_config(NULL, SR_CONF_LIMIT_FRAMES,
- g_variant_new_uint64(1));
- break;
- }
- }
- }
-
- // Add notification of reconfigure events
- disconnect(this, SLOT(on_config_changed()));
- connect(dev_inst.get(), SIGNAL(config_changed()),
- this, SLOT(on_config_changed()));
-
- // Update sweep timing widgets.
- update_sample_count_selector();
- update_sample_rate_selector();
-}
-
-void SamplingBar::commit_sample_count()
-{
- uint64_t sample_count = 0;
-
- if (_updating_sample_count)
- return;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- sample_count = _sample_count.value();
-
- // Set the sample count
- assert(!_updating_sample_count);
- _updating_sample_count = true;
- if (_sample_count_supported &&
- !dev_inst->set_config(NULL, SR_CONF_LIMIT_SAMPLES,
- g_variant_new_uint64(sample_count))) {
- qDebug() << "Failed to configure sample count.";
- return;
- }
- _updating_sample_count = false;
-}
-
-void SamplingBar::commit_sample_rate()
-{
- uint64_t sample_rate = 0;
-
- if (_updating_sample_rate)
- return;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- sample_rate = _sample_rate.value();
- if (sample_rate == 0)
- return;
-
- // Set the samplerate
- assert(!_updating_sample_rate);
- _updating_sample_rate = true;
- if (!dev_inst->set_config(NULL, SR_CONF_SAMPLERATE,
- g_variant_new_uint64(sample_rate))) {
- qDebug() << "Failed to configure samplerate.";
- return;
- }
- _updating_sample_rate = false;
-}
-
-void SamplingBar::on_device_selected()
-{
- if (_updating_device_selector)
- return;
-
- const shared_ptr<device::DevInst> dev_inst = get_selected_device();
- if (!dev_inst)
- return;
-
- _session.set_device(dev_inst);
-
- update_device_config_widgets();
-}
-
-void SamplingBar::on_sample_count_changed()
-{
- commit_sample_count();
-}
-
-void SamplingBar::on_sample_rate_changed()
-{
- commit_sample_rate();
-}
-
-void SamplingBar::on_run_stop()
-{
- commit_sample_count();
- commit_sample_rate();
- run_stop();
-}
-
-void SamplingBar::on_config_changed()
-{
- commit_sample_count();
- update_sample_count_selector();
- commit_sample_rate();
- update_sample_rate_selector();
-}
-
-} // namespace toolbars
-} // namespace pv
diff --git a/pv/util.cpp b/pv/util.cpp
new file mode 100644
index 0000000..f7ee772
--- /dev/null
+++ b/pv/util.cpp
@@ -0,0 +1,239 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "util.hpp"
+
+#include <extdef.h>
+
+#include <assert.h>
+
+#include <algorithm>
+#include <sstream>
+
+#include <QTextStream>
+#include <QDebug>
+
+using namespace Qt;
+
+namespace pv {
+namespace util {
+
+static QTextStream& operator<<(QTextStream& stream, SIPrefix prefix)
+{
+ switch (prefix) {
+ case SIPrefix::yocto: return stream << 'y';
+ case SIPrefix::zepto: return stream << 'z';
+ case SIPrefix::atto: return stream << 'a';
+ case SIPrefix::femto: return stream << 'f';
+ case SIPrefix::pico: return stream << 'p';
+ case SIPrefix::nano: return stream << 'n';
+ case SIPrefix::micro: return stream << QChar(0x03BC);
+ case SIPrefix::milli: return stream << 'm';
+ case SIPrefix::kilo: return stream << 'k';
+ case SIPrefix::mega: return stream << 'M';
+ case SIPrefix::giga: return stream << 'G';
+ case SIPrefix::tera: return stream << 'T';
+ case SIPrefix::peta: return stream << 'P';
+ case SIPrefix::exa: return stream << 'E';
+ case SIPrefix::zetta: return stream << 'Z';
+ case SIPrefix::yotta: return stream << 'Y';
+
+ default: return stream;
+ }
+}
+
+int exponent(SIPrefix prefix)
+{
+ return 3 * (static_cast<int>(prefix) - static_cast<int>(SIPrefix::none));
+}
+
+static SIPrefix successor(SIPrefix prefix)
+{
+ assert(prefix != SIPrefix::yotta);
+ return static_cast<SIPrefix>(static_cast<int>(prefix) + 1);
+}
+
+// Insert the timestamp value into the stream in fixed-point notation
+// (and honor the precision)
+static QTextStream& operator<<(QTextStream& stream, const Timestamp& t)
+{
+ // The multiprecision types already have a function and a stream insertion
+ // operator to convert them to a string, however these functions abuse a
+ // precision value of zero to print all available decimal places instead of
+ // none, and the boost authors refuse to fix this because they don't want
+ // to break buggy code that relies on this bug.
+ // (https://svn.boost.org/trac/boost/ticket/10103)
+ // Therefore we have to work around the case where precision is zero.
+
+ int precision = stream.realNumberPrecision();
+
+ std::ostringstream ss;
+ ss << std::fixed;
+
+ if (stream.numberFlags() & QTextStream::ForceSign)
+ ss << std::showpos;
+
+ if (0 == precision)
+ ss << std::setprecision(1) << round(t);
+ else
+ ss << std::setprecision(precision) << t;
+
+ std::string str(ss.str());
+ if (0 == precision) {
+ // remove the separator and the unwanted decimal place
+ str.resize(str.size() - 2);
+ }
+
+ return stream << QString::fromStdString(str);
+}
+
+QString format_time_si(
+ const Timestamp& v,
+ SIPrefix prefix,
+ unsigned int precision,
+ QString unit,
+ bool sign)
+{
+ if (prefix == SIPrefix::unspecified) {
+ // No prefix given, calculate it
+
+ if (v.is_zero()) {
+ prefix = SIPrefix::none;
+ } else {
+ int exp = exponent(SIPrefix::yotta);
+ prefix = SIPrefix::yocto;
+ while ((fabs(v) * pow(Timestamp(10), exp)) > 999 &&
+ prefix < SIPrefix::yotta) {
+ prefix = successor(prefix);
+ exp -= 3;
+ }
+ }
+ }
+
+ assert(prefix >= SIPrefix::yocto);
+ assert(prefix <= SIPrefix::yotta);
+
+ const Timestamp multiplier = pow(Timestamp(10), -exponent(prefix));
+
+ QString s;
+ QTextStream ts(&s);
+ if (sign && !v.is_zero())
+ ts << forcesign;
+ ts
+ << qSetRealNumberPrecision(precision)
+ << (v * multiplier)
+ << ' '
+ << prefix
+ << unit;
+
+ return s;
+}
+
+QString format_time_si_adjusted(
+ const Timestamp& t,
+ SIPrefix prefix,
+ unsigned precision,
+ QString unit,
+ bool sign)
+{
+ // The precision is always given without taking the prefix into account
+ // so we need to deduct the number of decimals the prefix might imply
+ const int prefix_order = -exponent(prefix);
+
+ const unsigned int relative_prec =
+ (prefix >= SIPrefix::none) ? precision :
+ std::max((int)(precision - prefix_order), 0);
+
+ return format_time_si(t, prefix, relative_prec, unit, sign);
+}
+
+
+// Helper for 'format_time_minutes()'.
+static QString pad_number(unsigned int number, int length)
+{
+ return QString("%1").arg(number, length, 10, QChar('0'));
+}
+
+QString format_time_minutes(const Timestamp& t, signed precision, bool sign)
+{
+ const Timestamp whole_seconds = floor(abs(t));
+ const Timestamp days = floor(whole_seconds / (60 * 60 * 24));
+ const unsigned int hours = fmod(whole_seconds / (60 * 60), 24).convert_to<uint>();
+ const unsigned int minutes = fmod(whole_seconds / 60, 60).convert_to<uint>();
+ const unsigned int seconds = fmod(whole_seconds, 60).convert_to<uint>();
+
+ QString s;
+ QTextStream ts(&s);
+
+ if (t < 0)
+ ts << "-";
+ else if (sign)
+ ts << "+";
+
+ bool use_padding = false;
+
+ // DD
+ if (days) {
+ ts << days.str().c_str() << ":";
+ use_padding = true;
+ }
+
+ // HH
+ if (hours || days) {
+ ts << pad_number(hours, use_padding ? 2 : 0) << ":";
+ use_padding = true;
+ }
+
+ // MM
+ ts << pad_number(minutes, use_padding ? 2 : 0);
+
+ ts << ":";
+
+ // SS
+ ts << pad_number(seconds, 2);
+
+ if (precision) {
+ ts << ".";
+
+ const Timestamp fraction = fabs(t) - whole_seconds;
+
+ std::ostringstream ss;
+ ss
+ << std::fixed
+ << std::setprecision(precision)
+ << std::setfill('0')
+ << fraction;
+ std::string fs = ss.str();
+
+ // Copy all digits, inserting spaces as unit separators
+ for (int i = 1; i <= precision; i++) {
+ // Start at index 2 to skip the "0." at the beginning
+ ts << fs.at(1 + i);
+
+ if ((i > 0) && (i % 3 == 0) && (i != precision))
+ ts << " ";
+ }
+ }
+
+ return s;
+}
+
+} // namespace util
+} // namespace pv
diff --git a/pv/util.hpp b/pv/util.hpp
new file mode 100644
index 0000000..71177cc
--- /dev/null
+++ b/pv/util.hpp
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_UTIL_HPP
+#define PULSEVIEW_UTIL_HPP
+
+#include <cmath>
+
+#ifndef Q_MOC_RUN
+#include <boost/multiprecision/cpp_dec_float.hpp>
+#endif
+
+#include <QMetaType>
+#include <QString>
+
+namespace pv {
+namespace util {
+
+enum class TimeUnit {
+ Time = 1,
+ Samples = 2
+};
+
+enum class SIPrefix {
+ unspecified = -1,
+ yocto, zepto,
+ atto, femto, pico,
+ nano, micro, milli,
+ none,
+ kilo, mega, giga,
+ tera, peta, exa,
+ zetta, yotta
+};
+
+/// Returns the exponent that corresponds to a given prefix.
+int exponent(SIPrefix prefix);
+
+/// Timestamp type providing yoctosecond resolution.
+typedef boost::multiprecision::number<
+ boost::multiprecision::cpp_dec_float<24>,
+ boost::multiprecision::et_off> Timestamp;
+
+/**
+ * Formats a given timestamp with the specified SI prefix.
+ *
+ * If 'prefix' is left 'unspecified', the function chooses a prefix so that
+ * the value in front of the decimal point is between 1 and 999.
+ *
+ * The default value "s" for the unit argument makes the most sense when
+ * formatting time values, but a different value can be given if the function
+ * is reused to format a value of another quantity.
+ *
+ * @param t The value to format.
+ * @param prefix The SI prefix to use.
+ * @param precision The number of digits after the decimal separator.
+ * @param unit The unit of quantity.
+ * @param sign Whether or not to add a sign also for positive numbers.
+ *
+ * @return The formatted value.
+ */
+QString format_time_si(
+ const Timestamp& t,
+ SIPrefix prefix = SIPrefix::unspecified,
+ unsigned precision = 0,
+ QString unit = "s",
+ bool sign = true);
+
+/**
+ * Wrapper around 'format_time_si()' that interprets the given 'precision'
+ * value as the number of decimal places if the timestamp would be formatted
+ * without a SI prefix (using 'SIPrefix::none') and adjusts the precision to
+ * match the given 'prefix'
+ *
+ * @param t The value to format.
+ * @param prefix The SI prefix to use.
+ * @param precision The number of digits after the decimal separator if the
+ * 'prefix' would be 'SIPrefix::none', see above for more information.
+ * @param unit The unit of quantity.
+ * @param sign Whether or not to add a sign also for positive numbers.
+ *
+ * @return The formatted value.
+ */
+QString format_time_si_adjusted(
+ const Timestamp& t,
+ SIPrefix prefix,
+ unsigned precision = 0,
+ QString unit = "s",
+ bool sign = true);
+
+/**
+ * Formats the given timestamp using "[+-]DD:HH:MM:SS.mmm uuu nnn ppp..." format.
+ *
+ * "DD" and "HH" are left out if they would be "00" (but if "DD" is generated,
+ * "HH" is also always generated. The "MM:SS" part is always produced, the
+ * number of subsecond digits can be influenced using the 'precision' parameter.
+ *
+ * @param t The value to format.
+ * @param precision The number of digits after the decimal separator.
+ * @param sign Whether or not to add a sign also for positive numbers.
+ *
+ * @return The formatted value.
+ */
+QString format_time_minutes(
+ const Timestamp& t,
+ signed precision = 0,
+ bool sign = true);
+
+} // namespace util
+} // namespace pv
+
+Q_DECLARE_METATYPE(pv::util::Timestamp)
+
+#endif // PULSEVIEW_UTIL_HPP
diff --git a/pv/view/analogsignal.cpp b/pv/view/analogsignal.cpp
index c3f2738..2597a8f 100644
--- a/pv/view/analogsignal.cpp
+++ b/pv/view/analogsignal.cpp
@@ -20,21 +20,31 @@
#include <extdef.h>
-#include <math.h>
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
+#include <limits>
-#include "analogsignal.h"
-#include "pv/data/analog.h"
-#include "pv/data/analogsnapshot.h"
-#include "pv/view/view.h"
+#include "analogsignal.hpp"
+#include "pv/data/analog.hpp"
+#include "pv/data/analogsegment.hpp"
+#include "pv/view/view.hpp"
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
-using boost::shared_ptr;
using std::max;
+using std::make_pair;
using std::min;
+using std::shared_ptr;
using std::deque;
+using sigrok::Channel;
+
namespace pv {
namespace view {
+const int AnalogSignal::NominalHeight = 80;
+
const QColor AnalogSignal::SignalColours[4] = {
QColor(0xC4, 0xA0, 0x00), // Yellow
QColor(0x87, 0x20, 0x7A), // Magenta
@@ -44,13 +54,16 @@ const QColor AnalogSignal::SignalColours[4] = {
const float AnalogSignal::EnvelopeThreshold = 256.0f;
-AnalogSignal::AnalogSignal(shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel *const probe, shared_ptr<data::Analog> data) :
- Signal(dev_inst, probe),
- _data(data),
- _scale(1.0f)
+AnalogSignal::AnalogSignal(
+ pv::Session &session,
+ shared_ptr<Channel> channel,
+ shared_ptr<data::Analog> data) :
+ Signal(session, channel),
+ data_(data),
+ scale_index_(0),
+ scale_index_drag_offset_(0)
{
- _colour = SignalColours[probe->index % countof(SignalColours)];
+ set_colour(SignalColours[channel_->index() % countof(SignalColours)]);
}
AnalogSignal::~AnalogSignal()
@@ -59,83 +72,98 @@ AnalogSignal::~AnalogSignal()
shared_ptr<pv::data::SignalData> AnalogSignal::data() const
{
- return _data;
+ return data_;
}
shared_ptr<pv::data::Analog> AnalogSignal::analog_data() const
{
- return _data;
+ return data_;
}
-void AnalogSignal::set_scale(float scale)
+std::pair<int, int> AnalogSignal::v_extents() const
{
- _scale = scale;
+ const int h = NominalHeight / 2;
+ return make_pair(-h, h);
}
-void AnalogSignal::paint_back(QPainter &p, int left, int right)
+int AnalogSignal::scale_handle_offset() const
{
- if (_probe->enabled)
- paint_axis(p, get_y(), left, right);
+ return ((scale_index_drag_offset_ - scale_index_) *
+ NominalHeight / 4) - NominalHeight / 2;
}
-void AnalogSignal::paint_mid(QPainter &p, int left, int right)
+void AnalogSignal::scale_handle_dragged(int offset)
{
- assert(_data);
- assert(right >= left);
+ scale_index_ = scale_index_drag_offset_ -
+ (offset + NominalHeight / 2) / (NominalHeight / 4);
+}
- assert(_view);
- const int y = _v_offset - _view->v_offset();
+void AnalogSignal::scale_handle_drag_release()
+{
+ scale_index_drag_offset_ = scale_index_;
+}
- const double scale = _view->scale();
- assert(scale > 0);
+void AnalogSignal::paint_back(QPainter &p, const ViewItemPaintParams &pp)
+{
+ if (channel_->enabled()) {
+ Trace::paint_back(p, pp);
+ paint_axis(p, pp, get_visual_y());
+ }
+}
+
+void AnalogSignal::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
+{
+ assert(data_);
+ assert(owner_);
- const double offset = _view->offset();
+ const int y = get_visual_y();
- if (!_probe->enabled)
+ if (!channel_->enabled())
return;
- const deque< shared_ptr<pv::data::AnalogSnapshot> > &snapshots =
- _data->get_snapshots();
- if (snapshots.empty())
+ const deque< shared_ptr<pv::data::AnalogSegment> > &segments =
+ data_->analog_segments();
+ if (segments.empty())
return;
- const shared_ptr<pv::data::AnalogSnapshot> &snapshot =
- snapshots.front();
+ const shared_ptr<pv::data::AnalogSegment> &segment =
+ segments.front();
- const double pixels_offset = offset / scale;
- const double samplerate = _data->samplerate();
- const double start_time = _data->get_start_time();
- const int64_t last_sample = snapshot->get_sample_count() - 1;
- const double samples_per_pixel = samplerate * scale;
- const double start = samplerate * (offset - start_time);
- const double end = start + samples_per_pixel * (right - left);
+ const double pixels_offset = pp.pixels_offset();
+ const double samplerate = max(1.0, segment->samplerate());
+ const pv::util::Timestamp& start_time = segment->start_time();
+ const int64_t last_sample = segment->get_sample_count() - 1;
+ const double samples_per_pixel = samplerate * pp.scale();
+ const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
+ const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
- const int64_t start_sample = min(max((int64_t)floor(start),
+ const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
(int64_t)0), last_sample);
- const int64_t end_sample = min(max((int64_t)ceil(end) + 1,
+ const int64_t end_sample = min(max((ceil(end) + 1).convert_to<int64_t>(),
(int64_t)0), last_sample);
if (samples_per_pixel < EnvelopeThreshold)
- paint_trace(p, snapshot, y, left,
+ paint_trace(p, segment, y, pp.left(),
start_sample, end_sample,
pixels_offset, samples_per_pixel);
else
- paint_envelope(p, snapshot, y, left,
+ paint_envelope(p, segment, y, pp.left(),
start_sample, end_sample,
pixels_offset, samples_per_pixel);
}
void AnalogSignal::paint_trace(QPainter &p,
- const shared_ptr<pv::data::AnalogSnapshot> &snapshot,
+ const shared_ptr<pv::data::AnalogSegment> &segment,
int y, int left, const int64_t start, const int64_t end,
const double pixels_offset, const double samples_per_pixel)
{
+ const float scale = this->scale();
const int64_t sample_count = end - start;
- const float *const samples = snapshot->get_samples(start, end);
+ const float *const samples = segment->get_samples(start, end);
assert(samples);
- p.setPen(_colour);
+ p.setPen(colour_);
QPointF *points = new QPointF[sample_count];
QPointF *point = points;
@@ -144,7 +172,7 @@ void AnalogSignal::paint_trace(QPainter &p,
const float x = (sample / samples_per_pixel -
pixels_offset) + left;
*point++ = QPointF(x,
- y - samples[sample - start] * _scale);
+ y - samples[sample - start] * scale);
}
p.drawPolyline(points, point - points);
@@ -154,39 +182,41 @@ void AnalogSignal::paint_trace(QPainter &p,
}
void AnalogSignal::paint_envelope(QPainter &p,
- const shared_ptr<pv::data::AnalogSnapshot> &snapshot,
+ const shared_ptr<pv::data::AnalogSegment> &segment,
int y, int left, const int64_t start, const int64_t end,
const double pixels_offset, const double samples_per_pixel)
{
- using pv::data::AnalogSnapshot;
+ using pv::data::AnalogSegment;
+
+ const float scale = this->scale();
- AnalogSnapshot::EnvelopeSection e;
- snapshot->get_envelope_section(e, start, end, samples_per_pixel);
+ AnalogSegment::EnvelopeSection e;
+ segment->get_envelope_section(e, start, end, samples_per_pixel);
if (e.length < 2)
return;
p.setPen(QPen(Qt::NoPen));
- p.setBrush(_colour);
+ p.setBrush(colour_);
QRectF *const rects = new QRectF[e.length];
QRectF *rect = rects;
- for(uint64_t sample = 0; sample < e.length-1; sample++) {
+ for (uint64_t sample = 0; sample < e.length-1; sample++) {
const float x = ((e.scale * sample + e.start) /
samples_per_pixel - pixels_offset) + left;
- const AnalogSnapshot::EnvelopeSample *const s =
+ const AnalogSegment::EnvelopeSample *const s =
e.samples + sample;
// We overlap this sample with the next so that vertical
// gaps do not appear during steep rising or falling edges
- const float b = y - max(s->max, (s+1)->min) * _scale;
- const float t = y - min(s->min, (s+1)->max) * _scale;
+ const float b = y - max(s->max, (s+1)->min) * scale;
+ const float t = y - min(s->min, (s+1)->max) * scale;
float h = b - t;
- if(h >= 0.0f && h <= 1.0f)
+ if (h >= 0.0f && h <= 1.0f)
h = 1.0f;
- if(h <= 0.0f && h >= -1.0f)
+ if (h <= 0.0f && h >= -1.0f)
h = -1.0f;
*rect++ = QRectF(x, t, 1.0f, h);
@@ -198,5 +228,15 @@ void AnalogSignal::paint_envelope(QPainter &p,
delete[] e.samples;
}
+float AnalogSignal::scale() const
+{
+ const float seq[] = {1.0f, 2.0f, 5.0f};
+ const int offset = std::numeric_limits<int>::max() / (2 * countof(seq));
+ const std::div_t d = std::div(
+ (int)(scale_index_ + countof(seq) * offset),
+ countof(seq));
+ return powf(10.0f, d.quot - offset) * seq[d.rem];
+}
+
} // namespace view
} // namespace pv
diff --git a/pv/view/analogsignal.h b/pv/view/analogsignal.hpp
similarity index 51%
rename from pv/view/analogsignal.h
rename to pv/view/analogsignal.hpp
index f22d128..938d92d 100644
--- a/pv/view/analogsignal.h
+++ b/pv/view/analogsignal.hpp
@@ -18,18 +18,18 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_ANALOGSIGNAL_H
-#define PULSEVIEW_PV_VIEW_ANALOGSIGNAL_H
+#ifndef PULSEVIEW_PV_VIEW_ANALOGSIGNAL_HPP
+#define PULSEVIEW_PV_VIEW_ANALOGSIGNAL_HPP
-#include "signal.h"
+#include "signal.hpp"
-#include <boost/shared_ptr.hpp>
+#include <memory>
namespace pv {
namespace data {
class Analog;
-class AnalogSnapshot;
+class AnalogSegment;
}
namespace view {
@@ -37,56 +37,82 @@ namespace view {
class AnalogSignal : public Signal
{
private:
+ static const int NominalHeight;
static const QColor SignalColours[4];
static const float EnvelopeThreshold;
public:
- AnalogSignal(boost::shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel *const probe,
- boost::shared_ptr<pv::data::Analog> data);
+ AnalogSignal(pv::Session &session,
+ std::shared_ptr<sigrok::Channel> channel,
+ std::shared_ptr<pv::data::Analog> data);
virtual ~AnalogSignal();
- boost::shared_ptr<pv::data::SignalData> data() const;
+ std::shared_ptr<pv::data::SignalData> data() const;
- boost::shared_ptr<pv::data::Analog> analog_data() const;
+ std::shared_ptr<pv::data::Analog> analog_data() const;
- void set_scale(float scale);
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ std::pair<int, int> v_extents() const;
+
+ /**
+ * Returns the offset to show the drag handle.
+ */
+ int scale_handle_offset() const;
+
+ /**
+ * Handles the scale handle being dragged to an offset.
+ * @param offset the offset the scale handle was dragged to.
+ */
+ void scale_handle_dragged(int offset);
+
+ /**
+ * @copydoc pv::view::Signal::signal_scale_handle_drag_release()
+ */
+ void scale_handle_drag_release();
/**
* Paints the background layer of the signal with a QPainter
* @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal.
- * @param right the x-coordinate of the right edge of the signal.
- **/
- void paint_back(QPainter &p, int left, int right);
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_back(QPainter &p, const ViewItemPaintParams &pp);
/**
* Paints the mid-layer of the signal with a QPainter
* @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal.
- * @param right the x-coordinate of the right edge of the signal.
- **/
- void paint_mid(QPainter &p, int left, int right);
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_mid(QPainter &p, const ViewItemPaintParams &pp);
private:
void paint_trace(QPainter &p,
- const boost::shared_ptr<pv::data::AnalogSnapshot> &snapshot,
+ const std::shared_ptr<pv::data::AnalogSegment> &segment,
int y, int left, const int64_t start, const int64_t end,
const double pixels_offset, const double samples_per_pixel);
void paint_envelope(QPainter &p,
- const boost::shared_ptr<pv::data::AnalogSnapshot> &snapshot,
+ const std::shared_ptr<pv::data::AnalogSegment> &segment,
int y, int left, const int64_t start, const int64_t end,
const double pixels_offset, const double samples_per_pixel);
+ /**
+ * Computes the scale factor from the scale index.
+ */
+ float scale() const;
+
private:
- boost::shared_ptr<pv::data::Analog> _data;
- float _scale;
+ std::shared_ptr<pv::data::Analog> data_;
+
+ int scale_index_;
+ int scale_index_drag_offset_;
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_ANALOGSIGNAL_H
+#endif // PULSEVIEW_PV_VIEW_ANALOGSIGNAL_HPP
diff --git a/pv/view/cursor.cpp b/pv/view/cursor.cpp
index ca22eef..7432e30 100644
--- a/pv/view/cursor.cpp
+++ b/pv/view/cursor.cpp
@@ -18,141 +18,83 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "cursor.h"
+#include "cursor.hpp"
-#include "ruler.h"
-#include "view.h"
+#include "ruler.hpp"
+#include "view.hpp"
+#include "pv/util.hpp"
+#include <QApplication>
#include <QBrush>
#include <QPainter>
#include <QPointF>
#include <QRect>
#include <QRectF>
-#include <stdio.h>
+#include <cassert>
+#include <cstdio>
+#include <limits>
-#include <extdef.h>
-
-using boost::shared_ptr;
+using std::abs;
+using std::shared_ptr;
+using std::numeric_limits;
namespace pv {
namespace view {
-const QColor Cursor::LineColour(32, 74, 135);
const QColor Cursor::FillColour(52, 101, 164);
-const QColor Cursor::HighlightColour(83, 130, 186);
-const QColor Cursor::TextColour(Qt::white);
-const int Cursor::Offset = 1;
+Cursor::Cursor(View &view, double time) :
+ TimeMarker(view, FillColour, time)
+{
+}
-const int Cursor::ArrowSize = 4;
+bool Cursor::enabled() const
+{
+ return view_.cursors_shown();
+}
-Cursor::Cursor(View &view, double time) :
- TimeMarker(view, LineColour, time)
+QString Cursor::get_text() const
{
+ const shared_ptr<Cursor> other = get_other_cursor();
+ const pv::util::Timestamp& diff = abs(time_ - other->time_);
+
+ return Ruler::format_time_with_distance(
+ diff, time_, view_.tick_prefix(), view_.time_unit(), view_.tick_precision());
}
-QRectF Cursor::get_label_rect(const QRect &rect) const
+QRectF Cursor::label_rect(const QRectF &rect) const
{
const shared_ptr<Cursor> other(get_other_cursor());
assert(other);
- const float x = (_time - _view.offset()) / _view.scale();
+ const float x = ((time_ - view_.offset())/ view_.scale()).convert_to<float>();
+
+ QFontMetrics m(QApplication::font());
+ QSize text_size = m.boundingRect(get_text()).size();
const QSizeF label_size(
- _text_size.width() + View::LabelPadding.width() * 2,
- _text_size.height() + View::LabelPadding.height() * 2);
+ text_size.width() + LabelPadding.width() * 2,
+ text_size.height() + LabelPadding.height() * 2);
const float top = rect.height() - label_size.height() -
- Cursor::Offset - Cursor::ArrowSize - 0.5f;
+ TimeMarker::ArrowSize - 0.5f;
const float height = label_size.height();
- if (_time > other->time())
+ const pv::util::Timestamp& other_time = other->time();
+
+ if (time_ > other_time ||
+ (abs(time_ - other_time).is_zero() && this > other.get()))
return QRectF(x, top, label_size.width(), height);
else
- return QRectF(x - label_size.width(), top,
- label_size.width(), height);
-}
-
-void Cursor::paint_label(QPainter &p, const QRect &rect,
- unsigned int prefix)
-{
- const shared_ptr<Cursor> other(get_other_cursor());
- assert(other);
-
- compute_text_size(p, prefix);
- const QRectF r(get_label_rect(rect));
-
- const QPointF left_points[] = {
- r.topLeft(),
- r.topRight(),
- r.bottomRight(),
- QPointF(r.left() + ArrowSize, r.bottom()),
- QPointF(r.left(), rect.bottom()),
- };
-
- const QPointF right_points[] = {
- r.topRight(),
- r.topLeft(),
- r.bottomLeft(),
- QPointF(r.right() - ArrowSize, r.bottom()),
- QPointF(r.right(), rect.bottom()),
- };
-
- const QPointF left_highlight_points[] = {
- QPointF(r.left() + 1, r.top() + 1),
- QPointF(r.right() - 1, r.top() + 1),
- QPointF(r.right() - 1, r.bottom() - 1),
- QPointF(r.left() + ArrowSize - 1, r.bottom() - 1),
- QPointF(r.left() + 1, rect.bottom() - 1),
- };
-
- const QPointF right_highlight_points[] = {
- QPointF(r.right() - 1, r.top() + 1),
- QPointF(r.left() + 1, r.top() + 1),
- QPointF(r.left() + 1, r.bottom() - 1),
- QPointF(r.right() - ArrowSize + 1, r.bottom() - 1),
- QPointF(r.right() - 1, rect.bottom() - 1),
- };
-
- const QPointF *const points = (_time > other->time()) ?
- left_points : right_points;
- const QPointF *const highlight_points = (_time > other->time()) ?
- left_highlight_points : right_highlight_points;
-
- if (selected()) {
- p.setPen(highlight_pen());
- p.setBrush(Qt::transparent);
- p.drawPolygon(points, countof(left_points));
- }
-
- p.setPen(Qt::transparent);
- p.setBrush(FillColour);
- p.drawPolygon(points, countof(left_points));
-
- p.setPen(HighlightColour);
- p.setBrush(Qt::transparent);
- p.drawPolygon(highlight_points, countof(left_highlight_points));
-
- p.setPen(LineColour);
- p.setBrush(Qt::transparent);
- p.drawPolygon(points, countof(left_points));
-
- p.setPen(TextColour);
- p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter,
- Ruler::format_time(_time, prefix, 2));
-}
-
-void Cursor::compute_text_size(QPainter &p, unsigned int prefix)
-{
- _text_size = p.boundingRect(QRectF(), 0,
- Ruler::format_time(_time, prefix, 2)).size();
+ return QRectF(x - label_size.width(), top, label_size.width(), height);
}
shared_ptr<Cursor> Cursor::get_other_cursor() const
{
- const CursorPair &cursors = _view.cursors();
- return (cursors.first().get() == this) ?
- cursors.second() : cursors.first();
+ const shared_ptr<CursorPair> cursors(view_.cursors());
+ assert(cursors);
+ return (cursors->first().get() == this) ?
+ cursors->second() : cursors->first();
}
} // namespace view
diff --git a/pv/view/cursor.h b/pv/view/cursor.hpp
similarity index 64%
rename from pv/view/cursor.h
rename to pv/view/cursor.hpp
index 290365e..e64beaa 100644
--- a/pv/view/cursor.h
+++ b/pv/view/cursor.hpp
@@ -18,12 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_CURSOR_H
-#define PULSEVIEW_PV_VIEW_CURSOR_H
+#ifndef PULSEVIEW_PV_VIEW_CURSOR_HPP
+#define PULSEVIEW_PV_VIEW_CURSOR_HPP
-#include "timemarker.h"
+#include "timemarker.hpp"
-#include <boost/shared_ptr.hpp>
+#include <memory>
#include <QSizeF>
@@ -37,14 +37,7 @@ class Cursor : public TimeMarker
Q_OBJECT
public:
- static const QColor LineColour;
static const QColor FillColour;
- static const QColor HighlightColour;
- static const QColor TextColour;
-
- static const int Offset;
-
- static const int ArrowSize;
public:
/**
@@ -56,31 +49,27 @@ public:
public:
/**
- * Gets the marker label rectangle.
- * @param rect The rectangle of the ruler client area.
- * @return Returns the label rectangle.
+ * Returns true if the item is visible and enabled.
*/
- QRectF get_label_rect(const QRect &rect) const;
+ bool enabled() const;
/**
- * Paints the cursor's label to the ruler.
- * @param p The painter to draw with.
- * @param rect The rectangle of the ruler client area.
- * @param prefix The index of the SI prefix to use.
+ * Gets the text to show in the marker.
*/
- void paint_label(QPainter &p, const QRect &rect,
- unsigned int prefix);
+ QString get_text() const;
-private:
- void compute_text_size(QPainter &p, unsigned int prefix);
-
- boost::shared_ptr<Cursor> get_other_cursor() const;
+ /**
+ * Gets the marker label rectangle.
+ * @param rect The rectangle of the ruler client area.
+ * @return Returns the label rectangle.
+ */
+ QRectF label_rect(const QRectF &rect) const;
private:
- QSizeF _text_size;
+ std::shared_ptr<Cursor> get_other_cursor() const;
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_CURSOR_H
+#endif // PULSEVIEW_PV_VIEW_CURSOR_HPP
diff --git a/pv/view/cursorpair.cpp b/pv/view/cursorpair.cpp
index ed8829d..f9d3e53 100644
--- a/pv/view/cursorpair.cpp
+++ b/pv/view/cursorpair.cpp
@@ -18,46 +18,75 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "cursorpair.h"
+#include "cursorpair.hpp"
-#include "ruler.h"
-#include "view.h"
+#include "ruler.hpp"
+#include "view.hpp"
+#include "pv/util.hpp"
+#include <cassert>
#include <algorithm>
-using boost::shared_ptr;
using std::max;
using std::make_pair;
using std::min;
+using std::shared_ptr;
using std::pair;
namespace pv {
namespace view {
const int CursorPair::DeltaPadding = 8;
+const QColor CursorPair::ViewportFillColour(220, 231, 243);
CursorPair::CursorPair(View &view) :
- _first(new Cursor(view, 0.0)),
- _second(new Cursor(view, 1.0)),
- _view(view)
+ TimeItem(view),
+ first_(new Cursor(view, 0.0)),
+ second_(new Cursor(view, 1.0))
{
}
+bool CursorPair::enabled() const
+{
+ return view_.cursors_shown();
+}
+
shared_ptr<Cursor> CursorPair::first() const
{
- return _first;
+ return first_;
}
shared_ptr<Cursor> CursorPair::second() const
{
- return _second;
+ return second_;
+}
+
+void CursorPair::set_time(const pv::util::Timestamp& time)
+{
+ const pv::util::Timestamp delta = second_->time() - first_->time();
+ first_->set_time(time);
+ second_->set_time(time + delta);
}
-QRectF CursorPair::get_label_rect(const QRect &rect) const
+float CursorPair::get_x() const
{
- const QSizeF label_size(
- _text_size.width() + View::LabelPadding.width() * 2,
- _text_size.height() + View::LabelPadding.height() * 2);
+ return (first_->get_x() + second_->get_x()) / 2.0f;
+}
+
+QPoint CursorPair::point(const QRect &rect) const
+{
+ return first_->point(rect);
+}
+
+pv::widgets::Popup* CursorPair::create_popup(QWidget *parent)
+{
+ (void)parent;
+ return nullptr;
+}
+
+QRectF CursorPair::label_rect(const QRectF &rect) const
+{
+ const QSizeF label_size(text_size_ + LabelPadding * 2);
const pair<float, float> offsets(get_cursor_offsets());
const pair<float, float> normal_offsets(
(offsets.first < offsets.second) ? offsets :
@@ -69,86 +98,98 @@ QRectF CursorPair::get_label_rect(const QRect &rect) const
(float)rect.width() + height);
return QRectF(left, rect.height() - label_size.height() -
- Cursor::ArrowSize - Cursor::Offset - 0.5f,
+ TimeMarker::ArrowSize - 0.5f,
right - left, height);
}
-void CursorPair::draw_markers(QPainter &p,
- const QRect &rect, unsigned int prefix)
+void CursorPair::paint_label(QPainter &p, const QRect &rect, bool hover)
{
- assert(_first);
- assert(_second);
+ assert(first_);
+ assert(second_);
+
+ if (!enabled())
+ return;
- compute_text_size(p, prefix);
- QRectF delta_rect(get_label_rect(rect));
+ const QColor text_colour =
+ ViewItem::select_text_colour(Cursor::FillColour);
+
+ p.setPen(text_colour);
+ compute_text_size(p);
+ QRectF delta_rect(label_rect(rect));
const int radius = delta_rect.height() / 2;
const QRectF text_rect(delta_rect.intersected(
rect).adjusted(radius, 0, -radius, 0));
- if(text_rect.width() >= _text_size.width())
- {
+ if (text_rect.width() >= text_size_.width()) {
const int highlight_radius = delta_rect.height() / 2 - 2;
- p.setBrush(Cursor::FillColour);
- p.setPen(Cursor::LineColour);
+ if (selected()) {
+ p.setBrush(Qt::transparent);
+ p.setPen(highlight_pen());
+ p.drawRoundedRect(delta_rect, radius, radius);
+ }
+
+ p.setBrush(hover ? Cursor::FillColour.lighter() :
+ Cursor::FillColour);
+ p.setPen(Cursor::FillColour.darker());
p.drawRoundedRect(delta_rect, radius, radius);
delta_rect.adjust(1, 1, -1, -1);
- p.setPen(Cursor::HighlightColour);
+ p.setPen(Cursor::FillColour.lighter());
p.drawRoundedRect(delta_rect, highlight_radius, highlight_radius);
- p.setPen(Cursor::TextColour);
+ p.setPen(text_colour);
p.drawText(text_rect, Qt::AlignCenter | Qt::AlignVCenter,
- Ruler::format_time(_second->time() - _first->time(), prefix, 2));
+ format_string());
}
-
- // Paint the cursor markers
- _first->paint_label(p, rect, prefix);
- _second->paint_label(p, rect, prefix);
}
-void CursorPair::draw_viewport_background(QPainter &p,
- const QRect &rect)
+void CursorPair::paint_back(QPainter &p, const ViewItemPaintParams &pp)
{
+ if (!enabled())
+ return;
+
p.setPen(Qt::NoPen);
- p.setBrush(QBrush(View::CursorAreaColour));
+ p.setBrush(QBrush(ViewportFillColour));
const pair<float, float> offsets(get_cursor_offsets());
const int l = (int)max(min(
offsets.first, offsets.second), 0.0f);
const int r = (int)min(max(
- offsets.first, offsets.second), (float)rect.width());
+ offsets.first, offsets.second), (float)pp.width());
- p.drawRect(l, 0, r - l, rect.height());
+ p.drawRect(l, pp.top(), r - l, pp.height());
}
-void CursorPair::draw_viewport_foreground(QPainter &p,
- const QRect &rect)
+QString CursorPair::format_string()
{
- assert(_first);
- assert(_second);
+ const pv::util::SIPrefix prefix = view_.tick_prefix();
+ const pv::util::Timestamp diff = abs(second_->time() - first_->time());
+
+ const QString s1 = Ruler::format_time_with_distance(
+ diff, diff, prefix, view_.time_unit(), view_.tick_precision(), false);
+ const QString s2 = util::format_time_si(
+ 1 / diff, pv::util::SIPrefix::unspecified, 4, "Hz", false);
- _first->paint(p, rect);
- _second->paint(p, rect);
+ return QString("%1 / %2").arg(s1).arg(s2);
}
-void CursorPair::compute_text_size(QPainter &p, unsigned int prefix)
+void CursorPair::compute_text_size(QPainter &p)
{
- assert(_first);
- assert(_second);
+ assert(first_);
+ assert(second_);
- _text_size = p.boundingRect(QRectF(), 0, Ruler::format_time(
- _second->time() - _first->time(), prefix, 2)).size();
+ text_size_ = p.boundingRect(QRectF(), 0, format_string()).size();
}
pair<float, float> CursorPair::get_cursor_offsets() const
{
- assert(_first);
- assert(_second);
+ assert(first_);
+ assert(second_);
return pair<float, float>(
- (_first->time() - _view.offset()) / _view.scale(),
- (_second->time() - _view.offset()) / _view.scale());
+ ((first_->time() - view_.offset()) / view_.scale()).convert_to<float>(),
+ ((second_->time() - view_.offset()) / view_.scale()).convert_to<float>());
}
} // namespace view
diff --git a/pv/view/cursorpair.hpp b/pv/view/cursorpair.hpp
new file mode 100644
index 0000000..345700f
--- /dev/null
+++ b/pv/view/cursorpair.hpp
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_CURSORPAIR_HPP
+#define PULSEVIEW_PV_VIEW_CURSORPAIR_HPP
+
+#include "cursor.hpp"
+
+#include <memory>
+
+#include <QPainter>
+
+class QPainter;
+
+namespace pv {
+namespace view {
+
+class CursorPair : public TimeItem
+{
+private:
+ static const int DeltaPadding;
+ static const QColor ViewportFillColour;
+
+public:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this cursor pair.
+ */
+ CursorPair(View &view);
+
+public:
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Returns a pointer to the first cursor.
+ */
+ std::shared_ptr<Cursor> first() const;
+
+ /**
+ * Returns a pointer to the second cursor.
+ */
+ std::shared_ptr<Cursor> second() const;
+
+ /**
+ * Sets the time of the marker.
+ */
+ void set_time(const pv::util::Timestamp& time) override;
+
+ float get_x() const;
+
+ QPoint point(const QRect &rect) const;
+
+ pv::widgets::Popup* create_popup(QWidget *parent);
+
+public:
+ QRectF label_rect(const QRectF &rect) const;
+
+ /**
+ * Paints the marker's label to the ruler.
+ * @param p The painter to draw with.
+ * @param rect The rectangle of the ruler client area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Paints the background layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_back(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Constructs the string to display.
+ */
+ QString format_string();
+
+ void compute_text_size(QPainter &p);
+
+ std::pair<float, float> get_cursor_offsets() const;
+
+private:
+ std::shared_ptr<Cursor> first_, second_;
+
+ QSizeF text_size_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_CURSORPAIR_HPP
diff --git a/pv/view/decodetrace.cpp b/pv/view/decodetrace.cpp
index 618de9a..e0f3a0f 100644
--- a/pv/view/decodetrace.cpp
+++ b/pv/view/decodetrace.cpp
@@ -22,10 +22,15 @@ extern "C" {
#include <libsigrokdecode/libsigrokdecode.h>
}
+#include <mutex>
+
#include <extdef.h>
-#include <boost/foreach.hpp>
+#include <tuple>
+
#include <boost/functional/hash.hpp>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/shared_mutex.hpp>
#include <QAction>
#include <QApplication>
@@ -34,26 +39,36 @@ extern "C" {
#include <QLabel>
#include <QMenu>
#include <QPushButton>
-
-#include "decodetrace.h"
-
-#include <pv/sigsession.h>
-#include <pv/data/decoderstack.h>
-#include <pv/data/decode/decoder.h>
-#include <pv/data/logic.h>
-#include <pv/data/logicsnapshot.h>
-#include <pv/data/decode/annotation.h>
-#include <pv/view/logicsignal.h>
-#include <pv/view/view.h>
-#include <pv/widgets/decodergroupbox.h>
-#include <pv/widgets/decodermenu.h>
-
-using boost::dynamic_pointer_cast;
-using boost::shared_ptr;
+#include <QToolTip>
+
+#include "decodetrace.hpp"
+
+#include <pv/session.hpp>
+#include <pv/data/decoderstack.hpp>
+#include <pv/data/decode/decoder.hpp>
+#include <pv/data/logic.hpp>
+#include <pv/data/logicsegment.hpp>
+#include <pv/data/decode/annotation.hpp>
+#include <pv/view/logicsignal.hpp>
+#include <pv/view/view.hpp>
+#include <pv/view/viewport.hpp>
+#include <pv/widgets/decodergroupbox.hpp>
+#include <pv/widgets/decodermenu.hpp>
+
+using boost::shared_lock;
+using boost::shared_mutex;
+using std::dynamic_pointer_cast;
using std::list;
+using std::lock_guard;
+using std::make_pair;
using std::max;
+using std::make_pair;
using std::map;
using std::min;
+using std::pair;
+using std::shared_ptr;
+using std::tie;
+using std::unordered_set;
using std::vector;
namespace pv {
@@ -111,24 +126,26 @@ const QColor DecodeTrace::OutlineColours[16] = {
QColor(0x6B, 0x23, 0x37)
};
-DecodeTrace::DecodeTrace(pv::SigSession &session,
- boost::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
+DecodeTrace::DecodeTrace(pv::Session &session,
+ std::shared_ptr<pv::data::DecoderStack> decoder_stack, int index) :
Trace(QString::fromUtf8(
decoder_stack->stack().front()->decoder()->name)),
- _session(session),
- _decoder_stack(decoder_stack),
- _delete_mapper(this),
- _show_hide_mapper(this)
+ session_(session),
+ decoder_stack_(decoder_stack),
+ row_height_(0),
+ max_visible_rows_(0),
+ delete_mapper_(this),
+ show_hide_mapper_(this)
{
- assert(_decoder_stack);
+ assert(decoder_stack_);
- _colour = DecodeColours[index % countof(DecodeColours)];
+ set_colour(DecodeColours[index % countof(DecodeColours)]);
- connect(_decoder_stack.get(), SIGNAL(new_decode_data()),
+ connect(decoder_stack_.get(), SIGNAL(new_decode_data()),
this, SLOT(on_new_decode_data()));
- connect(&_delete_mapper, SIGNAL(mapped(int)),
+ connect(&delete_mapper_, SIGNAL(mapped(int)),
this, SLOT(on_delete_decoder(int)));
- connect(&_show_hide_mapper, SIGNAL(mapped(int)),
+ connect(&show_hide_mapper_, SIGNAL(mapped(int)),
this, SLOT(on_show_hide_decoder(int)));
}
@@ -137,71 +154,51 @@ bool DecodeTrace::enabled() const
return true;
}
-const boost::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
+const std::shared_ptr<pv::data::DecoderStack>& DecodeTrace::decoder() const
{
- return _decoder_stack;
+ return decoder_stack_;
}
-void DecodeTrace::set_view(pv::view::View *view)
+pair<int, int> DecodeTrace::v_extents() const
{
- assert(view);
- Trace::set_view(view);
+ const int row_height = (ViewItemPaintParams::text_height() * 6) / 4;
+
+ return make_pair(-row_height, row_height * max_visible_rows_);
}
-void DecodeTrace::paint_back(QPainter &p, int left, int right)
+void DecodeTrace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
{
- Trace::paint_back(p, left, right);
- paint_axis(p, get_y(), left, right);
+ Trace::paint_back(p, pp);
+ paint_axis(p, pp, get_visual_y());
}
-void DecodeTrace::paint_mid(QPainter &p, int left, int right)
+void DecodeTrace::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
{
using namespace pv::data::decode;
- const double scale = _view->scale();
- assert(scale > 0);
-
- double samplerate = _decoder_stack->samplerate();
-
- _cur_row_headings.clear();
-
- // Show sample rate as 1Hz when it is unknown
- if (samplerate == 0.0)
- samplerate = 1.0;
-
- const double pixels_offset = (_view->offset() -
- _decoder_stack->get_start_time()) / scale;
- const double samples_per_pixel = samplerate * scale;
-
- const uint64_t start_sample = (uint64_t)max((left + pixels_offset) *
- samples_per_pixel, 0.0);
- const uint64_t end_sample = (uint64_t)max((right + pixels_offset) *
- samples_per_pixel, 0.0);
-
- QFontMetrics m(QApplication::font());
- const int text_height = m.boundingRect(QRect(), 0, "Tg").height();
+ const int text_height = ViewItemPaintParams::text_height();
+ row_height_ = (text_height * 6) / 4;
const int annotation_height = (text_height * 5) / 4;
- const int row_height = (text_height * 6) / 4;
-
- assert(_decoder_stack);
- const QString err = _decoder_stack->error_message();
- if (!err.isEmpty())
- {
- draw_unresolved_period(p, annotation_height, left, right,
- samples_per_pixel, pixels_offset);
- draw_error(p, err, left, right);
+
+ assert(decoder_stack_);
+ const QString err = decoder_stack_->error_message();
+ if (!err.isEmpty()) {
+ draw_unresolved_period(
+ p, annotation_height, pp.left(), pp.right());
+ draw_error(p, err, pp);
return;
}
// Iterate through the rows
- assert(_view);
- int y = get_y();
+ int y = get_visual_y();
+ pair<uint64_t, uint64_t> sample_range = get_sample_range(
+ pp.left(), pp.right());
- assert(_decoder_stack);
+ assert(decoder_stack_);
+ const vector<Row> rows(decoder_stack_->get_visible_rows());
- const vector<Row> rows(_decoder_stack->get_visible_rows());
- for (size_t i = 0; i < rows.size(); i++)
- {
+ visible_rows_.clear();
+ for (size_t i = 0; i < rows.size(); i++) {
const Row &row = rows[i];
size_t base_colour = 0x13579BDF;
@@ -211,55 +208,49 @@ void DecodeTrace::paint_mid(QPainter &p, int left, int right)
base_colour >>= 16;
vector<Annotation> annotations;
- _decoder_stack->get_annotation_subset(annotations, row,
- start_sample, end_sample);
+ decoder_stack_->get_annotation_subset(annotations, row,
+ sample_range.first, sample_range.second);
if (!annotations.empty()) {
- BOOST_FOREACH(const Annotation &a, annotations)
- draw_annotation(a, p, get_text_colour(),
- annotation_height, left, right,
- samples_per_pixel, pixels_offset, y,
- base_colour);
- y += row_height;
-
- _cur_row_headings.push_back(row.title());
+ draw_annotations(annotations, p, annotation_height, pp, y,
+ base_colour);
+
+ y += row_height_;
+
+ visible_rows_.push_back(rows[i]);
}
}
// Draw the hatching
- draw_unresolved_period(p, annotation_height, left, right,
- samples_per_pixel, pixels_offset);
+ draw_unresolved_period(p, annotation_height, pp.left(), pp.right());
+
+ // Update the maximum row count if needed
+ max_visible_rows_ = std::max(max_visible_rows_, (int)visible_rows_.size());
}
-void DecodeTrace::paint_fore(QPainter &p, int left, int right)
+void DecodeTrace::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
{
using namespace pv::data::decode;
- (void)right;
-
- QFontMetrics m(QApplication::font());
- const int text_height = m.boundingRect(QRect(), 0, "Tg").height();
- const int row_height = (text_height * 6) / 4;
+ assert(row_height_);
- for (size_t i = 0; i < _cur_row_headings.size(); i++)
- {
- const int y = i * row_height + get_y();
+ for (size_t i = 0; i < visible_rows_.size(); i++) {
+ const int y = i * row_height_ + get_visual_y();
p.setPen(QPen(Qt::NoPen));
p.setBrush(QApplication::palette().brush(QPalette::WindowText));
- if (i != 0)
- {
+ if (i != 0) {
const QPointF points[] = {
- QPointF(left, y - ArrowSize),
- QPointF(left + ArrowSize, y),
- QPointF(left, y + ArrowSize)
+ QPointF(pp.left(), y - ArrowSize),
+ QPointF(pp.left() + ArrowSize, y),
+ QPointF(pp.left(), y + ArrowSize)
};
p.drawPolygon(points, countof(points));
}
- const QRect r(left + ArrowSize * 2, y - row_height / 2,
- right - left, row_height);
- const QString h(_cur_row_headings[i]);
+ const QRect r(pp.left() + ArrowSize * 2, y - row_height_ / 2,
+ pp.right() - pp.left(), row_height_);
+ const QString h(visible_rows_[i].title());
const int f = Qt::AlignLeft | Qt::AlignVCenter |
Qt::TextDontClip;
@@ -282,29 +273,25 @@ void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form)
assert(form);
assert(parent);
- assert(_decoder_stack);
+ assert(decoder_stack_);
// Add the standard options
Trace::populate_popup_form(parent, form);
// Add the decoder options
- _bindings.clear();
- _probe_selectors.clear();
- _decoder_forms.clear();
+ bindings_.clear();
+ channel_selectors_.clear();
+ decoder_forms_.clear();
- const list< shared_ptr<Decoder> >& stack = _decoder_stack->stack();
+ const list< shared_ptr<Decoder> >& stack = decoder_stack_->stack();
- if (stack.empty())
- {
+ if (stack.empty()) {
QLabel *const l = new QLabel(
tr("<p><i>No decoders in the stack</i></p>"));
l->setAlignment(Qt::AlignCenter);
form->addRow(l);
- }
- else
- {
- list< shared_ptr<Decoder> >::const_iterator iter =
- stack.begin();
+ } else {
+ auto iter = stack.cbegin();
for (int i = 0; i < (int)stack.size(); i++, iter++) {
shared_ptr<Decoder> dec(*iter);
create_decoder_form(i, dec, parent, form);
@@ -343,11 +330,56 @@ QMenu* DecodeTrace::create_context_menu(QWidget *parent)
return menu;
}
+void DecodeTrace::draw_annotations(vector<pv::data::decode::Annotation> annotations,
+ QPainter &p, int h, const ViewItemPaintParams &pp, int y,
+ size_t base_colour)
+{
+ using namespace pv::data::decode;
+
+ vector<Annotation> a_block;
+ int prev_ann_pos = INT_MIN;
+
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ // Gather all annotations that form a visual "block" and draw them as such
+ for (const Annotation &a : annotations) {
+
+ const int end = a.end_sample() / samples_per_pixel - pixels_offset;
+ const int delta = end - prev_ann_pos;
+
+ // Some annotations are in reverse order, so we cannot
+ // simply check for delta > 1
+ if (abs(delta) > 1) {
+ // Block was broken, draw it
+ if (a_block.size() == 1)
+ draw_annotation(a_block.front(), p, h, pp, y, base_colour);
+ else
+ if (a_block.size() > 0)
+ draw_annotation_block(a_block, p, h, y, base_colour);
+
+ a_block.clear();
+ }
+
+ a_block.push_back(a);
+ prev_ann_pos = end;
+ }
+
+ if (a_block.size() == 1)
+ draw_annotation(a_block.front(), p, h, pp, y, base_colour);
+ else
+ draw_annotation_block(a_block, p, h, y, base_colour);
+}
+
void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
- QPainter &p, QColor text_color, int h, int left, int right,
- double samples_per_pixel, double pixels_offset, int y,
+ QPainter &p, int h, const ViewItemPaintParams &pp, int y,
size_t base_colour) const
{
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
const double start = a.start_sample() / samples_per_pixel -
pixels_offset;
const double end = a.end_sample() / samples_per_pixel -
@@ -357,19 +389,52 @@ void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a,
const QColor &fill = Colours[colour];
const QColor &outline = OutlineColours[colour];
- if (start > right + DrawPadding || end < left - DrawPadding)
+ if (start > pp.right() + DrawPadding || end < pp.left() - DrawPadding)
return;
if (a.start_sample() == a.end_sample())
- draw_instant(a, p, fill, outline, text_color, h,
- start, y);
+ draw_instant(a, p, fill, outline, h, start, y);
else
- draw_range(a, p, fill, outline, text_color, h,
- start, end, y);
+ draw_range(a, p, fill, outline, h, start, end, y);
+}
+
+void DecodeTrace::draw_annotation_block(
+ vector<pv::data::decode::Annotation> annotations, QPainter &p, int h,
+ int y, size_t base_colour) const
+{
+ using namespace pv::data::decode;
+
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ const double start = annotations.front().start_sample() /
+ samples_per_pixel - pixels_offset;
+ const double end = annotations.back().end_sample() /
+ samples_per_pixel - pixels_offset;
+
+ const double top = y + .5 - h / 2;
+ const double bottom = y + .5 + h / 2;
+
+ const size_t colour = (base_colour + annotations.front().format()) %
+ countof(Colours);
+
+ // Check if all annotations are of the same type (i.e. we can use one color)
+ // or if we should use a neutral color (i.e. gray)
+ const int format = annotations.front().format();
+ const bool single_format = std::all_of(
+ annotations.begin(), annotations.end(),
+ [&](const Annotation &a) { return a.format() == format; });
+
+ p.setPen((single_format ? OutlineColours[colour] : Qt::gray));
+ p.setBrush(QBrush((single_format ? Colours[colour] : Qt::gray),
+ Qt::Dense4Pattern));
+ p.drawRoundedRect(
+ QRectF(start, top, end - start, bottom - top), h/4, h/4);
}
void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
- QColor fill, QColor outline, QColor text_color, int h, double x, int y) const
+ QColor fill, QColor outline, int h, double x, int y) const
{
const QString text = a.annotations().empty() ?
QString() : a.annotations().back();
@@ -381,12 +446,12 @@ void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &
p.setBrush(fill);
p.drawRoundedRect(rect, h / 2, h / 2);
- p.setPen(text_color);
+ p.setPen(Qt::black);
p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text);
}
void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
- QColor fill, QColor outline, QColor text_color, int h, double start,
+ QColor fill, QColor outline, int h, double start,
double end, int y) const
{
const double top = y + .5 - h / 2;
@@ -397,8 +462,7 @@ void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
p.setBrush(fill);
// If the two ends are within 1 pixel, draw a vertical line
- if (start + 1.0 > end)
- {
+ if (start + 1.0 > end) {
p.drawLine(QPointF(start, top), QPointF(start, bottom));
return;
}
@@ -424,13 +488,13 @@ void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
if (rect.width() <= 4)
return;
- p.setPen(text_color);
+ p.setPen(Qt::black);
// Try to find an annotation that will fit
QString best_annotation;
int best_width = 0;
- BOOST_FOREACH(const QString &a, annotations) {
+ for (const QString &a : annotations) {
const int w = p.boundingRect(QRectF(), 0, a).width();
if (w <= rect.width() && w > best_width)
best_annotation = a, best_width = w;
@@ -445,15 +509,15 @@ void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p,
}
void DecodeTrace::draw_error(QPainter &p, const QString &message,
- int left, int right)
+ const ViewItemPaintParams &pp)
{
- const int y = get_y();
+ const int y = get_visual_y();
p.setPen(ErrorBgColour.darker());
p.setBrush(ErrorBgColour);
const QRectF bounding_rect =
- QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX);
+ QRectF(pp.width(), INT_MIN / 2 + y, pp.width(), INT_MAX);
const QRectF text_rect = p.boundingRect(bounding_rect,
Qt::AlignCenter, message);
const float r = text_rect.height() / 4;
@@ -461,47 +525,53 @@ void DecodeTrace::draw_error(QPainter &p, const QString &message,
p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r,
Qt::AbsoluteSize);
- p.setPen(get_text_colour());
+ p.setPen(Qt::black);
p.drawText(text_rect, message);
}
void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
- int right, double samples_per_pixel, double pixels_offset)
+ int right) const
{
using namespace pv::data;
using pv::data::decode::Decoder;
- assert(_decoder_stack);
+ double samples_per_pixel, pixels_offset;
+
+ assert(decoder_stack_);
shared_ptr<Logic> data;
shared_ptr<LogicSignal> logic_signal;
- const list< shared_ptr<Decoder> > &stack = _decoder_stack->stack();
+ const list< shared_ptr<Decoder> > &stack = decoder_stack_->stack();
- // We get the logic data of the first probe in the list.
+ // We get the logic data of the first channel in the list.
// This works because we are currently assuming all
- // LogicSignals have the same data/snapshot
- BOOST_FOREACH (const shared_ptr<Decoder> &dec, stack)
+ // LogicSignals have the same data/segment
+ for (const shared_ptr<Decoder> &dec : stack)
if (dec && !dec->channels().empty() &&
((logic_signal = (*dec->channels().begin()).second)) &&
((data = logic_signal->logic_data())))
break;
- if (!data || data->get_snapshots().empty())
+ if (!data || data->logic_segments().empty())
return;
- const shared_ptr<LogicSnapshot> snapshot =
- data->get_snapshots().front();
- assert(snapshot);
- const int64_t sample_count = (int64_t)snapshot->get_sample_count();
+ const shared_ptr<LogicSegment> segment =
+ data->logic_segments().front();
+ assert(segment);
+ const int64_t sample_count = (int64_t)segment->get_sample_count();
if (sample_count == 0)
return;
- const int64_t samples_decoded = _decoder_stack->samples_decoded();
+ const int64_t samples_decoded = decoder_stack_->samples_decoded();
if (sample_count == samples_decoded)
return;
- const int y = get_y();
+ const int y = get_visual_y();
+
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
const double start = max(samples_decoded /
samples_per_pixel - pixels_offset, left - 1.0);
const double end = min(sample_count / samples_per_pixel -
@@ -517,6 +587,125 @@ void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left,
p.drawRect(no_decode_rect);
}
+pair<double, double> DecodeTrace::get_pixels_offset_samples_per_pixel() const
+{
+ assert(owner_);
+ assert(decoder_stack_);
+
+ const View *view = owner_->view();
+ assert(view);
+
+ const double scale = view->scale();
+ assert(scale > 0);
+
+ const double pixels_offset =
+ ((view->offset() - decoder_stack_->start_time()) / scale).convert_to<double>();
+
+ double samplerate = decoder_stack_->samplerate();
+
+ // Show sample rate as 1Hz when it is unknown
+ if (samplerate == 0.0)
+ samplerate = 1.0;
+
+ return make_pair(pixels_offset, samplerate * scale);
+}
+
+pair<uint64_t, uint64_t> DecodeTrace::get_sample_range(
+ int x_start, int x_end) const
+{
+ double samples_per_pixel, pixels_offset;
+ tie(pixels_offset, samples_per_pixel) =
+ get_pixels_offset_samples_per_pixel();
+
+ const uint64_t start = (uint64_t)max(
+ (x_start + pixels_offset) * samples_per_pixel, 0.0);
+ const uint64_t end = (uint64_t)max(
+ (x_end + pixels_offset) * samples_per_pixel, 0.0);
+
+ return make_pair(start, end);
+}
+
+int DecodeTrace::get_row_at_point(const QPoint &point)
+{
+ if (!row_height_)
+ return -1;
+
+ const int y = (point.y() - get_visual_y() + row_height_ / 2);
+
+ /* Integer divison of (x-1)/x would yield 0, so we check for this. */
+ if (y < 0)
+ return -1;
+
+ const int row = y / row_height_;
+
+ if (row >= (int)visible_rows_.size())
+ return -1;
+
+ return row;
+}
+
+const QString DecodeTrace::get_annotation_at_point(const QPoint &point)
+{
+ using namespace pv::data::decode;
+
+ if (!enabled())
+ return QString();
+
+ const pair<uint64_t, uint64_t> sample_range =
+ get_sample_range(point.x(), point.x() + 1);
+ const int row = get_row_at_point(point);
+ if (row < 0)
+ return QString();
+
+ vector<pv::data::decode::Annotation> annotations;
+
+ assert(decoder_stack_);
+ decoder_stack_->get_annotation_subset(annotations, visible_rows_[row],
+ sample_range.first, sample_range.second);
+
+ return (annotations.empty()) ?
+ QString() : annotations[0].annotations().front();
+}
+
+void DecodeTrace::hover_point_changed()
+{
+ assert(owner_);
+
+ const View *const view = owner_->view();
+ assert(view);
+
+ QPoint hp = view->hover_point();
+ QString ann = get_annotation_at_point(hp);
+
+ assert(view);
+
+ if (!row_height_ || ann.isEmpty()) {
+ QToolTip::hideText();
+ return;
+ }
+
+ const int hover_row = get_row_at_point(hp);
+
+ QFontMetrics m(QToolTip::font());
+ const QRect text_size = m.boundingRect(QRect(), 0, ann);
+
+ // This is OS-specific and unfortunately we can't query it, so
+ // use an approximation to at least try to minimize the error.
+ const int padding = 8;
+
+ // Make sure the tool tip doesn't overlap with the mouse cursor.
+ // If it did, the tool tip would constantly hide and re-appear.
+ // We also push it up by one row so that it appears above the
+ // decode trace, not below.
+ hp.setX(hp.x() - (text_size.width() / 2) - padding);
+
+ hp.setY(get_visual_y() - (row_height_ / 2) +
+ (hover_row * row_height_) -
+ row_height_ - text_size.height() - padding);
+
+ QToolTip::showText(view->viewport()->mapToGlobal(hp), ann);
+}
+
void DecodeTrace::create_decoder_form(int index,
shared_ptr<data::decode::Decoder> &dec, QWidget *parent,
QFormLayout *form)
@@ -527,139 +716,145 @@ void DecodeTrace::create_decoder_form(int index,
const srd_decoder *const decoder = dec->decoder();
assert(decoder);
+ const bool decoder_deletable = index > 0;
+
pv::widgets::DecoderGroupBox *const group =
new pv::widgets::DecoderGroupBox(
- QString::fromUtf8(decoder->name));
+ QString::fromUtf8(decoder->name), nullptr, decoder_deletable);
group->set_decoder_visible(dec->shown());
- _delete_mapper.setMapping(group, index);
- connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map()));
+ if (decoder_deletable) {
+ delete_mapper_.setMapping(group, index);
+ connect(group, SIGNAL(delete_decoder()), &delete_mapper_, SLOT(map()));
+ }
- _show_hide_mapper.setMapping(group, index);
+ show_hide_mapper_.setMapping(group, index);
connect(group, SIGNAL(show_hide_decoder()),
- &_show_hide_mapper, SLOT(map()));
+ &show_hide_mapper_, SLOT(map()));
QFormLayout *const decoder_form = new QFormLayout;
group->add_layout(decoder_form);
// Add the mandatory channels
- for(l = decoder->channels; l; l = l->next) {
+ for (l = decoder->channels; l; l = l->next) {
const struct srd_channel *const pdch =
(struct srd_channel *)l->data;
- QComboBox *const combo = create_probe_selector(parent, dec, pdch);
+ QComboBox *const combo = create_channel_selector(parent, dec, pdch);
connect(combo, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_probe_selected(int)));
+ this, SLOT(on_channel_selected(int)));
decoder_form->addRow(tr("<b>%1</b> (%2) *")
.arg(QString::fromUtf8(pdch->name))
.arg(QString::fromUtf8(pdch->desc)), combo);
- const ProbeSelector s = {combo, dec, pdch};
- _probe_selectors.push_back(s);
+ const ChannelSelector s = {combo, dec, pdch};
+ channel_selectors_.push_back(s);
}
// Add the optional channels
- for(l = decoder->opt_channels; l; l = l->next) {
+ for (l = decoder->opt_channels; l; l = l->next) {
const struct srd_channel *const pdch =
(struct srd_channel *)l->data;
- QComboBox *const combo = create_probe_selector(parent, dec, pdch);
+ QComboBox *const combo = create_channel_selector(parent, dec, pdch);
connect(combo, SIGNAL(currentIndexChanged(int)),
- this, SLOT(on_probe_selected(int)));
+ this, SLOT(on_channel_selected(int)));
decoder_form->addRow(tr("<b>%1</b> (%2)")
.arg(QString::fromUtf8(pdch->name))
.arg(QString::fromUtf8(pdch->desc)), combo);
- const ProbeSelector s = {combo, dec, pdch};
- _probe_selectors.push_back(s);
+ const ChannelSelector s = {combo, dec, pdch};
+ channel_selectors_.push_back(s);
}
// Add the options
- shared_ptr<prop::binding::DecoderOptions> binding(
- new prop::binding::DecoderOptions(_decoder_stack, dec));
+ shared_ptr<binding::Decoder> binding(
+ new binding::Decoder(decoder_stack_, dec));
binding->add_properties_to_form(decoder_form, true);
- _bindings.push_back(binding);
+ bindings_.push_back(binding);
form->addRow(group);
- _decoder_forms.push_back(group);
+ decoder_forms_.push_back(group);
}
-QComboBox* DecodeTrace::create_probe_selector(
+QComboBox* DecodeTrace::create_channel_selector(
QWidget *parent, const shared_ptr<data::decode::Decoder> &dec,
const srd_channel *const pdch)
{
assert(dec);
- const vector< shared_ptr<Signal> > sigs = _session.get_signals();
+ const auto sigs(session_.signals());
+
+ vector< shared_ptr<Signal> > sig_list(sigs.begin(), sigs.end());
+ std::sort(sig_list.begin(), sig_list.end(),
+ [](const shared_ptr<Signal> &a, const shared_ptr<Signal> b) {
+ return a->name().compare(b->name()) < 0; });
- assert(_decoder_stack);
- const map<const srd_channel*,
- shared_ptr<LogicSignal> >::const_iterator probe_iter =
- dec->channels().find(pdch);
+ assert(decoder_stack_);
+ const auto channel_iter = dec->channels().find(pdch);
QComboBox *selector = new QComboBox(parent);
- selector->addItem("-", qVariantFromValue((void*)NULL));
+ selector->addItem("-", qVariantFromValue((void*)nullptr));
- if (probe_iter == dec->channels().end())
+ if (channel_iter == dec->channels().end())
selector->setCurrentIndex(0);
- for(size_t i = 0; i < sigs.size(); i++) {
- const shared_ptr<view::Signal> s(sigs[i]);
+ for (const shared_ptr<view::Signal> &s : sig_list) {
assert(s);
-
- if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled())
- {
- selector->addItem(s->get_name(),
+ if (dynamic_pointer_cast<LogicSignal>(s) && s->enabled()) {
+ selector->addItem(s->name(),
qVariantFromValue((void*)s.get()));
- if ((*probe_iter).second == s)
- selector->setCurrentIndex(i + 1);
+
+ if (channel_iter != dec->channels().end() &&
+ (*channel_iter).second == s)
+ selector->setCurrentIndex(
+ selector->count() - 1);
}
}
return selector;
}
-void DecodeTrace::commit_decoder_probes(shared_ptr<data::decode::Decoder> &dec)
+void DecodeTrace::commit_decoder_channels(shared_ptr<data::decode::Decoder> &dec)
{
assert(dec);
- map<const srd_channel*, shared_ptr<LogicSignal> > probe_map;
- const vector< shared_ptr<Signal> > sigs = _session.get_signals();
+ map<const srd_channel*, shared_ptr<LogicSignal> > channel_map;
+
+ const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
- BOOST_FOREACH(const ProbeSelector &s, _probe_selectors)
- {
- if(s._decoder != dec)
+ for (const ChannelSelector &s : channel_selectors_) {
+ if (s.decoder_ != dec)
break;
const LogicSignal *const selection =
- (LogicSignal*)s._combo->itemData(
- s._combo->currentIndex()).value<void*>();
+ (LogicSignal*)s.combo_->itemData(
+ s.combo_->currentIndex()).value<void*>();
- BOOST_FOREACH(shared_ptr<Signal> sig, sigs)
- if(sig.get() == selection) {
- probe_map[s._pdch] =
+ for (shared_ptr<Signal> sig : sigs)
+ if (sig.get() == selection) {
+ channel_map[s.pdch_] =
dynamic_pointer_cast<LogicSignal>(sig);
break;
}
}
- dec->set_probes(probe_map);
+ dec->set_channels(channel_map);
}
-void DecodeTrace::commit_probes()
+void DecodeTrace::commit_channels()
{
- assert(_decoder_stack);
- BOOST_FOREACH(shared_ptr<data::decode::Decoder> dec,
- _decoder_stack->stack())
- commit_decoder_probes(dec);
+ assert(decoder_stack_);
+ for (shared_ptr<data::decode::Decoder> dec : decoder_stack_->stack())
+ commit_decoder_channels(dec);
- _decoder_stack->begin_decode();
+ decoder_stack_->begin_decode();
}
void DecodeTrace::on_new_decode_data()
{
- if (_view)
- _view->update_viewport();
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
}
void DecodeTrace::delete_pressed()
@@ -669,44 +864,44 @@ void DecodeTrace::delete_pressed()
void DecodeTrace::on_delete()
{
- _session.remove_decode_signal(this);
+ session_.remove_decode_signal(this);
}
-void DecodeTrace::on_probe_selected(int)
+void DecodeTrace::on_channel_selected(int)
{
- commit_probes();
+ commit_channels();
}
void DecodeTrace::on_stack_decoder(srd_decoder *decoder)
{
assert(decoder);
- assert(_decoder_stack);
- _decoder_stack->push(shared_ptr<data::decode::Decoder>(
+ assert(decoder_stack_);
+ decoder_stack_->push(shared_ptr<data::decode::Decoder>(
new data::decode::Decoder(decoder)));
- _decoder_stack->begin_decode();
+ decoder_stack_->begin_decode();
create_popup_form();
}
void DecodeTrace::on_delete_decoder(int index)
{
- _decoder_stack->remove(index);
+ decoder_stack_->remove(index);
// Update the popup
create_popup_form();
- _decoder_stack->begin_decode();
+ decoder_stack_->begin_decode();
}
void DecodeTrace::on_show_hide_decoder(int index)
{
using pv::data::decode::Decoder;
- const list< shared_ptr<Decoder> > stack(_decoder_stack->stack());
+ const list< shared_ptr<Decoder> > stack(decoder_stack_->stack());
// Find the decoder in the stack
- list< shared_ptr<Decoder> >::const_iterator iter = stack.begin();
- for(int i = 0; i < index; i++, iter++)
+ auto iter = stack.cbegin();
+ for (int i = 0; i < index; i++, iter++)
assert(iter != stack.end());
shared_ptr<Decoder> dec = *iter;
@@ -715,10 +910,11 @@ void DecodeTrace::on_show_hide_decoder(int index)
const bool show = !dec->shown();
dec->show(show);
- assert(index < (int)_decoder_forms.size());
- _decoder_forms[index]->set_decoder_visible(show);
+ assert(index < (int)decoder_forms_.size());
+ decoder_forms_[index]->set_decoder_visible(show);
- _view->update_viewport();
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
}
} // namespace view
diff --git a/pv/view/decodetrace.h b/pv/view/decodetrace.h
deleted file mode 100644
index 9a991fc..0000000
--- a/pv/view/decodetrace.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_VIEW_DECODETRACE_H
-#define PULSEVIEW_PV_VIEW_DECODETRACE_H
-
-#include "trace.h"
-
-#include <list>
-#include <map>
-
-#include <QSignalMapper>
-
-#include <boost/shared_ptr.hpp>
-
-#include <pv/prop/binding/decoderoptions.h>
-
-struct srd_channel;
-struct srd_decoder;
-
-class QComboBox;
-
-namespace pv {
-
-class SigSession;
-
-namespace data {
-class DecoderStack;
-
-namespace decode {
-class Annotation;
-class Decoder;
-class Row;
-}
-}
-
-namespace widgets {
-class DecoderGroupBox;
-}
-
-namespace view {
-
-class DecodeTrace : public Trace
-{
- Q_OBJECT
-
-private:
- struct ProbeSelector
- {
- const QComboBox *_combo;
- const boost::shared_ptr<pv::data::decode::Decoder> _decoder;
- const srd_channel *_pdch;
- };
-
-private:
- static const QColor DecodeColours[4];
- static const QColor ErrorBgColour;
- static const QColor NoDecodeColour;
-
- static const int ArrowSize;
- static const double EndCapWidth;
- static const int DrawPadding;
-
- static const QColor Colours[16];
- static const QColor OutlineColours[16];
-
-public:
- DecodeTrace(pv::SigSession &session,
- boost::shared_ptr<pv::data::DecoderStack> decoder_stack,
- int index);
-
- bool enabled() const;
-
- const boost::shared_ptr<pv::data::DecoderStack>& decoder() const;
-
- void set_view(pv::view::View *view);
-
- /**
- * Paints the background layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal.
- * @param right the x-coordinate of the right edge of the signal.
- **/
- void paint_back(QPainter &p, int left, int right);
-
- /**
- * Paints the mid-layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal
- * @param right the x-coordinate of the right edge of the signal
- **/
- void paint_mid(QPainter &p, int left, int right);
-
- /**
- * Paints the foreground layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal
- * @param right the x-coordinate of the right edge of the signal
- **/
- void paint_fore(QPainter &p, int left, int right);
-
- void populate_popup_form(QWidget *parent, QFormLayout *form);
-
- QMenu* create_context_menu(QWidget *parent);
-
- void delete_pressed();
-
-private:
- void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p,
- QColor text_colour, int text_height, int left, int right,
- double samples_per_pixel, double pixels_offset, int y,
- size_t base_colour) const;
-
- void draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
- QColor fill, QColor outline, QColor text_color, int h, double x,
- int y) const;
-
- void draw_range(const pv::data::decode::Annotation &a, QPainter &p,
- QColor fill, QColor outline, QColor text_color, int h, double start,
- double end, int y) const;
-
- void draw_error(QPainter &p, const QString &message,
- int left, int right);
-
- void draw_unresolved_period(QPainter &p, int h, int left,
- int right, double samples_per_pixel, double pixels_offset);
-
- void create_decoder_form(int index,
- boost::shared_ptr<pv::data::decode::Decoder> &dec,
- QWidget *parent, QFormLayout *form);
-
- QComboBox* create_probe_selector(QWidget *parent,
- const boost::shared_ptr<pv::data::decode::Decoder> &dec,
- const srd_channel *const pdch);
-
- void commit_decoder_probes(
- boost::shared_ptr<data::decode::Decoder> &dec);
-
- void commit_probes();
-
-private slots:
- void on_new_decode_data();
-
- void on_delete();
-
- void on_probe_selected(int);
-
- void on_stack_decoder(srd_decoder *decoder);
-
- void on_delete_decoder(int index);
-
- void on_show_hide_decoder(int index);
-
-private:
- pv::SigSession &_session;
- boost::shared_ptr<pv::data::DecoderStack> _decoder_stack;
-
- uint64_t _decode_start, _decode_end;
-
- std::list< boost::shared_ptr<pv::prop::binding::DecoderOptions> >
- _bindings;
-
- std::list<ProbeSelector> _probe_selectors;
- std::vector<pv::widgets::DecoderGroupBox*> _decoder_forms;
-
- std::vector<QString> _cur_row_headings;
-
- QSignalMapper _delete_mapper, _show_hide_mapper;
-};
-
-} // namespace view
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEW_DECODETRACE_H
diff --git a/pv/view/decodetrace.hpp b/pv/view/decodetrace.hpp
new file mode 100644
index 0000000..4964a95
--- /dev/null
+++ b/pv/view/decodetrace.hpp
@@ -0,0 +1,217 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_DECODETRACE_HPP
+#define PULSEVIEW_PV_VIEW_DECODETRACE_HPP
+
+#include "trace.hpp"
+
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <QSignalMapper>
+
+#include <pv/binding/decoder.hpp>
+#include <pv/data/decode/row.hpp>
+
+struct srd_channel;
+struct srd_decoder;
+
+class QComboBox;
+
+namespace pv {
+
+class Session;
+
+namespace data {
+class DecoderStack;
+
+namespace decode {
+class Annotation;
+class Decoder;
+class Row;
+}
+}
+
+namespace widgets {
+class DecoderGroupBox;
+}
+
+namespace view {
+
+class DecodeTrace : public Trace
+{
+ Q_OBJECT
+
+private:
+ struct ChannelSelector
+ {
+ const QComboBox *combo_;
+ const std::shared_ptr<pv::data::decode::Decoder> decoder_;
+ const srd_channel *pdch_;
+ };
+
+private:
+ static const QColor DecodeColours[4];
+ static const QColor ErrorBgColour;
+ static const QColor NoDecodeColour;
+
+ static const int ArrowSize;
+ static const double EndCapWidth;
+ static const int DrawPadding;
+
+ static const QColor Colours[16];
+ static const QColor OutlineColours[16];
+
+public:
+ DecodeTrace(pv::Session &session,
+ std::shared_ptr<pv::data::DecoderStack> decoder_stack,
+ int index);
+
+ bool enabled() const;
+
+ const std::shared_ptr<pv::data::DecoderStack>& decoder() const;
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ std::pair<int, int> v_extents() const;
+
+ /**
+ * Paints the background layer of the trace with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_back(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Paints the mid-layer of the trace with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_mid(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the trace with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, const ViewItemPaintParams &pp);
+
+ void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+ QMenu* create_context_menu(QWidget *parent);
+
+ void delete_pressed();
+
+private:
+ void draw_annotations(std::vector<pv::data::decode::Annotation> annotations,
+ QPainter &p, int h, const ViewItemPaintParams &pp, int y,
+ size_t base_colour);
+
+ void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p,
+ int text_height, const ViewItemPaintParams &pp, int y,
+ size_t base_colour) const;
+
+ void draw_annotation_block(std::vector<pv::data::decode::Annotation> a,
+ QPainter &p, int h, int y, size_t base_colour) const;
+
+ void draw_instant(const pv::data::decode::Annotation &a, QPainter &p,
+ QColor fill, QColor outline, int h, double x, int y) const;
+
+ void draw_range(const pv::data::decode::Annotation &a, QPainter &p,
+ QColor fill, QColor outline, int h, double start,
+ double end, int y) const;
+
+ void draw_error(QPainter &p, const QString &message,
+ const ViewItemPaintParams &pp);
+
+ void draw_unresolved_period(QPainter &p, int h, int left,
+ int right) const;
+
+ std::pair<double, double> get_pixels_offset_samples_per_pixel() const;
+
+ /**
+ * Determines the start and end sample for a given pixel range.
+ * @param x_start the X coordinate of the start sample in the view
+ * @param x_end the X coordinate of the end sample in the view
+ * @return Returns a pair containing the start sample and the end
+ * sample that correspond to the start and end coordinates.
+ */
+ std::pair<uint64_t, uint64_t> get_sample_range(int x_start, int x_end) const;
+
+ int get_row_at_point(const QPoint &point);
+
+ const QString get_annotation_at_point(const QPoint &point);
+
+ void create_decoder_form(int index,
+ std::shared_ptr<pv::data::decode::Decoder> &dec,
+ QWidget *parent, QFormLayout *form);
+
+ QComboBox* create_channel_selector(QWidget *parent,
+ const std::shared_ptr<pv::data::decode::Decoder> &dec,
+ const srd_channel *const pdch);
+
+ void commit_decoder_channels(
+ std::shared_ptr<data::decode::Decoder> &dec);
+
+ void commit_channels();
+
+public:
+ void hover_point_changed();
+
+private Q_SLOTS:
+ void on_new_decode_data();
+
+ void on_delete();
+
+ void on_channel_selected(int);
+
+ void on_stack_decoder(srd_decoder *decoder);
+
+ void on_delete_decoder(int index);
+
+ void on_show_hide_decoder(int index);
+
+private:
+ pv::Session &session_;
+ std::shared_ptr<pv::data::DecoderStack> decoder_stack_;
+
+ uint64_t decode_start_, decode_end_;
+
+ std::list< std::shared_ptr<pv::binding::Decoder> >
+ bindings_;
+
+ std::list<ChannelSelector> channel_selectors_;
+ std::vector<pv::widgets::DecoderGroupBox*> decoder_forms_;
+
+ std::vector<data::decode::Row> visible_rows_;
+ int row_height_, max_visible_rows_;
+
+ QSignalMapper delete_mapper_, show_hide_mapper_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_DECODETRACE_HPP
diff --git a/pv/view/flag.cpp b/pv/view/flag.cpp
new file mode 100644
index 0000000..779ea2e
--- /dev/null
+++ b/pv/view/flag.cpp
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "timemarker.hpp"
+#include "view.hpp"
+
+#include <QColor>
+#include <QFormLayout>
+#include <QLineEdit>
+#include <QMenu>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <pv/widgets/popup.hpp>
+
+using std::shared_ptr;
+
+namespace pv {
+namespace view {
+
+const QColor Flag::FillColour(0x73, 0xD2, 0x16);
+
+Flag::Flag(View &view, const pv::util::Timestamp& time, const QString &text) :
+ TimeMarker(view, FillColour, time),
+ text_(text)
+{
+}
+
+Flag::Flag(const Flag &flag) :
+ TimeMarker(flag.view_, FillColour, flag.time_),
+ std::enable_shared_from_this<pv::view::Flag>(flag)
+{
+}
+
+bool Flag::enabled() const
+{
+ return true;
+}
+
+QString Flag::get_text() const
+{
+ return text_;
+}
+
+pv::widgets::Popup* Flag::create_popup(QWidget *parent)
+{
+ using pv::widgets::Popup;
+
+ Popup *const popup = TimeMarker::create_popup(parent);
+ popup->set_position(parent->mapToGlobal(
+ point(parent->rect())), Popup::Bottom);
+
+ QFormLayout *const form = (QFormLayout*)popup->layout();
+
+ QLineEdit *const text_edit = new QLineEdit(popup);
+ text_edit->setText(text_);
+
+ connect(text_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(on_text_changed(const QString&)));
+
+ form->insertRow(0, tr("Text"), text_edit);
+
+ return popup;
+}
+
+QMenu* Flag::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = new QMenu(parent);
+
+ QAction *const del = new QAction(tr("Delete"), this);
+ del->setShortcuts(QKeySequence::Delete);
+ connect(del, SIGNAL(triggered()), this, SLOT(on_delete()));
+ menu->addAction(del);
+
+ return menu;
+}
+
+void Flag::delete_pressed()
+{
+ on_delete();
+}
+
+void Flag::on_delete()
+{
+ view_.remove_flag(shared_ptr<Flag>(shared_from_this()));
+}
+
+void Flag::on_text_changed(const QString &text)
+{
+ text_ = text;
+ view_.time_item_appearance_changed(true, false);
+}
+
+} // namespace view
+} // namespace pv
diff --git a/pv/view/cursorpair.h b/pv/view/flag.hpp
similarity index 50%
rename from pv/view/cursorpair.h
rename to pv/view/flag.hpp
index 815276e..bc82934 100644
--- a/pv/view/cursorpair.h
+++ b/pv/view/flag.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,64 +18,66 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_CURSORPAIR_H
-#define PULSEVIEW_PV_VIEW_CURSORPAIR_H
+#ifndef PULSEVIEW_PV_VIEW_FLAG_HPP
+#define PULSEVIEW_PV_VIEW_FLAG_HPP
-#include "cursor.h"
+#include <memory>
-#include <boost/shared_ptr.hpp>
+#include "timemarker.hpp"
-#include <QPainter>
-
-class QPainter;
+class QMenu;
namespace pv {
namespace view {
-class CursorPair
+class Flag : public TimeMarker,
+ public std::enable_shared_from_this<pv::view::Flag>
{
-private:
- static const int DeltaPadding;
+ Q_OBJECT
+
+public:
+ static const QColor FillColour;
public:
/**
* Constructor.
* @param view A reference to the view that owns this cursor pair.
+ * @param time The time to set the flag to.
+ * @param text The text of the marker.
*/
- CursorPair(View &view);
+ Flag(View &view, const pv::util::Timestamp& time, const QString &text);
/**
- * Returns a pointer to the first cursor.
+ * Copy constructor.
*/
- boost::shared_ptr<Cursor> first() const;
+ Flag(const Flag &flag);
/**
- * Returns a pointer to the second cursor.
+ * Returns true if the item is visible and enabled.
*/
- boost::shared_ptr<Cursor> second() const;
+ bool enabled() const;
-public:
- QRectF get_label_rect(const QRect &rect) const;
+ /**
+ * Gets the text to show in the marker.
+ */
+ QString get_text() const;
- void draw_markers(QPainter &p,
- const QRect &rect, unsigned int prefix);
+ pv::widgets::Popup* create_popup(QWidget *parent);
- void draw_viewport_background(QPainter &p, const QRect &rect);
+ QMenu* create_context_menu(QWidget *parent);
- void draw_viewport_foreground(QPainter &p, const QRect &rect);
+ void delete_pressed();
- void compute_text_size(QPainter &p, unsigned int prefix);
+private Q_SLOTS:
+ void on_delete();
- std::pair<float, float> get_cursor_offsets() const;
+ void on_text_changed(const QString &text);
private:
- boost::shared_ptr<Cursor> _first, _second;
- const View &_view;
-
- QSizeF _text_size;
+ QString text_;
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_CURSORPAIR_H
+#endif // PULSEVIEW_PV_VIEW_FLAG_HPP
diff --git a/pv/view/header.cpp b/pv/view/header.cpp
index a790252..1977d05 100644
--- a/pv/view/header.cpp
+++ b/pv/view/header.cpp
@@ -18,15 +18,16 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "header.h"
-#include "view.h"
+#include "header.hpp"
+#include "view.hpp"
-#include "signal.h"
-#include "../sigsession.h"
+#include "signal.hpp"
+#include "tracegroup.hpp"
-#include <assert.h>
+#include <cassert>
+#include <algorithm>
-#include <boost/foreach.hpp>
+#include <boost/iterator/filter_iterator.hpp>
#include <QApplication>
#include <QMenu>
@@ -34,270 +35,187 @@
#include <QPainter>
#include <QRect>
-#include <pv/widgets/popup.h>
+#include <pv/session.hpp>
+#include <pv/widgets/popup.hpp>
-using boost::shared_ptr;
+using boost::make_filter_iterator;
+using std::dynamic_pointer_cast;
using std::max;
using std::make_pair;
+using std::min;
using std::pair;
+using std::shared_ptr;
+using std::stable_sort;
using std::vector;
namespace pv {
namespace view {
const int Header::Padding = 12;
+const int Header::BaselineOffset = 5;
-Header::Header(View &parent) :
- MarginWidget(parent),
- _dragging(false)
+static bool item_selected(shared_ptr<TraceTreeItem> r)
{
- setFocusPolicy(Qt::ClickFocus);
- setMouseTracking(true);
-
- connect(&_view.session(), SIGNAL(signals_changed()),
- this, SLOT(on_signals_changed()));
-
- connect(&_view, SIGNAL(signals_moved()),
- this, SLOT(on_signals_moved()));
+ return r->selected();
+}
- // Trigger the initial event manually. The default device has signals
- // which were created before this object came into being
- on_signals_changed();
+Header::Header(View &parent) :
+ MarginWidget(parent)
+{
}
QSize Header::sizeHint() const
{
- int max_width = 0;
-
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
- BOOST_FOREACH(shared_ptr<Trace> t, traces) {
- assert(t);
- max_width = max(max_width, (int)t->get_label_rect(0).width());
- }
-
- return QSize(max_width + Padding, 0);
+ QRectF max_rect(-Padding, 0, Padding, 0);
+ const vector<shared_ptr<TraceTreeItem>> items(
+ view_.list_by_type<TraceTreeItem>());
+ for (auto &i : items)
+ if (i->enabled())
+ max_rect = max_rect.united(i->label_rect(QRect()));
+ return QSize(max_rect.width() + Padding + BaselineOffset, 0);
}
-shared_ptr<Trace> Header::get_mouse_over_trace(const QPoint &pt)
+QSize Header::extended_size_hint() const
{
- const int w = width();
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
-
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- {
- assert(t);
- if (t->pt_in_label_rect(0, w, pt))
- return t;
- }
-
- return shared_ptr<Trace>();
+ return sizeHint() + QSize(ViewItem::HighlightRadius, 0);
}
-void Header::clear_selection()
+vector< shared_ptr<ViewItem> > Header::items()
{
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
- BOOST_FOREACH(const shared_ptr<Trace> t, traces) {
- assert(t);
- t->select(false);
- }
-
- update();
+ const vector<shared_ptr<TraceTreeItem>> items(
+ view_.list_by_type<TraceTreeItem>());
+ return vector< shared_ptr<ViewItem> >(items.begin(), items.end());
}
-void Header::paintEvent(QPaintEvent*)
+shared_ptr<ViewItem> Header::get_mouse_over_item(const QPoint &pt)
{
- const int w = width();
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
-
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
-
- const bool dragging = !_drag_traces.empty();
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- {
- assert(t);
-
- const bool highlight = !dragging && t->pt_in_label_rect(
- 0, w, _mouse_point);
- t->paint_label(painter, w, highlight);
- }
-
- painter.end();
+ const QRect r(0, 0, width() - BaselineOffset, height());
+ const vector<shared_ptr<TraceTreeItem>> items(
+ view_.list_by_type<TraceTreeItem>());
+ for (auto i = items.rbegin(); i != items.rend(); i++)
+ if ((*i)->enabled() && (*i)->label_rect(r).contains(pt))
+ return *i;
+ return shared_ptr<TraceTreeItem>();
}
-void Header::mousePressEvent(QMouseEvent *event)
+void Header::paintEvent(QPaintEvent*)
{
- assert(event);
+ // The trace labels are not drawn with the arrows exactly on the
+ // left edge of the widget, because then the selection shadow
+ // would be clipped away.
+ const QRect rect(0, 0, width() - BaselineOffset, height());
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
+ vector< shared_ptr<RowItem> > items(
+ view_.list_by_type<RowItem>());
- if (event->button() & Qt::LeftButton) {
- _mouse_down_point = event->pos();
+ stable_sort(items.begin(), items.end(),
+ [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
+ return a->point(QRect()).y() < b->point(QRect()).y(); });
- // Save the offsets of any signals which will be dragged
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- if (t->selected())
- _drag_traces.push_back(
- make_pair(t, t->get_v_offset()));
- }
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
- // Select the signal if it has been clicked
- const shared_ptr<Trace> mouse_over_trace =
- get_mouse_over_trace(event->pos());
- if (mouse_over_trace) {
- if (mouse_over_trace->selected())
- mouse_over_trace->select(false);
- else {
- mouse_over_trace->select(true);
-
- if (~QApplication::keyboardModifiers() &
- Qt::ControlModifier)
- _drag_traces.clear();
-
- // Add the signal to the drag list
- if (event->button() & Qt::LeftButton)
- _drag_traces.push_back(
- make_pair(mouse_over_trace,
- mouse_over_trace->get_v_offset()));
- }
- }
+ for (const shared_ptr<RowItem> r : items) {
+ assert(r);
- if (~QApplication::keyboardModifiers() & Qt::ControlModifier) {
- // Unselect all other signals because the Ctrl is not
- // pressed
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- if (t != mouse_over_trace)
- t->select(false);
+ const bool highlight = !item_dragging_ &&
+ r->label_rect(rect).contains(mouse_point_);
+ r->paint_label(painter, rect, highlight);
}
- selection_changed();
- update();
-}
-
-void Header::mouseReleaseEvent(QMouseEvent *event)
-{
- using pv::widgets::Popup;
-
- assert(event);
- if (event->button() == Qt::LeftButton) {
- if (_dragging)
- _view.normalize_layout();
- else
- {
- const shared_ptr<Trace> mouse_over_trace =
- get_mouse_over_trace(event->pos());
- if (mouse_over_trace) {
- Popup *const p =
- mouse_over_trace->create_popup(&_view);
- p->set_position(mapToGlobal(QPoint(width(),
- mouse_over_trace->get_y())),
- Popup::Right);
- p->show();
- }
- }
-
- _dragging = false;
- _drag_traces.clear();
- }
+ painter.end();
}
-void Header::mouseMoveEvent(QMouseEvent *event)
+void Header::contextMenuEvent(QContextMenuEvent *event)
{
- assert(event);
- _mouse_point = event->pos();
-
- if (!(event->buttons() & Qt::LeftButton))
+ const shared_ptr<ViewItem> r = get_mouse_over_item(mouse_point_);
+ if (!r)
return;
- if ((event->pos() - _mouse_down_point).manhattanLength() <
- QApplication::startDragDistance())
- return;
+ QMenu *menu = r->create_context_menu(this);
+ if (!menu)
+ menu = new QMenu(this);
- // Move the signals if we are dragging
- if (!_drag_traces.empty())
+ const vector< shared_ptr<TraceTreeItem> > items(
+ view_.list_by_type<TraceTreeItem>());
+ if (std::count_if(items.begin(), items.end(), item_selected) > 1)
{
- _dragging = true;
-
- const int delta = event->pos().y() - _mouse_down_point.y();
-
- for (std::list<std::pair<boost::weak_ptr<Trace>,
- int> >::iterator i = _drag_traces.begin();
- i != _drag_traces.end(); i++) {
- const boost::shared_ptr<Trace> trace((*i).first);
- if (trace) {
- const int y = (*i).second + delta;
- const int y_snap =
- ((y + View::SignalSnapGridSize / 2) /
- View::SignalSnapGridSize) *
- View::SignalSnapGridSize;
- trace->set_v_offset(y_snap);
-
- // Ensure the trace is selected
- trace->select();
- }
-
- }
-
- signals_moved();
+ menu->addSeparator();
+
+ QAction *const group = new QAction(tr("Group"), this);
+ QList<QKeySequence> shortcuts;
+ shortcuts.append(QKeySequence(Qt::ControlModifier | Qt::Key_G));
+ group->setShortcuts(shortcuts);
+ connect(group, SIGNAL(triggered()), this, SLOT(on_group()));
+ menu->addAction(group);
}
- update();
-}
-
-void Header::leaveEvent(QEvent*)
-{
- _mouse_point = QPoint(-1, -1);
- update();
-}
-
-void Header::contextMenuEvent(QContextMenuEvent *event)
-{
- const shared_ptr<Trace> t = get_mouse_over_trace(_mouse_point);
-
- if (t)
- t->create_context_menu(this)->exec(event->globalPos());
+ menu->exec(event->globalPos());
}
void Header::keyPressEvent(QKeyEvent *e)
{
assert(e);
- switch (e->key())
- {
- case Qt::Key_Delete:
- {
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- if (t->selected())
- t->delete_pressed();
- break;
- }
- }
-}
+ MarginWidget::keyPressEvent(e);
-void Header::on_signals_changed()
-{
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
- BOOST_FOREACH(shared_ptr<Trace> t, traces) {
- assert(t);
- connect(t.get(), SIGNAL(visibility_changed()),
- this, SLOT(update()));
- connect(t.get(), SIGNAL(text_changed()),
- this, SLOT(on_trace_text_changed()));
- connect(t.get(), SIGNAL(colour_changed()),
- this, SLOT(update()));
- }
+ if (e->key() == Qt::Key_G && e->modifiers() == Qt::ControlModifier)
+ on_group();
+ else if (e->key() == Qt::Key_U && e->modifiers() == Qt::ControlModifier)
+ on_ungroup();
}
-void Header::on_signals_moved()
+void Header::on_group()
{
- update();
+ const vector< shared_ptr<TraceTreeItem> > items(
+ view_.list_by_type<TraceTreeItem>());
+ vector< shared_ptr<TraceTreeItem> > selected_items(
+ make_filter_iterator(item_selected, items.begin(), items.end()),
+ make_filter_iterator(item_selected, items.end(), items.end()));
+ stable_sort(selected_items.begin(), selected_items.end(),
+ [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
+ return a->visual_v_offset() < b->visual_v_offset(); });
+
+ shared_ptr<TraceGroup> group(new TraceGroup());
+ shared_ptr<TraceTreeItem> mouse_down_item(
+ std::dynamic_pointer_cast<TraceTreeItem>(mouse_down_item_));
+ shared_ptr<TraceTreeItem> focus_item(
+ mouse_down_item ? mouse_down_item : selected_items.front());
+
+ assert(focus_item);
+ assert(focus_item->owner());
+ focus_item->owner()->add_child_item(group);
+
+ // Set the group v_offset here before reparenting
+ group->force_to_v_offset(focus_item->layout_v_offset() +
+ focus_item->v_extents().first);
+
+ for (size_t i = 0; i < selected_items.size(); i++) {
+ const shared_ptr<TraceTreeItem> &r = selected_items[i];
+ assert(r->owner());
+ r->owner()->remove_child_item(r);
+ group->add_child_item(r);
+
+ // Put the items at 1-pixel offsets, so that restack will
+ // stack them in the right order
+ r->set_layout_v_offset(i);
+ }
}
-void Header::on_trace_text_changed()
+void Header::on_ungroup()
{
- update();
- geometry_updated();
+ bool restart;
+ do {
+ restart = false;
+ const vector< shared_ptr<TraceGroup> > groups(
+ view_.list_by_type<TraceGroup>());
+ for (const shared_ptr<TraceGroup> tg : groups)
+ if (tg->selected()) {
+ tg->ungroup();
+ restart = true;
+ break;
+ }
+ } while (restart);
}
} // namespace view
diff --git a/pv/view/header.h b/pv/view/header.hpp
similarity index 56%
rename from pv/view/header.h
rename to pv/view/header.hpp
index 5474f10..ade6d33 100644
--- a/pv/view/header.h
+++ b/pv/view/header.hpp
@@ -18,22 +18,21 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_HEADER_H
-#define PULSEVIEW_PV_VIEW_HEADER_H
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
+#ifndef PULSEVIEW_PV_VIEW_HEADER_HPP
+#define PULSEVIEW_PV_VIEW_HEADER_HPP
#include <list>
+#include <memory>
#include <utility>
-#include "marginwidget.h"
+#include "marginwidget.hpp"
namespace pv {
namespace view {
-class Trace;
+class TraceTreeItem;
class View;
+class ViewItem;
class Header : public MarginWidget
{
@@ -47,48 +46,49 @@ public:
QSize sizeHint() const;
+ /**
+ * The extended area that the header widget would like to be sized to.
+ * @remarks This area is the area specified by sizeHint, extended by
+ * the area to overlap the viewport.
+ */
+ QSize extended_size_hint() const;
+
+ /**
+ * The horizontal offset, relative to the left edge of the widget,
+ * where the arrows of the trace labels end.
+ */
+ static const int BaselineOffset;
+
private:
- boost::shared_ptr<pv::view::Trace> get_mouse_over_trace(
+ /**
+ * Gets the row items.
+ */
+ std::vector< std::shared_ptr<pv::view::ViewItem> > items();
+
+ /**
+ * Gets the first view item which has a label that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ std::shared_ptr<pv::view::ViewItem> get_mouse_over_item(
const QPoint &pt);
- void clear_selection();
-
private:
void paintEvent(QPaintEvent *event);
private:
- void mousePressEvent(QMouseEvent * event);
-
- void mouseReleaseEvent(QMouseEvent *event);
-
- void mouseMoveEvent(QMouseEvent *event);
-
- void leaveEvent(QEvent *event);
-
void contextMenuEvent(QContextMenuEvent *event);
void keyPressEvent(QKeyEvent *e);
-private slots:
- void on_signals_changed();
-
- void on_signals_moved();
-
- void on_trace_text_changed();
-
-signals:
- void signals_moved();
-
-private:
- QPoint _mouse_point;
- QPoint _mouse_down_point;
- bool _dragging;
+private Q_SLOTS:
+ void on_group();
- std::list<std::pair<boost::weak_ptr<Trace>, int> >
- _drag_traces;
+ void on_ungroup();
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_HEADER_H
+#endif // PULSEVIEW_PV_VIEW_HEADER_HPP
diff --git a/pv/view/logicsignal.cpp b/pv/view/logicsignal.cpp
index 0b44264..78bfdec 100644
--- a/pv/view/logicsignal.cpp
+++ b/pv/view/logicsignal.cpp
@@ -20,27 +20,44 @@
#include <extdef.h>
-#include <math.h>
+#include <cassert>
+#include <cmath>
+#include <algorithm>
+
+#include <QApplication>
#include <QFormLayout>
#include <QToolBar>
-#include "logicsignal.h"
-#include "view.h"
+#include "logicsignal.hpp"
+#include "view.hpp"
+
+#include <pv/session.hpp>
+#include <pv/devicemanager.hpp>
+#include <pv/devices/device.hpp>
+#include <pv/data/logic.hpp>
+#include <pv/data/logicsegment.hpp>
+#include <pv/view/view.hpp>
-#include <pv/sigsession.h>
-#include <pv/device/devinst.h>
-#include <pv/data/logic.h>
-#include <pv/data/logicsnapshot.h>
-#include <pv/view/view.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
-using boost::shared_ptr;
using std::deque;
using std::max;
+using std::make_pair;
using std::min;
using std::pair;
+using std::shared_ptr;
using std::vector;
+using sigrok::Channel;
+using sigrok::ConfigKey;
+using sigrok::Capability;
+using sigrok::Error;
+using sigrok::Trigger;
+using sigrok::TriggerStage;
+using sigrok::TriggerMatch;
+using sigrok::TriggerMatchType;
+
namespace pv {
namespace view {
@@ -63,18 +80,50 @@ const QColor LogicSignal::SignalColours[10] = {
QColor(0xEE, 0xEE, 0xEC), // White
};
-LogicSignal::LogicSignal(shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel *const probe, shared_ptr<data::Logic> data) :
- Signal(dev_inst, probe),
- _data(data),
- _trigger_none(NULL),
- _trigger_rising(NULL),
- _trigger_high(NULL),
- _trigger_falling(NULL),
- _trigger_low(NULL),
- _trigger_change(NULL)
+QColor LogicSignal::TriggerMarkerBackgroundColour = QColor(0xED, 0xD4, 0x00);
+const int LogicSignal::TriggerMarkerPadding = 2;
+const char* LogicSignal::TriggerMarkerIcons[8] = {
+ nullptr,
+ ":/icons/trigger-marker-low.svg",
+ ":/icons/trigger-marker-high.svg",
+ ":/icons/trigger-marker-rising.svg",
+ ":/icons/trigger-marker-falling.svg",
+ ":/icons/trigger-marker-change.svg",
+ nullptr,
+ nullptr
+};
+
+QCache<QString, const QIcon> LogicSignal::icon_cache_;
+QCache<QString, const QPixmap> LogicSignal::pixmap_cache_;
+
+LogicSignal::LogicSignal(
+ pv::Session &session,
+ shared_ptr<devices::Device> device,
+ shared_ptr<Channel> channel,
+ shared_ptr<data::Logic> data) :
+ Signal(session, channel),
+ signal_height_(QFontMetrics(QApplication::font()).height() * 2),
+ device_(device),
+ data_(data),
+ trigger_none_(nullptr),
+ trigger_rising_(nullptr),
+ trigger_high_(nullptr),
+ trigger_falling_(nullptr),
+ trigger_low_(nullptr),
+ trigger_change_(nullptr)
{
- _colour = SignalColours[probe->index % countof(SignalColours)];
+ shared_ptr<Trigger> trigger;
+
+ set_colour(SignalColours[channel->index() % countof(SignalColours)]);
+
+ /* Populate this channel's trigger setting with whatever we
+ * find in the current session trigger, if anything. */
+ trigger_match_ = nullptr;
+ if ((trigger = session_.session()->trigger()))
+ for (auto stage : trigger->stages())
+ for (auto match : stage->matches())
+ if (match->channel() == channel_)
+ trigger_match_ = match->type();
}
LogicSignal::~LogicSignal()
@@ -83,71 +132,84 @@ LogicSignal::~LogicSignal()
shared_ptr<pv::data::SignalData> LogicSignal::data() const
{
- return _data;
+ return data_;
}
shared_ptr<pv::data::Logic> LogicSignal::logic_data() const
{
- return _data;
+ return data_;
+}
+
+void LogicSignal::set_logic_data(std::shared_ptr<pv::data::Logic> data)
+{
+ data_ = data;
}
-void LogicSignal::paint_back(QPainter &p, int left, int right)
+std::pair<int, int> LogicSignal::v_extents() const
{
- if (_probe->enabled)
- paint_axis(p, get_y(), left, right);
+ const int signal_margin =
+ QFontMetrics(QApplication::font()).height() / 2;
+ return make_pair(-signal_height_ - signal_margin, signal_margin);
}
-void LogicSignal::paint_mid(QPainter &p, int left, int right)
+int LogicSignal::scale_handle_offset() const
{
- using pv::view::View;
+ return -signal_height_;
+}
+void LogicSignal::scale_handle_dragged(int offset)
+{
+ const int font_height = QFontMetrics(QApplication::font()).height();
+ const int units = (-offset / font_height);
+ signal_height_ = ((units < 1) ? 1 : units) * font_height;
+}
+
+void LogicSignal::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
+{
QLineF *line;
vector< pair<int64_t, bool> > edges;
- assert(_probe);
- assert(_data);
- assert(right >= left);
+ assert(channel_);
+ assert(data_);
+ assert(owner_);
- assert(_view);
- const int y = _v_offset - _view->v_offset();
-
- const double scale = _view->scale();
- assert(scale > 0);
-
- const double offset = _view->offset();
+ const int y = get_visual_y();
- if (!_probe->enabled)
+ if (!channel_->enabled())
return;
- const float high_offset = y - View::SignalHeight + 0.5f;
+ const float high_offset = y - signal_height_ + 0.5f;
const float low_offset = y + 0.5f;
- const deque< shared_ptr<pv::data::LogicSnapshot> > &snapshots =
- _data->get_snapshots();
- if (snapshots.empty())
+ const deque< shared_ptr<pv::data::LogicSegment> > &segments =
+ data_->logic_segments();
+ if (segments.empty())
return;
- const shared_ptr<pv::data::LogicSnapshot> &snapshot =
- snapshots.front();
+ const shared_ptr<pv::data::LogicSegment> &segment =
+ segments.front();
- double samplerate = _data->samplerate();
+ double samplerate = segment->samplerate();
// Show sample rate as 1Hz when it is unknown
if (samplerate == 0.0)
samplerate = 1.0;
- const double pixels_offset = offset / scale;
- const double start_time = _data->get_start_time();
- const int64_t last_sample = snapshot->get_sample_count() - 1;
- const double samples_per_pixel = samplerate * scale;
- const double start = samplerate * (offset - start_time);
- const double end = start + samples_per_pixel * (right - left);
-
- snapshot->get_subsampled_edges(edges,
- min(max((int64_t)floor(start), (int64_t)0), last_sample),
- min(max((int64_t)ceil(end), (int64_t)0), last_sample),
- samples_per_pixel / Oversampling, _probe->index);
+ const double pixels_offset = pp.pixels_offset();
+ const pv::util::Timestamp& start_time = segment->start_time();
+ const int64_t last_sample = segment->get_sample_count() - 1;
+ const double samples_per_pixel = samplerate * pp.scale();
+ const pv::util::Timestamp start = samplerate * (pp.offset() - start_time);
+ const pv::util::Timestamp end = start + samples_per_pixel * pp.width();
+
+ const int64_t start_sample = min(max(floor(start).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+ const uint64_t end_sample = min(max(ceil(end).convert_to<int64_t>(),
+ (int64_t)0), last_sample);
+
+ segment->get_subsampled_edges(edges, start_sample, end_sample,
+ samples_per_pixel / Oversampling, channel_->index());
assert(edges.size() >= 2);
// Paint the edges
@@ -155,11 +217,9 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right)
QLineF *const edge_lines = new QLineF[edge_count];
line = edge_lines;
- for (vector<pv::data::LogicSnapshot::EdgePair>::const_iterator i =
- edges.begin() + 1;
- i != edges.end() - 1; i++) {
+ for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
const float x = ((*i).first / samples_per_pixel -
- pixels_offset) + left;
+ pixels_offset) + pp.left();
*line++ = QLineF(x, high_offset, x, low_offset);
}
@@ -173,14 +233,51 @@ void LogicSignal::paint_mid(QPainter &p, int left, int right)
p.setPen(HighColour);
paint_caps(p, cap_lines, edges, true, samples_per_pixel,
- pixels_offset, left, high_offset);
+ pixels_offset, pp.left(), high_offset);
p.setPen(LowColour);
paint_caps(p, cap_lines, edges, false, samples_per_pixel,
- pixels_offset, left, low_offset);
+ pixels_offset, pp.left(), low_offset);
delete[] cap_lines;
}
+void LogicSignal::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
+{
+ // Draw the trigger marker
+ if (!trigger_match_ || !channel_->enabled())
+ return;
+
+ const int y = get_visual_y();
+ const vector<int32_t> trig_types = get_trigger_types();
+ for (int32_t type_id : trig_types) {
+ const TriggerMatchType *const type =
+ TriggerMatchType::get(type_id);
+ if (trigger_match_ != type || type_id < 0 ||
+ (size_t)type_id >= countof(TriggerMarkerIcons) ||
+ !TriggerMarkerIcons[type_id])
+ continue;
+
+ const QPixmap *const pixmap = get_pixmap(
+ TriggerMarkerIcons[type_id]);
+ if (!pixmap)
+ continue;
+
+ const float pad = TriggerMarkerPadding - 0.5f;
+ const QSize size = pixmap->size();
+ const QPoint point(
+ pp.right() - size.width() - pad * 2,
+ y - (signal_height_ + size.height()) / 2);
+
+ p.setPen(QPen(TriggerMarkerBackgroundColour.darker()));
+ p.setBrush(TriggerMarkerBackgroundColour);
+ p.drawRoundedRect(QRectF(point, size).adjusted(
+ -pad, -pad, pad, pad), pad, pad);
+ p.drawPixmap(point, *pixmap);
+
+ break;
+ }
+}
+
void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
vector< pair<int64_t, bool> > &edges, bool level,
double samples_per_pixel, double pixels_offset, float x_offset,
@@ -188,8 +285,7 @@ void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
{
QLineF *line = lines;
- for (vector<pv::data::LogicSnapshot::EdgePair>::const_iterator i =
- edges.begin(); i != (edges.end() - 1); i++)
+ for (auto i = edges.begin(); i != (edges.end() - 1); i++)
if ((*i).second == level) {
*line++ = QLineF(
((*i).first / samples_per_pixel -
@@ -203,150 +299,191 @@ void LogicSignal::paint_caps(QPainter &p, QLineF *const lines,
void LogicSignal::init_trigger_actions(QWidget *parent)
{
- _trigger_none = new QAction(QIcon(":/icons/trigger-none.svg"),
+ trigger_none_ = new QAction(*get_icon(":/icons/trigger-none.svg"),
tr("No trigger"), parent);
- _trigger_none->setCheckable(true);
- connect(_trigger_none, SIGNAL(triggered()),
- this, SLOT(on_trigger_none()));
+ trigger_none_->setCheckable(true);
+ connect(trigger_none_, SIGNAL(triggered()), this, SLOT(on_trigger()));
- _trigger_rising = new QAction(QIcon(":/icons/trigger-rising.svg"),
+ trigger_rising_ = new QAction(*get_icon(":/icons/trigger-rising.svg"),
tr("Trigger on rising edge"), parent);
- _trigger_rising->setCheckable(true);
- connect(_trigger_rising, SIGNAL(triggered()),
- this, SLOT(on_trigger_rising()));
+ trigger_rising_->setCheckable(true);
+ connect(trigger_rising_, SIGNAL(triggered()), this, SLOT(on_trigger()));
- _trigger_high = new QAction(QIcon(":/icons/trigger-high.svg"),
+ trigger_high_ = new QAction(*get_icon(":/icons/trigger-high.svg"),
tr("Trigger on high level"), parent);
- _trigger_high->setCheckable(true);
- connect(_trigger_high, SIGNAL(triggered()),
- this, SLOT(on_trigger_high()));
+ trigger_high_->setCheckable(true);
+ connect(trigger_high_, SIGNAL(triggered()), this, SLOT(on_trigger()));
- _trigger_falling = new QAction(QIcon(":/icons/trigger-falling.svg"),
+ trigger_falling_ = new QAction(*get_icon(":/icons/trigger-falling.svg"),
tr("Trigger on falling edge"), parent);
- _trigger_falling->setCheckable(true);
- connect(_trigger_falling, SIGNAL(triggered()),
- this, SLOT(on_trigger_falling()));
+ trigger_falling_->setCheckable(true);
+ connect(trigger_falling_, SIGNAL(triggered()), this, SLOT(on_trigger()));
- _trigger_low = new QAction(QIcon(":/icons/trigger-low.svg"),
+ trigger_low_ = new QAction(*get_icon(":/icons/trigger-low.svg"),
tr("Trigger on low level"), parent);
- _trigger_low->setCheckable(true);
- connect(_trigger_low, SIGNAL(triggered()),
- this, SLOT(on_trigger_low()));
+ trigger_low_->setCheckable(true);
+ connect(trigger_low_, SIGNAL(triggered()), this, SLOT(on_trigger()));
- _trigger_change = new QAction(QIcon(":/icons/trigger-change.svg"),
+ trigger_change_ = new QAction(*get_icon(":/icons/trigger-change.svg"),
tr("Trigger on rising or falling edge"), parent);
- _trigger_change->setCheckable(true);
- connect(_trigger_change, SIGNAL(triggered()),
- this, SLOT(on_trigger_change()));
+ trigger_change_->setCheckable(true);
+ connect(trigger_change_, SIGNAL(triggered()), this, SLOT(on_trigger()));
}
-void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
+const vector<int32_t> LogicSignal::get_trigger_types() const
{
- GVariant *gvar;
-
- Signal::populate_popup_form(parent, form);
-
- // Add the trigger actions
- assert(_dev_inst);
- if ((gvar = _dev_inst->list_config(NULL, SR_CONF_TRIGGER_TYPE)))
- {
- const char *const trig_types =
- g_variant_get_string(gvar, NULL);
-
- if (trig_types && trig_types[0] != '\0')
- {
- _trigger_bar = new QToolBar(parent);
-
- init_trigger_actions(_trigger_bar);
- _trigger_bar->addAction(_trigger_none);
- add_trigger_action(trig_types, 'r', _trigger_rising);
- add_trigger_action(trig_types, '1', _trigger_high);
- add_trigger_action(trig_types, 'f', _trigger_falling);
- add_trigger_action(trig_types, '0', _trigger_low);
- add_trigger_action(trig_types, 'c', _trigger_change);
-
- update_trigger_actions();
-
- form->addRow(tr("Trigger"), _trigger_bar);
- }
-
- g_variant_unref(gvar);
+ const auto sr_dev = device_->device();
+ if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) {
+ const Glib::VariantContainerBase gvar =
+ sr_dev->config_list(ConfigKey::TRIGGER_MATCH);
+ return Glib::VariantBase::cast_dynamic<
+ Glib::Variant<vector<int32_t>>>(gvar).get();
+ } else {
+ return vector<int32_t>();
}
}
-void LogicSignal::add_trigger_action(const char *trig_types, char type,
- QAction *action)
+QAction* LogicSignal::action_from_trigger_type(const TriggerMatchType *type)
{
- while(*trig_types)
- if(*trig_types++ == type) {
- _trigger_bar->addAction(action);
+ QAction *action;
+
+ action = trigger_none_;
+ if (type) {
+ switch (type->id()) {
+ case SR_TRIGGER_ZERO:
+ action = trigger_low_;
break;
+ case SR_TRIGGER_ONE:
+ action = trigger_high_;
+ break;
+ case SR_TRIGGER_RISING:
+ action = trigger_rising_;
+ break;
+ case SR_TRIGGER_FALLING:
+ action = trigger_falling_;
+ break;
+ case SR_TRIGGER_EDGE:
+ action = trigger_change_;
+ break;
+ default:
+ assert(0);
}
+ }
+
+ return action;
}
-void LogicSignal::update_trigger_actions()
+const TriggerMatchType *LogicSignal::trigger_type_from_action(QAction *action)
{
- const char cur_trigger = _probe->trigger ?
- _probe->trigger[0] : '\0';
- _trigger_none->setChecked(cur_trigger == '\0');
- _trigger_rising->setChecked(cur_trigger == 'r');
- _trigger_high->setChecked(cur_trigger == '1');
- _trigger_falling->setChecked(cur_trigger == 'f');
- _trigger_low->setChecked(cur_trigger == '0');
- _trigger_change->setChecked(cur_trigger == 'c');
+ if (action == trigger_low_)
+ return TriggerMatchType::ZERO;
+ else if (action == trigger_high_)
+ return TriggerMatchType::ONE;
+ else if (action == trigger_rising_)
+ return TriggerMatchType::RISING;
+ else if (action == trigger_falling_)
+ return TriggerMatchType::FALLING;
+ else if (action == trigger_change_)
+ return TriggerMatchType::EDGE;
+ else
+ return nullptr;
}
-void LogicSignal::set_trigger(char type)
+void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
{
- const char trigger_type_string[2] = {type, 0};
- const char *const trigger_string =
- (type != 0) ? trigger_type_string : NULL;
-
- assert(_dev_inst);
- const sr_dev_inst *const sdi = _dev_inst->dev_inst();
- assert(sdi);
+ Signal::populate_popup_form(parent, form);
- const int probe_count = g_slist_length(sdi->channels);
- assert(probe_count > 0);
+ const vector<int32_t> trig_types = get_trigger_types();
- assert(_probe && _probe->index < probe_count);
+ if (!trig_types.empty()) {
+ trigger_bar_ = new QToolBar(parent);
+ init_trigger_actions(trigger_bar_);
+ trigger_bar_->addAction(trigger_none_);
+ trigger_none_->setChecked(!trigger_match_);
- for (int i = 0; i < probe_count; i++) {
- sr_dev_trigger_set(sdi, i, (i == _probe->index) ?
- trigger_string : NULL);
+ for (auto type_id : trig_types) {
+ const TriggerMatchType *const type =
+ TriggerMatchType::get(type_id);
+ QAction *const action = action_from_trigger_type(type);
+ trigger_bar_->addAction(action);
+ action->setChecked(trigger_match_ == type);
+ }
+ form->addRow(tr("Trigger"), trigger_bar_);
}
-
- update_trigger_actions();
}
-void LogicSignal::on_trigger_none()
+void LogicSignal::modify_trigger()
{
- set_trigger('\0');
-}
+ auto trigger = session_.session()->trigger();
+ auto new_trigger = session_.device_manager().context()->create_trigger("pulseview");
+
+ if (trigger) {
+ for (auto stage : trigger->stages()) {
+ const auto &matches = stage->matches();
+ if (std::none_of(matches.begin(), matches.end(),
+ [&](shared_ptr<TriggerMatch> match) {
+ return match->channel() != channel_; }))
+ continue;
+
+ auto new_stage = new_trigger->add_stage();
+ for (auto match : stage->matches()) {
+ if (match->channel() == channel_)
+ continue;
+ new_stage->add_match(match->channel(), match->type());
+ }
+ }
+ }
-void LogicSignal::on_trigger_rising()
-{
- set_trigger('r');
-}
+ if (trigger_match_) {
+ // Until we can let the user decide how to group trigger matches
+ // into stages, put all of the matches into a single stage --
+ // most devices only support a single trigger stage.
+ if (new_trigger->stages().empty())
+ new_trigger->add_stage();
-void LogicSignal::on_trigger_high()
-{
- set_trigger('1');
+ new_trigger->stages().back()->add_match(channel_, trigger_match_);
+ }
+
+ session_.session()->set_trigger(
+ new_trigger->stages().empty() ? nullptr : new_trigger);
+
+ if (owner_)
+ owner_->row_item_appearance_changed(false, true);
}
-void LogicSignal::on_trigger_falling()
+const QIcon* LogicSignal::get_icon(const char *path)
{
- set_trigger('f');
+ const QIcon *icon = icon_cache_.take(path);
+ if (!icon) {
+ icon = new QIcon(path);
+ icon_cache_.insert(path, icon);
+ }
+
+ return icon;
}
-void LogicSignal::on_trigger_low()
+const QPixmap* LogicSignal::get_pixmap(const char *path)
{
- set_trigger('0');
+ const QPixmap *pixmap = pixmap_cache_.take(path);
+ if (!pixmap) {
+ pixmap = new QPixmap(path);
+ pixmap_cache_.insert(path, pixmap);
+ }
+
+ return pixmap;
}
-void LogicSignal::on_trigger_change()
+void LogicSignal::on_trigger()
{
- set_trigger('c');
+ QAction *action;
+
+ action_from_trigger_type(trigger_match_)->setChecked(false);
+
+ action = (QAction *)sender();
+ action->setChecked(true);
+ trigger_match_ = trigger_type_from_action(action);
+
+ modify_trigger();
}
} // namespace view
diff --git a/pv/view/logicsignal.h b/pv/view/logicsignal.h
deleted file mode 100644
index c3e1666..0000000
--- a/pv/view/logicsignal.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_VIEW_LOGICSIGNAL_H
-#define PULSEVIEW_PV_VIEW_LOGICSIGNAL_H
-
-#include "signal.h"
-
-#include <boost/shared_ptr.hpp>
-
-class QToolBar;
-
-namespace pv {
-
-namespace data {
-class Logic;
-}
-
-namespace view {
-
-class LogicSignal : public Signal
-{
- Q_OBJECT
-
-private:
- static const float Oversampling;
-
- static const QColor EdgeColour;
- static const QColor HighColour;
- static const QColor LowColour;
-
- static const QColor SignalColours[10];
-
-public:
- LogicSignal(boost::shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel *const probe,
- boost::shared_ptr<pv::data::Logic> data);
-
- virtual ~LogicSignal();
-
- boost::shared_ptr<pv::data::SignalData> data() const;
-
- boost::shared_ptr<pv::data::Logic> logic_data() const;
-
- /**
- * Paints the background layer of the signal with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal.
- * @param right the x-coordinate of the right edge of the signal.
- **/
- void paint_back(QPainter &p, int left, int right);
-
- /**
- * Paints the mid-layer of the signal with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal.
- * @param right the x-coordinate of the right edge of the signal.
- **/
- void paint_mid(QPainter &p, int left, int right);
-
-private:
-
- void paint_caps(QPainter &p, QLineF *const lines,
- std::vector< std::pair<int64_t, bool> > &edges,
- bool level, double samples_per_pixel, double pixels_offset,
- float x_offset, float y_offset);
-
- void init_trigger_actions(QWidget *parent);
-
- void populate_popup_form(QWidget *parent, QFormLayout *form);
-
- void add_trigger_action(const char *trig_types, char type,
- QAction *action);
-
- void update_trigger_actions();
-
- void set_trigger(char type);
-
-private slots:
- void on_trigger_none();
- void on_trigger_rising();
- void on_trigger_high();
- void on_trigger_falling();
- void on_trigger_low();
- void on_trigger_change();
-
-private:
- boost::shared_ptr<pv::data::Logic> _data;
-
- QToolBar *_trigger_bar;
- QAction *_trigger_none;
- QAction *_trigger_rising;
- QAction *_trigger_high;
- QAction *_trigger_falling;
- QAction *_trigger_low;
- QAction *_trigger_change;
-};
-
-} // namespace view
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEW_LOGICSIGNAL_H
diff --git a/pv/view/logicsignal.hpp b/pv/view/logicsignal.hpp
new file mode 100644
index 0000000..8c3525e
--- /dev/null
+++ b/pv/view/logicsignal.hpp
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_LOGICSIGNAL_HPP
+#define PULSEVIEW_PV_VIEW_LOGICSIGNAL_HPP
+
+#include <QCache>
+
+#include "signal.hpp"
+
+#include <memory>
+
+class QIcon;
+class QToolBar;
+
+namespace sigrok {
+class TriggerMatchType;
+}
+
+namespace pv {
+
+namespace devices {
+class Device;
+}
+
+namespace data {
+class Logic;
+}
+
+namespace view {
+
+class LogicSignal : public Signal
+{
+ Q_OBJECT
+
+private:
+ static const float Oversampling;
+
+ static const QColor EdgeColour;
+ static const QColor HighColour;
+ static const QColor LowColour;
+
+ static const QColor SignalColours[10];
+
+ static QColor TriggerMarkerBackgroundColour;
+ static const int TriggerMarkerPadding;
+ static const char* TriggerMarkerIcons[8];
+
+public:
+ LogicSignal(pv::Session &session,
+ std::shared_ptr<devices::Device> device,
+ std::shared_ptr<sigrok::Channel> channel,
+ std::shared_ptr<pv::data::Logic> data);
+
+ virtual ~LogicSignal();
+
+ std::shared_ptr<pv::data::SignalData> data() const;
+
+ std::shared_ptr<pv::data::Logic> logic_data() const;
+
+ void set_logic_data(std::shared_ptr<pv::data::Logic> data);
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ std::pair<int, int> v_extents() const;
+
+ /**
+ * Returns the offset to show the drag handle.
+ */
+ int scale_handle_offset() const;
+
+ /**
+ * Handles the scale handle being dragged to an offset.
+ * @param offset the offset the scale handle was dragged to.
+ */
+ void scale_handle_dragged(int offset);
+
+ /**
+ * Paints the mid-layer of the signal with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with..
+ */
+ void paint_mid(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the signal with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_fore(QPainter &p, const ViewItemPaintParams &pp);
+
+private:
+ void paint_caps(QPainter &p, QLineF *const lines,
+ std::vector< std::pair<int64_t, bool> > &edges,
+ bool level, double samples_per_pixel, double pixels_offset,
+ float x_offset, float y_offset);
+
+ void init_trigger_actions(QWidget *parent);
+
+ const std::vector<int32_t> get_trigger_types() const;
+ QAction* action_from_trigger_type(
+ const sigrok::TriggerMatchType *match);
+ const sigrok::TriggerMatchType* trigger_type_from_action(
+ QAction *action);
+ void populate_popup_form(QWidget *parent, QFormLayout *form);
+ void modify_trigger();
+
+ static const QIcon* get_icon(const char *path);
+ static const QPixmap* get_pixmap(const char *path);
+
+private Q_SLOTS:
+ void on_trigger();
+
+private:
+ int signal_height_;
+
+ std::shared_ptr<pv::devices::Device> device_;
+ std::shared_ptr<pv::data::Logic> data_;
+
+ const sigrok::TriggerMatchType *trigger_match_;
+ QToolBar *trigger_bar_;
+ QAction *trigger_none_;
+ QAction *trigger_rising_;
+ QAction *trigger_high_;
+ QAction *trigger_falling_;
+ QAction *trigger_low_;
+ QAction *trigger_change_;
+
+ static QCache<QString, const QIcon> icon_cache_;
+ static QCache<QString, const QPixmap> pixmap_cache_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_LOGICSIGNAL_HPP
diff --git a/pv/view/marginwidget.cpp b/pv/view/marginwidget.cpp
index 539551d..0d65761 100644
--- a/pv/view/marginwidget.cpp
+++ b/pv/view/marginwidget.cpp
@@ -18,17 +18,60 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "view.h"
+#include <QMenu>
+#include <QMouseEvent>
-#include "marginwidget.h"
+#include "view.hpp"
+
+#include "marginwidget.hpp"
+
+#include <pv/widgets/popup.hpp>
+
+using std::shared_ptr;
namespace pv {
namespace view {
MarginWidget::MarginWidget(View &parent) :
- QWidget(&parent),
- _view(parent)
+ ViewWidget(parent)
+{
+ setAttribute(Qt::WA_NoSystemBackground, true);
+}
+
+void MarginWidget::item_clicked(const shared_ptr<ViewItem> &item)
+{
+ if (item && item->enabled())
+ show_popup(item);
+}
+
+void MarginWidget::show_popup(const shared_ptr<ViewItem> &item)
{
+ pv::widgets::Popup *const p = item->create_popup(this);
+ if (p)
+ p->show();
+}
+
+void MarginWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ const shared_ptr<ViewItem> r = get_mouse_over_item(mouse_point_);
+ if (!r)
+ return;
+
+ QMenu *menu = r->create_context_menu(this);
+ if (menu)
+ menu->exec(event->globalPos());
+}
+
+void MarginWidget::keyPressEvent(QKeyEvent *e)
+{
+ assert(e);
+
+ if (e->key() == Qt::Key_Delete) {
+ const auto items = this->items();
+ for (auto &i : items)
+ if (i->selected())
+ i->delete_pressed();
+ }
}
} // namespace view
diff --git a/pv/view/selectableitem.h b/pv/view/marginwidget.hpp
similarity index 50%
rename from pv/view/selectableitem.h
rename to pv/view/marginwidget.hpp
index d321775..963d717 100644
--- a/pv/view/selectableitem.h
+++ b/pv/view/marginwidget.hpp
@@ -18,64 +18,55 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_SELECTABLEITEM_H
-#define PULSEVIEW_PV_SELECTABLEITEM_H
+#ifndef PULSEVIEW_PV_MARGINWIDGET_HPP
+#define PULSEVIEW_PV_MARGINWIDGET_HPP
-#include <list>
+#include <memory>
-#include <QPen>
+#include <QPoint>
-class QAction;
-class QMenu;
-class QWidget;
+#include "viewwidget.hpp"
namespace pv {
-
-namespace widgets {
-class Popup;
-}
-
namespace view {
-class SelectableItem : public QObject
+class ViewItem;
+
+class MarginWidget : public ViewWidget
{
Q_OBJECT
-private:
- static const int HighlightRadius;
-
public:
- SelectableItem();
+ MarginWidget(pv::view::View &parent);
-public:
/**
- * Returns true if the signal has been selected by the user.
+ * The extended area that the margin widget would like to be sized to.
+ * @remarks This area is the area specified by sizeHint, extended by
+ * the area to overlap the viewport.
*/
- bool selected() const;
+ virtual QSize extended_size_hint() const = 0;
+protected:
/**
- * Selects or deselects the signal.
+ * Indicates the event an a view item has been clicked.
+ * @param item the view item that has been clicked.
*/
- void select(bool select = true);
+ virtual void item_clicked(
+ const std::shared_ptr<pv::view::ViewItem> &item);
-public:
- virtual QMenu* create_context_menu(QWidget *parent);
-
- virtual pv::widgets::Popup* create_popup(QWidget *parent) = 0;
-
- virtual void delete_pressed();
-
-protected:
- static QPen highlight_pen();
+ /**
+ * Shows the popup of a the specified @c ViewItem .
+ * @param item The item to show the popup for.
+ */
+ void show_popup(const std::shared_ptr<ViewItem> &item);
protected:
- QWidget *_context_parent;
+ virtual void contextMenuEvent(QContextMenuEvent *event);
-private:
- bool _selected;
+ virtual void keyPressEvent(QKeyEvent *e);
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_SELECTABLEITEM_H
+#endif // PULSEVIEW_PV_MARGINWIDGET_HPP
diff --git a/pv/view/marginwidget.cpp b/pv/view/rowitem.cpp
similarity index 82%
copy from pv/view/marginwidget.cpp
copy to pv/view/rowitem.cpp
index 539551d..4ad8b59 100644
--- a/pv/view/marginwidget.cpp
+++ b/pv/view/rowitem.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,16 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "view.h"
-
-#include "marginwidget.h"
+#include "rowitem.hpp"
namespace pv {
namespace view {
-MarginWidget::MarginWidget(View &parent) :
- QWidget(&parent),
- _view(parent)
+void RowItem::hover_point_changed()
{
}
diff --git a/pv/view/marginwidget.h b/pv/view/rowitem.hpp
similarity index 71%
copy from pv/view/marginwidget.h
copy to pv/view/rowitem.hpp
index 42dffa7..1c48786 100644
--- a/pv/view/marginwidget.h
+++ b/pv/view/rowitem.hpp
@@ -18,36 +18,23 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_MARGINWIDGET_H
-#define PULSEVIEW_PV_MARGINWIDGET_H
+#ifndef PULSEVIEW_PV_VIEW_ROWITEM_HPP
+#define PULSEVIEW_PV_VIEW_ROWITEM_HPP
-#include <QWidget>
+#include "viewitem.hpp"
namespace pv {
namespace view {
-class View;
-
-class MarginWidget : public QWidget
+class RowItem : public ViewItem
{
Q_OBJECT
public:
- MarginWidget(pv::view::View &parent);
-
-public slots:
- virtual void clear_selection() = 0;
-
-signals:
- void selection_changed();
-
- void geometry_updated();
-
-protected:
- pv::view::View &_view;
+ virtual void hover_point_changed();
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_MARGINWIDGET_H
+#endif // PULSEVIEW_PV_VIEW_ROWITEM_HPP
diff --git a/pv/view/ruler.cpp b/pv/view/ruler.cpp
index aec3de9..31ec121 100644
--- a/pv/view/ruler.cpp
+++ b/pv/view/ruler.cpp
@@ -18,258 +18,258 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "ruler.h"
-
-#include "cursor.h"
-#include "view.h"
-#include "viewport.h"
-
#include <extdef.h>
-#include <assert.h>
-#include <math.h>
-#include <limits.h>
-
#include <QApplication>
+#include <QFontMetrics>
#include <QMouseEvent>
-#include <QPainter>
-#include <QTextStream>
-#include <pv/widgets/popup.h>
+#include "ruler.hpp"
+#include "view.hpp"
using namespace Qt;
-using boost::shared_ptr;
+
+using std::shared_ptr;
+using std::vector;
namespace pv {
namespace view {
-const int Ruler::RulerHeight = 30;
+const float Ruler::RulerHeight = 2.5f; // x Text Height
const int Ruler::MinorTickSubdivision = 4;
-const int Ruler::ScaleUnits[3] = {1, 2, 5};
-
-const QString Ruler::SIPrefixes[9] =
- {"f", "p", "n", QChar(0x03BC), "m", "", "k", "M", "G"};
-const int Ruler::FirstSIPrefixPower = -15;
-const int Ruler::HoverArrowSize = 5;
+const float Ruler::HoverArrowSize = 0.5f; // x Text Height
Ruler::Ruler(View &parent) :
- MarginWidget(parent),
- _dragging(false)
+ MarginWidget(parent)
{
setMouseTracking(true);
- connect(&_view, SIGNAL(hover_point_changed()),
+ connect(&view_, SIGNAL(hover_point_changed()),
this, SLOT(hover_point_changed()));
+ connect(&view_, SIGNAL(offset_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(scale_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(tick_prefix_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(tick_precision_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(tick_period_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
+ connect(&view_, SIGNAL(time_unit_changed()),
+ this, SLOT(invalidate_tick_position_cache()));
}
-void Ruler::clear_selection()
+QSize Ruler::sizeHint() const
{
- CursorPair &cursors = _view.cursors();
- cursors.first()->select(false);
- cursors.second()->select(false);
- update();
+ const int text_height = calculate_text_height();
+ return QSize(0, RulerHeight * text_height);
}
-QString Ruler::format_time(double t, unsigned int prefix,
- unsigned int precision)
+QSize Ruler::extended_size_hint() const
{
- const double multiplier = pow(10.0,
- (int)- prefix * 3 - FirstSIPrefixPower);
-
- QString s;
- QTextStream ts(&s);
- ts.setRealNumberPrecision(precision);
- ts << fixed << forcesign << (t * multiplier) <<
- SIPrefixes[prefix] << "s";
- return s;
+ QRectF max_rect;
+ std::vector< std::shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto &i : items)
+ max_rect = max_rect.united(i->label_rect(QRect()));
+ return QSize(0, sizeHint().height() - max_rect.top() / 2 +
+ ViewItem::HighlightRadius);
}
-QSize Ruler::sizeHint() const
+QString Ruler::format_time_with_distance(
+ const pv::util::Timestamp& distance,
+ const pv::util::Timestamp& t,
+ pv::util::SIPrefix prefix,
+ pv::util::TimeUnit unit,
+ unsigned precision,
+ bool sign)
{
- return QSize(0, RulerHeight);
+ const unsigned limit = 60;
+
+ if (t.is_zero())
+ return "0";
+
+ // If we have to use samples then we have no alternative formats
+ if (unit == pv::util::TimeUnit::Samples)
+ return pv::util::format_time_si_adjusted(t, prefix, precision, "sa", sign);
+
+ // View zoomed way out -> low precision (0), big distance (>=60s)
+ // -> DD:HH:MM
+ if ((precision == 0) && (distance >= limit))
+ return pv::util::format_time_minutes(t, 0, sign);
+
+ // View in "normal" range -> medium precision, medium step size
+ // -> HH:MM:SS.mmm... or xxxx (si unit) if less than limit seconds
+ // View zoomed way in -> high precision (>3), low step size (<1s)
+ // -> HH:MM:SS.mmm... or xxxx (si unit) if less than limit seconds
+ if (abs(t) < limit)
+ return pv::util::format_time_si_adjusted(t, prefix, precision, "s", sign);
+ else
+ return pv::util::format_time_minutes(t, precision, sign);
}
-void Ruler::paintEvent(QPaintEvent*)
+vector< shared_ptr<ViewItem> > Ruler::items()
{
+ const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
+ return vector< shared_ptr<ViewItem> >(
+ time_items.begin(), time_items.end());
+}
- const double SpacingIncrement = 32.0f;
- const double MinValueSpacing = 32.0f;
- const int ValueMargin = 3;
-
- QPainter p(this);
- p.setRenderHint(QPainter::Antialiasing);
-
- double min_width = SpacingIncrement, typical_width;
- double tick_period;
- unsigned int prefix;
-
- // Find tick spacing, and number formatting that does not cause
- // value to collide.
- do
- {
- const double min_period = _view.scale() * min_width;
-
- const int order = (int)floorf(log10f(min_period));
- const double order_decimal = pow(10.0, order);
-
- unsigned int unit = 0;
-
- do
- {
- tick_period = order_decimal * ScaleUnits[unit++];
- } while (tick_period < min_period && unit < countof(ScaleUnits));
-
- prefix = (order - FirstSIPrefixPower) / 3;
- assert(prefix < countof(SIPrefixes));
-
+shared_ptr<ViewItem> Ruler::get_mouse_over_item(const QPoint &pt)
+{
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto i = items.rbegin(); i != items.rend(); i++)
+ if ((*i)->enabled() && (*i)->label_rect(rect()).contains(pt))
+ return *i;
+ return nullptr;
+}
- typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX,
- AlignLeft | AlignTop, format_time(_view.offset(),
- prefix)).width() + MinValueSpacing;
+void Ruler::paintEvent(QPaintEvent*)
+{
+ if (!tick_position_cache_) {
+ auto ffunc = [this](const pv::util::Timestamp& t) {
+ return format_time_with_distance(
+ this->view_.tick_period(),
+ t,
+ this->view_.tick_prefix(),
+ this->view_.time_unit(),
+ this->view_.tick_precision());
+ };
+
+ tick_position_cache_ = calculate_tick_positions(
+ view_.tick_period(),
+ view_.offset(),
+ view_.scale(),
+ width(),
+ ffunc);
+ }
- min_width += SpacingIncrement;
+ const int ValueMargin = 3;
- } while(typical_width > tick_period / _view.scale());
+ const int text_height = calculate_text_height();
+ const int ruler_height = RulerHeight * text_height;
+ const int major_tick_y1 = text_height + ValueMargin * 2;
+ const int minor_tick_y1 = (major_tick_y1 + ruler_height) / 2;
- const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX,
- AlignLeft | AlignTop, "8").height();
+ QPainter p(this);
// Draw the tick marks
p.setPen(palette().color(foregroundRole()));
- const double minor_tick_period = tick_period / MinorTickSubdivision;
- const double first_major_division =
- floor(_view.offset() / tick_period);
- const double first_minor_division =
- ceil(_view.offset() / minor_tick_period);
- const double t0 = first_major_division * tick_period;
-
- int division = (int)round(first_minor_division -
- first_major_division * MinorTickSubdivision) - 1;
-
- const int major_tick_y1 = text_height + ValueMargin * 2;
- const int tick_y2 = height();
- const int minor_tick_y1 = (major_tick_y1 + tick_y2) / 2;
-
- double x;
-
- do {
- const double t = t0 + division * minor_tick_period;
- x = (t - _view.offset()) / _view.scale();
-
- if (division % MinorTickSubdivision == 0)
- {
- // Draw a major tick
- p.drawText(x, ValueMargin, 0, text_height,
- AlignCenter | AlignTop | TextDontClip,
- format_time(t, prefix));
- p.drawLine(QPointF(x, major_tick_y1),
- QPointF(x, tick_y2));
- }
- else
- {
- // Draw a minor tick
- p.drawLine(QPointF(x, minor_tick_y1),
- QPointF(x, tick_y2));
- }
-
- division++;
-
- } while (x < width());
+ for (const auto& tick: tick_position_cache_->major) {
+ p.drawText(tick.first, ValueMargin, 0, text_height,
+ AlignCenter | AlignTop | TextDontClip, tick.second);
+ p.drawLine(QPointF(tick.first, major_tick_y1),
+ QPointF(tick.first, ruler_height));
+ }
- // Draw the cursors
- if (_view.cursors_shown())
- _view.cursors().draw_markers(p, rect(), prefix);
+ for (const auto& tick: tick_position_cache_->minor) {
+ p.drawLine(QPointF(tick, minor_tick_y1),
+ QPointF(tick, ruler_height));
+ }
// Draw the hover mark
- draw_hover_mark(p);
+ draw_hover_mark(p, text_height);
+
+ p.setRenderHint(QPainter::Antialiasing);
- p.end();
+ // The cursor labels are not drawn with the arrows exactly on the
+ // bottom line of the widget, because then the selection shadow
+ // would be clipped away.
+ const QRect r = rect().adjusted(0, 0, 0, -ViewItem::HighlightRadius);
+
+ // Draw the items
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto &i : items) {
+ const bool highlight = !item_dragging_ &&
+ i->label_rect(r).contains(mouse_point_);
+ i->paint_label(p, r, highlight);
+ }
}
-void Ruler::mouseMoveEvent(QMouseEvent *e)
+Ruler::TickPositions Ruler::calculate_tick_positions(
+ const pv::util::Timestamp& major_period,
+ const pv::util::Timestamp& offset,
+ const double scale,
+ const int width,
+ std::function<QString(const pv::util::Timestamp&)> format_function)
{
- if (!(e->buttons() & Qt::LeftButton))
- return;
-
- if ((e->pos() - _mouse_down_point).manhattanLength() <
- QApplication::startDragDistance())
- return;
+ TickPositions tp;
- _dragging = true;
+ const pv::util::Timestamp minor_period = major_period / MinorTickSubdivision;
+ const pv::util::Timestamp first_major_division = floor(offset / major_period);
+ const pv::util::Timestamp first_minor_division = ceil(offset / minor_period);
+ const pv::util::Timestamp t0 = first_major_division * major_period;
- if (shared_ptr<TimeMarker> m = _grabbed_marker.lock())
- m->set_time(_view.offset() +
- ((double)e->x() + 0.5) * _view.scale());
-}
+ int division = (round(first_minor_division -
+ first_major_division * MinorTickSubdivision)).convert_to<int>() - 1;
-void Ruler::mousePressEvent(QMouseEvent *e)
-{
- if (e->buttons() & Qt::LeftButton)
- {
- _mouse_down_point = e->pos();
-
- _grabbed_marker.reset();
-
- clear_selection();
-
- if (_view.cursors_shown()) {
- CursorPair &cursors = _view.cursors();
- if (cursors.first()->get_label_rect(
- rect()).contains(e->pos()))
- _grabbed_marker = cursors.first();
- else if (cursors.second()->get_label_rect(
- rect()).contains(e->pos()))
- _grabbed_marker = cursors.second();
+ double x;
+
+ do {
+ pv::util::Timestamp t = t0 + division * minor_period;
+ x = ((t - offset) / scale).convert_to<double>();
+
+ if (division % MinorTickSubdivision == 0) {
+ // Recalculate 't' without using 'minor_period' which is a fraction
+ t = t0 + division / MinorTickSubdivision * major_period;
+ tp.major.emplace_back(x, format_function(t));
+ } else {
+ tp.minor.emplace_back(x);
}
- if (shared_ptr<TimeMarker> m = _grabbed_marker.lock())
- m->select();
+ division++;
+ } while (x < width);
- selection_changed();
- }
+ return tp;
}
-void Ruler::mouseReleaseEvent(QMouseEvent *)
+void Ruler::mouseDoubleClickEvent(QMouseEvent *e)
{
- using pv::widgets::Popup;
-
- if (!_dragging)
- if (shared_ptr<TimeMarker> m = _grabbed_marker.lock()) {
- Popup *const p = m->create_popup(&_view);
- p->set_position(mapToGlobal(QPoint(m->get_x(),
- height())), Popup::Bottom);
- p->show();
- }
-
- _dragging = false;
- _grabbed_marker.reset();
+ view_.add_flag(view_.offset() + ((double)e->x() + 0.5) * view_.scale());
}
-void Ruler::draw_hover_mark(QPainter &p)
+void Ruler::draw_hover_mark(QPainter &p, int text_height)
{
- const int x = _view.hover_point().x();
+ const int x = view_.hover_point().x();
- if (x == -1 || _dragging)
+ if (x == -1)
return;
p.setPen(QPen(Qt::NoPen));
p.setBrush(QBrush(palette().color(foregroundRole())));
- const int b = height() - 1;
+ const int b = RulerHeight * text_height;
+ const float hover_arrow_size = HoverArrowSize * text_height;
const QPointF points[] = {
QPointF(x, b),
- QPointF(x - HoverArrowSize, b - HoverArrowSize),
- QPointF(x + HoverArrowSize, b - HoverArrowSize)
+ QPointF(x - hover_arrow_size, b - hover_arrow_size),
+ QPointF(x + hover_arrow_size, b - hover_arrow_size)
};
p.drawPolygon(points, countof(points));
}
+int Ruler::calculate_text_height() const
+{
+ return QFontMetrics(font()).ascent();
+}
+
void Ruler::hover_point_changed()
{
update();
}
+void Ruler::invalidate_tick_position_cache()
+{
+ tick_position_cache_ = boost::none;
+}
+
+void Ruler::resizeEvent(QResizeEvent*)
+{
+ // the tick calculation depends on the width of this widget
+ invalidate_tick_position_cache();
+}
+
} // namespace view
} // namespace pv
diff --git a/pv/view/ruler.h b/pv/view/ruler.h
deleted file mode 100644
index dc4e7bb..0000000
--- a/pv/view/ruler.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_VIEW_RULER_H
-#define PULSEVIEW_PV_VIEW_RULER_H
-
-#include <boost/weak_ptr.hpp>
-
-#include "marginwidget.h"
-
-namespace pv {
-namespace view {
-
-class TimeMarker;
-class View;
-
-class Ruler : public MarginWidget
-{
- Q_OBJECT
-
-private:
- static const int RulerHeight;
- static const int MinorTickSubdivision;
- static const int ScaleUnits[3];
-
- static const QString SIPrefixes[9];
- static const int FirstSIPrefixPower;
-
- static const int HoverArrowSize;
-
-public:
- Ruler(View &parent);
-
- void clear_selection();
-
- static QString format_time(double t, unsigned int prefix,
- unsigned precision = 0);
-
-public:
- QSize sizeHint() const;
-
-private:
- void paintEvent(QPaintEvent *event);
-
- void mouseMoveEvent(QMouseEvent *e);
- void mousePressEvent(QMouseEvent *e);
- void mouseReleaseEvent(QMouseEvent *);
-
-private:
- /**
- * Draw a hover arrow under the cursor position.
- */
- void draw_hover_mark(QPainter &p);
-
-private slots:
- void hover_point_changed();
-
-private:
- boost::weak_ptr<TimeMarker> _grabbed_marker;
- QPoint _mouse_down_point;
- bool _dragging;
-};
-
-} // namespace view
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEW_RULER_H
diff --git a/pv/view/ruler.hpp b/pv/view/ruler.hpp
new file mode 100644
index 0000000..cf63eaf
--- /dev/null
+++ b/pv/view/ruler.hpp
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_RULER_HPP
+#define PULSEVIEW_PV_VIEW_RULER_HPP
+
+#include <functional>
+#include <memory>
+
+#include <boost/optional.hpp>
+
+#include "marginwidget.hpp"
+#include <pv/util.hpp>
+
+namespace RulerTest {
+class tick_position_test_0;
+class tick_position_test_1;
+class tick_position_test_2;
+}
+
+namespace pv {
+namespace view {
+
+class TimeItem;
+class ViewItem;
+
+class Ruler : public MarginWidget
+{
+ Q_OBJECT
+
+ friend class RulerTest::tick_position_test_0;
+ friend class RulerTest::tick_position_test_1;
+ friend class RulerTest::tick_position_test_2;
+
+private:
+
+ /// Height of the ruler in multipes of the text height
+ static const float RulerHeight;
+
+ static const int MinorTickSubdivision;
+
+ /// Height of the hover arrow in multiples of the text height
+ static const float HoverArrowSize;
+
+public:
+ Ruler(View &parent);
+
+public:
+ QSize sizeHint() const;
+
+ /**
+ * The extended area that the header widget would like to be sized to.
+ * @remarks This area is the area specified by sizeHint, extended by
+ * the area to overlap the viewport.
+ */
+ QSize extended_size_hint() const;
+
+ /**
+ * Formats a timestamp depending on its distance to another timestamp.
+ *
+ * Heuristic function, useful when multiple timestamps should be put side by
+ * side. The function procedes in the following order:
+ * - If 't' is zero, "0" is returned.
+ * - If 'unit' is 'TimeUnit::Samples', 'pv::util::format_time_si_adjusted()'
+ * is used to format 't'.
+ * - If a zoomed out view is detected (determined by 'precision' and
+ * 'distance'), 'pv::util::format_time_minutes() is used.
+ * - For timestamps "near the origin" (determined by 'distance'),
+ * 'pv::util::format_time_si_adjusted()' is used.
+ * - If none of the previous was true, 'pv::util::format_time_minutes()'
+ * is used again.
+ *
+ * @param distance The distance between the timestamp to format and
+ * an adjacent one.
+ * @param t The value to format
+ * @param prefix The SI prefix to use.
+ * @param unit The representation of the timestamp value.
+ * @param precision The number of digits after the decimal separator.
+ * @param sign Whether or not to add a sign also for positive numbers.
+ *
+ * @return The formated value.
+ */
+ static QString format_time_with_distance(
+ const pv::util::Timestamp& distance,
+ const pv::util::Timestamp& t,
+ pv::util::SIPrefix prefix = pv::util::SIPrefix::unspecified,
+ pv::util::TimeUnit unit = pv::util::TimeUnit::Time,
+ unsigned precision = 0,
+ bool sign = true);
+
+private:
+ /**
+ * Gets the time items.
+ */
+ std::vector< std::shared_ptr<pv::view::ViewItem> > items();
+
+ /**
+ * Gets the first view item which has a label that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ std::shared_ptr<pv::view::ViewItem> get_mouse_over_item(
+ const QPoint &pt);
+
+ void paintEvent(QPaintEvent *event);
+
+ void mouseDoubleClickEvent(QMouseEvent *e);
+
+ /**
+ * Draw a hover arrow under the cursor position.
+ * @param p The painter to draw into.
+ * @param text_height The height of a single text ascent.
+ */
+ void draw_hover_mark(QPainter &p, int text_height);
+
+ int calculate_text_height() const;
+
+ struct TickPositions
+ {
+ std::vector<std::pair<double, QString>> major;
+ std::vector<double> minor;
+ };
+
+ /**
+ * Holds the tick positions so that they don't have to be recalculated on
+ * every redraw. Set by 'paintEvent()' when needed.
+ */
+ boost::optional<TickPositions> tick_position_cache_;
+
+ /**
+ * Calculates the major and minor tick positions.
+ *
+ * @param major_period The period between the major ticks.
+ * @param offset The time at the left border of the ruler.
+ * @param scale The scale in seconds per pixel.
+ * @param width the Width of the ruler.
+ * @param format_function A function used to format the major tick times.
+ * @return An object of type 'TickPositions' that contains the major tick
+ * positions together with the labels at that ticks, and the minor
+ * tick positions.
+ */
+ static TickPositions calculate_tick_positions(
+ const pv::util::Timestamp& major_period,
+ const pv::util::Timestamp& offset,
+ const double scale,
+ const int width,
+ std::function<QString(const pv::util::Timestamp&)> format_function);
+
+protected:
+ void resizeEvent(QResizeEvent*) override;
+
+private Q_SLOTS:
+ void hover_point_changed();
+
+ // Resets the 'tick_position_cache_'.
+ void invalidate_tick_position_cache();
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_RULER_HPP
diff --git a/pv/view/selectableitem.cpp b/pv/view/selectableitem.cpp
deleted file mode 100644
index 3f4f6da..0000000
--- a/pv/view/selectableitem.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "selectableitem.h"
-
-#include <QApplication>
-#include <QMenu>
-#include <QPalette>
-
-namespace pv {
-namespace view {
-
-const int SelectableItem::HighlightRadius = 6;
-
-SelectableItem::SelectableItem() :
- _context_parent(NULL),
- _selected(false)
-{
-}
-
-bool SelectableItem::selected() const
-{
- return _selected;
-}
-
-void SelectableItem::select(bool select)
-{
- _selected = select;
-}
-
-QMenu* SelectableItem::create_context_menu(QWidget *parent)
-{
- _context_parent = parent;
- return new QMenu(parent);
-}
-
-void SelectableItem::delete_pressed()
-{
-}
-
-QPen SelectableItem::highlight_pen()
-{
- return QPen(QApplication::palette().brush(
- QPalette::Highlight), HighlightRadius,
- Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
-}
-
-} // namespace view
-} // namespace pv
diff --git a/pv/view/signal.cpp b/pv/view/signal.cpp
index 3312c59..67f8dde 100644
--- a/pv/view/signal.cpp
+++ b/pv/view/signal.cpp
@@ -21,32 +21,35 @@
#include <extdef.h>
#include <assert.h>
-#include <math.h>
+#include <cmath>
#include <QApplication>
#include <QFormLayout>
+#include <QKeyEvent>
+#include <QLineEdit>
#include <QMenu>
-#include <libsigrok/libsigrok.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
-#include "signal.h"
-#include "view.h"
+#include "signal.hpp"
+#include "view.hpp"
-#include <pv/device/devinst.h>
+using std::shared_ptr;
+using std::make_shared;
-using boost::shared_ptr;
+using sigrok::Channel;
namespace pv {
namespace view {
-const char *const ProbeNames[] = {
+const char *const ChannelNames[] = {
"CLK",
"DATA",
"IN",
"OUT",
"RST",
- "Tx",
- "Rx",
+ "TX",
+ "RX",
"EN",
"SCLK",
"MOSI",
@@ -56,54 +59,81 @@ const char *const ProbeNames[] = {
"SCL"
};
-Signal::Signal(shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel *const probe) :
- Trace(probe->name),
- _dev_inst(dev_inst),
- _probe(probe),
- _name_widget(NULL),
- _updating_name_widget(false)
+Signal::Signal(pv::Session &session,
+ std::shared_ptr<sigrok::Channel> channel) :
+ Trace(QString::fromUtf8(channel->name().c_str())),
+ session_(session),
+ channel_(channel),
+ scale_handle_(make_shared<SignalScaleHandle>(*this)),
+ items_({scale_handle_}),
+ name_widget_(nullptr)
{
- assert(_probe);
+ assert(channel_);
}
void Signal::set_name(QString name)
{
Trace::set_name(name);
- _updating_name_widget = true;
- _name_widget->setEditText(name);
- _updating_name_widget = false;
+
+ if (name != name_widget_->currentText())
+ name_widget_->setEditText(name);
+
+ // Store the channel name in sigrok::Channel so that it
+ // will end up in the .sr file upon save.
+ channel_->set_name(name.toUtf8().constData());
}
bool Signal::enabled() const
{
- return _probe->enabled;
+ return channel_->enabled();
}
void Signal::enable(bool enable)
{
- _dev_inst->enable_probe(_probe, enable);
- visibility_changed();
+ channel_->set_enabled(enable);
+
+ if (owner_)
+ owner_->extents_changed(true, true);
+}
+
+shared_ptr<Channel> Signal::channel() const
+{
+ return channel_;
}
-const sr_channel* Signal::probe() const
+const ViewItemOwner::item_list& Signal::child_items() const
{
- return _probe;
+ return items_;
+}
+
+void Signal::paint_back(QPainter &p, const ViewItemPaintParams &pp)
+{
+ if (channel_->enabled())
+ Trace::paint_back(p, pp);
}
void Signal::populate_popup_form(QWidget *parent, QFormLayout *form)
{
- _name_widget = new QComboBox(parent);
- _name_widget->setEditable(true);
+ name_widget_ = new QComboBox(parent);
+ name_widget_->setEditable(true);
+ name_widget_->setCompleter(0);
+
+ for (unsigned int i = 0; i < countof(ChannelNames); i++)
+ name_widget_->insertItem(i, ChannelNames[i]);
+
+ const int index = name_widget_->findText(name_, Qt::MatchExactly);
- for(unsigned int i = 0; i < countof(ProbeNames); i++)
- _name_widget->insertItem(i, ProbeNames[i]);
- _name_widget->setEditText(_probe->name);
+ if (index == -1) {
+ name_widget_->insertItem(0, name_);
+ name_widget_->setCurrentIndex(0);
+ } else {
+ name_widget_->setCurrentIndex(index);
+ }
- connect(_name_widget, SIGNAL(editTextChanged(const QString&)),
+ connect(name_widget_, SIGNAL(editTextChanged(const QString&)),
this, SLOT(on_text_changed(const QString&)));
- form->addRow(tr("Name"), _name_widget);
+ form->addRow(tr("Name"), name_widget_);
add_colour_option(parent, form);
}
diff --git a/pv/view/signal.h b/pv/view/signal.hpp
similarity index 53%
rename from pv/view/signal.h
rename to pv/view/signal.hpp
index 32d1817..517bbf4 100644
--- a/pv/view/signal.h
+++ b/pv/view/signal.hpp
@@ -18,39 +18,41 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_VIEW_SIGNAL_H
-#define PULSEVIEW_PV_VIEW_SIGNAL_H
+#ifndef PULSEVIEW_PV_VIEW_SIGNAL_HPP
+#define PULSEVIEW_PV_VIEW_SIGNAL_HPP
-#include <boost/shared_ptr.hpp>
+#include <memory>
#include <QComboBox>
#include <QWidgetAction>
#include <stdint.h>
-#include "trace.h"
+#include "signalscalehandle.hpp"
+#include "trace.hpp"
+#include "viewitemowner.hpp"
-struct sr_channel;
+namespace sigrok {
+ class Channel;
+}
namespace pv {
+class Session;
+
namespace data {
class SignalData;
}
-namespace device {
-class DevInst;
-}
-
namespace view {
-class Signal : public Trace
+class Signal : public Trace, public ViewItemOwner
{
Q_OBJECT
protected:
- Signal(boost::shared_ptr<pv::device::DevInst> dev_inst,
- const sr_channel *const probe);
+ Signal(pv::Session &session,
+ std::shared_ptr<sigrok::Channel> channel);
public:
/**
@@ -58,7 +60,7 @@ public:
*/
void set_name(QString name);
- virtual boost::shared_ptr<pv::data::SignalData> data() const = 0;
+ virtual std::shared_ptr<pv::data::SignalData> data() const = 0;
/**
* Returns true if the trace is visible and enabled.
@@ -67,7 +69,14 @@ public:
void enable(bool enable = true);
- const sr_channel* probe() const;
+ std::shared_ptr<sigrok::Channel> channel() const;
+
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ const item_list& child_items() const;
+
+ void paint_back(QPainter &p, const ViewItemPaintParams &pp);
virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
@@ -75,18 +84,36 @@ public:
void delete_pressed();
-private slots:
+ /**
+ * Returns the offset to show the drag handle.
+ */
+ virtual int scale_handle_offset() const = 0;
+
+ /**
+ * Handles the scale handle being dragged to an offset.
+ * @param offset the offset the scale handle was dragged to.
+ */
+ virtual void scale_handle_dragged(int offset) = 0;
+
+ /**
+ * Handles the scale handle being being released.
+ */
+ virtual void scale_handle_released() {};
+
+private Q_SLOTS:
void on_disable();
protected:
- boost::shared_ptr<pv::device::DevInst> _dev_inst;
- const sr_channel *const _probe;
+ pv::Session &session_;
+ std::shared_ptr<sigrok::Channel> channel_;
+
+ const std::shared_ptr<SignalScaleHandle> scale_handle_;
+ const item_list items_;
- QComboBox *_name_widget;
- bool _updating_name_widget;
+ QComboBox *name_widget_;
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_VIEW_SIGNAL_H
+#endif // PULSEVIEW_PV_VIEW_SIGNAL_HPP
diff --git a/pv/view/signalscalehandle.cpp b/pv/view/signalscalehandle.cpp
new file mode 100644
index 0000000..a4c2b41
--- /dev/null
+++ b/pv/view/signalscalehandle.cpp
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <algorithm>
+
+#include <QRadialGradient>
+
+#include "signal.hpp"
+#include "signalscalehandle.hpp"
+#include "tracetreeitemowner.hpp"
+
+using std::max;
+using std::min;
+
+namespace pv {
+namespace view {
+
+SignalScaleHandle::SignalScaleHandle(Signal &owner) :
+ owner_(owner)
+{
+}
+
+bool SignalScaleHandle::enabled() const
+{
+ return selected() || owner_.selected();
+}
+
+void SignalScaleHandle::select(bool select)
+{
+ ViewItem::select(select);
+ owner_.owner()->row_item_appearance_changed(true, true);
+}
+
+void SignalScaleHandle::drag_release()
+{
+ RowItem::drag_release();
+ owner_.scale_handle_released();
+ owner_.owner()->row_item_appearance_changed(true, true);
+}
+
+void SignalScaleHandle::drag_by(const QPoint &delta)
+{
+ owner_.scale_handle_dragged(
+ drag_point_.y() + delta.y() - owner_.get_visual_y());
+ owner_.owner()->row_item_appearance_changed(true, true);
+}
+
+QPoint SignalScaleHandle::point(const QRect &rect) const
+{
+ return owner_.point(rect) + QPoint(0, owner_.scale_handle_offset());
+}
+
+QRectF SignalScaleHandle::hit_box_rect(const ViewItemPaintParams &pp) const
+{
+ const int text_height = ViewItemPaintParams::text_height();
+ const double x = -pp.pixels_offset() - text_height / 2;
+ const double min_x = pp.left() + text_height;
+ const double max_x = pp.right() - text_height * 2;
+ return QRectF(min(max(x, min_x), max_x),
+ owner_.get_visual_y() + owner_.scale_handle_offset() -
+ text_height / 2,
+ text_height, text_height);
+}
+
+void SignalScaleHandle::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ const QRectF r(hit_box_rect(pp));
+ const QPointF c = (r.topLeft() + 2 * r.center()) / 3;
+ QRadialGradient gradient(c, r.width(), c);
+
+ if (selected()) {
+ gradient.setColorAt(0.0, QColor(255, 255, 255));
+ gradient.setColorAt(0.75, QColor(192, 192, 192));
+ gradient.setColorAt(1.0, QColor(128, 128, 128));
+ } else {
+ gradient.setColorAt(0.0, QColor(192, 192, 192));
+ gradient.setColorAt(0.75, QColor(128, 128, 128));
+ gradient.setColorAt(1.0, QColor(128, 128, 128));
+ }
+
+ p.setBrush(QBrush(gradient));
+ p.setPen(QColor(128, 128, 128));
+ p.drawEllipse(r);
+}
+
+} // view
+} // pv
diff --git a/pv/view/signalscalehandle.hpp b/pv/view/signalscalehandle.hpp
new file mode 100644
index 0000000..9d1ae93
--- /dev/null
+++ b/pv/view/signalscalehandle.hpp
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_SIGNALSCALEHANDLE_HPP
+#define PULSEVIEW_PV_VIEW_SIGNALSCALEHANDLE_HPP
+
+#include "rowitem.hpp"
+
+namespace pv {
+namespace view {
+
+class Signal;
+
+/**
+ * A row item owned by a @c Signal that implements the v-scale adjustment grab
+ * handle.
+ */
+class SignalScaleHandle : public RowItem
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ explicit SignalScaleHandle(Signal &owner);
+
+public:
+ /**
+ * Returns true if the parent item is enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Selects or deselects the signal.
+ */
+ void select(bool select = true);
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ void drag_release();
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ void drag_by(const QPoint &delta);
+
+ /**
+ * Get the drag point.
+ * @param rect the rectangle of the widget area.
+ */
+ QPoint point(const QRect &rect) const;
+
+ /**
+ * Computes the outline rectangle of the viewport hit-box.
+ * @param rect the rectangle of the viewport area.
+ * @return Returns the rectangle of the hit-box.
+ */
+ QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, const ViewItemPaintParams &pp);
+
+private:
+ Signal &owner_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_SIGNALSCALEHANDLE_HPP
diff --git a/pv/view/marginwidget.cpp b/pv/view/timeitem.cpp
similarity index 74%
copy from pv/view/marginwidget.cpp
copy to pv/view/timeitem.cpp
index 539551d..ec8dd1a 100644
--- a/pv/view/marginwidget.cpp
+++ b/pv/view/timeitem.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,17 +18,20 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "view.h"
-
-#include "marginwidget.h"
+#include "timeitem.hpp"
+#include "view.hpp"
namespace pv {
namespace view {
-MarginWidget::MarginWidget(View &parent) :
- QWidget(&parent),
- _view(parent)
+TimeItem::TimeItem(View &view) :
+ view_(view) {
+}
+
+void TimeItem::drag_by(const QPoint &delta)
{
+ set_time(view_.offset() + (drag_point_.x() + delta.x() - 0.5) *
+ view_.scale());
}
} // namespace view
diff --git a/pv/view/marginwidget.h b/pv/view/timeitem.hpp
similarity index 57%
rename from pv/view/marginwidget.h
rename to pv/view/timeitem.hpp
index 42dffa7..cd6f5f2 100644
--- a/pv/view/marginwidget.h
+++ b/pv/view/timeitem.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,36 +18,47 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_MARGINWIDGET_H
-#define PULSEVIEW_PV_MARGINWIDGET_H
+#ifndef PULSEVIEW_PV_VIEW_TIMEITEM_HPP
+#define PULSEVIEW_PV_VIEW_TIMEITEM_HPP
-#include <QWidget>
+#include "viewitem.hpp"
namespace pv {
namespace view {
class View;
-class MarginWidget : public QWidget
+class TimeItem : public ViewItem
+
{
Q_OBJECT
-public:
- MarginWidget(pv::view::View &parent);
+protected:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this marker.
+ */
+ TimeItem(View &view);
-public slots:
- virtual void clear_selection() = 0;
+public:
+ /**
+ * Sets the time of the marker.
+ */
+ virtual void set_time(const pv::util::Timestamp& time) = 0;
-signals:
- void selection_changed();
+ virtual float get_x() const = 0;
- void geometry_updated();
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ void drag_by(const QPoint &delta);
protected:
- pv::view::View &_view;
+ View &view_;
};
} // namespace view
} // namespace pv
-#endif // PULSEVIEW_PV_MARGINWIDGET_H
+#endif // PULSEVIEW_PV_VIEW_TIMEITEM_HPP
diff --git a/pv/view/timemarker.cpp b/pv/view/timemarker.cpp
index a5d280b..b5662e4 100644
--- a/pv/view/timemarker.cpp
+++ b/pv/view/timemarker.cpp
@@ -18,56 +18,149 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "timemarker.h"
+#include <algorithm>
-#include "view.h"
+#include <extdef.h>
+#include "timemarker.hpp"
+
+#include "view.hpp"
+#include "pv/widgets/timestampspinbox.hpp"
+
+#include <QApplication>
#include <QFormLayout>
+#include <QFontMetrics>
#include <QPainter>
-#include <pv/widgets/popup.h>
+#include <pv/widgets/popup.hpp>
+
+using std::max;
+using std::min;
namespace pv {
namespace view {
-TimeMarker::TimeMarker(View &view, const QColor &colour, double time) :
- _view(view),
- _colour(colour),
- _time(time),
- _value_action(NULL),
- _value_widget(NULL),
- _updating_value_widget(false)
+const int TimeMarker::ArrowSize = 4;
+
+TimeMarker::TimeMarker(
+ View &view, const QColor &colour, const pv::util::Timestamp& time) :
+ TimeItem(view),
+ colour_(colour),
+ time_(time),
+ value_action_(nullptr),
+ value_widget_(nullptr),
+ updating_value_widget_(false)
+{
+}
+
+const pv::util::Timestamp& TimeMarker::time() const
{
+ return time_;
}
-double TimeMarker::time() const
+void TimeMarker::set_time(const pv::util::Timestamp& time)
{
- return _time;
+ time_ = time;
+
+ if (value_widget_) {
+ updating_value_widget_ = true;
+ value_widget_->setValue(time);
+ updating_value_widget_ = false;
+ }
+
+ view_.time_item_appearance_changed(true, true);
}
float TimeMarker::get_x() const
{
- return (_time - _view.offset()) / _view.scale();
+ return ((time_ - view_.offset()) / view_.scale()).convert_to<float>();
}
-void TimeMarker::set_time(double time)
+QPoint TimeMarker::point(const QRect &rect) const
{
- _time = time;
+ return QPoint(get_x(), rect.bottom());
+}
+
+QRectF TimeMarker::label_rect(const QRectF &rect) const
+{
+ QFontMetrics m(QApplication::font());
+ const QSizeF text_size(
+ max(m.boundingRect(get_text()).size().width(), ArrowSize),
+ m.height());
+ const QSizeF label_size(text_size + LabelPadding * 2);
+ const float top = rect.height() - label_size.height() -
+ TimeMarker::ArrowSize - 0.5f;
+ const float x = get_x();
- if (_value_widget) {
- _updating_value_widget = true;
- _value_widget->setValue(time);
- _updating_value_widget = false;
+ return QRectF(QPointF(x - label_size.width() / 2, top), label_size);
+}
+
+QRectF TimeMarker::hit_box_rect(const ViewItemPaintParams &pp) const
+{
+ const float x = get_x();
+ const float h = QFontMetrics(QApplication::font()).height();
+ return QRectF(x - h / 2.0f, pp.top(), h, pp.height());
+}
+
+void TimeMarker::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ if (!enabled())
+ return;
+
+ const qreal x = ((time_ - view_.offset()) / view_.scale()).convert_to<qreal>();
+ const QRectF r(label_rect(rect));
+
+ const QPointF points[] = {
+ r.topLeft(),
+ r.bottomLeft(),
+ QPointF(max(r.left(), x - ArrowSize), r.bottom()),
+ QPointF(x, rect.bottom()),
+ QPointF(min(r.right(), x + ArrowSize), r.bottom()),
+ r.bottomRight(),
+ r.topRight()
+ };
+
+ const QPointF highlight_points[] = {
+ QPointF(r.left() + 1, r.top() + 1),
+ QPointF(r.left() + 1, r.bottom() - 1),
+ QPointF(max(r.left() + 1, x - ArrowSize), r.bottom() - 1),
+ QPointF(min(max(r.left() + 1, x), r.right() - 1),
+ rect.bottom() - 1),
+ QPointF(min(r.right() - 1, x + ArrowSize), r.bottom() - 1),
+ QPointF(r.right() - 1, r.bottom() - 1),
+ QPointF(r.right() - 1, r.top() + 1),
+ };
+
+ if (selected()) {
+ p.setPen(highlight_pen());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(points, countof(points));
}
- time_changed();
+ p.setPen(Qt::transparent);
+ p.setBrush(hover ? colour_.lighter() : colour_);
+ p.drawPolygon(points, countof(points));
+
+ p.setPen(colour_.lighter());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(highlight_points, countof(highlight_points));
+
+ p.setPen(colour_.darker());
+ p.setBrush(Qt::transparent);
+ p.drawPolygon(points, countof(points));
+
+ p.setPen(select_text_colour(colour_));
+ p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_text());
}
-void TimeMarker::paint(QPainter &p, const QRect &rect)
+void TimeMarker::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
{
+ if (!enabled())
+ return;
+
const float x = get_x();
- p.setPen(_colour);
- p.drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom()));
+ p.setPen(colour_.darker());
+ p.drawLine(QPointF(x, pp.top()), QPointF(x, pp.bottom()));
}
pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
@@ -75,29 +168,27 @@ pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
using pv::widgets::Popup;
Popup *const popup = new Popup(parent);
+ popup->set_position(parent->mapToGlobal(
+ point(parent->rect())), Popup::Bottom);
+
QFormLayout *const form = new QFormLayout(popup);
popup->setLayout(form);
- _value_widget = new QDoubleSpinBox(parent);
- _value_widget->setDecimals(9);
- _value_widget->setSuffix("s");
- _value_widget->setSingleStep(1e-6);
- _value_widget->setValue(_time);
+ value_widget_ = new pv::widgets::TimestampSpinBox(parent);
+ value_widget_->setValue(time_);
- connect(_value_widget, SIGNAL(valueChanged(double)),
- this, SLOT(on_value_changed(double)));
+ connect(value_widget_, SIGNAL(valueChanged(const pv::util::Timestamp&)),
+ this, SLOT(on_value_changed(const pv::util::Timestamp&)));
- form->addRow(tr("Time"), _value_widget);
+ form->addRow(tr("Time"), value_widget_);
return popup;
}
-void TimeMarker::on_value_changed(double value)
+void TimeMarker::on_value_changed(const pv::util::Timestamp& value)
{
- if (!_updating_value_widget) {
- _time = value;
- time_changed();
- }
+ if (!updating_value_widget_)
+ set_time(value);
}
} // namespace view
diff --git a/pv/view/timemarker.h b/pv/view/timemarker.h
deleted file mode 100644
index fc058c2..0000000
--- a/pv/view/timemarker.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_VIEW_MARKER_H
-#define PULSEVIEW_PV_VIEW_MARKER_H
-
-#include <QColor>
-#include <QDoubleSpinBox>
-#include <QObject>
-#include <QRectF>
-#include <QWidgetAction>
-
-#include "selectableitem.h"
-
-class QPainter;
-class QRect;
-
-namespace pv {
-namespace view {
-
-class View;
-
-class TimeMarker : public SelectableItem
-{
- Q_OBJECT
-
-protected:
- /**
- * Constructor.
- * @param view A reference to the view that owns this marker.
- * @param colour A reference to the colour of this cursor.
- * @param time The time to set the flag to.
- */
- TimeMarker(View &view, const QColor &colour, double time);
-
-public:
- /**
- * Gets the time of the marker.
- */
- double time() const;
-
- /**
- * Sets the time of the marker.
- */
- void set_time(double time);
-
- float get_x() const;
-
- /**
- * Paints the marker to the viewport.
- * @param p The painter to draw with.
- * @param rect The rectangle of the viewport client area.
- */
- virtual void paint(QPainter &p, const QRect &rect);
-
- /**
- * Gets the marker label rectangle.
- * @param rect The rectangle of the ruler client area.
- * @return Returns the label rectangle.
- */
- virtual QRectF get_label_rect(const QRect &rect) const = 0;
-
- /**
- * Paints the marker's label to the ruler.
- * @param p The painter to draw with.
- * @param rect The rectangle of the ruler client area.
- * @param prefix The SI prefix to paint time value with.
- */
- virtual void paint_label(QPainter &p, const QRect &rect,
- unsigned int prefix) = 0;
-
- pv::widgets::Popup* create_popup(QWidget *parent);
-
-private slots:
- void on_value_changed(double value);
-
-signals:
- void time_changed();
-
-protected:
- View &_view;
- const QColor &_colour;
-
- double _time;
-
- QSizeF _text_size;
-
- QWidgetAction *_value_action;
- QDoubleSpinBox *_value_widget;
- bool _updating_value_widget;
-};
-
-} // namespace view
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEW_MARKER_H
diff --git a/pv/view/timemarker.hpp b/pv/view/timemarker.hpp
new file mode 100644
index 0000000..f16fea0
--- /dev/null
+++ b/pv/view/timemarker.hpp
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_MARKER_HPP
+#define PULSEVIEW_PV_VIEW_MARKER_HPP
+
+#include <QColor>
+#include <QDoubleSpinBox>
+#include <QObject>
+#include <QRectF>
+#include <QWidgetAction>
+
+#include "timeitem.hpp"
+
+class QPainter;
+class QRect;
+
+namespace pv {
+namespace widgets {
+ class TimestampSpinBox;
+}
+
+namespace view {
+
+class View;
+
+class TimeMarker : public TimeItem
+{
+ Q_OBJECT
+
+public:
+ static const int ArrowSize;
+
+protected:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this marker.
+ * @param colour A reference to the colour of this cursor.
+ * @param time The time to set the flag to.
+ */
+ TimeMarker(View &view, const QColor &colour, const pv::util::Timestamp& time);
+
+public:
+ /**
+ * Gets the time of the marker.
+ */
+ const pv::util::Timestamp& time() const;
+
+ /**
+ * Sets the time of the marker.
+ */
+ void set_time(const pv::util::Timestamp& time) override;
+
+ float get_x() const;
+
+ /**
+ * Gets the arrow-tip point of the time marker.
+ * @param rect the rectangle of the ruler area.
+ */
+ QPoint point(const QRect &rect) const;
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect) const;
+
+ /**
+ * Computes the outline rectangle of the viewport hit-box.
+ * @param rect the rectangle of the viewport area.
+ * @return Returns the rectangle of the hit-box.
+ */
+ QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
+
+ /**
+ * Gets the text to show in the marker.
+ */
+ virtual QString get_text() const = 0;
+
+ /**
+ * Paints the marker's label to the ruler.
+ * @param p The painter to draw with.
+ * @param rect The rectangle of the ruler client area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, const ViewItemPaintParams &pp);
+
+ virtual pv::widgets::Popup* create_popup(QWidget *parent);
+
+private Q_SLOTS:
+ void on_value_changed(const pv::util::Timestamp& value);
+
+protected:
+ const QColor &colour_;
+
+ pv::util::Timestamp time_;
+
+ QSizeF text_size_;
+
+ QWidgetAction *value_action_;
+ pv::widgets::TimestampSpinBox *value_widget_;
+ bool updating_value_widget_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_MARKER_HPP
diff --git a/pv/view/trace.cpp b/pv/view/trace.cpp
index d10448f..264541c 100644
--- a/pv/view/trace.cpp
+++ b/pv/view/trace.cpp
@@ -21,18 +21,19 @@
#include <extdef.h>
#include <assert.h>
-#include <math.h>
+#include <cmath>
#include <QApplication>
#include <QFormLayout>
+#include <QKeyEvent>
#include <QLineEdit>
-#include "trace.h"
-#include "tracepalette.h"
-#include "view.h"
+#include "trace.hpp"
+#include "tracepalette.hpp"
+#include "view.hpp"
-#include <pv/widgets/colourbutton.h>
-#include <pv/widgets/popup.h>
+#include <pv/widgets/colourbutton.hpp>
+#include <pv/widgets/popup.hpp>
namespace pv {
namespace view {
@@ -40,100 +41,71 @@ namespace view {
const QPen Trace::AxisPen(QColor(128, 128, 128, 64));
const int Trace::LabelHitPadding = 2;
+const QColor Trace::DarkBGColour(235, 235, 235); // Quite light grey
+const QColor Trace::BrightBGColour(245, 245, 245); // Very light grey
+
Trace::Trace(QString name) :
- _name(name),
- _v_offset(0),
- _popup(NULL),
- _popup_form(NULL)
+ name_(name),
+ coloured_bg_(true), // Default setting is set in MainWindow::setup_ui()
+ popup_(nullptr),
+ popup_form_(nullptr)
{
}
-QString Trace::get_name() const
+QString Trace::name() const
{
- return _name;
+ return name_;
}
void Trace::set_name(QString name)
{
- _name = name;
+ name_ = name;
}
-QColor Trace::get_colour() const
+QColor Trace::colour() const
{
- return _colour;
+ return colour_;
}
void Trace::set_colour(QColor colour)
{
- _colour = colour;
-}
-
-int Trace::get_v_offset() const
-{
- return _v_offset;
-}
-
-void Trace::set_v_offset(int v_offset)
-{
- _v_offset = v_offset;
-}
+ colour_ = colour;
-void Trace::set_view(pv::view::View *view)
-{
- assert(view);
- _view = view;
-}
-
-void Trace::paint_back(QPainter &p, int left, int right)
-{
- (void)p;
- (void)left;
- (void)right;
-}
-
-void Trace::paint_mid(QPainter &p, int left, int right)
-{
- (void)p;
- (void)left;
- (void)right;
+ bgcolour_ = colour;
+ bgcolour_.setAlpha(20);
}
-void Trace::paint_fore(QPainter &p, int left, int right)
+void Trace::set_coloured_bg(bool state)
{
- (void)p;
- (void)left;
- (void)right;
+ coloured_bg_ = state;
}
-void Trace::paint_label(QPainter &p, int right, bool hover)
+void Trace::paint_label(QPainter &p, const QRect &rect, bool hover)
{
- assert(_view);
- const int y = _v_offset - _view->v_offset();
+ const int y = get_visual_y();
- p.setBrush(_colour);
+ p.setBrush(colour_);
if (!enabled())
return;
- const QColor colour = get_colour();
-
- const QRectF label_rect = get_label_rect(right);
+ const QRectF r = label_rect(rect);
// Paint the label
+ const float label_arrow_length = r.height() / 2;
const QPointF points[] = {
- label_rect.topLeft(),
- label_rect.topRight(),
- QPointF(right, y),
- label_rect.bottomRight(),
- label_rect.bottomLeft()
+ r.topLeft(),
+ QPointF(r.right() - label_arrow_length, r.top()),
+ QPointF(r.right(), y),
+ QPointF(r.right() - label_arrow_length, r.bottom()),
+ r.bottomLeft()
};
-
const QPointF highlight_points[] = {
- QPointF(label_rect.left() + 1, label_rect.top() + 1),
- QPointF(label_rect.right(), label_rect.top() + 1),
- QPointF(right - 1, y),
- QPointF(label_rect.right(), label_rect.bottom() - 1),
- QPointF(label_rect.left() + 1, label_rect.bottom() - 1)
+ QPointF(r.left() + 1, r.top() + 1),
+ QPointF(r.right() - label_arrow_length, r.top() + 1),
+ QPointF(r.right() - 1, y),
+ QPointF(r.right() - label_arrow_length, r.bottom() - 1),
+ QPointF(r.left() + 1, r.bottom() - 1)
};
if (selected()) {
@@ -143,38 +115,28 @@ void Trace::paint_label(QPainter &p, int right, bool hover)
}
p.setPen(Qt::transparent);
- p.setBrush(hover ? colour.lighter() : colour);
+ p.setBrush(hover ? colour_.lighter() : colour_);
p.drawPolygon(points, countof(points));
- p.setPen(colour.lighter());
+ p.setPen(colour_.lighter());
p.setBrush(Qt::transparent);
p.drawPolygon(highlight_points, countof(highlight_points));
- p.setPen(colour.darker());
+ p.setPen(colour_.darker());
p.setBrush(Qt::transparent);
p.drawPolygon(points, countof(points));
// Paint the text
- p.setPen(get_text_colour());
+ p.setPen(select_text_colour(colour_));
p.setFont(QApplication::font());
- p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, _name);
-}
-
-bool Trace::pt_in_label_rect(int left, int right, const QPoint &point)
-{
- (void)left;
-
- const QRectF label = get_label_rect(right);
- return enabled() && QRectF(
- QPointF(label.left() - LabelHitPadding,
- label.top() - LabelHitPadding),
- QPointF(right, label.bottom() + LabelHitPadding)
- ).contains(point);
+ p.drawText(QRectF(r.x(), r.y(),
+ r.width() - label_arrow_length, r.height()),
+ Qt::AlignCenter | Qt::AlignVCenter, name_);
}
QMenu* Trace::create_context_menu(QWidget *parent)
{
- QMenu *const menu = SelectableItem::create_context_menu(parent);
+ QMenu *const menu = ViewItem::create_context_menu(parent);
return menu;
}
@@ -183,50 +145,59 @@ pv::widgets::Popup* Trace::create_popup(QWidget *parent)
{
using pv::widgets::Popup;
- _popup = new Popup(parent);
+ popup_ = new Popup(parent);
+ popup_->set_position(parent->mapToGlobal(
+ point(parent->rect())), Popup::Right);
create_popup_form();
- connect(_popup, SIGNAL(closed()),
+ connect(popup_, SIGNAL(closed()),
this, SLOT(on_popup_closed()));
- return _popup;
-}
-
-int Trace::get_y() const
-{
- return _v_offset - _view->v_offset();
+ return popup_;
}
-QRectF Trace::get_label_rect(int right)
+QRectF Trace::label_rect(const QRectF &rect) const
{
using pv::view::View;
- assert(_view);
-
QFontMetrics m(QApplication::font());
const QSize text_size(
- m.boundingRect(QRect(), 0, _name).width(),
- m.boundingRect(QRect(), 0, "Tg").height());
+ m.boundingRect(QRect(), 0, name_).width(), m.height());
const QSizeF label_size(
- text_size.width() + View::LabelPadding.width() * 2,
- ceilf((text_size.height() + View::LabelPadding.height() * 2) / 2) * 2);
- const float label_arrow_length = label_size.height() / 2;
+ text_size.width() + LabelPadding.width() * 2,
+ ceilf((text_size.height() + LabelPadding.height() * 2) / 2) * 2);
+ const float half_height = label_size.height() / 2;
return QRectF(
- right - label_arrow_length - label_size.width() - 0.5,
- get_y() + 0.5f - label_size.height() / 2,
- label_size.width(), label_size.height());
+ rect.right() - half_height - label_size.width() - 0.5,
+ get_visual_y() + 0.5f - half_height,
+ label_size.width() + half_height,
+ label_size.height());
}
-QColor Trace::get_text_colour() const
+void Trace::paint_back(QPainter &p, const ViewItemPaintParams &pp)
{
- return (_colour.lightness() > 64) ? Qt::black : Qt::white;
+ if (coloured_bg_)
+ p.setBrush(bgcolour_);
+ else
+ p.setBrush(bgcolour_state_ ? BrightBGColour : DarkBGColour);
+
+ p.setPen(QPen(Qt::NoPen));
+
+ const std::pair<int, int> extents = v_extents();
+
+ const int x = 0;
+ const int y = get_visual_y() + extents.first;
+ const int w = pp.right() - pp.left();
+ const int h = extents.second - extents.first;
+
+ p.drawRect(x, y, w, h);
}
-void Trace::paint_axis(QPainter &p, int y, int left, int right)
+void Trace::paint_axis(QPainter &p, const ViewItemPaintParams &pp, int y)
{
p.setPen(AxisPen);
- p.drawLine(QPointF(left, y + 0.5f), QPointF(right, y + 0.5f));
+ p.drawLine(QPointF(pp.left(), y + 0.5f), QPointF(pp.right(), y + 0.5f));
}
void Trace::add_colour_option(QWidget *parent, QFormLayout *form)
@@ -236,7 +207,7 @@ void Trace::add_colour_option(QWidget *parent, QFormLayout *form)
ColourButton *const colour_button = new ColourButton(
TracePalette::Rows, TracePalette::Cols, parent);
colour_button->set_palette(TracePalette::Colours);
- colour_button->set_colour(_colour);
+ colour_button->set_colour(colour_);
connect(colour_button, SIGNAL(selected(const QColor&)),
this, SLOT(on_colour_changed(const QColor&)));
@@ -250,19 +221,19 @@ void Trace::create_popup_form()
// Transfer the layout and the child widgets to a temporary widget
// which then goes out of scope destroying the layout and all the child
// widgets.
- if (_popup_form)
- QWidget().setLayout(_popup_form);
+ if (popup_form_)
+ QWidget().setLayout(popup_form_);
// Repopulate the popup
- _popup_form = new QFormLayout(_popup);
- _popup->setLayout(_popup_form);
- populate_popup_form(_popup, _popup_form);
+ popup_form_ = new QFormLayout(popup_);
+ popup_->setLayout(popup_form_);
+ populate_popup_form(popup_, popup_form_);
}
void Trace::populate_popup_form(QWidget *parent, QFormLayout *form)
{
QLineEdit *const name_edit = new QLineEdit(parent);
- name_edit->setText(_name);
+ name_edit->setText(name_);
connect(name_edit, SIGNAL(textChanged(const QString&)),
this, SLOT(on_text_changed(const QString&)));
form->addRow(tr("Name"), name_edit);
@@ -272,20 +243,24 @@ void Trace::populate_popup_form(QWidget *parent, QFormLayout *form)
void Trace::on_popup_closed()
{
- _popup = NULL;
- _popup_form = NULL;
+ popup_ = nullptr;
+ popup_form_ = nullptr;
}
void Trace::on_text_changed(const QString &text)
{
set_name(text);
- text_changed();
+
+ if (owner_)
+ owner_->extents_changed(true, false);
}
void Trace::on_colour_changed(const QColor &colour)
{
set_colour(colour);
- colour_changed();
+
+ if (owner_)
+ owner_->row_item_appearance_changed(true, true);
}
} // namespace view
diff --git a/pv/view/trace.h b/pv/view/trace.h
deleted file mode 100644
index ace93d5..0000000
--- a/pv/view/trace.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_VIEW_TRACE_H
-#define PULSEVIEW_PV_VIEW_TRACE_H
-
-#include <QColor>
-#include <QPainter>
-#include <QPen>
-#include <QRect>
-#include <QString>
-
-#include <stdint.h>
-
-#include "selectableitem.h"
-
-class QFormLayout;
-
-namespace pv {
-namespace view {
-
-class View;
-
-class Trace : public SelectableItem
-{
- Q_OBJECT
-
-private:
- static const QPen AxisPen;
- static const int LabelHitPadding;
-
-protected:
- Trace(QString name);
-
-public:
- /**
- * Gets the name of this signal.
- */
- QString get_name() const;
-
- /**
- * Sets the name of the signal.
- */
- virtual void set_name(QString name);
-
- /**
- * Get the colour of the signal.
- */
- QColor get_colour() const;
-
- /**
- * Set the colour of the signal.
- */
- void set_colour(QColor colour);
-
- /**
- * Gets the vertical layout offset of this signal.
- */
- int get_v_offset() const;
-
- /**
- * Sets the vertical layout offset of this signal.
- */
- void set_v_offset(int v_offset);
-
- /**
- * Returns true if the trace is visible and enabled.
- */
- virtual bool enabled() const = 0;
-
- virtual void set_view(pv::view::View *view);
-
- /**
- * Paints the background layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal
- * @param right the x-coordinate of the right edge of the signal
- **/
- virtual void paint_back(QPainter &p, int left, int right);
-
- /**
- * Paints the mid-layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal
- * @param right the x-coordinate of the right edge of the signal
- **/
- virtual void paint_mid(QPainter &p, int left, int right);
-
- /**
- * Paints the foreground layer of the trace with a QPainter
- * @param p the QPainter to paint into.
- * @param left the x-coordinate of the left edge of the signal
- * @param right the x-coordinate of the right edge of the signal
- **/
- virtual void paint_fore(QPainter &p, int left, int right);
-
- /**
- * Paints the signal label into a QGLWidget.
- * @param p the QPainter to paint into.
- * @param right the x-coordinate of the right edge of the header
- * area.
- * @param hover true if the label is being hovered over by the mouse.
- */
- virtual void paint_label(QPainter &p, int right, bool hover);
-
- /**
- * Determines if a point is in the header label rect.
- * @param left the x-coordinate of the left edge of the header
- * area.
- * @param right the x-coordinate of the right edge of the header
- * area.
- * @param point the point to test.
- */
- bool pt_in_label_rect(int left, int right, const QPoint &point);
-
- virtual QMenu* create_context_menu(QWidget *parent);
-
- pv::widgets::Popup* create_popup(QWidget *parent);
-
- /**
- * Gets the y-offset of the axis.
- */
- int get_y() const;
-
- /**
- * Computes the outline rectangle of a label.
- * @param p the QPainter to lay out text with.
- * @param right the x-coordinate of the right edge of the header
- * area.
- * @return Returns the rectangle of the signal label.
- */
- QRectF get_label_rect(int right);
-
-protected:
-
- /**
- * Gets the text colour.
- * @remarks This colour is computed by comparing the lightness
- * of the trace colour against a threshold to determine whether
- * white or black would be more visible.
- */
- QColor get_text_colour() const;
-
- /**
- * Paints a zero axis across the viewport.
- * @param p the QPainter to paint into.
- * @param y the y-offset of the axis.
- * @param left the x-coordinate of the left edge of the view.
- * @param right the x-coordinate of the right edge of the view.
- */
- void paint_axis(QPainter &p, int y, int left, int right);
-
- void add_colour_option(QWidget *parent, QFormLayout *form);
-
- void create_popup_form();
-
- virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
-
-private slots:
- void on_text_changed(const QString &text);
-
- void on_colour_changed(const QColor &colour);
-
- void on_popup_closed();
-
-signals:
- void visibility_changed();
- void text_changed();
- void colour_changed();
-
-protected:
- pv::view::View *_view;
-
- QString _name;
- QColor _colour;
- int _v_offset;
-
-private:
- pv::widgets::Popup *_popup;
- QFormLayout *_popup_form;
-};
-
-} // namespace view
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEW_TRACE_H
diff --git a/pv/view/trace.hpp b/pv/view/trace.hpp
new file mode 100644
index 0000000..cb5ac15
--- /dev/null
+++ b/pv/view/trace.hpp
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_TRACE_HPP
+#define PULSEVIEW_PV_VIEW_TRACE_HPP
+
+#include <QColor>
+#include <QPainter>
+#include <QPen>
+#include <QRect>
+#include <QString>
+
+#include <stdint.h>
+
+#include "tracetreeitem.hpp"
+
+class QFormLayout;
+
+namespace pv {
+
+namespace widgets {
+class Popup;
+}
+
+namespace view {
+
+class Trace : public TraceTreeItem
+{
+ Q_OBJECT
+
+private:
+ static const QPen AxisPen;
+ static const int LabelHitPadding;
+
+ static const QColor BrightBGColour;
+ static const QColor DarkBGColour;
+
+protected:
+ Trace(QString name);
+
+public:
+ /**
+ * Gets the name of this signal.
+ */
+ QString name() const;
+
+ /**
+ * Sets the name of the signal.
+ */
+ virtual void set_name(QString name);
+
+ /**
+ * Get the colour of the signal.
+ */
+ QColor colour() const;
+
+ /**
+ * Set the colour of the signal.
+ */
+ void set_colour(QColor colour);
+
+ /**
+ * Enables or disables the coloured background for this trace.
+ */
+ void set_coloured_bg(bool state);
+
+ /**
+ * Paints the signal label.
+ * @param p the QPainter to paint into.
+ * @param rect the rectangle of the header area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ virtual QMenu* create_context_menu(QWidget *parent);
+
+ pv::widgets::Popup* create_popup(QWidget *parent);
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect) const;
+
+protected:
+ /**
+ * Paints the background layer of the signal with a QPainter.
+ * @param p The QPainter to paint into.
+ * @param pp The painting parameters object to paint with.
+ */
+ virtual void paint_back(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Paints a zero axis across the viewport.
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ * @param y the y-offset of the axis.
+ */
+ void paint_axis(QPainter &p, const ViewItemPaintParams &pp, int y);
+
+ void add_colour_option(QWidget *parent, QFormLayout *form);
+
+ void create_popup_form();
+
+ virtual void populate_popup_form(QWidget *parent, QFormLayout *form);
+
+private Q_SLOTS:
+ void on_text_changed(const QString &text);
+
+ void on_colour_changed(const QColor &colour);
+
+ void on_popup_closed();
+
+protected:
+ QString name_;
+ QColor colour_, bgcolour_;
+ bool coloured_bg_, coloured_bg_state_;
+
+private:
+ pv::widgets::Popup *popup_;
+ QFormLayout *popup_form_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_TRACE_HPP
diff --git a/pv/view/tracegroup.cpp b/pv/view/tracegroup.cpp
new file mode 100644
index 0000000..9c1d49f
--- /dev/null
+++ b/pv/view/tracegroup.cpp
@@ -0,0 +1,226 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <extdef.h>
+#include <assert.h>
+
+#include <algorithm>
+
+#include <QMenu>
+#include <QPainter>
+
+#include "tracegroup.hpp"
+
+using std::pair;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace view {
+
+const int TraceGroup::Padding = 8;
+const int TraceGroup::Width = 12;
+const int TraceGroup::LineThickness = 5;
+const QColor TraceGroup::LineColour(QColor(0x55, 0x57, 0x53));
+
+TraceGroup::~TraceGroup()
+{
+ owner_ = nullptr;
+ clear_child_items();
+}
+
+bool TraceGroup::enabled() const
+{
+ return std::any_of(child_items().begin(), child_items().end(),
+ [](const shared_ptr<ViewItem> &r) { return r->enabled(); });
+}
+
+pv::Session& TraceGroup::session()
+{
+ assert(owner_);
+ return owner_->session();
+}
+
+const pv::Session& TraceGroup::session() const
+{
+ assert(owner_);
+ return owner_->session();
+}
+
+pv::view::View* TraceGroup::view()
+{
+ assert(owner_);
+ return owner_->view();
+}
+
+const pv::view::View* TraceGroup::view() const
+{
+ assert(owner_);
+ return owner_->view();
+}
+
+pair<int, int> TraceGroup::v_extents() const
+{
+ return TraceTreeItemOwner::v_extents();
+}
+
+void TraceGroup::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ const QRectF r = label_rect(rect).adjusted(
+ LineThickness / 2, LineThickness / 2,
+ -LineThickness / 2, -LineThickness / 2);
+
+ // Paint the label
+ const QPointF points[] = {
+ r.topRight(),
+ r.topLeft(),
+ r.bottomLeft(),
+ r.bottomRight()
+ };
+
+ if (selected()) {
+ const QPen pen(highlight_pen());
+ p.setPen(QPen(pen.brush(), pen.width() + LineThickness,
+ Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
+ p.setBrush(Qt::transparent);
+ p.drawPolyline(points, countof(points));
+ }
+
+ p.setPen(QPen(QBrush(LineColour.darker()), LineThickness,
+ Qt::SolidLine, Qt::SquareCap, Qt::RoundJoin));
+ p.drawPolyline(points, countof(points));
+ p.setPen(QPen(QBrush(hover ? LineColour.lighter() : LineColour),
+ LineThickness - 2, Qt::SolidLine, Qt::SquareCap,
+ Qt::RoundJoin));
+ p.drawPolyline(points, countof(points));
+}
+
+QRectF TraceGroup::label_rect(const QRectF &rect) const
+{
+ QRectF child_rect;
+ for (const shared_ptr<ViewItem> r : child_items())
+ if (r && r->enabled())
+ child_rect = child_rect.united(r->label_rect(rect));
+
+ return QRectF(child_rect.x() - Width - Padding, child_rect.y(),
+ Width, child_rect.height());
+}
+
+bool TraceGroup::pt_in_label_rect(int left, int right, const QPoint &point)
+{
+ (void)left;
+ (void)right;
+ (void)point;
+
+ return false;
+}
+
+QMenu* TraceGroup::create_context_menu(QWidget *parent)
+{
+ QMenu *const menu = new QMenu(parent);
+
+ QAction *const ungroup = new QAction(tr("Ungroup"), this);
+ ungroup->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
+ connect(ungroup, SIGNAL(triggered()), this, SLOT(on_ungroup()));
+ menu->addAction(ungroup);
+
+ return menu;
+}
+
+pv::widgets::Popup* TraceGroup::create_popup(QWidget *parent)
+{
+ (void)parent;
+ return nullptr;
+}
+
+int TraceGroup::owner_visual_v_offset() const
+{
+ return owner_ ? visual_v_offset() + owner_->owner_visual_v_offset() : 0;
+}
+
+void TraceGroup::restack_items()
+{
+ vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
+
+ // Sort by the centre line of the extents
+ stable_sort(items.begin(), items.end(),
+ [](const shared_ptr<TraceTreeItem> &a, const shared_ptr<TraceTreeItem> &b) {
+ const auto aext = a->v_extents();
+ const auto bext = b->v_extents();
+ return a->layout_v_offset() +
+ (aext.first + aext.second) / 2 <
+ b->layout_v_offset() +
+ (bext.first + bext.second) / 2;
+ });
+
+ int total_offset = 0;
+ for (shared_ptr<TraceTreeItem> r : items) {
+ const pair<int, int> extents = r->v_extents();
+ if (extents.first == 0 && extents.second == 0)
+ continue;
+
+ // We position disabled traces, so that they are close to the
+ // animation target positon should they be re-enabled
+ if (r->enabled())
+ total_offset += -extents.first;
+
+ if (!r->dragging())
+ r->set_layout_v_offset(total_offset);
+
+ if (r->enabled())
+ total_offset += extents.second;
+ }
+}
+
+unsigned int TraceGroup::depth() const
+{
+ return owner_ ? owner_->depth() + 1 : 0;
+}
+
+void TraceGroup::ungroup()
+{
+ const vector<shared_ptr<TraceTreeItem>> items(trace_tree_child_items());
+ clear_child_items();
+
+ for (shared_ptr<TraceTreeItem> r : items)
+ owner_->add_child_item(r);
+
+ owner_->remove_child_item(shared_from_this());
+}
+
+void TraceGroup::on_ungroup()
+{
+ ungroup();
+}
+
+void TraceGroup::row_item_appearance_changed(bool label, bool content)
+{
+ if (owner_)
+ owner_->row_item_appearance_changed(label, content);
+}
+
+void TraceGroup::extents_changed(bool horz, bool vert)
+{
+ if (owner_)
+ owner_->extents_changed(horz, vert);
+}
+
+} // namespace view
+} // namespace pv
diff --git a/pv/view/tracegroup.hpp b/pv/view/tracegroup.hpp
new file mode 100644
index 0000000..6407f1b
--- /dev/null
+++ b/pv/view/tracegroup.hpp
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_TRACEGROUP_HPP
+#define PULSEVIEW_PV_VIEW_TRACEGROUP_HPP
+
+#include "tracetreeitem.hpp"
+#include "tracetreeitemowner.hpp"
+
+namespace pv {
+namespace view {
+
+class TraceGroup : public TraceTreeItem, public TraceTreeItemOwner
+{
+ Q_OBJECT
+
+private:
+ static const int Padding;
+ static const int Width;
+ static const int LineThickness;
+ static const QColor LineColour;
+
+public:
+ /**
+ * Virtual destructor
+ */
+ virtual ~TraceGroup();
+
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const;
+
+ /**
+ * Returns the session of the onwer.
+ */
+ pv::Session& session();
+
+ /**
+ * Returns the session of the onwer.
+ */
+ const pv::Session& session() const;
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual pv::view::View* view();
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual const pv::view::View* view() const;
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ std::pair<int, int> v_extents() const;
+
+ /**
+ * Paints the signal label.
+ * @param p the QPainter to paint into.
+ * @param right the x-coordinate of the right edge of the header
+ * area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect) const;
+
+ /**
+ * Determines if a point is in the header label rect.
+ * @param left the x-coordinate of the left edge of the header
+ * area.
+ * @param right the x-coordinate of the right edge of the header
+ * area.
+ * @param point the point to test.
+ */
+ bool pt_in_label_rect(int left, int right, const QPoint &point);
+
+ QMenu* create_context_menu(QWidget *parent);
+
+ pv::widgets::Popup* create_popup(QWidget *parent);
+
+ /**
+ * Returns the total vertical offset of this trace and all it's owners
+ */
+ int owner_visual_v_offset() const;
+
+ void restack_items();
+
+ /**
+ * Returns the number of nested parents that this row item owner has.
+ */
+ unsigned int depth() const;
+
+ void ungroup();
+
+public:
+ void row_item_appearance_changed(bool label, bool content);
+
+ void extents_changed(bool horz, bool vert);
+
+private Q_SLOTS:
+ void on_ungroup();
+};
+
+} // view
+} // pv
+
+#endif // PULSEVIEW_PV_VIEW_TRACEGROUP_HPP
diff --git a/pv/view/tracepalette.cpp b/pv/view/tracepalette.cpp
index cefb952..3dcb63d 100644
--- a/pv/view/tracepalette.cpp
+++ b/pv/view/tracepalette.cpp
@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "tracepalette.h"
+#include "tracepalette.hpp"
namespace pv {
namespace view {
diff --git a/pv/view/tracepalette.h b/pv/view/tracepalette.hpp
similarity index 89%
rename from pv/view/tracepalette.h
rename to pv/view/tracepalette.hpp
index ae3c69a..e878a22 100644
--- a/pv/view/tracepalette.h
+++ b/pv/view/tracepalette.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_TRACEPALETTE_H
-#define PULSEVIEW_PV_TRACEPALETTE_H
+#ifndef PULSEVIEW_PV_TRACEPALETTE_HPP
+#define PULSEVIEW_PV_TRACEPALETTE_HPP
#include <QColor>
@@ -38,4 +38,4 @@ public:
} // view
} // pv
-#endif // PULSEVIEW_PV_VIEW_TRACEPALETTE_H
+#endif // PULSEVIEW_PV_VIEW_TRACEPALETTE_HPP
diff --git a/pv/view/tracetreeitem.cpp b/pv/view/tracetreeitem.cpp
new file mode 100644
index 0000000..44c5d4b
--- /dev/null
+++ b/pv/view/tracetreeitem.cpp
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <assert.h>
+
+#include "view.hpp"
+
+#include "tracetreeitem.hpp"
+
+namespace pv {
+namespace view {
+
+TraceTreeItem::TraceTreeItem() :
+ owner_(nullptr),
+ layout_v_offset_(0),
+ visual_v_offset_(0),
+ v_offset_animation_(this, "visual_v_offset")
+{
+}
+
+void TraceTreeItem::select(bool select)
+{
+ ViewItem::select(select);
+ owner_->row_item_appearance_changed(true, true);
+}
+
+int TraceTreeItem::layout_v_offset() const
+{
+ return layout_v_offset_;
+}
+
+void TraceTreeItem::set_layout_v_offset(int v_offset)
+{
+ if (layout_v_offset_ == v_offset)
+ return;
+
+ layout_v_offset_ = v_offset;
+
+ if (owner_)
+ owner_->extents_changed(false, true);
+}
+
+int TraceTreeItem::visual_v_offset() const
+{
+ return visual_v_offset_;
+}
+
+void TraceTreeItem::set_visual_v_offset(int v_offset)
+{
+ visual_v_offset_ = v_offset;
+
+ if (owner_)
+ owner_->row_item_appearance_changed(true, true);
+}
+
+void TraceTreeItem::force_to_v_offset(int v_offset)
+{
+ v_offset_animation_.stop();
+ layout_v_offset_ = visual_v_offset_ = v_offset;
+
+ if (owner_) {
+ owner_->row_item_appearance_changed(true, true);
+ owner_->extents_changed(false, true);
+ }
+}
+
+void TraceTreeItem::animate_to_layout_v_offset()
+{
+ if (visual_v_offset_ == layout_v_offset_ ||
+ (v_offset_animation_.endValue() == layout_v_offset_ &&
+ v_offset_animation_.state() == QAbstractAnimation::Running))
+ return;
+
+ v_offset_animation_.setDuration(100);
+ v_offset_animation_.setStartValue(visual_v_offset_);
+ v_offset_animation_.setEndValue(layout_v_offset_);
+ v_offset_animation_.setEasingCurve(QEasingCurve::OutQuad);
+ v_offset_animation_.start();
+}
+
+TraceTreeItemOwner* TraceTreeItem::owner() const
+{
+ return owner_;
+}
+
+void TraceTreeItem::set_owner(TraceTreeItemOwner *owner)
+{
+ assert(owner_ || owner);
+ v_offset_animation_.stop();
+
+ if (owner_) {
+ const int owner_offset = owner_->owner_visual_v_offset();
+ layout_v_offset_ += owner_offset;
+ visual_v_offset_ += owner_offset;
+ }
+
+ owner_ = owner;
+
+ if (owner_) {
+ const int owner_offset = owner_->owner_visual_v_offset();
+ layout_v_offset_ -= owner_offset;
+ visual_v_offset_ -= owner_offset;
+ }
+}
+
+int TraceTreeItem::get_visual_y() const
+{
+ assert(owner_);
+ return visual_v_offset_ + owner_->owner_visual_v_offset();
+}
+
+void TraceTreeItem::drag_by(const QPoint &delta)
+{
+ assert(owner_);
+ force_to_v_offset(drag_point_.y() + delta.y() -
+ owner_->owner_visual_v_offset());
+}
+
+QPoint TraceTreeItem::point(const QRect &rect) const
+{
+ return QPoint(rect.right(), get_visual_y());
+}
+
+void TraceTreeItem::set_bgcolour_state(bool state)
+{
+ bgcolour_state_ = state;
+}
+
+} // namespace view
+} // namespace pv
diff --git a/pv/view/tracetreeitem.hpp b/pv/view/tracetreeitem.hpp
new file mode 100644
index 0000000..ed0e7e8
--- /dev/null
+++ b/pv/view/tracetreeitem.hpp
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_TRACETREEITEM_HPP
+#define PULSEVIEW_PV_VIEW_TRACETREEITEM_HPP
+
+#include <memory>
+
+#include <QPropertyAnimation>
+
+#include "rowitem.hpp"
+
+namespace pv {
+namespace view {
+
+class TraceTreeItemOwner;
+
+class TraceTreeItem : public RowItem,
+ public std::enable_shared_from_this<pv::view::TraceTreeItem>
+{
+ Q_OBJECT
+ Q_PROPERTY(int visual_v_offset
+ READ visual_v_offset
+ WRITE set_visual_v_offset)
+
+public:
+ /**
+ * Constructor.
+ */
+ TraceTreeItem();
+
+ /**
+ * Gets the owner this item in the view item hierachy.
+ */
+ TraceTreeItemOwner* owner() const;
+
+ /**
+ * Selects or deselects the signal.
+ */
+ void select(bool select = true);
+
+ /**
+ * Gets the vertical layout offset of this signal.
+ */
+ int layout_v_offset() const;
+
+ /**
+ * Sets the vertical layout offset of this signal.
+ */
+ void set_layout_v_offset(int v_offset);
+
+ /**
+ * Gets the vertical visual offset of this signal.
+ */
+ int visual_v_offset() const;
+
+ /**
+ * Sets the vertical visual offset of this signal.
+ */
+ void set_visual_v_offset(int v_offset);
+
+ /**
+ * Sets the visual and layout offset of this signal.
+ */
+ void force_to_v_offset(int v_offset);
+
+ /**
+ * Begins an animation that will animate the visual offset toward
+ * the layout offset.
+ */
+ void animate_to_layout_v_offset();
+
+ /**
+ * Sets the owner this trace in the view trace hierachy.
+ * @param The new owner of the trace.
+ */
+ void set_owner(pv::view::TraceTreeItemOwner *owner);
+
+ /**
+ * Gets the visual y-offset of the axis.
+ */
+ int get_visual_y() const;
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ void drag_by(const QPoint &delta);
+
+ /**
+ * Gets the arrow-tip point of the row item marker.
+ * @param rect the rectangle of the header area.
+ */
+ QPoint point(const QRect &rect) const;
+
+ /**
+ * Sets the new background colour state: false = dark, true = bright.
+ * This is to allow for alternating backgrounds but has no effect
+ * when coloured background colours are used.
+ * @param state New bg color state to use.
+ */
+ void set_bgcolour_state(bool state);
+
+ /**
+ * Computes the vertical extents of the contents of this row item.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ virtual std::pair<int, int> v_extents() const = 0;
+
+protected:
+ TraceTreeItemOwner *owner_;
+
+ int layout_v_offset_;
+ int visual_v_offset_;
+
+ bool bgcolour_state_;
+
+private:
+ QPropertyAnimation v_offset_animation_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_TRACETREEITEM_HPP
diff --git a/pv/view/tracetreeitemowner.cpp b/pv/view/tracetreeitemowner.cpp
new file mode 100644
index 0000000..2417479
--- /dev/null
+++ b/pv/view/tracetreeitemowner.cpp
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cassert>
+
+#include "tracetreeitem.hpp"
+#include "tracetreeitemowner.hpp"
+#include "trace.hpp"
+
+using std::dynamic_pointer_cast;
+using std::max;
+using std::make_pair;
+using std::min;
+using std::pair;
+using std::set;
+using std::shared_ptr;
+using std::static_pointer_cast;
+using std::vector;
+
+namespace pv {
+namespace view {
+
+const ViewItemOwner::item_list& TraceTreeItemOwner::child_items() const
+{
+ return items_;
+}
+
+vector< std::shared_ptr<TraceTreeItem> >
+TraceTreeItemOwner::trace_tree_child_items() const
+{
+ vector< shared_ptr<TraceTreeItem> > items;
+ for (auto &i : items_) {
+ assert(dynamic_pointer_cast<TraceTreeItem>(i));
+ const shared_ptr<TraceTreeItem> t(
+ static_pointer_cast<TraceTreeItem>(i));
+ items.push_back(t);
+ }
+
+ return items;
+}
+
+void TraceTreeItemOwner::clear_child_items()
+{
+ for (auto &t : trace_tree_child_items()) {
+ assert(t->owner() == this);
+ t->set_owner(nullptr);
+ }
+ items_.clear();
+}
+
+void TraceTreeItemOwner::add_child_item(std::shared_ptr<TraceTreeItem> item)
+{
+ assert(!item->owner());
+ item->set_owner(this);
+ items_.push_back(item);
+
+ extents_changed(true, true);
+}
+
+void TraceTreeItemOwner::remove_child_item(std::shared_ptr<TraceTreeItem> item)
+{
+ assert(item->owner() == this);
+ item->set_owner(nullptr);
+ auto iter = std::find(items_.begin(), items_.end(), item);
+ assert(iter != items_.end());
+ items_.erase(iter);
+
+ extents_changed(true, true);
+}
+
+pair<int, int> TraceTreeItemOwner::v_extents() const
+{
+ pair<int, int> extents(INT_MAX, INT_MIN);
+
+ for (const shared_ptr<TraceTreeItem> t : trace_tree_child_items()) {
+ assert(t);
+ if (!t->enabled())
+ continue;
+
+ const int child_offset = t->layout_v_offset();
+ const pair<int, int> child_extents = t->v_extents();
+ extents.first = min(child_extents.first + child_offset,
+ extents.first);
+ extents.second = max(child_extents.second + child_offset,
+ extents.second);
+ }
+
+ return extents;
+}
+
+bool TraceTreeItemOwner::reassign_bgcolour_states(bool next_bgcolour_state)
+{
+ vector< shared_ptr<TraceTreeItem> > items = trace_tree_child_items();
+
+ // Sort items according to vertical position
+ sort(items.begin(), items.end(),
+ [](const shared_ptr<TraceTreeItem> a, const shared_ptr<TraceTreeItem> b) {
+ return a->layout_v_offset() > b->layout_v_offset(); });
+
+ for (const shared_ptr<TraceTreeItem> item : items) {
+ item->set_bgcolour_state(next_bgcolour_state);
+ next_bgcolour_state = !next_bgcolour_state;
+ }
+
+ return next_bgcolour_state;
+}
+
+void TraceTreeItemOwner::restack_items()
+{
+}
+
+} // view
+} // pv
diff --git a/pv/view/tracetreeitemowner.hpp b/pv/view/tracetreeitemowner.hpp
new file mode 100644
index 0000000..6d4b6d1
--- /dev/null
+++ b/pv/view/tracetreeitemowner.hpp
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_TRACETREEITEMOWNER_HPP
+#define PULSEVIEW_PV_VIEW_TRACETREEITEMOWNER_HPP
+
+#include "viewitemowner.hpp"
+#include "tracetreeitem.hpp"
+
+namespace pv {
+
+class Session;
+
+namespace view {
+
+class TraceTreeItem;
+class View;
+
+class TraceTreeItemOwner : public ViewItemOwner
+{
+public:
+ /**
+ * Returns the view of the owner.
+ */
+ virtual pv::view::View* view() = 0;
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual const pv::view::View* view() const = 0;
+
+ virtual int owner_visual_v_offset() const = 0;
+
+ /**
+ * Returns the session of the onwer.
+ */
+ virtual pv::Session& session() = 0;
+
+ /**
+ * Returns the session of the owner.
+ */
+ virtual const pv::Session& session() const = 0;
+
+ /**
+ * Returns the number of nested parents that this row item owner has.
+ */
+ virtual unsigned int depth() const = 0;
+
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ virtual const item_list& child_items() const;
+
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ std::vector< std::shared_ptr<TraceTreeItem> >
+ trace_tree_child_items() const;
+
+ /**
+ * Clears the list of child items.
+ */
+ void clear_child_items();
+
+ /**
+ * Adds a child item to this object.
+ */
+ void add_child_item(std::shared_ptr<TraceTreeItem> item);
+
+ /**
+ * Removes a child item from this object.
+ */
+ void remove_child_item(std::shared_ptr<TraceTreeItem> item);
+
+ virtual void restack_items();
+
+ /**
+ * Computes the vertical extents of the contents of this row item owner.
+ * @return A pair containing the minimum and maximum y-values.
+ */
+ std::pair<int, int> v_extents() const;
+
+ /*
+ * Reassigns background color states to all its children, thereby
+ * providing them with alternating backgrounds.
+ * @param next_bgcolour_state First brightness state to use.
+ * @return The next brightness state to use.
+ */
+ bool reassign_bgcolour_states(bool next_bgcolour_state);
+
+public:
+ virtual void row_item_appearance_changed(bool label, bool content) = 0;
+
+ virtual void extents_changed(bool horz, bool vert) = 0;
+};
+
+} // view
+} // pv
+
+#endif // PULSEVIEW_PV_VIEW_TRACETREEITEMOWNER_HPP
diff --git a/pv/view/triggermarker.cpp b/pv/view/triggermarker.cpp
new file mode 100644
index 0000000..f15672b
--- /dev/null
+++ b/pv/view/triggermarker.cpp
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "triggermarker.hpp"
+#include "view.hpp"
+
+namespace pv {
+namespace view {
+
+const QColor TriggerMarker::Colour(0x00, 0x00, 0xB0);
+
+TriggerMarker::TriggerMarker(View &view, const pv::util::Timestamp& time) :
+ TimeItem(view),
+ time_(time)
+{
+}
+
+TriggerMarker::TriggerMarker(const TriggerMarker &marker) :
+ TimeItem(marker.view_),
+ time_(marker.time_)
+{
+}
+
+bool TriggerMarker::enabled() const
+{
+ return true;
+}
+
+bool TriggerMarker::is_draggable() const
+{
+ return false;
+}
+
+void TriggerMarker::set_time(const pv::util::Timestamp& time)
+{
+ time_ = time;
+
+ view_.time_item_appearance_changed(true, true);
+}
+
+float TriggerMarker::get_x() const
+{
+ return ((time_ - view_.offset()) / view_.scale()).convert_to<float>();
+}
+
+QPoint TriggerMarker::point(const QRect &rect) const
+{
+ return QPoint(get_x(), rect.bottom());
+}
+
+void TriggerMarker::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
+{
+ if (!enabled())
+ return;
+
+ QPen pen(Colour);
+ pen.setStyle(Qt::DashLine);
+
+ const float x = get_x();
+ p.setPen(pen);
+ p.drawLine(QPointF(x, pp.top()), QPointF(x, pp.bottom()));
+}
+
+} // namespace view
+} // namespace pv
diff --git a/pv/view/triggermarker.hpp b/pv/view/triggermarker.hpp
new file mode 100644
index 0000000..cbbcb36
--- /dev/null
+++ b/pv/view/triggermarker.hpp
@@ -0,0 +1,86 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_TRIGGER_MARKER_HPP
+#define PULSEVIEW_PV_VIEW_TRIGGER_MARKER_HPP
+
+#include "timeitem.hpp"
+
+namespace pv {
+namespace view {
+
+class TriggerMarker : public TimeItem
+{
+ Q_OBJECT
+
+public:
+ static const QColor Colour;
+
+public:
+ /**
+ * Constructor.
+ * @param view A reference to the view that owns this marker.
+ * @param time The time to set the marker to.
+ */
+ TriggerMarker(View &view, const pv::util::Timestamp& time);
+
+ /**
+ * Copy constructor.
+ */
+ TriggerMarker(const TriggerMarker &marker);
+
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ bool enabled() const override;
+
+ /**
+ Returns true if the item may be dragged/moved.
+ */
+ bool is_draggable() const override;
+
+ /**
+ * Sets the time of the marker.
+ */
+ void set_time(const pv::util::Timestamp& time) override;
+
+ float get_x() const override;
+
+ /**
+ * Gets the arrow-tip point of the time marker.
+ * @param rect the rectangle of the ruler area.
+ */
+ QPoint point(const QRect &rect) const override;
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ void paint_fore(QPainter &p, const ViewItemPaintParams &pp) override;
+
+private:
+ pv::util::Timestamp time_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_TRIGGER_MARKER_HPP
diff --git a/pv/view/view.cpp b/pv/view/view.cpp
index d117489..c9b08bf 100644
--- a/pv/view/view.cpp
+++ b/pv/view/view.cpp
@@ -22,406 +22,778 @@
#include <libsigrokdecode/libsigrokdecode.h>
#endif
-#include <assert.h>
-#include <limits.h>
-#include <math.h>
+#include <extdef.h>
-#include <boost/foreach.hpp>
+#include <algorithm>
+#include <cassert>
+#include <climits>
+#include <cmath>
+#include <iterator>
+#include <mutex>
+#include <unordered_set>
+#include <boost/thread/locks.hpp>
+
+#include <QApplication>
#include <QEvent>
+#include <QFontMetrics>
#include <QMouseEvent>
#include <QScrollBar>
-#include "decodetrace.h"
-#include "header.h"
-#include "ruler.h"
-#include "signal.h"
-#include "view.h"
-#include "viewport.h"
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include "analogsignal.hpp"
+#include "decodetrace.hpp"
+#include "header.hpp"
+#include "logicsignal.hpp"
+#include "ruler.hpp"
+#include "signal.hpp"
+#include "tracegroup.hpp"
+#include "triggermarker.hpp"
+#include "view.hpp"
+#include "viewport.hpp"
-#include "pv/sigsession.h"
-#include "pv/data/logic.h"
-#include "pv/data/logicsnapshot.h"
+#include "pv/session.hpp"
+#include "pv/devices/device.hpp"
+#include "pv/data/logic.hpp"
+#include "pv/data/logicsegment.hpp"
+#include "pv/util.hpp"
+
+using boost::shared_lock;
+using boost::shared_mutex;
-using boost::shared_ptr;
-using boost::weak_ptr;
using pv::data::SignalData;
+using pv::data::Segment;
+using pv::util::TimeUnit;
+using pv::util::Timestamp;
+
+using std::back_inserter;
+using std::copy_if;
using std::deque;
+using std::dynamic_pointer_cast;
+using std::inserter;
using std::list;
+using std::lock_guard;
using std::max;
using std::make_pair;
+using std::make_shared;
using std::min;
using std::pair;
using std::set;
+using std::set_difference;
+using std::shared_ptr;
+using std::unordered_map;
+using std::unordered_set;
using std::vector;
+using std::weak_ptr;
namespace pv {
namespace view {
-const double View::MaxScale = 1e9;
-const double View::MinScale = 1e-15;
+const Timestamp View::MaxScale("1e9");
+const Timestamp View::MinScale("1e-12");
const int View::MaxScrollValue = INT_MAX / 2;
+const int View::MaxViewAutoUpdateRate = 25; // No more than 25 Hz with sticky scrolling
-const int View::SignalHeight = 30;
-const int View::SignalMargin = 10;
-const int View::SignalSnapGridSize = 10;
+const int View::ScaleUnits[3] = {1, 2, 5};
-const QColor View::CursorAreaColour(220, 231, 243);
-
-const QSizeF View::LabelPadding(4, 0);
-
-View::View(SigSession &session, QWidget *parent) :
+View::View(Session &session, QWidget *parent) :
QAbstractScrollArea(parent),
- _session(session),
- _viewport(new Viewport(*this)),
- _ruler(new Ruler(*this)),
- _header(new Header(*this)),
- _scale(1e-6),
- _offset(0),
- _v_offset(0),
- _updating_scroll(false),
- _show_cursors(false),
- _cursors(*this),
- _hover_point(-1, -1)
+ session_(session),
+ viewport_(new Viewport(*this)),
+ ruler_(new Ruler(*this)),
+ header_(new Header(*this)),
+ scale_(1e-3),
+ offset_(0),
+ updating_scroll_(false),
+ sticky_scrolling_(false), // Default setting is set in MainWindow::setup_ui()
+ always_zoom_to_fit_(false),
+ tick_period_(0),
+ tick_prefix_(pv::util::SIPrefix::yocto),
+ tick_precision_(0),
+ time_unit_(util::TimeUnit::Time),
+ show_cursors_(false),
+ cursors_(new CursorPair(*this)),
+ next_flag_text_('A'),
+ trigger_markers_(),
+ hover_point_(-1, -1)
{
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
this, SLOT(h_scroll_value_changed(int)));
connect(verticalScrollBar(), SIGNAL(valueChanged(int)),
- this, SLOT(v_scroll_value_changed(int)));
+ this, SLOT(v_scroll_value_changed()));
- connect(&_session, SIGNAL(signals_changed()),
+ connect(&session_, SIGNAL(signals_changed()),
this, SLOT(signals_changed()));
- connect(&_session, SIGNAL(data_received()),
+ connect(&session_, SIGNAL(capture_state_changed(int)),
+ this, SLOT(capture_state_updated(int)));
+ connect(&session_, SIGNAL(data_received()),
this, SLOT(data_updated()));
- connect(&_session, SIGNAL(frame_ended()),
+ connect(&session_, SIGNAL(frame_ended()),
this, SLOT(data_updated()));
- connect(_cursors.first().get(), SIGNAL(time_changed()),
- this, SLOT(marker_time_changed()));
- connect(_cursors.second().get(), SIGNAL(time_changed()),
- this, SLOT(marker_time_changed()));
-
- connect(_header, SIGNAL(geometry_updated()),
- this, SLOT(on_geometry_updated()));
- connect(_header, SIGNAL(signals_moved()),
- this, SLOT(on_signals_moved()));
+ connect(header_, SIGNAL(selection_changed()),
+ ruler_, SLOT(clear_selection()));
+ connect(ruler_, SIGNAL(selection_changed()),
+ header_, SLOT(clear_selection()));
- connect(_header, SIGNAL(selection_changed()),
- _ruler, SLOT(clear_selection()));
- connect(_ruler, SIGNAL(selection_changed()),
- _header, SLOT(clear_selection()));
-
- connect(_header, SIGNAL(selection_changed()),
+ connect(header_, SIGNAL(selection_changed()),
this, SIGNAL(selection_changed()));
- connect(_ruler, SIGNAL(selection_changed()),
+ connect(ruler_, SIGNAL(selection_changed()),
this, SIGNAL(selection_changed()));
- setViewport(_viewport);
+ connect(this, SIGNAL(hover_point_changed()),
+ this, SLOT(on_hover_point_changed()));
+
+ connect(&lazy_event_handler_, SIGNAL(timeout()),
+ this, SLOT(process_sticky_events()));
+ lazy_event_handler_.setSingleShot(true);
+
+ connect(&delayed_view_updater_, SIGNAL(timeout()),
+ this, SLOT(perform_delayed_view_update()));
+ delayed_view_updater_.setSingleShot(true);
+ delayed_view_updater_.setInterval(1000 / MaxViewAutoUpdateRate);
+
+ setViewport(viewport_);
- _viewport->installEventFilter(this);
- _ruler->installEventFilter(this);
- _header->installEventFilter(this);
+ viewport_->installEventFilter(this);
+ ruler_->installEventFilter(this);
+ header_->installEventFilter(this);
// Trigger the initial event manually. The default device has signals
// which were created before this object came into being
signals_changed();
+
+ // make sure the transparent widgets are on the top
+ ruler_->raise();
+ header_->raise();
+
+ // Update the zoom state
+ calculate_tick_spacing();
+}
+
+Session& View::session()
+{
+ return session_;
+}
+
+const Session& View::session() const
+{
+ return session_;
+}
+
+View* View::view()
+{
+ return this;
+}
+
+const View* View::view() const
+{
+ return this;
+}
+
+Viewport* View::viewport()
+{
+ return viewport_;
}
-SigSession& View::session()
+const Viewport* View::viewport() const
{
- return _session;
+ return viewport_;
}
-const SigSession& View::session() const
+vector< shared_ptr<TimeItem> > View::time_items() const
{
- return _session;
+ const vector<shared_ptr<Flag>> f(flags());
+ vector<shared_ptr<TimeItem>> items(f.begin(), f.end());
+ items.push_back(cursors_);
+ items.push_back(cursors_->first());
+ items.push_back(cursors_->second());
+
+ for (auto trigger_marker : trigger_markers_)
+ items.push_back(trigger_marker);
+
+ return items;
}
double View::scale() const
{
- return _scale;
+ return scale_;
+}
+
+void View::set_scale(double scale)
+{
+ if (scale_ != scale) {
+ scale_ = scale;
+ Q_EMIT scale_changed();
+ }
+}
+
+const Timestamp& View::offset() const
+{
+ return offset_;
+}
+
+void View::set_offset(const pv::util::Timestamp& offset)
+{
+ if (offset_ != offset) {
+ offset_ = offset;
+ Q_EMIT offset_changed();
+ }
+}
+
+int View::owner_visual_v_offset() const
+{
+ return -verticalScrollBar()->sliderPosition();
+}
+
+void View::set_v_offset(int offset)
+{
+ verticalScrollBar()->setSliderPosition(offset);
+ header_->update();
+ viewport_->update();
+}
+
+unsigned int View::depth() const
+{
+ return 0;
}
-double View::offset() const
+pv::util::SIPrefix View::tick_prefix() const
{
- return _offset;
+ return tick_prefix_;
}
-int View::v_offset() const
+void View::set_tick_prefix(pv::util::SIPrefix tick_prefix)
{
- return _v_offset;
+ if (tick_prefix_ != tick_prefix) {
+ tick_prefix_ = tick_prefix;
+ Q_EMIT tick_prefix_changed();
+ }
+}
+
+unsigned int View::tick_precision() const
+{
+ return tick_precision_;
+}
+
+void View::set_tick_precision(unsigned tick_precision)
+{
+ if (tick_precision_ != tick_precision) {
+ tick_precision_ = tick_precision;
+ Q_EMIT tick_precision_changed();
+ }
+}
+
+const pv::util::Timestamp& View::tick_period() const
+{
+ return tick_period_;
+}
+
+void View::set_tick_period(const pv::util::Timestamp& tick_period)
+{
+ if (tick_period_ != tick_period) {
+ tick_period_ = tick_period;
+ Q_EMIT tick_period_changed();
+ }
+}
+
+TimeUnit View::time_unit() const
+{
+ return time_unit_;
+}
+
+void View::set_time_unit(pv::util::TimeUnit time_unit)
+{
+ if (time_unit_ != time_unit) {
+ time_unit_ = time_unit;
+ Q_EMIT time_unit_changed();
+ }
}
void View::zoom(double steps)
{
- zoom(steps, _viewport->width() / 2);
+ zoom(steps, viewport_->width() / 2);
}
void View::zoom(double steps, int offset)
{
- set_zoom(_scale * pow(3.0/2.0, -steps), offset);
+ set_zoom(scale_ * pow(3.0/2.0, -steps), offset);
}
-void View::zoom_fit()
+void View::zoom_fit(bool gui_state)
{
- const pair<double, double> extents = get_time_extents();
- const double delta = extents.second - extents.first;
- if (delta < 1e-12)
+ // Act as one-shot when stopped, toggle along with the GUI otherwise
+ if (session_.get_capture_state() == Session::Stopped) {
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+ } else {
+ always_zoom_to_fit_ = gui_state;
+ always_zoom_to_fit_changed(gui_state);
+ }
+
+ const pair<Timestamp, Timestamp> extents = get_time_extents();
+ const Timestamp delta = extents.second - extents.first;
+ if (delta < Timestamp("1e-12"))
return;
- assert(_viewport);
- const int w = _viewport->width();
+ assert(viewport_);
+ const int w = viewport_->width();
if (w <= 0)
return;
- const double scale = max(min(delta / w, MaxScale), MinScale);
- set_scale_offset(scale, extents.first);
+ const Timestamp scale = max(min(delta / w, MaxScale), MinScale);
+ set_scale_offset(scale.convert_to<double>(), extents.first);
}
void View::zoom_one_to_one()
{
using pv::data::SignalData;
- const vector< shared_ptr<Signal> > sigs(
- session().get_signals());
-
// Make a set of all the visible data objects
set< shared_ptr<SignalData> > visible_data = get_visible_data();
if (visible_data.empty())
return;
- double samplerate = 0.0;
- BOOST_FOREACH(const shared_ptr<SignalData> d, visible_data) {
- assert(d);
- samplerate = max(samplerate, d->samplerate());
- }
-
- if (samplerate == 0.0)
- return;
-
- assert(_viewport);
- const int w = _viewport->width();
+ assert(viewport_);
+ const int w = viewport_->width();
if (w <= 0)
return;
- set_zoom(1.0 / samplerate, w / 2);
+ set_zoom(1.0 / session_.get_samplerate(), w / 2);
}
-void View::set_scale_offset(double scale, double offset)
+void View::set_scale_offset(double scale, const Timestamp& offset)
{
- _scale = scale;
- _offset = offset;
+ // Disable sticky scrolling / always zoom to fit when acquisition runs
+ // and user drags the viewport
+ if ((scale_ == scale) && (offset_ != offset) &&
+ (session_.get_capture_state() == Session::Running)) {
+
+ if (sticky_scrolling_) {
+ sticky_scrolling_ = false;
+ sticky_scrolling_changed(false);
+ }
+
+ if (always_zoom_to_fit_) {
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+ }
+ }
+
+ set_scale(scale);
+ set_offset(offset);
+
+ calculate_tick_spacing();
update_scroll();
- _ruler->update();
- _viewport->update();
- scale_offset_changed();
+ ruler_->update();
+ viewport_->update();
}
-vector< shared_ptr<Trace> > View::get_traces() const
+set< shared_ptr<SignalData> > View::get_visible_data() const
{
- const vector< shared_ptr<Signal> > sigs(
- session().get_signals());
-#ifdef ENABLE_DECODE
- const vector< shared_ptr<DecodeTrace> > decode_sigs(
- session().get_decode_signals());
- vector< shared_ptr<Trace> > traces(
- sigs.size() + decode_sigs.size());
-#else
- vector< shared_ptr<Trace> > traces(sigs.size());
-#endif
+ const unordered_set< shared_ptr<Signal> > sigs(session().signals());
- vector< shared_ptr<Trace> >::iterator i = traces.begin();
- i = copy(sigs.begin(), sigs.end(), i);
-#ifdef ENABLE_DECODE
- i = copy(decode_sigs.begin(), decode_sigs.end(), i);
-#endif
+ // Make a set of all the visible data objects
+ set< shared_ptr<SignalData> > visible_data;
+ for (const shared_ptr<Signal> sig : sigs)
+ if (sig->enabled())
+ visible_data.insert(sig->data());
- stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets);
- return traces;
+ return visible_data;
}
-list<weak_ptr<SelectableItem> > View::selected_items() const
+pair<Timestamp, Timestamp> View::get_time_extents() const
{
- list<weak_ptr<SelectableItem> > items;
-
- // List the selected signals
- const vector< shared_ptr<Trace> > traces(get_traces());
- BOOST_FOREACH (shared_ptr<Trace> t, traces) {
- assert(t);
- if (t->selected())
- items.push_back(t);
+ boost::optional<Timestamp> left_time, right_time;
+ const set< shared_ptr<SignalData> > visible_data = get_visible_data();
+ for (const shared_ptr<SignalData> d : visible_data) {
+ const vector< shared_ptr<Segment> > segments =
+ d->segments();
+ for (const shared_ptr<Segment> &s : segments) {
+ double samplerate = s->samplerate();
+ samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
+
+ const Timestamp start_time = s->start_time();
+ left_time = left_time ?
+ min(*left_time, start_time) :
+ start_time;
+ right_time = right_time ?
+ max(*right_time, start_time + d->max_sample_count() / samplerate) :
+ start_time + d->max_sample_count() / samplerate;
+ }
}
- // List the selected cursors
- if (_cursors.first()->selected())
- items.push_back(_cursors.first());
- if (_cursors.second()->selected())
- items.push_back(_cursors.second());
+ if (!left_time || !right_time)
+ return make_pair(0, 0);
- return items;
+ assert(*left_time < *right_time);
+ return make_pair(*left_time, *right_time);
}
-set< shared_ptr<SignalData> > View::get_visible_data() const
+void View::enable_sticky_scrolling(bool state)
{
- const vector< shared_ptr<Signal> > sigs(
- session().get_signals());
-
- // Make a set of all the visible data objects
- set< shared_ptr<SignalData> > visible_data;
- BOOST_FOREACH(const shared_ptr<Signal> sig, sigs)
- if (sig->enabled())
- visible_data.insert(sig->data());
-
- return visible_data;
+ sticky_scrolling_ = state;
}
-pair<double, double> View::get_time_extents() const
+void View::enable_coloured_bg(bool state)
{
- const set< shared_ptr<SignalData> > visible_data = get_visible_data();
- if (visible_data.empty())
- return make_pair(0.0, 0.0);
-
- double left_time = DBL_MAX, right_time = DBL_MIN;
- BOOST_FOREACH(const shared_ptr<SignalData> d, visible_data)
- {
- const double start_time = d->get_start_time();
- double samplerate = d->samplerate();
- samplerate = (samplerate <= 0.0) ? 1.0 : samplerate;
-
- left_time = min(left_time, start_time);
- right_time = max(right_time, start_time +
- d->get_max_sample_count() / samplerate);
+ const vector<shared_ptr<TraceTreeItem>> items(
+ list_by_type<TraceTreeItem>());
+
+ for (shared_ptr<TraceTreeItem> i : items) {
+ // Can't cast to Trace because it's abstract, so we need to
+ // check for any derived classes individually
+
+ shared_ptr<AnalogSignal> a = dynamic_pointer_cast<AnalogSignal>(i);
+ if (a)
+ a->set_coloured_bg(state);
+
+ shared_ptr<LogicSignal> l = dynamic_pointer_cast<LogicSignal>(i);
+ if (l)
+ l->set_coloured_bg(state);
+
+ shared_ptr<DecodeTrace> d = dynamic_pointer_cast<DecodeTrace>(i);
+ if (d)
+ d->set_coloured_bg(state);
}
- assert(left_time < right_time);
- return make_pair(left_time, right_time);
+ viewport_->update();
}
bool View::cursors_shown() const
{
- return _show_cursors;
+ return show_cursors_;
}
void View::show_cursors(bool show)
{
- _show_cursors = show;
- _ruler->update();
- _viewport->update();
+ show_cursors_ = show;
+ ruler_->update();
+ viewport_->update();
}
void View::centre_cursors()
{
- const double time_width = _scale * _viewport->width();
- _cursors.first()->set_time(_offset + time_width * 0.4);
- _cursors.second()->set_time(_offset + time_width * 0.6);
- _ruler->update();
- _viewport->update();
+ const double time_width = scale_ * viewport_->width();
+ cursors_->first()->set_time(offset_ + time_width * 0.4);
+ cursors_->second()->set_time(offset_ + time_width * 0.6);
+ ruler_->update();
+ viewport_->update();
}
-CursorPair& View::cursors()
+std::shared_ptr<CursorPair> View::cursors() const
{
- return _cursors;
+ return cursors_;
}
-const CursorPair& View::cursors() const
+void View::add_flag(const Timestamp& time)
{
- return _cursors;
+ flags_.push_back(shared_ptr<Flag>(new Flag(*this, time,
+ QString("%1").arg(next_flag_text_))));
+
+ next_flag_text_ = (next_flag_text_ >= 'Z') ? 'A' :
+ (next_flag_text_ + 1);
+
+ time_item_appearance_changed(true, true);
}
-const QPoint& View::hover_point() const
+void View::remove_flag(std::shared_ptr<Flag> flag)
{
- return _hover_point;
+ flags_.remove(flag);
+ time_item_appearance_changed(true, true);
}
-void View::normalize_layout()
+vector< std::shared_ptr<Flag> > View::flags() const
{
- const vector< shared_ptr<Trace> > traces(get_traces());
+ vector< std::shared_ptr<Flag> > flags(flags_.begin(), flags_.end());
+ stable_sort(flags.begin(), flags.end(),
+ [](const shared_ptr<Flag> &a, const shared_ptr<Flag> &b) {
+ return a->time() < b->time();
+ });
- int v_min = INT_MAX;
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- v_min = min(t->get_v_offset(), v_min);
-
- const int delta = -min(v_min, 0);
- BOOST_FOREACH(shared_ptr<Trace> t, traces)
- t->set_v_offset(t->get_v_offset() + delta);
+ return flags;
+}
- verticalScrollBar()->setSliderPosition(_v_offset + delta);
- v_scroll_value_changed(verticalScrollBar()->sliderPosition());
+const QPoint& View::hover_point() const
+{
+ return hover_point_;
}
void View::update_viewport()
{
- assert(_viewport);
- _viewport->update();
+ assert(viewport_);
+ viewport_->update();
+ header_->update();
+}
+
+void View::restack_all_trace_tree_items()
+{
+ // Make a list of owners that is sorted from deepest first
+ const vector<shared_ptr<TraceTreeItem>> items(
+ list_by_type<TraceTreeItem>());
+ set< TraceTreeItemOwner* > owners;
+ for (const auto &r : items)
+ owners.insert(r->owner());
+ vector< TraceTreeItemOwner* > sorted_owners(owners.begin(), owners.end());
+ sort(sorted_owners.begin(), sorted_owners.end(),
+ [](const TraceTreeItemOwner* a, const TraceTreeItemOwner *b) {
+ return a->depth() > b->depth(); });
+
+ // Restack the items recursively
+ for (auto &o : sorted_owners)
+ o->restack_items();
+
+ // Re-assign background colors
+ bool next_bgcolour_state = 0;
+
+ for (auto &o : sorted_owners)
+ next_bgcolour_state = o->reassign_bgcolour_states(next_bgcolour_state);
+
+ // Animate the items to their destination
+ for (const auto &i : items)
+ i->animate_to_layout_v_offset();
+}
+
+void View::trigger_event(util::Timestamp location)
+{
+ trigger_markers_.push_back(shared_ptr<TriggerMarker>(
+ new TriggerMarker(*this, location)));
}
-void View::get_scroll_layout(double &length, double &offset) const
+void View::get_scroll_layout(double &length, Timestamp &offset) const
{
- const pair<double, double> extents = get_time_extents();
- length = (extents.second - extents.first) / _scale;
- offset = _offset / _scale;
+ const pair<Timestamp, Timestamp> extents = get_time_extents();
+ length = ((extents.second - extents.first) / scale_).convert_to<double>();
+ offset = offset_ / scale_;
}
void View::set_zoom(double scale, int offset)
{
- const double cursor_offset = _offset + _scale * offset;
- const double new_scale = max(min(scale, MaxScale), MinScale);
- const double new_offset = cursor_offset - new_scale * offset;
- set_scale_offset(new_scale, new_offset);
+ // Reset the "always zoom to fit" feature as the user changed the zoom
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+
+ const Timestamp cursor_offset = offset_ + scale_ * offset;
+ const Timestamp new_scale = max(min(Timestamp(scale), MaxScale), MinScale);
+ const Timestamp new_offset = cursor_offset - new_scale * offset;
+ set_scale_offset(new_scale.convert_to<double>(), new_offset);
+}
+
+void View::calculate_tick_spacing()
+{
+ const double SpacingIncrement = 10.0f;
+ const double MinValueSpacing = 40.0f;
+
+ // Figure out the highest numeric value visible on a label
+ const QSize areaSize = viewport_->size();
+ const Timestamp max_time = max(fabs(offset_),
+ fabs(offset_ + scale_ * areaSize.width()));
+
+ double min_width = SpacingIncrement;
+ double label_width, tick_period_width;
+
+ QFontMetrics m(QApplication::font());
+
+ // Copies of the member variables with the same name, used in the calculation
+ // and written back afterwards, so that we don't emit signals all the time
+ // during the calculation.
+ pv::util::Timestamp tick_period = tick_period_;
+ pv::util::SIPrefix tick_prefix = tick_prefix_;
+ unsigned tick_precision = tick_precision_;
+
+ do {
+ const double min_period = scale_ * min_width;
+
+ const int order = (int)floorf(log10f(min_period));
+ const pv::util::Timestamp order_decimal =
+ pow(pv::util::Timestamp(10), order);
+
+ // Allow for a margin of error so that a scale unit of 1 can be used.
+ // Otherwise, for a SU of 1 the tick period will almost always be below
+ // the min_period by a small amount - and thus skipped in favor of 2.
+ // Note: margin assumes that SU[0] and SU[1] contain the smallest values
+ double tp_margin = (ScaleUnits[0] + ScaleUnits[1]) / 2.0;
+ double tp_with_margin;
+ unsigned int unit = 0;
+
+ do {
+ tp_with_margin = order_decimal.convert_to<double>() *
+ (ScaleUnits[unit++] + tp_margin);
+ } while (tp_with_margin < min_period && unit < countof(ScaleUnits));
+
+ tick_period = order_decimal * ScaleUnits[unit - 1];
+ tick_prefix = static_cast<pv::util::SIPrefix>(
+ (order - pv::util::exponent(pv::util::SIPrefix::yocto)) / 3);
+
+ // Precision is the number of fractional digits required, not
+ // taking the prefix into account (and it must never be negative)
+ tick_precision = std::max(ceil(log10(1 / tick_period)).convert_to<int>(), 0);
+
+ tick_period_width = (tick_period / scale_).convert_to<double>();
+
+ const QString label_text = Ruler::format_time_with_distance(
+ tick_period, max_time, tick_prefix, time_unit_, tick_precision);
+
+ label_width = m.boundingRect(0, 0, INT_MAX, INT_MAX,
+ Qt::AlignLeft | Qt::AlignTop, label_text).width() +
+ MinValueSpacing;
+
+ min_width += SpacingIncrement;
+ } while (tick_period_width < label_width);
+
+ set_tick_period(tick_period);
+ set_tick_prefix(tick_prefix);
+ set_tick_precision(tick_precision);
}
void View::update_scroll()
{
- assert(_viewport);
+ assert(viewport_);
- const QSize areaSize = _viewport->size();
+ const QSize areaSize = viewport_->size();
// Set the horizontal scroll bar
- double length = 0, offset = 0;
+ double length = 0;
+ Timestamp offset;
get_scroll_layout(length, offset);
length = max(length - areaSize.width(), 0.0);
+ int major_tick_distance = (tick_period_ / scale_).convert_to<int>();
+
horizontalScrollBar()->setPageStep(areaSize.width() / 2);
+ horizontalScrollBar()->setSingleStep(major_tick_distance);
- _updating_scroll = true;
+ updating_scroll_ = true;
if (length < MaxScrollValue) {
horizontalScrollBar()->setRange(0, length);
- horizontalScrollBar()->setSliderPosition(offset);
+ horizontalScrollBar()->setSliderPosition(offset.convert_to<double>());
} else {
horizontalScrollBar()->setRange(0, MaxScrollValue);
horizontalScrollBar()->setSliderPosition(
- _offset * MaxScrollValue / (_scale * length));
+ (offset_ * MaxScrollValue / (scale_ * length)).convert_to<double>());
}
- _updating_scroll = false;
+ updating_scroll_ = false;
// Set the vertical scrollbar
verticalScrollBar()->setPageStep(areaSize.height());
- verticalScrollBar()->setRange(0,
- _viewport->get_total_height() + SignalMargin -
- areaSize.height());
+ verticalScrollBar()->setSingleStep(areaSize.height() / 8);
+
+ const pair<int, int> extents = v_extents();
+ verticalScrollBar()->setRange(extents.first - (areaSize.height() / 2),
+ extents.second - (areaSize.height() / 2));
}
void View::update_layout()
{
- setViewportMargins(_header->sizeHint().width(),
- _ruler->sizeHint().height(), 0, 0);
- _ruler->setGeometry(_viewport->x(), 0,
- _viewport->width(), _viewport->y());
- _header->setGeometry(0, _viewport->y(),
- _viewport->x(), _viewport->height());
+ setViewportMargins(
+ header_->sizeHint().width() - pv::view::Header::BaselineOffset,
+ ruler_->sizeHint().height(), 0, 0);
+ ruler_->setGeometry(viewport_->x(), 0,
+ viewport_->width(), ruler_->extended_size_hint().height());
+ header_->setGeometry(0, viewport_->y(),
+ header_->extended_size_hint().width(), viewport_->height());
update_scroll();
}
-bool View::compare_trace_v_offsets(const shared_ptr<Trace> &a,
- const shared_ptr<Trace> &b)
+void View::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ (void)p;
+ (void)rect;
+ (void)hover;
+}
+
+QRectF View::label_rect(const QRectF &rect)
+{
+ (void)rect;
+ return QRectF();
+}
+
+TraceTreeItemOwner* View::find_prevalent_trace_group(
+ const shared_ptr<sigrok::ChannelGroup> &group,
+ const unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+ &signal_map)
+{
+ assert(group);
+
+ unordered_set<TraceTreeItemOwner*> owners;
+ vector<TraceTreeItemOwner*> owner_list;
+
+ // Make a set and a list of all the owners
+ for (const auto &channel : group->channels()) {
+ const auto iter = signal_map.find(channel);
+ if (iter == signal_map.end())
+ continue;
+
+ TraceTreeItemOwner *const o = (*iter).second->owner();
+ owner_list.push_back(o);
+ owners.insert(o);
+ }
+
+ // Iterate through the list of owners, and find the most prevalent
+ size_t max_prevalence = 0;
+ TraceTreeItemOwner *prevalent_owner = nullptr;
+ for (TraceTreeItemOwner *owner : owners) {
+ const size_t prevalence = std::count_if(
+ owner_list.begin(), owner_list.end(),
+ [&](TraceTreeItemOwner *o) { return o == owner; });
+ if (prevalence > max_prevalence) {
+ max_prevalence = prevalence;
+ prevalent_owner = owner;
+ }
+ }
+
+ return prevalent_owner;
+}
+
+vector< shared_ptr<Trace> > View::extract_new_traces_for_channels(
+ const vector< shared_ptr<sigrok::Channel> > &channels,
+ const unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+ &signal_map,
+ set< shared_ptr<Trace> > &add_list)
+{
+ vector< shared_ptr<Trace> > filtered_traces;
+
+ for (const auto &channel : channels) {
+ const auto map_iter = signal_map.find(channel);
+ if (map_iter == signal_map.end())
+ continue;
+
+ shared_ptr<Trace> trace = (*map_iter).second;
+ const auto list_iter = add_list.find(trace);
+ if (list_iter == add_list.end())
+ continue;
+
+ filtered_traces.push_back(trace);
+ add_list.erase(list_iter);
+ }
+
+ return filtered_traces;
+}
+
+void View::determine_time_unit()
{
- assert(a);
- assert(b);
- return a->get_v_offset() < b->get_v_offset();
+ // Check whether we know the sample rate and hence can use time as the unit
+ if (time_unit_ == util::TimeUnit::Samples) {
+ const unordered_set< shared_ptr<Signal> > sigs(session().signals());
+
+ // Check all signals but...
+ for (const shared_ptr<Signal> signal : sigs) {
+ const shared_ptr<SignalData> data = signal->data();
+
+ // ...only check first segment of each
+ const vector< shared_ptr<Segment> > segments = data->segments();
+ if (!segments.empty())
+ if (segments[0]->samplerate()) {
+ set_time_unit(util::TimeUnit::Time);
+ break;
+ }
+ }
+ }
}
bool View::eventFilter(QObject *object, QEvent *event)
@@ -430,19 +802,19 @@ bool View::eventFilter(QObject *object, QEvent *event)
if (type == QEvent::MouseMove) {
const QMouseEvent *const mouse_event = (QMouseEvent*)event;
- if (object == _viewport)
- _hover_point = mouse_event->pos();
- else if (object == _ruler)
- _hover_point = QPoint(mouse_event->x(), 0);
- else if (object == _header)
- _hover_point = QPoint(0, mouse_event->y());
+ if (object == viewport_)
+ hover_point_ = mouse_event->pos();
+ else if (object == ruler_)
+ hover_point_ = QPoint(mouse_event->x(), 0);
+ else if (object == header_)
+ hover_point_ = QPoint(0, mouse_event->y());
else
- _hover_point = QPoint(-1, -1);
+ hover_point_ = QPoint(-1, -1);
hover_point_changed();
} else if (type == QEvent::Leave) {
- _hover_point = QPoint(-1, -1);
+ hover_point_ = QPoint(-1, -1);
hover_point_changed();
}
@@ -451,15 +823,17 @@ bool View::eventFilter(QObject *object, QEvent *event)
bool View::viewportEvent(QEvent *e)
{
- switch(e->type()) {
+ switch (e->type()) {
case QEvent::Paint:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::Wheel:
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
return false;
-
default:
return QAbstractScrollArea::viewportEvent(e);
}
@@ -470,69 +844,283 @@ void View::resizeEvent(QResizeEvent*)
update_layout();
}
+void View::row_item_appearance_changed(bool label, bool content)
+{
+ if (label)
+ header_->update();
+ if (content)
+ viewport_->update();
+}
+
+void View::time_item_appearance_changed(bool label, bool content)
+{
+ if (label)
+ ruler_->update();
+ if (content)
+ viewport_->update();
+}
+
+void View::extents_changed(bool horz, bool vert)
+{
+ sticky_events_ |=
+ (horz ? TraceTreeItemHExtentsChanged : 0) |
+ (vert ? TraceTreeItemVExtentsChanged : 0);
+ lazy_event_handler_.start();
+}
+
void View::h_scroll_value_changed(int value)
{
- if (_updating_scroll)
+ if (updating_scroll_)
return;
+ // Disable sticky scrolling when user moves the horizontal scroll bar
+ // during a running acquisition
+ if (sticky_scrolling_ && (session_.get_capture_state() == Session::Running)) {
+ sticky_scrolling_ = false;
+ sticky_scrolling_changed(false);
+ }
+
const int range = horizontalScrollBar()->maximum();
if (range < MaxScrollValue)
- _offset = _scale * value;
+ set_offset(scale_ * value);
else {
- double length = 0, offset;
+ double length = 0;
+ Timestamp offset;
get_scroll_layout(length, offset);
- _offset = _scale * length * value / MaxScrollValue;
+ set_offset(scale_ * length * value / MaxScrollValue);
}
- _ruler->update();
- _viewport->update();
+ ruler_->update();
+ viewport_->update();
}
-void View::v_scroll_value_changed(int value)
+void View::v_scroll_value_changed()
{
- _v_offset = value;
- _header->update();
- _viewport->update();
+ header_->update();
+ viewport_->update();
}
void View::signals_changed()
{
- int offset = SignalMargin + SignalHeight;
- const vector< shared_ptr<Trace> > traces(get_traces());
- BOOST_FOREACH(shared_ptr<Trace> t, traces) {
- t->set_view(this);
- t->set_v_offset(offset);
- offset += SignalHeight + 2 * SignalMargin;
+ using sigrok::Channel;
+
+ vector< shared_ptr<TraceTreeItem> > new_top_level_items;
+
+ const auto device = session_.device();
+ if (!device)
+ return;
+
+ shared_ptr<sigrok::Device> sr_dev = device->device();
+ assert(sr_dev);
+
+ const vector< shared_ptr<Channel> > channels(
+ sr_dev->channels());
+
+ // Make a list of traces that are being added, and a list of traces
+ // that are being removed
+ const vector<shared_ptr<Trace>> prev_trace_list = list_by_type<Trace>();
+ const set<shared_ptr<Trace>> prev_traces(
+ prev_trace_list.begin(), prev_trace_list.end());
+
+ const unordered_set< shared_ptr<Signal> > sigs(session_.signals());
+
+ set< shared_ptr<Trace> > traces(sigs.begin(), sigs.end());
+
+#ifdef ENABLE_DECODE
+ const vector< shared_ptr<DecodeTrace> > decode_traces(
+ session().get_decode_signals());
+ traces.insert(decode_traces.begin(), decode_traces.end());
+#endif
+
+ set< shared_ptr<Trace> > add_traces;
+ set_difference(traces.begin(), traces.end(),
+ prev_traces.begin(), prev_traces.end(),
+ inserter(add_traces, add_traces.begin()));
+
+ set< shared_ptr<Trace> > remove_traces;
+ set_difference(prev_traces.begin(), prev_traces.end(),
+ traces.begin(), traces.end(),
+ inserter(remove_traces, remove_traces.begin()));
+
+ // Make a look-up table of sigrok Channels to pulseview Signals
+ unordered_map<shared_ptr<sigrok::Channel>, shared_ptr<Signal> >
+ signal_map;
+ for (const shared_ptr<Signal> &sig : sigs)
+ signal_map[sig->channel()] = sig;
+
+ // Populate channel groups
+ for (auto entry : sr_dev->channel_groups()) {
+ const shared_ptr<sigrok::ChannelGroup> &group = entry.second;
+
+ if (group->channels().size() <= 1)
+ continue;
+
+ // Find best trace group to add to
+ TraceTreeItemOwner *owner = find_prevalent_trace_group(
+ group, signal_map);
+
+ // If there is no trace group, create one
+ shared_ptr<TraceGroup> new_trace_group;
+ if (!owner) {
+ new_trace_group.reset(new TraceGroup());
+ owner = new_trace_group.get();
+ }
+
+ // Extract traces for the trace group, removing them from
+ // the add list
+ const vector< shared_ptr<Trace> > new_traces_in_group =
+ extract_new_traces_for_channels(group->channels(),
+ signal_map, add_traces);
+
+ // Add the traces to the group
+ const pair<int, int> prev_v_extents = owner->v_extents();
+ int offset = prev_v_extents.second - prev_v_extents.first;
+ for (shared_ptr<Trace> trace : new_traces_in_group) {
+ assert(trace);
+ owner->add_child_item(trace);
+
+ const pair<int, int> extents = trace->v_extents();
+ if (trace->enabled())
+ offset += -extents.first;
+ trace->force_to_v_offset(offset);
+ if (trace->enabled())
+ offset += extents.second;
+ }
+
+ // If this is a new group, enqueue it in the new top level
+ // items list
+ if (!new_traces_in_group.empty() && new_trace_group)
+ new_top_level_items.push_back(new_trace_group);
+ }
+
+ // Enqueue the remaining logic channels in a group
+ vector< shared_ptr<Channel> > logic_channels;
+ copy_if(channels.begin(), channels.end(), back_inserter(logic_channels),
+ [](const shared_ptr<Channel>& c) {
+ return c->type() == sigrok::ChannelType::LOGIC; });
+ const vector< shared_ptr<Trace> > non_grouped_logic_signals =
+ extract_new_traces_for_channels(logic_channels,
+ signal_map, add_traces);
+ const shared_ptr<TraceGroup> non_grouped_trace_group(
+ make_shared<TraceGroup>());
+ for (shared_ptr<Trace> trace : non_grouped_logic_signals)
+ non_grouped_trace_group->add_child_item(trace);
+ new_top_level_items.push_back(non_grouped_trace_group);
+
+ // Enqueue the remaining channels as free ungrouped traces
+ const vector< shared_ptr<Trace> > new_top_level_signals =
+ extract_new_traces_for_channels(channels,
+ signal_map, add_traces);
+ new_top_level_items.insert(new_top_level_items.end(),
+ new_top_level_signals.begin(), new_top_level_signals.end());
+
+ // Enqueue any remaining traces i.e. decode traces
+ new_top_level_items.insert(new_top_level_items.end(),
+ add_traces.begin(), add_traces.end());
+
+ // Remove any removed traces
+ for (shared_ptr<Trace> trace : remove_traces) {
+ TraceTreeItemOwner *const owner = trace->owner();
+ assert(owner);
+ owner->remove_child_item(trace);
+ }
+
+ // Add and position the pending top levels items
+ for (auto item : new_top_level_items) {
+ add_child_item(item);
+
+ // Position the item after the last present item
+ int offset = v_extents().second;
+ const pair<int, int> extents = item->v_extents();
+ if (item->enabled())
+ offset += -extents.first;
+ item->force_to_v_offset(offset);
+ if (item->enabled())
+ offset += extents.second;
}
update_layout();
- normalize_layout();
+
+ header_->update();
+ viewport_->update();
}
-void View::data_updated()
+void View::capture_state_updated(int state)
{
- // Update the scroll bars
- update_scroll();
+ if (state == Session::Running) {
+ set_time_unit(util::TimeUnit::Samples);
+
+ trigger_markers_.clear();
+ }
+
+ if (state == Session::Stopped) {
+ // After acquisition has stopped we need to re-calculate the ticks once
+ // as it's otherwise done when the user pans or zooms, which is too late
+ calculate_tick_spacing();
- // Repaint the view
- _viewport->update();
+ // Reset "always zoom to fit", the acquisition has stopped
+ if (always_zoom_to_fit_) {
+ always_zoom_to_fit_ = false;
+ always_zoom_to_fit_changed(false);
+ }
+ }
}
-void View::marker_time_changed()
+void View::data_updated()
{
- _ruler->update();
- _viewport->update();
+ if (always_zoom_to_fit_ || sticky_scrolling_) {
+ if (!delayed_view_updater_.isActive())
+ delayed_view_updater_.start();
+ } else {
+ determine_time_unit();
+ update_scroll();
+ ruler_->update();
+ viewport_->update();
+ }
}
-void View::on_signals_moved()
+void View::perform_delayed_view_update()
{
+ if (always_zoom_to_fit_)
+ zoom_fit(true);
+
+ if (sticky_scrolling_) {
+ // Make right side of the view sticky
+ double length = 0;
+ Timestamp offset;
+ get_scroll_layout(length, offset);
+
+ const QSize areaSize = viewport_->size();
+ length = max(length - areaSize.width(), 0.0);
+
+ set_offset(scale_ * length);
+ }
+
+ determine_time_unit();
update_scroll();
- signals_moved();
+ ruler_->update();
+ viewport_->update();
}
-void View::on_geometry_updated()
+void View::process_sticky_events()
{
- update_layout();
+ if (sticky_events_ & TraceTreeItemHExtentsChanged)
+ update_layout();
+ if (sticky_events_ & TraceTreeItemVExtentsChanged) {
+ restack_all_trace_tree_items();
+ update_scroll();
+ }
+
+ // Clear the sticky events
+ sticky_events_ = 0;
+}
+
+void View::on_hover_point_changed()
+{
+ const vector<shared_ptr<TraceTreeItem>> trace_tree_items(
+ list_by_type<TraceTreeItem>());
+ for (shared_ptr<TraceTreeItem> r : trace_tree_items)
+ r->hover_point_changed();
}
} // namespace view
diff --git a/pv/view/view.h b/pv/view/view.h
deleted file mode 100644
index 0661637..0000000
--- a/pv/view/view.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * This file is part of the PulseView project.
- *
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PULSEVIEW_PV_VIEW_VIEW_H
-#define PULSEVIEW_PV_VIEW_VIEW_H
-
-#include <stdint.h>
-
-#include <set>
-#include <vector>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <QAbstractScrollArea>
-#include <QSizeF>
-
-#include <pv/data/signaldata.h>
-
-#include "cursorpair.h"
-
-namespace pv {
-
-class SigSession;
-
-namespace view {
-
-class Header;
-class Ruler;
-class Trace;
-class Viewport;
-
-class View : public QAbstractScrollArea {
- Q_OBJECT
-
-private:
- static const double MaxScale;
- static const double MinScale;
-
- static const int MaxScrollValue;
-
-public:
- static const int SignalHeight;
- static const int SignalMargin;
- static const int SignalSnapGridSize;
-
- static const QColor CursorAreaColour;
-
- static const QSizeF LabelPadding;
-
-public:
- explicit View(SigSession &session, QWidget *parent = 0);
-
- SigSession& session();
- const SigSession& session() const;
-
- /**
- * Returns the view time scale in seconds per pixel.
- */
- double scale() const;
-
- /**
- * Returns the time offset of the left edge of the view in
- * seconds.
- */
- double offset() const;
- int v_offset() const;
-
- void zoom(double steps);
- void zoom(double steps, int offset);
-
- void zoom_fit();
-
- void zoom_one_to_one();
-
- /**
- * Sets the scale and offset.
- * @param scale The new view scale in seconds per pixel.
- * @param offset The view time offset in seconds.
- */
- void set_scale_offset(double scale, double offset);
-
- std::vector< boost::shared_ptr<Trace> > get_traces() const;
-
- std::list<boost::weak_ptr<SelectableItem> > selected_items() const;
-
- std::set< boost::shared_ptr<pv::data::SignalData> >
- get_visible_data() const;
-
- std::pair<double, double> get_time_extents() const;
-
- /**
- * Returns true if cursors are displayed. false otherwise.
- */
- bool cursors_shown() const;
-
- /**
- * Shows or hides the cursors.
- */
- void show_cursors(bool show = true);
-
- /**
- * Moves the cursors to a convenient position in the view.
- */
- void centre_cursors();
-
- /**
- * Returns a reference to the pair of cursors.
- */
- CursorPair& cursors();
-
- /**
- * Returns a reference to the pair of cursors.
- */
- const CursorPair& cursors() const;
-
- const QPoint& hover_point() const;
-
- void normalize_layout();
-
- void update_viewport();
-
-signals:
- void hover_point_changed();
-
- void signals_moved();
-
- void selection_changed();
-
- void scale_offset_changed();
-
-private:
- void get_scroll_layout(double &length, double &offset) const;
-
- /**
- * Simultaneously sets the zoom and offset.
- * @param scale The scale to set the view to in seconds per pixel. This
- * value is clamped between MinScale and MaxScale.
- * @param offset The offset of the left edge of the view in seconds.
- */
- void set_zoom(double scale, int offset);
-
- void update_scroll();
-
- void update_layout();
-
- static bool compare_trace_v_offsets(
- const boost::shared_ptr<pv::view::Trace> &a,
- const boost::shared_ptr<pv::view::Trace> &b);
-
-private:
- bool eventFilter(QObject *object, QEvent *event);
-
- bool viewportEvent(QEvent *e);
-
- void resizeEvent(QResizeEvent *e);
-
-private slots:
-
- void h_scroll_value_changed(int value);
- void v_scroll_value_changed(int value);
-
- void signals_changed();
- void data_updated();
-
- void marker_time_changed();
-
- void on_signals_moved();
-
- void on_geometry_updated();
-
-private:
- SigSession &_session;
-
- Viewport *_viewport;
- Ruler *_ruler;
- Header *_header;
-
- /// The view time scale in seconds per pixel.
- double _scale;
-
- /// The view time offset in seconds.
- double _offset;
-
- int _v_offset;
- bool _updating_scroll;
-
- bool _show_cursors;
- CursorPair _cursors;
-
- QPoint _hover_point;
-};
-
-} // namespace view
-} // namespace pv
-
-#endif // PULSEVIEW_PV_VIEW_VIEW_H
diff --git a/pv/view/view.hpp b/pv/view/view.hpp
new file mode 100644
index 0000000..635273d
--- /dev/null
+++ b/pv/view/view.hpp
@@ -0,0 +1,404 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_VIEW_HPP
+#define PULSEVIEW_PV_VIEW_VIEW_HPP
+
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include <QAbstractScrollArea>
+#include <QSizeF>
+#include <QTimer>
+
+#include <pv/data/signaldata.hpp>
+#include <pv/util.hpp>
+
+#include "cursorpair.hpp"
+#include "flag.hpp"
+#include "tracetreeitemowner.hpp"
+
+namespace sigrok {
+class ChannelGroup;
+}
+
+namespace pv {
+
+class Session;
+
+namespace view {
+
+class CursorHeader;
+class Header;
+class Ruler;
+class Trace;
+class Viewport;
+class TriggerMarker;
+
+class View : public QAbstractScrollArea, public TraceTreeItemOwner {
+ Q_OBJECT
+
+private:
+ enum StickyEvents {
+ TraceTreeItemHExtentsChanged = 1,
+ TraceTreeItemVExtentsChanged = 2
+ };
+
+private:
+ static const pv::util::Timestamp MaxScale;
+ static const pv::util::Timestamp MinScale;
+
+ static const int MaxScrollValue;
+ static const int MaxViewAutoUpdateRate;
+
+ static const int ScaleUnits[3];
+
+public:
+ explicit View(Session &session, QWidget *parent = 0);
+
+ Session& session();
+ const Session& session() const;
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual pv::view::View* view();
+
+ /**
+ * Returns the view of the owner.
+ */
+ virtual const pv::view::View* view() const;
+
+ Viewport* viewport();
+
+ const Viewport* viewport() const;
+
+ /**
+ * Gets a list of time markers.
+ */
+ std::vector< std::shared_ptr<TimeItem> > time_items() const;
+
+ /**
+ * Returns the view time scale in seconds per pixel.
+ */
+ double scale() const;
+
+ /**
+ * Returns the time offset of the left edge of the view in
+ * seconds.
+ */
+ const pv::util::Timestamp& offset() const;
+
+ /**
+ * Returns the vertical scroll offset.
+ */
+ int owner_visual_v_offset() const;
+
+ /**
+ * Sets the visual v-offset.
+ */
+ void set_v_offset(int offset);
+
+ /**
+ * Returns the SI prefix to apply to the graticule time markings.
+ */
+ pv::util::SIPrefix tick_prefix() const;
+
+ /**
+ * Returns the number of fractional digits shown for the time markings.
+ */
+ unsigned int tick_precision() const;
+
+ /**
+ * Returns period of the graticule time markings.
+ */
+ const pv::util::Timestamp& tick_period() const;
+
+ /**
+ * Returns the unit of time currently used.
+ */
+ util::TimeUnit time_unit() const;
+
+ /**
+ * Returns the number of nested parents that this row item owner has.
+ */
+ unsigned int depth() const;
+
+ void zoom(double steps);
+ void zoom(double steps, int offset);
+
+ void zoom_fit(bool gui_state);
+
+ void zoom_one_to_one();
+
+ /**
+ * Sets the scale and offset.
+ * @param scale The new view scale in seconds per pixel.
+ * @param offset The view time offset in seconds.
+ */
+ void set_scale_offset(double scale, const pv::util::Timestamp& offset);
+
+ std::set< std::shared_ptr<pv::data::SignalData> >
+ get_visible_data() const;
+
+ std::pair<pv::util::Timestamp, pv::util::Timestamp> get_time_extents() const;
+
+ /**
+ * Enables or disables sticky scrolling, i.e. the view always shows
+ * the most recent samples when capturing data.
+ */
+ void enable_sticky_scrolling(bool state);
+
+ /**
+ * Enables or disables coloured trace backgrounds. If they're not
+ * coloured then they will use alternating colors.
+ */
+ void enable_coloured_bg(bool state);
+
+ /**
+ * Returns true if cursors are displayed. false otherwise.
+ */
+ bool cursors_shown() const;
+
+ /**
+ * Shows or hides the cursors.
+ */
+ void show_cursors(bool show = true);
+
+ /**
+ * Moves the cursors to a convenient position in the view.
+ */
+ void centre_cursors();
+
+ /**
+ * Returns a reference to the pair of cursors.
+ */
+ std::shared_ptr<CursorPair> cursors() const;
+
+ /**
+ * Adds a new flag at a specified time.
+ */
+ void add_flag(const pv::util::Timestamp& time);
+
+ /**
+ * Removes a flag from the list.
+ */
+ void remove_flag(std::shared_ptr<Flag> flag);
+
+ /**
+ * Gets the list of flags.
+ */
+ std::vector< std::shared_ptr<Flag> > flags() const;
+
+ const QPoint& hover_point() const;
+
+ void update_viewport();
+
+ void restack_all_trace_tree_items();
+
+Q_SIGNALS:
+ void hover_point_changed();
+
+ void selection_changed();
+
+ /// Emitted when the offset changed.
+ void offset_changed();
+
+ /// Emitted when the scale changed.
+ void scale_changed();
+
+ void sticky_scrolling_changed(bool state);
+
+ void always_zoom_to_fit_changed(bool state);
+
+ /// Emitted when the tick_prefix changed.
+ void tick_prefix_changed();
+
+ /// Emitted when the tick_precision changed.
+ void tick_precision_changed();
+
+ /// Emitted when the tick_period changed.
+ void tick_period_changed();
+
+ /// Emitted when the time_unit changed.
+ void time_unit_changed();
+
+public Q_SLOTS:
+ void trigger_event(util::Timestamp location);
+
+private:
+ void get_scroll_layout(double &length, pv::util::Timestamp &offset) const;
+
+ /**
+ * Simultaneously sets the zoom and offset.
+ * @param scale The scale to set the view to in seconds per pixel. This
+ * value is clamped between MinScale and MaxScale.
+ * @param offset The offset of the left edge of the view in seconds.
+ */
+ void set_zoom(double scale, int offset);
+
+ /**
+ * Find a tick spacing and number formatting that does not cause
+ * the values to collide.
+ */
+ void calculate_tick_spacing();
+
+ void update_scroll();
+
+ void update_layout();
+
+ /**
+ * Satisifies TraceTreeItem functionality.
+ * @param p the QPainter to paint into.
+ * @param rect the rectangle of the header area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ */
+ QRectF label_rect(const QRectF &rect);
+
+ TraceTreeItemOwner* find_prevalent_trace_group(
+ const std::shared_ptr<sigrok::ChannelGroup> &group,
+ const std::unordered_map<std::shared_ptr<sigrok::Channel>,
+ std::shared_ptr<Signal> > &signal_map);
+
+ static std::vector< std::shared_ptr<Trace> >
+ extract_new_traces_for_channels(
+ const std::vector< std::shared_ptr<sigrok::Channel> > &channels,
+ const std::unordered_map<std::shared_ptr<sigrok::Channel>,
+ std::shared_ptr<Signal> > &signal_map,
+ std::set< std::shared_ptr<Trace> > &add_list);
+
+ void determine_time_unit();
+
+ bool eventFilter(QObject *object, QEvent *event);
+
+ bool viewportEvent(QEvent *e);
+
+ void resizeEvent(QResizeEvent *e);
+
+public:
+ void row_item_appearance_changed(bool label, bool content);
+ void time_item_appearance_changed(bool label, bool content);
+
+ void extents_changed(bool horz, bool vert);
+
+private Q_SLOTS:
+
+ void h_scroll_value_changed(int value);
+ void v_scroll_value_changed();
+
+ void signals_changed();
+ void capture_state_updated(int state);
+ void data_updated();
+
+ void perform_delayed_view_update();
+
+ void process_sticky_events();
+
+ void on_hover_point_changed();
+
+ /**
+ * Sets the 'offset_' member and emits the 'offset_changed'
+ * signal if needed.
+ */
+ void set_offset(const pv::util::Timestamp& offset);
+
+ /**
+ * Sets the 'scale_' member and emits the 'scale_changed'
+ * signal if needed.
+ */
+ void set_scale(double scale);
+
+ /**
+ * Sets the 'tick_prefix_' member and emits the 'tick_prefix_changed'
+ * signal if needed.
+ */
+ void set_tick_prefix(pv::util::SIPrefix tick_prefix);
+
+ /**
+ * Sets the 'tick_precision_' member and emits the 'tick_precision_changed'
+ * signal if needed.
+ */
+ void set_tick_precision(unsigned tick_precision);
+
+ /**
+ * Sets the 'tick_period_' member and emits the 'tick_period_changed'
+ * signal if needed.
+ */
+ void set_tick_period(const pv::util::Timestamp& tick_period);
+
+ /**
+ * Sets the 'time_unit' member and emits the 'time_unit_changed'
+ * signal if needed.
+ */
+ void set_time_unit(pv::util::TimeUnit time_unit);
+
+private:
+ Session &session_;
+
+ Viewport *viewport_;
+ Ruler *ruler_;
+ Header *header_;
+
+ /// The view time scale in seconds per pixel.
+ double scale_;
+
+ /// The view time offset in seconds.
+ pv::util::Timestamp offset_;
+
+ bool updating_scroll_;
+ bool sticky_scrolling_;
+ bool always_zoom_to_fit_;
+ QTimer delayed_view_updater_;
+
+ pv::util::Timestamp tick_period_;
+ pv::util::SIPrefix tick_prefix_;
+ unsigned int tick_precision_;
+ util::TimeUnit time_unit_;
+
+ bool show_cursors_;
+ std::shared_ptr<CursorPair> cursors_;
+
+ std::list< std::shared_ptr<Flag> > flags_;
+ char next_flag_text_;
+
+ std::vector< std::shared_ptr<TriggerMarker> > trigger_markers_;
+
+ QPoint hover_point_;
+
+ unsigned int sticky_events_;
+ QTimer lazy_event_handler_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_VIEW_HPP
diff --git a/pv/view/viewitem.cpp b/pv/view/viewitem.cpp
new file mode 100644
index 0000000..e9602f8
--- /dev/null
+++ b/pv/view/viewitem.cpp
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "viewitem.hpp"
+
+#include <climits>
+
+#include <QApplication>
+#include <QMenu>
+#include <QPalette>
+
+namespace pv {
+namespace view {
+
+const QSizeF ViewItem::LabelPadding(4, 0);
+const int ViewItem::HighlightRadius = 3;
+
+ViewItem::ViewItem() :
+ context_parent_(nullptr),
+ drag_point_(INT_MIN, INT_MIN),
+ selected_(false)
+{
+}
+
+bool ViewItem::selected() const
+{
+ return selected_;
+}
+
+void ViewItem::select(bool select)
+{
+ selected_ = select;
+}
+
+bool ViewItem::is_draggable() const
+{
+ return true;
+}
+
+bool ViewItem::dragging() const
+{
+ return drag_point_.x() != INT_MIN && drag_point_.y() != INT_MIN;
+}
+
+void ViewItem::drag()
+{
+ if (is_draggable())
+ drag_point_ = point(QRect());
+}
+
+void ViewItem::drag_release()
+{
+ drag_point_ = QPoint(INT_MIN, INT_MIN);
+}
+
+QRectF ViewItem::label_rect(const QRectF &rect) const
+{
+ (void)rect;
+ return QRectF();
+}
+
+QRectF ViewItem::hit_box_rect(const ViewItemPaintParams &pp) const
+{
+ (void)pp;
+ return QRectF();
+}
+
+QMenu* ViewItem::create_context_menu(QWidget *parent)
+{
+ context_parent_ = parent;
+ return new QMenu(parent);
+}
+
+widgets::Popup* ViewItem::create_popup(QWidget *parent)
+{
+ (void)parent;
+ return nullptr;
+}
+
+void ViewItem::delete_pressed()
+{
+}
+
+QPen ViewItem::highlight_pen()
+{
+ return QPen(QApplication::palette().brush(
+ QPalette::Highlight), HighlightRadius * 2,
+ Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
+}
+
+void ViewItem::paint_label(QPainter &p, const QRect &rect, bool hover)
+{
+ (void)p;
+ (void)rect;
+ (void)hover;
+}
+
+void ViewItem::paint_back(QPainter &p, const ViewItemPaintParams &pp)
+{
+ (void)p;
+ (void)pp;
+}
+
+void ViewItem::paint_mid(QPainter &p, const ViewItemPaintParams &pp)
+{
+ (void)p;
+ (void)pp;
+}
+
+void ViewItem::paint_fore(QPainter &p, const ViewItemPaintParams &pp)
+{
+ (void)p;
+ (void)pp;
+}
+
+QColor ViewItem::select_text_colour(QColor background)
+{
+ return (background.lightness() > 110) ? Qt::black : Qt::white;
+}
+
+} // namespace view
+} // namespace pv
diff --git a/pv/view/viewitem.hpp b/pv/view/viewitem.hpp
new file mode 100644
index 0000000..a4eb6ae
--- /dev/null
+++ b/pv/view/viewitem.hpp
@@ -0,0 +1,178 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEWITEM_HPP
+#define PULSEVIEW_PV_VIEWITEM_HPP
+
+#include <list>
+
+#include <QPen>
+
+#include "viewitempaintparams.hpp"
+
+class QAction;
+class QMenu;
+class QWidget;
+
+namespace pv {
+
+namespace widgets {
+class Popup;
+}
+
+namespace view {
+
+class ViewItemOwner;
+
+class ViewItem : public QObject
+{
+ Q_OBJECT
+
+public:
+ static const QSizeF LabelPadding;
+ static const int HighlightRadius;
+
+public:
+ ViewItem();
+
+public:
+ /**
+ * Returns true if the item is visible and enabled.
+ */
+ virtual bool enabled() const = 0;
+
+ /**
+ * Returns true if the item has been selected by the user.
+ */
+ bool selected() const;
+
+ /**
+ * Selects or deselects the signal.
+ */
+ virtual void select(bool select = true);
+
+ /**
+ * Returns true if the item may be dragged/moved.
+ */
+ virtual bool is_draggable() const;
+
+ /**
+ * Returns true if the item is being dragged.
+ */
+ bool dragging() const;
+
+ /**
+ * Sets this item into the dragged state.
+ */
+ void drag();
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ virtual void drag_release();
+
+ /**
+ * Drags the item to a delta relative to the drag point.
+ * @param delta the offset from the drag point.
+ */
+ virtual void drag_by(const QPoint &delta) = 0;
+
+ /**
+ * Get the drag point.
+ * @param rect the rectangle of the widget area.
+ */
+ virtual QPoint point(const QRect &rect) const = 0;
+
+ /**
+ * Computes the outline rectangle of a label.
+ * @param rect the rectangle of the header area.
+ * @return Returns the rectangle of the signal label.
+ * @remarks The default implementation returns an empty rectangle.
+ */
+ virtual QRectF label_rect(const QRectF &rect) const;
+
+ /**
+ * Computes the outline rectangle of the viewport hit-box.
+ * @param rect the rectangle of the viewport area.
+ * @return Returns the rectangle of the hit-box.
+ * @remarks The default implementation returns an empty hit-box.
+ */
+ virtual QRectF hit_box_rect(const ViewItemPaintParams &pp) const;
+
+ /**
+ * Paints the signal label.
+ * @param p the QPainter to paint into.
+ * @param rect the rectangle of the header area.
+ * @param hover true if the label is being hovered over by the mouse.
+ */
+ virtual void paint_label(QPainter &p, const QRect &rect, bool hover);
+
+ /**
+ * Paints the background layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_back(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Paints the mid-layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_mid(QPainter &p, const ViewItemPaintParams &pp);
+
+ /**
+ * Paints the foreground layer of the item with a QPainter
+ * @param p the QPainter to paint into.
+ * @param pp the painting parameters object to paint with.
+ */
+ virtual void paint_fore(QPainter &p, const ViewItemPaintParams &pp);
+
+public:
+ /**
+ * Gets the text colour.
+ * @remarks This colour is computed by comparing the lightness
+ * of the trace colour against a threshold to determine whether
+ * white or black would be more visible.
+ */
+ static QColor select_text_colour(QColor background);
+
+public:
+ virtual QMenu* create_context_menu(QWidget *parent);
+
+ virtual pv::widgets::Popup* create_popup(QWidget *parent);
+
+ virtual void delete_pressed();
+
+protected:
+ static QPen highlight_pen();
+
+protected:
+ QWidget *context_parent_;
+ QPoint drag_point_;
+
+private:
+ bool selected_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWITEM_HPP
diff --git a/pv/view/viewitemiterator.hpp b/pv/view/viewitemiterator.hpp
new file mode 100644
index 0000000..eed67a9
--- /dev/null
+++ b/pv/view/viewitemiterator.hpp
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_VIEWITEMITERATOR_HPP
+#define PULSEVIEW_PV_VIEW_VIEWITEMITERATOR_HPP
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <stack>
+#include <type_traits>
+#include <vector>
+
+#include <pv/session.hpp>
+
+namespace pv {
+namespace view {
+
+template<class Owner, class Item> class ViewItemIterator
+{
+public:
+ typedef typename Owner::item_list::const_iterator child_iterator;
+ typedef std::shared_ptr<Item> value_type;
+ typedef ptrdiff_t difference_type;
+ typedef value_type pointer;
+ typedef const value_type& reference;
+ typedef std::forward_iterator_tag iterator_category;
+
+public:
+ ViewItemIterator(Owner *owner) :
+ owner_stack_({owner}) {}
+
+ ViewItemIterator(Owner *owner, child_iterator iter) :
+ owner_stack_({owner}) {
+ assert(owner);
+ if (iter != owner->child_items().end())
+ iter_stack_.push(iter);
+ }
+
+ ViewItemIterator(const ViewItemIterator<Owner, Item> &o) :
+ owner_stack_(o.owner_stack_),
+ iter_stack_(o.iter_stack_) {}
+
+ reference operator*() const {
+ return *iter_stack_.top();
+ }
+
+ reference operator->() const {
+ return *this;
+ }
+
+ ViewItemIterator<Owner, Item>& operator++() {
+ using std::dynamic_pointer_cast;
+ using std::shared_ptr;
+
+ assert(!owner_stack_.empty());
+ assert(!iter_stack_.empty());
+
+ shared_ptr<Owner> owner(dynamic_pointer_cast<Owner>(
+ *iter_stack_.top()));
+ if (owner && !owner->child_items().empty()) {
+ owner_stack_.push(owner.get());
+ iter_stack_.push(owner->child_items().begin());
+ } else {
+ while (!iter_stack_.empty() && (++iter_stack_.top()) ==
+ owner_stack_.top()->child_items().end()) {
+ owner_stack_.pop();
+ iter_stack_.pop();
+ }
+ }
+
+ return *this;
+ }
+
+ ViewItemIterator<Owner, Item> operator++(int) {
+ ViewItemIterator<Owner, Item> pre = *this;
+ ++*this;
+ return pre;
+ }
+
+ bool operator==(const ViewItemIterator &o) const {
+ return (iter_stack_.empty() && o.iter_stack_.empty()) || (
+ iter_stack_.size() == o.iter_stack_.size() &&
+ owner_stack_.top() == o.owner_stack_.top() &&
+ iter_stack_.top() == o.iter_stack_.top());
+ }
+
+ bool operator!=(const ViewItemIterator &o) const {
+ return !((const ViewItemIterator&)*this == o);
+ }
+
+ void swap(ViewItemIterator<Owner, Item>& other) {
+ swap(owner_stack_, other.owner_stack_);
+ swap(iter_stack_, other.iter_stack_);
+ }
+
+private:
+ std::stack<Owner*> owner_stack_;
+ std::stack<child_iterator> iter_stack_;
+};
+
+template<class Owner, class Item>
+void swap(ViewItemIterator<Owner, Item>& a, ViewItemIterator<Owner, Item>& b)
+{
+ a.swap(b);
+}
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_VIEWITEMITERATOR_HPP
diff --git a/pv/data/decode/rowdata.cpp b/pv/view/viewitemowner.cpp
similarity index 58%
copy from pv/data/decode/rowdata.cpp
copy to pv/view/viewitemowner.cpp
index 87da27c..8a70478 100644
--- a/pv/data/decode/rowdata.cpp
+++ b/pv/view/viewitemowner.cpp
@@ -18,41 +18,43 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "rowdata.h"
-
+#include <cassert>
+
+#include "tracetreeitem.hpp"
+#include "tracetreeitemowner.hpp"
+#include "trace.hpp"
+
+using std::dynamic_pointer_cast;
+using std::max;
+using std::make_pair;
+using std::min;
+using std::pair;
+using std::set;
+using std::shared_ptr;
using std::vector;
namespace pv {
-namespace data {
-namespace decode {
+namespace view {
-RowData::RowData()
+ViewItemOwner::iterator ViewItemOwner::begin()
{
+ return iterator(this, items_.begin());
}
-uint64_t RowData::get_max_sample() const
+ViewItemOwner::iterator ViewItemOwner::end()
{
- if (_annotations.empty())
- return 0;
- return _annotations.back().end_sample();
+ return iterator(this);
}
-void RowData::get_annotation_subset(
- vector<pv::data::decode::Annotation> &dest,
- uint64_t start_sample, uint64_t end_sample) const
+ViewItemOwner::const_iterator ViewItemOwner::begin() const
{
- for (vector<Annotation>::const_iterator i = _annotations.begin();
- i != _annotations.end(); i++)
- if ((*i).end_sample() > start_sample &&
- (*i).start_sample() <= end_sample)
- dest.push_back(*i);
+ return const_iterator(this, items_.cbegin());
}
-void RowData::push_annotation(const Annotation &a)
+ViewItemOwner::const_iterator ViewItemOwner::end() const
{
- _annotations.push_back(a);
+ return const_iterator(this);
}
-} // decode
-} // data
+} // view
} // pv
diff --git a/pv/view/viewitemowner.hpp b/pv/view/viewitemowner.hpp
new file mode 100644
index 0000000..8d34059
--- /dev/null
+++ b/pv/view/viewitemowner.hpp
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_VIEWITEMOWNER_HPP
+#define PULSEVIEW_PV_VIEW_VIEWITEMOWNER_HPP
+
+#include <memory>
+#include <vector>
+
+#include "viewitemiterator.hpp"
+
+namespace pv {
+
+class Session;
+
+namespace view {
+
+class ViewItem;
+class View;
+
+class ViewItemOwner
+{
+public:
+ typedef std::vector< std::shared_ptr<ViewItem> > item_list;
+ typedef ViewItemIterator<ViewItemOwner, ViewItem> iterator;
+ typedef ViewItemIterator<const ViewItemOwner, ViewItem> const_iterator;
+
+public:
+ /**
+ * Returns a list of row items owned by this object.
+ */
+ virtual const item_list& child_items() const = 0;
+
+ /**
+ * Returns a depth-first iterator at the beginning of the child ViewItem
+ * tree.
+ */
+ iterator begin();
+
+ /**
+ * Returns a depth-first iterator at the end of the child ViewItem tree.
+ */
+ iterator end();
+
+ /**
+ * Returns a constant depth-first iterator at the beginning of the
+ * child ViewItem tree.
+ */
+ const_iterator begin() const;
+
+ /**
+ * Returns a constant depth-first iterator at the end of the child
+ * ViewItem tree.
+ */
+ const_iterator end() const;
+
+ /**
+ * Creates a list of decendant signals filtered by type.
+ */
+ template<class T>
+ std::vector< std::shared_ptr<T> > list_by_type() {
+ std::vector< std::shared_ptr<T> > items;
+ for (const auto &r : *this) {
+ std::shared_ptr<T> p = std::dynamic_pointer_cast<T>(r);
+ if (p)
+ items.push_back(p);
+ }
+
+ return items;
+ }
+
+protected:
+ item_list items_;
+};
+
+} // view
+} // pv
+
+#endif // PULSEVIEW_PV_VIEW_VIEWITEMOWNER_HPP
diff --git a/pv/device/device.h b/pv/view/viewitempaintparams.cpp
similarity index 63%
rename from pv/device/device.h
rename to pv/view/viewitempaintparams.cpp
index b9e1729..998045c 100644
--- a/pv/device/device.h
+++ b/pv/view/viewitempaintparams.cpp
@@ -18,34 +18,34 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_DEVICE_DEVICE_H
-#define PULSEVIEW_PV_DEVICE_DEVICE_H
+#include <cassert>
-#include "devinst.h"
+#include <QApplication>
+#include <QFontMetrics>
+
+#include "viewitempaintparams.hpp"
namespace pv {
-namespace device {
+namespace view {
-class Device : public DevInst
+ViewItemPaintParams::ViewItemPaintParams(
+ const QRect &rect, double scale, const pv::util::Timestamp& offset) :
+ rect_(rect),
+ scale_(scale),
+ offset_(offset)
{
-public:
- Device(sr_dev_inst *dev_inst);
-
- sr_dev_inst* dev_inst() const;
-
- void use(SigSession *owner) throw(QString);
-
- void release();
+ assert(scale > 0.0);
+}
- std::string format_device_title() const;
-
- bool is_trigger_enabled() const;
-
-private:
- sr_dev_inst *const _sdi;
-};
+QFont ViewItemPaintParams::font()
+{
+ return QApplication::font();
+}
-} // device
-} // pv
+int ViewItemPaintParams::text_height()
+{
+ return QFontMetrics(font()).height();
+}
-#endif // PULSVIEW_PV_DEVICE_DEVICE_H
+} // namespace view
+} // namespace pv
diff --git a/pv/view/viewitempaintparams.hpp b/pv/view/viewitempaintparams.hpp
new file mode 100644
index 0000000..bba0d70
--- /dev/null
+++ b/pv/view/viewitempaintparams.hpp
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_VIEWITEMPAINTPARAMS_HPP
+#define PULSEVIEW_PV_VIEW_VIEWITEMPAINTPARAMS_HPP
+
+#include "pv/util.hpp"
+
+#include <QFont>
+#include <QRect>
+
+namespace pv {
+namespace view {
+
+class ViewItemPaintParams
+{
+public:
+ ViewItemPaintParams(
+ const QRect &rect, double scale, const pv::util::Timestamp& offset);
+
+ QRect rect() const {
+ return rect_;
+ }
+
+ double scale() const {
+ return scale_;
+ }
+
+ const pv::util::Timestamp& offset() const {
+ return offset_;
+ }
+
+ int left() const {
+ return rect_.left();
+ }
+
+ int right() const {
+ return rect_.right();
+ }
+
+ int top() const {
+ return rect_.top();
+ }
+
+ int bottom() const {
+ return rect_.bottom();
+ }
+
+ int width() const {
+ return rect_.width();
+ }
+
+ int height() const {
+ return rect_.height();
+ }
+
+ double pixels_offset() const {
+ return (offset_ / scale_).convert_to<double>();
+ }
+
+public:
+ static QFont font();
+
+ static int text_height();
+
+private:
+ QRect rect_;
+ double scale_;
+ pv::util::Timestamp offset_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_VIEWITEMPAINTPARAMS_HPP
diff --git a/pv/view/viewport.cpp b/pv/view/viewport.cpp
index 3b06cf9..9e13666 100644
--- a/pv/view/viewport.cpp
+++ b/pv/view/viewport.cpp
@@ -18,103 +18,178 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "view.h"
-#include "viewport.h"
+#include <cassert>
+#include <cmath>
+#include <algorithm>
+#include <limits>
-#include "signal.h"
-#include "../sigsession.h"
+#include "signal.hpp"
+#include "view.hpp"
+#include "viewitempaintparams.hpp"
+#include "viewport.hpp"
-#include <QMouseEvent>
+#include <pv/session.hpp>
-#include <boost/foreach.hpp>
+#include <QMouseEvent>
-using boost::shared_ptr;
+using std::abs;
+using std::back_inserter;
+using std::copy;
+using std::dynamic_pointer_cast;
using std::max;
using std::min;
+using std::none_of;
+using std::numeric_limits;
+using std::shared_ptr;
+using std::stable_sort;
using std::vector;
namespace pv {
namespace view {
Viewport::Viewport(View &parent) :
- QWidget(&parent),
- _view(parent)
+ ViewWidget(parent),
+ pinch_zoom_active_(false)
{
- setMouseTracking(true);
setAutoFillBackground(true);
setBackgroundRole(QPalette::Base);
+}
- connect(&_view.session(), SIGNAL(signals_changed()),
- this, SLOT(on_signals_changed()));
+shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
+{
+ const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
+ const vector< shared_ptr<ViewItem> > items(this->items());
+ for (auto i = items.rbegin(); i != items.rend(); i++)
+ if ((*i)->enabled() &&
+ (*i)->hit_box_rect(pp).contains(pt))
+ return *i;
+ return nullptr;
+}
- connect(&_view, SIGNAL(signals_moved()),
- this, SLOT(on_signals_moved()));
+void Viewport::item_hover(const shared_ptr<ViewItem> &item)
+{
+ if (item && item->is_draggable())
+ setCursor(dynamic_pointer_cast<RowItem>(item) ?
+ Qt::SizeVerCursor : Qt::SizeHorCursor);
+ else
+ unsetCursor();
+}
- // Trigger the initial event manually. The default device has signals
- // which were created before this object came into being
- on_signals_changed();
+void Viewport::drag()
+{
+ drag_offset_ = view_.offset();
+ drag_v_offset_ = view_.owner_visual_v_offset();
}
-int Viewport::get_total_height() const
+void Viewport::drag_by(const QPoint &delta)
{
- int h = 0;
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
- BOOST_FOREACH(const shared_ptr<Trace> t, traces) {
- assert(t);
- h = max(t->get_v_offset() + View::SignalHeight, h);
- }
+ if (drag_offset_ == boost::none)
+ return;
- return h;
+ view_.set_scale_offset(view_.scale(),
+ (*drag_offset_ - delta.x() * view_.scale()));
+
+ view_.set_v_offset(-drag_v_offset_ - delta.y());
}
-void Viewport::paintEvent(QPaintEvent*)
+void Viewport::drag_release()
{
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
+ drag_offset_ = boost::none;
+}
- QPainter p(this);
- p.setRenderHint(QPainter::Antialiasing);
+vector< shared_ptr<ViewItem> > Viewport::items()
+{
+ vector< shared_ptr<ViewItem> > items;
+ const std::vector< shared_ptr<ViewItem> > view_items(
+ view_.list_by_type<ViewItem>());
+ copy(view_items.begin(), view_items.end(), back_inserter(items));
+ const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
+ copy(time_items.begin(), time_items.end(), back_inserter(items));
+ return items;
+}
- if (_view.cursors_shown())
- _view.cursors().draw_viewport_background(p, rect());
+bool Viewport::touch_event(QTouchEvent *event)
+{
+ QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
- // Plot the signal
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- {
- assert(t);
- t->paint_back(p, 0, width());
+ if (touchPoints.count() != 2) {
+ pinch_zoom_active_ = false;
+ return false;
}
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- t->paint_mid(p, 0, width());
+ const QTouchEvent::TouchPoint &touchPoint0 = touchPoints.first();
+ const QTouchEvent::TouchPoint &touchPoint1 = touchPoints.last();
+
+ if (!pinch_zoom_active_ ||
+ (event->touchPointStates() & Qt::TouchPointPressed)) {
+ pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.pos().x()).convert_to<double>();
+ pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.pos().x()).convert_to<double>();
+ pinch_zoom_active_ = true;
+ }
- BOOST_FOREACH(const shared_ptr<Trace> t, traces)
- t->paint_fore(p, 0, width());
+ double w = touchPoint1.pos().x() - touchPoint0.pos().x();
+ if (abs(w) >= 1.0) {
+ const double scale =
+ fabs((pinch_offset1_ - pinch_offset0_) / w);
+ double offset = pinch_offset0_ - touchPoint0.pos().x() * scale;
+ if (scale > 0)
+ view_.set_scale_offset(scale, offset);
+ }
- if (_view.cursors_shown())
- _view.cursors().draw_viewport_foreground(p, rect());
+ if (event->touchPointStates() & Qt::TouchPointReleased) {
+ pinch_zoom_active_ = false;
+
+ if (touchPoint0.state() & Qt::TouchPointReleased) {
+ // Primary touch released
+ drag_release();
+ } else {
+ // Update the mouse down fields so that continued
+ // dragging with the primary touch will work correctly
+ mouse_down_point_ = touchPoint0.pos().toPoint();
+ drag();
+ }
+ }
- p.end();
+ return true;
}
-void Viewport::mousePressEvent(QMouseEvent *event)
+void Viewport::paintEvent(QPaintEvent*)
{
- assert(event);
+ vector< shared_ptr<RowItem> > row_items(view_.list_by_type<RowItem>());
+ assert(none_of(row_items.begin(), row_items.end(),
+ [](const shared_ptr<RowItem> &r) { return !r; }));
- _mouse_down_point = event->pos();
- _mouse_down_offset = _view.offset();
-}
+ stable_sort(row_items.begin(), row_items.end(),
+ [](const shared_ptr<RowItem> &a, const shared_ptr<RowItem> &b) {
+ return a->point(QRect()).y() < b->point(QRect()).y(); });
-void Viewport::mouseMoveEvent(QMouseEvent *event)
-{
- assert(event);
+ const vector< shared_ptr<TimeItem> > time_items(view_.time_items());
+ assert(none_of(time_items.begin(), time_items.end(),
+ [](const shared_ptr<TimeItem> &t) { return !t; }));
- if (event->buttons() & Qt::LeftButton)
- {
- _view.set_scale_offset(_view.scale(),
- _mouse_down_offset +
- (_mouse_down_point - event->pos()).x() *
- _view.scale());
- }
+ QPainter p(this);
+ p.setRenderHint(QPainter::Antialiasing);
+
+ const ViewItemPaintParams pp(rect(), view_.scale(), view_.offset());
+
+ for (const shared_ptr<TimeItem> t : time_items)
+ t->paint_back(p, pp);
+ for (const shared_ptr<RowItem> r : row_items)
+ r->paint_back(p, pp);
+
+ for (const shared_ptr<TimeItem> t : time_items)
+ t->paint_mid(p, pp);
+ for (const shared_ptr<RowItem> r : row_items)
+ r->paint_mid(p, pp);
+
+ for (const shared_ptr<RowItem> r : row_items)
+ r->paint_fore(p, pp);
+
+ p.setRenderHint(QPainter::Antialiasing, false);
+ for (const shared_ptr<TimeItem> t : time_items)
+ t->paint_fore(p, pp);
+
+ p.end();
}
void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
@@ -122,40 +197,31 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
assert(event);
if (event->buttons() & Qt::LeftButton)
- _view.zoom(2.0, event->x());
+ view_.zoom(2.0, event->x());
else if (event->buttons() & Qt::RightButton)
- _view.zoom(-2.0, event->x());
+ view_.zoom(-2.0, event->x());
}
-void Viewport::wheelEvent(QWheelEvent *event)
+void Viewport::wheelEvent(QWheelEvent *e)
{
- assert(event);
-
- if (event->orientation() == Qt::Vertical) {
- // Vertical scrolling is interpreted as zooming in/out
- _view.zoom(event->delta() / 120, event->x());
- } else if (event->orientation() == Qt::Horizontal) {
+ assert(e);
+
+ if (e->orientation() == Qt::Vertical) {
+ if (e->modifiers() & Qt::ControlModifier) {
+ // Vertical scrolling with the control key pressed
+ // is intrepretted as vertical scrolling
+ view_.set_v_offset(-view_.owner_visual_v_offset() -
+ (e->delta() * height()) / (8 * 120));
+ } else {
+ // Vertical scrolling is interpreted as zooming in/out
+ view_.zoom(e->delta() / 120, e->x());
+ }
+ } else if (e->orientation() == Qt::Horizontal) {
// Horizontal scrolling is interpreted as moving left/right
- _view.set_scale_offset(_view.scale(),
- event->delta() * _view.scale()
- + _view.offset());
+ view_.set_scale_offset(view_.scale(),
+ e->delta() * view_.scale() + view_.offset());
}
}
-void Viewport::on_signals_changed()
-{
- const vector< shared_ptr<Trace> > traces(_view.get_traces());
- BOOST_FOREACH(shared_ptr<Trace> t, traces) {
- assert(t);
- connect(t.get(), SIGNAL(visibility_changed()),
- this, SLOT(update()));
- }
-}
-
-void Viewport::on_signals_moved()
-{
- update();
-}
-
} // namespace view
} // namespace pv
diff --git a/pv/view/viewport.hpp b/pv/view/viewport.hpp
new file mode 100644
index 0000000..704e731
--- /dev/null
+++ b/pv/view/viewport.hpp
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEW_VIEWPORT_HPP
+#define PULSEVIEW_PV_VIEW_VIEWPORT_HPP
+
+#include <boost/optional.hpp>
+
+#include <QTimer>
+#include <QTouchEvent>
+
+#include "pv/util.hpp"
+#include "viewwidget.hpp"
+
+class QPainter;
+class QPaintEvent;
+class Session;
+
+namespace pv {
+namespace view {
+
+class View;
+
+class Viewport : public ViewWidget
+{
+ Q_OBJECT
+
+public:
+ explicit Viewport(View &parent);
+
+private:
+ /**
+ * Indicates when a view item is being hovered over.
+ * @param item The item that is being hovered over, or @c nullptr
+ * if no view item is being hovered over.
+ */
+ void item_hover(const std::shared_ptr<pv::view::ViewItem> &item);
+
+ /**
+ * Gets the first view item which has a hit-box that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ std::shared_ptr<pv::view::ViewItem> get_mouse_over_item(
+ const QPoint &pt);
+
+ /**
+ * Sets this item into the dragged state.
+ */
+ void drag();
+
+ /**
+ * Drag the background by the delta offset.
+ * @param delta the drag offset in pixels.
+ */
+ void drag_by(const QPoint &delta);
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ void drag_release();
+
+ /**
+ * Gets the items in the view widget.
+ */
+ std::vector< std::shared_ptr<pv::view::ViewItem> > items();
+
+ /**
+ * Handles touch begin update and end events.
+ * @param e the event that triggered this handler.
+ */
+ bool touch_event(QTouchEvent *e);
+
+private:
+ void paintEvent(QPaintEvent *event);
+
+ void mouseDoubleClickEvent(QMouseEvent * event);
+ void wheelEvent(QWheelEvent *event);
+
+private:
+ boost::optional<pv::util::Timestamp> drag_offset_;
+ int drag_v_offset_;
+
+ double pinch_offset0_;
+ double pinch_offset1_;
+ bool pinch_zoom_active_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEW_VIEWPORT_HPP
diff --git a/pv/view/viewwidget.cpp b/pv/view/viewwidget.cpp
new file mode 100644
index 0000000..30d96eb
--- /dev/null
+++ b/pv/view/viewwidget.cpp
@@ -0,0 +1,301 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <QApplication>
+#include <QMouseEvent>
+#include <QTouchEvent>
+
+#include "tracetreeitem.hpp"
+#include "view.hpp"
+#include "viewwidget.hpp"
+
+using std::any_of;
+using std::shared_ptr;
+using std::vector;
+
+namespace pv {
+namespace view {
+
+ViewWidget::ViewWidget(View &parent) :
+ QWidget(&parent),
+ view_(parent),
+ item_dragging_(false)
+{
+ setFocusPolicy(Qt::ClickFocus);
+ setAttribute(Qt::WA_AcceptTouchEvents, true);
+ setMouseTracking(true);
+}
+
+void ViewWidget::clear_selection()
+{
+ const auto items = this->items();
+ for (auto &i : items)
+ i->select(false);
+}
+
+void ViewWidget::item_hover(const shared_ptr<ViewItem> &item)
+{
+ (void)item;
+}
+
+void ViewWidget::item_clicked(const shared_ptr<ViewItem> &item)
+{
+ (void)item;
+}
+
+bool ViewWidget::accept_drag() const
+{
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ const vector< shared_ptr<TraceTreeItem> > trace_tree_items(
+ view_.list_by_type<TraceTreeItem>());
+
+ const bool any_row_items_selected = any_of(
+ trace_tree_items.begin(), trace_tree_items.end(),
+ [](const shared_ptr<TraceTreeItem> &r) { return r->selected(); });
+
+ const bool any_time_items_selected = any_of(items.begin(), items.end(),
+ [](const shared_ptr<TimeItem> &i) { return i->selected(); });
+
+ if (any_row_items_selected && !any_time_items_selected) {
+ // Check all the drag items share a common owner
+ TraceTreeItemOwner *item_owner = nullptr;
+ for (shared_ptr<TraceTreeItem> r : trace_tree_items)
+ if (r->dragging()) {
+ if (!item_owner)
+ item_owner = r->owner();
+ else if (item_owner != r->owner())
+ return false;
+ }
+
+ return true;
+ } else if (any_time_items_selected && !any_row_items_selected) {
+ return true;
+ }
+
+ // A background drag is beginning
+ return true;
+}
+
+bool ViewWidget::mouse_down() const
+{
+ return mouse_down_point_.x() != INT_MIN &&
+ mouse_down_point_.y() != INT_MIN;
+}
+
+void ViewWidget::drag_items(const QPoint &delta)
+{
+ bool item_dragged = false;
+
+ // Drag the row items
+ const vector< shared_ptr<RowItem> > row_items(
+ view_.list_by_type<RowItem>());
+ for (shared_ptr<RowItem> r : row_items)
+ if (r->dragging()) {
+ r->drag_by(delta);
+
+ // Ensure the trace is selected
+ r->select();
+
+ item_dragged = true;
+ }
+
+ // If an item is being dragged, update the stacking
+ TraceTreeItemOwner *item_owner = nullptr;
+ const vector< shared_ptr<TraceTreeItem> > trace_tree_items(
+ view_.list_by_type<TraceTreeItem>());
+ for (shared_ptr<TraceTreeItem> i : trace_tree_items)
+ if (i->dragging())
+ item_owner = i->owner();
+
+ if (item_owner) {
+ item_owner->restack_items();
+ for (shared_ptr<TraceTreeItem> i : trace_tree_items)
+ i->animate_to_layout_v_offset();
+ }
+
+ // Drag the time items
+ const vector< shared_ptr<TimeItem> > items(view_.time_items());
+ for (auto &i : items)
+ if (i->dragging()) {
+ i->drag_by(delta);
+ item_dragged = true;
+ }
+
+ // Do the background drag
+ if (!item_dragged)
+ drag_by(delta);
+}
+
+void ViewWidget::drag()
+{
+}
+
+void ViewWidget::drag_by(const QPoint &delta)
+{
+ (void)delta;
+}
+
+void ViewWidget::drag_release()
+{
+}
+
+void ViewWidget::mouse_left_press_event(QMouseEvent *event)
+{
+ (void)event;
+
+ const bool ctrl_pressed =
+ QApplication::keyboardModifiers() & Qt::ControlModifier;
+
+ // Clear selection if control is not pressed and this item is unselected
+ if ((!mouse_down_item_ || !mouse_down_item_->selected()) &&
+ !ctrl_pressed)
+ clear_selection();
+
+ // Set the signal selection state if the item has been clicked
+ if (mouse_down_item_) {
+ if (ctrl_pressed)
+ mouse_down_item_->select(!mouse_down_item_->selected());
+ else
+ mouse_down_item_->select(true);
+ }
+
+ // Save the offsets of any signals which will be dragged
+ bool item_dragged = false;
+ const auto items = this->items();
+ for (auto &i : items)
+ if (i->selected()) {
+ item_dragged = true;
+ i->drag();
+ }
+
+ // Do the background drag
+ if (!item_dragged)
+ drag();
+
+ selection_changed();
+}
+
+void ViewWidget::mouse_left_release_event(QMouseEvent *event)
+{
+ assert(event);
+
+ auto items = this->items();
+ const bool ctrl_pressed =
+ QApplication::keyboardModifiers() & Qt::ControlModifier;
+
+ // Unselect everything if control is not pressed
+ const shared_ptr<ViewItem> mouse_over =
+ get_mouse_over_item(event->pos());
+
+ for (auto &i : items)
+ i->drag_release();
+
+ if (item_dragging_)
+ view_.restack_all_trace_tree_items();
+ else {
+ if (!ctrl_pressed) {
+ for (shared_ptr<ViewItem> i : items)
+ if (mouse_down_item_ != i)
+ i->select(false);
+
+ if (mouse_down_item_)
+ item_clicked(mouse_down_item_);
+ }
+ }
+
+ item_dragging_ = false;
+}
+
+bool ViewWidget::touch_event(QTouchEvent *e)
+{
+ (void)e;
+ return false;
+}
+
+bool ViewWidget::event(QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ if (touch_event(static_cast<QTouchEvent *>(event)))
+ return true;
+ break;
+
+ default:
+ break;
+ }
+
+ return QWidget::event(event);
+}
+
+void ViewWidget::mousePressEvent(QMouseEvent *event)
+{
+ assert(event);
+
+ mouse_down_point_ = event->pos();
+ mouse_down_item_ = get_mouse_over_item(event->pos());
+
+ if (event->button() & Qt::LeftButton)
+ mouse_left_press_event(event);
+}
+
+void ViewWidget::mouseReleaseEvent(QMouseEvent *event)
+{
+ assert(event);
+ if (event->button() & Qt::LeftButton)
+ mouse_left_release_event(event);
+
+ mouse_down_point_ = QPoint(INT_MIN, INT_MIN);
+ mouse_down_item_ = nullptr;
+}
+
+void ViewWidget::mouseMoveEvent(QMouseEvent *e)
+{
+ assert(e);
+ mouse_point_ = e->pos();
+
+ if (!e->buttons())
+ item_hover(get_mouse_over_item(e->pos()));
+ else if (e->buttons() & Qt::LeftButton) {
+ if (!item_dragging_) {
+ if ((e->pos() - mouse_down_point_).manhattanLength() <
+ QApplication::startDragDistance())
+ return;
+
+ if (!accept_drag())
+ return;
+
+ item_dragging_ = true;
+ }
+
+ // Do the drag
+ drag_items(e->pos() - mouse_down_point_);
+ }
+}
+
+void ViewWidget::leaveEvent(QEvent*)
+{
+ mouse_point_ = QPoint(-1, -1);
+ update();
+}
+
+} // namespace view
+} // namespace pv
diff --git a/pv/view/viewwidget.hpp b/pv/view/viewwidget.hpp
new file mode 100644
index 0000000..e2c04f2
--- /dev/null
+++ b/pv/view/viewwidget.hpp
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_VIEWWIDGET_HPP
+#define PULSEVIEW_PV_VIEWWIDGET_HPP
+
+#include <memory>
+
+#include <QWidget>
+
+class QTouchEvent;
+
+namespace pv {
+namespace view {
+
+class View;
+class ViewItem;
+
+class ViewWidget : public QWidget
+{
+ Q_OBJECT
+
+protected:
+ ViewWidget(View &parent);
+
+ /**
+ * Indicates when a view item is being hovered over.
+ * @param item The item that is being hovered over, or @c nullptr
+ * if no view item is being hovered over.
+ * @remarks the default implementation does nothing.
+ */
+ virtual void item_hover(
+ const std::shared_ptr<pv::view::ViewItem> &item);
+
+ /**
+ * Indicates the event an a view item has been clicked.
+ * @param item the view item that has been clicked.
+ * @remarks the default implementation does nothing.
+ */
+ virtual void item_clicked(
+ const std::shared_ptr<pv::view::ViewItem> &item);
+
+ /**
+ * Returns true if the selection of row items allows dragging.
+ * @return Returns true if the drag is acceptable.
+ */
+ bool accept_drag() const;
+
+ /**
+ * Returns true if the mouse button is down.
+ */
+ bool mouse_down() const;
+
+ /**
+ * Drag the dragging items by the delta offset.
+ * @param delta the drag offset in pixels.
+ */
+ void drag_items(const QPoint &delta);
+
+ /**
+ * Sets this item into the dragged state.
+ */
+ virtual void drag();
+
+ /**
+ * Drag the background by the delta offset.
+ * @param delta the drag offset in pixels.
+ * @remarks The default implementation does nothing.
+ */
+ virtual void drag_by(const QPoint &delta);
+
+ /**
+ * Sets this item into the un-dragged state.
+ */
+ virtual void drag_release();
+
+ /**
+ * Gets the items in the view widget.
+ */
+ virtual std::vector< std::shared_ptr<pv::view::ViewItem> > items() = 0;
+
+ /**
+ * Gets the first view item which has a hit-box that contains @c pt .
+ * @param pt the point to search with.
+ * @return the view item that has been found, or and empty
+ * @c shared_ptr if no item was found.
+ */
+ virtual std::shared_ptr<pv::view::ViewItem> get_mouse_over_item(
+ const QPoint &pt) = 0;
+
+ /**
+ * Handles left mouse button press events.
+ * @param event the mouse event that triggered this handler.
+ */
+ void mouse_left_press_event(QMouseEvent *event);
+
+ /**
+ * Handles left mouse button release events.
+ * @param event the mouse event that triggered this handler.
+ */
+ void mouse_left_release_event(QMouseEvent *event);
+
+ /**
+ * Handles touch begin update and end events.
+ * @param e the event that triggered this handler.
+ */
+ virtual bool touch_event(QTouchEvent *e);
+
+protected:
+ bool event(QEvent *event);
+
+ void mousePressEvent(QMouseEvent * event);
+ void mouseReleaseEvent(QMouseEvent *event);
+ void mouseMoveEvent(QMouseEvent *event);
+
+ void leaveEvent(QEvent *event);
+
+public Q_SLOTS:
+ void clear_selection();
+
+Q_SIGNALS:
+ void selection_changed();
+
+protected:
+ pv::view::View &view_;
+ QPoint mouse_point_;
+ QPoint mouse_down_point_;
+ std::shared_ptr<ViewItem> mouse_down_item_;
+ bool item_dragging_;
+};
+
+} // namespace view
+} // namespace pv
+
+#endif // PULSEVIEW_PV_VIEWWIDGET_HPP
diff --git a/pv/widgets/colourbutton.cpp b/pv/widgets/colourbutton.cpp
index fd417c1..13b9673 100644
--- a/pv/widgets/colourbutton.cpp
+++ b/pv/widgets/colourbutton.cpp
@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "colourbutton.h"
+#include "colourbutton.hpp"
#include <assert.h>
@@ -32,37 +32,35 @@ const int ColourButton::SwatchMargin = 7;
ColourButton::ColourButton(int rows, int cols, QWidget *parent) :
QPushButton("", parent),
- _popup(rows, cols, this)
+ popup_(rows, cols, this)
{
connect(this, SIGNAL(clicked(bool)), this, SLOT(on_clicked(bool)));
- connect(&_popup, SIGNAL(selected(int, int)),
+ connect(&popup_, SIGNAL(selected(int, int)),
this, SLOT(on_selected(int, int)));
}
ColourPopup& ColourButton::popup()
{
- return _popup;
+ return popup_;
}
const QColor& ColourButton::colour() const
{
- return _cur_colour;
+ return cur_colour_;
}
void ColourButton::set_colour(QColor colour)
{
- _cur_colour = colour;
+ cur_colour_ = colour;
- const unsigned int rows = _popup.well_array().numRows();
- const unsigned int cols = _popup.well_array().numCols();
+ const unsigned int rows = popup_.well_array().numRows();
+ const unsigned int cols = popup_.well_array().numCols();
for (unsigned int r = 0; r < rows; r++)
for (unsigned int c = 0; c < cols; c++)
- if (_popup.well_array().cellBrush(r, c).color() ==
- colour)
- {
- _popup.well_array().setSelected(r, c);
- _popup.well_array().setCurrent(r, c);
+ if (popup_.well_array().cellBrush(r, c).color() == colour) {
+ popup_.well_array().setSelected(r, c);
+ popup_.well_array().setCurrent(r, c);
return;
}
}
@@ -71,25 +69,25 @@ void ColourButton::set_palette(const QColor *const palette)
{
assert(palette);
- const unsigned int rows = _popup.well_array().numRows();
- const unsigned int cols = _popup.well_array().numCols();
+ const unsigned int rows = popup_.well_array().numRows();
+ const unsigned int cols = popup_.well_array().numCols();
for (unsigned int r = 0; r < rows; r++)
for (unsigned int c = 0; c < cols; c++)
- _popup.well_array().setCellBrush(r, c,
+ popup_.well_array().setCellBrush(r, c,
QBrush(palette[r * cols + c]));
}
void ColourButton::on_clicked(bool)
{
- _popup.set_position(mapToGlobal(rect().center()), Popup::Bottom);
- _popup.show();
+ popup_.set_position(mapToGlobal(rect().center()), Popup::Bottom);
+ popup_.show();
}
void ColourButton::on_selected(int row, int col)
{
- _cur_colour = _popup.well_array().cellBrush(row, col).color();
- selected(_cur_colour);
+ cur_colour_ = popup_.well_array().cellBrush(row, col).color();
+ selected(cur_colour_);
}
void ColourButton::paintEvent(QPaintEvent *e)
@@ -101,7 +99,7 @@ void ColourButton::paintEvent(QPaintEvent *e)
const QRect r = rect().adjusted(SwatchMargin, SwatchMargin,
-SwatchMargin, -SwatchMargin);
p.setPen(QApplication::palette().color(QPalette::Dark));
- p.setBrush(QBrush(_cur_colour));
+ p.setBrush(QBrush(cur_colour_));
p.drawRect(r);
}
diff --git a/pv/widgets/colourbutton.h b/pv/widgets/colourbutton.hpp
similarity index 85%
rename from pv/widgets/colourbutton.h
rename to pv/widgets/colourbutton.hpp
index 45a3fff..86e8e98 100644
--- a/pv/widgets/colourbutton.h
+++ b/pv/widgets/colourbutton.hpp
@@ -18,12 +18,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_COLOURBUTTON_H
-#define PULSEVIEW_PV_WIDGETS_COLOURBUTTON_H
+#ifndef PULSEVIEW_PV_WIDGETS_COLOURBUTTON_HPP
+#define PULSEVIEW_PV_WIDGETS_COLOURBUTTON_HPP
#include <QPushButton>
-#include "colourpopup.h"
+#include "colourpopup.hpp"
namespace pv {
namespace widgets {
@@ -49,20 +49,20 @@ public:
private:
void paintEvent(QPaintEvent *e);
-private slots:
+private Q_SLOTS:
void on_clicked(bool);
void on_selected(int row, int col);
-signals:
+Q_SIGNALS:
void selected(const QColor &colour);
private:
- ColourPopup _popup;
- QColor _cur_colour;
+ ColourPopup popup_;
+ QColor cur_colour_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_COLOURBUTTON_H
+#endif // PULSEVIEW_PV_WIDGETS_COLOURBUTTON_HPP
diff --git a/pv/widgets/colourpopup.cpp b/pv/widgets/colourpopup.cpp
index 816a4c5..8c8b138 100644
--- a/pv/widgets/colourpopup.cpp
+++ b/pv/widgets/colourpopup.cpp
@@ -18,28 +18,28 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "colourpopup.h"
+#include "colourpopup.hpp"
namespace pv {
namespace widgets {
ColourPopup::ColourPopup(int rows, int cols, QWidget *parent) :
Popup(parent),
- _well_array(rows, cols, this),
- _layout(this)
+ well_array_(rows, cols, this),
+ layout_(this)
{
- _layout.addWidget(&_well_array);
- setLayout(&_layout);
+ layout_.addWidget(&well_array_);
+ setLayout(&layout_);
- connect(&_well_array, SIGNAL(selected(int, int)),
+ connect(&well_array_, SIGNAL(selected(int, int)),
this, SIGNAL(selected(int, int)));
- connect(&_well_array, SIGNAL(selected(int, int)),
+ connect(&well_array_, SIGNAL(selected(int, int)),
this, SLOT(colour_selected(int, int)));
}
-QWellArray& ColourPopup::well_array()
+WellArray& ColourPopup::well_array()
{
- return _well_array;
+ return well_array_;
}
void ColourPopup::colour_selected(int, int)
diff --git a/pv/widgets/colourpopup.h b/pv/widgets/colourpopup.hpp
similarity index 79%
copy from pv/widgets/colourpopup.h
copy to pv/widgets/colourpopup.hpp
index 4f5c52e..2abf1b7 100644
--- a/pv/widgets/colourpopup.h
+++ b/pv/widgets/colourpopup.hpp
@@ -18,11 +18,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H
-#define PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H
+#ifndef PULSEVIEW_PV_WIDGETS_COLOURPOPUP_HPP
+#define PULSEVIEW_PV_WIDGETS_COLOURPOPUP_HPP
-#include "popup.h"
-#include "wellarray.h"
+#include "popup.hpp"
+#include "wellarray.hpp"
#include <QVBoxLayout>
@@ -36,20 +36,20 @@ class ColourPopup : public Popup
public:
ColourPopup(int rows, int cols, QWidget *partent);
- QWellArray& well_array();
+ WellArray& well_array();
-signals:
+Q_SIGNALS:
void selected(int row, int col);
-private slots:
+private Q_SLOTS:
void colour_selected(int, int);
private:
- QWellArray _well_array;
- QVBoxLayout _layout;
+ WellArray well_array_;
+ QVBoxLayout layout_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H
+#endif // PULSEVIEW_PV_WIDGETS_COLOURPOPUP_HPP
diff --git a/pv/widgets/decodergroupbox.cpp b/pv/widgets/decodergroupbox.cpp
index 2cbd532..81e5e90 100644
--- a/pv/widgets/decodergroupbox.cpp
+++ b/pv/widgets/decodergroupbox.cpp
@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "decodergroupbox.h"
+#include "decodergroupbox.hpp"
#include <QHBoxLayout>
#include <QLabel>
@@ -30,45 +30,47 @@
namespace pv {
namespace widgets {
-DecoderGroupBox::DecoderGroupBox(QString title, QWidget *parent) :
+DecoderGroupBox::DecoderGroupBox(QString title, QWidget *parent, bool isDeletable) :
QWidget(parent),
- _layout(new QGridLayout),
- _show_hide_button(QIcon(":/icons/decoder-shown.svg"), QString(), this)
+ layout_(new QGridLayout),
+ show_hide_button_(QIcon(":/icons/decoder-shown.svg"), QString(), this)
{
- _layout->setContentsMargins(0, 0, 0, 0);
- setLayout(_layout);
+ layout_->setContentsMargins(0, 0, 0, 0);
+ setLayout(layout_);
- _layout->addWidget(new QLabel(QString("<h3>%1</h3>").arg(title)),
+ layout_->addWidget(new QLabel(QString("<h3>%1</h3>").arg(title)),
0, 0);
- _layout->setColumnStretch(0, 1);
+ layout_->setColumnStretch(0, 1);
QHBoxLayout *const toolbar = new QHBoxLayout;
- _layout->addLayout(toolbar, 0, 1);
+ layout_->addLayout(toolbar, 0, 1);
- _show_hide_button.setFlat(true);
- _show_hide_button.setIconSize(QSize(16, 16));
- connect(&_show_hide_button, SIGNAL(clicked()),
+ show_hide_button_.setFlat(true);
+ show_hide_button_.setIconSize(QSize(16, 16));
+ connect(&show_hide_button_, SIGNAL(clicked()),
this, SIGNAL(show_hide_decoder()));
- toolbar->addWidget(&_show_hide_button);
+ toolbar->addWidget(&show_hide_button_);
- QPushButton *const delete_button = new QPushButton(
- QIcon(":/icons/decoder-delete.svg"), QString(), this);
- delete_button->setFlat(true);
- delete_button->setIconSize(QSize(16, 16));
- connect(delete_button, SIGNAL(clicked()),
- this, SIGNAL(delete_decoder()));
- toolbar->addWidget(delete_button);
+ if (isDeletable) {
+ QPushButton *const delete_button = new QPushButton(
+ QIcon(":/icons/decoder-delete.svg"), QString(), this);
+ delete_button->setFlat(true);
+ delete_button->setIconSize(QSize(16, 16));
+ connect(delete_button, SIGNAL(clicked()),
+ this, SIGNAL(delete_decoder()));
+ toolbar->addWidget(delete_button);
+ }
}
void DecoderGroupBox::add_layout(QLayout *layout)
{
assert(layout);
- _layout->addLayout(layout, 1, 0, 1, 2);
+ layout_->addLayout(layout, 1, 0, 1, 2);
}
void DecoderGroupBox::set_decoder_visible(bool visible)
{
- _show_hide_button.setIcon(QIcon(visible ?
+ show_hide_button_.setIcon(QIcon(visible ?
":/icons/decoder-shown.svg" :
":/icons/decoder-hidden.svg"));
}
diff --git a/pv/widgets/decodergroupbox.h b/pv/widgets/decodergroupbox.hpp
similarity index 79%
rename from pv/widgets/decodergroupbox.h
rename to pv/widgets/decodergroupbox.hpp
index 2302a97..7554431 100644
--- a/pv/widgets/decodergroupbox.h
+++ b/pv/widgets/decodergroupbox.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_H
-#define PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_H
+#ifndef PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_HPP
+#define PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_HPP
#include <QPushButton>
@@ -34,23 +34,24 @@ class DecoderGroupBox : public QWidget
Q_OBJECT
public:
- DecoderGroupBox(QString title, QWidget *parent = NULL);
+ DecoderGroupBox(QString title, QWidget *parent = nullptr,
+ bool isDeletable = true);
void add_layout(QLayout *layout);
void set_decoder_visible(bool visible);
-signals:
+Q_SIGNALS:
void delete_decoder();
void show_hide_decoder();
private:
- QGridLayout *const _layout;
- QPushButton _show_hide_button;
+ QGridLayout *const layout_;
+ QPushButton show_hide_button_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_H
+#endif // PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_HPP
diff --git a/pv/widgets/decodermenu.cpp b/pv/widgets/decodermenu.cpp
index 2f828a2..7da77bd 100644
--- a/pv/widgets/decodermenu.cpp
+++ b/pv/widgets/decodermenu.cpp
@@ -18,37 +18,38 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <cassert>
+
#include <libsigrokdecode/libsigrokdecode.h>
-#include "decodermenu.h"
+#include "decodermenu.hpp"
namespace pv {
namespace widgets {
DecoderMenu::DecoderMenu(QWidget *parent, bool first_level_decoder) :
QMenu(parent),
- _mapper(this)
+ mapper_(this)
{
GSList *l = g_slist_sort(g_slist_copy(
(GSList*)srd_decoder_list()), decoder_name_cmp);
- for(; l; l = l->next)
- {
+ for (; l; l = l->next) {
const srd_decoder *const d = (srd_decoder*)l->data;
assert(d);
- const bool have_probes = (d->channels || d->opt_channels) != 0;
- if (first_level_decoder == have_probes) {
+ const bool have_channels = (d->channels || d->opt_channels) != 0;
+ if (first_level_decoder == have_channels) {
QAction *const action =
addAction(QString::fromUtf8(d->name));
action->setData(qVariantFromValue(l->data));
- _mapper.setMapping(action, action);
+ mapper_.setMapping(action, action);
connect(action, SIGNAL(triggered()),
- &_mapper, SLOT(map()));
+ &mapper_, SLOT(map()));
}
}
g_slist_free(l);
- connect(&_mapper, SIGNAL(mapped(QObject*)),
+ connect(&mapper_, SIGNAL(mapped(QObject*)),
this, SLOT(on_action(QObject*)));
}
diff --git a/pv/widgets/decodermenu.h b/pv/widgets/decodermenu.hpp
similarity index 86%
copy from pv/widgets/decodermenu.h
copy to pv/widgets/decodermenu.hpp
index 20dd412..e154cd2 100644
--- a/pv/widgets/decodermenu.h
+++ b/pv/widgets/decodermenu.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_DECODERMENU_H
-#define PULSEVIEW_PV_WIDGETS_DECODERMENU_H
+#ifndef PULSEVIEW_PV_WIDGETS_DECODERMENU_HPP
+#define PULSEVIEW_PV_WIDGETS_DECODERMENU_HPP
#include <QMenu>
#include <QSignalMapper>
@@ -40,17 +40,17 @@ private:
static int decoder_name_cmp(const void *a, const void *b);
-private slots:
+private Q_SLOTS:
void on_action(QObject *action);
-signals:
+Q_SIGNALS:
void decoder_selected(srd_decoder *decoder);
private:
- QSignalMapper _mapper;
+ QSignalMapper mapper_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_DECODERMENU_H
+#endif // PULSEVIEW_PV_WIDGETS_DECODERMENU_HPP
diff --git a/pv/widgets/devicetoolbutton.cpp b/pv/widgets/devicetoolbutton.cpp
new file mode 100644
index 0000000..a0fc10e
--- /dev/null
+++ b/pv/widgets/devicetoolbutton.cpp
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <cassert>
+
+#include <QTimer>
+#include <QToolTip>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include <pv/devicemanager.hpp>
+#include <pv/devices/device.hpp>
+
+#include "devicetoolbutton.hpp"
+
+using std::list;
+using std::shared_ptr;
+using std::string;
+using std::weak_ptr;
+using std::vector;
+
+using pv::devices::Device;
+
+namespace pv {
+namespace widgets {
+
+DeviceToolButton::DeviceToolButton(QWidget *parent,
+ DeviceManager &device_manager,
+ QAction *connect_action) :
+ QToolButton(parent),
+ device_manager_(device_manager),
+ connect_action_(connect_action),
+ menu_(this),
+ mapper_(this),
+ devices_()
+{
+ setPopupMode(QToolButton::MenuButtonPopup);
+ setMenu(&menu_);
+ setDefaultAction(connect_action_);
+ setMinimumWidth(QFontMetrics(font()).averageCharWidth() * 24);
+
+ connect(&mapper_, SIGNAL(mapped(QObject*)),
+ this, SLOT(on_action(QObject*)));
+
+ connect(&menu_, SIGNAL(hovered(QAction*)),
+ this, SLOT(on_menu_hovered(QAction*)));
+}
+
+shared_ptr<Device> DeviceToolButton::selected_device()
+{
+ return selected_device_;
+}
+
+void DeviceToolButton::set_device_list(
+ const list< shared_ptr<Device> > &devices, shared_ptr<Device> selected)
+{
+ selected_device_ = selected;
+ setText(selected ? QString::fromStdString(
+ selected->display_name(device_manager_)) : "<No Device>");
+ devices_ = vector< weak_ptr<Device> >(devices.begin(), devices.end());
+ update_device_list();
+}
+
+void DeviceToolButton::update_device_list()
+{
+ menu_.clear();
+ menu_.addAction(connect_action_);
+ menu_.setDefaultAction(connect_action_);
+ menu_.addSeparator();
+
+ for (weak_ptr<Device> dev_weak_ptr : devices_) {
+ shared_ptr<Device> dev(dev_weak_ptr.lock());
+ if (!dev)
+ continue;
+
+ QAction *const a = new QAction(QString::fromStdString(
+ dev->display_name(device_manager_)), this);
+ a->setCheckable(true);
+ a->setChecked(selected_device_ == dev);
+ a->setData(qVariantFromValue((void*)dev.get()));
+ a->setToolTip(QString::fromStdString(dev->full_name()));
+ mapper_.setMapping(a, a);
+
+ connect(a, SIGNAL(triggered()), &mapper_, SLOT(map()));
+
+ menu_.addAction(a);
+ }
+}
+
+void DeviceToolButton::on_action(QObject *action)
+{
+ assert(action);
+
+ Device *const dev = (Device*)((QAction*)action)->data().value<void*>();
+ for (weak_ptr<Device> dev_weak_ptr : devices_) {
+ shared_ptr<Device> dev_ptr(dev_weak_ptr);
+ if (dev_ptr.get() == dev) {
+ selected_device_ = shared_ptr<Device>(dev_ptr);
+ break;
+ }
+ }
+
+ update_device_list();
+ setText(QString::fromStdString(
+ selected_device_->display_name(device_manager_)));
+
+ device_selected();
+}
+
+void DeviceToolButton::on_menu_hovered(QAction *action)
+{
+ assert(action);
+
+ // Only show the tooltip for device entries (they hold
+ // device pointers in their data field)
+ if (!action->data().isValid())
+ return;
+
+ device_tooltip_ = action->toolTip();
+
+ if (QToolTip::isVisible())
+ on_menu_hover_timeout();
+ else
+ QTimer::singleShot(1000, this, SLOT(on_menu_hover_timeout()));
+}
+
+void DeviceToolButton::on_menu_hover_timeout()
+{
+ if (device_tooltip_.isEmpty())
+ return;
+
+ QToolTip::showText(QCursor::pos(), device_tooltip_);
+}
+
+} // widgets
+} // pv
diff --git a/pv/widgets/devicetoolbutton.hpp b/pv/widgets/devicetoolbutton.hpp
new file mode 100644
index 0000000..ed69a4b
--- /dev/null
+++ b/pv/widgets/devicetoolbutton.hpp
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2014 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_WIDGETS_DEVICETOOLBUTTON_HPP
+#define PULSEVIEW_PV_WIDGETS_DEVICETOOLBUTTON_HPP
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <QAction>
+#include <QMenu>
+#include <QSignalMapper>
+#include <QToolButton>
+
+struct srd_decoder;
+
+namespace pv {
+
+class DeviceManager;
+
+namespace devices {
+class Device;
+}
+
+namespace widgets {
+
+class DeviceToolButton : public QToolButton
+{
+ Q_OBJECT;
+
+public:
+ /**
+ * Constructor
+ * @param parent the parent widget.
+ * @param device_manager the device manager.
+ * @param connect_action the connect-to-device action.
+ */
+ DeviceToolButton(QWidget *parent, DeviceManager &device_manager,
+ QAction *connect_action);
+
+ /**
+ * Returns a reference to the selected device.
+ */
+ std::shared_ptr<devices::Device> selected_device();
+
+ /**
+ * Sets the current list of devices.
+ * @param device the list of devices.
+ * @param selected_device the currently active device.
+ */
+ void set_device_list(
+ const std::list< std::shared_ptr<devices::Device> > &devices,
+ std::shared_ptr<devices::Device> selected);
+
+private:
+ /**
+ * Repopulates the menu from the device list.
+ */
+ void update_device_list();
+
+private Q_SLOTS:
+ void on_action(QObject *action);
+
+ void on_menu_hovered(QAction *action);
+
+ void on_menu_hover_timeout();
+
+Q_SIGNALS:
+ void device_selected();
+
+private:
+ DeviceManager &device_manager_;
+ QAction *const connect_action_;
+
+ QMenu menu_;
+ QSignalMapper mapper_;
+
+ std::shared_ptr<devices::Device> selected_device_;
+ std::vector< std::weak_ptr<devices::Device> > devices_;
+
+ QString device_tooltip_;
+};
+
+} // widgets
+} // pv
+
+#endif // PULSEVIEW_PV_WIDGETS_DEVICETOOLBUTTON_HPP
diff --git a/pv/widgets/exportmenu.cpp b/pv/widgets/exportmenu.cpp
new file mode 100644
index 0000000..e9fb415
--- /dev/null
+++ b/pv/widgets/exportmenu.cpp
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+#include <utility>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include "exportmenu.hpp"
+
+using std::find_if;
+using std::map;
+using std::pair;
+using std::string;
+using std::shared_ptr;
+
+using sigrok::Context;
+using sigrok::OutputFormat;
+
+namespace pv {
+namespace widgets {
+
+ExportMenu::ExportMenu(QWidget *parent, shared_ptr<Context> context,
+ std::vector<QAction *>open_actions) :
+ QMenu(parent),
+ context_(context),
+ mapper_(this)
+{
+ assert(context);
+
+ if (!open_actions.empty()) {
+ bool first_action = true;
+ for (auto open_action : open_actions) {
+ addAction(open_action);
+
+ if (first_action) {
+ first_action = false;
+ setDefaultAction(open_action);
+ }
+ }
+ addSeparator();
+ }
+
+ const map<string, shared_ptr<OutputFormat> > formats =
+ context->output_formats();
+
+ for (const pair<string, shared_ptr<OutputFormat> > &f : formats) {
+ if (f.first == "srzip")
+ continue;
+
+ assert(f.second);
+ QAction *const action = addAction(tr("Export %1...")
+ .arg(QString::fromStdString(f.second->description())));
+ action->setData(qVariantFromValue((void*)f.second.get()));
+ mapper_.setMapping(action, action);
+ connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
+ }
+
+ connect(&mapper_, SIGNAL(mapped(QObject*)),
+ this, SLOT(on_action(QObject*)));
+}
+
+void ExportMenu::on_action(QObject *action)
+{
+ assert(action);
+
+ const map<string, shared_ptr<OutputFormat> > formats =
+ context_->output_formats();
+ const auto iter = find_if(formats.cbegin(), formats.cend(),
+ [&](const pair<string, shared_ptr<OutputFormat> > &f) {
+ return f.second.get() ==
+ ((QAction*)action)->data().value<void*>(); });
+ if (iter == formats.cend())
+ return;
+
+ format_selected((*iter).second);
+}
+
+} // widgets
+} // pv
diff --git a/pv/widgets/decodermenu.h b/pv/widgets/exportmenu.hpp
similarity index 61%
copy from pv/widgets/decodermenu.h
copy to pv/widgets/exportmenu.hpp
index 20dd412..e3b1f23 100644
--- a/pv/widgets/decodermenu.h
+++ b/pv/widgets/exportmenu.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,39 +18,42 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_DECODERMENU_H
-#define PULSEVIEW_PV_WIDGETS_DECODERMENU_H
+#ifndef PULSEVIEW_PV_WIDGETS_EXPORTMENU_HPP
+#define PULSEVIEW_PV_WIDGETS_EXPORTMENU_HPP
+
+#include <memory>
#include <QMenu>
#include <QSignalMapper>
-struct srd_decoder;
+namespace sigrok {
+class Context;
+class OutputFormat;
+}
namespace pv {
namespace widgets {
-class DecoderMenu : public QMenu
+class ExportMenu : public QMenu
{
Q_OBJECT;
public:
- DecoderMenu(QWidget *parent, bool first_level_decoder = false);
-
-private:
- static int decoder_name_cmp(const void *a, const void *b);
-
+ ExportMenu(QWidget *parent, std::shared_ptr<sigrok::Context> context,
+ std::vector<QAction *>open_actions = std::vector<QAction *>());
-private slots:
+private Q_SLOTS:
void on_action(QObject *action);
-signals:
- void decoder_selected(srd_decoder *decoder);
+Q_SIGNALS:
+ void format_selected(std::shared_ptr<sigrok::OutputFormat> format);
private:
- QSignalMapper _mapper;
+ std::shared_ptr<sigrok::Context> context_;
+ QSignalMapper mapper_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_DECODERMENU_H
+#endif // PULSEVIEW_PV_WIDGETS_EXPORTMENU_HPP
diff --git a/pv/data/signaldata.cpp b/pv/widgets/hidingmenubar.cpp
similarity index 56%
copy from pv/data/signaldata.cpp
copy to pv/widgets/hidingmenubar.cpp
index 04f1d3f..0da364b 100644
--- a/pv/data/signaldata.cpp
+++ b/pv/widgets/hidingmenubar.cpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,32 +18,39 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "signaldata.h"
+#include <QKeyEvent>
+
+#include "hidingmenubar.hpp"
namespace pv {
-namespace data {
+namespace widgets {
-SignalData::SignalData() :
- _start_time(0),
- _samplerate(0)
+HidingMenuBar::HidingMenuBar(QWidget *parent) :
+ QMenuBar(parent)
{
+ setHidden(true);
+ connect(this, SIGNAL(triggered(QAction*)),
+ this, SLOT(item_triggered()));
}
-double SignalData::samplerate() const
+void HidingMenuBar::focusOutEvent(QFocusEvent *e)
{
- return _samplerate;
+ if (e->reason() != Qt::PopupFocusReason)
+ setHidden(true);
+ QMenuBar::focusOutEvent(e);
}
-void SignalData::set_samplerate(double samplerate)
+void HidingMenuBar::keyPressEvent(QKeyEvent *e)
{
- _samplerate = samplerate;
- clear();
+ if (e->key() == Qt::Key_Escape)
+ setHidden(true);
+ QMenuBar::keyPressEvent(e);
}
-double SignalData::get_start_time() const
+void HidingMenuBar::item_triggered()
{
- return _start_time;
+ setHidden(true);
}
-} // namespace data
+} // namespace widgets
} // namespace pv
diff --git a/pv/widgets/colourpopup.h b/pv/widgets/hidingmenubar.hpp
similarity index 50%
rename from pv/widgets/colourpopup.h
rename to pv/widgets/hidingmenubar.hpp
index 4f5c52e..4cf5d16 100644
--- a/pv/widgets/colourpopup.h
+++ b/pv/widgets/hidingmenubar.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,38 +18,49 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H
-#define PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H
+#ifndef PULSEVIEW_PV_WIDGETS_HIDINGMENUBAR_HPP
+#define PULSEVIEW_PV_WIDGETS_HIDINGMENUBAR_HPP
-#include "popup.h"
-#include "wellarray.h"
-
-#include <QVBoxLayout>
+#include <QMenuBar>
namespace pv {
namespace widgets {
-class ColourPopup : public Popup
+/**
+ * A menu bar widget that only remains visible while it retains focus.
+ */
+class HidingMenuBar : public QMenuBar
{
Q_OBJECT
public:
- ColourPopup(int rows, int cols, QWidget *partent);
-
- QWellArray& well_array();
+ /**
+ * Constructor
+ * @param parent The parent widget.
+ */
+ HidingMenuBar(QWidget *parent = nullptr);
-signals:
- void selected(int row, int col);
+private:
+ /**
+ * Handles the event that the widget loses keyboard focus.
+ * @param e the representation of the event details.
+ */
+ void focusOutEvent(QFocusEvent *e);
-private slots:
- void colour_selected(int, int);
+ /**
+ * Handles the event that a key is depressed.
+ * @param e the representation of the event details.
+ */
+ void keyPressEvent(QKeyEvent *e);
-private:
- QWellArray _well_array;
- QVBoxLayout _layout;
+private Q_SLOTS:
+ /**
+ * Handles a menu items being triggered.
+ */
+ void item_triggered();
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H
+#endif // PULSEVIEW_PV_WIDGETS_HIDINGMENUBAR_HPP
diff --git a/pv/widgets/importmenu.cpp b/pv/widgets/importmenu.cpp
new file mode 100644
index 0000000..a56bff7
--- /dev/null
+++ b/pv/widgets/importmenu.cpp
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+#include <utility>
+
+#include <libsigrokcxx/libsigrokcxx.hpp>
+
+#include "importmenu.hpp"
+
+using std::find_if;
+using std::map;
+using std::pair;
+using std::string;
+using std::shared_ptr;
+
+using sigrok::Context;
+using sigrok::InputFormat;
+
+namespace pv {
+namespace widgets {
+
+ImportMenu::ImportMenu(QWidget *parent, shared_ptr<Context> context,
+ QAction *open_action) :
+ QMenu(parent),
+ context_(context),
+ mapper_(this)
+{
+ assert(context);
+
+ if (open_action) {
+ addAction(open_action);
+ setDefaultAction(open_action);
+ addSeparator();
+ }
+
+ const map<string, shared_ptr<InputFormat> > formats =
+ context->input_formats();
+
+ for (const pair<string, shared_ptr<InputFormat> > &f : formats) {
+ assert(f.second);
+ QAction *const action = addAction(tr("Import %1...")
+ .arg(QString::fromStdString(f.second->description())));
+ action->setData(qVariantFromValue((void*)f.second.get()));
+ mapper_.setMapping(action, action);
+ connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
+ }
+
+ connect(&mapper_, SIGNAL(mapped(QObject*)),
+ this, SLOT(on_action(QObject*)));
+}
+
+void ImportMenu::on_action(QObject *action)
+{
+ assert(action);
+
+ const map<string, shared_ptr<InputFormat> > formats =
+ context_->input_formats();
+ const auto iter = find_if(formats.cbegin(), formats.cend(),
+ [&](const pair<string, shared_ptr<InputFormat> > &f) {
+ return f.second.get() ==
+ ((QAction*)action)->data().value<void*>(); });
+ if (iter == formats.cend())
+ return;
+
+ format_selected((*iter).second);
+}
+
+} // widgets
+} // pv
diff --git a/pv/widgets/decodermenu.h b/pv/widgets/importmenu.hpp
similarity index 62%
rename from pv/widgets/decodermenu.h
rename to pv/widgets/importmenu.hpp
index 20dd412..b56631e 100644
--- a/pv/widgets/decodermenu.h
+++ b/pv/widgets/importmenu.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2013 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Joel Holdsworth <joel at airwebreathe.org.uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,39 +18,42 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_DECODERMENU_H
-#define PULSEVIEW_PV_WIDGETS_DECODERMENU_H
+#ifndef PULSEVIEW_PV_WIDGETS_IMPORTMENU_HPP
+#define PULSEVIEW_PV_WIDGETS_IMPORTMENU_HPP
+
+#include <memory>
#include <QMenu>
#include <QSignalMapper>
-struct srd_decoder;
+namespace sigrok {
+class Context;
+class InputFormat;
+}
namespace pv {
namespace widgets {
-class DecoderMenu : public QMenu
+class ImportMenu : public QMenu
{
Q_OBJECT;
public:
- DecoderMenu(QWidget *parent, bool first_level_decoder = false);
-
-private:
- static int decoder_name_cmp(const void *a, const void *b);
-
+ ImportMenu(QWidget *parent, std::shared_ptr<sigrok::Context> context,
+ QAction *open_action = nullptr);
-private slots:
+private Q_SLOTS:
void on_action(QObject *action);
-signals:
- void decoder_selected(srd_decoder *decoder);
+Q_SIGNALS:
+ void format_selected(std::shared_ptr<sigrok::InputFormat> format);
private:
- QSignalMapper _mapper;
+ std::shared_ptr<sigrok::Context> context_;
+ QSignalMapper mapper_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_DECODERMENU_H
+#endif // PULSEVIEW_PV_WIDGETS_IMPORTMENU_HPP
diff --git a/pv/widgets/popup.cpp b/pv/widgets/popup.cpp
index 6857358..17bc719 100644
--- a/pv/widgets/popup.cpp
+++ b/pv/widgets/popup.cpp
@@ -23,8 +23,11 @@
#include <assert.h>
#include <QtGui>
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QLineEdit>
-#include "popup.h"
+#include "popup.hpp"
using std::max;
using std::min;
@@ -38,54 +41,95 @@ const unsigned int Popup::MarginWidth = 6;
Popup::Popup(QWidget *parent) :
QWidget(parent, Qt::Popup | Qt::FramelessWindowHint),
- _point(),
- _pos(Left)
+ point_(),
+ pos_(Left)
{
}
const QPoint& Popup::point() const
{
- return _point;
+ return point_;
}
Popup::Position Popup::position() const
{
- return _pos;
+ return pos_;
}
void Popup::set_position(const QPoint point, Position pos)
{
- _point = point, _pos = pos;
+ point_ = point, pos_ = pos;
setContentsMargins(
MarginWidth + ((pos == Right) ? ArrowLength : 0),
MarginWidth + ((pos == Bottom) ? ArrowLength : 0),
MarginWidth + ((pos == Left) ? ArrowLength : 0),
MarginWidth + ((pos == Top) ? ArrowLength : 0));
+}
+
+bool Popup::eventFilter(QObject *obj, QEvent *evt)
+{
+ QKeyEvent *keyEvent;
+ (void)obj;
+
+ if (evt->type() == QEvent::KeyPress) {
+ keyEvent = static_cast<QKeyEvent*>(evt);
+ if (keyEvent->key() == Qt::Key_Enter ||
+ keyEvent->key() == Qt::Key_Return) {
+ close();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Popup::show()
+{
+ QLineEdit* le;
+
+ QWidget::show();
+
+ // We want to close the popup when the Enter key was
+ // pressed and the first editable widget had focus.
+ if ((le = this->findChild<QLineEdit*>())) {
+
+ // For combo boxes we need to hook into the parent of
+ // the line edit (i.e. the QComboBox). For edit boxes
+ // we hook into the widget directly.
+ if (le->parent()->metaObject()->className() ==
+ this->metaObject()->className())
+ le->installEventFilter(this);
+ else
+ le->parent()->installEventFilter(this);
+
+ le->selectAll();
+ le->setFocus();
+ }
}
bool Popup::space_for_arrow() const
{
// Check if there is room for the arrow
- switch (_pos) {
+ switch (pos_) {
case Right:
- if (_point.x() > x())
+ if (point_.x() > x())
return false;
return true;
case Bottom:
- if (_point.y() > y())
+ if (point_.y() > y())
return false;
return true;
case Left:
- if (_point.x() < (x() + width()))
+ if (point_.x() < (x() + width()))
return false;
return true;
case Top:
- if (_point.y() < (y() + height()))
+ if (point_.y() < (y() + height()))
return false;
return true;
}
@@ -97,11 +141,10 @@ QPolygon Popup::arrow_polygon() const
{
QPolygon poly;
- const QPoint p = mapFromGlobal(_point);
+ const QPoint p = mapFromGlobal(point_);
const int l = ArrowLength + ArrowOverlap;
- switch (_pos)
- {
+ switch (pos_) {
case Right:
poly << QPoint(p.x() + l, p.y() - l);
break;
@@ -110,7 +153,7 @@ QPolygon Popup::arrow_polygon() const
poly << QPoint(p.x() - l, p.y() + l);
break;
- case Left:
+ case Left:
case Top:
poly << QPoint(p.x() - l, p.y() - l);
break;
@@ -118,14 +161,13 @@ QPolygon Popup::arrow_polygon() const
poly << p;
- switch (_pos)
- {
+ switch (pos_) {
case Right:
case Bottom:
poly << QPoint(p.x() + l, p.y() + l);
break;
- case Left:
+ case Left:
poly << QPoint(p.x() - l, p.y() + l);
break;
@@ -145,11 +187,11 @@ QRegion Popup::arrow_region() const
QRect Popup::bubble_rect() const
{
return QRect(
- QPoint((_pos == Right) ? ArrowLength : 0,
- (_pos == Bottom) ? ArrowLength : 0),
- QSize(width() - ((_pos == Left || _pos == Right) ?
+ QPoint((pos_ == Right) ? ArrowLength : 0,
+ (pos_ == Bottom) ? ArrowLength : 0),
+ QSize(width() - ((pos_ == Left || pos_ == Right) ?
ArrowLength : 0),
- height() - ((_pos == Top || _pos == Bottom) ?
+ height() - ((pos_ == Top || pos_ == Bottom) ?
ArrowLength : 0)));
}
@@ -184,19 +226,19 @@ void Popup::reposition_widget()
QPoint o;
const QRect screen_rect = QApplication::desktop()->availableGeometry(
- QApplication::desktop()->screenNumber(_point));
+ QApplication::desktop()->screenNumber(point_));
- if (_pos == Right || _pos == Left)
+ if (pos_ == Right || pos_ == Left)
o.ry() = -height() / 2;
else
o.rx() = -width() / 2;
- if (_pos == Left)
+ if (pos_ == Left)
o.rx() = -width();
- else if(_pos == Top)
+ else if (pos_ == Top)
o.ry() = -height();
- o += _point;
+ o += point_;
move(max(min(o.x(), screen_rect.right() - width()),
screen_rect.left()),
max(min(o.y(), screen_rect.bottom() - height()),
@@ -235,7 +277,7 @@ void Popup::paintEvent(QPaintEvent*)
const QRegion a(arrow_region());
const QRegion arrow_outline = a.subtracted(
- a.translated(ArrowOffsets[_pos]));
+ a.translated(ArrowOffsets[pos_]));
painter.setClipRegion(bubble_outline.subtracted(a).united(
arrow_outline));
@@ -255,7 +297,7 @@ void Popup::mouseReleaseEvent(QMouseEvent *e)
// We need our own out-of-bounds click handler because QWidget counts
// the drop-shadow region as inside the widget
- if(!bubble_rect().contains(e->pos()))
+ if (!bubble_rect().contains(e->pos()))
close();
}
@@ -266,4 +308,3 @@ void Popup::showEvent(QShowEvent*)
} // namespace widgets
} // namespace pv
-
diff --git a/pv/widgets/popup.h b/pv/widgets/popup.hpp
similarity index 88%
rename from pv/widgets/popup.h
rename to pv/widgets/popup.hpp
index 0bc6d0f..c6f931f 100644
--- a/pv/widgets/popup.h
+++ b/pv/widgets/popup.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_POPUP_H
-#define PULSEVIEW_PV_WIDGETS_POPUP_H
+#ifndef PULSEVIEW_PV_WIDGETS_POPUP_HPP
+#define PULSEVIEW_PV_WIDGETS_POPUP_HPP
#include <QWidget>
@@ -52,6 +52,10 @@ public:
void set_position(const QPoint point, Position pos);
+ bool eventFilter(QObject *obj, QEvent *evt);
+
+ void show();
+
private:
bool space_for_arrow() const;
@@ -79,15 +83,15 @@ private:
protected:
void showEvent(QShowEvent *e);
-signals:
+Q_SIGNALS:
void closed();
private:
- QPoint _point;
- Position _pos;
+ QPoint point_;
+ Position pos_;
};
} // namespace widgets
} // namespace pv
-#endif // PULSEVIEW_PV_WIDGETS_POPUP_H
+#endif // PULSEVIEW_PV_WIDGETS_POPUP_HPP
diff --git a/pv/widgets/popuptoolbutton.cpp b/pv/widgets/popuptoolbutton.cpp
index 74743ed..ff335a9 100644
--- a/pv/widgets/popuptoolbutton.cpp
+++ b/pv/widgets/popuptoolbutton.cpp
@@ -20,38 +20,38 @@
#include <assert.h>
-#include "popuptoolbutton.h"
+#include "popuptoolbutton.hpp"
namespace pv {
namespace widgets {
PopupToolButton::PopupToolButton(QWidget *parent) :
QToolButton(parent),
- _popup(NULL)
+ popup_(nullptr)
{
connect(this, SIGNAL(clicked(bool)), this, SLOT(on_clicked(bool)));
}
Popup* PopupToolButton::popup() const
{
- return _popup;
+ return popup_;
}
void PopupToolButton::set_popup(Popup *popup)
{
assert(popup);
- _popup = popup;
+ popup_ = popup;
}
void PopupToolButton::on_clicked(bool)
{
- if(!_popup)
+ if (!popup_)
return;
const QRect r = rect();
- _popup->set_position(mapToGlobal(QPoint((r.left() + r.right()) / 2,
+ popup_->set_position(mapToGlobal(QPoint((r.left() + r.right()) / 2,
((r.top() + r.bottom() * 3) / 4))), Popup::Bottom);
- _popup->show();
+ popup_->show();
}
} // widgets
diff --git a/pv/widgets/popuptoolbutton.h b/pv/widgets/popuptoolbutton.hpp
similarity index 84%
rename from pv/widgets/popuptoolbutton.h
rename to pv/widgets/popuptoolbutton.hpp
index b619b6f..3586aaf 100644
--- a/pv/widgets/popuptoolbutton.h
+++ b/pv/widgets/popuptoolbutton.hpp
@@ -18,10 +18,10 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_H
-#define PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_H
+#ifndef PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_HPP
+#define PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_HPP
-#include "popup.h"
+#include "popup.hpp"
#include <QToolButton>
@@ -39,14 +39,14 @@ public:
void set_popup(Popup *popup);
-private slots:
+private Q_SLOTS:
void on_clicked(bool);
private:
- Popup *_popup;
+ Popup *popup_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_H
+#endif // PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_HPP
diff --git a/pv/widgets/sweeptimingwidget.cpp b/pv/widgets/sweeptimingwidget.cpp
index 00ebb76..ec404c3 100644
--- a/pv/widgets/sweeptimingwidget.cpp
+++ b/pv/widgets/sweeptimingwidget.cpp
@@ -18,7 +18,9 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "sweeptimingwidget.h"
+#include "sweeptimingwidget.hpp"
+
+#include <cstdlib>
#include <vector>
@@ -26,6 +28,7 @@
#include <extdef.h>
+using std::abs;
using std::vector;
namespace pv {
@@ -34,35 +37,35 @@ namespace widgets {
SweepTimingWidget::SweepTimingWidget(const char *suffix,
QWidget *parent) :
QWidget(parent),
- _suffix(suffix),
- _layout(this),
- _value(this),
- _list(this),
- _value_type(None)
+ suffix_(suffix),
+ layout_(this),
+ value_(this),
+ list_(this),
+ value_type_(None)
{
setContentsMargins(0, 0, 0, 0);
- _value.setDecimals(0);
- _value.setSuffix(QString::fromUtf8(suffix));
+ value_.setDecimals(0);
+ value_.setSuffix(QString::fromUtf8(suffix));
- connect(&_list, SIGNAL(currentIndexChanged(int)),
+ connect(&list_, SIGNAL(currentIndexChanged(int)),
this, SIGNAL(value_changed()));
- connect(&_value, SIGNAL(editingFinished()),
+ connect(&value_, SIGNAL(editingFinished()),
this, SIGNAL(value_changed()));
- setLayout(&_layout);
- _layout.setMargin(0);
- _layout.addWidget(&_list);
- _layout.addWidget(&_value);
+ setLayout(&layout_);
+ layout_.setMargin(0);
+ layout_.addWidget(&list_);
+ layout_.addWidget(&value_);
show_none();
}
void SweepTimingWidget::show_none()
{
- _value_type = None;
- _value.hide();
- _list.hide();
+ value_type_ = None;
+ value_.hide();
+ list_.hide();
}
void SweepTimingWidget::show_min_max_step(uint64_t min, uint64_t max,
@@ -71,30 +74,29 @@ void SweepTimingWidget::show_min_max_step(uint64_t min, uint64_t max,
assert(max > min);
assert(step > 0);
- _value_type = MinMaxStep;
+ value_type_ = MinMaxStep;
- _value.setRange(min, max);
- _value.setSingleStep(step);
+ value_.setRange(min, max);
+ value_.setSingleStep(step);
- _value.show();
- _list.hide();
+ value_.show();
+ list_.hide();
}
void SweepTimingWidget::show_list(const uint64_t *vals, size_t count)
{
- _value_type = List;
+ value_type_ = List;
- _list.clear();
- for (size_t i = 0; i < count; i++)
- {
- char *const s = sr_si_string_u64(vals[i], _suffix);
- _list.addItem(QString::fromUtf8(s),
+ list_.clear();
+ for (size_t i = 0; i < count; i++) {
+ char *const s = sr_si_string_u64(vals[i], suffix_);
+ list_.addItem(QString::fromUtf8(s),
qVariantFromValue(vals[i]));
g_free(s);
}
- _value.hide();
- _list.show();
+ value_.hide();
+ list_.show();
}
void SweepTimingWidget::show_125_list(uint64_t min, uint64_t max)
@@ -139,18 +141,17 @@ void SweepTimingWidget::show_125_list(uint64_t min, uint64_t max)
uint64_t SweepTimingWidget::value() const
{
- switch(_value_type)
- {
+ switch(value_type_) {
case None:
return 0;
case MinMaxStep:
- return (uint64_t)_value.value();
+ return (uint64_t)value_.value();
case List:
{
- const int index = _list.currentIndex();
- return (index >= 0) ? _list.itemData(
+ const int index = list_.currentIndex();
+ return (index >= 0) ? list_.itemData(
index).value<uint64_t>() : 0;
}
@@ -163,21 +164,21 @@ uint64_t SweepTimingWidget::value() const
void SweepTimingWidget::set_value(uint64_t value)
{
- _value.setValue(value);
+ value_.setValue(value);
- int best_match = _list.count() - 1;
+ int best_match = list_.count() - 1;
int64_t best_variance = INT64_MAX;
- for (int i = 0; i < _list.count(); i++) {
+ for (int i = 0; i < list_.count(); i++) {
const int64_t this_variance = abs(
- (int64_t)value - _list.itemData(i).value<int64_t>());
+ (int64_t)value - list_.itemData(i).value<int64_t>());
if (this_variance < best_variance) {
best_variance = this_variance;
best_match = i;
}
}
- _list.setCurrentIndex(best_match);
+ list_.setCurrentIndex(best_match);
}
} // widgets
diff --git a/pv/widgets/sweeptimingwidget.h b/pv/widgets/sweeptimingwidget.hpp
similarity index 80%
rename from pv/widgets/sweeptimingwidget.h
rename to pv/widgets/sweeptimingwidget.hpp
index b66188f..e9e4dcf 100644
--- a/pv/widgets/sweeptimingwidget.h
+++ b/pv/widgets/sweeptimingwidget.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_H
-#define PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_H
+#ifndef PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_HPP
+#define PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_HPP
#include <libsigrok/libsigrok.h>
@@ -45,7 +45,7 @@ private:
};
public:
- SweepTimingWidget(const char *suffix, QWidget *parent = NULL);
+ SweepTimingWidget(const char *suffix, QWidget *parent = nullptr);
void show_none();
void show_min_max_step(uint64_t min, uint64_t max, uint64_t step);
@@ -55,21 +55,21 @@ public:
uint64_t value() const;
void set_value(uint64_t value);
-signals:
+Q_SIGNALS:
void value_changed();
private:
- const char *const _suffix;
+ const char *const suffix_;
- QHBoxLayout _layout;
+ QHBoxLayout layout_;
- QDoubleSpinBox _value;
- QComboBox _list;
+ QDoubleSpinBox value_;
+ QComboBox list_;
- ValueType _value_type;
+ ValueType value_type_;
};
} // widgets
} // pv
-#endif // PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_H
+#endif // PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_HPP
diff --git a/pv/widgets/timestampspinbox.cpp b/pv/widgets/timestampspinbox.cpp
new file mode 100644
index 0000000..463a846
--- /dev/null
+++ b/pv/widgets/timestampspinbox.cpp
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Jens Steinhauser <jens.steinhauser at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "timestampspinbox.hpp"
+
+#include <QLineEdit>
+#include <QRegExp>
+
+namespace pv {
+namespace widgets {
+
+TimestampSpinBox::TimestampSpinBox(QWidget* parent)
+ : QAbstractSpinBox(parent)
+ , precision_(9)
+ , stepsize_("1e-6")
+{
+ connect(this, SIGNAL(editingFinished()), this, SLOT(on_editingFinished()));
+
+ updateEdit();
+}
+
+void TimestampSpinBox::stepBy(int steps)
+{
+ setValue(value_ + steps * stepsize_);
+}
+
+QAbstractSpinBox::StepEnabled TimestampSpinBox::stepEnabled() const
+{
+ return QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled;
+}
+
+unsigned TimestampSpinBox::precision() const
+{
+ return precision_;
+}
+
+void TimestampSpinBox::setPrecision(unsigned precision)
+{
+ precision_ = precision;
+ updateEdit();
+}
+
+const pv::util::Timestamp& TimestampSpinBox::singleStep() const
+{
+ return stepsize_;
+}
+
+void TimestampSpinBox::setSingleStep(const pv::util::Timestamp& step)
+{
+ stepsize_ = step;
+}
+
+const pv::util::Timestamp& TimestampSpinBox::value() const
+{
+ return value_;
+}
+
+QSize TimestampSpinBox::minimumSizeHint() const
+{
+ const QFontMetrics fm(fontMetrics());
+ const int l = round(value_).str().size() + precision_ + 10;
+ const int w = fm.width(QString(l, '0'));
+ const int h = lineEdit()->minimumSizeHint().height();
+ return QSize(w, h);
+}
+
+void TimestampSpinBox::setValue(const pv::util::Timestamp& val)
+{
+ if (val == value_)
+ return;
+
+ value_ = val;
+ updateEdit();
+ Q_EMIT valueChanged(value_);
+}
+
+void TimestampSpinBox::on_editingFinished()
+{
+ if (!lineEdit()->isModified())
+ return;
+ lineEdit()->setModified(false);
+
+ QRegExp re(R"(\s*([-+]?)\s*([0-9]+\.?[0-9]*).*)");
+
+ if (re.exactMatch(text())) {
+ QStringList captures = re.capturedTexts();
+ captures.removeFirst(); // remove entire match
+ QString str = captures.join("");
+ setValue(pv::util::Timestamp(str.toStdString()));
+ } else {
+ // replace the malformed entered string with the old value
+ updateEdit();
+ }
+}
+
+void TimestampSpinBox::updateEdit()
+{
+ QString newtext = pv::util::format_time_si(
+ value_, pv::util::SIPrefix::none, precision_);
+ lineEdit()->setText(newtext);
+}
+
+} // widgets
+} // pv
diff --git a/pv/widgets/timestampspinbox.hpp b/pv/widgets/timestampspinbox.hpp
new file mode 100644
index 0000000..bac7cac
--- /dev/null
+++ b/pv/widgets/timestampspinbox.hpp
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Jens Steinhauser <jens.steinhauser at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PULSEVIEW_PV_WIDGETS_TIMESTAMPSPINBOX_HPP
+#define PULSEVIEW_PV_WIDGETS_TIMESTAMPSPINBOX_HPP
+
+#include "pv/util.hpp"
+
+#include <QAbstractSpinBox>
+
+namespace pv {
+namespace widgets {
+
+class TimestampSpinBox : public QAbstractSpinBox
+{
+ Q_OBJECT
+
+ Q_PROPERTY(unsigned precision
+ READ precision
+ WRITE setPrecision)
+
+ // Needed because of some strange behaviour of the Qt4 MOC that would add
+ // a reference to a 'staticMetaObject' member of 'pv::util' (the namespace)
+ // if pv::util::Timestamp is used directly in the Q_PROPERTY macros below.
+ // Didn't happen with the Qt5 MOC in this case, however others have had
+ // similar problems with Qt5: https://bugreports.qt.io/browse/QTBUG-37519
+ typedef pv::util::Timestamp Timestamp;
+
+ Q_PROPERTY(Timestamp singleStep
+ READ singleStep
+ WRITE setSingleStep)
+
+ Q_PROPERTY(Timestamp value
+ READ value
+ WRITE setValue
+ NOTIFY valueChanged
+ USER true)
+
+public:
+ TimestampSpinBox(QWidget* parent = nullptr);
+
+ void stepBy(int steps) override;
+
+ StepEnabled stepEnabled() const override;
+
+ unsigned precision() const;
+ void setPrecision(unsigned precision);
+
+ const pv::util::Timestamp& singleStep() const;
+ void setSingleStep(const pv::util::Timestamp& step);
+
+ const pv::util::Timestamp& value() const;
+
+ QSize minimumSizeHint() const override;
+
+public Q_SLOTS:
+ void setValue(const pv::util::Timestamp& val);
+
+Q_SIGNALS:
+ void valueChanged(const pv::util::Timestamp&);
+
+private Q_SLOTS:
+ void on_editingFinished();
+
+private:
+ unsigned precision_;
+ pv::util::Timestamp stepsize_;
+ pv::util::Timestamp value_;
+
+ void updateEdit();
+};
+
+} // widgets
+} // pv
+
+#endif
diff --git a/pv/widgets/wellarray.cpp b/pv/widgets/wellarray.cpp
index dcc3b41..1f3bf69 100644
--- a/pv/widgets/wellarray.cpp
+++ b/pv/widgets/wellarray.cpp
@@ -44,9 +44,12 @@
#include <QStyle>
#include <QStyleOptionFrame>
-#include "wellarray.h"
+#include "wellarray.hpp"
-void QWellArray::paintEvent(QPaintEvent *e)
+namespace pv {
+namespace widgets {
+
+void WellArray::paintEvent(QPaintEvent *e)
{
QRect r = e->rect();
int cx = r.x();
@@ -93,11 +96,11 @@ void QWellArray::paintEvent(QPaintEvent *e)
}
}
-struct QWellArrayData {
+struct WellArrayData {
QBrush *brush;
};
-QWellArray::QWellArray(int rows, int cols, QWidget *parent)
+WellArray::WellArray(int rows, int cols, QWidget *parent)
: QWidget(parent)
,nrows(rows), ncols(cols)
{
@@ -111,14 +114,14 @@ QWellArray::QWellArray(int rows, int cols, QWidget *parent)
selRow = -1;
}
-QSize QWellArray::sizeHint() const
+QSize WellArray::sizeHint() const
{
ensurePolished();
return gridSize().boundedTo(QSize(640, 480));
}
-void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect)
+void WellArray::paintCell(QPainter* p, int row, int col, const QRect &rect)
{
int b = 3; //margin
@@ -148,7 +151,7 @@ void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect)
/*!
Reimplement this function to change the contents of the well array.
*/
-void QWellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r)
+void WellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r)
{
if (d) {
p->fillRect(r, d->brush[row*numCols()+col]);
@@ -160,14 +163,14 @@ void QWellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r
}
}
-void QWellArray::mousePressEvent(QMouseEvent *e)
+void WellArray::mousePressEvent(QMouseEvent *e)
{
// The current cell marker is set to the cell the mouse is pressed in
QPoint pos = e->pos();
setCurrent(rowAt(pos.y()), columnAt(pos.x()));
}
-void QWellArray::mouseReleaseEvent(QMouseEvent * /* event */)
+void WellArray::mouseReleaseEvent(QMouseEvent * /* event */)
{
// The current cell marker is set to the cell the mouse is clicked in
setSelected(curRow, curCol);
@@ -179,7 +182,7 @@ void QWellArray::mouseReleaseEvent(QMouseEvent * /* event */)
the same as the currently selected cell.
*/
-void QWellArray::setCurrent(int row, int col)
+void WellArray::setCurrent(int row, int col)
{
if ((curRow == row) && (curCol == col))
return;
@@ -203,7 +206,7 @@ void QWellArray::setCurrent(int row, int col)
Does not set the position of the focus indicator.
*/
-void QWellArray::setSelected(int row, int col)
+void WellArray::setSelected(int row, int col)
{
int oldRow = selRow;
int oldCol = selCol;
@@ -217,18 +220,18 @@ void QWellArray::setSelected(int row, int col)
updateCell(oldRow, oldCol);
updateCell(selRow, selCol);
if (row >= 0)
- emit selected(row, col);
+ Q_EMIT selected(row, col);
}
-void QWellArray::focusInEvent(QFocusEvent*)
+void WellArray::focusInEvent(QFocusEvent*)
{
updateCell(curRow, curCol);
}
-void QWellArray::setCellBrush(int row, int col, const QBrush &b)
+void WellArray::setCellBrush(int row, int col, const QBrush &b)
{
if (!d) {
- d = new QWellArrayData;
+ d = new WellArrayData;
int i = numRows()*numCols();
d->brush = new QBrush[i];
}
@@ -241,7 +244,7 @@ void QWellArray::setCellBrush(int row, int col, const QBrush &b)
set, Qt::NoBrush is returned.
*/
-QBrush QWellArray::cellBrush(int row, int col)
+QBrush WellArray::cellBrush(int row, int col)
{
if (d && row >= 0 && row < numRows() && col >= 0 && col < numCols())
return d->brush[row*numCols()+col];
@@ -253,30 +256,30 @@ QBrush QWellArray::cellBrush(int row, int col)
/*!\reimp
*/
-void QWellArray::focusOutEvent(QFocusEvent*)
+void WellArray::focusOutEvent(QFocusEvent*)
{
updateCell(curRow, curCol);
}
/*\reimp
*/
-void QWellArray::keyPressEvent(QKeyEvent* e)
+void WellArray::keyPressEvent(QKeyEvent* e)
{
switch(e->key()) { // Look at the key code
case Qt::Key_Left: // If 'left arrow'-key,
- if(curCol > 0) // and cr't not in leftmost col
+ if (curCol > 0) // and cr't not in leftmost col
setCurrent(curRow, curCol - 1); // set cr't to next left column
break;
case Qt::Key_Right: // Correspondingly...
- if(curCol < numCols()-1)
+ if (curCol < numCols()-1)
setCurrent(curRow, curCol + 1);
break;
case Qt::Key_Up:
- if(curRow > 0)
+ if (curRow > 0)
setCurrent(curRow - 1, curCol);
break;
case Qt::Key_Down:
- if(curRow < numRows()-1)
+ if (curRow < numRows()-1)
setCurrent(curRow + 1, curCol);
break;
case Qt::Key_Space:
@@ -288,3 +291,6 @@ void QWellArray::keyPressEvent(QKeyEvent* e)
}
}
+
+} // namespace wellarray
+} // namespace pv
diff --git a/pv/widgets/wellarray.h b/pv/widgets/wellarray.hpp
similarity index 94%
rename from pv/widgets/wellarray.h
rename to pv/widgets/wellarray.hpp
index dad088c..8132eb0 100644
--- a/pv/widgets/wellarray.h
+++ b/pv/widgets/wellarray.hpp
@@ -41,16 +41,19 @@
#include <QWidget>
-struct QWellArrayData;
+namespace pv {
+namespace widgets {
-class QWellArray : public QWidget
+struct WellArrayData;
+
+class WellArray : public QWidget
{
Q_OBJECT
Q_PROPERTY(int selectedColumn READ selectedColumn)
Q_PROPERTY(int selectedRow READ selectedRow)
public:
- QWellArray(int rows, int cols, QWidget* parent=0);
+ WellArray(int rows, int cols, QWidget* parent=0);
QString cellContent(int row, int col) const;
int selectedColumn() const { return selCol; }
@@ -104,7 +107,7 @@ public:
inline void updateCell(int row, int column) { update(cellGeometry(row, column)); }
-signals:
+Q_SIGNALS:
void selected(int row, int col);
protected:
@@ -119,7 +122,7 @@ protected:
void paintEvent(QPaintEvent *);
private:
- Q_DISABLE_COPY(QWellArray)
+ Q_DISABLE_COPY(WellArray)
int nrows;
int ncols;
@@ -129,5 +132,8 @@ private:
int curCol;
int selRow;
int selCol;
- QWellArrayData *d;
+ WellArrayData *d;
};
+
+} // namespace wellarray
+} // namespace pv
diff --git a/signalhandler.cpp b/signalhandler.cpp
index 8c9244b..fbbcf22 100644
--- a/signalhandler.cpp
+++ b/signalhandler.cpp
@@ -18,7 +18,7 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include "signalhandler.h"
+#include "signalhandler.hpp"
#include <assert.h>
#include <signal.h>
@@ -29,11 +29,11 @@
#include <QDebug>
#include <QSocketNotifier>
-int SignalHandler::_sockets[2];
+int SignalHandler::sockets_[2];
bool SignalHandler::prepare_signals()
{
- if(socketpair(AF_UNIX, SOCK_STREAM, 0, _sockets) != 0)
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets_) != 0)
return false;
struct sigaction sig_action;
@@ -42,10 +42,10 @@ bool SignalHandler::prepare_signals()
sigemptyset(&sig_action.sa_mask);
sig_action.sa_flags = SA_RESTART;
- if(sigaction(SIGINT, &sig_action, 0) != 0 ||
+ if (sigaction(SIGINT, &sig_action, 0) != 0 ||
sigaction(SIGTERM, &sig_action, 0) != 0) {
- close(_sockets[0]);
- close(_sockets[1]);
+ close(sockets_[0]);
+ close(sockets_[1]);
return false;
}
@@ -53,21 +53,20 @@ bool SignalHandler::prepare_signals()
}
SignalHandler::SignalHandler(QObject* parent) : QObject(parent),
- _socket_notifier(0)
+ socket_notifier_(0)
{
- _socket_notifier = new QSocketNotifier(_sockets[1],
+ socket_notifier_ = new QSocketNotifier(sockets_[1],
QSocketNotifier::Read, this);
- connect(_socket_notifier, SIGNAL(activated(int)),
+ connect(socket_notifier_, SIGNAL(activated(int)),
SLOT(on_socket_notifier_activated()));
}
void SignalHandler::on_socket_notifier_activated()
{
- _socket_notifier->setEnabled(false);
+ socket_notifier_->setEnabled(false);
int sig_number;
- if(read(_sockets[1], &sig_number, sizeof(int)) !=
- sizeof(int)) {
+ if (read(sockets_[1], &sig_number, sizeof(int)) != sizeof(int)) {
qDebug() << "Failed to catch signal";
abort();
}
@@ -75,20 +74,19 @@ void SignalHandler::on_socket_notifier_activated()
switch(sig_number)
{
case SIGINT:
- emit int_received();
+ Q_EMIT int_received();
break;
case SIGTERM:
- emit term_received();
+ Q_EMIT term_received();
break;
}
- _socket_notifier->setEnabled(true);
+ socket_notifier_->setEnabled(true);
}
void SignalHandler::handle_signals(int sig_number)
{
- if(write(_sockets[0], &sig_number, sizeof(int)) !=
- sizeof(int)) {
+ if (write(sockets_[0], &sig_number, sizeof(int)) != sizeof(int)) {
// Failed to handle signal
abort();
}
diff --git a/signalhandler.h b/signalhandler.hpp
similarity index 83%
rename from signalhandler.h
rename to signalhandler.hpp
index 7ecd612..a8ddccb 100644
--- a/signalhandler.h
+++ b/signalhandler.hpp
@@ -18,8 +18,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef SIGNALHANDLER_H
-#define SIGNALHANDLER_H
+#ifndef SIGNALHANDLER_HPP
+#define SIGNALHANDLER_HPP
#include <QObject>
@@ -33,23 +33,23 @@ public:
static bool prepare_signals();
public:
- explicit SignalHandler(QObject* parent = NULL);
+ explicit SignalHandler(QObject* parent = nullptr);
-signals:
+Q_SIGNALS:
void int_received();
void term_received();
-private slots:
+private Q_SLOTS:
void on_socket_notifier_activated();
private:
static void handle_signals(int sig_number);
private:
- QSocketNotifier* _socket_notifier;
+ QSocketNotifier* socket_notifier_;
private:
- static int _sockets[2];
+ static int sockets_[2];
};
-#endif // SIGNALHANDLER_H
+#endif // SIGNALHANDLER_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 0ea69f0..ef98a9c 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -18,111 +18,120 @@
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
-option(ENABLE_DECODE "Build with libsigrokdecode" FALSE)
-
-list(APPEND PKGDEPS libsigrok>=0.3.0)
-
-if(ENABLE_DECODE)
- list(APPEND PKGDEPS libsigrokdecode>=0.3.0)
-endif()
-
-find_package(PkgConfig)
-pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS})
-
-# Find the platform's thread library (needed for boost-thread).
-# This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value.
-find_package(Threads)
-
-if(WIN32)
- # On Windows/MinGW we need to use 'thread_win32' instead of 'thread'.
- # The library is named libboost_thread_win32* (not libboost_thread*).
- find_package(Boost 1.42 COMPONENTS filesystem system thread_win32 unit_test_framework REQUIRED)
-else()
- find_package(Boost 1.42 COMPONENTS filesystem system thread unit_test_framework REQUIRED)
-endif()
-
-
-find_program(QT_QMAKE_EXECUTABLE NAMES qmake4 qmake-qt4 qmake-mac)
-find_package(Qt4 REQUIRED)
-
set(pulseview_TEST_SOURCES
${PROJECT_SOURCE_DIR}/pv/devicemanager.cpp
- ${PROJECT_SOURCE_DIR}/pv/sigsession.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/cursorpair.cpp
+ ${PROJECT_SOURCE_DIR}/pv/session.cpp
+ ${PROJECT_SOURCE_DIR}/pv/storesession.cpp
+ ${PROJECT_SOURCE_DIR}/pv/util.cpp
+ ${PROJECT_SOURCE_DIR}/pv/binding/binding.cpp
+ ${PROJECT_SOURCE_DIR}/pv/binding/device.cpp
${PROJECT_SOURCE_DIR}/pv/data/analog.cpp
- ${PROJECT_SOURCE_DIR}/pv/data/analogsnapshot.cpp
+ ${PROJECT_SOURCE_DIR}/pv/data/analogsegment.cpp
${PROJECT_SOURCE_DIR}/pv/data/logic.cpp
- ${PROJECT_SOURCE_DIR}/pv/data/logicsnapshot.cpp
- ${PROJECT_SOURCE_DIR}/pv/data/snapshot.cpp
+ ${PROJECT_SOURCE_DIR}/pv/data/logicsegment.cpp
+ ${PROJECT_SOURCE_DIR}/pv/data/segment.cpp
${PROJECT_SOURCE_DIR}/pv/data/signaldata.cpp
- ${PROJECT_SOURCE_DIR}/pv/device/device.cpp
- ${PROJECT_SOURCE_DIR}/pv/device/devinst.cpp
- ${PROJECT_SOURCE_DIR}/pv/device/file.cpp
- ${PROJECT_SOURCE_DIR}/pv/device/inputfile.cpp
- ${PROJECT_SOURCE_DIR}/pv/device/sessionfile.cpp
+ ${PROJECT_SOURCE_DIR}/pv/devices/device.cpp
+ ${PROJECT_SOURCE_DIR}/pv/devices/file.cpp
+ ${PROJECT_SOURCE_DIR}/pv/devices/hardwaredevice.cpp
+ ${PROJECT_SOURCE_DIR}/pv/devices/inputfile.cpp
+ ${PROJECT_SOURCE_DIR}/pv/devices/sessionfile.cpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/bool.cpp
${PROJECT_SOURCE_DIR}/pv/prop/double.cpp
${PROJECT_SOURCE_DIR}/pv/prop/enum.cpp
${PROJECT_SOURCE_DIR}/pv/prop/int.cpp
${PROJECT_SOURCE_DIR}/pv/prop/property.cpp
${PROJECT_SOURCE_DIR}/pv/prop/string.cpp
- ${PROJECT_SOURCE_DIR}/pv/prop/binding/binding.cpp
+ ${PROJECT_SOURCE_DIR}/pv/popups/channels.cpp
${PROJECT_SOURCE_DIR}/pv/view/analogsignal.cpp
${PROJECT_SOURCE_DIR}/pv/view/cursor.cpp
${PROJECT_SOURCE_DIR}/pv/view/cursorpair.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/flag.cpp
${PROJECT_SOURCE_DIR}/pv/view/header.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.cpp
${PROJECT_SOURCE_DIR}/pv/view/marginwidget.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/rowitem.cpp
${PROJECT_SOURCE_DIR}/pv/view/ruler.cpp
- ${PROJECT_SOURCE_DIR}/pv/view/selectableitem.cpp
${PROJECT_SOURCE_DIR}/pv/view/signal.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/signalscalehandle.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/timeitem.cpp
${PROJECT_SOURCE_DIR}/pv/view/timemarker.cpp
${PROJECT_SOURCE_DIR}/pv/view/trace.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/tracegroup.cpp
${PROJECT_SOURCE_DIR}/pv/view/tracepalette.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/tracetreeitem.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/tracetreeitemowner.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/triggermarker.cpp
${PROJECT_SOURCE_DIR}/pv/view/view.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewitem.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewitemowner.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewitempaintparams.cpp
${PROJECT_SOURCE_DIR}/pv/view/viewport.cpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewwidget.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/colourpopup.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/popup.cpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/popuptoolbutton.cpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/sweeptimingwidget.cpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/timestampspinbox.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/wellarray.cpp
- data/analogsnapshot.cpp
- data/logicsnapshot.cpp
+ data/analogsegment.cpp
+ data/logicsegment.cpp
+ view/ruler.cpp
test.cpp
+ util.cpp
)
# This list includes only QObject derived class headers.
set(pulseview_TEST_HEADERS
- ${PROJECT_SOURCE_DIR}/pv/sigsession.h
- ${PROJECT_SOURCE_DIR}/pv/device/devinst.h
- ${PROJECT_SOURCE_DIR}/pv/prop/double.h
- ${PROJECT_SOURCE_DIR}/pv/prop/enum.h
- ${PROJECT_SOURCE_DIR}/pv/prop/int.h
- ${PROJECT_SOURCE_DIR}/pv/prop/property.h
- ${PROJECT_SOURCE_DIR}/pv/prop/string.h
- ${PROJECT_SOURCE_DIR}/pv/view/cursor.h
- ${PROJECT_SOURCE_DIR}/pv/view/header.h
- ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.h
- ${PROJECT_SOURCE_DIR}/pv/view/marginwidget.h
- ${PROJECT_SOURCE_DIR}/pv/view/ruler.h
- ${PROJECT_SOURCE_DIR}/pv/view/selectableitem.h
- ${PROJECT_SOURCE_DIR}/pv/view/signal.h
- ${PROJECT_SOURCE_DIR}/pv/view/timemarker.h
- ${PROJECT_SOURCE_DIR}/pv/view/trace.h
- ${PROJECT_SOURCE_DIR}/pv/view/view.h
- ${PROJECT_SOURCE_DIR}/pv/view/viewport.h
- ${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.h
- ${PROJECT_SOURCE_DIR}/pv/widgets/colourpopup.h
- ${PROJECT_SOURCE_DIR}/pv/widgets/popup.h
- ${PROJECT_SOURCE_DIR}/pv/widgets/wellarray.h
+ ${PROJECT_SOURCE_DIR}/pv/session.hpp
+ ${PROJECT_SOURCE_DIR}/pv/storesession.hpp
+ ${PROJECT_SOURCE_DIR}/pv/binding/device.hpp
+ ${PROJECT_SOURCE_DIR}/pv/devices/device.hpp
+ ${PROJECT_SOURCE_DIR}/pv/popups/channels.hpp
+ ${PROJECT_SOURCE_DIR}/pv/popups/deviceoptions.hpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/bool.hpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/double.hpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/enum.hpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/int.hpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/property.hpp
+ ${PROJECT_SOURCE_DIR}/pv/prop/string.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/cursor.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/flag.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/header.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/marginwidget.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/rowitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/ruler.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/signal.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/signalscalehandle.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/timeitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/timemarker.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/trace.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/tracegroup.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/tracetreeitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/triggermarker.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/view.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewitem.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewport.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/viewwidget.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/colourpopup.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/popup.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/popuptoolbutton.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/sweeptimingwidget.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/timestampspinbox.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/wellarray.hpp
)
if(ENABLE_DECODE)
list(APPEND pulseview_TEST_SOURCES
+ ${PROJECT_SOURCE_DIR}/pv/binding/decoder.cpp
${PROJECT_SOURCE_DIR}/pv/data/decoderstack.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/annotation.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/decoder.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/row.cpp
${PROJECT_SOURCE_DIR}/pv/data/decode/rowdata.cpp
- ${PROJECT_SOURCE_DIR}/pv/prop/binding/decoderoptions.cpp
${PROJECT_SOURCE_DIR}/pv/view/decodetrace.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp
${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp
@@ -130,41 +139,26 @@ if(ENABLE_DECODE)
)
list(APPEND pulseview_TEST_HEADERS
- ${PROJECT_SOURCE_DIR}/pv/data/decoderstack.h
- ${PROJECT_SOURCE_DIR}/pv/view/decodetrace.h
- ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.h
- ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.h
+ ${PROJECT_SOURCE_DIR}/pv/data/decoderstack.hpp
+ ${PROJECT_SOURCE_DIR}/pv/view/decodetrace.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.hpp
+ ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.hpp
)
endif()
-qt4_wrap_cpp(pulseview_TEST_HEADERS_MOC ${pulseview_TEST_HEADERS})
-
-if(ENABLE_DECODE)
- add_definitions(-DENABLE_DECODE)
+if(Qt5Core_FOUND)
+ qt5_wrap_cpp(pulseview_TEST_HEADERS_MOC ${pulseview_TEST_HEADERS})
+else()
+ # Workaround for QTBUG-22829: -DBOOST_NEXT_PRIOR_HPP_INCLUDED.
+ # https://bugreports.qt.io/browse/QTBUG-22829
+ qt4_wrap_cpp(pulseview_TEST_HEADERS_MOC ${pulseview_TEST_HEADERS}
+ OPTIONS -DBOOST_NEXT_PRIOR_HPP_INCLUDED)
+ include(${QT_USE_FILE})
endif()
# On MinGW we need to use static linking.
if(NOT WIN32)
-add_definitions(-DBOOST_TEST_DYN_LINK)
-endif()
-
-add_definitions(${QT_DEFINITIONS})
-
-include_directories(
- ${Boost_INCLUDE_DIRS}
- ${PKGDEPS_INCLUDE_DIRS}
-)
-
-set(PULSEVIEW_LINK_LIBS
- ${Boost_LIBRARIES}
- ${CMAKE_THREAD_LIBS_INIT}
- ${PKGDEPS_LIBRARIES}
- ${QT_LIBRARIES}
-)
-
-if(WIN32)
- # Workaround for a MinGW linking issue.
- list(APPEND PULSEVIEW_LINK_LIBS "-llzma -llcms2")
+ add_definitions(-DBOOST_TEST_DYN_LINK)
endif()
add_executable(pulseview-test
diff --git a/test/data/analogsnapshot.cpp b/test/data/analogsegment.cpp
similarity index 66%
rename from test/data/analogsnapshot.cpp
rename to test/data/analogsegment.cpp
index 200d262..2593bf6 100644
--- a/test/data/analogsnapshot.cpp
+++ b/test/data/analogsegment.cpp
@@ -24,13 +24,14 @@
#include <boost/test/unit_test.hpp>
-#include "../../pv/data/analogsnapshot.h"
+#include <pv/data/analogsegment.hpp>
-using pv::data::AnalogSnapshot;
+using pv::data::AnalogSegment;
-BOOST_AUTO_TEST_SUITE(AnalogSnapshotTest)
+#if 0
+BOOST_AUTO_TEST_SUITE(AnalogSegmentTest)
-void push_analog(AnalogSnapshot &s, unsigned int num_samples,
+void push_analog(AnalogSegment &s, unsigned int num_samples,
float value)
{
float *const data = new float[num_samples];
@@ -43,18 +44,18 @@ void push_analog(AnalogSnapshot &s, unsigned int num_samples,
BOOST_AUTO_TEST_CASE(Basic)
{
- // Create an empty AnalogSnapshot object
- AnalogSnapshot s;
+ // Create an empty AnalogSegment object
+ AnalogSegment s;
- //----- Test AnalogSnapshot::push_analog -----//
+ //----- Test AnalogSegment::push_analog -----//
BOOST_CHECK(s.get_sample_count() == 0);
- for (unsigned int i = 0; i < AnalogSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 0; i < AnalogSegment::ScaleStepCount; i++)
{
- const AnalogSnapshot::Envelope &m = s._envelope_levels[i];
+ const AnalogSegment::Envelope &m = s.envelope_levels_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.samples == NULL);
+ BOOST_CHECK(m.samples == nullptr);
}
// Push 8 samples of all zeros
@@ -63,12 +64,12 @@ BOOST_AUTO_TEST_CASE(Basic)
BOOST_CHECK(s.get_sample_count() == 8);
// There should not be enough samples to have a single mip map sample
- for (unsigned int i = 0; i < AnalogSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 0; i < AnalogSegment::ScaleStepCount; i++)
{
- const AnalogSnapshot::Envelope &m = s._envelope_levels[i];
+ const AnalogSegment::Envelope &m = s.envelope_levels_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.samples == NULL);
+ BOOST_CHECK(m.samples == nullptr);
}
// Push 8 samples of 1.0s to bring the total up to 16
@@ -76,39 +77,40 @@ BOOST_AUTO_TEST_CASE(Basic)
// There should now be enough data for exactly one sample
// in mip map level 0, and that sample should be 0
- const AnalogSnapshot::Envelope &e0 = s._envelope_levels[0];
+ const AnalogSegment::Envelope &e0 = s.envelope_levels_[0];
BOOST_CHECK_EQUAL(e0.length, 1);
- BOOST_CHECK_EQUAL(e0.data_length, AnalogSnapshot::EnvelopeDataUnit);
- BOOST_REQUIRE(e0.samples != NULL);
+ BOOST_CHECK_EQUAL(e0.data_length, AnalogSegment::EnvelopeDataUnit);
+ BOOST_REQUIRE(e0.samples != nullptr);
BOOST_CHECK_EQUAL(e0.samples[0].min, 0.0f);
BOOST_CHECK_EQUAL(e0.samples[0].max, 1.0f);
// The higher levels should still be empty
- for (unsigned int i = 1; i < AnalogSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 1; i < AnalogSegment::ScaleStepCount; i++)
{
- const AnalogSnapshot::Envelope &m = s._envelope_levels[i];
+ const AnalogSegment::Envelope &m = s.envelope_levels_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.samples == NULL);
+ BOOST_CHECK(m.samples == nullptr);
}
// Push 240 samples of all zeros to bring the total up to 256
push_analog(s, 240, -1.0f);
BOOST_CHECK_EQUAL(e0.length, 16);
- BOOST_CHECK_EQUAL(e0.data_length, AnalogSnapshot::EnvelopeDataUnit);
+ BOOST_CHECK_EQUAL(e0.data_length, AnalogSegment::EnvelopeDataUnit);
for (unsigned int i = 1; i < e0.length; i++) {
BOOST_CHECK_EQUAL(e0.samples[i].min, -1.0f);
BOOST_CHECK_EQUAL(e0.samples[i].max, -1.0f);
}
- const AnalogSnapshot::Envelope &e1 = s._envelope_levels[1];
+ const AnalogSegment::Envelope &e1 = s.envelope_levels_[1];
BOOST_CHECK_EQUAL(e1.length, 1);
- BOOST_CHECK_EQUAL(e1.data_length, AnalogSnapshot::EnvelopeDataUnit);
- BOOST_REQUIRE(e1.samples != NULL);
+ BOOST_CHECK_EQUAL(e1.data_length, AnalogSegment::EnvelopeDataUnit);
+ BOOST_REQUIRE(e1.samples != nullptr);
BOOST_CHECK_EQUAL(e1.samples[0].min, -1.0f);
BOOST_CHECK_EQUAL(e1.samples[0].max, 1.0f);
}
BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/test/data/decoderstack.cpp b/test/data/decoderstack.cpp
index d6e4f75..70ed7e0 100644
--- a/test/data/decoderstack.cpp
+++ b/test/data/decoderstack.cpp
@@ -21,35 +21,36 @@
#include <libsigrokdecode/libsigrokdecode.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */
#include <boost/test/unit_test.hpp>
-#include <libsigrok/libsigrok.h>
+#include <libsigrokcxx/libsigrokcxx.hpp>
-#include "../../pv/data/decoderstack.h"
-#include "../../pv/devicemanager.h"
-#include "../../pv/sigsession.h"
-#include "../../pv/view/decodetrace.h"
+#include "../../pv/data/decoderstack.hpp"
+#include "../../pv/devicemanager.hpp"
+#include "../../pv/session.hpp"
+#include "../../pv/view/decodetrace.hpp"
-using boost::shared_ptr;
using pv::data::DecoderStack;
using pv::data::decode::Decoder;
using pv::view::DecodeTrace;
+using std::shared_ptr;
using std::vector;
+#if 0
BOOST_AUTO_TEST_SUITE(DecoderStackTest)
BOOST_AUTO_TEST_CASE(TwoDecoderStack)
{
- sr_context *ctx = NULL;
+ sr_context *ctx = nullptr;
BOOST_REQUIRE(sr_init(&ctx) == SR_OK);
BOOST_REQUIRE(ctx);
- BOOST_REQUIRE(srd_init(NULL) == SRD_OK);
+ BOOST_REQUIRE(srd_init(nullptr) == SRD_OK);
srd_decoder_load_all();
{
pv::DeviceManager dm(ctx);
- pv::SigSession ss(dm);
+ pv::Session ss(dm);
const GSList *l = srd_decoder_list();
BOOST_REQUIRE(l);
@@ -70,8 +71,8 @@ BOOST_AUTO_TEST_CASE(TwoDecoderStack)
BOOST_REQUIRE(dec1);
// Wait for the decode threads to complete
- dec0->_decode_thread.join();
- dec1->_decode_thread.join();
+ dec0->decode_thread_.join();
+ dec1->decode_thread_.join();
// Check there were no errors
BOOST_CHECK_EQUAL(dec0->error_message().isEmpty(), true);
@@ -84,3 +85,4 @@ BOOST_AUTO_TEST_CASE(TwoDecoderStack)
}
BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/test/data/logicsnapshot.cpp b/test/data/logicsegment.cpp
similarity index 68%
rename from test/data/logicsnapshot.cpp
rename to test/data/logicsegment.cpp
index 5fc1721..fabcbd4 100644
--- a/test/data/logicsnapshot.cpp
+++ b/test/data/logicsegment.cpp
@@ -24,14 +24,23 @@
#include <boost/test/unit_test.hpp>
-#include "../../pv/data/logicsnapshot.h"
+#include <pv/data/logicsegment.hpp>
-using pv::data::LogicSnapshot;
+using pv::data::LogicSegment;
using std::vector;
-BOOST_AUTO_TEST_SUITE(LogicSnapshotTest)
+// Dummy, remove again when unit tests are fixed.
+BOOST_AUTO_TEST_SUITE(DummyTestSuite)
+BOOST_AUTO_TEST_CASE(DummyTestCase)
+{
+ BOOST_CHECK_EQUAL(1, 1);
+}
+BOOST_AUTO_TEST_SUITE_END()
+
+#if 0
+BOOST_AUTO_TEST_SUITE(LogicSegmentTest)
-void push_logic(LogicSnapshot &s, unsigned int length, uint8_t value)
+void push_logic(LogicSegment &s, unsigned int length, uint8_t value)
{
sr_datafeed_logic logic;
logic.unitsize = 1;
@@ -44,40 +53,40 @@ void push_logic(LogicSnapshot &s, unsigned int length, uint8_t value)
BOOST_AUTO_TEST_CASE(Pow2)
{
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(0, 0), 0);
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(1, 0), 1);
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(2, 0), 2);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(0, 0), 0);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(1, 0), 1);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(2, 0), 2);
BOOST_CHECK_EQUAL(
- LogicSnapshot::pow2_ceil(INT64_MIN, 0), INT64_MIN);
+ LogicSegment::pow2_ceil(INT64_MIN, 0), INT64_MIN);
BOOST_CHECK_EQUAL(
- LogicSnapshot::pow2_ceil(INT64_MAX, 0), INT64_MAX);
+ LogicSegment::pow2_ceil(INT64_MAX, 0), INT64_MAX);
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(0, 1), 0);
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(1, 1), 2);
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(2, 1), 2);
- BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(3, 1), 4);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(0, 1), 0);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(1, 1), 2);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(2, 1), 2);
+ BOOST_CHECK_EQUAL(LogicSegment::pow2_ceil(3, 1), 4);
}
BOOST_AUTO_TEST_CASE(Basic)
{
- // Create an empty LogicSnapshot object
+ // Create an empty LogicSegment object
sr_datafeed_logic logic;
logic.length = 0;
logic.unitsize = 1;
- logic.data = NULL;
+ logic.data = nullptr;
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
- //----- Test LogicSnapshot::push_logic -----//
+ //----- Test LogicSegment::push_logic -----//
BOOST_CHECK(s.get_sample_count() == 0);
- for (unsigned int i = 0; i < LogicSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 0; i < LogicSegment::ScaleStepCount; i++)
{
- const LogicSnapshot::MipMapLevel &m = s._mip_map[i];
+ const LogicSegment::MipMapLevel &m = s.mip_map_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.data == NULL);
+ BOOST_CHECK(m.data == nullptr);
}
// Push 8 samples of all zeros
@@ -86,12 +95,12 @@ BOOST_AUTO_TEST_CASE(Basic)
BOOST_CHECK(s.get_sample_count() == 8);
// There should not be enough samples to have a single mip map sample
- for (unsigned int i = 0; i < LogicSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 0; i < LogicSegment::ScaleStepCount; i++)
{
- const LogicSnapshot::MipMapLevel &m = s._mip_map[i];
+ const LogicSegment::MipMapLevel &m = s.mip_map_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.data == NULL);
+ BOOST_CHECK(m.data == nullptr);
}
// Push 8 samples of 0x11s to bring the total up to 16
@@ -99,41 +108,41 @@ BOOST_AUTO_TEST_CASE(Basic)
// There should now be enough data for exactly one sample
// in mip map level 0, and that sample should be 0
- const LogicSnapshot::MipMapLevel &m0 = s._mip_map[0];
+ const LogicSegment::MipMapLevel &m0 = s.mip_map_[0];
BOOST_CHECK_EQUAL(m0.length, 1);
- BOOST_CHECK_EQUAL(m0.data_length, LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(m0.data != NULL);
+ BOOST_CHECK_EQUAL(m0.data_length, LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(m0.data != nullptr);
BOOST_CHECK_EQUAL(((uint8_t*)m0.data)[0], 0x11);
// The higher levels should still be empty
- for (unsigned int i = 1; i < LogicSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 1; i < LogicSegment::ScaleStepCount; i++)
{
- const LogicSnapshot::MipMapLevel &m = s._mip_map[i];
+ const LogicSegment::MipMapLevel &m = s.mip_map_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.data == NULL);
+ BOOST_CHECK(m.data == nullptr);
}
// Push 240 samples of all zeros to bring the total up to 256
push_logic(s, 240, 0);
BOOST_CHECK_EQUAL(m0.length, 16);
- BOOST_CHECK_EQUAL(m0.data_length, LogicSnapshot::MipMapDataUnit);
+ BOOST_CHECK_EQUAL(m0.data_length, LogicSegment::MipMapDataUnit);
BOOST_CHECK_EQUAL(((uint8_t*)m0.data)[1], 0x11);
for (unsigned int i = 2; i < m0.length; i++)
BOOST_CHECK_EQUAL(((uint8_t*)m0.data)[i], 0);
- const LogicSnapshot::MipMapLevel &m1 = s._mip_map[1];
+ const LogicSegment::MipMapLevel &m1 = s.mip_map_[1];
BOOST_CHECK_EQUAL(m1.length, 1);
- BOOST_CHECK_EQUAL(m1.data_length, LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(m1.data != NULL);
+ BOOST_CHECK_EQUAL(m1.data_length, LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(m1.data != nullptr);
BOOST_CHECK_EQUAL(((uint8_t*)m1.data)[0], 0x11);
- //----- Test LogicSnapshot::get_subsampled_edges -----//
+ //----- Test LogicSegment::get_subsampled_edges -----//
// Test a full view at full zoom.
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
s.get_subsampled_edges(edges, 0, 255, 1, 0);
BOOST_REQUIRE_EQUAL(edges.size(), 4);
@@ -167,19 +176,19 @@ BOOST_AUTO_TEST_CASE(LargeData)
for (unsigned int i = 0; i < Length; i++)
*data++ = (uint8_t)(i >> 8);
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
delete[] (uint8_t*)logic.data;
BOOST_CHECK(s.get_sample_count() == Length);
// Check mip map level 0
- BOOST_CHECK_EQUAL(s._mip_map[0].length, 62500);
- BOOST_CHECK_EQUAL(s._mip_map[0].data_length,
- LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(s._mip_map[0].data != NULL);
+ BOOST_CHECK_EQUAL(s.mip_map_[0].length, 62500);
+ BOOST_CHECK_EQUAL(s.mip_map_[0].data_length,
+ LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(s.mip_map_[0].data != nullptr);
prev_sample = 0;
- for (unsigned int i = 0; i < s._mip_map[0].length;)
+ for (unsigned int i = 0; i < s.mip_map_[0].length;)
{
BOOST_TEST_MESSAGE("Testing mip_map[0].data[" << i << "]");
@@ -188,7 +197,7 @@ BOOST_AUTO_TEST_CASE(LargeData)
prev_sample ^ sample);
prev_sample = sample;
- for (int j = 1; i < s._mip_map[0].length && j < 16; j++)
+ for (int j = 1; i < s.mip_map_[0].length && j < 16; j++)
{
BOOST_TEST_MESSAGE("Testing mip_map[0].data[" << i << "]");
BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, 0);
@@ -196,13 +205,13 @@ BOOST_AUTO_TEST_CASE(LargeData)
}
// Check mip map level 1
- BOOST_CHECK_EQUAL(s._mip_map[1].length, 3906);
- BOOST_CHECK_EQUAL(s._mip_map[1].data_length,
- LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(s._mip_map[1].data != NULL);
+ BOOST_CHECK_EQUAL(s.mip_map_[1].length, 3906);
+ BOOST_CHECK_EQUAL(s.mip_map_[1].data_length,
+ LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(s.mip_map_[1].data != nullptr);
prev_sample = 0;
- for (unsigned int i = 0; i < s._mip_map[1].length; i++)
+ for (unsigned int i = 0; i < s.mip_map_[1].length; i++)
{
BOOST_TEST_MESSAGE("Testing mip_map[1].data[" << i << "]");
@@ -214,13 +223,13 @@ BOOST_AUTO_TEST_CASE(LargeData)
}
// Check mip map level 2
- BOOST_CHECK_EQUAL(s._mip_map[2].length, 244);
- BOOST_CHECK_EQUAL(s._mip_map[2].data_length,
- LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(s._mip_map[2].data != NULL);
+ BOOST_CHECK_EQUAL(s.mip_map_[2].length, 244);
+ BOOST_CHECK_EQUAL(s.mip_map_[2].data_length,
+ LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(s.mip_map_[2].data != nullptr);
prev_sample = 0;
- for (unsigned int i = 0; i < s._mip_map[2].length; i++)
+ for (unsigned int i = 0; i < s.mip_map_[2].length; i++)
{
BOOST_TEST_MESSAGE("Testing mip_map[2].data[" << i << "]");
@@ -232,27 +241,27 @@ BOOST_AUTO_TEST_CASE(LargeData)
}
// Check mip map level 3
- BOOST_CHECK_EQUAL(s._mip_map[3].length, 15);
- BOOST_CHECK_EQUAL(s._mip_map[3].data_length,
- LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(s._mip_map[3].data != NULL);
+ BOOST_CHECK_EQUAL(s.mip_map_[3].length, 15);
+ BOOST_CHECK_EQUAL(s.mip_map_[3].data_length,
+ LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(s.mip_map_[3].data != nullptr);
- for (unsigned int i = 0; i < s._mip_map[3].length; i++)
- BOOST_CHECK_EQUAL(*((uint8_t*)s._mip_map[3].data + i),
+ for (unsigned int i = 0; i < s.mip_map_[3].length; i++)
+ BOOST_CHECK_EQUAL(*((uint8_t*)s.mip_map_[3].data + i),
0xFF);
// Check the higher levels
- for (unsigned int i = 4; i < LogicSnapshot::ScaleStepCount; i++)
+ for (unsigned int i = 4; i < LogicSegment::ScaleStepCount; i++)
{
- const LogicSnapshot::MipMapLevel &m = s._mip_map[i];
+ const LogicSegment::MipMapLevel &m = s.mip_map_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.data == NULL);
+ BOOST_CHECK(m.data == nullptr);
}
- //----- Test LogicSnapshot::get_subsampled_edges -----//
+ //----- Test LogicSegment::get_subsampled_edges -----//
// Check in normal case
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
s.get_subsampled_edges(edges, 0, Length-1, 1, 7);
BOOST_CHECK_EQUAL(edges.size(), 32);
@@ -278,9 +287,9 @@ BOOST_AUTO_TEST_CASE(Pulses)
const int Period = 64;
const int Length = Cycles * Period;
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
- //----- Create a LogicSnapshot -----//
+ //----- Create a LogicSegment -----//
sr_datafeed_logic logic;
logic.unitsize = 1;
logic.length = Length;
@@ -293,23 +302,23 @@ BOOST_AUTO_TEST_CASE(Pulses)
*p++ = 0x00;
}
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
delete[] (uint8_t*)logic.data;
//----- Check the mip-map -----//
// Check mip map level 0
- BOOST_CHECK_EQUAL(s._mip_map[0].length, 12);
- BOOST_CHECK_EQUAL(s._mip_map[0].data_length,
- LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(s._mip_map[0].data != NULL);
+ BOOST_CHECK_EQUAL(s.mip_map_[0].length, 12);
+ BOOST_CHECK_EQUAL(s.mip_map_[0].data_length,
+ LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(s.mip_map_[0].data != nullptr);
- for (unsigned int i = 0; i < s._mip_map[0].length;) {
+ for (unsigned int i = 0; i < s.mip_map_[0].length;) {
BOOST_TEST_MESSAGE("Testing mip_map[0].data[" << i << "]");
BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, 0xFF);
for (int j = 1;
- i < s._mip_map[0].length &&
- j < Period/LogicSnapshot::MipMapScaleFactor; j++) {
+ i < s.mip_map_[0].length &&
+ j < Period/LogicSegment::MipMapScaleFactor; j++) {
BOOST_TEST_MESSAGE(
"Testing mip_map[0].data[" << i << "]");
BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, 0x00);
@@ -317,11 +326,11 @@ BOOST_AUTO_TEST_CASE(Pulses)
}
// Check the higher levels are all inactive
- for (unsigned int i = 1; i < LogicSnapshot::ScaleStepCount; i++) {
- const LogicSnapshot::MipMapLevel &m = s._mip_map[i];
+ for (unsigned int i = 1; i < LogicSegment::ScaleStepCount; i++) {
+ const LogicSegment::MipMapLevel &m = s.mip_map_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.data == NULL);
+ BOOST_CHECK(m.data == nullptr);
}
//----- Test get_subsampled_edges at reduced scale -----//
@@ -341,9 +350,9 @@ BOOST_AUTO_TEST_CASE(LongPulses)
const int Length = Cycles * Period;
int j;
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
- //----- Create a LogicSnapshot -----//
+ //----- Create a LogicSegment -----//
sr_datafeed_logic logic;
logic.unitsize = 8;
logic.length = Length * 8;
@@ -357,25 +366,25 @@ BOOST_AUTO_TEST_CASE(LongPulses)
*p++ = 0;
}
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
delete[] (uint64_t*)logic.data;
//----- Check the mip-map -----//
// Check mip map level 0
- BOOST_CHECK_EQUAL(s._mip_map[0].length, 12);
- BOOST_CHECK_EQUAL(s._mip_map[0].data_length,
- LogicSnapshot::MipMapDataUnit);
- BOOST_REQUIRE(s._mip_map[0].data != NULL);
+ BOOST_CHECK_EQUAL(s.mip_map_[0].length, 12);
+ BOOST_CHECK_EQUAL(s.mip_map_[0].data_length,
+ LogicSegment::MipMapDataUnit);
+ BOOST_REQUIRE(s.mip_map_[0].data != nullptr);
- for (unsigned int i = 0; i < s._mip_map[0].length;) {
- for (j = 0; i < s._mip_map[0].length && j < 2; j++) {
+ for (unsigned int i = 0; i < s.mip_map_[0].length;) {
+ for (j = 0; i < s.mip_map_[0].length && j < 2; j++) {
BOOST_TEST_MESSAGE(
"Testing mip_map[0].data[" << i << "]");
BOOST_CHECK_EQUAL(s.get_subsample(0, i++), ~0);
}
- for (; i < s._mip_map[0].length &&
- j < Period/LogicSnapshot::MipMapScaleFactor; j++) {
+ for (; i < s.mip_map_[0].length &&
+ j < Period/LogicSegment::MipMapScaleFactor; j++) {
BOOST_TEST_MESSAGE(
"Testing mip_map[0].data[" << i << "]");
BOOST_CHECK_EQUAL(s.get_subsample(0, i++), 0);
@@ -383,11 +392,11 @@ BOOST_AUTO_TEST_CASE(LongPulses)
}
// Check the higher levels are all inactive
- for (unsigned int i = 1; i < LogicSnapshot::ScaleStepCount; i++) {
- const LogicSnapshot::MipMapLevel &m = s._mip_map[i];
+ for (unsigned int i = 1; i < LogicSegment::ScaleStepCount; i++) {
+ const LogicSegment::MipMapLevel &m = s.mip_map_[i];
BOOST_CHECK_EQUAL(m.length, 0);
BOOST_CHECK_EQUAL(m.data_length, 0);
- BOOST_CHECK(m.data == NULL);
+ BOOST_CHECK(m.data == nullptr);
}
//----- Test get_subsampled_edges at a full scale -----//
@@ -438,7 +447,7 @@ BOOST_AUTO_TEST_CASE(LisaMUsbHid)
bool state = false;
int lastEdgePos = 0;
- //----- Create a LogicSnapshot -----//
+ //----- Create a LogicSegment -----//
sr_datafeed_logic logic;
logic.unitsize = 1;
logic.length = Length;
@@ -454,10 +463,10 @@ BOOST_AUTO_TEST_CASE(LisaMUsbHid)
state = !state;
}
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
delete[] (uint64_t*)logic.data;
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
/* The trailing edge of the pulse train is falling in the source data.
@@ -470,9 +479,9 @@ BOOST_AUTO_TEST_CASE(LisaMUsbHid)
}
/*
- * This test checks the rendering of wide data (more than 8 probes)
+ * This test checks the rendering of wide data (more than 8 channels)
* Probe signals are either all-high, or all-low, but are interleaved such that
- * they would toggle during every sample if treated like 8 probes.
+ * they would toggle during every sample if treated like 8 channels.
* The packet contains a large number of samples, so the mipmap generation kicks
* in.
*
@@ -491,9 +500,9 @@ BOOST_AUTO_TEST_CASE(WideData)
for (int i = 0; i < Length; i++)
data[i] = 0x0FF0;
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
edges.clear();
s.get_subsampled_edges(edges, 0, Length-1, 1, 0);
@@ -523,12 +532,13 @@ BOOST_AUTO_TEST_CASE(Sixteen)
for (int i = 0; i < Length; i++)
data[i] = 0xFFFE;
- LogicSnapshot s(logic);
+ LogicSegment s(logic);
- vector<LogicSnapshot::EdgePair> edges;
+ vector<LogicSegment::EdgePair> edges;
s.get_subsampled_edges(edges, 0, 2, 0.0004, 1);
BOOST_CHECK_EQUAL(edges.size(), 2);
}
BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/test/test.cpp b/test/test.cpp
index 04c9981..2d67f29 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -20,3 +20,9 @@
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
+#include "test/test.hpp"
+
+std::ostream& operator<<(std::ostream& stream, const QString& str)
+{
+ return stream << str.toUtf8().data();
+}
diff --git a/test/test.cpp b/test/test.hpp
similarity index 77%
copy from test/test.cpp
copy to test/test.hpp
index 04c9981..1400370 100644
--- a/test/test.cpp
+++ b/test/test.hpp
@@ -1,7 +1,7 @@
/*
* This file is part of the PulseView project.
*
- * Copyright (C) 2012 Joel Holdsworth <joel at airwebreathe.org.uk>
+ * Copyright (C) 2015 Jens Steinhauser <jens.steinhauser at gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,5 +18,11 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#define BOOST_TEST_MAIN
-#include <boost/test/unit_test.hpp>
+#ifndef PULSEVIEW_TEST_TEST_HPP
+#define PULSEVIEW_TEST_TEST_HPP
+
+#include <QString>
+
+std::ostream& operator<<(std::ostream& stream, const QString& str);
+
+#endif
diff --git a/test/util.cpp b/test/util.cpp
new file mode 100644
index 0000000..21a32c5
--- /dev/null
+++ b/test/util.cpp
@@ -0,0 +1,240 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Jens Steinhauser <jens.steinhauser at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include "pv/util.hpp"
+#include "test/test.hpp"
+
+using namespace pv::util;
+using ts = pv::util::Timestamp;
+
+namespace {
+ QChar mu = QChar(0x03BC);
+
+ pv::util::SIPrefix unspecified = pv::util::SIPrefix::unspecified;
+ pv::util::SIPrefix yocto = pv::util::SIPrefix::yocto;
+ pv::util::SIPrefix nano = pv::util::SIPrefix::nano;
+ pv::util::SIPrefix micro = pv::util::SIPrefix::micro;
+ pv::util::SIPrefix milli = pv::util::SIPrefix::milli;
+ pv::util::SIPrefix none = pv::util::SIPrefix::none;
+ pv::util::SIPrefix kilo = pv::util::SIPrefix::kilo;
+ pv::util::SIPrefix yotta = pv::util::SIPrefix::yotta;
+
+ pv::util::TimeUnit Time = pv::util::TimeUnit::Time;
+}
+
+BOOST_AUTO_TEST_SUITE(UtilTest)
+
+BOOST_AUTO_TEST_CASE(exponent_test)
+{
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::yocto), -24);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::zepto), -21);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::atto), -18);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::femto), -15);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::pico), -12);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::nano), -9);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::micro), -6);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::milli), -3);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::none), 0);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::kilo), 3);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::mega), 6);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::giga), 9);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::tera), 12);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::peta), 15);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::exa), 18);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::zetta), 21);
+ BOOST_CHECK_EQUAL(exponent(SIPrefix::yotta), 24);
+}
+
+BOOST_AUTO_TEST_CASE(format_time_si_test)
+{
+ // check prefix calculation
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("0")), "0 s");
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-24")), "+1 ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-23")), "+10 ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-22")), "+100 ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-21")), "+1 zs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-20")), "+10 zs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-19")), "+100 zs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-18")), "+1 as");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-17")), "+10 as");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-16")), "+100 as");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-15")), "+1 fs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-14")), "+10 fs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-13")), "+100 fs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-12")), "+1 ps");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-11")), "+10 ps");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-10")), "+100 ps");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-9")), "+1 ns");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-8")), "+10 ns");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-7")), "+100 ns");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-6")), QString("+1 ") + mu + "s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-5")), QString("+10 ") + mu + "s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-4")), QString("+100 ") + mu + "s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-3")), "+1 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-2")), "+10 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-1")), "+100 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e0")), "+1 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e1")), "+10 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e2")), "+100 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e3")), "+1 ks");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e4")), "+10 ks");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e5")), "+100 ks");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e6")), "+1 Ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e7")), "+10 Ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e8")), "+100 Ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e9")), "+1 Gs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e10")), "+10 Gs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e11")), "+100 Gs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e12")), "+1 Ts");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e13")), "+10 Ts");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e14")), "+100 Ts");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e15")), "+1 Ps");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e16")), "+10 Ps");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e17")), "+100 Ps");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e18")), "+1 Es");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e19")), "+10 Es");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e20")), "+100 Es");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e21")), "+1 Zs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e22")), "+10 Zs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e23")), "+100 Zs");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e24")), "+1 Ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e25")), "+10 Ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e26")), "+100 Ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e27")), "+1000 Ys");
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1234")), "+1 ks");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1234"), kilo, 3), "+1.234 ks");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1234.5678")), "+1 ks");
+
+ // check prefix
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-24"), yocto), "+1 ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-21"), yocto), "+1000 ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("0"), yocto), "0 ys");
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-4"), milli), "+0 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-4"), milli, 1), "+0.1 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1000"), milli), "+1000000 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("0"), milli), "0 ms");
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-1"), none), "+0 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-1"), none, 1), "+0.1 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e-1"), none, 2), "+0.10 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1"), none), "+1 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e1"), none), "+10 s");
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e23"), yotta), "+0 Ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e23"), yotta, 1), "+0.1 Ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1e27"), yotta), "+1000 Ys");
+ BOOST_CHECK_EQUAL(format_time_si(ts("0"), yotta), "0 Ys");
+
+ // check precision, rounding
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678")), "+1 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.4")), "+1 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.5")), "+2 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.9")), "+2 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), unspecified, 2), "+1.23 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), unspecified, 3), "+1.235 s");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), milli, 3), "+1234.568 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.2345678"), milli, 0), "+1235 ms");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1.2"), unspecified, 3), "+1.200 s");
+
+ // check unit and sign
+
+ BOOST_CHECK_EQUAL(format_time_si(ts("-1"), none, 0, "V", true), "-1 V");
+ BOOST_CHECK_EQUAL(format_time_si(ts("-1"), none, 0, "V", false), "-1 V");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1"), none, 0, "V", true), "+1 V");
+ BOOST_CHECK_EQUAL(format_time_si(ts("1"), none, 0, "V", false), "1 V");
+}
+
+BOOST_AUTO_TEST_CASE(format_time_si_adjusted_test)
+{
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-1.5"), milli), "-1500 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-1.0"), milli), "-1000 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-0.2"), milli), "-200 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts("-0.1"), milli), "-100 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.0"), milli), "0 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.1"), milli), "+100 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.2"), milli), "+200 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.3"), milli), "+300 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.4"), milli), "+400 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.5"), milli), "+500 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.6"), milli), "+600 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.7"), milli), "+700 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.8"), milli), "+800 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "0.9"), milli), "+900 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.0"), milli), "+1000 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.1"), milli), "+1100 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.2"), milli), "+1200 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.3"), milli), "+1300 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.4"), milli), "+1400 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), milli), "+1500 ms");
+
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), milli, 6), "+1500.000 ms");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 6), "+1500000000 ns");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 8), "+1500000000 ns");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 9), "+1500000000 ns");
+ BOOST_CHECK_EQUAL(format_time_si_adjusted(ts( "1.5"), nano, 10), "+1500000000.0 ns");
+}
+
+BOOST_AUTO_TEST_CASE(format_time_minutes_test)
+{
+ using namespace std::placeholders;
+
+ auto fmt = std::bind(format_time_minutes, _1, _2, true);
+
+ BOOST_CHECK_EQUAL(fmt(ts( 0), 0), "+0:00");
+ BOOST_CHECK_EQUAL(fmt(ts( 1), 0), "+0:01");
+ BOOST_CHECK_EQUAL(fmt(ts( 59), 0), "+0:59");
+ BOOST_CHECK_EQUAL(fmt(ts( 60), 0), "+1:00");
+ BOOST_CHECK_EQUAL(fmt(ts( -1), 0), "-0:01");
+ BOOST_CHECK_EQUAL(fmt(ts( -59), 0), "-0:59");
+ BOOST_CHECK_EQUAL(fmt(ts( -60), 0), "-1:00");
+ BOOST_CHECK_EQUAL(fmt(ts( 100), 0), "+1:40");
+ BOOST_CHECK_EQUAL(fmt(ts( -100), 0), "-1:40");
+ BOOST_CHECK_EQUAL(fmt(ts( 4000), 0), "+1:06:40");
+ BOOST_CHECK_EQUAL(fmt(ts(-4000), 0), "-1:06:40");
+ BOOST_CHECK_EQUAL(fmt(ts(12000), 0), "+3:20:00");
+ BOOST_CHECK_EQUAL(fmt(ts(15000), 0), "+4:10:00");
+ BOOST_CHECK_EQUAL(fmt(ts(20000), 0), "+5:33:20");
+ BOOST_CHECK_EQUAL(fmt(ts(25000), 0), "+6:56:40");
+
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 0), "+123:04:05:06");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 1), "+123:04:05:06.0");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 2), "+123:04:05:06.01");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 3), "+123:04:05:06.007");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 4), "+123:04:05:06.007 0");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 5), "+123:04:05:06.007 01");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 6), "+123:04:05:06.007 008");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 7), "+123:04:05:06.007 008 0");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 8), "+123:04:05:06.007 008 01");
+ BOOST_CHECK_EQUAL(fmt(ts("10641906.007008009"), 9), "+123:04:05:06.007 008 009");
+
+ BOOST_CHECK_EQUAL(format_time_minutes(ts( 0), 0, false), "0:00");
+ BOOST_CHECK_EQUAL(format_time_minutes(ts( 100), 0, false), "1:40");
+ BOOST_CHECK_EQUAL(format_time_minutes(ts(-100), 0, false), "-1:40");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/view/ruler.cpp b/test/view/ruler.cpp
new file mode 100644
index 0000000..a6dd1c9
--- /dev/null
+++ b/test/view/ruler.cpp
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the PulseView project.
+ *
+ * Copyright (C) 2015 Jens Steinhauser <jens.steinhauser at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/test/floating_point_comparison.hpp>
+
+#include "pv/view/ruler.hpp"
+#include "test/test.hpp"
+
+using namespace pv::view;
+
+namespace {
+ QString format(const pv::util::Timestamp& t)
+ {
+ return pv::util::format_time_si(t, pv::util::SIPrefix::none, 6);
+ }
+
+ const double e = 0.0001;
+};
+
+BOOST_AUTO_TEST_SUITE(RulerTest)
+
+BOOST_AUTO_TEST_CASE(tick_position_test_0)
+{
+ const pv::util::Timestamp major_period("0.1");
+ const pv::util::Timestamp offset("0");
+ const double scale(0.001);
+ const int width(500);
+
+ const Ruler::TickPositions ts = Ruler::calculate_tick_positions(
+ major_period, offset, scale, width, format);
+
+ BOOST_REQUIRE_EQUAL(ts.major.size(), 6);
+
+ BOOST_CHECK_CLOSE(ts.major[0].first, 0, e);
+ BOOST_CHECK_CLOSE(ts.major[1].first, 100, e);
+ BOOST_CHECK_CLOSE(ts.major[2].first, 200, e);
+ BOOST_CHECK_CLOSE(ts.major[3].first, 300, e);
+ BOOST_CHECK_CLOSE(ts.major[4].first, 400, e);
+ BOOST_CHECK_CLOSE(ts.major[5].first, 500, e);
+
+ BOOST_CHECK_EQUAL(ts.major[0].second, "0.000000 s");
+ BOOST_CHECK_EQUAL(ts.major[1].second, "+0.100000 s");
+ BOOST_CHECK_EQUAL(ts.major[2].second, "+0.200000 s");
+ BOOST_CHECK_EQUAL(ts.major[3].second, "+0.300000 s");
+ BOOST_CHECK_EQUAL(ts.major[4].second, "+0.400000 s");
+ BOOST_CHECK_EQUAL(ts.major[5].second, "+0.500000 s");
+
+ BOOST_REQUIRE_EQUAL(ts.minor.size(), 16);
+
+ BOOST_CHECK_CLOSE(ts.minor[ 0], -25, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 1], 25, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 2], 50, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 3], 75, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 4], 125, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 5], 150, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 6], 175, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 7], 225, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 8], 250, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 9], 275, e);
+ BOOST_CHECK_CLOSE(ts.minor[10], 325, e);
+ BOOST_CHECK_CLOSE(ts.minor[11], 350, e);
+ BOOST_CHECK_CLOSE(ts.minor[12], 375, e);
+ BOOST_CHECK_CLOSE(ts.minor[13], 425, e);
+ BOOST_CHECK_CLOSE(ts.minor[14], 450, e);
+ BOOST_CHECK_CLOSE(ts.minor[15], 475, e);
+}
+
+BOOST_AUTO_TEST_CASE(tick_position_test_1)
+{
+ const pv::util::Timestamp major_period("0.1");
+ const pv::util::Timestamp offset("-0.463");
+ const double scale(0.001);
+ const int width(500);
+
+ const Ruler::TickPositions ts = Ruler::calculate_tick_positions(
+ major_period, offset, scale, width, format);
+
+ BOOST_REQUIRE_EQUAL(ts.major.size(), 5);
+
+ BOOST_CHECK_CLOSE(ts.major[0].first, 63, e);
+ BOOST_CHECK_CLOSE(ts.major[1].first, 163, e);
+ BOOST_CHECK_CLOSE(ts.major[2].first, 263, e);
+ BOOST_CHECK_CLOSE(ts.major[3].first, 363, e);
+ BOOST_CHECK_CLOSE(ts.major[4].first, 463, e);
+
+ BOOST_CHECK_EQUAL(ts.major[0].second, "-0.400000 s");
+ BOOST_CHECK_EQUAL(ts.major[1].second, "-0.300000 s");
+ BOOST_CHECK_EQUAL(ts.major[2].second, "-0.200000 s");
+ BOOST_CHECK_EQUAL(ts.major[3].second, "-0.100000 s");
+ BOOST_CHECK_EQUAL(ts.major[4].second, "0.000000 s");
+
+ BOOST_REQUIRE_EQUAL(ts.minor.size(), 17);
+ BOOST_CHECK_CLOSE(ts.minor[ 0], -12, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 1], 13, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 2], 38, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 3], 88, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 4], 113, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 5], 138, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 6], 188, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 7], 213, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 8], 238, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 9], 288, e);
+ BOOST_CHECK_CLOSE(ts.minor[10], 313, e);
+ BOOST_CHECK_CLOSE(ts.minor[11], 338, e);
+ BOOST_CHECK_CLOSE(ts.minor[12], 388, e);
+ BOOST_CHECK_CLOSE(ts.minor[13], 413, e);
+ BOOST_CHECK_CLOSE(ts.minor[14], 438, e);
+ BOOST_CHECK_CLOSE(ts.minor[15], 488, e);
+ BOOST_CHECK_CLOSE(ts.minor[16], 513, e);
+}
+
+BOOST_AUTO_TEST_CASE(tick_position_test_2)
+{
+ const pv::util::Timestamp major_period("20");
+ const pv::util::Timestamp offset("8");
+ const double scale(0.129746);
+ const int width(580);
+
+ const Ruler::TickPositions ts = Ruler::calculate_tick_positions(
+ major_period, offset, scale, width, format);
+
+ const double mp = 5;
+ const int off = 8;
+
+ BOOST_REQUIRE_EQUAL(ts.major.size(), 4);
+
+ BOOST_CHECK_CLOSE(ts.major[0].first, ( 4 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.major[1].first, ( 8 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.major[2].first, (12 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.major[3].first, (16 * mp - off) / scale, e);
+
+ BOOST_CHECK_EQUAL(ts.major[0].second, "+20.000000 s");
+ BOOST_CHECK_EQUAL(ts.major[1].second, "+40.000000 s");
+ BOOST_CHECK_EQUAL(ts.major[2].second, "+60.000000 s");
+ BOOST_CHECK_EQUAL(ts.major[3].second, "+80.000000 s");
+
+ BOOST_REQUIRE_EQUAL(ts.minor.size(), 13);
+
+ BOOST_CHECK_CLOSE(ts.minor[ 0], ( 1 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 1], ( 2 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 2], ( 3 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 3], ( 5 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 4], ( 6 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 5], ( 7 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 6], ( 9 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 7], (10 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 8], (11 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[ 9], (13 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[10], (14 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[11], (15 * mp - off) / scale, e);
+ BOOST_CHECK_CLOSE(ts.minor[12], (17 * mp - off) / scale, e);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pulseview.git
More information about the debian-science-commits
mailing list