]> sigrok.org Git - pulseview.git/commitdiff
Session: Fix issue #67 by improving error handling master github/master
authorSoeren Apel <redacted>
Thu, 14 Mar 2024 20:58:47 +0000 (21:58 +0100)
committerSoeren Apel <redacted>
Thu, 14 Mar 2024 20:59:45 +0000 (21:59 +0100)
90 files changed:
CMakeLists.txt
INSTALL
README
config.h.in
doc/pulseview.1
l10n/es_MX.ts
l10n/ja_jp.ts [new file with mode: 0644]
l10n/zh_cn.ts [new file with mode: 0644]
main.cpp
manual/CMakeLists.txt
manual/manual.txt
pv/application.cpp
pv/application.hpp
pv/binding/device.cpp
pv/data/analog.cpp
pv/data/analog.hpp
pv/data/decode/annotation.cpp
pv/data/decode/decoder.cpp
pv/data/decode/decoder.hpp
pv/data/decode/row.cpp
pv/data/decodesignal.cpp
pv/data/decodesignal.hpp
pv/data/logic.cpp
pv/data/logic.hpp
pv/data/logicsegment.cpp
pv/data/logicsegment.hpp
pv/data/mathsignal.cpp
pv/data/mathsignal.hpp
pv/data/segment.hpp
pv/data/signalbase.cpp
pv/data/signalbase.hpp
pv/data/signaldata.hpp
pv/dialogs/settings.cpp
pv/dialogs/settings.hpp
pv/dialogs/storeprogress.cpp
pv/dialogs/storeprogress.hpp
pv/exprtk.hpp
pv/globalsettings.cpp
pv/globalsettings.hpp
pv/logging.cpp
pv/mainwindow.cpp
pv/mainwindow.hpp
pv/popups/channels.cpp
pv/popups/channels.hpp
pv/session.cpp
pv/session.hpp
pv/storesession.cpp
pv/subwindows/decoder_selector/model.cpp
pv/subwindows/decoder_selector/subwindow.cpp
pv/subwindows/decoder_selector/subwindow.hpp
pv/toolbars/mainbar.cpp
pv/views/decoder_binary/QHexView.cpp
pv/views/decoder_binary/QHexView.hpp
pv/views/decoder_binary/view.cpp
pv/views/decoder_binary/view.hpp
pv/views/tabular_decoder/model.cpp
pv/views/tabular_decoder/view.cpp
pv/views/trace/analogsignal.cpp
pv/views/trace/analogsignal.hpp
pv/views/trace/cursor.cpp
pv/views/trace/cursorpair.cpp
pv/views/trace/decodetrace.cpp
pv/views/trace/decodetrace.hpp
pv/views/trace/flag.cpp
pv/views/trace/flag.hpp
pv/views/trace/logicsignal.cpp
pv/views/trace/logicsignal.hpp
pv/views/trace/marginwidget.cpp
pv/views/trace/mathsignal.cpp
pv/views/trace/ruler.cpp
pv/views/trace/signal.cpp
pv/views/trace/signal.hpp
pv/views/trace/timemarker.cpp
pv/views/trace/timemarker.hpp
pv/views/trace/trace.cpp
pv/views/trace/tracegroup.cpp
pv/views/trace/view.cpp
pv/views/trace/view.hpp
pv/views/trace/viewport.cpp
pv/views/trace/viewport.hpp
pv/views/viewbase.hpp
pv/widgets/decodermenu.cpp
pv/widgets/devicetoolbutton.cpp
pv/widgets/exportmenu.cpp
pv/widgets/flowlayout.cpp
pv/widgets/importmenu.cpp
pv/widgets/popup.cpp
pv/widgets/sweeptimingwidget.cpp
pv/widgets/timestampspinbox.cpp
translations.qrc

index add9c02f9923464ba02ed981022b49c899ad8b0f..ec86073dcf956eb97fc03345f95e0b95abbcca70 100644 (file)
@@ -48,6 +48,7 @@ option(ENABLE_DECODE "Build with libsigrokdecode" TRUE)
 option(ENABLE_FLOW "Build with libsigrokflow" FALSE)
 option(ENABLE_TESTS "Enable unit tests" FALSE)
 option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE)
+option(ENABLE_TS_UPDATE "Update .ts source files (Qt l10n)" FALSE)
 
 if(WIN32)
        # On Windows/MinGW we need to statically link to libraries.
@@ -74,8 +75,59 @@ add_subdirectory(manual)
 #= Dependencies
 #-------------------------------------------------------------------------------
 
+include(CheckCSourceCompiles)
+include(CheckCXXCompilerFlag)
+include(CheckCXXSourceCompiles)
+include(CMakePushCheckState)
+include(memaccess)
+
+find_package(PkgConfig)
+
+if(CMAKE_VERSION VERSION_EQUAL "3.8.0" OR CMAKE_VERSION VERSION_GREATER "3.8.0")
+       check_cxx_compiler_flag("-std=c++17" HAVE_STD_CXX_17)
+       check_cxx_compiler_flag("-std=c++14" HAVE_STD_CXX_14)
+       check_cxx_compiler_flag("-std=c++11" HAVE_STD_CXX_11)
+       if(HAVE_STD_CXX_17)
+               message(STATUS "Using C++17 for the application build")
+               set(CMAKE_CXX_STANDARD 17)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++17")
+       elseif(HAVE_STD_CXX_14)
+               message(STATUS "Using C++14 for the application build")
+               set(CMAKE_CXX_STANDARD 14)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++14")
+       elseif(HAVE_STD_CXX_11)
+               message(STATUS "Using C++11 for the application build")
+               set(CMAKE_CXX_STANDARD 11)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++11")
+       else()
+               message(FATAL_ERROR "Need modern C++, at least language standard 11")
+       endif()
+else()
+       check_cxx_compiler_flag("-std=c++14" HAVE_STD_CXX_14)
+       check_cxx_compiler_flag("-std=c++11" HAVE_STD_CXX_11)
+       if(HAVE_STD_CXX_14)
+               message(STATUS "Using C++14 for the application build")
+               set(CMAKE_CXX_STANDARD 14)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++14")
+       elseif(HAVE_STD_CXX_11)
+               message(STATUS "Using C++11 for the application build")
+               set(CMAKE_CXX_STANDARD 11)
+               set(REQUIRED_STD_CXX_FLAGS "-std=c++11")
+       else()
+               message(FATAL_ERROR "Need modern C++, at least language standard 11")
+       endif()
+endif()
+
 list(APPEND PKGDEPS glib-2.0>=2.28.0)
-list(APPEND PKGDEPS glibmm-2.4>=2.28.0)
+
+# Try to find the prefered glibmm-2.4. If not found then add glibmm-2.68
+# to the dependency list.
+pkg_check_modules(GLIBMM_2_4 glibmm-2.4>=2.28.0)
+if(GLIBMM_2_4_FOUND)
+       list(APPEND PKGDEPS glibmm-2.4>=2.28.0)
+else()
+       list(APPEND PKGDEPS glibmm-2.68>=2.68.0)
+endif()
 
 if(ENABLE_FLOW)
        list(APPEND PKGDEPS gstreamermm-1.0>=1.8.0)
@@ -93,7 +145,6 @@ if(ANDROID)
        list(APPEND PKGDEPS libsigrokandroidutils>=0.1.0)
 endif()
 
-find_package(PkgConfig)
 pkg_check_modules(LIBSRCXX ${LIBSR_CXX_BINDING})
 if(NOT LIBSRCXX_FOUND OR NOT LIBSRCXX_VERSION)
        message(FATAL_ERROR "libsigrok C++ bindings missing, check libsigrok's 'configure' output (missing dependencies?)")
@@ -102,20 +153,35 @@ pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS})
 
 set(CMAKE_AUTOMOC TRUE)
 
-find_package(Qt5 5.3 COMPONENTS Core Gui LinguistTools Widgets Svg REQUIRED)
-
-message(STATUS "Qt version: ${Qt5_VERSION}")
+# Check for Qt5, and check for Qt6 if Qt5 is not found.
+set(QT_COMPONENTS Core Gui LinguistTools Widgets Svg)
+find_package(Qt5 5.3 QUIET COMPONENTS Core)
+if(Qt5_FOUND)
+       find_package(Qt5 5.3 COMPONENTS ${QT_COMPONENTS} REQUIRED)
+       message(STATUS "Qt version: ${Qt5_VERSION}")
+else()
+       find_package(Qt6 6.2 COMPONENTS ${QT_COMPONENTS} REQUIRED)
+       message(STATUS "Qt version: ${Qt6_VERSION}")
+endif()
 
 if(WIN32)
-       # MXE workaround: Use pkg-config to find Qt5 libs.
+       # MXE workaround: Use pkg-config to find Qt5 and Qt6 libs.
        # https://github.com/mxe/mxe/issues/1642
        # Not required (and doesn't work) on MSYS2.
        if(NOT DEFINED ENV{MSYSTEM})
-               pkg_check_modules(QT5ALL REQUIRED Qt5Widgets>=5.3 Qt5Gui>=5.3 Qt5Svg>=5.3)
+               if(Qt5_FOUND)
+                       pkg_check_modules(QT5ALL REQUIRED Qt5Widgets>=5.3 Qt5Gui>=5.3 Qt5Svg>=5.3)
+               else()
+                       pkg_check_modules(QT6ALL REQUIRED Qt6Widgets>=6.2 Qt6Gui>=6.2 Qt6Svg>=6.2)
+               endif()
        endif()
 endif()
 
-set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg)
+if(Qt5_FOUND)
+       set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg)
+else()
+       set(QT_LIBRARIES Qt6::Gui Qt6::Widgets Qt6::Svg)
+endif()
 
 set(BOOSTCOMPS filesystem serialization system)
 if(ENABLE_TESTS)
@@ -151,48 +217,66 @@ find_package(Threads REQUIRED)
 
 # Helper for checking for atomics
 function(check_working_cxx_atomics varname additional_lib)
-  include(CheckCXXSourceCompiles)
-  include(CMakePushCheckState)
-  cmake_push_check_state()
-  set(CMAKE_REQUIRED_FLAGS "-std=c++11")
-  set(CMAKE_REQUIRED_LIBRARIES "${additional_lib}")
-  set(CMAKE_REQUIRED_QUIET 1)
-  CHECK_CXX_SOURCE_COMPILES("
+       cmake_push_check_state()
+       set(CMAKE_REQUIRED_FLAGS "${REQUIRED_STD_CXX_FLAGS}")
+       set(CMAKE_REQUIRED_LIBRARIES "${additional_lib}")
+       set(CMAKE_REQUIRED_QUIET 1)
+       CHECK_CXX_SOURCE_COMPILES("
 #include <atomic>
 std::atomic<int> x;
 int main() {
-  return std::atomic_fetch_add_explicit(&x, 1, std::memory_order_seq_cst);
+       return std::atomic_fetch_add_explicit(&x, 1, std::memory_order_seq_cst);
 }
 " ${varname})
-  cmake_pop_check_state()
+       cmake_pop_check_state()
 endfunction(check_working_cxx_atomics)
 
 # First check if atomics work without the library.
 # If not, check if the library exists, and atomics work with it.
 check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITHOUT_LIB "")
 if(HAVE_CXX_ATOMICS_WITHOUT_LIB)
-  message(STATUS "Atomics provided by the C-library - yes")
+       message(STATUS "Atomics provided by the C-library - yes")
 else()
-  message(STATUS "Atomics provided by the C-library - no")
-  find_library(LIBATOMIC_LIBRARY NAMES atomic PATH_SUFFIXES lib)
-  if(LIBATOMIC_LIBRARY)
-    check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB "${LIBATOMIC_LIBRARY}")
-    if (HAVE_CXX_ATOMICS_WITH_LIB)
-      message(STATUS "Atomics provided by libatomic - yes")
-    else()
-      message(STATUS "Atomics provided by libatomic - no")
-      message(FATAL_ERROR "Compiler must support std::atomic!")
-    endif()
-  else()
-    message(FATAL_ERROR "Compiler appears to require libatomic, but cannot find it.")
-  endif()
+       message(STATUS "Atomics provided by the C-library - no")
+       find_library(LIBATOMIC_LIBRARY NAMES atomic PATH_SUFFIXES lib)
+       if(LIBATOMIC_LIBRARY)
+               check_working_cxx_atomics(HAVE_CXX_ATOMICS_WITH_LIB "${LIBATOMIC_LIBRARY}")
+               if (HAVE_CXX_ATOMICS_WITH_LIB)
+                       message(STATUS "Atomics provided by libatomic - yes")
+               else()
+                       message(STATUS "Atomics provided by libatomic - no")
+                       message(FATAL_ERROR "Compiler must support std::atomic!")
+               endif()
+       else()
+               message(FATAL_ERROR "Compiler appears to require libatomic, but cannot find it.")
+       endif()
+endif()
+
+# Check availability of features which depend on library versions.
+# TODO Ideally use check_symbol_exists() instead, reduce boilerplate.
+if(ENABLE_DECODE)
+       cmake_push_check_state()
+       set(CMAKE_REQUIRED_INCLUDES "${PKGDEPS_INCLUDE_DIRS}")
+       set(CMAKE_REQUIRED_LIBRARIES "sigrokdecode")
+       foreach (LPATH ${PKGDEPS_LIBRARY_DIRS})
+               list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "-L${LPATH}")
+       endforeach ()
+       check_c_source_compiles("
+       #include <libsigrokdecode/libsigrokdecode.h>
+       int main(int argc, char *argv[])
+       {
+               (void)argc;
+               (void)argv;
+               return srd_session_send_eof(NULL);
+       }
+       " HAVE_SRD_SESSION_SEND_EOF)
+       cmake_pop_check_state()
 endif()
 
 #===============================================================================
 #= System Introspection
 #-------------------------------------------------------------------------------
 
-include(memaccess)
 memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS)
 
 #===============================================================================
@@ -202,7 +286,11 @@ memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS)
 set(PV_TITLE PulseView)
 set(PV_VERSION_STRING "0.5.0")
 
-set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.4_VERSION})
+if(GLIBMM_2_4_FOUND)
+       set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.4_VERSION})
+else()
+       set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.68_VERSION})
+endif()
 
 include(GetGitRevisionDescription)
 
@@ -439,7 +527,11 @@ if(ANDROID)
        )
 endif()
 
-qt5_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+if(Qt5_FOUND)
+       qt5_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+else()
+       qt6_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES})
+endif()
 
 #===============================================================================
 #= Translations
@@ -451,10 +543,19 @@ if (NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
        configure_file("translations.qrc" "translations.qrc" COPYONLY)
 endif ()
 
-qt5_add_translation(QM_FILES ${TS_FILES})
-qt5_create_translation(QM_FILES ${pulseview_SOURCES} ${TS_FILES})
-
-qt5_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
+if(Qt5_FOUND)
+       qt5_add_translation(QM_FILES ${TS_FILES})
+       qt5_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
+       if (ENABLE_TS_UPDATE)
+               qt5_create_translation(QM_FILES ${pulseview_SOURCES} ${TS_FILES})
+       endif ()
+else()
+       qt6_add_translation(QM_FILES ${TS_FILES})
+       qt6_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
+       if (ENABLE_TS_UPDATE)
+               qt6_create_translation(QM_FILES ${pulseview_SOURCES} ${TS_FILES})
+       endif ()
+endif()
 
 #===============================================================================
 #= Global Definitions
@@ -463,7 +564,8 @@ qt5_add_resources(pulseview_RESOURCES_RCC ${CMAKE_BINARY_DIR}/translations.qrc)
 add_definitions(-DQT_NO_KEYWORDS)
 add_definitions(-D__STDC_LIMIT_MACROS)
 add_definitions(-Wall -Wextra)
-add_definitions(-std=c++11)
+add_definitions(${REQUIRED_STD_CXX_FLAGS})
+
 add_definitions(-DBOOST_MATH_DISABLE_FLOAT128=1)
 if(WIN32)
        add_definitions(-Wa,-mbig-obj -O3)
@@ -535,12 +637,18 @@ if(WIN32)
        # We also need QWindowsIntegrationPlugin, Qt5PlatformSupport (only for
        # Qt < 5.8.0), and all Qt libs and their dependencies.
        add_definitions(-DQT_STATICPLUGIN)
-       list(APPEND PULSEVIEW_LINK_LIBS Qt5::QSvgPlugin)
-       list(APPEND PULSEVIEW_LINK_LIBS Qt5::QWindowsIntegrationPlugin)
-       if(Qt5Gui_VERSION VERSION_LESS 5.8.0)
-               list(APPEND PULSEVIEW_LINK_LIBS -lQt5PlatformSupport)
+       if(Qt5_FOUND)
+               list(APPEND PULSEVIEW_LINK_LIBS Qt5::QSvgPlugin)
+               list(APPEND PULSEVIEW_LINK_LIBS Qt5::QWindowsIntegrationPlugin)
+               if(Qt5Gui_VERSION VERSION_LESS 5.8.0)
+                       list(APPEND PULSEVIEW_LINK_LIBS -lQt5PlatformSupport)
+               endif()
+               list(APPEND PULSEVIEW_LINK_LIBS ${QT5ALL_LDFLAGS})
+       else()
+               list(APPEND PULSEVIEW_LINK_LIBS Qt6::QSvgPlugin)
+               list(APPEND PULSEVIEW_LINK_LIBS Qt6::QWindowsIntegrationPlugin)
+               list(APPEND PULSEVIEW_LINK_LIBS ${QT6ALL_LDFLAGS})
        endif()
-       list(APPEND PULSEVIEW_LINK_LIBS ${QT5ALL_LDFLAGS})
 endif()
 
 if(ENABLE_STACKTRACE)
diff --git a/INSTALL b/INSTALL
index da89be8d014974312b214ecbe9192c937e556b55..e777959f83adf2b85806780ff70bd4ed6a2d6fbf 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -16,7 +16,8 @@ Requirements
  - libglib >= 2.28.0
  - glibmm-2.4 (>= 2.28.0)
  - Qt5 (>= 5.3), including the following components:
-    - Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg, Qt5LinguistTools
+    - Qt5Core, Qt5Gui, Qt5Widgets, Qt5Svg,
+      Qt5LinguistTools (qttools5-dev, qttools5-dev-tools)
     - Qt translation package (optional; needed at runtime, not build time)
  - libboost >= 1.55 (including the following libs):
     - libboost-system
diff --git a/README b/README
index d4056b3cd33c0d03b1045bfe05e5d6f76a480d68..61de9b21df76ce1647796ed31b64dc3cdc1f7be0 100644 (file)
--- a/README
+++ b/README
@@ -109,7 +109,7 @@ Mailing list
 IRC
 ---
 
-You can find the sigrok developers in the #sigrok IRC channel on Freenode.
+You can find the sigrok developers in the #sigrok IRC channel on Libera.Chat.
 
 
 Website
index 7720001a94861339fbfe0458f63c100cb405b1ac..f5855d18ac8c44ec2911bb9362669bae97c2e3aa 100644 (file)
@@ -34,6 +34,9 @@
 /* Platform properties */
 #cmakedefine HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS
 
+/* Presence of features which depend on library versions. */
+#cmakedefine HAVE_SRD_SESSION_SEND_EOF 1
+
 #define PV_GLIBMM_VERSION "@PV_GLIBMM_VERSION@"
 
 #endif
index 9663fdf94f3ba5387c66e31d87468b457c15f484..749f6daba85c696ac3790bc685228388804d654a 100644 (file)
@@ -138,6 +138,9 @@ Quit, i.e. shutdown PulseView (closing all session tabs).
 .TP
 .B "CTRL+w"
 Close the current session tab.
+.TP
+.B "SHIFT+mouse wheel"
+Scroll horizontally instead of zooming in/out.
 .SH "EXIT STATUS"
 .B PulseView
 exits with 0 on success, 1 on most failures.
index b7426b04a5b5feca0bc61a3950b4c3a7331b6fc8..77afc7fe5e9be34e21582bdf667137ed570cb55d 100644 (file)
@@ -4,9 +4,9 @@
 <context>
     <name>Application</name>
     <message>
-        <location filename="../pv/application.cpp" line="129"/>
+        <location filename="../pv/application.cpp" line="137"/>
         <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
-        <translation>Algunas partes de la aplicación aún pueden usar el idioma anterior. Volver a abrir las ventanas afectadas o reiniciar la aplicación solucionará esto.</translation>
+        <translation>Algunas partes de la aplicación podrían seguir usando el idioma anterior. Volver a abrir las ventanas afectadas o reiniciar la aplicación solucionará esto.</translation>
     </message>
 </context>
 <context>
@@ -35,7 +35,7 @@
 <context>
     <name>QHexView</name>
     <message>
-        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="291"/>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="339"/>
         <source>No data available</source>
         <translation>Datos no disponibles</translation>
     </message>
 <context>
     <name>QObject</name>
     <message>
-        <location filename="../main.cpp" line="114"/>
+        <location filename="../main.cpp" line="116"/>
         <source>Stack trace of previous crash:</source>
-        <translation>Stack trace de crash previo:</translation>
+        <translation>Seguimiento de pila del fallo anterior:</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="128"/>
+        <location filename="../main.cpp" line="130"/>
         <source>Don&apos;t show this message again</source>
-        <translation>No mostrar este mensaje de nuevo</translation>
+        <translation>No volver a mostrar este mensaje</translation>
     </message>
     <message>
-        <location filename="../main.cpp" line="131"/>
+        <location filename="../main.cpp" line="133"/>
         <source>When %1 last crashed, it created a stack trace.
 A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
-        <translation>Cuando %1 se bloqueó por última vez, creó un stack trace.\nSe guardó un formulario legible en el disco y fue escrito en el log. Puedes acceder a el desde el dialogo de configuración.</translation>
+        <translation>Cuando %1 se bloqueó por última vez, creó un seguimiento de pila.
+Se guardó un formulario legible para humanosen el disco y fue escrito en el log. Puedes acceder a el desde el dialogo de configuración.</translation>
     </message>
     <message>
         <location filename="../pv/devicemanager.cpp" line="65"/>
@@ -77,79 +78,80 @@ A human-readable form has been saved to disk and was written to the log. You may
         <translation>PulseView</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="279"/>
+        <location filename="../pv/mainwindow.cpp" line="284"/>
         <source>Decoder Selector</source>
-        <translation>Selección de decoder</translation>
+        <translation>Selección de decodificador</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="332"/>
+        <location filename="../pv/mainwindow.cpp" line="337"/>
         <source>Session %1</source>
         <translation>Sesión %1</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="514"/>
+        <location filename="../pv/mainwindow.cpp" line="519"/>
         <source>Create New Session</source>
-        <translation>Crear Nueva Sesión</translation>
+        <translation>Crear nueva sesión</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="520"/>
+        <location filename="../pv/mainwindow.cpp" line="525"/>
         <source>Start/Stop Acquisition</source>
-        <translation>Iniciar/Detener Adquisición</translation>
+        <translation>Iniciar/Detener adquisición</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="528"/>
+        <location filename="../pv/mainwindow.cpp" line="533"/>
         <source>Settings</source>
-        <translation>Configuraciones</translation>
+        <translation>Ajustes</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="580"/>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
         <source>Reload</source>
         <translation>Recargar</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="580"/>
-        <location filename="../pv/mainwindow.cpp" line="583"/>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <location filename="../pv/mainwindow.cpp" line="592"/>
         <source>Run</source>
         <translation>Ejecutar</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <location filename="../pv/mainwindow.cpp" line="598"/>
         <source>Stop</source>
         <translation>Detener</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="635"/>
-        <location filename="../pv/mainwindow.cpp" line="829"/>
-        <location filename="../pv/mainwindow.cpp" line="855"/>
+        <location filename="../pv/mainwindow.cpp" line="644"/>
+        <location filename="../pv/mainwindow.cpp" line="867"/>
+        <location filename="../pv/mainwindow.cpp" line="893"/>
         <source>Confirmation</source>
         <translation>Confirmación</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="636"/>
+        <location filename="../pv/mainwindow.cpp" line="645"/>
         <source>There is unsaved data. Close anyway?</source>
-        <translation>Hay datos sin guardar. Cerrar de todos modos?</translation>
+        <translation>Hay datos sin guardar. ¿Cerrar de todos modos?</translation>
     </message>
     <message>
-        <location filename="../pv/mainwindow.cpp" line="830"/>
-        <location filename="../pv/mainwindow.cpp" line="856"/>
+        <location filename="../pv/mainwindow.cpp" line="868"/>
+        <location filename="../pv/mainwindow.cpp" line="894"/>
         <source>This session contains unsaved data. Close it anyway?</source>
-        <translation>Esta sesión contiene datos sin almacenar. Cerrar de todos modos?</translation>
+        <translation>Esta sesión contiene datos sin guardar. ¿Cerrar de todos modos?</translation>
     </message>
 </context>
 <context>
     <name>pv::Session</name>
     <message>
-        <location filename="../pv/session.cpp" line="521"/>
+        <location filename="../pv/session.cpp" line="559"/>
         <source>Failed to select device</source>
+        <translatorcomment>Si en el panel &quot;Sources and forms&quot; la cadena de texto está dentro de &quot;show_session_error(tr(&quot;Failed to ...&quot;),e)&quot; traducir &quot;Failed &quot; como &quot;Error&quot;</translatorcomment>
         <translation>Error al seleccionar dispositivo</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="578"/>
+        <location filename="../pv/session.cpp" line="616"/>
         <source>Failed to open device</source>
         <translation>Error al abrir dispositivo</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="684"/>
+        <location filename="../pv/session.cpp" line="722"/>
         <source>Error</source>
         <translation>Error</translation>
     </message>
@@ -158,32 +160,37 @@ A human-readable form has been saved to disk and was written to the log. You may
         <translation type="vanished">Formato de entrada inesperado: %s</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="685"/>
+        <location filename="../pv/session.cpp" line="396"/>
+        <source>Can&apos;t restore generated signal of unknown type %1 (%2)</source>
+        <translation>No se puede restaurar la señal generada de tipo desconocido %1 ( %2)</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="723"/>
         <source>Unexpected input format: %1</source>
-        <translation type="unfinished"></translation>
+        <translation>Formato de entrada inesperado: %1</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="720"/>
+        <location filename="../pv/session.cpp" line="758"/>
         <source>Failed to load %1</source>
         <translation>Error al cargar %1</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="759"/>
+        <location filename="../pv/session.cpp" line="797"/>
         <source>No active device set, can&apos;t start acquisition.</source>
         <translation>No hay un dispositivo activo configurado, no se puede iniciar la adquisición.</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="772"/>
+        <location filename="../pv/session.cpp" line="810"/>
         <source>No channels enabled.</source>
         <translation>No hay canales habilitados.</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="1261"/>
+        <location filename="../pv/session.cpp" line="1318"/>
         <source>Out of memory, acquisition stopped.</source>
         <translation>Sin memoria, la adquisición se detuvo.</translation>
     </message>
     <message>
-        <location filename="../pv/session.cpp" line="1467"/>
+        <location filename="../pv/session.cpp" line="1525"/>
         <source>Can&apos;t handle more than 64 logic channels.</source>
         <translation>No puede manejar más de 64 canales lógicos.</translation>
     </message>
@@ -211,8 +218,9 @@ A human-readable form has been saved to disk and was written to the log. You may
         <translation>No se puede guardar el rango sin datos de muestra.</translation>
     </message>
     <message>
-        <location filename="../pv/storesession.cpp" line="188"/>
-        <location filename="../pv/storesession.cpp" line="295"/>
+        <location filename="../pv/storesession.cpp" line="191"/>
+        <location filename="../pv/storesession.cpp" line="298"/>
+        <location filename="../pv/storesession.cpp" line="313"/>
         <source>Error while saving: </source>
         <translation>Error al guardar: </translation>
     </message>
@@ -220,93 +228,128 @@ A human-readable form has been saved to disk and was written to the log. You may
 <context>
     <name>pv::binding::Device</name>
     <message>
-        <location filename="../pv/binding/device.cpp" line="97"/>
+        <location filename="../pv/binding/device.cpp" line="82"/>
+        <source>Note for device developers: Ignoring device configuration capability &apos;%1&apos; as it is missing GET and/or SET</source>
+        <translation>Nota para desarrolladores de dispositivos: Ignorar la capacidad de configuración del dispositivo &apos;%1&apos;, ya que falta GET y/o SET</translation>
+    </message>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="107"/>
         <source>No Limit</source>
-        <translation type="unfinished"></translation>
+        <translation>Sín límite</translation>
     </message>
 </context>
 <context>
     <name>pv::data::DecodeSignal</name>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="198"/>
+        <location filename="../pv/data/decodesignal.cpp" line="223"/>
         <source>No decoders</source>
         <translation>Sin decodificadores</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="205"/>
+        <location filename="../pv/data/decodesignal.cpp" line="230"/>
         <source>There are no channels assigned to this decoder</source>
         <translation>No hay canales asignados a este decodificador</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="219"/>
+        <location filename="../pv/data/decodesignal.cpp" line="244"/>
         <source>One or more required channels have not been specified</source>
         <translation>No se han especificado uno o más canales requeridos</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="238"/>
+        <location filename="../pv/data/decodesignal.cpp" line="260"/>
         <source>No input data</source>
         <translation>Sin datos de entrada</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="1172"/>
+        <location filename="../pv/data/decodesignal.cpp" line="1325"/>
         <source>Decoder reported an error</source>
         <translation>El decodificador reportó un error</translation>
     </message>
     <message>
-        <location filename="../pv/data/decodesignal.cpp" line="1308"/>
+        <location filename="../pv/data/decodesignal.cpp" line="1484"/>
         <source>Failed to create decoder instance</source>
         <translation>Error al crear la instancia del decodificador</translation>
     </message>
 </context>
+<context>
+    <name>pv::data::MathSignal</name>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="107"/>
+        <source>Math%1</source>
+        <translation>Math%1</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="306"/>
+        <source>No expression defined, nothing to do</source>
+        <translation>Sin expresión definida, nada que hacer</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="345"/>
+        <source>%1 at line %2, column %3: %4</source>
+        <translation>%1 en línea %2, columna %3: %4</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="364"/>
+        <location filename="../pv/data/mathsignal.cpp" line="536"/>
+        <source>&quot;%1&quot; isn&apos;t a valid analog signal</source>
+        <translation>&quot;%1&quot; no es una señal analógica válida</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="374"/>
+        <location filename="../pv/data/mathsignal.cpp" line="611"/>
+        <source>No data will be generated as %1 must be enabled</source>
+        <translation>No se generarán datos ya que %1 debe estar habilitado</translation>
+    </message>
+</context>
 <context>
     <name>pv::data::SignalBase</name>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="485"/>
+        <location filename="../pv/data/signalbase.cpp" line="525"/>
         <source>Signal average</source>
         <translation>Promedio de la señal</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="486"/>
+        <location filename="../pv/data/signalbase.cpp" line="526"/>
         <source>0.9V (for 1.8V CMOS)</source>
         <translation>0.9V (para 1.8V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="487"/>
+        <location filename="../pv/data/signalbase.cpp" line="527"/>
         <source>1.8V (for 3.3V CMOS)</source>
         <translation>1.8V (para 3.3V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="488"/>
+        <location filename="../pv/data/signalbase.cpp" line="528"/>
         <source>2.5V (for 5.0V CMOS)</source>
         <translation>2.5V (para 5.0V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="489"/>
+        <location filename="../pv/data/signalbase.cpp" line="529"/>
         <source>1.5V (for TTL)</source>
         <translation>1.5V (para TTL)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="494"/>
+        <location filename="../pv/data/signalbase.cpp" line="534"/>
         <source>Signal average +/- 15%</source>
         <translation>Promedio de la señal +/- 15%</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="495"/>
+        <location filename="../pv/data/signalbase.cpp" line="535"/>
         <source>0.3V/1.2V (for 1.8V CMOS)</source>
         <translation>0.3V/1.2V (para 1.8V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="496"/>
+        <location filename="../pv/data/signalbase.cpp" line="536"/>
         <source>0.7V/2.5V (for 3.3V CMOS)</source>
         <translation>0.7V/2.5V (para 3.3V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="497"/>
+        <location filename="../pv/data/signalbase.cpp" line="537"/>
         <source>1.3V/3.7V (for 5.0V CMOS)</source>
         <translation>1.3V/3.7V (para 5.0V CMOS)</translation>
     </message>
     <message>
-        <location filename="../pv/data/signalbase.cpp" line="498"/>
+        <location filename="../pv/data/signalbase.cpp" line="538"/>
         <source>0.8V/2.0V (for TTL)</source>
         <translation>0.8V/2.0V (para TTL)</translation>
     </message>
@@ -316,7 +359,7 @@ A human-readable form has been saved to disk and was written to the log. You may
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="58"/>
         <source>&amp;Scan for devices using driver above</source>
-        <translation>E&amp;Scanea por dispositivos utilizando el driver de arriba</translation>
+        <translation>E&amp;scanea por dispositivos utilizando el controlador de arriba</translation>
     </message>
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="63"/>
@@ -326,7 +369,7 @@ A human-readable form has been saved to disk and was written to the log. You may
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="75"/>
         <source>Step 1: Choose the driver</source>
-        <translation>Paso 1: Elige el driver</translation>
+        <translation>Paso 1: Elige el controlador</translation>
     </message>
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="79"/>
@@ -336,7 +379,7 @@ A human-readable form has been saved to disk and was written to the log. You may
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="80"/>
         <source>Serial &amp;Port</source>
-        <translation>&amp;Puerto Serial</translation>
+        <translation>&amp;Puerto serial</translation>
     </message>
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="81"/>
@@ -356,7 +399,7 @@ A human-readable form has been saved to disk and was written to the log. You may
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="140"/>
         <source>Step 3: Scan for devices</source>
-        <translation>Paso 3: Escanear por dispositivos</translation>
+        <translation>Paso 3: Escanea por dispositivos</translation>
     </message>
     <message>
         <location filename="../pv/dialogs/connect.cpp" line="146"/>
@@ -379,7 +422,7 @@ A human-readable form has been saved to disk and was written to the log. You may
     </message>
     <message>
         <location filename="../pv/dialogs/settings.cpp" line="153"/>
-        <location filename="../pv/dialogs/settings.cpp" line="397"/>
+        <location filename="../pv/dialogs/settings.cpp" line="415"/>
         <source>Decoders</source>
         <translation>Decodificadores</translation>
     </message>
@@ -391,293 +434,327 @@ A human-readable form has been saved to disk and was written to the log. You may
     <message>
         <location filename="../pv/dialogs/settings.cpp" line="172"/>
         <source>Logging</source>
-        <translation>Logging</translation>
+        <translation>Registros</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="236"/>
+        <location filename="../pv/dialogs/settings.cpp" line="241"/>
         <source>User interface language</source>
-        <translation>Lenguaje de la interfaz de usuario</translation>
+        <translation>Idioma de la interfaz de usuario</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="247"/>
+        <location filename="../pv/dialogs/settings.cpp" line="252"/>
         <source>User interface theme</source>
         <translation>Tema de la interfaz de usuario</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="249"/>
+        <location filename="../pv/dialogs/settings.cpp" line="254"/>
         <source>(You may need to restart PulseView for all UI elements to update)</source>
         <translation>(Es posible que deba reiniciar PulseView para que se actualicen todos los elementos de la interfaz de usuario)</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="255"/>
+        <location filename="../pv/dialogs/settings.cpp" line="260"/>
         <source>System Default</source>
-        <translation>Default del sistema</translation>
+        <translation>Por defecto del sistema</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="268"/>
+        <location filename="../pv/dialogs/settings.cpp" line="273"/>
         <source>Qt widget style</source>
-        <translation>Estilo de Qt widget</translation>
+        <translation>Estilo de widget Qt</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="270"/>
+        <location filename="../pv/dialogs/settings.cpp" line="275"/>
         <source>(Dark themes look best with the Fusion style)</source>
         <translation>(Los temas oscuros se ven mejor con el estilo Fusion)</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="277"/>
+        <location filename="../pv/dialogs/settings.cpp" line="282"/>
         <source>Save session &amp;setup along with .sr file</source>
         <translation>Guardar &amp;configuración de sesión junto al archivo .sr</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="291"/>
+        <location filename="../pv/dialogs/settings.cpp" line="286"/>
+        <source>Start acquisition for all open sessions when clicking &apos;Run&apos;</source>
+        <translation>Iniciar adquisición para todas las sesiones abiertas al dar clic en &apos;Ejecutar&apos;</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="301"/>
         <source>Trace View</source>
-        <translation>Vista de trazo</translation>
+        <translation>Vista de señales</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="299"/>
+        <location filename="../pv/dialogs/settings.cpp" line="309"/>
         <source>Use colored trace &amp;background</source>
-        <translation>Use &amp;fondo de trazas coloreado</translation>
+        <translation>Usar &amp;fondo de trazos coloreado</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="303"/>
+        <location filename="../pv/dialogs/settings.cpp" line="313"/>
         <source>Constantly perform &amp;zoom-to-fit during acquisition</source>
         <translation>Realizar constantemente zoom para &amp;ajustar durante la adquisición</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="307"/>
+        <location filename="../pv/dialogs/settings.cpp" line="317"/>
         <source>Perform a zoom-to-&amp;fit when acquisition stops</source>
-        <translation>Realice un zoom para ajustar cuando la adquisición se &amp;detenga</translation>
+        <translation>Realizar un zoom para ajustar cuando la adquisición se &amp;detenga</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="311"/>
         <source>Show time zero at the trigger</source>
-        <translation>Mostrar el tiempo cero en el trigger</translation>
+        <translation type="vanished">Mostrar el tiempo cero en el trigger</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="315"/>
+        <location filename="../pv/dialogs/settings.cpp" line="325"/>
         <source>Always keep &amp;newest samples at the right edge during capture</source>
-        <translation>Mantenga siempre las muestras más &amp;recientes en el borde derecho durante la captura</translation>
+        <translation>Mantener siempre las muestras más &amp;recientes en el borde derecho durante la captura</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="319"/>
+        <location filename="../pv/dialogs/settings.cpp" line="333"/>
         <source>Show data &amp;sampling points</source>
-        <translation>Mostrar puntos de datos &amp;sampleados</translation>
+        <translation>Mostrar puntos de datos mue&amp;streados</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="323"/>
         <source>Fill high areas of logic signals</source>
-        <translation>Rellenar áreas altas de señales lógicas</translation>
+        <translation type="vanished">Rellenar áreas altas de señales lógicas</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="321"/>
+        <source>Show time zero at the &amp;trigger</source>
+        <translation>Mostrar tiempo cero en el &amp;disparo</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="329"/>
+        <source>Allow &amp;vertical dragging in the view area</source>
+        <translation>Permitir arrastre &amp;vertical in el área de vista</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="337"/>
+        <source>Fill &amp;high areas of logic signals</source>
+        <translation>Llenar áreas en &amp;alto de señales lógicas</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="330"/>
+        <location filename="../pv/dialogs/settings.cpp" line="344"/>
         <source>Color to fill high areas of logic signals with</source>
         <translation>Color para llenar áreas altas de señales lógicas</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="334"/>
+        <location filename="../pv/dialogs/settings.cpp" line="348"/>
         <source>Show analog minor grid in addition to div grid</source>
-        <translation>Mostrar cuadrícula menor analogíca además de cuadrícula por div</translation>
+        <translation>Mostrar cuadrícula menor analógica además de cuadrícula por división</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="338"/>
+        <location filename="../pv/dialogs/settings.cpp" line="352"/>
         <source>Highlight mouse cursor using a vertical marker line</source>
-        <translation>Resalte el cursor del mouse usando una línea de marcador vertical</translation>
+        <translation>Resaltar el cursor del mouse usando una línea de marcador vertical</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="342"/>
-        <location filename="../pv/dialogs/settings.cpp" line="368"/>
-        <location filename="../pv/dialogs/settings.cpp" line="377"/>
+        <location filename="../pv/dialogs/settings.cpp" line="356"/>
+        <source>Keep active item on ruler selected when editing popup is closed</source>
+        <translation>Mantener el elemento activo seleccionado en la regla cuando se cierre la ventana de edición emergente</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="360"/>
+        <location filename="../pv/dialogs/settings.cpp" line="386"/>
+        <location filename="../pv/dialogs/settings.cpp" line="395"/>
         <source> pixels</source>
         <translation> píxeles</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="347"/>
+        <location filename="../pv/dialogs/settings.cpp" line="365"/>
         <source>Maximum distance from edges before markers snap to them</source>
         <translation>Distancia máxima desde los bordes antes de que los marcadores se ajusten a ellos</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="354"/>
+        <location filename="../pv/dialogs/settings.cpp" line="372"/>
         <source>Color to fill cursor area with</source>
         <translation>Color para llenar el área del cursor</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="357"/>
+        <location filename="../pv/dialogs/settings.cpp" line="375"/>
         <source>None</source>
         <translation>Ninguna</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="358"/>
+        <location filename="../pv/dialogs/settings.cpp" line="376"/>
         <source>Background</source>
         <translation>Fondo</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="359"/>
+        <location filename="../pv/dialogs/settings.cpp" line="377"/>
         <source>Dots</source>
         <translation>Puntos</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="364"/>
+        <location filename="../pv/dialogs/settings.cpp" line="382"/>
         <source>Conversion threshold display mode (analog traces only)</source>
-        <translation>Modo de visualización del umbral de conversión (solo trazas analógicas)</translation>
+        <translation>Modo de visualización del umbral de conversión (solo trazos analógicos)</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="373"/>
+        <location filename="../pv/dialogs/settings.cpp" line="391"/>
         <source>Default analog trace div height</source>
-        <translation>Altura de div de trazo análogo por defecto</translation>
+        <translation>Altura de división de trazo analógico por defecto</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="382"/>
+        <location filename="../pv/dialogs/settings.cpp" line="400"/>
         <source>Default logic trace height</source>
         <translation>Altura de trazo lógico por defecto</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="405"/>
+        <location filename="../pv/dialogs/settings.cpp" line="423"/>
         <source>Allow configuration of &amp;initial signal state</source>
         <translation>Permitir configuración de estado de señal &amp;inicial</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="409"/>
+        <location filename="../pv/dialogs/settings.cpp" line="427"/>
         <source>Always show all &amp;rows, even if no annotation is visible</source>
         <translation>Mostrar siempre todas las &amp;filas, incluso si no hay ninguna anotación visible</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="417"/>
+        <location filename="../pv/dialogs/settings.cpp" line="435"/>
         <source>Annotation export format</source>
         <translation>Formato de exportación de anotaciones</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="418"/>
+        <location filename="../pv/dialogs/settings.cpp" line="436"/>
         <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
         <translation>%s = rango de muestra; %d: nombre del decodificador; %r: nombre de fila; %c: nombre de clase</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="421"/>
+        <location filename="../pv/dialogs/settings.cpp" line="439"/>
         <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
         <translation>%1: texto de anotación más largo; %a: todos los textos de anotación; %q: use comillas</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="441"/>
+        <location filename="../pv/dialogs/settings.cpp" line="459"/>
         <source>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</source>
         <translation>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="442"/>
+        <location filename="../pv/dialogs/settings.cpp" line="460"/>
         <source>GNU GPL, version 3 or later</source>
         <translation>GNU GPL, versión 3 o posterior</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="453"/>
+        <location filename="../pv/dialogs/settings.cpp" line="471"/>
         <source>Versions, libraries and features:</source>
-        <translation>Versiones, librerías y características:</translation>
+        <translation>Versiones, bibliotecas y características:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="460"/>
+        <location filename="../pv/dialogs/settings.cpp" line="478"/>
         <source>Firmware search paths:</source>
         <translation>Rutas de búsqueda de firmware:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="467"/>
+        <location filename="../pv/dialogs/settings.cpp" line="485"/>
         <source>Protocol decoder search paths:</source>
         <translation>Ruta de búsqueda del decodificador de protocolo:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="474"/>
+        <location filename="../pv/dialogs/settings.cpp" line="488"/>
+        <source>&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)&lt;/td&gt;&lt;/tr&gt;</source>
+        <translation>&lt;tr&gt; &lt;td colspan = &quot;2&quot;&gt; (Nota: Establecer variable de entorno SIGROKDECODE_DIR para agregar un directorio personalizado) &lt;/td&gt; &lt;/tr&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="493"/>
         <source>Supported hardware drivers:</source>
-        <translation>Drivers de hardware soportados:</translation>
+        <translation>Controladores de hardware soportados:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="481"/>
+        <location filename="../pv/dialogs/settings.cpp" line="500"/>
         <source>Supported input formats:</source>
         <translation>Formatos de entrada soportados:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="488"/>
+        <location filename="../pv/dialogs/settings.cpp" line="507"/>
         <source>Supported output formats:</source>
         <translation>Formatos de salida soportados:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="496"/>
+        <location filename="../pv/dialogs/settings.cpp" line="515"/>
         <source>Supported protocol decoders:</source>
         <translation>Decodificadores de protocolo soportados:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="504"/>
+        <location filename="../pv/dialogs/settings.cpp" line="523"/>
         <source>Available Translations:</source>
-        <translation type="unfinished"></translation>
+        <translation>Traducciones disponibles:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="552"/>
+        <location filename="../pv/dialogs/settings.cpp" line="571"/>
         <source>Log level:</source>
-        <translation>Nivel de log:</translation>
+        <translation>Nivel de registro:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="557"/>
+        <location filename="../pv/dialogs/settings.cpp" line="576"/>
         <source> lines</source>
         <translation> líneas</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="566"/>
+        <location filename="../pv/dialogs/settings.cpp" line="585"/>
         <source>Length of background buffer:</source>
         <translation>Longitud del búfer de fondo:</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="572"/>
+        <location filename="../pv/dialogs/settings.cpp" line="591"/>
         <source>&amp;Save to File</source>
         <translation>&amp;Guardar en archivo</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="579"/>
+        <location filename="../pv/dialogs/settings.cpp" line="598"/>
         <source>&amp;Pop out</source>
-        <translation>&amp;Pop out</translation>
+        <translation>Des&amp;plegar</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="648"/>
+        <location filename="../pv/dialogs/settings.cpp" line="667"/>
         <source>You selected a dark theme.
 Should I set the user-adjustable colors to better suit your choice?
 
 Please keep in mind that PulseView may need a restart to display correctly.</source>
-        <translation>Seleccionaste el tema obscuro.\nDebería de establecer los colores ajustables por el usuario que mejor se ajustan a tu elección?\n\nPor favor ten en cuenta que Pulseview tal vez se tenga que reiniciar para mostrar correctamente.</translation>
+        <translation>Seleccionaste un tema oscuro.
+¿Debería de establecer los colores ajustables por el usuario a los que mejor se ajustan a tu elección?
+
+Por favor ten en cuenta que Pulseview tal vez se tenga que reiniciar para mostrarse correctamente.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="654"/>
+        <location filename="../pv/dialogs/settings.cpp" line="673"/>
         <source>You selected a bright theme.
 Should I set the user-adjustable colors to better suit your choice?
 
 Please keep in mind that PulseView may need a restart to display correctly.</source>
-        <translation>Seleccionaste el tema brillante.\nDebería de establecer los colores ajustables por el usuario que mejor se ajustan a tu elección?\n\nPor favor ten en cuenta que Pulseview tal vez se tenga que reiniciar para mostrar correctamente.</translation>
+        <translation>Seleccionaste un tema claro.
+¿Debería de establecer los colores ajustables por el usuario a los que mejor se ajustan a tu elección?
+
+Por favor ten en cuenta que Pulseview tal vez se tenga que reiniciar para mostrarse correctamente.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="807"/>
+        <location filename="../pv/dialogs/settings.cpp" line="844"/>
         <source>Save Log</source>
-        <translation>Guardar log</translation>
+        <translation>Guardar registro</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="807"/>
+        <location filename="../pv/dialogs/settings.cpp" line="844"/>
         <source>Log Files (*.txt *.log);;All Files (*)</source>
-        <translation>Archivos de log (*.txt *.log);;Todos los archivos (*)</translation>
+        <translation>Archivos de registro (*.txt *.log);;Todos los archivos (*)</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="819"/>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
         <source>Success</source>
         <translation>Éxito</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="819"/>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
         <source>Log saved to %1.</source>
-        <translation>Log guardado en %1.</translation>
+        <translation>Registro guardado en %1.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="829"/>
+        <location filename="../pv/dialogs/settings.cpp" line="866"/>
         <source>Error</source>
         <translation>Error</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="829"/>
+        <location filename="../pv/dialogs/settings.cpp" line="866"/>
         <source>File %1 could not be written to.</source>
         <translation>No se pudo escribir en el archivo%1.</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/settings.cpp" line="843"/>
+        <location filename="../pv/dialogs/settings.cpp" line="880"/>
         <source>%1 Log</source>
         <translation>%1 Log</translation>
     </message>
@@ -695,7 +772,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <translation>Cancelar</translation>
     </message>
     <message>
-        <location filename="../pv/dialogs/storeprogress.cpp" line="85"/>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="89"/>
         <source>Failed to save session.</source>
         <translation>Error al guardar sesión.</translation>
     </message>
@@ -705,8 +782,8 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/popups/channels.cpp" line="62"/>
         <location filename="../pv/popups/channels.cpp" line="63"/>
-        <location filename="../pv/popups/channels.cpp" line="273"/>
-        <location filename="../pv/popups/channels.cpp" line="300"/>
+        <location filename="../pv/popups/channels.cpp" line="278"/>
+        <location filename="../pv/popups/channels.cpp" line="305"/>
         <source>All</source>
         <translation>Todo</translation>
     </message>
@@ -720,7 +797,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <location filename="../pv/popups/channels.cpp" line="66"/>
         <location filename="../pv/popups/channels.cpp" line="67"/>
         <source>Analog</source>
-        <translation>Análogo</translation>
+        <translation>Análogico</translation>
     </message>
     <message>
         <location filename="../pv/popups/channels.cpp" line="68"/>
@@ -753,8 +830,8 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <translation>Habilitar: </translation>
     </message>
     <message>
-        <location filename="../pv/popups/channels.cpp" line="281"/>
-        <location filename="../pv/popups/channels.cpp" line="301"/>
+        <location filename="../pv/popups/channels.cpp" line="286"/>
+        <location filename="../pv/popups/channels.cpp" line="306"/>
         <source>None</source>
         <translation>Ninguna</translation>
     </message>
@@ -809,7 +886,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/subwindows/decoder_selector/model.cpp" line="40"/>
         <source>Decoder</source>
-        <translation>Decoder</translation>
+        <translation>Decodificador</translation>
     </message>
     <message>
         <location filename="../pv/subwindows/decoder_selector/model.cpp" line="41"/>
@@ -824,7 +901,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/subwindows/decoder_selector/model.cpp" line="49"/>
         <source>All Decoders</source>
-        <translation>Todos los decoders</translation>
+        <translation>Todos los decodificadores</translation>
     </message>
 </context>
 <context>
@@ -832,43 +909,43 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="49"/>
         <source>Select a decoder to see its description here.</source>
-        <translation>Seleccione un decoder para ver su descripción aquí.</translation>
+        <translation>Selecciona un decodificador para ver su descripción aquí.</translation>
     </message>
     <message>
-        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="247"/>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="248"/>
         <source>, %1</source>
         <translation>, %1</translation>
     </message>
     <message>
-        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="264"/>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="265"/>
         <source>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</source>
-        <translation>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</translation>
+        <translation>&lt;p align=&apos;right&apos;&gt;Etiquetas: %1&lt;/p&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="311"/>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="312"/>
         <source>Protocol decoder &lt;b&gt;%1&lt;/b&gt; requires input type &lt;b&gt;%2&lt;/b&gt; which several decoders provide.&lt;br&gt;Choose which one to use:&lt;br&gt;</source>
         <translation>Decodificador de protocolo &lt;b&gt;%1&lt;/b&gt; requiere tipo de entrada &lt;b&gt;%2&lt;/b&gt; que proporcionan varios decodificadores.&lt;br&gt;Elige cúal usar:&lt;br&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="319"/>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="320"/>
         <source>Choose Decoder</source>
-        <translation>Elige Decoder</translation>
+        <translation>Elige decodificador</translation>
     </message>
 </context>
 <context>
     <name>pv::toolbars::MainBar</name>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="121"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="124"/>
         <source>New &amp;View</source>
         <translation>Nueva &amp;Vista</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="127"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="130"/>
         <source>&amp;Open...</source>
         <translation>&amp;Abrir...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="134"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="141"/>
         <source>Restore Session Setu&amp;p...</source>
         <translation>Restaurar Configu&amp;ración de Sesión...</translation>
     </message>
@@ -877,156 +954,161 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <translation type="vanished">G&amp;uardar Como...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="138"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="145"/>
         <source>&amp;Save...</source>
-        <translation type="unfinished">&amp;Guardar...</translation>
+        <translation>&amp;Guardar...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="145"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="156"/>
         <source>Save &amp;As...</source>
-        <translation type="unfinished"></translation>
+        <translation>Guardar Como...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="151"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="162"/>
         <source>Save Selected &amp;Range As...</source>
         <translation>Guardar &amp;Rango Seleccionado Como...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="158"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="173"/>
         <source>Save Session Setu&amp;p...</source>
         <translation>Guardar Confi&amp;guración de Sesión...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="164"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="179"/>
         <source>&amp;Export</source>
-        <translation>&amp;Exporta</translation>
+        <translation>&amp;Exportar</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="170"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="185"/>
         <source>&amp;Import</source>
-        <translation>&amp;Importa</translation>
+        <translation>&amp;Importar</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="174"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="189"/>
         <source>&amp;Connect to Device...</source>
-        <translation>&amp;Conecta a Dispositivo...</translation>
+        <translation>&amp;Conectar a Dispositivo...</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="236"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="251"/>
         <source>Add protocol decoder</source>
         <translation>Agregar decodificador de protocolo</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="252"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="261"/>
+        <source>Add math signal</source>
+        <translation>Agregar señal matemática</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="277"/>
         <source>Configure Device</source>
-        <translation>Configura Dispositivo</translation>
+        <translation>Configurar Dispositivo</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="256"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="281"/>
         <source>Configure Channels</source>
-        <translation>Configura Canales</translation>
+        <translation>Configurar Canales</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="370"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="395"/>
         <source>Failed to get sample rate list:</source>
         <translation>Error al obtener la lista de frecuencia de muestreo:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="433"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="458"/>
         <source>Failed to get sample rate:</source>
-        <translation>Error al obtener la lista de frecuencia de muestreo:</translation>
+        <translation>Error al obtener la frecuencia de muestreo:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="474"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="499"/>
         <source>Failed to get sample limit list:</source>
         <translation>Error al obtener la lista de límites de muestra:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="564"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="589"/>
         <source>Failed to configure samplerate:</source>
         <translation>Error al configurar frecuencia de muestreo:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="591"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="616"/>
         <source>Failed to configure sample count:</source>
         <translation>Error al configurar cuenta de muestras:</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="629"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
         <source>Missing Cursors</source>
         <translation>Cursores Faltantes</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="629"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
         <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
-        <translation>Debe configurar los cursores antes de poder guardar los datos encerrados en un archivo de sesión (por ejemplo, usando el botón Mostrar Cursores).</translation>
+        <translation>Debes configurar los cursores antes de poder guardar los datos encerrados en un archivo de sesión (por ejemplo, usando el botón Mostrar Cursores).</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="647"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
         <source>Invalid Range</source>
         <translation>Rango Inválido</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="647"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
         <source>The cursors don&apos;t define a valid range of samples.</source>
         <translation>Los cursores no definen un rango válido de muestras.</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="659"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="684"/>
         <source>%1 files </source>
         <translation>%1 archivos </translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="667"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="717"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="692"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="742"/>
         <source>All Files</source>
         <translation>Todos los archivos</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="671"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="848"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
         <source>Save File</source>
         <translation>Guardar Archivo</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="683"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="708"/>
         <source>Export %1</source>
-        <translation>Exporta %1</translation>
+        <translation>Exportar %1</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="714"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="739"/>
         <source>%1 files</source>
         <translation>%1 archivos</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="725"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="750"/>
         <source>Import File</source>
-        <translation>Importa Archivo</translation>
+        <translation>Importar Archivo</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="734"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="759"/>
         <source>Import %1</source>
-        <translation>Importa %1</translation>
+        <translation>Importar %1</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="807"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="865"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
         <source>Open File</source>
         <translation>Abrir Archivo</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="807"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
         <source>sigrok Sessions (*.sr);;All Files (*)</source>
         <translation>Sesiones sigrok (*sr);;Todos los archivos (*)</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="848"/>
-        <location filename="../pv/toolbars/mainbar.cpp" line="865"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
         <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
         <translation>Configurciones de Sesión de PulseView (*.pvs);;Todos los Archivos (*)</translation>
     </message>
     <message>
-        <location filename="../pv/toolbars/mainbar.cpp" line="926"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="958"/>
         <source>Total sampling time: %1</source>
         <translation>Tiempo de muestreo total: %1</translation>
     </message>
@@ -1046,7 +1128,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
         <source>Hexdump</source>
-        <translation>Hexdump</translation>
+        <translation>Volcado hexadecimal</translation>
     </message>
     <message>
         <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
@@ -1054,32 +1136,32 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <translation>&amp;Guardar...</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="258"/>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="298"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
         <source>Save Binary Data</source>
         <translation>Guardar Datos Binarios</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="258"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
         <source>Binary Data Files (*.bin);;All Files (*)</source>
-        <translation>Archivos de Datos Binarios (*.txt);;Todos los archivos (*)</translation>
+        <translation>Archivos de Datos Binarios (*.bin);;Todos los archivos (*)</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="277"/>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="329"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
         <source>Error</source>
         <translation>Error</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="277"/>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="329"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
         <source>File %1 could not be written to.</source>
         <translation>No se pudo escribir en el archivo%1.</translation>
     </message>
     <message>
-        <location filename="../pv/views/decoder_binary/view.cpp" line="298"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
         <source>Hex Dumps (*.txt);;All Files (*)</source>
-        <translation>Hex Dumps (*.txt);;Todos los archivos (*)</translation>
+        <translation>Volcados hexadecimales (*.txt);;Todos los archivos (*)</translation>
     </message>
 </context>
 <context>
@@ -1087,42 +1169,42 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
         <source>Sample</source>
-        <translation type="unfinished"></translation>
+        <translation>Muestra</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
         <source>Time</source>
-        <translation type="unfinished">Tiempo</translation>
+        <translation>Tiempo</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
         <source>Decoder</source>
-        <translation type="unfinished">Decoder</translation>
+        <translation>Decodificador</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
         <source>Ann Row</source>
-        <translation type="unfinished"></translation>
+        <translation>Fila de anotación</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
         <source>Ann Class</source>
-        <translation type="unfinished"></translation>
+        <translation>Clase de anotación</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
         <source>Value</source>
-        <translation type="unfinished"></translation>
+        <translation>Valor</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
         <source>s</source>
-        <translation type="unfinished"></translation>
+        <translation>s</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
         <source>sa</source>
-        <translation type="unfinished"></translation>
+        <translation>sa</translation>
     </message>
 </context>
 <context>
@@ -1130,120 +1212,120 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
         <source>Decoder:</source>
-        <translation type="unfinished">Decodificador:</translation>
+        <translation>Decodificador:</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
         <source>Hide Hidden Rows/Classes</source>
-        <translation type="unfinished"></translation>
+        <translation>Ocultar Filas/Columnas ocultas</translation>
     </message>
     <message>
         <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
         <source>&amp;Save...</source>
-        <translation type="unfinished">&amp;Guardar...</translation>
+        <translation>&amp;Guardar...</translation>
     </message>
     <message>
-        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
         <source>Save Annotations as CSV</source>
-        <translation type="unfinished"></translation>
+        <translation>Guardar anotaciones como CSV</translation>
     </message>
     <message>
-        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
         <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
-        <translation type="unfinished"></translation>
+        <translation>Archivos CSV (*.csv);;Archivos de texto (*.txt);;Todos los archivos (*)</translation>
     </message>
     <message>
-        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
         <source>Error</source>
-        <translation type="unfinished">Error</translation>
+        <translation>Error</translation>
     </message>
     <message>
-        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
         <source>File %1 could not be written to.</source>
-        <translation type="unfinished">No se pudo escribir en el archivo%1.</translation>
+        <translation>No se pudo escribir en el archivo%1.</translation>
     </message>
 </context>
 <context>
     <name>pv::views::trace::AnalogSignal</name>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="994"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="802"/>
         <source>Number of pos vertical divs</source>
-        <translation>Número de divisiones verticales pos</translation>
+        <translation>Número de divisiones verticales positivas</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1001"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="810"/>
         <source>Number of neg vertical divs</source>
-        <translation>Número de divisiones verticales neg</translation>
+        <translation>Número de divisiones verticales negativas</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1006"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="815"/>
         <source> pixels</source>
         <translation> píxeles</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1010"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="819"/>
         <source>Div height</source>
-        <translation>Altura de div</translation>
+        <translation>Altura de división</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1027"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="837"/>
         <source>V/div</source>
-        <translation>V/div</translation>
+        <translation>V/división</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1031"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="841"/>
         <source>Vertical resolution</source>
         <translation>Resolución vertical</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1040"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="850"/>
         <source>Autoranging</source>
         <translation>Autorango</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1045"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="855"/>
         <source>none</source>
         <translation>ninguna</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1047"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="857"/>
         <source>to logic via threshold</source>
         <translation>a nivel lógico a partir de umbral</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1049"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="859"/>
         <source>to logic via schmitt-trigger</source>
-        <translation>a nivel lógico a partir de schmitt trigger</translation>
+        <translation>a nivel lógico a partir de schmitt-trigger</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1055"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="865"/>
         <source>Conversion</source>
         <translation>Conversión</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1064"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="874"/>
         <source>Conversion threshold(s)</source>
         <translation>Umbral(es) de conversión</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1074"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="884"/>
         <source>analog</source>
-        <translation>análogo</translation>
+        <translation>señal análogica</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1075"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="885"/>
         <source>converted</source>
-        <translation>convertida</translation>
+        <translation>señal convertida</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1076"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="886"/>
         <source>analog+converted</source>
-        <translation>Analógico+convertido</translation>
+        <translation>señales analógica + convertida</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/analogsignal.cpp" line="1081"/>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="891"/>
         <source>Show traces for</source>
-        <translation>Mostrar trazos para</translation>
+        <translation>Mostrar trazos de</translation>
     </message>
 </context>
 <context>
@@ -1259,154 +1341,154 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/views/trace/cursorpair.cpp" line="128"/>
         <source>Display interval</source>
-        <translation>Muestra intervalo</translation>
+        <translation>Mostrar intervalo</translation>
     </message>
     <message>
         <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
         <source>Display frequency</source>
-        <translation>Muestra frecuencia</translation>
+        <translation>Mostrar frecuencia</translation>
     </message>
     <message>
         <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
         <source>Display samples</source>
-        <translation>Muestra samples</translation>
+        <translation>Mostrar muestras</translation>
     </message>
 </context>
 <context>
     <name>pv::views::trace::DecodeTrace</name>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="449"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="456"/>
         <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
-        <translation>&lt;p&gt;&lt;i&gt;No hay decodificadores en el stack.&lt;/i&gt;&lt;/p&gt;</translation>
+        <translation>&lt;p&gt;&lt;i&gt;No hay decodificadores en la pila.&lt;/i&gt;&lt;/p&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="460"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="467"/>
         <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
         <translation>&lt;i&gt;* Canales requeridos&lt;/i&gt;</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="464"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="471"/>
         <source>Stack Decoder</source>
-        <translation>Decodificadores</translation>
+        <translation>Apilar Decodificador</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="465"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="472"/>
         <source>Stack a higher-level decoder on top of this one</source>
         <translation>Apilar un decodificador de nivel superior encima de este</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="479"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="486"/>
         <source>Delete</source>
         <translation>Eliminar</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="521"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="528"/>
         <source>Resume decoding</source>
         <translation>Reanudar decodificación</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="528"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="535"/>
         <source>Pause decoding</source>
         <translation>Pausar decodificación</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="536"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="543"/>
         <source>Copy annotation text to clipboard</source>
-        <translation>Copiar texto de anotación al clipboard</translation>
+        <translation>Copiar texto de anotación al portapapeles</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="545"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="552"/>
         <source>Export all annotations</source>
         <translation>Exportar todas las anotaciones</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="552"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="559"/>
         <source>Export all annotations for this row</source>
         <translation>Exportar todas las anotaciones para esta fila</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="561"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="568"/>
         <source>Export all annotations, starting here</source>
         <translation>Exportar todas las anotaciones, comenzando aquí</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="568"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="575"/>
         <source>Export annotations for this row, starting here</source>
         <translation>Exportar todas las anotaciones para esta fila, comenzando aquí</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="577"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="584"/>
         <source>Export all annotations within cursor range</source>
         <translation>Exportar todas las anotaciones dentro del rango del cursor</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="584"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="591"/>
         <source>Export annotations for this row within cursor range</source>
         <translation>Exportar todas las anotaciones para esta fila dentro del rango del cursor</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1076"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1079"/>
         <source>%1:
 %2</source>
         <translation>%1\n%2</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1120"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1123"/>
         <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
         <translation>&lt;b&gt;%1&lt;/b&gt; (%2) %3</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1190"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
         <source>Export annotations</source>
         <translation>Exportar anotaciones</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1190"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
         <source>Text Files (*.txt);;All Files (*)</source>
-        <translation>Archivos de texto (*.txt *.log);;Todos los archivos (*)</translation>
+        <translation>Archivos de texto (*.txt);;Todos los archivos (*)</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1255"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
         <source>Error</source>
         <translation>Error</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1255"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
         <source>File %1 could not be written to.</source>
         <translation>No se pudo escribir en el archivo%1.</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1308"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1324"/>
         <source>Show this row</source>
-        <translation>Muestra esta fila</translation>
+        <translation>Mostrar esta fila</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1319"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1335"/>
         <source>Show All</source>
-        <translation>Muestra todo</translation>
+        <translation>Mostrar todo</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/decodetrace.cpp" line="1327"/>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1343"/>
         <source>Hide All</source>
-        <translation>Oculta todo</translation>
+        <translation>Ocultar todo</translation>
     </message>
 </context>
 <context>
     <name>pv::views::trace::Flag</name>
     <message>
-        <location filename="../pv/views/trace/flag.cpp" line="132"/>
+        <location filename="../pv/views/trace/flag.cpp" line="144"/>
         <source>Text</source>
         <translation>Texto</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/flag.cpp" line="141"/>
+        <location filename="../pv/views/trace/flag.cpp" line="153"/>
         <source>Delete</source>
         <translation>Eliminar</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/flag.cpp" line="146"/>
+        <location filename="../pv/views/trace/flag.cpp" line="158"/>
         <source>Disable snapping</source>
-        <translation>Deshabilita snapping</translation>
+        <translation>Deshabilitar snapping</translation>
     </message>
 </context>
 <context>
@@ -1420,57 +1502,591 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
 <context>
     <name>pv::views::trace::LogicSignal</name>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="451"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="423"/>
         <source>No trigger</source>
         <translation>Sin trigger</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="456"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="428"/>
         <source>Trigger on rising edge</source>
         <translation>Trigger en flanco de subida</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="461"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="433"/>
         <source>Trigger on high level</source>
         <translation>Trigger en nivel alto</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="466"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="438"/>
         <source>Trigger on falling edge</source>
         <translation>Trigger en flanco de bajada</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="471"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="443"/>
         <source>Trigger on low level</source>
         <translation>Trigger en nivel bajo</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="476"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="448"/>
         <source>Trigger on rising or falling edge</source>
         <translation>Trigger en flanco de subida o bajada</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="535"/>
         <source> pixels</source>
         <translation> pixeles</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="567"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="539"/>
         <source>Trace height</source>
         <translation>Altura del trazo</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/logicsignal.cpp" line="591"/>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
         <source>Trigger</source>
         <translation>Trigger</translation>
     </message>
 </context>
+<context>
+    <name>pv::views::trace::MathEditDialog</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="88"/>
+        <source>Math Expression Editor</source>
+        <translation>Editor de expresiones matemáticas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="93"/>
+        <source>Inputs:</source>
+        <translation>Entradas:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="99"/>
+        <source>Variables:</source>
+        <translation>Variables:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="101"/>
+        <source>Basic operators:</source>
+        <translation>Operadores básicos:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="108"/>
+        <source>Assignments:</source>
+        <translation>Asignaciones:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="118"/>
+        <source>General purpose functions:</source>
+        <translation>Funciones de propósito general:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="119"/>
+        <source>abs(x)         Absolute value of x</source>
+        <translation>abs(x)            Valor absoluto de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="120"/>
+        <source>avg(x, y, ...) Average of all input values</source>
+        <translation>avg(x, y, ...)    Promedio de todos los valores de entrada</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="121"/>
+        <source>ceil(x)                Smallest integer that is greater than or equal to x</source>
+        <translation>ceil(x)           Entero más pequeño que es mayor o igual a x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="122"/>
+        <source>clamp(lb, x, ub)       Clamp x in range between lb and ub, where lb &lt; ub</source>
+        <translation>clamp(lb, x, ub)  Fija x en el rango entre lb y ub, donde lb &lt; ub</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="123"/>
+        <source>equal(x, y)    Equality test between x and y using normalised epsilon</source>
+        <translation>equal(x, y)       Prueba de igualdad entre x e y usando epsilon normalizado</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="124"/>
+        <source>erf(x)         Error function of x</source>
+        <translation>erf(x)            Función error de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="125"/>
+        <source>erfc(x)                Complimentary error function of x</source>
+        <translation>erfc(x)           Función de error complementario de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="126"/>
+        <source>exp(x)         e to the power of x</source>
+        <translation>exp(x)            e a la potencia de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="127"/>
+        <source>expm1(x)       e to the power of x minus 1, where x is very small.</source>
+        <translation>expm1(x)  e a la potencia de x menos 1, donde z es muy pequeño.</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="128"/>
+        <source>floor(x)               Largest integer that is less than or equal to x</source>
+        <translation>floor(x)          Entero más grande que is menor o igual a x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="129"/>
+        <source>frac(x)                Fractional portion of x</source>
+        <translation>frac(x)           Porción fraccional de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="130"/>
+        <source>hypot(x)               Hypotenuse of x and y (i.e. sqrt(x*x + y*y))</source>
+        <translation>hypot(x)          Hipotenusa de x e y (es decir sqrt(x*x + y*y))</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="131"/>
+        <source>iclamp(lb, x, ub)      Inverse-clamp x outside of the range lb and ub, where lb &lt; ub.
+               If x is within the range it will snap to the closest bound</source>
+        <translation>iclamp(lb, x, ub) Fijación inversa de x fuera del rango lb y ub, donde lb &lt; ub.
+               Si x está en el rango se fijará al límite más cercano</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="132"/>
+        <source>inrange(lb, x, ub)     In-range returns true when x is within the range lb and ub, where lb &lt; ub.</source>
+        <translation>inrange(lb, x, ub)        En-rango regresa verdadero cuando x está en el rango lb y ub, donde lb &lt; ub.</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="133"/>
+        <source>log(x)         Natural logarithm of x</source>
+        <translation>log(x)            Logaritmo natural de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="134"/>
+        <source>log10(x)               Base 10 logarithm of x</source>
+        <translation>log10(x)          Logaritmo base 10 de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="138"/>
+        <source>log1p(x)               Natural logarithm of 1 + x, where x is very small</source>
+        <translation>log1p(x)          Logaritmo natural de 1 + x, donde x es muy pequeño</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="139"/>
+        <source>log2(x)                Base 2 logarithm of x</source>
+        <translation>log2(x)           Logaritmo base 2 de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="140"/>
+        <source>logn(x)                Base N logarithm of x, where n is a positive integer</source>
+        <translation>logn(x)           Logaritmo base N de x, donde n es un entero positivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="141"/>
+        <source>max(x, y, ...) Largest value of all the inputs</source>
+        <translation>max(x, y, ...)    Valor más grande de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="142"/>
+        <source>min(x, y, ...) Smallest value of all the inputs</source>
+        <translation>min(x, y, ...)    Valor más pequeño de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="143"/>
+        <source>mul(x, y, ...) Product of all the inputs</source>
+        <translation>mul(x, y, ...)    Producto de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="144"/>
+        <source>ncdf(x)                Normal cumulative distribution function</source>
+        <translation>ncdf(x)           Función de distribución acumulativa normal</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="145"/>
+        <source>nequal(x, y)   Not-equal test between x and y using normalised epsilon</source>
+        <translation>nequal(x, y)      Prueba de no igualdad entre x e y usando epsilon normalizado</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="146"/>
+        <source>pow(x, y)      x to the power of y</source>
+        <translation>pow(x, y) x a la potencia de y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="147"/>
+        <source>root(x, n)     Nth-Root of x, where n is a positive integer</source>
+        <translation>root(x, n)        Enésima raíz de x, donde n es un entero positivo</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="148"/>
+        <source>round(x)               Round x to the nearest integer</source>
+        <translation>round(x)          Redondear x al entero más cercano</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="149"/>
+        <source>roundn(x, n)   Round x to n decimal places, where n &gt; 0 and is an integer</source>
+        <translation>roundn(x, n)      Redondear x a n lugares decimales, donde n &gt; 0 y es un entero</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="150"/>
+        <source>sgn(x)         Sign of x; -1 if x &lt; 0, +1 if x &gt; 0, else zero</source>
+        <translation>sgn(x)            Sigon de x; -1 si x &lt; 0, +1 si x &gt; 0, otro cero</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="151"/>
+        <source>sqrt(x)                Square root of x, where x &gt;= 0</source>
+        <translation>sqrt(x)           Raíz cuadrada de x, donde x &gt;= 0</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="152"/>
+        <source>sum(x, y, ..,) Sum of all the inputs</source>
+        <translation>sum(x, y, ..,)    Suma de todas las entradas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="153"/>
+        <source>swap(x, y)     Swap the values of the variables x and y and return the current value of y</source>
+        <translation>swap(x, y)        Intercambia los valores de las variables x e y y regresa el valor actual de y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="154"/>
+        <source>trunc(x)               Integer portion of x</source>
+        <translation>trunc(x)          Porción entera de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="158"/>
+        <source>Trigonometry functions:</source>
+        <translation>Funciones trigonométricas:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="159"/>
+        <source>acos(x)                Arc cosine of x expressed in radians. Interval [-1,+1]</source>
+        <translation>acos(x)           Arco coseno de x expresado en radianes. Intervalo [-1,+1]</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="160"/>
+        <source>acosh(x)               Inverse hyperbolic cosine of x expressed in radians</source>
+        <translation>acosh(x)          Coseno hiperbólico inverso de x expresado en radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="161"/>
+        <source>asin(x)                Arc sine of x expressed in radians. Interval [-1,+1]</source>
+        <translation>asin(x)           Arco seno de x expresado en radianes. Intervalo [-1,+1]</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="162"/>
+        <source>asinh(x)               Inverse hyperbolic sine of x expressed in radians</source>
+        <translation>asinh(x)          Seno hiperbólico inverso de x expresado en radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="163"/>
+        <source>atan(x)                Arc tangent of x expressed in radians. Interval [-1,+1]</source>
+        <translation>atan(x)           Arco tangente de x expresado en radianes. Intervalo [-1,+1]</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="164"/>
+        <source>atan2(x, y)    Arc tangent of (x / y) expressed in radians. [-pi,+pi]  </source>
+        <translation>atan2(x, y)       Arco tangente de (x / y) expresado en radianes. [-pi,+pi]  </translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="165"/>
+        <source>atanh(x)               Inverse hyperbolic tangent of x expressed in radians</source>
+        <translation>atanh(x)          Tangente hiperbólica inversa de x expresada en radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="166"/>
+        <source>cos(x)         Cosine of x</source>
+        <translation>cos(x)            Coseno de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="167"/>
+        <source>cosh(x)                Hyperbolic cosine of x</source>
+        <translation>cosh(x)           Coseno hiperbólico de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="168"/>
+        <source>cot(x)         Cotangent of x</source>
+        <translation>cot(x)            Cotangente de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="169"/>
+        <source>csc(x)         Cosectant of x</source>
+        <translation>csc(x)            Cosecante de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="170"/>
+        <source>sec(x)         Secant of x</source>
+        <translation>sec(x)            Secante de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="171"/>
+        <source>sin(x)         Sine of x</source>
+        <translation>sin(x)            Seno de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="172"/>
+        <source>sinc(x)                Sine cardinal of x</source>
+        <translation>sinc(x)           Seno cardinal de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="173"/>
+        <source>sinh(x)                Hyperbolic sine of x</source>
+        <translation>sinh(x)           Seno hiperbólico de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="174"/>
+        <source>tan(x)         Tangent of x</source>
+        <translation>tan(x)            Tangente de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="175"/>
+        <source>tanh(x)                Hyperbolic tangent of x</source>
+        <translation>tanh(x)           Tangente hiperbólica de x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="176"/>
+        <source>deg2rad(x)     Convert x from degrees to radians</source>
+        <translation>deg2rad(x)        Convierte x de grados sexagesimales a radianes</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="177"/>
+        <source>deg2grad(x)    Convert x from degrees to gradians</source>
+        <translation>deg2grad(x)       Convierte x de grados sexagesimales a grados centesimales</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="178"/>
+        <source>rad2deg(x)     Convert x from radians to degrees</source>
+        <translation>rad2deg(x)        Convierte x de radianes a grados sexagesimales</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="179"/>
+        <source>grad2deg(x)    Convert x from gradians to degrees</source>
+        <translation>grad2deg(x)       Convierte x de grados centesimales a grados sexagesimales</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="183"/>
+        <source>Logic operators:</source>
+        <translation>Operadores lógicos:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="200"/>
+        <source>Comparisons:</source>
+        <translation>Comparaciones:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="201"/>
+        <source>x = y or x == y        True only if x is strictly equal to y</source>
+        <translation>x = y o x == y    Verdadero solo si x es estrictemente igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="202"/>
+        <source>x &lt;&gt; y or x != y True only if x does not equal y</source>
+        <translation>x &lt;&gt; y o x != y     Verdadero solo si x no es igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="203"/>
+        <source>x &lt; y               True only if x is less than y</source>
+        <translation>x &lt; y          Verdadero solo si x es menor que y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="204"/>
+        <source>x &lt;= y              True only if x is less than or equal to y</source>
+        <translation>x &lt;= y         Verdadero solo si x es menor o igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="205"/>
+        <source>x &gt; y               True only if x is greater than y</source>
+        <translation>x &gt; y          Verdadero solo si x es mayor que y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="206"/>
+        <source>x &gt;= y              True only if x is greater than or equal to y</source>
+        <translation>x &gt;= y         Verdadero solo si x es mayor o igual a y</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="207"/>
+        <source>Flow control:</source>
+        <translation>Fujo de cotrol:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="208"/>
+        <source>{ ... }                Beginning and end of instruction block</source>
+        <translation>{ ... }           Inicio y fin de un bloque de instrucciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="209"/>
+        <source>if (x, y, z)   If x is true then return y else return z
+if (x) y;                      variant without implied else
+if (x) { y };          variant with an instruction block
+if (x) y; else z;              variant with explicit else
+if (x) { y } else { z };       variant with instruction blocks</source>
+        <translation>if (x, y, z)      Si x es verdadero entonces regresa y si no regresa z
+if (x) y;                      variante sin si no implicado
+if (x) { y };          variante con un bloque de instrucción
+if (x) y; else z;              variante con si no explícito
+if (x) { y } else { z };       variante con bloques de instrucciones</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="210"/>
+        <source>x ? y : z      Ternary operator, equivalent to &apos;if (x, y, z)&apos;</source>
+        <translation>x ? y : z Operador ternario, equivalente a &apos;if (x, y, z)&apos;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="211"/>
+        <source>switch {               The first true case condition that is encountered will
+ case x &gt; 1: a;     determine the result of the switch. If none of the case
+ case x &lt; 1: b;     conditions hold true, the default action is used
+ default:     c;       to determine the return value
+}</source>
+        <translation>switch {          La primera condición de caso verdadera que es encontrada
+ case x &gt; 1: a;     determina el resultado del interruptor. Si ninguno de las condiciones de
+ case x &lt; 1: b;     los casos se mantienen verdaderas, la acción predeterminada se usa
+ default:     c;       para determinar el valor de retorno
+}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="215"/>
+        <source>while (conditon) {     Evaluates expression repeatedly as long as condition is true,
+ expression;   returning the last value of expression
+}</source>
+        <translation>while (conditon) {        Evalúa la expresión repetidamente siempre que la condición sea verdadera,
+expresión; devuelve el último valor de la expresión
+}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="216"/>
+        <source>repeat         Evalues expression repeatedly as long as condition is false,
+ expression;   returning the last value of expression
+until (condition)
+</source>
+        <translation>repeat            Evalúa la expresión repetidamente siempre que la condición sea falsa
+expresión; devuelve el último valor de la expresión
+hasta que (condición)
+</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="217"/>
+        <source>for (var x := 0; condition; x += 1) {  Repeatedly evaluates expression while the condition is true,
+ expression                    while evaluating the &apos;increment&apos; expression on each loop
+}</source>
+        <translation>for (var x := 0; condición; x += 1) {    Evalua repetidamente la expresión mientras la condición sea verdadera,
+ expresión                    mientras se evalúa la expresión de &apos;incremento&apos; en cada bucle
+}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="218"/>
+        <source>break          Terminates the execution of the nearest enclosed loop, returning NaN</source>
+        <translation>break             Finaliza la ejecución del bucle cerrado más cercano, devolviendo NaN</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="219"/>
+        <source>break[x]               Terminates the execution of the nearest enclosed loop, returning x</source>
+        <translation>break[x]          Finaliza la ejecución del bucle cerrado más cercano, devolviendo x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="220"/>
+        <source>continue               Interrupts loop execution and resumes with the next loop iteration</source>
+        <translation>continue          Interrumpe la ejecución del bucle y se reanuda con la siguiente iteración de bucle</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="221"/>
+        <source>return[x]              Returns immediately from within the current expression, returning x</source>
+        <translation>return[x]         Devuelve inmediatamente desde la expresión actual, regresando x</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="222"/>
+        <source>~(expr; expr; ...)     Evaluates each sub-expression and returns the value of the last one
+~{expr; expr; ...}</source>
+        <translation>~(expr; expr; ...)        Evalúa cada subexpresión y devuelve el valor de la última
+~{expr; expr; ...}</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="234"/>
+        <source>Copy to expression</source>
+        <translation>Copiar a expresión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="247"/>
+        <source>Basics</source>
+        <translation>Básicas</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="248"/>
+        <source>Functions 1</source>
+        <translation>Funciones 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="249"/>
+        <source>Functions 2</source>
+        <translation>Funciones 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="250"/>
+        <source>Trigonometry</source>
+        <translation>Trigonometría</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="251"/>
+        <source>Logic</source>
+        <translation>Lógica</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="252"/>
+        <source>Flow Control 1</source>
+        <translation>Flujo de control 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="253"/>
+        <source>Flow Control 2</source>
+        <translation>Flujo de control 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="254"/>
+        <source>Examples</source>
+        <translation>Ejemplos</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathSignal</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="317"/>
+        <source>Expression</source>
+        <translation>Espresión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="321"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="331"/>
+        <source>same as session</source>
+        <translation>igual que la sesión</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="322"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="332"/>
+        <source>100</source>
+        <translation>100</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="323"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="333"/>
+        <source>10000</source>
+        <translation>10000</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="324"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="334"/>
+        <source>1000000</source>
+        <translation>1000000</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="327"/>
+        <source>Number of Samples</source>
+        <translation>Número de muestras</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="335"/>
+        <source>Sample rate</source>
+        <translation>Tasa de muestreo</translation>
+    </message>
+</context>
 <context>
     <name>pv::views::trace::Ruler</name>
     <message>
         <location filename="../pv/views/trace/ruler.cpp" line="153"/>
         <source>Create marker here</source>
-        <translation>Crear marcador aqui</translation>
+        <translation>Crear marcador aquí</translation>
     </message>
     <message>
         <location filename="../pv/views/trace/ruler.cpp" line="157"/>
@@ -1485,12 +2101,12 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/views/trace/ruler.cpp" line="175"/>
         <source>Disable mouse hover marker</source>
-        <translation>Deshabilitar marcador de desplazamiento del mouse</translation>
+        <translation>Deshabilitar marcador de desplazamiento del ratón</translation>
     </message>
     <message>
         <location filename="../pv/views/trace/ruler.cpp" line="175"/>
         <source>Enable mouse hover marker</source>
-        <translation>Habilitar marcador de desplazamiento del mouse</translation>
+        <translation>Habilitar marcador de desplazamiento del ratón</translation>
     </message>
 </context>
 <context>
@@ -1501,7 +2117,12 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <translation>Nombre</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/signal.cpp" line="164"/>
+        <location filename="../pv/views/trace/signal.cpp" line="167"/>
+        <source>Remove</source>
+        <translation>Eliminar</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="169"/>
         <source>Disable</source>
         <translation>Deshabilitar</translation>
     </message>
@@ -1541,13 +2162,13 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/views/trace/standardbar.cpp" line="95"/>
         <source>Display a single segment</source>
-        <translation>Mostrar solo un segmento</translation>
+        <translation>Mostrar un solo segmento</translation>
     </message>
 </context>
 <context>
     <name>pv::views::trace::TimeMarker</name>
     <message>
-        <location filename="../pv/views/trace/timemarker.cpp" line="191"/>
+        <location filename="../pv/views/trace/timemarker.cpp" line="198"/>
         <source>Time</source>
         <translation>Tiempo</translation>
     </message>
@@ -1555,17 +2176,17 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
 <context>
     <name>pv::views::trace::Trace</name>
     <message>
-        <location filename="../pv/views/trace/trace.cpp" line="206"/>
+        <location filename="../pv/views/trace/trace.cpp" line="229"/>
         <source>Create marker here</source>
-        <translation>Crear marcador aqui</translation>
+        <translation>Crear marcador aquí</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/trace.cpp" line="315"/>
+        <location filename="../pv/views/trace/trace.cpp" line="338"/>
         <source>Color</source>
         <translation>Color</translation>
     </message>
     <message>
-        <location filename="../pv/views/trace/trace.cpp" line="380"/>
+        <location filename="../pv/views/trace/trace.cpp" line="403"/>
         <source>Name</source>
         <translation>Nombre</translation>
     </message>
@@ -1578,12 +2199,20 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
         <translation>Desagrupar</translation>
     </message>
 </context>
+<context>
+    <name>pv::views::trace::View</name>
+    <message>
+        <location filename="../pv/views/trace/view.cpp" line="1610"/>
+        <source>Create marker here</source>
+        <translation>Crear marcador aquí</translation>
+    </message>
+</context>
 <context>
     <name>pv::widgets::DecoderGroupBox</name>
     <message>
         <location filename="../pv/widgets/decodergroupbox.cpp" line="48"/>
         <source>Show/hide this decoder trace</source>
-        <translation>Mostrar / ocultar este trazo de decodificador</translation>
+        <translation>Mostrar/ocultar este trazo de decodificador</translation>
     </message>
     <message>
         <location filename="../pv/widgets/decodergroupbox.cpp" line="58"/>
@@ -1594,8 +2223,8 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
 <context>
     <name>pv::widgets::DeviceToolButton</name>
     <message>
-        <location filename="../pv/widgets/devicetoolbutton.cpp" line="75"/>
-        <location filename="../pv/widgets/devicetoolbutton.cpp" line="82"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="80"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="87"/>
         <source>&lt;No Device&gt;</source>
         <translation>&lt;Sin dispositivo&gt;</translation>
     </message>
@@ -1605,7 +2234,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/widgets/exportmenu.cpp" line="71"/>
         <source>Export %1...</source>
-        <translation>Exporta %1...</translation>
+        <translation>Exportar %1...</translation>
     </message>
 </context>
 <context>
@@ -1613,7 +2242,7 @@ Please keep in mind that PulseView may need a restart to display correctly.</sou
     <message>
         <location filename="../pv/widgets/importmenu.cpp" line="68"/>
         <source>Import %1...</source>
-        <translation>Importa %1...</translation>
+        <translation>Importar %1...</translation>
     </message>
 </context>
 </TS>
diff --git a/l10n/ja_jp.ts b/l10n/ja_jp.ts
new file mode 100644 (file)
index 0000000..164ea30
--- /dev/null
@@ -0,0 +1,2201 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ja_JP">
+<context>
+    <name>Application</name>
+    <message>
+        <location filename="../pv/application.cpp" line="131"/>
+        <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
+        <translation>アプリケーションの一部では、以前の言語が引き続き使用される場合があります。 影響を受けたウィンドウを再度開くか、アプリケーションを再起動すると、これが改善されます。</translation>
+    </message>
+</context>
+<context>
+    <name>QApplication</name>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="274"/>
+        <source>Error when scanning device driver &apos;%1&apos;: %2</source>
+        <translation>デバイスドライバ&apos;%1&apos;をスキャンするときにエラーが発生しました:%2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="70"/>
+        <source>Querying config key %1 is not allowed</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="79"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="93"/>
+        <source>Unknown type supplied when attempting to query %1</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>QHexView</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="339"/>
+        <source>No data available</source>
+        <translation>データなし</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../main.cpp" line="116"/>
+        <source>Stack trace of previous crash:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="130"/>
+        <source>Don&apos;t show this message again</source>
+        <translation>このメッセージを二度と表示しない</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="133"/>
+        <source>When %1 last crashed, it created a stack trace.
+A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
+        <translation>%1が最後にクラッシュしたとき、スタックトレースが作成されました。
+人間が読める形式がディスクに保存され、ログに書き込まれました。 設定ダイアログからアクセスできます。</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="65"/>
+        <source>Cancel</source>
+        <translation>キャンセル</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="96"/>
+        <source>Scanning for devices that driver %1 can access...</source>
+        <translation>ドライバー %1 がアクセスできるデバイスをスキャンしています...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::MainWindow</name>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="70"/>
+        <source>PulseView</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="279"/>
+        <source>Decoder Selector</source>
+        <translation>デコーダー選択</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="332"/>
+        <source>Session %1</source>
+        <translation>セッション %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="514"/>
+        <source>Create New Session</source>
+        <translation>新規セッションを作成</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="520"/>
+        <source>Start/Stop Acquisition</source>
+        <translation>取得の開始/停止</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="528"/>
+        <source>Settings</source>
+        <translation>設定</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="580"/>
+        <source>Reload</source>
+        <translation>再読込</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="580"/>
+        <location filename="../pv/mainwindow.cpp" line="583"/>
+        <source>Run</source>
+        <translation>実行</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <source>Stop</source>
+        <translation>停止</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="635"/>
+        <location filename="../pv/mainwindow.cpp" line="860"/>
+        <location filename="../pv/mainwindow.cpp" line="886"/>
+        <source>Confirmation</source>
+        <translation>確認</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="636"/>
+        <source>There is unsaved data. Close anyway?</source>
+        <translation>保存されていないデータがあります。 閉じますか?</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="861"/>
+        <location filename="../pv/mainwindow.cpp" line="887"/>
+        <source>This session contains unsaved data. Close it anyway?</source>
+        <translation>このセッションには、保存されていないデータが含まれています。 閉じますか?</translation>
+    </message>
+</context>
+<context>
+    <name>pv::Session</name>
+    <message>
+        <location filename="../pv/session.cpp" line="396"/>
+        <source>Can&apos;t restore generated signal of unknown type %1 (%2)</source>
+        <translation>不明なタイプ%1 (%2)の生成された信号を復元できません</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="559"/>
+        <source>Failed to select device</source>
+        <translation>デバイスの選択に失敗しました</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="616"/>
+        <source>Failed to open device</source>
+        <translation>デバイスを開くことができませんでした</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="722"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="723"/>
+        <source>Unexpected input format: %1</source>
+        <translation>予期しない入力形式:%1%</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="758"/>
+        <source>Failed to load %1</source>
+        <translation>%1 のロードに失敗しました</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="797"/>
+        <source>No active device set, can&apos;t start acquisition.</source>
+        <translation>アクティブなデバイスが設定されていないため、取得を開始できません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="810"/>
+        <source>No channels enabled.</source>
+        <translation>チャネルが有効になっていません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1311"/>
+        <source>Out of memory, acquisition stopped.</source>
+        <translation>メモリが不足しているため、取得が停止しました。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1518"/>
+        <source>Can&apos;t handle more than 64 logic channels.</source>
+        <translation>64を超えるロジックチャネルを処理することはできません。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::StoreSession</name>
+    <message>
+        <location filename="../pv/storesession.cpp" line="114"/>
+        <source>Can&apos;t save logic channel without data.</source>
+        <translation>データなしでロジックチャネルを保存することはできません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="130"/>
+        <source>Can&apos;t save analog channel without data.</source>
+        <translation>データなしでアナログチャンネルを保存することはできません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="142"/>
+        <source>No channels enabled.</source>
+        <translation>チャネルが有効になっていません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="167"/>
+        <source>Can&apos;t save range without sample data.</source>
+        <translation>サンプルデータなしでは範囲を保存できません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="188"/>
+        <location filename="../pv/storesession.cpp" line="295"/>
+        <location filename="../pv/storesession.cpp" line="310"/>
+        <source>Error while saving: </source>
+        <translation>保存中のエラー:</translation>
+    </message>
+</context>
+<context>
+    <name>pv::binding::Device</name>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="82"/>
+        <source>Note for device developers: Ignoring device configuration capability &apos;%1&apos; as it is missing GET and/or SET</source>
+        <translation>デバイス開発者への注意:GETやSETがないため、デバイス構成機能&apos;%1&apos;を無視します</translation>
+    </message>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="107"/>
+        <source>No Limit</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::DecodeSignal</name>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="220"/>
+        <source>No decoders</source>
+        <translation>デコーダーなし</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="227"/>
+        <source>There are no channels assigned to this decoder</source>
+        <translation>このデコーダーに割り当てられたチャネルはありません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="241"/>
+        <source>One or more required channels have not been specified</source>
+        <translation>1つ以上の必要なチャネルが指定されていません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="257"/>
+        <source>No input data</source>
+        <translation>入力データなし</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1310"/>
+        <source>Decoder reported an error</source>
+        <translation>デコーダーがエラーを報告しました</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1464"/>
+        <source>Failed to create decoder instance</source>
+        <translation>デコーダーインスタンスの作成に失敗しました</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::MathSignal</name>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="107"/>
+        <source>Math%1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="306"/>
+        <source>No expression defined, nothing to do</source>
+        <translation>式は定義されていません、何もしません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="345"/>
+        <source>%1 at line %2, column %3: %4</source>
+        <translation>%1  行 %2, 列 %3: %4</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="364"/>
+        <location filename="../pv/data/mathsignal.cpp" line="536"/>
+        <source>&quot;%1&quot; isn&apos;t a valid analog signal</source>
+        <translation>&quot;%1&quot; は有効なアナログ信号ではありません</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="374"/>
+        <location filename="../pv/data/mathsignal.cpp" line="611"/>
+        <source>No data will be generated as %1 must be enabled</source>
+        <translation>%1を有効にする必要があるため、データは生成されません</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::SignalBase</name>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="525"/>
+        <source>Signal average</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="526"/>
+        <source>0.9V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="527"/>
+        <source>1.8V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="528"/>
+        <source>2.5V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="529"/>
+        <source>1.5V (for TTL)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="534"/>
+        <source>Signal average +/- 15%</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="535"/>
+        <source>0.3V/1.2V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="536"/>
+        <source>0.7V/2.5V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="537"/>
+        <source>1.3V/3.7V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="538"/>
+        <source>0.8V/2.0V (for TTL)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Connect</name>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="58"/>
+        <source>&amp;Scan for devices using driver above</source>
+        <translation>上記のドライバーを使用してデバイスをスキャンする</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="63"/>
+        <source>Connect to Device</source>
+        <translation>デバイスに接続</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="75"/>
+        <source>Step 1: Choose the driver</source>
+        <translation>ステップ1:ドライバーを選択</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="79"/>
+        <source>&amp;USB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="80"/>
+        <source>Serial &amp;Port</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="81"/>
+        <source>&amp;TCP/IP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="116"/>
+        <source>Protocol:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="134"/>
+        <source>Step 2: Choose the interface</source>
+        <translation>ステップ2:インターフェースを選択</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="140"/>
+        <source>Step 3: Scan for devices</source>
+        <translation>ステップ3:デバイスをスキャン</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="146"/>
+        <source>Step 4: Select the device</source>
+        <translation>ステップ4:デバイスを選択</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Settings</name>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="134"/>
+        <location filename="../pv/dialogs/settings.cpp" line="213"/>
+        <source>General</source>
+        <translation>全般</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="143"/>
+        <source>Views</source>
+        <translation>表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="153"/>
+        <location filename="../pv/dialogs/settings.cpp" line="406"/>
+        <source>Decoders</source>
+        <translation>デコーダー</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="163"/>
+        <source>About</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="172"/>
+        <source>Logging</source>
+        <translation>ロギング</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="236"/>
+        <source>User interface language</source>
+        <translation>表示言語</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="247"/>
+        <source>User interface theme</source>
+        <translation>表示のテーマ</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="249"/>
+        <source>(You may need to restart PulseView for all UI elements to update)</source>
+        <translation>(すべてのUI要素を更新するには、PulseViewを再起動する必要がある場合があります)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="255"/>
+        <source>System Default</source>
+        <translation>システムのデフォルト</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="268"/>
+        <source>Qt widget style</source>
+        <translation>Qt widget スタイル</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="270"/>
+        <source>(Dark themes look best with the Fusion style)</source>
+        <translation>(ダークテーマはFusionスタイルで最もよく見えます)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="277"/>
+        <source>Save session &amp;setup along with .sr file</source>
+        <translation>セッション設定を.srファイルと一緒に保存します</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="281"/>
+        <source>Start acquisition for all open sessions when clicking &apos;Run&apos;</source>
+        <translation>[実行]をクリックすると、開いているすべてのセッションの取得を開始します</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="296"/>
+        <source>Trace View</source>
+        <translation>トレースビュー</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="304"/>
+        <source>Use colored trace &amp;background</source>
+        <translation>色付きのトレースと背景を使用する(&amp;b)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="308"/>
+        <source>Constantly perform &amp;zoom-to-fit during acquisition</source>
+        <translation>取得中は常にズームツーフィットを実行します(&amp;z)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="312"/>
+        <source>Perform a zoom-to-&amp;fit when acquisition stops</source>
+        <translation>取得が停止したときにズームツーフィットする(&amp;f)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="316"/>
+        <source>Show time zero at the &amp;trigger</source>
+        <translation>トリガーで時間ゼロを表示する(&amp;t)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="320"/>
+        <source>Always keep &amp;newest samples at the right edge during capture</source>
+        <translation>キャプチャ中は常に最新のサンプルを右端に保持(&amp;n)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="324"/>
+        <source>Allow &amp;vertical dragging in the view area</source>
+        <translation>ビューエリアでの垂直方向のドラッグを許可する(&amp;v)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="328"/>
+        <source>Show data &amp;sampling points</source>
+        <translation>データサンプリングポイントを表示する(&amp;s)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="332"/>
+        <source>Fill &amp;high areas of logic signals</source>
+        <translation>ロジック信号の高い領域を埋める(&amp;h)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="339"/>
+        <source>Color to fill high areas of logic signals with</source>
+        <translation>論理信号の高い領域を塗りつぶす色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="343"/>
+        <source>Show analog minor grid in addition to div grid</source>
+        <translation>divグリッドに加えてアナログマイナーグリッドを表示する</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="347"/>
+        <source>Highlight mouse cursor using a vertical marker line</source>
+        <translation>垂直マーカーラインを使用してマウスカーソルを強調表示します</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="351"/>
+        <location filename="../pv/dialogs/settings.cpp" line="377"/>
+        <location filename="../pv/dialogs/settings.cpp" line="386"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="356"/>
+        <source>Maximum distance from edges before markers snap to them</source>
+        <translation>マーカーがエッジにスナップする前のエッジからの最大距離</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="363"/>
+        <source>Color to fill cursor area with</source>
+        <translation>カーソル領域を塗りつぶす色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="366"/>
+        <source>None</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="367"/>
+        <source>Background</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="368"/>
+        <source>Dots</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="373"/>
+        <source>Conversion threshold display mode (analog traces only)</source>
+        <translation>変換閾値表示モード(アナログトレースのみ)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="382"/>
+        <source>Default analog trace div height</source>
+        <translation>デフォルトのアナログトレースdivの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="391"/>
+        <source>Default logic trace height</source>
+        <translation>デフォルトのロジックトレースの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="414"/>
+        <source>Allow configuration of &amp;initial signal state</source>
+        <translation>初期信号状態の構成を許可する(&amp;i)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="418"/>
+        <source>Always show all &amp;rows, even if no annotation is visible</source>
+        <translation>注釈が表示されていない場合でも、常にすべての行を表示します(&amp;r)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="426"/>
+        <source>Annotation export format</source>
+        <translation>注釈のエクスポート形式</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="427"/>
+        <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
+        <translation>%s = サンプル範囲; %d: デコーダー名; %r: 行名; %c: クラス名</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="430"/>
+        <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
+        <translation>%1: 最長の注釈テキスト; %a: すべての注釈テキスト; %q: 引用符を使用</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="450"/>
+        <source>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="451"/>
+        <source>GNU GPL, version 3 or later</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="462"/>
+        <source>Versions, libraries and features:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="469"/>
+        <source>Firmware search paths:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="476"/>
+        <source>Protocol decoder search paths:</source>
+        <translation>プロトコルデコーダの検索パス:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="479"/>
+        <source>&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)&lt;/td&gt;&lt;/tr&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="484"/>
+        <source>Supported hardware drivers:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="491"/>
+        <source>Supported input formats:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="498"/>
+        <source>Supported output formats:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="506"/>
+        <source>Supported protocol decoders:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="514"/>
+        <source>Available Translations:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="562"/>
+        <source>Log level:</source>
+        <translation>ログレベル:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="567"/>
+        <source> lines</source>
+        <translation>行数</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="576"/>
+        <source>Length of background buffer:</source>
+        <translation>バックグラウンドバッファの長さ:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="582"/>
+        <source>&amp;Save to File</source>
+        <translation>ファイルに保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="589"/>
+        <source>&amp;Pop out</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="658"/>
+        <source>You selected a dark theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>ダークテーマを選択しました。
+選択に合わせてユーザー調整可能な色を設定する必要があります
+
+正しく表示するには、PulseViewを再起動する必要がある場合があることに注意してください。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="664"/>
+        <source>You selected a bright theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>明るいテーマを選択しました。
+選択に合わせてユーザー調整可能な色を設定する必要があります
+
+正しく表示するには、PulseViewを再起動する必要がある場合があることに注意してください。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="829"/>
+        <source>Save Log</source>
+        <translation>ログ保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="829"/>
+        <source>Log Files (*.txt *.log);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="841"/>
+        <source>Success</source>
+        <translation>成功</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="841"/>
+        <source>Log saved to %1.</source>
+        <translation>ログは %1 に保存されました。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="851"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="851"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル %1 に書き込めませんでした。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="865"/>
+        <source>%1 Log</source>
+        <translation>%1 ログ</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::StoreProgress</name>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Saving...</source>
+        <translation>保存中...</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Cancel</source>
+        <translation>キャンセル</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="89"/>
+        <source>Failed to save session.</source>
+        <translation>セッションの保存に失敗しました。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::popups::Channels</name>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="62"/>
+        <location filename="../pv/popups/channels.cpp" line="63"/>
+        <location filename="../pv/popups/channels.cpp" line="273"/>
+        <location filename="../pv/popups/channels.cpp" line="300"/>
+        <source>All</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="64"/>
+        <location filename="../pv/popups/channels.cpp" line="65"/>
+        <source>Logic</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="66"/>
+        <location filename="../pv/popups/channels.cpp" line="67"/>
+        <source>Analog</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="68"/>
+        <source>Named</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="69"/>
+        <source>Unnamed</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="70"/>
+        <source>Changing</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="71"/>
+        <source>Non-changing</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="141"/>
+        <source>Disable: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="149"/>
+        <source>Enable: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="281"/>
+        <location filename="../pv/popups/channels.cpp" line="301"/>
+        <source>None</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Bool</name>
+    <message>
+        <location filename="../pv/prop/bool.cpp" line="51"/>
+        <location filename="../pv/prop/bool.cpp" line="82"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Double</name>
+    <message>
+        <location filename="../pv/prop/double.cpp" line="65"/>
+        <location filename="../pv/prop/double.cpp" line="96"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Enum</name>
+    <message>
+        <location filename="../pv/prop/enum.cpp" line="113"/>
+        <location filename="../pv/prop/enum.cpp" line="176"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Int</name>
+    <message>
+        <location filename="../pv/prop/int.cpp" line="63"/>
+        <location filename="../pv/prop/int.cpp" line="127"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::String</name>
+    <message>
+        <location filename="../pv/prop/string.cpp" line="59"/>
+        <location filename="../pv/prop/string.cpp" line="84"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::DecoderCollectionModel</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="40"/>
+        <source>Decoder</source>
+        <translation>デコーダー</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="41"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="42"/>
+        <source>ID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="49"/>
+        <source>All Decoders</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::SubWindow</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="49"/>
+        <source>Select a decoder to see its description here.</source>
+        <translation>ここに説明を表示するには、デコーダーを選択してください。</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="248"/>
+        <source>, %1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="265"/>
+        <source>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="312"/>
+        <source>Protocol decoder &lt;b&gt;%1&lt;/b&gt; requires input type &lt;b&gt;%2&lt;/b&gt; which several decoders provide.&lt;br&gt;Choose which one to use:&lt;br&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="320"/>
+        <source>Choose Decoder</source>
+        <translation>デコーダーを選択</translation>
+    </message>
+</context>
+<context>
+    <name>pv::toolbars::MainBar</name>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="124"/>
+        <source>New &amp;View</source>
+        <translation>新しいビュー(&amp;V)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="130"/>
+        <source>&amp;Open...</source>
+        <translation>開く(&amp;O)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="137"/>
+        <source>Restore Session Setu&amp;p...</source>
+        <translation>セッション設定を復元(&amp;p)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="141"/>
+        <source>&amp;Save...</source>
+        <translation>保存(&amp;S)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="148"/>
+        <source>Save &amp;As...</source>
+        <translation>名前をつけて保存(&amp;A)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="154"/>
+        <source>Save Selected &amp;Range As...</source>
+        <translation>選択した範囲を名前を付けて保存(&amp;R)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="161"/>
+        <source>Save Session Setu&amp;p...</source>
+        <translation>セッション設定を保存(&amp;p)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="167"/>
+        <source>&amp;Export</source>
+        <translation>エクスポート(&amp;E)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="173"/>
+        <source>&amp;Import</source>
+        <translation>インポート(&amp;I)</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="177"/>
+        <source>&amp;Connect to Device...</source>
+        <translation>デバイスへ接続(&amp;C)...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="239"/>
+        <source>Add protocol decoder</source>
+        <translation>プロトコルデコーダーを追加</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="249"/>
+        <source>Add math signal</source>
+        <translation>math信号を追加</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="265"/>
+        <source>Configure Device</source>
+        <translation>デバイスの構成</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="269"/>
+        <source>Configure Channels</source>
+        <translation>チャンネルの構成</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="383"/>
+        <source>Failed to get sample rate list:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="446"/>
+        <source>Failed to get sample rate:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="487"/>
+        <source>Failed to get sample limit list:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="577"/>
+        <source>Failed to configure samplerate:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="604"/>
+        <source>Failed to configure sample count:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="642"/>
+        <source>Missing Cursors</source>
+        <translation>カーソルがありません</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="642"/>
+        <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
+        <translation>カーソルで囲まれたデータをセッションファイルに保存する前に、カーソルを設定する必要があります(たとえば、[カーソルの表示]ボタンを使用)。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="660"/>
+        <source>Invalid Range</source>
+        <translation>範囲が無効です</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="660"/>
+        <source>The cursors don&apos;t define a valid range of samples.</source>
+        <translation>カーソルはサンプルの有効な範囲を定義していません。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>%1 files </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="680"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="730"/>
+        <source>All Files</source>
+        <translation>すべてのファイル</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="684"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="861"/>
+        <source>Save File</source>
+        <translation>ファイル保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <source>Export %1</source>
+        <translation>エクスポート %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="727"/>
+        <source>%1 files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="738"/>
+        <source>Import File</source>
+        <translation>インポートファイル</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="747"/>
+        <source>Import %1</source>
+        <translation>インポート %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="820"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="878"/>
+        <source>Open File</source>
+        <translation>ファイルを開く</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="820"/>
+        <source>sigrok Sessions (*.sr);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="861"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="878"/>
+        <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="946"/>
+        <source>Total sampling time: %1</source>
+        <translation>総サンプリング時間: %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::decoder_binary::View</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="83"/>
+        <source>Decoder:</source>
+        <translation>デコーダー:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="87"/>
+        <source>Show data as</source>
+        <translation>データ表示形式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
+        <source>Hexdump</source>
+        <translation>16進ダンプ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
+        <source>&amp;Save...</source>
+        <translation>保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="266"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="306"/>
+        <source>Save Binary Data</source>
+        <translation>バイナリデータを保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="266"/>
+        <source>Binary Data Files (*.bin);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="285"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="337"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="285"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="337"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル%1に書き込めませんでした。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="306"/>
+        <source>Hex Dumps (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::AnnotationCollectionModel</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
+        <source>Sample</source>
+        <translation>サンプル</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
+        <source>Time</source>
+        <translation>時間</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
+        <source>Decoder</source>
+        <translation>デコーダー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
+        <source>Ann Row</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
+        <source>Ann Class</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
+        <source>Value</source>
+        <translation>値</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>s</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>sa</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::View</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
+        <source>Decoder:</source>
+        <translation>デコーダー:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
+        <source>Hide Hidden Rows/Classes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
+        <source>&amp;Save...</source>
+        <translation>保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <source>Save Annotations as CSV</source>
+        <translation>注釈をCSVとして保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="374"/>
+        <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="442"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル%1に書き込めませんでした。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::AnalogSignal</name>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="802"/>
+        <source>Number of pos vertical divs</source>
+        <translation>正の垂直divの数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="810"/>
+        <source>Number of neg vertical divs</source>
+        <translation>負の垂直divの数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="815"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="819"/>
+        <source>Div height</source>
+        <translation>Divの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="837"/>
+        <source>V/div</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="841"/>
+        <source>Vertical resolution</source>
+        <translation>垂直解像度</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="850"/>
+        <source>Autoranging</source>
+        <translation>オートレンジ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="855"/>
+        <source>none</source>
+        <translation>なし</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="857"/>
+        <source>to logic via threshold</source>
+        <translation>閾値でロジック変換</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="859"/>
+        <source>to logic via schmitt-trigger</source>
+        <translation>シュミットトリガーでロジック変換</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="865"/>
+        <source>Conversion</source>
+        <translation>変換</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="874"/>
+        <source>Conversion threshold(s)</source>
+        <translation>変換閾値</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="884"/>
+        <source>analog</source>
+        <translation>アナログ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="885"/>
+        <source>converted</source>
+        <translation>変換済</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="886"/>
+        <source>analog+converted</source>
+        <translation>アナログ+変換済</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="891"/>
+        <source>Show traces for</source>
+        <translation>トレースを表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Cursor</name>
+    <message>
+        <location filename="../pv/views/trace/cursor.cpp" line="97"/>
+        <source>Disable snapping</source>
+        <translation>スナップを無効にする</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::CursorPair</name>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="128"/>
+        <source>Display interval</source>
+        <translation>間隔表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
+        <source>Display frequency</source>
+        <translation>周波数表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
+        <source>Display samples</source>
+        <translation>サンプル数表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::DecodeTrace</name>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="446"/>
+        <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="457"/>
+        <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="461"/>
+        <source>Stack Decoder</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="462"/>
+        <source>Stack a higher-level decoder on top of this one</source>
+        <translation>この上に高レベルのデコーダーをスタック</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="476"/>
+        <source>Delete</source>
+        <translation>削除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="518"/>
+        <source>Resume decoding</source>
+        <translation>デコードを再開</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="525"/>
+        <source>Pause decoding</source>
+        <translation>デコードを一時中断</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="533"/>
+        <source>Copy annotation text to clipboard</source>
+        <translation>注釈テキストをクリップボードにコピー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="542"/>
+        <source>Export all annotations</source>
+        <translation>すべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="549"/>
+        <source>Export all annotations for this row</source>
+        <translation>この行のすべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="558"/>
+        <source>Export all annotations, starting here</source>
+        <translation>ここから始めて、すべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="565"/>
+        <source>Export annotations for this row, starting here</source>
+        <translation>ここから始めて、この行の注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="574"/>
+        <source>Export all annotations within cursor range</source>
+        <translation>カーソル範囲内のすべての注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="581"/>
+        <source>Export annotations for this row within cursor range</source>
+        <translation>カーソル範囲内のこの行の注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1060"/>
+        <source>%1:
+%2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1104"/>
+        <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1173"/>
+        <source>Export annotations</source>
+        <translation>注釈をエクスポート</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1173"/>
+        <source>Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1238"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1238"/>
+        <source>File %1 could not be written to.</source>
+        <translation>ファイル%1に書き込めませんでした。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1291"/>
+        <source>Show this row</source>
+        <translation>この行を表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1302"/>
+        <source>Show All</source>
+        <translation>すべて表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1310"/>
+        <source>Hide All</source>
+        <translation>すべて非表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Flag</name>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="132"/>
+        <source>Text</source>
+        <translation>テキスト</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="141"/>
+        <source>Delete</source>
+        <translation>削除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="146"/>
+        <source>Disable snapping</source>
+        <translation>スナップを無効</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Header</name>
+    <message>
+        <location filename="../pv/views/trace/header.cpp" line="137"/>
+        <source>Group</source>
+        <translation>グループ</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::LogicSignal</name>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="423"/>
+        <source>No trigger</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="428"/>
+        <source>Trigger on rising edge</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="433"/>
+        <source>Trigger on high level</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="438"/>
+        <source>Trigger on falling edge</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="443"/>
+        <source>Trigger on low level</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="448"/>
+        <source>Trigger on rising or falling edge</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="535"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="539"/>
+        <source>Trace height</source>
+        <translation>トレースの高さ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
+        <source>Trigger</source>
+        <translation>トリガー</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathEditDialog</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="88"/>
+        <source>Math Expression Editor</source>
+        <translation>数式エディタ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="93"/>
+        <source>Inputs:</source>
+        <translation>入力:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="99"/>
+        <source>Variables:</source>
+        <translation>変数:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="101"/>
+        <source>Basic operators:</source>
+        <translation>基本的な演算子:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="108"/>
+        <source>Assignments:</source>
+        <translation>割り当て:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="118"/>
+        <source>General purpose functions:</source>
+        <translation>汎用機能:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="119"/>
+        <source>abs(x)         Absolute value of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="120"/>
+        <source>avg(x, y, ...) Average of all input values</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="121"/>
+        <source>ceil(x)                Smallest integer that is greater than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="122"/>
+        <source>clamp(lb, x, ub)       Clamp x in range between lb and ub, where lb &lt; ub</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="123"/>
+        <source>equal(x, y)    Equality test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="124"/>
+        <source>erf(x)         Error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="125"/>
+        <source>erfc(x)                Complimentary error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="126"/>
+        <source>exp(x)         e to the power of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="127"/>
+        <source>expm1(x)       e to the power of x minus 1, where x is very small.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="128"/>
+        <source>floor(x)               Largest integer that is less than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="129"/>
+        <source>frac(x)                Fractional portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="130"/>
+        <source>hypot(x)               Hypotenuse of x and y (i.e. sqrt(x*x + y*y))</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="131"/>
+        <source>iclamp(lb, x, ub)      Inverse-clamp x outside of the range lb and ub, where lb &lt; ub.
+               If x is within the range it will snap to the closest bound</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="132"/>
+        <source>inrange(lb, x, ub)     In-range returns true when x is within the range lb and ub, where lb &lt; ub.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="133"/>
+        <source>log(x)         Natural logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="134"/>
+        <source>log10(x)               Base 10 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="138"/>
+        <source>log1p(x)               Natural logarithm of 1 + x, where x is very small</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="139"/>
+        <source>log2(x)                Base 2 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="140"/>
+        <source>logn(x)                Base N logarithm of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="141"/>
+        <source>max(x, y, ...) Largest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="142"/>
+        <source>min(x, y, ...) Smallest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="143"/>
+        <source>mul(x, y, ...) Product of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="144"/>
+        <source>ncdf(x)                Normal cumulative distribution function</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="145"/>
+        <source>nequal(x, y)   Not-equal test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="146"/>
+        <source>pow(x, y)      x to the power of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="147"/>
+        <source>root(x, n)     Nth-Root of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="148"/>
+        <source>round(x)               Round x to the nearest integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="149"/>
+        <source>roundn(x, n)   Round x to n decimal places, where n &gt; 0 and is an integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="150"/>
+        <source>sgn(x)         Sign of x; -1 if x &lt; 0, +1 if x &gt; 0, else zero</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="151"/>
+        <source>sqrt(x)                Square root of x, where x &gt;= 0</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="152"/>
+        <source>sum(x, y, ..,) Sum of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="153"/>
+        <source>swap(x, y)     Swap the values of the variables x and y and return the current value of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="154"/>
+        <source>trunc(x)               Integer portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="158"/>
+        <source>Trigonometry functions:</source>
+        <translation>三角関数:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="159"/>
+        <source>acos(x)                Arc cosine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="160"/>
+        <source>acosh(x)               Inverse hyperbolic cosine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="161"/>
+        <source>asin(x)                Arc sine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="162"/>
+        <source>asinh(x)               Inverse hyperbolic sine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="163"/>
+        <source>atan(x)                Arc tangent of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="164"/>
+        <source>atan2(x, y)    Arc tangent of (x / y) expressed in radians. [-pi,+pi]  </source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="165"/>
+        <source>atanh(x)               Inverse hyperbolic tangent of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="166"/>
+        <source>cos(x)         Cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="167"/>
+        <source>cosh(x)                Hyperbolic cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="168"/>
+        <source>cot(x)         Cotangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="169"/>
+        <source>csc(x)         Cosectant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="170"/>
+        <source>sec(x)         Secant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="171"/>
+        <source>sin(x)         Sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="172"/>
+        <source>sinc(x)                Sine cardinal of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="173"/>
+        <source>sinh(x)                Hyperbolic sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="174"/>
+        <source>tan(x)         Tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="175"/>
+        <source>tanh(x)                Hyperbolic tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="176"/>
+        <source>deg2rad(x)     Convert x from degrees to radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="177"/>
+        <source>deg2grad(x)    Convert x from degrees to gradians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="178"/>
+        <source>rad2deg(x)     Convert x from radians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="179"/>
+        <source>grad2deg(x)    Convert x from gradians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="183"/>
+        <source>Logic operators:</source>
+        <translation>論理演算子:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="200"/>
+        <source>Comparisons:</source>
+        <translation>比較:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="201"/>
+        <source>x = y or x == y        True only if x is strictly equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="202"/>
+        <source>x &lt;&gt; y or x != y True only if x does not equal y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="203"/>
+        <source>x &lt; y               True only if x is less than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="204"/>
+        <source>x &lt;= y              True only if x is less than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="205"/>
+        <source>x &gt; y               True only if x is greater than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="206"/>
+        <source>x &gt;= y              True only if x is greater than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="207"/>
+        <source>Flow control:</source>
+        <translation>フロー制御:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="208"/>
+        <source>{ ... }                Beginning and end of instruction block</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="209"/>
+        <source>if (x, y, z)   If x is true then return y else return z
+if (x) y;                      variant without implied else
+if (x) { y };          variant with an instruction block
+if (x) y; else z;              variant with explicit else
+if (x) { y } else { z };       variant with instruction blocks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="210"/>
+        <source>x ? y : z      Ternary operator, equivalent to &apos;if (x, y, z)&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="211"/>
+        <source>switch {               The first true case condition that is encountered will
+ case x &gt; 1: a;     determine the result of the switch. If none of the case
+ case x &lt; 1: b;     conditions hold true, the default action is used
+ default:     c;       to determine the return value
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="215"/>
+        <source>while (conditon) {     Evaluates expression repeatedly as long as condition is true,
+ expression;   returning the last value of expression
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="216"/>
+        <source>repeat         Evalues expression repeatedly as long as condition is false,
+ expression;   returning the last value of expression
+until (condition)
+</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="217"/>
+        <source>for (var x := 0; condition; x += 1) {  Repeatedly evaluates expression while the condition is true,
+ expression                    while evaluating the &apos;increment&apos; expression on each loop
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="218"/>
+        <source>break          Terminates the execution of the nearest enclosed loop, returning NaN</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="219"/>
+        <source>break[x]               Terminates the execution of the nearest enclosed loop, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="220"/>
+        <source>continue               Interrupts loop execution and resumes with the next loop iteration</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="221"/>
+        <source>return[x]              Returns immediately from within the current expression, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="222"/>
+        <source>~(expr; expr; ...)     Evaluates each sub-expression and returns the value of the last one
+~{expr; expr; ...}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="234"/>
+        <source>Copy to expression</source>
+        <translation>式にコピー</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="247"/>
+        <source>Basics</source>
+        <translation>基本</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="248"/>
+        <source>Functions 1</source>
+        <translation>関数 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="249"/>
+        <source>Functions 2</source>
+        <translation>関数 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="250"/>
+        <source>Trigonometry</source>
+        <translation>三角関数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="251"/>
+        <source>Logic</source>
+        <translation>論理</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="252"/>
+        <source>Flow Control 1</source>
+        <translation>フロー制御 1</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="253"/>
+        <source>Flow Control 2</source>
+        <translation>フロー制御 2</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="254"/>
+        <source>Examples</source>
+        <translation>例</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathSignal</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="313"/>
+        <source>Expression</source>
+        <translation>式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="317"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="327"/>
+        <source>same as session</source>
+        <translation>セッションと同じ</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="318"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="328"/>
+        <source>100</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="319"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="329"/>
+        <source>10000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="320"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="330"/>
+        <source>1000000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="323"/>
+        <source>Number of Samples</source>
+        <translation>サンプル数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="331"/>
+        <source>Sample rate</source>
+        <translation>サンプルレート</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Ruler</name>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="153"/>
+        <source>Create marker here</source>
+        <translation>ここでマーカーを作成</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="157"/>
+        <source>Set as zero point</source>
+        <translation>ゼロポイントとして設定</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="162"/>
+        <source>Reset zero point</source>
+        <translation>ゼロポイントをリセット</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Disable mouse hover marker</source>
+        <translation>マウスホバーマーカーを無効</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Enable mouse hover marker</source>
+        <translation>マウスホバーマーカーを有効</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Signal</name>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="153"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="167"/>
+        <source>Remove</source>
+        <translation>削除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="169"/>
+        <source>Disable</source>
+        <translation>無効</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::StandardBar</name>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="54"/>
+        <source>Zoom &amp;In</source>
+        <translation>ズームイン(&amp;I)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="62"/>
+        <source>Zoom &amp;Out</source>
+        <translation>ズームアウト(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="70"/>
+        <source>Zoom to &amp;Fit</source>
+        <translation>ウインドウに合わせる(&amp;F)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="82"/>
+        <source>Show &amp;Cursors</source>
+        <translation>カーソル表示(&amp;C)</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="85"/>
+        <source>Display last segment only</source>
+        <translation>最後のセグメントのみ表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="90"/>
+        <source>Display last complete segment only</source>
+        <translation>最後の完全なセグメントのみ表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="95"/>
+        <source>Display a single segment</source>
+        <translation>単一のセグメントを表示</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TimeMarker</name>
+    <message>
+        <location filename="../pv/views/trace/timemarker.cpp" line="191"/>
+        <source>Time</source>
+        <translation>時間</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Trace</name>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="229"/>
+        <source>Create marker here</source>
+        <translation>ここにマーカーを作成</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="338"/>
+        <source>Color</source>
+        <translation>色</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="403"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TraceGroup</name>
+    <message>
+        <location filename="../pv/views/trace/tracegroup.cpp" line="140"/>
+        <source>Ungroup</source>
+        <translation>グループ解除</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DecoderGroupBox</name>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="48"/>
+        <source>Show/hide this decoder trace</source>
+        <translation>このデコーダートレースを表示/非表示</translation>
+    </message>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="58"/>
+        <source>Delete this decoder trace</source>
+        <translation>このデコーダートレースを削除</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DeviceToolButton</name>
+    <message>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="75"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="82"/>
+        <source>&lt;No Device&gt;</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ExportMenu</name>
+    <message>
+        <location filename="../pv/widgets/exportmenu.cpp" line="71"/>
+        <source>Export %1...</source>
+        <translation>エクスポート %1...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ImportMenu</name>
+    <message>
+        <location filename="../pv/widgets/importmenu.cpp" line="68"/>
+        <source>Import %1...</source>
+        <translation>インポート %1...</translation>
+    </message>
+</context>
+</TS>
diff --git a/l10n/zh_cn.ts b/l10n/zh_cn.ts
new file mode 100644 (file)
index 0000000..937a96c
--- /dev/null
@@ -0,0 +1,2207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN" sourcelanguage="en">
+<context>
+    <name>Application</name>
+    <message>
+        <location filename="../pv/application.cpp" line="135"/>
+        <source>Some parts of the application may still use the previous language. Re-opening the affected windows or restarting the application will remedy this.</source>
+        <translation>应用程序的某些部分可能仍然使用以前的语言。重新打开受影响的窗口或重新启动应用程序将解决此问题。</translation>
+    </message>
+</context>
+<context>
+    <name>QApplication</name>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="274"/>
+        <source>Error when scanning device driver &apos;%1&apos;: %2</source>
+        <translation>扫描设备驱动程序时出错 &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="70"/>
+        <source>Querying config key %1 is not allowed</source>
+        <translation>不允许查询 %1 配置</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="79"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../pv/devices/device.cpp" line="93"/>
+        <source>Unknown type supplied when attempting to query %1</source>
+        <translation>查询 %1 时 返回未知类型</translation>
+    </message>
+</context>
+<context>
+    <name>QHexView</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/QHexView.cpp" line="339"/>
+        <source>No data available</source>
+        <translation>无可用数据</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../main.cpp" line="116"/>
+        <source>Stack trace of previous crash:</source>
+        <translation>跟踪上次崩溃日志:</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="130"/>
+        <source>Don&apos;t show this message again</source>
+        <translation>不再显示此消息</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="133"/>
+        <source>When %1 last crashed, it created a stack trace.
+A human-readable form has been saved to disk and was written to the log. You may access it from the settings dialog.</source>
+        <translation>上次崩溃时 %1 ,创建了日志文件。
+可在 设置\Logging 中查看。</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="65"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+    <message>
+        <location filename="../pv/devicemanager.cpp" line="96"/>
+        <source>Scanning for devices that driver %1 can access...</source>
+        <translation>正在扫描驱动程序 %1 可以访问的设备...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::MainWindow</name>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="70"/>
+        <source>PulseView</source>
+        <translation>PulseView</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="284"/>
+        <source>Decoder Selector</source>
+        <translation>协议解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="337"/>
+        <source>Session %1</source>
+        <translation>会话 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="519"/>
+        <source>Create New Session</source>
+        <translation>创建新会话</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="525"/>
+        <source>Start/Stop Acquisition</source>
+        <translation>开始/停止 采集</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="533"/>
+        <source>Settings</source>
+        <translation>设置</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <source>Reload</source>
+        <translation>重新加载</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="589"/>
+        <location filename="../pv/mainwindow.cpp" line="592"/>
+        <source>Run</source>
+        <translation>开始</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="598"/>
+        <source>Stop</source>
+        <translation>停止</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="644"/>
+        <location filename="../pv/mainwindow.cpp" line="867"/>
+        <location filename="../pv/mainwindow.cpp" line="893"/>
+        <source>Confirmation</source>
+        <translation>确认</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="645"/>
+        <source>There is unsaved data. Close anyway?</source>
+        <translation>数据未保存。是否关闭?</translation>
+    </message>
+    <message>
+        <location filename="../pv/mainwindow.cpp" line="868"/>
+        <location filename="../pv/mainwindow.cpp" line="894"/>
+        <source>This session contains unsaved data. Close it anyway?</source>
+        <translation>此会话数据未保存。是否关闭?</translation>
+    </message>
+</context>
+<context>
+    <name>pv::Session</name>
+    <message>
+        <location filename="../pv/session.cpp" line="396"/>
+        <source>Can&apos;t restore generated signal of unknown type %1 (%2)</source>
+        <translation>无法还原生成的未知类型的信号 %1 (%2)</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="559"/>
+        <source>Failed to select device</source>
+        <translation>无法选择的设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="616"/>
+        <source>Failed to open device</source>
+        <translation>无法打开的设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="722"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="723"/>
+        <source>Unexpected input format: %1</source>
+        <translation>不支持的输入格式: %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="758"/>
+        <source>Failed to load %1</source>
+        <translation>无法加载 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="797"/>
+        <source>No active device set, can&apos;t start acquisition.</source>
+        <translation>未设置活动设备,无法采集。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="810"/>
+        <source>No channels enabled.</source>
+        <translation>未启用任何通道。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1311"/>
+        <source>Out of memory, acquisition stopped.</source>
+        <translation>内存不足,采集停止。</translation>
+    </message>
+    <message>
+        <location filename="../pv/session.cpp" line="1518"/>
+        <source>Can&apos;t handle more than 64 logic channels.</source>
+        <translation>无法处理超过64个逻辑通道。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::StoreSession</name>
+    <message>
+        <location filename="../pv/storesession.cpp" line="114"/>
+        <source>Can&apos;t save logic channel without data.</source>
+        <translation>无法保存,逻辑通道内没有数据。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="130"/>
+        <source>Can&apos;t save analog channel without data.</source>
+        <translation>无法保存,模拟通道内没有数据。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="142"/>
+        <source>No channels enabled.</source>
+        <translation>未启用任何通道。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="167"/>
+        <source>Can&apos;t save range without sample data.</source>
+        <translation>无法保存,光标范围内没有数据。</translation>
+    </message>
+    <message>
+        <location filename="../pv/storesession.cpp" line="192"/>
+        <location filename="../pv/storesession.cpp" line="299"/>
+        <location filename="../pv/storesession.cpp" line="314"/>
+        <source>Error while saving: </source>
+        <translation>保存时出错: </translation>
+    </message>
+</context>
+<context>
+    <name>pv::binding::Device</name>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="82"/>
+        <source>Note for device developers: Ignoring device configuration capability &apos;%1&apos; as it is missing GET and/or SET</source>
+        <translation>设备开发人员注意:缺少 GET/SET,忽略该设备配置功能 &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../pv/binding/device.cpp" line="107"/>
+        <source>No Limit</source>
+        <translation>没有限制</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::DecodeSignal</name>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="223"/>
+        <source>No decoders</source>
+        <translation>没有解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="230"/>
+        <source>There are no channels assigned to this decoder</source>
+        <translation>解码器没有选择通道</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="244"/>
+        <source>One or more required channels have not been specified</source>
+        <translation>解码器缺少一个或多个通道</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="260"/>
+        <source>No input data</source>
+        <translation>无数据输入</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1325"/>
+        <source>Decoder reported an error</source>
+        <translation>解码器解码错误</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/decodesignal.cpp" line="1484"/>
+        <source>Failed to create decoder instance</source>
+        <translation>无法创建的解码器实例</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::MathSignal</name>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="107"/>
+        <source>Math%1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="306"/>
+        <source>No expression defined, nothing to do</source>
+        <translation>未定义表达式,不执行任何操作</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="345"/>
+        <source>%1 at line %2, column %3: %4</source>
+        <translation>%1 行 %2, 列 %3: %4</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="364"/>
+        <location filename="../pv/data/mathsignal.cpp" line="536"/>
+        <source>&quot;%1&quot; isn&apos;t a valid analog signal</source>
+        <translation>&quot;%1&quot; 不是有效的模拟信号</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/mathsignal.cpp" line="374"/>
+        <location filename="../pv/data/mathsignal.cpp" line="611"/>
+        <source>No data will be generated as %1 must be enabled</source>
+        <translation>不会生成数据,因为必须启用 %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::data::SignalBase</name>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="525"/>
+        <source>Signal average</source>
+        <translation>平均信号电平</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="526"/>
+        <source>0.9V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="527"/>
+        <source>1.8V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="528"/>
+        <source>2.5V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="529"/>
+        <source>1.5V (for TTL)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="534"/>
+        <source>Signal average +/- 15%</source>
+        <translation>平均信号电平 +/- 15%</translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="535"/>
+        <source>0.3V/1.2V (for 1.8V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="536"/>
+        <source>0.7V/2.5V (for 3.3V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="537"/>
+        <source>1.3V/3.7V (for 5.0V CMOS)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/data/signalbase.cpp" line="538"/>
+        <source>0.8V/2.0V (for TTL)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Connect</name>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="58"/>
+        <source>&amp;Scan for devices using driver above</source>
+        <translation>使用上述驱动程序扫描设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="63"/>
+        <source>Connect to Device</source>
+        <translation>连接到设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="75"/>
+        <source>Step 1: Choose the driver</source>
+        <translation>步骤1:选择驱动程序</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="79"/>
+        <source>&amp;USB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="80"/>
+        <source>Serial &amp;Port</source>
+        <translation>串行和端口</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="81"/>
+        <source>&amp;TCP/IP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="116"/>
+        <source>Protocol:</source>
+        <translation>协议:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="134"/>
+        <source>Step 2: Choose the interface</source>
+        <translation>步骤2:选择接口</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="140"/>
+        <source>Step 3: Scan for devices</source>
+        <translation>步骤3:扫描设备</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/connect.cpp" line="146"/>
+        <source>Step 4: Select the device</source>
+        <translation>步骤4:选择设备</translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::Settings</name>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="134"/>
+        <location filename="../pv/dialogs/settings.cpp" line="213"/>
+        <source>General</source>
+        <translation>常规</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="143"/>
+        <source>Views</source>
+        <translation>显示</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="153"/>
+        <location filename="../pv/dialogs/settings.cpp" line="411"/>
+        <source>Decoders</source>
+        <translation>解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="163"/>
+        <source>About</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="172"/>
+        <source>Logging</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="241"/>
+        <source>User interface language</source>
+        <translation>语言</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="252"/>
+        <source>User interface theme</source>
+        <translation>主题</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="254"/>
+        <source>(You may need to restart PulseView for all UI elements to update)</source>
+        <translation>(您可能需要重新启动PulseView才能更新所有UI元素)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="260"/>
+        <source>System Default</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="273"/>
+        <source>Qt widget style</source>
+        <translation>QT 软件界面风格</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="275"/>
+        <source>(Dark themes look best with the Fusion style)</source>
+        <translation>(深色主题与Fusion风格搭配效果最佳)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="282"/>
+        <source>Save session &amp;setup along with .sr file</source>
+        <translation>将会话设置与 .sr文件一起保存</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="286"/>
+        <source>Start acquisition for all open sessions when clicking &apos;Run&apos;</source>
+        <translation>单击“开始”时启动所有打开会话的采集</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="301"/>
+        <source>Trace View</source>
+        <translation>采集视图</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="309"/>
+        <source>Use colored trace &amp;background</source>
+        <translation>使用彩色通道背景</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="313"/>
+        <source>Constantly perform &amp;zoom-to-fit during acquisition</source>
+        <translation>采集数据过程中不断调整和缩放以适应窗口 ( &amp;Z )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="317"/>
+        <source>Perform a zoom-to-&amp;fit when acquisition stops</source>
+        <translation>采集停止时执行调整和缩放以适应窗口 ( &amp;A )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="321"/>
+        <source>Show time zero at the &amp;trigger</source>
+        <translation>在触发器处显示时间零点 ( &amp;T )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="325"/>
+        <source>Always keep &amp;newest samples at the right edge during capture</source>
+        <translation>采集过程中始终跟踪右侧最新数据 ( &amp;Q )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="329"/>
+        <source>Allow &amp;vertical dragging in the view area</source>
+        <translation>允许在视图区域中垂直拖动</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="333"/>
+        <source>Show data &amp;sampling points</source>
+        <translation>显示数据采样点</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="337"/>
+        <source>Fill &amp;high areas of logic signals</source>
+        <translation>填充逻辑信号高电平的区域 ( &amp;H )</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="344"/>
+        <source>Color to fill high areas of logic signals with</source>
+        <translation>填充逻辑信号高电平区域的颜色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="348"/>
+        <source>Show analog minor grid in addition to div grid</source>
+        <translation>模拟通道除了div网格之外,还显示更多垂直细分网格</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="352"/>
+        <source>Highlight mouse cursor using a vertical marker line</source>
+        <translation>用竖线突出显示鼠标光标位置</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="356"/>
+        <location filename="../pv/dialogs/settings.cpp" line="382"/>
+        <location filename="../pv/dialogs/settings.cpp" line="391"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="361"/>
+        <source>Maximum distance from edges before markers snap to them</source>
+        <translation>光标吸附到边沿前的最大距离</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="368"/>
+        <source>Color to fill cursor area with</source>
+        <translation>填充光标范围内的颜色</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="371"/>
+        <source>None</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="372"/>
+        <source>Background</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="373"/>
+        <source>Dots</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="378"/>
+        <source>Conversion threshold display mode (analog traces only)</source>
+        <translation>转换阈值显示模式(仅限模拟通道)</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="387"/>
+        <source>Default analog trace div height</source>
+        <translation>模拟通道垂直高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="396"/>
+        <source>Default logic trace height</source>
+        <translation>逻辑通道垂直高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="419"/>
+        <source>Allow configuration of &amp;initial signal state</source>
+        <translation>允许配置初始信号状态</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="423"/>
+        <source>Always show all &amp;rows, even if no annotation is visible</source>
+        <translation>始终显示所有解码,即使未解码</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="431"/>
+        <source>Annotation export format</source>
+        <translation>解码数据导出格式</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="432"/>
+        <source>%s = sample range; %d: decoder name; %r: row name; %c: class name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="435"/>
+        <source>%1: longest annotation text; %a: all annotation texts; %q: use quotation marks</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="455"/>
+        <source>%1&lt;br /&gt;&lt;a href=&quot;http://%2&quot;&gt;%2&lt;/a&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="456"/>
+        <source>GNU GPL, version 3 or later</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="467"/>
+        <source>Versions, libraries and features:</source>
+        <translation>版本、库和功能:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="474"/>
+        <source>Firmware search paths:</source>
+        <translation>固件搜索路径:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="481"/>
+        <source>Protocol decoder search paths:</source>
+        <translation>协议解码器搜索路径:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="484"/>
+        <source>&lt;tr&gt;&lt;td colspan=&quot;2&quot;&gt;(Note: Set environment variable SIGROKDECODE_DIR to add a custom directory)&lt;/td&gt;&lt;/tr&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="489"/>
+        <source>Supported hardware drivers:</source>
+        <translation>支持的硬件驱动程序:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="496"/>
+        <source>Supported input formats:</source>
+        <translation>支持的输入格式:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="503"/>
+        <source>Supported output formats:</source>
+        <translation>支持的输出格式:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="511"/>
+        <source>Supported protocol decoders:</source>
+        <translation>支持的协议解码器:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="519"/>
+        <source>Available Translations:</source>
+        <translation>可用翻译:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="567"/>
+        <source>Log level:</source>
+        <translation>日志级别:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="572"/>
+        <source> lines</source>
+        <translation> 行</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="581"/>
+        <source>Length of background buffer:</source>
+        <translation>日志缓存长度:</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="587"/>
+        <source>&amp;Save to File</source>
+        <translation>保存到文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="594"/>
+        <source>&amp;Pop out</source>
+        <translation>独立弹出日志窗口</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="663"/>
+        <source>You selected a dark theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>您选择了一个深色主题。
+是否相应地调整特定的颜色,以更好地协调?
+PulseView可能需要重新启动才能正确显示。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="669"/>
+        <source>You selected a bright theme.
+Should I set the user-adjustable colors to better suit your choice?
+
+Please keep in mind that PulseView may need a restart to display correctly.</source>
+        <translation>您选择了一个明亮主题。
+是否相应地调整特定的颜色,以更好地协调?
+PulseView可能需要重新启动才能正确显示。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="834"/>
+        <source>Save Log</source>
+        <translation>保存日志</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="834"/>
+        <source>Log Files (*.txt *.log);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="846"/>
+        <source>Success</source>
+        <translation>成功</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="846"/>
+        <source>Log saved to %1.</source>
+        <translation>日志已保存到 %1。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="856"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/settings.cpp" line="870"/>
+        <source>%1 Log</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::dialogs::StoreProgress</name>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Saving...</source>
+        <translation>保存中...</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="44"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+    <message>
+        <location filename="../pv/dialogs/storeprogress.cpp" line="89"/>
+        <source>Failed to save session.</source>
+        <translation>未能保存会话。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::popups::Channels</name>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="62"/>
+        <location filename="../pv/popups/channels.cpp" line="63"/>
+        <location filename="../pv/popups/channels.cpp" line="278"/>
+        <location filename="../pv/popups/channels.cpp" line="305"/>
+        <source>All</source>
+        <translation>全部</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="64"/>
+        <location filename="../pv/popups/channels.cpp" line="65"/>
+        <source>Logic</source>
+        <translation>逻辑</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="66"/>
+        <location filename="../pv/popups/channels.cpp" line="67"/>
+        <source>Analog</source>
+        <translation>模拟</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="68"/>
+        <source>Named</source>
+        <translation>更名过</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="69"/>
+        <source>Unnamed</source>
+        <translation>未更名</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="70"/>
+        <source>Changing</source>
+        <translation>有数据的</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="71"/>
+        <source>Non-changing</source>
+        <translation>无数据的</translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="141"/>
+        <source>Disable: </source>
+        <translation>禁用: </translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="149"/>
+        <source>Enable: </source>
+        <translation>启用: </translation>
+    </message>
+    <message>
+        <location filename="../pv/popups/channels.cpp" line="286"/>
+        <location filename="../pv/popups/channels.cpp" line="306"/>
+        <source>None</source>
+        <translation>全关</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Bool</name>
+    <message>
+        <location filename="../pv/prop/bool.cpp" line="51"/>
+        <location filename="../pv/prop/bool.cpp" line="82"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Double</name>
+    <message>
+        <location filename="../pv/prop/double.cpp" line="65"/>
+        <location filename="../pv/prop/double.cpp" line="96"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Enum</name>
+    <message>
+        <location filename="../pv/prop/enum.cpp" line="113"/>
+        <location filename="../pv/prop/enum.cpp" line="176"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::Int</name>
+    <message>
+        <location filename="../pv/prop/int.cpp" line="63"/>
+        <location filename="../pv/prop/int.cpp" line="127"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::prop::String</name>
+    <message>
+        <location filename="../pv/prop/string.cpp" line="59"/>
+        <location filename="../pv/prop/string.cpp" line="84"/>
+        <source>Querying config key %1 resulted in %2</source>
+        <translation>查询配置 &apos;%1&apos;: %2</translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::DecoderCollectionModel</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="40"/>
+        <source>Decoder</source>
+        <translation>解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="41"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="42"/>
+        <source>ID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/model.cpp" line="49"/>
+        <source>All Decoders</source>
+        <translation>所有解码器</translation>
+    </message>
+</context>
+<context>
+    <name>pv::subwindows::decoder_selector::SubWindow</name>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="49"/>
+        <source>Select a decoder to see its description here.</source>
+        <translation>选择解码器以在此处查看其说明。</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="248"/>
+        <source>, %1</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="265"/>
+        <source>&lt;p align=&apos;right&apos;&gt;Tags: %1&lt;/p&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="312"/>
+        <source>Protocol decoder &lt;b&gt;%1&lt;/b&gt; requires input type &lt;b&gt;%2&lt;/b&gt; which several decoders provide.&lt;br&gt;Choose which one to use:&lt;br&gt;</source>
+        <translation>协议解码器 &lt;b&gt;%1&lt;/b&gt; 有不同的&lt;b&gt;%2&lt;/b&gt; 协议类型。&lt;br&gt;选择要使用的:&lt;br&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/subwindows/decoder_selector/subwindow.cpp" line="320"/>
+        <source>Choose Decoder</source>
+        <translation>选择解码器</translation>
+    </message>
+</context>
+<context>
+    <name>pv::toolbars::MainBar</name>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="124"/>
+        <source>New &amp;View</source>
+        <translation>新建视图</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="130"/>
+        <source>&amp;Open...</source>
+        <translation>打开... ( &amp;O )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="141"/>
+        <source>Restore Session Setu&amp;p...</source>
+        <translation>从文件打开会话设置...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="145"/>
+        <source>&amp;Save...</source>
+        <translation>保存... ( &amp;S )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="156"/>
+        <source>Save &amp;As...</source>
+        <translation>另存为... ( &amp;A )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="162"/>
+        <source>Save Selected &amp;Range As...</source>
+        <translation>将所选范围另存为... ( &amp;R )</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="173"/>
+        <source>Save Session Setu&amp;p...</source>
+        <translation>保存会话设置...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="179"/>
+        <source>&amp;Export</source>
+        <translation>导出</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="185"/>
+        <source>&amp;Import</source>
+        <translation>导入</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="189"/>
+        <source>&amp;Connect to Device...</source>
+        <translation>连接到设备...</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="251"/>
+        <source>Add protocol decoder</source>
+        <translation>添加协议解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="261"/>
+        <source>Add math signal</source>
+        <translation>添加 math 信号</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="277"/>
+        <source>Configure Device</source>
+        <translation>设备配置</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="281"/>
+        <source>Configure Channels</source>
+        <translation>通道配置</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="395"/>
+        <source>Failed to get sample rate list:</source>
+        <translation>无法获取采样率列表:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="458"/>
+        <source>Failed to get sample rate:</source>
+        <translation>无法获取采样率:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="499"/>
+        <source>Failed to get sample limit list:</source>
+        <translation>无法获取采样限制列表:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="589"/>
+        <source>Failed to configure samplerate:</source>
+        <translation>无法配置采样率:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="616"/>
+        <source>Failed to configure sample count:</source>
+        <translation>无法配置采样计数:</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
+        <source>Missing Cursors</source>
+        <translation>缺少光标</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="654"/>
+        <source>You need to set the cursors before you can save the data enclosed by them to a session file (e.g. using the Show Cursors button).</source>
+        <translation>您需要设置光标,然后才能将光标范围内的数据保存到会话文件中(例如,使用“显示光标”按钮)。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>Invalid Range</source>
+        <translation>无效的范围</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="672"/>
+        <source>The cursors don&apos;t define a valid range of samples.</source>
+        <translation>光标没有定义有效的采样范围。</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="684"/>
+        <source>%1 files </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="692"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="742"/>
+        <source>All Files</source>
+        <translation>所有文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="696"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <source>Save File</source>
+        <translation>保存文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="708"/>
+        <source>Export %1</source>
+        <translation>导出 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="739"/>
+        <source>%1 files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="750"/>
+        <source>Import File</source>
+        <translation>导入文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="759"/>
+        <source>Import %1</source>
+        <translation>导入 %1</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
+        <source>Open File</source>
+        <translation>打开文件</translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="832"/>
+        <source>sigrok Sessions (*.sr);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="873"/>
+        <location filename="../pv/toolbars/mainbar.cpp" line="890"/>
+        <source>PulseView Session Setups (*.pvs);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/toolbars/mainbar.cpp" line="958"/>
+        <source>Total sampling time: %1</source>
+        <translation>总采样时间: %1</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::decoder_binary::View</name>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="83"/>
+        <source>Decoder:</source>
+        <translation>解码器:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="87"/>
+        <source>Show data as</source>
+        <translation>数据显示格式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="93"/>
+        <source>Hexdump</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="110"/>
+        <source>&amp;Save...</source>
+        <translation>保存... ( &amp;S )</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
+        <source>Save Binary Data</source>
+        <translation>保存二进制数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="270"/>
+        <source>Binary Data Files (*.bin);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="289"/>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="349"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/decoder_binary/view.cpp" line="310"/>
+        <source>Hex Dumps (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::AnnotationCollectionModel</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="56"/>
+        <source>Sample</source>
+        <translation>采样</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="57"/>
+        <source>Time</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="58"/>
+        <source>Decoder</source>
+        <translation>解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="59"/>
+        <source>Ann Row</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="60"/>
+        <source>Ann Class</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="61"/>
+        <source>Value</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>s</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/model.cpp" line="83"/>
+        <source>sa</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::tabular_decoder::View</name>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="176"/>
+        <source>Decoder:</source>
+        <translation>解码器:</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="198"/>
+        <source>Hide Hidden Rows/Classes</source>
+        <translation>隐藏隐藏的行/列</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="202"/>
+        <source>&amp;Save...</source>
+        <translation>保存... ( &amp;S )</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
+        <source>Save Annotations as CSV</source>
+        <translation>将解码数据另存为CSV</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="378"/>
+        <source>CSV Files (*.csv);;Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/tabular_decoder/view.cpp" line="446"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::AnalogSignal</name>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="802"/>
+        <source>Number of pos vertical divs</source>
+        <translation>正区间div垂直细分网格数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="810"/>
+        <source>Number of neg vertical divs</source>
+        <translation>负区间div垂直细分网格数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="815"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="819"/>
+        <source>Div height</source>
+        <translation>Div高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="837"/>
+        <source>V/div</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="841"/>
+        <source>Vertical resolution</source>
+        <translation>垂直分辨率</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="850"/>
+        <source>Autoranging</source>
+        <translation>自动量程</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="855"/>
+        <source>none</source>
+        <translation>无</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="857"/>
+        <source>to logic via threshold</source>
+        <translation>通过阈值转换为逻辑</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="859"/>
+        <source>to logic via schmitt-trigger</source>
+        <translation>通过施密特触发器转换为逻辑</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="865"/>
+        <source>Conversion</source>
+        <translation>转换</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="874"/>
+        <source>Conversion threshold(s)</source>
+        <translation>转换阈值</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="884"/>
+        <source>analog</source>
+        <translation>模拟</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="885"/>
+        <source>converted</source>
+        <translation>转换</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="886"/>
+        <source>analog+converted</source>
+        <translation>模拟+转换</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/analogsignal.cpp" line="891"/>
+        <source>Show traces for</source>
+        <translation>显示采集</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Cursor</name>
+    <message>
+        <location filename="../pv/views/trace/cursor.cpp" line="97"/>
+        <source>Disable snapping</source>
+        <translation>禁用捕捉</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::CursorPair</name>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="128"/>
+        <source>Display interval</source>
+        <translation>显示间隔</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="140"/>
+        <source>Display frequency</source>
+        <translation>显示频率</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/cursorpair.cpp" line="152"/>
+        <source>Display samples</source>
+        <translation>显示采样</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::DecodeTrace</name>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="456"/>
+        <source>&lt;p&gt;&lt;i&gt;No decoders in the stack&lt;/i&gt;&lt;/p&gt;</source>
+        <translation>&lt;p&gt;&lt;i&gt;堆叠中没有解码器&lt;/i&gt;&lt;/p&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="467"/>
+        <source>&lt;i&gt;* Required channels&lt;/i&gt;</source>
+        <translation>&lt;i&gt;* 所需通道&lt;/i&gt;</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="471"/>
+        <source>Stack Decoder</source>
+        <translation>堆叠解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="472"/>
+        <source>Stack a higher-level decoder on top of this one</source>
+        <translation>在这之上面堆叠一个更高级别的解码器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="486"/>
+        <source>Delete</source>
+        <translation>删除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="528"/>
+        <source>Resume decoding</source>
+        <translation>继续解码</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="535"/>
+        <source>Pause decoding</source>
+        <translation>暂停解码</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="543"/>
+        <source>Copy annotation text to clipboard</source>
+        <translation>将解码数据复制到剪贴板</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="552"/>
+        <source>Export all annotations</source>
+        <translation>导出所有解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="559"/>
+        <source>Export all annotations for this row</source>
+        <translation>导出此行的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="568"/>
+        <source>Export all annotations, starting here</source>
+        <translation>导出从这以后所有的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="575"/>
+        <source>Export annotations for this row, starting here</source>
+        <translation>导出从这以后此行的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="584"/>
+        <source>Export all annotations within cursor range</source>
+        <translation>导出光标范围内所有的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="591"/>
+        <source>Export annotations for this row within cursor range</source>
+        <translation>导出光标范围内此行的解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1079"/>
+        <source>%1:
+%2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1123"/>
+        <source>&lt;b&gt;%1&lt;/b&gt; (%2) %3</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
+        <source>Export annotations</source>
+        <translation>导出解码数据</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1192"/>
+        <source>Text Files (*.txt);;All Files (*)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1257"/>
+        <source>File %1 could not be written to.</source>
+        <translation>文件 %1 无法保存。</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1324"/>
+        <source>Show this row</source>
+        <translation>显示此行</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1335"/>
+        <source>Show All</source>
+        <translation>全部显示</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/decodetrace.cpp" line="1343"/>
+        <source>Hide All</source>
+        <translation>全部隐藏</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Flag</name>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="132"/>
+        <source>Text</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="141"/>
+        <source>Delete</source>
+        <translation>删除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/flag.cpp" line="146"/>
+        <source>Disable snapping</source>
+        <translation>禁用捕捉</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Header</name>
+    <message>
+        <location filename="../pv/views/trace/header.cpp" line="137"/>
+        <source>Group</source>
+        <translation>组</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::LogicSignal</name>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="423"/>
+        <source>No trigger</source>
+        <translation>无触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="428"/>
+        <source>Trigger on rising edge</source>
+        <translation>上升沿触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="433"/>
+        <source>Trigger on high level</source>
+        <translation>高电平触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="438"/>
+        <source>Trigger on falling edge</source>
+        <translation>下降沿触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="443"/>
+        <source>Trigger on low level</source>
+        <translation>低电平触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="448"/>
+        <source>Trigger on rising or falling edge</source>
+        <translation>上升沿或下降沿触发</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="535"/>
+        <source> pixels</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="539"/>
+        <source>Trace height</source>
+        <translation>通道高度</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/logicsignal.cpp" line="563"/>
+        <source>Trigger</source>
+        <translation>触发</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathEditDialog</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="88"/>
+        <source>Math Expression Editor</source>
+        <translation>Math 表达式编辑器</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="93"/>
+        <source>Inputs:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="99"/>
+        <source>Variables:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="101"/>
+        <source>Basic operators:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="108"/>
+        <source>Assignments:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="118"/>
+        <source>General purpose functions:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="119"/>
+        <source>abs(x)         Absolute value of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="120"/>
+        <source>avg(x, y, ...) Average of all input values</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="121"/>
+        <source>ceil(x)                Smallest integer that is greater than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="122"/>
+        <source>clamp(lb, x, ub)       Clamp x in range between lb and ub, where lb &lt; ub</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="123"/>
+        <source>equal(x, y)    Equality test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="124"/>
+        <source>erf(x)         Error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="125"/>
+        <source>erfc(x)                Complimentary error function of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="126"/>
+        <source>exp(x)         e to the power of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="127"/>
+        <source>expm1(x)       e to the power of x minus 1, where x is very small.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="128"/>
+        <source>floor(x)               Largest integer that is less than or equal to x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="129"/>
+        <source>frac(x)                Fractional portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="130"/>
+        <source>hypot(x)               Hypotenuse of x and y (i.e. sqrt(x*x + y*y))</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="131"/>
+        <source>iclamp(lb, x, ub)      Inverse-clamp x outside of the range lb and ub, where lb &lt; ub.
+               If x is within the range it will snap to the closest bound</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="132"/>
+        <source>inrange(lb, x, ub)     In-range returns true when x is within the range lb and ub, where lb &lt; ub.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="133"/>
+        <source>log(x)         Natural logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="134"/>
+        <source>log10(x)               Base 10 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="138"/>
+        <source>log1p(x)               Natural logarithm of 1 + x, where x is very small</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="139"/>
+        <source>log2(x)                Base 2 logarithm of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="140"/>
+        <source>logn(x)                Base N logarithm of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="141"/>
+        <source>max(x, y, ...) Largest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="142"/>
+        <source>min(x, y, ...) Smallest value of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="143"/>
+        <source>mul(x, y, ...) Product of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="144"/>
+        <source>ncdf(x)                Normal cumulative distribution function</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="145"/>
+        <source>nequal(x, y)   Not-equal test between x and y using normalised epsilon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="146"/>
+        <source>pow(x, y)      x to the power of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="147"/>
+        <source>root(x, n)     Nth-Root of x, where n is a positive integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="148"/>
+        <source>round(x)               Round x to the nearest integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="149"/>
+        <source>roundn(x, n)   Round x to n decimal places, where n &gt; 0 and is an integer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="150"/>
+        <source>sgn(x)         Sign of x; -1 if x &lt; 0, +1 if x &gt; 0, else zero</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="151"/>
+        <source>sqrt(x)                Square root of x, where x &gt;= 0</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="152"/>
+        <source>sum(x, y, ..,) Sum of all the inputs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="153"/>
+        <source>swap(x, y)     Swap the values of the variables x and y and return the current value of y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="154"/>
+        <source>trunc(x)               Integer portion of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="158"/>
+        <source>Trigonometry functions:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="159"/>
+        <source>acos(x)                Arc cosine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="160"/>
+        <source>acosh(x)               Inverse hyperbolic cosine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="161"/>
+        <source>asin(x)                Arc sine of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="162"/>
+        <source>asinh(x)               Inverse hyperbolic sine of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="163"/>
+        <source>atan(x)                Arc tangent of x expressed in radians. Interval [-1,+1]</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="164"/>
+        <source>atan2(x, y)    Arc tangent of (x / y) expressed in radians. [-pi,+pi]  </source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="165"/>
+        <source>atanh(x)               Inverse hyperbolic tangent of x expressed in radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="166"/>
+        <source>cos(x)         Cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="167"/>
+        <source>cosh(x)                Hyperbolic cosine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="168"/>
+        <source>cot(x)         Cotangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="169"/>
+        <source>csc(x)         Cosectant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="170"/>
+        <source>sec(x)         Secant of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="171"/>
+        <source>sin(x)         Sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="172"/>
+        <source>sinc(x)                Sine cardinal of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="173"/>
+        <source>sinh(x)                Hyperbolic sine of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="174"/>
+        <source>tan(x)         Tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="175"/>
+        <source>tanh(x)                Hyperbolic tangent of x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="176"/>
+        <source>deg2rad(x)     Convert x from degrees to radians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="177"/>
+        <source>deg2grad(x)    Convert x from degrees to gradians</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="178"/>
+        <source>rad2deg(x)     Convert x from radians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="179"/>
+        <source>grad2deg(x)    Convert x from gradians to degrees</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="183"/>
+        <source>Logic operators:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="200"/>
+        <source>Comparisons:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="201"/>
+        <source>x = y or x == y        True only if x is strictly equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="202"/>
+        <source>x &lt;&gt; y or x != y True only if x does not equal y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="203"/>
+        <source>x &lt; y               True only if x is less than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="204"/>
+        <source>x &lt;= y              True only if x is less than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="205"/>
+        <source>x &gt; y               True only if x is greater than y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="206"/>
+        <source>x &gt;= y              True only if x is greater than or equal to y</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="207"/>
+        <source>Flow control:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="208"/>
+        <source>{ ... }                Beginning and end of instruction block</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="209"/>
+        <source>if (x, y, z)   If x is true then return y else return z
+if (x) y;                      variant without implied else
+if (x) { y };          variant with an instruction block
+if (x) y; else z;              variant with explicit else
+if (x) { y } else { z };       variant with instruction blocks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="210"/>
+        <source>x ? y : z      Ternary operator, equivalent to &apos;if (x, y, z)&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="211"/>
+        <source>switch {               The first true case condition that is encountered will
+ case x &gt; 1: a;     determine the result of the switch. If none of the case
+ case x &lt; 1: b;     conditions hold true, the default action is used
+ default:     c;       to determine the return value
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="215"/>
+        <source>while (conditon) {     Evaluates expression repeatedly as long as condition is true,
+ expression;   returning the last value of expression
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="216"/>
+        <source>repeat         Evalues expression repeatedly as long as condition is false,
+ expression;   returning the last value of expression
+until (condition)
+</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="217"/>
+        <source>for (var x := 0; condition; x += 1) {  Repeatedly evaluates expression while the condition is true,
+ expression                    while evaluating the &apos;increment&apos; expression on each loop
+}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="218"/>
+        <source>break          Terminates the execution of the nearest enclosed loop, returning NaN</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="219"/>
+        <source>break[x]               Terminates the execution of the nearest enclosed loop, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="220"/>
+        <source>continue               Interrupts loop execution and resumes with the next loop iteration</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="221"/>
+        <source>return[x]              Returns immediately from within the current expression, returning x</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="222"/>
+        <source>~(expr; expr; ...)     Evaluates each sub-expression and returns the value of the last one
+~{expr; expr; ...}</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="234"/>
+        <source>Copy to expression</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="247"/>
+        <source>Basics</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="248"/>
+        <source>Functions 1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="249"/>
+        <source>Functions 2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="250"/>
+        <source>Trigonometry</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="251"/>
+        <source>Logic</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="252"/>
+        <source>Flow Control 1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="253"/>
+        <source>Flow Control 2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="254"/>
+        <source>Examples</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::MathSignal</name>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="317"/>
+        <source>Expression</source>
+        <translation>表达式</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="321"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="331"/>
+        <source>same as session</source>
+        <translation>与会话相同</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="322"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="332"/>
+        <source>100</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="323"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="333"/>
+        <source>10000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="324"/>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="334"/>
+        <source>1000000</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="327"/>
+        <source>Number of Samples</source>
+        <translation>采样数</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/mathsignal.cpp" line="335"/>
+        <source>Sample rate</source>
+        <translation>采样率</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Ruler</name>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="153"/>
+        <source>Create marker here</source>
+        <translation>在此处创建标记</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="157"/>
+        <source>Set as zero point</source>
+        <translation>设为零点</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="162"/>
+        <source>Reset zero point</source>
+        <translation>重置零点</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Disable mouse hover marker</source>
+        <translation>禁用鼠标位置标记</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/ruler.cpp" line="175"/>
+        <source>Enable mouse hover marker</source>
+        <translation>启用鼠标位置标记</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Signal</name>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="153"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="167"/>
+        <source>Remove</source>
+        <translation>删除</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/signal.cpp" line="169"/>
+        <source>Disable</source>
+        <translation>关闭</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::StandardBar</name>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="54"/>
+        <source>Zoom &amp;In</source>
+        <translation>放大</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="62"/>
+        <source>Zoom &amp;Out</source>
+        <translation>缩小</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="70"/>
+        <source>Zoom to &amp;Fit</source>
+        <translation>适应窗口</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="82"/>
+        <source>Show &amp;Cursors</source>
+        <translation>显示光标</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="85"/>
+        <source>Display last segment only</source>
+        <translation>仅显示最后一段</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="90"/>
+        <source>Display last complete segment only</source>
+        <translation>仅显示最后一个完整分段</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/standardbar.cpp" line="95"/>
+        <source>Display a single segment</source>
+        <translation>仅显示一段</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TimeMarker</name>
+    <message>
+        <location filename="../pv/views/trace/timemarker.cpp" line="191"/>
+        <source>Time</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::Trace</name>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="229"/>
+        <source>Create marker here</source>
+        <translation>在此处创建标记</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="338"/>
+        <source>Color</source>
+        <translation>颜色</translation>
+    </message>
+    <message>
+        <location filename="../pv/views/trace/trace.cpp" line="403"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::TraceGroup</name>
+    <message>
+        <location filename="../pv/views/trace/tracegroup.cpp" line="140"/>
+        <source>Ungroup</source>
+        <translation>取消组合</translation>
+    </message>
+</context>
+<context>
+    <name>pv::views::trace::View</name>
+    <message>
+        <location filename="../pv/views/trace/view.cpp" line="1610"/>
+        <source>Create marker here</source>
+        <translation>在此处创建标记</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DecoderGroupBox</name>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="48"/>
+        <source>Show/hide this decoder trace</source>
+        <translation>显示/隐藏此解码器采集</translation>
+    </message>
+    <message>
+        <location filename="../pv/widgets/decodergroupbox.cpp" line="58"/>
+        <source>Delete this decoder trace</source>
+        <translation>删除此解码器采集</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::DeviceToolButton</name>
+    <message>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="80"/>
+        <location filename="../pv/widgets/devicetoolbutton.cpp" line="87"/>
+        <source>&lt;No Device&gt;</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ExportMenu</name>
+    <message>
+        <location filename="../pv/widgets/exportmenu.cpp" line="71"/>
+        <source>Export %1...</source>
+        <translation>导出 %1...</translation>
+    </message>
+</context>
+<context>
+    <name>pv::widgets::ImportMenu</name>
+    <message>
+        <location filename="../pv/widgets/importmenu.cpp" line="68"/>
+        <source>Import %1...</source>
+        <translation>导入 %1...</translation>
+    </message>
+</context>
+</TS>
index 3a5ac29fc2bca41ae597f812ee90d88040b2a02f..0867187c57b23815dc6bb0de32efbe16001233cf 100644 (file)
--- a/main.cpp
+++ b/main.cpp
 
 #ifdef _WIN32
 #include <QtPlugin>
+#ifdef QT_STATIC
 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
 Q_IMPORT_PLUGIN(QSvgPlugin)
 #endif
+#endif
 
 using std::exception;
 using std::ifstream;
@@ -330,7 +332,7 @@ int main(int argc, char *argv[])
                // Create the device manager, initialise the drivers
                pv::DeviceManager device_manager(context, driver, do_scan);
 
-               a.collect_version_info(context);
+               a.collect_version_info(device_manager);
                if (show_version) {
                        a.print_version_info();
                } else {
index c425f36b3bdd7eb3cf246b6c593a97218a8edcfd..3b881a56d6f3e61216742750a3bdcc48feb294fe 100644 (file)
@@ -19,6 +19,8 @@
 
 cmake_minimum_required(VERSION 2.8.12)
 
+project(PV_MANUAL)
+
 # External dependencies, required and optional tools.
 find_program(ASCIIDOCTOR_EXECUTABLE NAMES asciidoctor)
 find_program(ASCIIDOCTOR_PDF_EXECUTABLE NAMES asciidoctor-pdf)
@@ -34,6 +36,22 @@ set(MANUAL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/manual.txt")
 set(MANUAL_OUT_HTML "${CMAKE_CURRENT_BINARY_DIR}/manual.html")
 set(MANUAL_OUT_PDF "${CMAKE_CURRENT_BINARY_DIR}/manual.pdf")
 
+# Make in-source images/ content available to the output hierarchy for
+# the inspection of created output documents during development in the
+# case of out-of-source build configurations.
+if (NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/images")
+       message(STATUS "creating symlink for manual's images/ subdirectory")
+       execute_process(
+               COMMAND ${CMAKE_COMMAND} -E create_symlink
+                       "${CMAKE_CURRENT_SOURCE_DIR}/images"
+                       "${CMAKE_CURRENT_BINARY_DIR}/images"
+               RESULT_VARIABLE IMAGES_LINK_RESULT
+       )
+       if (NOT IMAGES_LINK_RESULT EQUAL 0)
+               message(WARNING "manual rendering will lack images")
+       endif ()
+endif ()
+
 # Manual related make(1) targets.
 add_custom_target(manual-html
        COMMAND ${ASCIIDOCTOR_EXECUTABLE}
@@ -57,7 +75,7 @@ if (ASCIIDOCTOR_PDF_EXECUTABLE)
                BYPRODUCTS ${MANUAL_OUT_PDF}
                DEPENDS ${MANUAL_SRC}
                WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-               COMMENT "Generating manual, HTML output"
+               COMMENT "Generating manual, PDF output"
        )
 else ()
        add_custom_target(manual-pdf
index 19baff63157442dddefa605b8a7600a0225601d1..11983e5f2662fcfc6c5a23be8d9341bbaa377886 100644 (file)
@@ -1,6 +1,6 @@
 PulseView User Manual
 =====================
-unreleased development snapshot, dated 2020-02-29
+unreleased pulseview development snapshot, manual last changed 2020-02-29
 :doctype: book
 :imagesdir: ./images
 :sectnums:
index 3a0ac0f13a5dc7986ca737615d9858998f775055..cce6393c39054fb4a961fdc1bd789863e9495d69 100644 (file)
@@ -69,7 +69,7 @@ Application::Application(int &argc, char* argv[]) :
 
 const QStringList Application::get_languages() const
 {
-       QStringList files = QDir(":/l10n/").entryList(QStringList("*.qm"), QDir::Files);
+       const QStringList files = QDir(":/l10n/").entryList(QStringList("*.qm"), QDir::Files);
 
        QStringList result;
        result << "en";  // Add default language to the set
@@ -86,7 +86,9 @@ const QStringList Application::get_languages() const
 const QString Application::get_language_editors(const QString& language) const
 {
        if (language == "de") return "Sören Apel, Uwe Hermann";
-       if (language == "es_mx") return "Carlos Diaz";
+       if (language == "es_MX") return "Carlos Diaz, Ulices Avila Hernandez";
+       if (language == "ja_jp") return "Yukari Shoji";
+       if (language == "zh_cn") return "ZtyPro";
 
        return QString();
 }
@@ -106,7 +108,11 @@ void Application::switch_language(const QString& language)
                        qWarning() << "Translation resource" << resource << "not found";
 
                // Qt translations
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               QString tr_path(QLibraryInfo::path(QLibraryInfo::TranslationsPath));
+#else
                QString tr_path(QLibraryInfo::location(QLibraryInfo::TranslationsPath));
+#endif
 
                if (qt_translator_.load("qt_" + language, tr_path))
                        installTranslator(&qt_translator_);
@@ -143,7 +149,7 @@ void Application::on_setting_changed(const QString &key, const QVariant &value)
                switch_language(value.toString());
 }
 
-void Application::collect_version_info(shared_ptr<sigrok::Context> context)
+void Application::collect_version_info(pv::DeviceManager &device_manager)
 {
        // Library versions and features
        version_info_.emplace_back(applicationName(), applicationVersion());
@@ -211,17 +217,18 @@ void Application::collect_version_info(shared_ptr<sigrok::Context> context)
 #endif
 
        // Device drivers
-       for (auto& entry : context->drivers())
-               driver_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
-                       QString::fromUtf8(entry.second->long_name().c_str()));
+       for (auto& entry : device_manager.context()->drivers())
+               if (device_manager.driver_supported(entry.second))
+                       driver_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
+                               QString::fromUtf8(entry.second->long_name().c_str()));
 
        // Input formats
-       for (auto& entry : context->input_formats())
+       for (auto& entry : device_manager.context()->input_formats())
                input_format_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
                        QString::fromUtf8(entry.second->description().c_str()));
 
        // Output formats
-       for (auto& entry : context->output_formats())
+       for (auto& entry : device_manager.context()->output_formats())
                output_format_list_.emplace_back(QString::fromUtf8(entry.first.c_str()),
                        QString::fromUtf8(entry.second->description().c_str()));
 
index 75ddbc0603a5b62936caa028def364dd85b38fa7..bfe4657009ae82407e139261914eb22ff461d2c9 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <libsigrokcxx/libsigrokcxx.hpp>
 
+#include "devicemanager.hpp"
 #include "globalsettings.hpp"
 
 using std::shared_ptr;
@@ -47,7 +48,7 @@ public:
 
        void on_setting_changed(const QString &key, const QVariant &value);
 
-       void collect_version_info(shared_ptr<sigrok::Context> context);
+       void collect_version_info(pv::DeviceManager &device_manager);
        void print_version_info();
 
        vector< pair<QString, QString> > get_version_info() const;
index 7bbc1bbf52839618a3e5905a82bf29fb1794689c..855052cddc7e909a7c77c62783f50154ea702dd1 100644 (file)
@@ -74,7 +74,9 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
 
                        // Ignore common read-only keys
                        if ((key->id() == SR_CONF_CONTINUOUS) || (key->id() == SR_CONF_TRIGGER_MATCH) ||
-                           (key->id() == SR_CONF_CONN) || (key->id() == SR_CONF_SERIALCOMM))
+                           (key->id() == SR_CONF_CONN) || (key->id() == SR_CONF_SERIALCOMM) || (key->id() == SR_CONF_NUM_LOGIC_CHANNELS) ||
+                           (key->id() == SR_CONF_NUM_ANALOG_CHANNELS) || (key->id() == SR_CONF_SESSIONFILE) || (key->id() == SR_CONF_CAPTUREFILE) ||
+                           (key->id() == SR_CONF_CAPTURE_UNITSIZE))
                                continue;
 
                        qDebug() << QString(tr("Note for device developers: Ignoring device configuration capability '%1' " \
@@ -121,6 +123,7 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
                case SR_CONF_RLE:
                case SR_CONF_POWER_OFF:
                case SR_CONF_AVERAGING:
+               case SR_CONF_CONTINUOUS:
                        bind_bool(descr, "", get, set);
                        break;
 
index 9010097cee28bd9f314de88d378eaee8de6a56cf..da9736b53fe29210d808fbb54a86c06c64889833 100644 (file)
@@ -64,9 +64,11 @@ uint32_t Analog::get_segment_count() const
 
 void Analog::clear()
 {
-       segments_.clear();
+       if (!segments_.empty()) {
+               segments_.clear();
 
-       samples_cleared();
+               samples_cleared();
+       }
 }
 
 void Analog::set_samplerate(double value)
index 630dc9db435bf36617aca313d220de8126a4ebdf..560732f8bef8bebf238e948b37249ccd8d4f5141 100644 (file)
@@ -73,8 +73,6 @@ Q_SIGNALS:
 
        void min_max_changed(float min, float max);
 
-       void segment_completed();
-
 private Q_SLOTS:
        void on_segment_completed();
 
index 7f2334788f70f93c2a734d680c90d6a8b957d293..f8a5c47b66236a8be66327a2c9ea05568970749f 100644 (file)
@@ -17,9 +17,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-extern "C" {
 #include <libsigrokdecode/libsigrokdecode.h>
-}
 
 #include <cassert>
 #include <vector>
index f644b8af3a3356e03db91af8b948119e2cb4be17..ebcbc1c9bfa45a2c25e297becd0e243910f90596 100644 (file)
@@ -327,6 +327,26 @@ void Decoder::on_class_visibility_changed()
        annotation_visibility_changed();
 }
 
+bool Decoder::has_logic_output() const
+{
+       return (srd_decoder_->logic_output_channels != nullptr);
+}
+
+const vector<DecoderLogicOutputChannel> Decoder::logic_output_channels() const
+{
+       vector<DecoderLogicOutputChannel> result;
+
+       for (GSList *l = srd_decoder_->logic_output_channels; l; l = l->next) {
+               const srd_decoder_logic_output_channel* ch_data =
+                       (srd_decoder_logic_output_channel*)l->data;
+
+               result.emplace_back(QString::fromUtf8(ch_data->id),
+                       QString::fromUtf8(ch_data->desc));
+       }
+
+       return result;
+}
+
 }  // namespace decode
 }  // namespace data
 }  // namespace pv
index 86a371f26c67c11d72abfe80a3715cf2292921ae..ac1052e62c9dfcb1cc270455686bfc0b619e003f 100644 (file)
@@ -89,6 +89,12 @@ struct DecodeChannel
        const srd_channel *pdch_;
 };
 
+struct DecoderLogicOutputChannel {
+       DecoderLogicOutputChannel (QString id, QString desc) :
+               id(id), desc(desc) {};
+       QString id, desc;
+};
+
 struct DecodeBinaryClassInfo
 {
        uint32_t bin_class_id;
@@ -139,6 +145,9 @@ public:
        uint32_t get_binary_class_count() const;
        const DecodeBinaryClassInfo* get_binary_class(uint32_t id) const;
 
+       bool has_logic_output() const;
+       const vector<DecoderLogicOutputChannel> logic_output_channels() const;
+
 Q_SIGNALS:
        void annotation_visibility_changed();
 
index d127a2eadaf3045180452a148777f33cb30c9f08..6212c92195bb888838348122f14624d0e83f2edc 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <cassert>
+#include <QDebug>
 
 #include "decoder.hpp"
 #include "row.hpp"
@@ -149,17 +150,31 @@ const QColor Row::color() const
 
 const QColor Row::get_class_color(uint32_t ann_class_id) const
 {
-       return ann_class_color_.at(ann_class_id);
+       try {
+               return ann_class_color_.at(ann_class_id);
+       } catch (std::out_of_range &e) {
+               qWarning() << "Warning: annotation type" << decoder_->get_ann_class_by_id(ann_class_id)->name
+                       << "(" << ann_class_id << ")" << "not assigned to any annotation row!";
+               return QColor(20, 20, 20);
+       }
 }
 
 const QColor Row::get_bright_class_color(uint32_t ann_class_id) const
 {
-       return ann_bright_class_color_.at(ann_class_id);
+       try {
+               return ann_bright_class_color_.at(ann_class_id);
+       } catch (std::out_of_range &e) {
+               return QColor(20, 20, 20);
+       }
 }
 
 const QColor Row::get_dark_class_color(uint32_t ann_class_id) const
 {
-       return ann_dark_class_color_.at(ann_class_id);
+       try {
+               return ann_dark_class_color_.at(ann_class_id);
+       } catch (std::out_of_range &e) {
+               return QColor(20, 20, 20);
+       }
 }
 
 bool Row::has_hidden_classes() const
index 640e58148ef751e3aa7557dca2d7af5d526c2240..9c695a1baa8247dfa09be12c8cdd9b2ee5956d34 100644 (file)
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "config.h"
+
 #include <cstring>
 #include <forward_list>
 #include <limits>
 
 #include <QDebug>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QRegularExpression>
+#endif
 
 #include "logic.hpp"
 #include "logicsegment.hpp"
@@ -33,6 +38,7 @@
 #include <pv/globalsettings.hpp>
 #include <pv/session.hpp>
 
+using std::dynamic_pointer_cast;
 using std::lock_guard;
 using std::make_shared;
 using std::min;
@@ -67,6 +73,20 @@ DecodeSignal::~DecodeSignal()
        reset_decode(true);
 }
 
+void DecodeSignal::set_name(QString name)
+{
+       SignalBase::set_name(name);
+
+       update_output_signals();
+}
+
+void DecodeSignal::set_color(QColor color)
+{
+       SignalBase::set_color(color);
+
+       update_output_signals();
+}
+
 const vector< shared_ptr<Decoder> >& DecodeSignal::decoder_stack() const
 {
        return stack_;
@@ -95,6 +115,7 @@ void DecodeSignal::stack_decoder(const srd_decoder *decoder, bool restart_decode
        stack_config_changed_ = true;
        auto_assign_signals(dec);
        commit_decoder_channels();
+       update_output_signals();
 
        decoder_stacked((void*)dec.get());
 
@@ -166,6 +187,10 @@ void DecodeSignal::reset_decode(bool shutting_down)
        current_segment_id_ = 0;
        segments_.clear();
 
+       for (const shared_ptr<decode::Decoder>& dec : stack_)
+               if (dec->has_logic_output())
+                       output_logic_[dec->get_srd_decoder()]->clear();
+
        logic_mux_data_.reset();
        logic_mux_data_invalid_ = true;
 
@@ -231,9 +256,6 @@ void DecodeSignal::begin_decode()
                logic_mux_data_ = make_shared<Logic>(ch_count);
        }
 
-       // Receive notifications when new sample data is available
-       connect_input_notifiers();
-
        if (get_input_segment_count() == 0)
                set_error_message(tr("No input data"));
 
@@ -274,6 +296,9 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
 {
        bool new_assignment = false;
 
+       // Disconnect all input signal notifications so we don't have duplicate connections
+       disconnect_input_notifiers();
+
        // Try to auto-select channels that don't have signals assigned yet
        for (decode::DecodeChannel& ch : channels_) {
                // If a decoder is given, auto-assign only its channels
@@ -284,7 +309,11 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
                        continue;
 
                QString ch_name = ch.name.toLower();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               ch_name = ch_name.replace(QRegularExpression("[-_.]"), " ");
+#else
                ch_name = ch_name.replace(QRegExp("[-_.]"), " ");
+#endif
 
                shared_ptr<data::SignalBase> match;
                for (const shared_ptr<data::SignalBase>& s : session_.signalbases()) {
@@ -292,7 +321,11 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
                                continue;
 
                        QString s_name = s->name().toLower();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       s_name = s_name.replace(QRegularExpression("[-_.]"), " ");
+#else
                        s_name = s_name.replace(QRegExp("[-_.]"), " ");
+#endif
 
                        if (s->logic_data() &&
                                ((ch_name.contains(s_name)) || (s_name.contains(ch_name)))) {
@@ -321,6 +354,9 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
        }
 
        if (new_assignment) {
+               // Receive notifications when new sample data is available
+               connect_input_notifiers();
+
                logic_mux_data_invalid_ = true;
                stack_config_changed_ = true;
                commit_decoder_channels();
@@ -330,12 +366,18 @@ void DecodeSignal::auto_assign_signals(const shared_ptr<Decoder> dec)
 
 void DecodeSignal::assign_signal(const uint16_t channel_id, shared_ptr<const SignalBase> signal)
 {
+       // Disconnect all input signal notifications so we don't have duplicate connections
+       disconnect_input_notifiers();
+
        for (decode::DecodeChannel& ch : channels_)
                if (ch.id == channel_id) {
                        ch.assigned_signal = signal;
                        logic_mux_data_invalid_ = true;
                }
 
+       // Receive notifications when new sample data is available
+       connect_input_notifiers();
+
        stack_config_changed_ = true;
        commit_decoder_channels();
        channels_updated();
@@ -349,6 +391,62 @@ int DecodeSignal::get_assigned_signal_count() const
                [](decode::DecodeChannel ch) { return ch.assigned_signal.get(); });
 }
 
+void DecodeSignal::update_output_signals()
+{
+       for (const shared_ptr<decode::Decoder>& dec : stack_) {
+               assert(dec);
+
+               if (dec->has_logic_output()) {
+                       const vector<decode::DecoderLogicOutputChannel> logic_channels =
+                               dec->logic_output_channels();
+
+                       // All signals of a decoder share the same LogicSegment, so it's
+                       // sufficient to check for only the first channel
+                       const decode::DecoderLogicOutputChannel& first_ch = logic_channels[0];
+
+                       bool ch_exists = false;
+                       for (const shared_ptr<SignalBase>& signal : output_signals_)
+                               if (signal->internal_name() == first_ch.id)
+                                       ch_exists = true;
+
+                       if (!ch_exists) {
+                               shared_ptr<Logic> logic_data = make_shared<Logic>(logic_channels.size());
+                               logic_data->set_samplerate(get_samplerate());
+                               output_logic_[dec->get_srd_decoder()] = logic_data;
+                               output_logic_muxed_data_[dec->get_srd_decoder()] = vector<uint8_t>();
+
+                               shared_ptr<LogicSegment> logic_segment = make_shared<data::LogicSegment>(
+                                       *logic_data, 0, (logic_data->num_channels() + 7) / 8, get_samplerate());
+                               logic_data->push_segment(logic_segment);
+
+                               uint index = 0;
+                               for (const decode::DecoderLogicOutputChannel& logic_ch : logic_channels) {
+                                       shared_ptr<data::SignalBase> signal =
+                                               make_shared<data::SignalBase>(nullptr, LogicChannel);
+                                       signal->set_internal_name(logic_ch.id);
+                                       signal->set_index(index);
+                                       signal->set_data(logic_data);
+                                       output_signals_.push_back(signal);
+                                       session_.add_generated_signal(signal);
+                                       index++;
+                               }
+                       } else {
+                               shared_ptr<Logic> logic_data = output_logic_[dec->get_srd_decoder()];
+                               logic_data->set_samplerate(get_samplerate());
+                               for (shared_ptr<LogicSegment>& segment : logic_data->logic_segments())
+                                       segment->set_samplerate(get_samplerate());
+                       }
+               }
+       }
+
+       for (shared_ptr<SignalBase> s : output_signals_) {
+               s->set_name(s->internal_name() + " (" + name() + ")");
+               s->set_color(color());
+       }
+
+       // TODO Assert that all sample rates are the same as the session's
+}
+
 void DecodeSignal::set_initial_pin_state(const uint16_t channel_id, const int init_state)
 {
        for (decode::DecodeChannel& ch : channels_)
@@ -463,7 +561,6 @@ vector<const Row*> DecodeSignal::get_rows(bool visible_only) const
        return rows;
 }
 
-
 uint64_t DecodeSignal::get_annotation_count(const Row* row, uint32_t segment_id) const
 {
        if (segment_id >= segments_.size())
@@ -661,7 +758,11 @@ void DecodeSignal::save_settings(QSettings &settings) const
        for (const shared_ptr<Decoder>& decoder : stack_) {
                settings.beginGroup("decoder" + QString::number(decoder_idx++));
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               settings.setValue("id", (const char *)decoder->get_srd_decoder()->id);
+#else
                settings.setValue("id", decoder->get_srd_decoder()->id);
+#endif
                settings.setValue("visible", decoder->visible());
 
                // Save decoder options
@@ -723,6 +824,8 @@ void DecodeSignal::save_settings(QSettings &settings) const
 
                settings.endGroup();
        }
+
+       // TODO Save logic output signal settings
 }
 
 void DecodeSignal::restore_settings(QSettings &settings)
@@ -821,10 +924,15 @@ void DecodeSignal::restore_settings(QSettings &settings)
                settings.endGroup();
        }
 
+       connect_input_notifiers();
+
        // Update the internal structures
        stack_config_changed_ = true;
        update_channel_list();
        commit_decoder_channels();
+       update_output_signals();
+
+       // TODO Restore logic output signal settings
 
        begin_decode();
 }
@@ -1164,6 +1272,7 @@ void DecodeSignal::logic_mux_proc()
                                        output_segment->set_complete();
 
                                if (segment_id < get_input_segment_count() - 1) {
+
                                        // Process next segment
                                        segment_id++;
 
@@ -1178,6 +1287,10 @@ void DecodeSignal::logic_mux_proc()
                                        unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
                                        logic_mux_cond_.wait(logic_mux_lock);
                                }
+                       } else {
+                               // Input segments aren't all complete yet but samples_to_process is 0, wait for more input data
+                               unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
+                               logic_mux_cond_.wait(logic_mux_lock);
                        }
                }
        } while (!logic_mux_interrupt_);
@@ -1277,6 +1390,14 @@ void DecodeSignal::decode_proc()
 
                        // If the input segment is complete, we've exhausted this segment
                        if (input_segment->is_complete()) {
+#if defined HAVE_SRD_SESSION_SEND_EOF && HAVE_SRD_SESSION_SEND_EOF
+                               // Tell protocol decoders about the end of
+                               // the input data, which may result in more
+                               // annotations being emitted
+                               (void)srd_session_send_eof(srd_session_);
+                               new_annotations();
+#endif
+
                                if (current_segment_id_ < (logic_mux_data_->logic_segments().size() - 1)) {
                                        // Process next segment
                                        current_segment_id_++;
@@ -1308,6 +1429,10 @@ void DecodeSignal::decode_proc()
                                        unique_lock<mutex> input_wait_lock(input_mutex_);
                                        decode_input_cond_.wait(input_wait_lock);
                                }
+                       } else {
+                               // Input segment isn't complete yet but samples_to_process is 0, wait for more input data
+                               unique_lock<mutex> input_wait_lock(input_mutex_);
+                               decode_input_cond_.wait(input_wait_lock);
                        }
 
                }
@@ -1343,6 +1468,9 @@ void DecodeSignal::start_srd_session()
                return;
        }
 
+       // Update the samplerates for the output logic channels
+       update_output_signals();
+
        // Create the session
        srd_session_new(&srd_session_);
        assert(srd_session_);
@@ -1376,6 +1504,9 @@ void DecodeSignal::start_srd_session()
        srd_pd_output_callback_add(srd_session_, SRD_OUTPUT_BINARY,
                DecodeSignal::binary_callback, this);
 
+       srd_pd_output_callback_add(srd_session_, SRD_OUTPUT_LOGIC,
+               DecodeSignal::logic_output_callback, this);
+
        srd_session_start(srd_session_);
 
        // We just recreated the srd session, so all stack changes are applied now
@@ -1390,6 +1521,9 @@ void DecodeSignal::terminate_srd_session()
        // those stacks which still are processing data while the
        // application no longer wants them to.
        if (srd_session_) {
+#if defined HAVE_SRD_SESSION_SEND_EOF && HAVE_SRD_SESSION_SEND_EOF
+               (void)srd_session_send_eof(srd_session_);
+#endif
                srd_session_terminate_reset(srd_session_);
 
                // Metadata is cleared also, so re-set it
@@ -1419,20 +1553,35 @@ void DecodeSignal::stop_srd_session()
 
 void DecodeSignal::connect_input_notifiers()
 {
-       // Disconnect the notification slot from the previous set of signals
-       disconnect(this, SLOT(on_data_cleared()));
-       disconnect(this, SLOT(on_data_received()));
-
        // Connect the currently used signals to our slot
        for (decode::DecodeChannel& ch : channels_) {
                if (!ch.assigned_signal)
                        continue;
+               const data::SignalBase *signal = ch.assigned_signal.get();
 
+               connect(signal, SIGNAL(samples_cleared()),
+                       this, SLOT(on_data_cleared()), Qt::UniqueConnection);
+               connect(signal, SIGNAL(samples_added(uint64_t, uint64_t, uint64_t)),
+                       this, SLOT(on_data_received()), Qt::UniqueConnection);
+
+               if (signal->logic_data())
+                       connect(signal->logic_data().get(), SIGNAL(segment_completed()),
+                               this, SLOT(on_input_segment_completed()), Qt::UniqueConnection);
+       }
+}
+
+void DecodeSignal::disconnect_input_notifiers()
+{
+       // Disconnect the notification slot from the previous set of signals
+       for (decode::DecodeChannel& ch : channels_) {
+               if (!ch.assigned_signal)
+                       continue;
                const data::SignalBase *signal = ch.assigned_signal.get();
-               connect(signal, &data::SignalBase::samples_cleared,
-                       this, &DecodeSignal::on_data_cleared);
-               connect(signal, &data::SignalBase::samples_added,
-                       this, &DecodeSignal::on_data_received);
+               disconnect(signal, nullptr, this, SLOT(on_data_cleared()));
+               disconnect(signal, nullptr, this, SLOT(on_data_received()));
+
+               if (signal->logic_data())
+                       disconnect(signal->logic_data().get(), nullptr, this, SLOT(on_input_segment_completed()));
        }
 }
 
@@ -1610,6 +1759,77 @@ void DecodeSignal::binary_callback(srd_proto_data *pdata, void *decode_signal)
        ds->new_binary_data(ds->current_segment_id_, (void*)dec, pdb->bin_class);
 }
 
+void DecodeSignal::logic_output_callback(srd_proto_data *pdata, void *decode_signal)
+{
+       assert(pdata);
+       assert(decode_signal);
+
+       DecodeSignal *const ds = (DecodeSignal*)decode_signal;
+       assert(ds);
+
+       if (ds->decode_interrupt_)
+               return;
+
+       lock_guard<mutex> lock(ds->output_mutex_);
+
+       assert(pdata->pdo);
+       assert(pdata->pdo->di);
+       const srd_decoder *const decc = pdata->pdo->di->decoder;
+       assert(decc);
+
+       const srd_proto_data_logic *const pdl = (const srd_proto_data_logic*)pdata->data;
+       assert(pdl);
+
+       // FIXME Only one group supported for now
+       if (pdl->logic_group > 0) {
+               qWarning() << "Received logic output state change for group" << pdl->logic_group << "from decoder" \
+                       << QString::fromUtf8(decc->name) << "but only group 0 is currently supported";
+               return;
+       }
+
+       shared_ptr<Logic> output_logic = ds->output_logic_.at(decc);
+
+       vector< shared_ptr<Segment> > segments = output_logic->segments();
+
+       shared_ptr<LogicSegment> last_segment;
+
+       if (!segments.empty())
+               last_segment = dynamic_pointer_cast<LogicSegment>(segments.back());
+       else {
+               // Happens when the data was cleared - all segments are gone then
+               // segment_id is always 0 as it's the first segment
+               last_segment = make_shared<data::LogicSegment>(
+                       *output_logic, 0, (output_logic->num_channels() + 7) / 8, output_logic->get_samplerate());
+               output_logic->push_segment(last_segment);
+       }
+
+       if (pdata->start_sample < pdata->end_sample) {
+               vector<uint8_t> data;
+               const unsigned int unit_size = last_segment->unit_size();
+               data.resize(unit_size * (1 + pdl->repeat_count));
+
+               if (unit_size == 1)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint8_t*)pdl->data);
+               else if (unit_size == 2)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint16_t*)pdl->data);
+               else if (unit_size <= 4)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint32_t*)pdl->data);
+               else if (unit_size <= 8)
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               data.data()[i * unit_size] = *((uint64_t*)pdl->data);
+               else
+                       for (unsigned int i = 0; i <= pdl->repeat_count; i++)
+                               memcpy((void*)&data.data()[i * unit_size], (void*)pdl->data, unit_size);
+
+               last_segment->append_payload(data.data(), data.size());
+       } else
+               qWarning() << "Ignoring malformed logic output state change for group" << pdl->logic_group << "from decoder" \
+                       << QString::fromUtf8(decc->name) << "from" << pdata->start_sample << "to" << pdata->end_sample;
+}
+
 void DecodeSignal::on_capture_state_changed(int state)
 {
        // If a new acquisition was started, we need to start decoding from scratch
@@ -1627,14 +1847,15 @@ void DecodeSignal::on_data_cleared()
 void DecodeSignal::on_data_received()
 {
        // If we detected a lack of input data when trying to start decoding,
-       // we have set an error message. Only try again if we now have data
+       // we have set an error message. Bail out if we still don't have data
        // to work with
        if ((!error_message_.isEmpty()) && (get_input_segment_count() == 0))
                return;
-       else {
+
+       if (!error_message_.isEmpty()) {
                error_message_.clear();
                // TODO Emulate noquote()
-               qDebug().nospace() << name() << ": Error cleared";
+               qDebug().nospace() << name() << ": Input data available, error cleared";
        }
 
        if (!logic_mux_thread_.joinable())
@@ -1643,6 +1864,12 @@ void DecodeSignal::on_data_received()
                logic_mux_cond_.notify_one();
 }
 
+void DecodeSignal::on_input_segment_completed()
+{
+       if (!logic_mux_thread_.joinable())
+               logic_mux_cond_.notify_one();
+}
+
 void DecodeSignal::on_annotation_visibility_changed()
 {
        annotation_visibility_changed();
index f4783be22706ee18a9ce11b2c23d2b2e17d95b6c..333e953b2d63e66c415b403092ad48cc2975b0f5 100644 (file)
@@ -103,6 +103,16 @@ public:
        DecodeSignal(pv::Session &session);
        virtual ~DecodeSignal();
 
+       /**
+        * Sets the name of the signal.
+        */
+       virtual void set_name(QString name);
+
+       /**
+        * Set the color of the signal.
+        */
+       virtual void set_color(QColor color);
+
        bool is_decode_signal() const;
        const vector< shared_ptr<Decoder> >& decoder_stack() const;
 
@@ -121,6 +131,8 @@ public:
        void assign_signal(const uint16_t channel_id, shared_ptr<const SignalBase> signal);
        int get_assigned_signal_count() const;
 
+       void update_output_signals();
+
        void set_initial_pin_state(const uint16_t channel_id, const int init_state);
 
        virtual double get_samplerate() const;
@@ -210,11 +222,13 @@ private:
        void stop_srd_session();
 
        void connect_input_notifiers();
+       void disconnect_input_notifiers();
 
        void create_decode_segment();
 
        static void annotation_callback(srd_proto_data *pdata, void *decode_signal);
        static void binary_callback(srd_proto_data *pdata, void *decode_signal);
+       static void logic_output_callback(srd_proto_data *pdata, void *decode_signal);
 
 Q_SIGNALS:
        void decoder_stacked(void* decoder); ///< decoder is of type decode::Decoder*
@@ -230,6 +244,7 @@ private Q_SLOTS:
        void on_capture_state_changed(int state);
        void on_data_cleared();
        void on_data_received();
+       void on_input_segment_completed();
 
        void on_annotation_visibility_changed();
 
@@ -258,6 +273,10 @@ private:
        atomic<bool> decode_interrupt_, logic_mux_interrupt_;
 
        bool decode_paused_;
+
+       map<const srd_decoder*, shared_ptr<Logic>> output_logic_;
+       map<const srd_decoder*, vector<uint8_t>> output_logic_muxed_data_;
+       vector< shared_ptr<SignalBase>> output_signals_;
 };
 
 } // namespace data
index d2f89e856cd4ae58a9e682341670b88a8d003c3f..4a13e568bc80ff38770a245b1e9b38b1861d15a9 100644 (file)
@@ -49,6 +49,8 @@ void Logic::push_segment(shared_ptr<LogicSegment> &segment)
 
        if ((samplerate_ == 1) && (segment->samplerate() > 1))
                samplerate_ = segment->samplerate();
+
+       connect(segment.get(), SIGNAL(completed()), this, SLOT(on_segment_completed()));
 }
 
 const deque< shared_ptr<LogicSegment> >& Logic::logic_segments() const
@@ -73,9 +75,11 @@ uint32_t Logic::get_segment_count() const
 
 void Logic::clear()
 {
-       segments_.clear();
+       if (!segments_.empty()) {
+               segments_.clear();
 
-       samples_cleared();
+               samples_cleared();
+       }
 }
 
 void Logic::set_samplerate(double value)
@@ -104,5 +108,10 @@ void Logic::notify_samples_added(shared_ptr<Segment> segment, uint64_t start_sam
        samples_added(segment, start_sample, end_sample);
 }
 
+void Logic::on_segment_completed()
+{
+       segment_completed();
+}
+
 } // namespace data
 } // namespace pv
index b5e532c45e9a0523d13991f39fb8c653d498eca3..07f8222c1946cc699828c6ac3cdc6c3d761eb67d 100644 (file)
@@ -71,6 +71,9 @@ Q_SIGNALS:
        void samples_added(SharedPtrToSegment segment, uint64_t start_sample,
                uint64_t end_sample);
 
+private Q_SLOTS:
+       void on_segment_completed();
+
 private:
        double samplerate_;
        const unsigned int num_channels_;
index be56c1505263fd44c19901ce8b055112f6888378..739b2b9ee2a97652de2d4d4e3530041c1613af2c 100644 (file)
@@ -363,6 +363,31 @@ void LogicSegment::append_payload(void *data, uint64_t data_size)
                        prev_sample_count + 1, prev_sample_count + 1);
 }
 
+void LogicSegment::append_subsignal_payload(unsigned int index, void *data,
+       uint64_t data_size, vector<uint8_t>& destination)
+{
+       if (index == 0)
+               destination.resize(data_size * unit_size_, 0);
+
+       // Set the bits for this sub-signal where needed
+       // Note: the bytes in *data must either be 0 or 1, nothing else
+       unsigned int index_byte_offs = index / 8;
+       uint8_t* output_data = destination.data() + index_byte_offs;
+       uint8_t* input_data = (uint8_t*)data;
+
+       for (uint64_t i = 0; i < data_size; i++) {
+               assert((i * unit_size_ + index_byte_offs) < destination.size());
+               *output_data |= (input_data[i] << index);
+               output_data += unit_size_;
+       }
+
+       if (index == owner_.num_channels() - 1) {
+               // We gathered sample data of all sub-signals, let's append it
+               append_payload(destination.data(), destination.size());
+               destination.clear();
+       }
+}
+
 void LogicSegment::get_samples(int64_t start_sample,
        int64_t end_sample, uint8_t* dest) const
 {
@@ -643,7 +668,7 @@ void LogicSegment::append_payload_to_mipmap()
                else if (unit_size_ == 4)
                        downsampleT<uint32_t>(src_ptr, dest_ptr, count);
                else if (unit_size_ == 8)
-                       downsampleT<uint8_t>(src_ptr, dest_ptr, count);
+                       downsampleT<uint64_t>(src_ptr, dest_ptr, count);
                else
                        downsampleGeneric(src_ptr, dest_ptr, count);
                len_sample -= count;
index 2e37ed2d248df56c22d1e0d1cd703d7835425fcb..35e8eca95f9212091475e2b0bd2bd77fd593e7f8 100644 (file)
@@ -86,6 +86,19 @@ public:
        void append_payload(shared_ptr<sigrok::Logic> logic);
        void append_payload(void *data, uint64_t data_size);
 
+       /**
+        * Appends sample data for a single channel where each byte
+        * represents one sample - if it's 0 the state is low, if 1 high.
+        * Other values are not permitted.
+        * Assumes that all channels are having samples added and in the
+        * order of 0..n, not n..0.
+        * Also assumes the the number of samples added for each channel
+        * is constant for every invokation for 0..n. The number of samples
+        * hence may only change when index is 0.
+        */
+       void append_subsignal_payload(unsigned int index, void *data,
+               uint64_t data_size, vector<uint8_t>& destination);
+
        void get_samples(int64_t start_sample, int64_t end_sample, uint8_t* dest) const;
 
        /**
index f7de2ff29103a0662e1dce62a74cb33a8372203c..adb27131e6d43bc00a89e788cf398c07ed57229a 100644 (file)
@@ -307,8 +307,13 @@ void MathSignal::begin_generation()
                return;
        }
 
-       disconnect(this, SLOT(on_data_received()));
-       disconnect(this, SLOT(on_enabled_changed()));
+       disconnect(&session_, SIGNAL(data_received()), this, SLOT(on_data_received()));
+
+       for (const shared_ptr<SignalBase>& sb : session_.signalbases()) {
+               if (sb->analog_data())
+                       disconnect(sb->analog_data().get(), nullptr, this, SLOT(on_data_received()));
+               disconnect(sb.get(), nullptr, this, SLOT(on_enabled_changed()));
+       }
 
        fnc_sample_ = new fnc_sample<double>(*this);
 
@@ -371,8 +376,7 @@ void MathSignal::begin_generation()
        if (error_message_.isEmpty()) {
                // Connect to the session data notification if we have no input signals
                if (input_signals_.empty())
-                       connect(&session_, SIGNAL(data_received()),
-                               this, SLOT(on_data_received()));
+                       connect(&session_, SIGNAL(data_received()),     this, SLOT(on_data_received()));
 
                gen_interrupt_ = false;
                gen_thread_ = std::thread(&MathSignal::generation_proc, this);
index e40dbfb3cf61125643d945b2b760846f4069efee..98adbe8099b57058ba1c1a3e4fa243e49b8b7439 100644 (file)
@@ -105,13 +105,6 @@ private:
        bool all_input_signals_enabled(QString &disabled_signals) const;
 
 Q_SIGNALS:
-       void samples_cleared();
-
-       void samples_added(uint64_t segment_id, uint64_t start_sample,
-               uint64_t end_sample);
-
-       void min_max_changed(float min, float max);
-
        void expression_changed(QString expression);
 
 private Q_SLOTS:
index 66085fafc935d151f5ded69cc7863bff984858a4..f4601d0444c0a9bab0aa88dd9efe3e1996dde9e4 100644 (file)
@@ -24,6 +24,7 @@
 #include "pv/util.hpp"
 
 #include <atomic>
+#include <memory>
 #include <mutex>
 #include <thread>
 #include <deque>
index 578d908f8d52a036b4e5c9f4a553a34865264bb3..97f705084e91fc654c85ab7ce07cbfec64a3df50 100644 (file)
@@ -124,11 +124,12 @@ SignalBase::SignalBase(shared_ptr<sigrok::Channel> channel, ChannelType channel_
        conversion_type_(NoConversion),
        min_value_(0),
        max_value_(0),
+       index_(0),
        error_message_("")
 {
        if (channel_) {
-               internal_name_ = QString::fromStdString(channel_->name());
-               index_ = channel_->index();
+               set_internal_name(QString::fromStdString(channel_->name()));
+               set_index(channel_->index());
        }
 
        connect(&delayed_conversion_starter_, SIGNAL(timeout()),
@@ -219,6 +220,9 @@ QString SignalBase::internal_name() const
 void SignalBase::set_internal_name(QString internal_name)
 {
        internal_name_ = internal_name;
+
+       // Use this name also for the QObject instance
+       setObjectName(internal_name);
 }
 
 QString SignalBase::display_name() const
@@ -315,15 +319,22 @@ shared_ptr<data::Logic> SignalBase::logic_data() const
        if (!data_)
                return nullptr;
 
-       shared_ptr<Logic> result = dynamic_pointer_cast<Logic>(data_);
+       shared_ptr<Logic> result;
 
        if (((conversion_type_ == A2LConversionByThreshold) ||
                (conversion_type_ == A2LConversionBySchmittTrigger)))
                result = dynamic_pointer_cast<Logic>(converted_data_);
+       else
+               result = dynamic_pointer_cast<Logic>(data_);
 
        return result;
 }
 
+shared_ptr<pv::data::SignalData> SignalBase::data() const
+{
+       return data_;
+}
+
 bool SignalBase::segment_is_complete(uint32_t segment_id) const
 {
        bool result = true;
@@ -579,7 +590,12 @@ void SignalBase::restore_settings(QSettings &settings)
                QVariant value = settings.value("color");
 
                // Workaround for Qt QColor serialization bug on OSX
-               if ((QMetaType::Type)(value.type()) == QMetaType::QColor)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               bool is_qcolor = (QMetaType::Type)(value.typeId()) == QMetaType::QColor;
+#else
+               bool is_qcolor = (QMetaType::Type)(value.type()) == QMetaType::QColor;
+#endif
+               if (is_qcolor)
                        set_color(value.value<QColor>());
                else
                        set_color(QColor::fromRgba(value.value<uint32_t>()));
@@ -712,6 +728,8 @@ void SignalBase::convert_single_segment_range(shared_ptr<AnalogSegment> asegment
                delete[] lsamples;
                delete[] asamples;
        }
+
+       samples_added(lsegment->segment_id(), start_sample, end_sample);
 }
 
 void SignalBase::convert_single_segment(shared_ptr<AnalogSegment> asegment,
@@ -745,6 +763,9 @@ void SignalBase::convert_single_segment(shared_ptr<AnalogSegment> asegment,
                // we do another round of sample conversion.
        } while ((complete_state != old_complete_state) ||
                (end_sample - old_end_sample >= ConversionBlockSize));
+
+       if (complete_state)
+               lsegment->set_complete();
 }
 
 void SignalBase::conversion_thread_proc()
@@ -771,6 +792,7 @@ void SignalBase::conversion_thread_proc()
 
        shared_ptr<AnalogSegment> asegment = analog_data->analog_segments().front();
        assert(asegment);
+       connect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
 
        const shared_ptr<Logic> logic_data = dynamic_pointer_cast<Logic>(converted_data_);
        assert(logic_data);
@@ -792,11 +814,15 @@ void SignalBase::conversion_thread_proc()
                if (asegment->is_complete() &&
                        analog_data->analog_segments().size() > logic_data->logic_segments().size()) {
 
+                       disconnect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
+
                        // There are more segments to process
                        segment_id++;
 
                        try {
                                asegment = analog_data->analog_segments().at(segment_id);
+                               disconnect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
+                               connect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
                        } catch (out_of_range&) {
                                qDebug() << "Conversion error for" << name() << ": no analog segment" \
                                        << segment_id << ", segments size is" << analog_data->analog_segments().size();
@@ -808,14 +834,16 @@ void SignalBase::conversion_thread_proc()
                        logic_data->push_segment(new_segment);
 
                        lsegment = logic_data->logic_segments().back();
-               }
-
-               // No more samples/segments to process, wait for data or interrupt
-               if (!conversion_interrupt_) {
-                       unique_lock<mutex> input_lock(conversion_input_mutex_);
-                       conversion_input_cond_.wait(input_lock);
+               } else {
+                       // No more samples/segments to process, wait for data or interrupt
+                       if (!conversion_interrupt_) {
+                               unique_lock<mutex> input_lock(conversion_input_mutex_);
+                               conversion_input_cond_.wait(input_lock);
+                       }
                }
        } while (!conversion_interrupt_);
+
+       disconnect(asegment.get(), SIGNAL(completed()), this, SLOT(on_input_segment_completed()));
 }
 
 void SignalBase::start_conversion(bool delayed_start)
@@ -827,10 +855,10 @@ void SignalBase::start_conversion(bool delayed_start)
 
        stop_conversion();
 
-       if (converted_data_)
+       if (converted_data_ && (converted_data_->get_segment_count() > 0)) {
                converted_data_->clear();
-
-       samples_cleared();
+               samples_cleared();
+       }
 
        conversion_interrupt_ = false;
        conversion_thread_ = std::thread(&SignalBase::conversion_thread_proc, this);
@@ -856,10 +884,10 @@ void SignalBase::stop_conversion()
 
 void SignalBase::on_samples_cleared()
 {
-       if (converted_data_)
+       if (converted_data_ && (converted_data_->get_segment_count() > 0)) {
                converted_data_->clear();
-
-       samples_cleared();
+               samples_cleared();
+       }
 }
 
 void SignalBase::on_samples_added(SharedPtrToSegment segment, uint64_t start_sample,
@@ -879,6 +907,15 @@ void SignalBase::on_samples_added(SharedPtrToSegment segment, uint64_t start_sam
        samples_added(segment->segment_id(), start_sample, end_sample);
 }
 
+void SignalBase::on_input_segment_completed()
+{
+       if (conversion_type_ != NoConversion)
+               if (conversion_thread_.joinable()) {
+                       // Notify the conversion thread since it's running
+                       conversion_input_cond_.notify_one();
+               }
+}
+
 void SignalBase::on_min_max_changed(float min, float max)
 {
        // Restart conversion if one is enabled and uses a calculated threshold
index ee927aae27bf911c96f2c396a4a14fc42a22d33a..19f8143dc77e3499a92ecb0d17fb07fe411605be 100644 (file)
@@ -87,7 +87,7 @@ private:
 class SignalBase : public QObject, public enable_shared_from_this<SignalBase>
 {
        Q_OBJECT
-       Q_PROPERTY(QString error_message READ get_error_message)
+       Q_PROPERTY(QString error_message READ get_error_message NOTIFY error_message_changed)
 
 public:
        enum ChannelType {
@@ -219,7 +219,7 @@ public:
        /**
         * Set the color of the signal.
         */
-       void set_color(QColor color);
+       virtual void set_color(QColor color);
 
        /**
         * Get the background color of the signal.
@@ -251,6 +251,11 @@ public:
         */
        shared_ptr<pv::data::Logic> logic_data() const;
 
+       /**
+        * Get the primary internal data object, i.e. the data that was acquired from the device.
+        */
+       shared_ptr<pv::data::SignalData> data() const;
+
        /**
         * Determines whether a given segment is complete (i.e. end-of-frame has
         * been seen). It only considers the original data, not the converted data.
@@ -374,7 +379,7 @@ Q_SIGNALS:
        void enabled_changed(const bool &value);
        void name_changed(const QString &name);
        void color_changed(const QColor &color);
-       void error_message_changed(const QString &msg);
+       void error_message_changed(QString msg);
        void conversion_type_changed(const ConversionType t);
 
        void samples_cleared();
@@ -389,6 +394,8 @@ private Q_SLOTS:
        void on_samples_added(SharedPtrToSegment segment, uint64_t start_sample,
                uint64_t end_sample);
 
+       void on_input_segment_completed();
+
        void on_min_max_changed(float min, float max);
 
        void on_capture_state_changed(int state);
index 6d2283abeaddc962d6529dc1b9150df94a459428..7a9d6d39b96bada864b2f46017c802587dedf37e 100644 (file)
@@ -54,6 +54,9 @@ public:
        virtual void set_samplerate(double value) = 0;
 
        virtual double get_samplerate() const = 0;
+
+Q_SIGNALS:
+       void segment_completed();
 };
 
 } // namespace data
index b07a63696fb4b11d3c6c69cbf8b5beb79d7da60d..55b7e826291eb69f057025582febef7b65c319be 100644 (file)
@@ -84,7 +84,7 @@ public:
 };
 
 Settings::Settings(DeviceManager &device_manager, QWidget *parent) :
-       QDialog(parent, nullptr),
+       QDialog(parent),
        device_manager_(device_manager)
 {
        resize(600, 400);
@@ -220,7 +220,7 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
        QComboBox *language_cb = new QComboBox();
        Application* a = qobject_cast<Application*>(QApplication::instance());
 
-       QString current_language = settings.value(GlobalSettings::Key_General_Language).toString();
+       QString current_language = settings.value(GlobalSettings::Key_General_Language, "en").toString();
        for (const QString& language : a->get_languages()) {
                const QLocale locale = QLocale(language);
                const QString desc = locale.languageToString(locale.language());
@@ -231,8 +231,13 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
                        language_cb->setCurrentIndex(index);
                }
        }
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(language_cb, SIGNAL(currentTextChanged(const QString&)),
+               this, SLOT(on_general_language_changed(const QString&)));
+#else
        connect(language_cb, SIGNAL(currentIndexChanged(const QString&)),
                this, SLOT(on_general_language_changed(const QString&)));
+#endif
        general_layout->addRow(tr("User interface language"), language_cb);
 
        // Theme combobox
@@ -261,7 +266,7 @@ QWidget *Settings::get_general_settings_form(QWidget *parent) const
        if (current_style.isEmpty())
                style_cb->setCurrentIndex(0);
        else
-               style_cb->setCurrentIndex(style_cb->findText(current_style, nullptr));
+               style_cb->setCurrentIndex(style_cb->findText(current_style));
 
        connect(style_cb, SIGNAL(currentIndexChanged(int)),
                this, SLOT(on_general_style_changed(int)));
@@ -313,19 +318,23 @@ QWidget *Settings::get_view_settings_form(QWidget *parent) const
 
        cb = create_checkbox(GlobalSettings::Key_View_TriggerIsZeroTime,
                SLOT(on_view_triggerIsZero_changed(int)));
-       trace_view_layout->addRow(tr("Show time zero at the trigger"), cb);
+       trace_view_layout->addRow(tr("Show time zero at the &trigger"), cb);
 
        cb = create_checkbox(GlobalSettings::Key_View_StickyScrolling,
                SLOT(on_view_stickyScrolling_changed(int)));
        trace_view_layout->addRow(tr("Always keep &newest samples at the right edge during capture"), cb);
 
+       cb = create_checkbox(GlobalSettings::Key_View_AllowVerticalDragging,
+               SLOT(on_view_allowVerticalDragging_changed(int)));
+       trace_view_layout->addRow(tr("Allow &vertical dragging in the view area"), cb);
+
        cb = create_checkbox(GlobalSettings::Key_View_ShowSamplingPoints,
                SLOT(on_view_showSamplingPoints_changed(int)));
        trace_view_layout->addRow(tr("Show data &sampling points"), cb);
 
        cb = create_checkbox(GlobalSettings::Key_View_FillSignalHighAreas,
                SLOT(on_view_fillSignalHighAreas_changed(int)));
-       trace_view_layout->addRow(tr("Fill high areas of logic signals"), cb);
+       trace_view_layout->addRow(tr("Fill &high areas of logic signals"), cb);
 
        ColorButton* high_fill_cb = new ColorButton(parent);
        high_fill_cb->set_color(QColor::fromRgba(
@@ -342,6 +351,10 @@ QWidget *Settings::get_view_settings_form(QWidget *parent) const
                SLOT(on_view_showHoverMarker_changed(int)));
        trace_view_layout->addRow(tr("Highlight mouse cursor using a vertical marker line"), cb);
 
+       cb = create_checkbox(GlobalSettings::Key_View_KeepRulerItemSelected,
+               SLOT(on_view_keepRulerItemSelected_changed(int)));
+       trace_view_layout->addRow(tr("Keep active item on ruler selected when editing popup is closed"), cb);
+
        QSpinBox *snap_distance_sb = new QSpinBox();
        snap_distance_sb->setRange(0, 1000);
        snap_distance_sb->setSuffix(tr(" pixels"));
@@ -720,6 +733,12 @@ void Settings::on_view_stickyScrolling_changed(int state)
        settings.setValue(GlobalSettings::Key_View_StickyScrolling, state ? true : false);
 }
 
+void Settings::on_view_allowVerticalDragging_changed(int state)
+{
+       GlobalSettings settings;
+       settings.setValue(GlobalSettings::Key_View_AllowVerticalDragging, state ? true : false);
+}
+
 void Settings::on_view_showSamplingPoints_changed(int state)
 {
        GlobalSettings settings;
@@ -750,6 +769,12 @@ void Settings::on_view_showHoverMarker_changed(int state)
        settings.setValue(GlobalSettings::Key_View_ShowHoverMarker, state ? true : false);
 }
 
+void Settings::on_view_keepRulerItemSelected_changed(int state)
+{
+       GlobalSettings settings;
+       settings.setValue(GlobalSettings::Key_View_KeepRulerItemSelected, state ? true : false);
+}
+
 void Settings::on_view_snapDistance_changed(int value)
 {
        GlobalSettings settings;
index d419350c6821c592251a9175878f666be57f0edf..bd3572ff977620daeff29968a9fc50a163312543 100644 (file)
@@ -68,11 +68,13 @@ private Q_SLOTS:
        void on_view_triggerIsZero_changed(int state);
        void on_view_coloredBG_changed(int state);
        void on_view_stickyScrolling_changed(int state);
+       void on_view_allowVerticalDragging_changed(int state);
        void on_view_showSamplingPoints_changed(int state);
        void on_view_fillSignalHighAreas_changed(int state);
        void on_view_fillSignalHighAreaColor_changed(QColor color);
        void on_view_showAnalogMinorGrid_changed(int state);
        void on_view_showHoverMarker_changed(int state);
+       void on_view_keepRulerItemSelected_changed(int state);
        void on_view_snapDistance_changed(int value);
        void on_view_cursorFillColor_changed(QColor color);
        void on_view_conversionThresholdDispMode_changed(int state);
index 8699ccefdfbcca6fe2bae451f9c34072d0848717..0bcfbf2c75ca772510068b0309a4053e74fef5a3 100644 (file)
@@ -50,6 +50,7 @@ StoreProgress::StoreProgress(const QString &file_name,
                this, SLOT(on_progress_updated()));
        connect(&session_, SIGNAL(store_successful()),
                &session, SLOT(on_data_saved()));
+       connect(this, SIGNAL(canceled()), this, SLOT(on_cancel()));
 
        // Since we're not setting any progress in case of an error, the dialog
        // will pop up after the minimumDuration time has been reached - 4000 ms
@@ -112,10 +113,17 @@ void StoreProgress::on_progress_updated()
                setMaximum(p.second);
        } else {
                const QString err = session_.error();
-               if (!err.isEmpty() && !showing_error_)
+               if (err.isEmpty())
+                       close();
+               else if (!showing_error_)
                        show_error();
        }
 }
 
+void StoreProgress::on_cancel()
+{
+       session_.cancel();
+}
+
 }  // namespace dialogs
 }  // namespace pv
index 730a9e423d9a51aec37d2b9177106288202770fe..f355acc69e2f3b6c8ff35eac2a389d2b93aa56e9 100644 (file)
@@ -62,6 +62,7 @@ private:
 
 private Q_SLOTS:
        void on_progress_updated();
+       void on_cancel();
 
 private:
        pv::StoreSession session_;
index d1f6cf1520d02fa4b98aa4bc867ecc44039763ca..84e35b61ffc152ef754638acb9a12e0647ab9267 100644 (file)
@@ -416,7 +416,7 @@ namespace exprtk
          std::string data_;
       };
 
-      static const std::string reserved_words[] =
+      const std::string reserved_words[] =
                                   {
                                     "break",  "case",  "continue",  "default",  "false",  "for",
                                     "if", "else", "ilike",  "in", "like", "and",  "nand", "nor",
@@ -427,7 +427,7 @@ namespace exprtk
 
       static const std::size_t reserved_words_size = sizeof(reserved_words) / sizeof(std::string);
 
-      static const std::string reserved_symbols[] =
+      const std::string reserved_symbols[] =
                                   {
                                     "abs",  "acos",  "acosh",  "and",  "asin",  "asinh", "atan",
                                     "atanh", "atan2", "avg",  "break", "case", "ceil",  "clamp",
@@ -446,7 +446,7 @@ namespace exprtk
 
       static const std::size_t reserved_symbols_size = sizeof(reserved_symbols) / sizeof(std::string);
 
-      static const std::string base_function_list[] =
+      const std::string base_function_list[] =
                                   {
                                     "abs", "acos",  "acosh", "asin",  "asinh", "atan",  "atanh",
                                     "atan2",  "avg",  "ceil",  "clamp",  "cos",  "cosh",  "cot",
@@ -461,28 +461,28 @@ namespace exprtk
 
       static const std::size_t base_function_list_size = sizeof(base_function_list) / sizeof(std::string);
 
-      static const std::string logic_ops_list[] =
+      const std::string logic_ops_list[] =
                                   {
                                     "and", "nand", "nor", "not", "or",  "xnor", "xor", "&", "|"
                                   };
 
       static const std::size_t logic_ops_list_size = sizeof(logic_ops_list) / sizeof(std::string);
 
-      static const std::string cntrl_struct_list[] =
+      const std::string cntrl_struct_list[] =
                                   {
                                      "if", "switch", "for", "while", "repeat", "return"
                                   };
 
       static const std::size_t cntrl_struct_list_size = sizeof(cntrl_struct_list) / sizeof(std::string);
 
-      static const std::string arithmetic_ops_list[] =
+      const std::string arithmetic_ops_list[] =
                                   {
                                     "+", "-", "*", "/", "%", "^"
                                   };
 
       static const std::size_t arithmetic_ops_list_size = sizeof(arithmetic_ops_list) / sizeof(std::string);
 
-      static const std::string assignment_ops_list[] =
+      const std::string assignment_ops_list[] =
                                   {
                                     ":=", "+=", "-=",
                                     "*=", "/=", "%="
@@ -490,7 +490,7 @@ namespace exprtk
 
       static const std::size_t assignment_ops_list_size = sizeof(assignment_ops_list) / sizeof(std::string);
 
-      static const std::string inequality_ops_list[] =
+      const std::string inequality_ops_list[] =
                                   {
                                      "<",  "<=", "==",
                                      "=",  "!=", "<>",
index 4fa9bc334d27e4e470ca30afe0ae0d52715da9e9..ecca21d5216ed534ef433f0b95740cb14cd34f0b 100644 (file)
@@ -58,6 +58,7 @@ const QString GlobalSettings::Key_View_ZoomToFitAfterAcq = "View_ZoomToFitAfterA
 const QString GlobalSettings::Key_View_TriggerIsZeroTime = "View_TriggerIsZeroTime";
 const QString GlobalSettings::Key_View_ColoredBG = "View_ColoredBG";
 const QString GlobalSettings::Key_View_StickyScrolling = "View_StickyScrolling";
+const QString GlobalSettings::Key_View_AllowVerticalDragging = "View_AllowVerticalDragging";
 const QString GlobalSettings::Key_View_ShowSamplingPoints = "View_ShowSamplingPoints";
 const QString GlobalSettings::Key_View_FillSignalHighAreas = "View_FillSignalHighAreas";
 const QString GlobalSettings::Key_View_FillSignalHighAreaColor = "View_FillSignalHighAreaColor";
@@ -66,6 +67,7 @@ const QString GlobalSettings::Key_View_ConversionThresholdDispMode = "View_Conve
 const QString GlobalSettings::Key_View_DefaultDivHeight = "View_DefaultDivHeight";
 const QString GlobalSettings::Key_View_DefaultLogicHeight = "View_DefaultLogicHeight";
 const QString GlobalSettings::Key_View_ShowHoverMarker = "View_ShowHoverMarker";
+const QString GlobalSettings::Key_View_KeepRulerItemSelected = "View_KeepRulerItemSelected";
 const QString GlobalSettings::Key_View_SnapDistance = "View_SnapDistance";
 const QString GlobalSettings::Key_View_CursorFillColor = "View_CursorFillColor";
 const QString GlobalSettings::Key_View_CursorShowFrequency = "View_CursorShowFrequency";
@@ -124,6 +126,10 @@ void GlobalSettings::set_defaults_where_needed()
        if (!contains(Key_View_ZoomToFitAfterAcq))
                setValue(Key_View_ZoomToFitAfterAcq, true);
 
+       // Allow vertical dragging by default
+       if (!contains(Key_View_AllowVerticalDragging))
+               setValue(Key_View_AllowVerticalDragging, true);
+
        // Enable colored trace backgrounds by default
        if (!contains(Key_View_ColoredBG))
                setValue(Key_View_ColoredBG, true);
@@ -147,6 +153,9 @@ void GlobalSettings::set_defaults_where_needed()
        if (!contains(Key_View_ShowHoverMarker))
                setValue(Key_View_ShowHoverMarker, true);
 
+       if (!contains(Key_View_KeepRulerItemSelected))
+               setValue(Key_View_KeepRulerItemSelected, false);
+
        if (!contains(Key_View_SnapDistance))
                setValue(Key_View_SnapDistance, 15);
 
@@ -325,7 +334,11 @@ void GlobalSettings::store_gvariant(QSettings &settings, GVariant *v)
                g_variant_get_size(v));
 
        settings.setValue("value", var_data);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       settings.setValue("type", (const char *)var_type_str);
+#else
        settings.setValue("type", var_type_str);
+#endif
 
        g_free(var_type_str);
 }
@@ -337,8 +350,11 @@ GVariant* GlobalSettings::restore_gvariant(QSettings &settings)
 
        QByteArray data = settings.value("value").toByteArray();
 
-       gpointer var_data = g_memdup((gconstpointer)data.constData(),
-               (guint)data.size());
+#if GLIB_CHECK_VERSION(2, 67, 3)  // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
+       gpointer var_data = g_memdup2((gconstpointer)data.constData(), (gsize)data.size());
+#else
+       gpointer var_data = g_memdup((gconstpointer)data.constData(), (guint)data.size());
+#endif
 
        GVariant *value = g_variant_new_from_data(var_type, var_data,
                data.size(), false, g_free, var_data);
@@ -363,8 +379,11 @@ Glib::VariantBase GlobalSettings::restore_variantbase(QSettings &settings)
 
        QByteArray data = settings.value("value").toByteArray();
 
-       gpointer var_data = g_memdup((gconstpointer)data.constData(),
-               (guint)data.size());
+#if GLIB_CHECK_VERSION(2, 67, 3)  // See https://discourse.gnome.org/t/port-your-module-from-g-memdup-to-g-memdup2-now/5538
+       gpointer var_data = g_memdup2((gconstpointer)data.constData(), (gsize)data.size());
+#else
+       gpointer var_data = g_memdup((gconstpointer)data.constData(), (guint)data.size());
+#endif
 
        GVariant *value = g_variant_new_from_data(var_type, var_data,
                data.size(), false, g_free, var_data);
index 2b8bf17f5fba3dca6fa47a4872626d3b22dfb055..f6239a6b3d089f726cea54afd1bffaf3f211b14a 100644 (file)
@@ -63,6 +63,7 @@ public:
        static const QString Key_View_TriggerIsZeroTime;
        static const QString Key_View_ColoredBG;
        static const QString Key_View_StickyScrolling;
+       static const QString Key_View_AllowVerticalDragging;
        static const QString Key_View_ShowSamplingPoints;
        static const QString Key_View_FillSignalHighAreas;
        static const QString Key_View_FillSignalHighAreaColor;
@@ -71,6 +72,7 @@ public:
        static const QString Key_View_DefaultDivHeight;
        static const QString Key_View_DefaultLogicHeight;
        static const QString Key_View_ShowHoverMarker;
+       static const QString Key_View_KeepRulerItemSelected;
        static const QString Key_View_SnapDistance;
        static const QString Key_View_CursorFillColor;
        static const QString Key_View_CursorShowInterval;
index e42a9e54e94add49cb46b34f3a5ab216650e6c3f..7dab545de93cd3989af8d72a69867e470a5b0753 100644 (file)
@@ -191,8 +191,13 @@ int Logging::log_srd(void *cb_data, int loglevel, const char *format, va_list ar
        char *text = g_strdup_vprintf(format, args);
 
        QString s = QString::fromUtf8(text);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+       for (QString& substring : s.split("\n", Qt::SkipEmptyParts))
+                       logging.log(substring, LogSource_srd);
+#else
        for (QString& substring : s.split("\n", QString::SkipEmptyParts))
                        logging.log(substring, LogSource_srd);
+#endif
        g_free(text);
 
        return SR_OK;
index 8e3cb9d1085f9c72588c519dc74e9e5bbb8b8179..8646175e2b32f7a6b7f0a7ec7233f07d136d77fb 100644 (file)
@@ -79,6 +79,8 @@ MainWindow::MainWindow(DeviceManager &device_manager, QWidget *parent) :
 {
        setup_ui();
        restore_ui_settings();
+       connect(this, SIGNAL(session_error_raised(const QString, const QString)),
+               this, SLOT(on_session_error_raised(const QString, const QString)));
 }
 
 MainWindow::~MainWindow()
@@ -95,7 +97,7 @@ MainWindow::~MainWindow()
 void MainWindow::show_session_error(const QString text, const QString info_text)
 {
        // TODO Emulate noquote()
-       qDebug() << "Notifying user of session error:" << info_text;
+       qDebug() << "Notifying user of session error: " << text << "; " << info_text;
 
        QMessageBox msg;
        msg.setText(text + "\n\n" + info_text);
@@ -193,6 +195,9 @@ shared_ptr<views::ViewBase> MainWindow::add_view(views::ViewType type,
                qobject_cast<views::ViewBase*>(v.get()),
                SLOT(trigger_event(int, util::Timestamp)));
 
+       connect(&session, SIGNAL(session_error_raised(const QString, const QString)),
+               this, SLOT(on_session_error_raised(const QString, const QString)));
+
        if (type == views::ViewTypeTrace) {
                views::trace::View *tv =
                        qobject_cast<views::trace::View*>(v.get());
@@ -547,10 +552,14 @@ void MainWindow::setup_ui()
        session_selector_.setCornerWidget(static_tab_widget_, Qt::TopLeftCorner);
        session_selector_.setTabsClosable(true);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       close_application_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this, SLOT(close()));
+       close_current_tab_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this, SLOT(on_close_current_tab()));
+#else
        close_application_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this, SLOT(close()));
-       close_application_shortcut_->setAutoRepeat(false);
-
        close_current_tab_shortcut_ = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this, SLOT(on_close_current_tab()));
+#endif
+       close_application_shortcut_->setAutoRepeat(false);
 
        connect(new_session_button_, SIGNAL(clicked(bool)),
                this, SLOT(on_new_session_clicked()));
@@ -669,7 +678,7 @@ void MainWindow::on_run_stop_clicked()
                vector< shared_ptr<Session> > hw_sessions;
 
                // Make a list of all sessions where a hardware device is used
-               for (shared_ptr<Session> s : sessions_) {
+               for (const shared_ptr<Session>& s : sessions_) {
                        shared_ptr<devices::HardwareDevice> hw_device =
                                        dynamic_pointer_cast< devices::HardwareDevice >(s->device());
                        if (!hw_device)
@@ -687,8 +696,7 @@ void MainWindow::on_run_stop_clicked()
                        if (any_running)
                                s->stop_capture();
                        else
-                               s->start_capture([&](QString message) {
-                                       show_session_error("Capture failed", message); });
+                               s->start_capture([&](QString message) {Q_EMIT session_error_raised("Capture failed", message);});
        } else {
 
                shared_ptr<Session> session = last_focused_session_;
@@ -698,8 +706,7 @@ void MainWindow::on_run_stop_clicked()
 
                switch (session->get_capture_state()) {
                case Session::Stopped:
-                       session->start_capture([&](QString message) {
-                               show_session_error("Capture failed", message); });
+                       session->start_capture([&](QString message) {Q_EMIT session_error_raised("Capture failed", message);});
                        break;
                case Session::AwaitingTrigger:
                case Session::Running:
@@ -977,4 +984,8 @@ void MainWindow::on_close_current_tab()
        on_tab_close_requested(tab);
 }
 
+void MainWindow::on_session_error_raised(const QString text, const QString info_text) {
+       MainWindow::show_session_error(text, info_text);
+}
+
 } // namespace pv
index e10d1817f947ecb68387cf2f8bd154a3736050cf..493e3a55cc0dec32b3ca6e1471138dd0a0bdeac7 100644 (file)
@@ -115,8 +115,12 @@ private:
 
        virtual bool restoreState(const QByteArray &state, int version = 0);
 
+Q_SIGNALS:
+       void session_error_raised(const QString text, const QString info_text);
+
 public Q_SLOTS:
        void on_run_stop_clicked();
+       void on_session_error_raised(const QString text, const QString info_text);
 
 private Q_SLOTS:
        void on_add_view(ViewType type, Session *session);
index ecdc59a35466cd82f378cd1393f5f366bb091ccf..ce2e8e98e08e0f7f3e88356b68ddce4aad1d4c66 100644 (file)
@@ -158,8 +158,13 @@ Channels::Channels(Session &session, QWidget *parent) :
        layout_.addRow(&filter_buttons_bar_);
 
        // Connect the check-box signal mapper
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&check_box_mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_channel_checked(QObject*)));
+#else
        connect(&check_box_mapper_, SIGNAL(mapped(QWidget*)),
                this, SLOT(on_channel_checked(QWidget*)));
+#endif
 }
 
 void Channels::set_all_channels(bool set)
@@ -354,7 +359,11 @@ void Channels::showEvent(QShowEvent *event)
        updating_channels_ = false;
 }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+void Channels::on_channel_checked(QObject *widget)
+#else
 void Channels::on_channel_checked(QWidget *widget)
+#endif
 {
        if (updating_channels_)
                return;
index c176eb7a95184850ca89dc1b2c90d7ce4d1d660e..66d284b00dfcefcadd003a1b7bbd73b021b6f274 100644 (file)
@@ -82,7 +82,11 @@ private:
        void showEvent(QShowEvent *event);
 
 private Q_SLOTS:
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       void on_channel_checked(QObject *widget);
+#else
        void on_channel_checked(QWidget *widget);
+#endif
 
        void enable_all_channels();
        void disable_all_channels();
index d30dbc0697cfac67631ab7500028ebf21a4faef8..b4ecc6a87c2f4886c3a812df1191cea08542e4f8 100644 (file)
@@ -126,6 +126,8 @@ Session::Session(DeviceManager &device_manager, QString name) :
        cur_samplerate_(0),
        data_saved_(true)
 {
+       // Use this name also for the QObject instance
+       setObjectName(name_);
 }
 
 Session::~Session()
@@ -175,6 +177,9 @@ void Session::set_name(QString name)
 
        name_ = name;
 
+       // Use this name also for the QObject instance
+       setObjectName(name_);
+
        name_changed();
 }
 
@@ -609,6 +614,9 @@ void Session::set_device(shared_ptr<devices::Device> device)
        } catch (const QString &e) {
                device_.reset();
                MainWindow::show_session_error(tr("Failed to open device"), e);
+       } catch (const sigrok::Error &e) {
+               device_.reset();
+               MainWindow::show_session_error(tr("Failed to open device"), QString(e.what()));
        }
 
        if (device_) {
@@ -751,8 +759,11 @@ void Session::load_file(QString file_name, QString setup_file_name,
                                        file_name.toStdString())));
        } catch (Error& e) {
                MainWindow::show_session_error(tr("Failed to load %1").arg(file_name), e.what());
-               set_default_device();
-               main_bar_->update_device_list();
+               return;
+       }
+
+       if (!device_) {
+               MainWindow::show_session_error(errorMessage, "");
                return;
        }
 
@@ -771,7 +782,7 @@ void Session::load_file(QString file_name, QString setup_file_name,
        main_bar_->update_device_list();
 
        start_capture([&, errorMessage](QString infoMessage) {
-               MainWindow::show_session_error(errorMessage, infoMessage); });
+               Q_EMIT session_error_raised(errorMessage, infoMessage); });
 
        // Only set save path if we loaded an srzip file
        if (dynamic_pointer_cast<devices::SessionFile>(device_))
@@ -824,6 +835,8 @@ void Session::start_capture(function<void (const QString)> error_handler)
                name_changed();
        }
 
+       acq_start_time_ = Glib::DateTime::create_now_local();
+
        // Begin the session
        sampling_thread_ = std::thread(&Session::sample_thread_proc, this, error_handler);
 }
@@ -918,6 +931,11 @@ double Session::get_samplerate() const
        return samplerate;
 }
 
+Glib::DateTime Session::get_acquisition_start_time() const
+{
+       return acq_start_time_;
+}
+
 uint32_t Session::get_highest_segment_id() const
 {
        return highest_segment_id_;
index 91f98b581b8c0ed2a342321caf075cedff279b2d..94338c20e259144a7bb9ae3bad96edc9ef4c44f5 100644 (file)
@@ -35,6 +35,8 @@
 #include <thread>
 #include <vector>
 
+#include <glibmm/datetime.h>
+
 #include <QObject>
 #include <QSettings>
 #include <QString>
@@ -185,6 +187,7 @@ public:
        void stop_capture();
 
        double get_samplerate() const;
+       Glib::DateTime get_acquisition_start_time() const;
 
        uint32_t get_highest_segment_id() const;
        uint64_t get_segment_sample_count(uint32_t segment_id) const;
@@ -264,6 +267,7 @@ Q_SIGNALS:
        void data_received();
 
        void add_view(ViewType type, Session *session);
+       void session_error_raised(const QString text, const QString info_text);
 
 public Q_SLOTS:
        void on_data_saved();
@@ -311,6 +315,7 @@ private:
        bool frame_began_;
 
        QElapsedTimer acq_time_;
+       Glib::DateTime acq_start_time_;
 
        MetadataObjManager metadata_obj_manager_;
 
index ed0b0defaaac41034bdf843e5858cae89f40fe19..5eb6a8ad25aacbec2085500ad95a6ec9363a0ad1 100644 (file)
@@ -184,6 +184,9 @@ bool StoreSession::start()
                        {{ConfigKey::SAMPLERATE, Glib::Variant<guint64>::create(
                                any_segment->samplerate())}});
                output_->receive(meta);
+
+               auto header = context->create_header_packet(session_.get_acquisition_start_time());
+               output_->receive(header);
        } catch (Error& error) {
                error_ = tr("Error while saving: ") + error.what();
                return false;
@@ -301,10 +304,14 @@ void StoreSession::store_proc(vector< shared_ptr<data::SignalBase> > achannel_li
                units_stored_ = unit_count_ - (sample_count_ >> progress_scale);
        }
 
-       auto dfend = context->create_end_packet();
-       const string ldata_str = output_->receive(dfend);
-       if (output_stream_.is_open())
-               output_stream_ << ldata_str;
+       try {
+               auto dfend = context->create_end_packet();
+               const string ldata_str = output_->receive(dfend);
+               if (output_stream_.is_open())
+                       output_stream_ << ldata_str;
+       } catch (Error& error) {
+               error_ = tr("Error while saving: ") + error.what();
+       }
 
        // Zeroing the progress variables indicates completion
        units_stored_ = unit_count_ = 0;
index 2a4182b05840462e2c8a38f9285a26af19f792a9..0c4dab59c3811d1b83ea922d1eb3098f80802070 100644 (file)
@@ -134,7 +134,7 @@ QVariant DecoderCollectionModel::data(const QModelIndex& index, int role) const
 Qt::ItemFlags DecoderCollectionModel::flags(const QModelIndex& index) const
 {
        if (!index.isValid())
-               return nullptr;
+               return Qt::NoItemFlags;
 
        return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 }
index 2c65dcf2e4f3f928ac38216a4303fd7fe543b8be..0c4b76bb8c14aca9fcb58688f037579c205dd13e 100644 (file)
@@ -74,7 +74,8 @@ void QCustomTreeView::currentChanged(const QModelIndex& current,
        const QModelIndex& previous)
 {
        QTreeView::currentChanged(current, previous);
-       currentChanged(current);
+
+       current_changed(current);
 }
 
 
@@ -158,7 +159,7 @@ SubWindow::SubWindow(Session& session, QWidget* parent) :
        connect(filter, SIGNAL(returnPressed()),
                this, SLOT(on_filter_return_pressed()));
 
-       connect(tree_view_, SIGNAL(currentChanged(const QModelIndex&)),
+       connect(tree_view_, SIGNAL(current_changed(const QModelIndex&)),
                this, SLOT(on_item_changed(const QModelIndex&)));
        connect(tree_view_, SIGNAL(activated(const QModelIndex&)),
                this, SLOT(on_item_activated(const QModelIndex&)));
index c189fb24860efb085a859617e5bd8bf4bcb896eb..f4ec022329b425d63f48f3be766a0b9b40b23582 100644 (file)
@@ -99,7 +99,7 @@ public:
        void currentChanged(const QModelIndex& current, const QModelIndex& previous);
 
 Q_SIGNALS:
-       void currentChanged(const QModelIndex& current);
+       void current_changed(const QModelIndex& current);
 };
 
 class SubWindow : public SubWindowBase
index b35e14661373d88325eb4a28c1d73a580d587e4f..cd46c063bdc8a84d78d1e42e1c616c2fc8a3b270 100644 (file)
@@ -130,7 +130,11 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        action_open_->setText(tr("&Open..."));
        action_open_->setIcon(QIcon::fromTheme("document-open",
                QIcon(":/icons/document-open.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       action_open_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_O));
+#else
        action_open_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_O));
+#endif
        connect(action_open_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionOpen_triggered()));
 
@@ -141,7 +145,11 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        action_save_->setText(tr("&Save..."));
        action_save_->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       action_save_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+#else
        action_save_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+#endif
        connect(action_save_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionSave_triggered()));
 
@@ -154,7 +162,11 @@ MainBar::MainBar(Session &session, QWidget *parent, pv::views::trace::View *view
        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")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_R));
+#else
        action_save_selection_as_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
+#endif
        connect(action_save_selection_as_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionSaveSelectionAs_triggered()));
 
@@ -548,7 +560,7 @@ void MainBar::update_device_config_widgets()
                sample_count_supported_ = true;
 
        // Add notification of reconfigure events
-       disconnect(this, SLOT(on_config_changed()));
+       // Note: No need to disconnect the previous signal as that QObject instance is destroyed
        connect(&opts->binding(), SIGNAL(config_changed()),
                this, SLOT(on_config_changed()));
 
index ed003949f242c67f482667a26b315554c1b90fd8..0badadc77eeb9923c8bd7d192986dd70f3bf0880 100644 (file)
@@ -27,6 +27,8 @@
  * SOFTWARE.
  */
 
+#include <limits>
+
 #include <QApplication>
 #include <QClipboard>
 #include <QDebug>
@@ -40,6 +42,8 @@
 
 #include "QHexView.hpp"
 
+using std::make_pair;
+
 const unsigned int BYTES_PER_LINE   = 16;
 const unsigned int HEXCHARS_IN_LINE = BYTES_PER_LINE * 3 - 1;
 const unsigned int GAP_ADR_HEX      = 10;
@@ -53,7 +57,9 @@ QHexView::QHexView(QWidget *parent):
        data_(nullptr),
        selectBegin_(0),
        selectEnd_(0),
-       cursorPos_(0)
+       cursorPos_(0),
+       visible_range_(0, 0),
+       highlighted_sample_(std::numeric_limits<uint64_t>::max())
 {
        setFont(QFont("Courier", 10));
 
@@ -67,11 +73,13 @@ QHexView::QHexView(QWidget *parent):
                chunk_colors_.emplace_back(100, 149, 237); // QColorConstants::Svg::cornflowerblue
                chunk_colors_.emplace_back(60, 179, 113);  // QColorConstants::Svg::mediumseagreen
                chunk_colors_.emplace_back(210, 180, 140); // QColorConstants::Svg::tan
+               visible_range_color_ = QColor("#fff5ee");  // QColorConstants::Svg::seashell
        } else {
                // Color is dark
-               chunk_colors_.emplace_back(0, 0, 139);   // QColorConstants::Svg::darkblue
-               chunk_colors_.emplace_back(34, 139, 34); // QColorConstants::Svg::forestgreen
-               chunk_colors_.emplace_back(160, 82, 45); // QColorConstants::Svg::sienna
+               chunk_colors_.emplace_back(0, 0, 139);    // QColorConstants::Svg::darkblue
+               chunk_colors_.emplace_back(34, 139, 34);  // QColorConstants::Svg::forestgreen
+               chunk_colors_.emplace_back(160, 82, 45);  // QColorConstants::Svg::sienna
+               visible_range_color_ = QColor("#fff5ee"); // QColorConstants::Svg::seashell
        }
 }
 
@@ -105,6 +113,20 @@ void QHexView::set_data(const DecodeBinaryClass* data)
        viewport()->update();
 }
 
+void QHexView::set_visible_sample_range(uint64_t start, uint64_t end)
+{
+       visible_range_ = make_pair(start, end);
+
+       viewport()->update();
+}
+
+void QHexView::set_highlighted_data_sample(uint64_t sample)
+{
+       highlighted_sample_ = sample;
+
+       viewport()->update();
+}
+
 unsigned int QHexView::get_bytes_per_line() const
 {
        return BYTES_PER_LINE;
@@ -116,6 +138,8 @@ void QHexView::clear()
        data_ = nullptr;
        data_size_ = 0;
 
+       highlighted_sample_ = std::numeric_limits<uint64_t>::max();
+
        viewport()->update();
 }
 
@@ -216,17 +240,35 @@ void QHexView::initialize_byte_iterator(size_t offset)
 
        if (current_chunk_id_ < data_->chunks.size())
                current_chunk_ = data_->chunks[current_chunk_id_];
+
+       current_chunk_sample_ = current_chunk_.sample;
+
+       // Obtain sample of next chunk if there is one
+       if ((current_chunk_id_ + 1) < data_->chunks.size())
+               next_chunk_sample_ = data_->chunks[current_chunk_id_ + 1].sample;
+       else
+               next_chunk_sample_ = std::numeric_limits<uint64_t>::max();
 }
 
-uint8_t QHexView::get_next_byte(bool* is_next_chunk)
+uint8_t QHexView::get_next_byte(bool* is_new_chunk)
 {
-       if (is_next_chunk != nullptr)
-               *is_next_chunk = (current_chunk_offset_ == 0);
+       if (is_new_chunk != nullptr)
+               *is_new_chunk = (current_chunk_offset_ == 0);
 
        uint8_t v = 0;
        if (current_chunk_offset_ < current_chunk_.data.size())
                v = current_chunk_.data[current_chunk_offset_];
 
+       current_chunk_sample_ = current_chunk_.sample;
+
+       if (is_new_chunk) {
+               // Obtain sample of next chunk if there is one
+               if ((current_chunk_id_ + 1) < data_->chunks.size())
+                       next_chunk_sample_ = data_->chunks[current_chunk_id_ + 1].sample;
+               else
+                       next_chunk_sample_ = std::numeric_limits<uint64_t>::max();
+       }
+
        current_offset_++;
        current_chunk_offset_++;
 
@@ -269,6 +311,12 @@ void QHexView::paintEvent(QPaintEvent *event)
 {
        QPainter painter(viewport());
 
+       QFont normal_font = painter.font();
+       QFont bold_font = painter.font();
+       bold_font.setWeight(QFont::Bold);
+
+       bool bold_font_was_used = false;
+
        // Calculate and update the widget and paint area sizes
        QSize widgetSize = getFullSize();
        setMinimumWidth(widgetSize.width());
@@ -334,13 +382,15 @@ void QHexView::paintEvent(QPaintEvent *event)
                        charHeight_ - 3, QString::number(offset, 16).toUpper());
 
        // Paint hex values
-       QBrush regular = palette().buttonText();
-       QBrush selected = palette().highlight();
+       QBrush regular_brush = palette().buttonText();
+       QBrush selected_brush = palette().highlight();
+       QBrush visible_range_brush = QBrush(visible_range_color_);
 
        bool multiple_chunks = (data_->chunks.size() > 1);
        unsigned int chunk_color = 0;
 
        initialize_byte_iterator(firstLineIdx * BYTES_PER_LINE);
+
        yStart = 2 * charHeight_;
        for (size_t lineIdx = firstLineIdx, y = yStart; lineIdx < lastLineIdx; lineIdx++) {
 
@@ -349,26 +399,42 @@ void QHexView::paintEvent(QPaintEvent *event)
                        size_t pos = (lineIdx * BYTES_PER_LINE + i) * 2;
 
                        // Fetch byte
-                       bool is_next_chunk;
-                       uint8_t byte_value = get_next_byte(&is_next_chunk);
+                       bool is_new_chunk;
+                       uint8_t byte_value = get_next_byte(&is_new_chunk);
 
-                       if (is_next_chunk) {
+                       if (is_new_chunk) {
                                chunk_color++;
                                if (chunk_color == chunk_colors_.size())
                                        chunk_color = 0;
+
+                               // New chunk means also new chunk sample, so check for required changes
+                               if (bold_font_was_used)
+                                       painter.setFont(normal_font);
+                               if ((highlighted_sample_ >= current_chunk_sample_) && (highlighted_sample_ < next_chunk_sample_)) {
+                                       painter.setFont(bold_font);
+                                       bold_font_was_used = true;
+                               }
+                       }
+
+                       // Restore default paint style
+                       painter.setBackground(regular_brush);
+                       painter.setBackgroundMode(Qt::TransparentMode);
+                       if (!multiple_chunks)
+                               painter.setPen(palette().color(QPalette::Text));
+                       else
+                               painter.setPen(chunk_colors_[chunk_color]);
+
+                       // Highlight needed because it's the range visible in main view?
+                       if ((current_chunk_sample_ >= visible_range_.first) && (current_chunk_sample_ < visible_range_.second)) {
+                               painter.setBackgroundMode(Qt::OpaqueMode);
+                               painter.setBackground(visible_range_brush);
                        }
 
+                       // Highlight for selection range needed? (takes priority over visible range highlight)
                        if ((pos >= selectBegin_) && (pos < selectEnd_)) {
                                painter.setBackgroundMode(Qt::OpaqueMode);
-                               painter.setBackground(selected);
+                               painter.setBackground(selected_brush);
                                painter.setPen(palette().color(QPalette::HighlightedText));
-                       } else {
-                               painter.setBackground(regular);
-                               painter.setBackgroundMode(Qt::TransparentMode);
-                               if (!multiple_chunks)
-                                       painter.setPen(palette().color(QPalette::Text));
-                               else
-                                       painter.setPen(chunk_colors_[chunk_color]);
                        }
 
                        // First nibble
@@ -379,7 +445,7 @@ void QHexView::paintEvent(QPaintEvent *event)
                        val = QString::number((byte_value & 0xF), 16).toUpper();
                        painter.drawText(x + charWidth_, y, val);
 
-                       if ((pos >= selectBegin_) && (pos < selectEnd_ - 1) && (i < BYTES_PER_LINE - 1))
+                       if ((i < BYTES_PER_LINE - 1) && (current_offset_ < data_size_))
                                painter.drawText(x + 2 * charWidth_, y, QString(' '));
 
                        x += 3 * charWidth_;
@@ -396,29 +462,56 @@ void QHexView::paintEvent(QPaintEvent *event)
                int x = posAscii_;
                for (size_t i = 0; (i < BYTES_PER_LINE) && (current_offset_ < data_size_); i++) {
                        // Fetch byte
-                       uint8_t ch = get_next_byte();
+                       bool is_new_chunk;
+                       uint8_t ch = get_next_byte(&is_new_chunk);
+
+                       if (is_new_chunk) {
+                               // New chunk means also new chunk sample, so check for required changes
+                               if (bold_font_was_used)
+                                       painter.setFont(normal_font);
+                               if ((highlighted_sample_ >= current_chunk_sample_) && (highlighted_sample_ < next_chunk_sample_)) {
+                                       painter.setFont(bold_font);
+                                       bold_font_was_used = true;
+                               }
+                       }
 
                        if ((ch < 0x20) || (ch > 0x7E))
                                ch = '.';
 
+                       // Restore default paint style
+                       painter.setBackgroundMode(Qt::TransparentMode);
+                       painter.setBackground(regular_brush);
+                       painter.setPen(palette().color(QPalette::Text));
+
+                       // Highlight needed because it's the range visible in main view?
+                       if ((current_chunk_sample_ >= visible_range_.first) && (current_chunk_sample_ < visible_range_.second)) {
+                               painter.setBackgroundMode(Qt::OpaqueMode);
+                               painter.setBackground(visible_range_brush);
+                       }
+
+                       // Highlight for selection range needed? (takes priority over visible range highlight)
                        size_t pos = (lineIdx * BYTES_PER_LINE + i) * 2;
                        if ((pos >= selectBegin_) && (pos < selectEnd_)) {
                                painter.setBackgroundMode(Qt::OpaqueMode);
-                               painter.setBackground(selected);
+                               painter.setBackground(selected_brush);
                                painter.setPen(palette().color(QPalette::HighlightedText));
-                       } else {
-                               painter.setBackgroundMode(Qt::TransparentMode);
-                               painter.setBackground(regular);
-                               painter.setPen(palette().color(QPalette::Text));
                        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       painter.drawText(x, y, QString(QChar(ch)));
+#else
                        painter.drawText(x, y, QString(ch));
+#endif
                        x += charWidth_;
                }
 
                y += charHeight_;
        }
 
+       // Restore painter defaults
+       painter.setBackgroundMode(Qt::TransparentMode);
+       painter.setBackground(regular_brush);
+
        // Paint cursor
        if (hasFocus()) {
                int x = (cursorPos_ % (2 * BYTES_PER_LINE));
index 5f46ba1cae72078a76ed1cbb49fd79719fa75475..4ba995e30148e44ed479adb01fe46dd14e4f18be 100644 (file)
@@ -54,6 +54,13 @@ public:
 
        void set_mode(Mode m);
        void set_data(const DecodeBinaryClass* data);
+
+       /* Sets range of samples that are visible in the main view */
+       void set_visible_sample_range(uint64_t start, uint64_t end);
+
+       /* Sets sample whose associated data we should highlight */
+       void set_highlighted_data_sample(uint64_t sample);
+
        unsigned int get_bytes_per_line() const;
 
        void clear();
@@ -67,7 +74,7 @@ public:
 
 protected:
        void initialize_byte_iterator(size_t offset);
-       uint8_t get_next_byte(bool* is_next_chunk = nullptr);
+       uint8_t get_next_byte(bool* is_new_chunk = nullptr);
 
        void paintEvent(QPaintEvent *event);
        void keyPressEvent(QKeyEvent *event);
@@ -95,8 +102,13 @@ private:
 
        size_t current_chunk_id_, current_chunk_offset_, current_offset_;
        DecodeBinaryDataChunk current_chunk_; // Cache locally so that we're not messed up when the vector is re-allocating its data
+       uint64_t current_chunk_sample_, next_chunk_sample_;
+
+       pair<uint64_t, uint64_t> visible_range_;
+       uint64_t highlighted_sample_;
 
        vector<QColor> chunk_colors_;
+       QColor visible_range_color_;
 };
 
 #endif // PULSEVIEW_PV_VIEWS_DECODER_BINARY_QHEXVIEW_HPP
index b5daaf0f84ef472dd7586cb9ab423a13cec6f156..6e61fa3aa1aa8bf688273ca5e8872c131435996a 100644 (file)
@@ -110,7 +110,11 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) :
        save_action_->setText(tr("&Save..."));
        save_action_->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       save_action_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+#else
        save_action_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+#endif
        connect(save_action_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionSave_triggered()));
 
@@ -129,9 +133,17 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) :
 
        parent->setSizePolicy(hex_view_->sizePolicy()); // TODO Must be updated when selected widget changes
 
+       // Set up metadata event handler
+       session_.metadata_obj_manager()->add_observer(this);
+
        reset_view_state();
 }
 
+View::~View()
+{
+       session_.metadata_obj_manager()->remove_observer(this);
+}
+
 ViewType View::get_type() const
 {
        return ViewTypeDecoderBinary;
@@ -319,10 +331,18 @@ void View::save_data_as_hex_dump(bool with_offset, bool with_ascii) const
                while (offset < selection.second) {
                        size_t end = std::min((uint64_t)(selection.second), offset + n);
                        offset = hex_view_->create_hex_line(offset, end, &s, with_offset, with_ascii);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+                       out_stream << s << Qt::endl;
+#else
                        out_stream << s << endl;
+#endif
                }
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+               out_stream << Qt::endl;
+#else
                out_stream << endl;
+#endif
 
                if (out_stream.status() != QTextStream::Ok) {
                        QMessageBox msg(parent_);
@@ -466,6 +486,25 @@ void View::on_actionSave_triggered(QAction* action)
        }
 }
 
+void View::on_metadata_object_changed(MetadataObject* obj,
+       MetadataValueType value_type)
+{
+       // Check if we need to update the model's data range. We only work on the
+       // end sample value because the start sample value is updated first and
+       // we need both
+       if ((obj->type() == MetadataObjMainViewRange) &&
+               (value_type == MetadataValueEndSample)) {
+
+               int64_t start_sample = obj->value(MetadataValueStartSample).toLongLong();
+               int64_t end_sample = obj->value(MetadataValueEndSample).toLongLong();
+
+               hex_view_->set_visible_sample_range(start_sample, end_sample);
+       }
+
+       if (obj->type() == MetadataObjMousePos)
+               hex_view_->set_highlighted_data_sample(obj->value(MetadataValueStartSample).toLongLong());
+}
+
 void View::perform_delayed_view_update()
 {
        if (signal_ && !binary_data_exists_)
index eea6666139a34b76685c36917de26f8d0a953837..530e60369de6e379941ef77ec1adbcff3c37e5c9 100644 (file)
@@ -25,8 +25,9 @@
 #include <QStackedWidget>
 #include <QToolButton>
 
-#include <pv/views/viewbase.hpp>
-#include <pv/data/decodesignal.hpp>
+#include "pv/metadata_obj.hpp"
+#include "pv/views/viewbase.hpp"
+#include "pv/data/decodesignal.hpp"
 
 #include "QHexView.hpp"
 
@@ -50,12 +51,13 @@ enum SaveType {
 extern const char* SaveTypeNames[SaveTypeCount];
 
 
-class View : public ViewBase
+class View : public ViewBase, public MetadataObjObserverInterface
 {
        Q_OBJECT
 
 public:
        explicit View(Session &session, bool is_main_view=false, QMainWindow *parent = nullptr);
+       ~View();
 
        virtual ViewType get_type() const;
 
@@ -90,6 +92,9 @@ private Q_SLOTS:
 
        void on_actionSave_triggered(QAction* action = nullptr);
 
+       virtual void on_metadata_object_changed(MetadataObject* obj,
+               MetadataValueType value_type);
+
        virtual void perform_delayed_view_update();
 
 private:
index e7a199f13da19248016e61e16d4c9065e1ac589f..def753b2fa44b99f8e71d1db508ed103d25b422d 100644 (file)
@@ -228,7 +228,9 @@ void AnnotationCollectionModel::set_signal_and_segment(data::DecodeSignal* signa
                return;
        }
 
-       disconnect(this, SLOT(on_annotation_visibility_changed()));
+       if (signal_)
+               for (const shared_ptr<Decoder>& dec : signal_->decoder_stack())
+                       disconnect(dec.get(), nullptr, this, SLOT(on_annotation_visibility_changed()));
 
        all_annotations_ = signal->get_all_annotations_by_segment(current_segment);
        signal_ = signal;
index 0aca4790e8ecc6d984df00129811a1a82fae7077..c1d7429080a221d0d3015d66b8f9fdece849ccb2 100644 (file)
@@ -202,7 +202,11 @@ View::View(Session &session, bool is_main_view, QMainWindow *parent) :
        save_action_->setText(tr("&Save..."));
        save_action_->setIcon(QIcon::fromTheme("document-save-as",
                QIcon(":/icons/document-save-as.png")));
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       save_action_->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_S));
+#else
        save_action_->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_S));
+#endif
        connect(save_action_, SIGNAL(triggered(bool)),
                this, SLOT(on_actionSave_triggered()));
 
@@ -662,7 +666,6 @@ void View::on_metadata_object_changed(MetadataObject* obj,
        // Check if we need to update the model's data range. We only work on the
        // end sample value because the start sample value is updated first and
        // we don't want to update the model twice
-
        if ((view_mode_selector_->currentIndex() == ViewModeVisible) &&
                (obj->type() == MetadataObjMainViewRange) &&
                (value_type == MetadataValueEndSample)) {
index e6b6d4966b710b0b6043926439083908f6233152..2a50d5716af2aaf9f88c9468b1e00f50f7b76b63 100644 (file)
@@ -75,7 +75,6 @@ const QPen AnalogSignal::AxisPen(QColor(0, 0, 0, 30 * 256 / 100), 2);
 const QColor AnalogSignal::GridMajorColor = QColor(0, 0, 0, 40 * 256 / 100);
 const QColor AnalogSignal::GridMinorColor = QColor(0, 0, 0, 20 * 256 / 100);
 
-const QColor AnalogSignal::SamplingPointColor(0x77, 0x77, 0x77);
 const QColor AnalogSignal::SamplingPointColorLo = QColor(200, 0, 0, 80 * 256 / 100);
 const QColor AnalogSignal::SamplingPointColorNe = QColor(0,   0, 0, 80 * 256 / 100);
 const QColor AnalogSignal::SamplingPointColorHi = QColor(0, 200, 0, 80 * 256 / 100);
@@ -95,22 +94,20 @@ const int AnalogSignal::MaxScaleIndex = 10;  // 1000 units/div
 const int AnalogSignal::InfoTextMarginRight = 20;
 const int AnalogSignal::InfoTextMarginBottom = 5;
 
-AnalogSignal::AnalogSignal(
-       pv::Session &session,
-       shared_ptr<data::SignalBase> base) :
-       Signal(session, base),
+AnalogSignal::AnalogSignal(pv::Session &session, shared_ptr<data::SignalBase> base) :
+       LogicSignal(session, base),
        value_at_hover_pos_(std::numeric_limits<float>::quiet_NaN()),
        scale_index_(4), // 20 per div
        pos_vdivs_(1),
        neg_vdivs_(1),
        resolution_(0),
-       display_type_(DisplayBoth),
+       display_type_(DisplayAnalog),
        autoranging_(true)
 {
        axis_pen_ = AxisPen;
 
        pv::data::Analog* analog_data =
-               dynamic_cast<pv::data::Analog*>(data().get());
+               dynamic_cast<pv::data::Analog*>(base_->analog_data().get());
 
        connect(analog_data, SIGNAL(min_max_changed(float, float)),
                this, SLOT(on_min_max_changed(float, float)));
@@ -128,23 +125,21 @@ AnalogSignal::AnalogSignal(
                settings.value(GlobalSettings::Key_View_ConversionThresholdDispMode).toInt();
        div_height_ = settings.value(GlobalSettings::Key_View_DefaultDivHeight).toInt();
 
+       update_logic_level_offsets();
        update_scale();
 }
 
-shared_ptr<pv::data::SignalData> AnalogSignal::data() const
-{
-       return base_->analog_data();
-}
-
 std::map<QString, QVariant> AnalogSignal::save_settings() const
 {
+       LogicSignal::save_settings();
+
        std::map<QString, QVariant> result;
 
        result["pos_vdivs"] = pos_vdivs_;
        result["neg_vdivs"] = neg_vdivs_;
        result["scale_index"] = scale_index_;
        result["display_type"] = display_type_;
-       result["autoranging"] = pos_vdivs_;
+       result["autoranging"] = autoranging_;
        result["div_height"] = div_height_;
 
        return result;
@@ -152,6 +147,8 @@ std::map<QString, QVariant> AnalogSignal::save_settings() const
 
 void AnalogSignal::restore_settings(std::map<QString, QVariant> settings)
 {
+       LogicSignal::restore_settings(settings);
+
        auto entry = settings.find("pos_vdivs");
        if (entry != settings.end())
                pos_vdivs_ = settings["pos_vdivs"].toInt();
@@ -179,6 +176,9 @@ void AnalogSignal::restore_settings(std::map<QString, QVariant> settings)
                const int old_height = div_height_;
                div_height_ = settings["div_height"].toInt();
 
+               update_logic_level_offsets();
+               update_scale();
+
                if ((div_height_ != old_height) && owner_) {
                        // Call order is important, otherwise the lazy event handler won't work
                        owner_->extents_changed(false, true);
@@ -191,6 +191,7 @@ pair<int, int> AnalogSignal::v_extents() const
 {
        const int ph = pos_vdivs_ * div_height_;
        const int nh = neg_vdivs_ * div_height_;
+
        return make_pair(-ph, nh);
 }
 
@@ -282,7 +283,8 @@ void AnalogSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
        }
 
        if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth))
-               paint_logic_mid(p, pp);
+               if (base_->logic_data())
+                       LogicSignal::paint_mid(p, pp);
 
        const QString err = base_->get_error_message();
        if (!err.isEmpty())
@@ -324,10 +326,13 @@ void AnalogSignal::paint_fore(QPainter &p, ViewItemPaintParams &pp)
                                v_extents().second - v_extents().first - InfoTextMarginBottom);
 
                p.drawText(bounding_rect, Qt::AlignRight | Qt::AlignBottom, infotext);
+
+               if (show_hover_marker_)
+                       paint_hover_marker(p);
        }
 
-       if (show_hover_marker_)
-               paint_hover_marker(p);
+       if ((display_type_ == DisplayConverted) || (display_type_ == DisplayBoth))
+               LogicSignal::paint_fore(p, pp);
 }
 
 void AnalogSignal::paint_grid(QPainter &p, int y, int left, int right)
@@ -527,186 +532,6 @@ void AnalogSignal::paint_envelope(QPainter &p,
        delete[] e.samples;
 }
 
-void AnalogSignal::paint_logic_mid(QPainter &p, ViewItemPaintParams &pp)
-{
-       QLineF *line;
-
-       vector< pair<int64_t, bool> > edges;
-
-       assert(base_);
-
-       const int y = get_visual_y();
-
-       if (!base_->enabled() || !base_->logic_data())
-               return;
-
-       const int signal_margin =
-               QFontMetrics(QApplication::font()).height() / 2;
-
-       const int ph = min(pos_vdivs_, 1) * div_height_;
-       const int nh = min(neg_vdivs_, 1) * div_height_;
-       const float high_offset = y - ph + signal_margin + 0.5f;
-       const float low_offset = y + nh - signal_margin - 0.5f;
-       const float signal_height = low_offset - high_offset;
-
-       shared_ptr<pv::data::LogicSegment> segment = get_logic_segment_to_paint();
-       if (!segment || (segment->get_sample_count() == 0))
-               return;
-
-       double samplerate = segment->samplerate();
-
-       // Show sample rate as 1Hz when it is unknown
-       if (samplerate == 0.0)
-               samplerate = 1.0;
-
-       const double pixels_offset = pp.pixels_offset();
-       const pv::util::Timestamp& start_time = segment->start_time();
-       const int64_t last_sample = (int64_t)segment->get_sample_count() - 1;
-       const double samples_per_pixel = samplerate * pp.scale();
-       const double pixels_per_sample = 1 / samples_per_pixel;
-       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 / LogicSignal::Oversampling, 0);
-       assert(edges.size() >= 2);
-
-       const float first_sample_x =
-               pp.left() + (edges.front().first / samples_per_pixel - pixels_offset);
-       const float last_sample_x =
-               pp.left() + (edges.back().first / samples_per_pixel - pixels_offset);
-
-       // Check whether we need to paint the sampling points
-       const bool show_sampling_points = show_sampling_points_ && (samples_per_pixel < 0.25);
-       vector<QRectF> sampling_points;
-       float sampling_point_x = first_sample_x;
-       int64_t sampling_point_sample = start_sample;
-       const int w = 2;
-
-       if (show_sampling_points)
-               sampling_points.reserve(end_sample - start_sample + 1);
-
-       vector<QRectF> high_rects;
-       float rising_edge_x;
-       bool rising_edge_seen = false;
-
-       // Paint the edges
-       const unsigned int edge_count = edges.size() - 2;
-       QLineF *const edge_lines = new QLineF[edge_count];
-       line = edge_lines;
-
-       if (edges.front().second) {
-               // Beginning of trace is high
-               rising_edge_x = first_sample_x;
-               rising_edge_seen = true;
-       }
-
-       for (auto i = edges.cbegin() + 1; i != edges.cend() - 1; i++) {
-               // Note: multiple edges occupying a single pixel are represented by an edge
-               // with undefined logic level. This means that only the first falling edge
-               // after a rising edge corresponds to said rising edge - and vice versa. If
-               // more edges with the same logic level follow, they denote multiple edges.
-
-               const float x = pp.left() + ((*i).first / samples_per_pixel - pixels_offset);
-               *line++ = QLineF(x, high_offset, x, low_offset);
-
-               if (fill_high_areas_) {
-                       // Any edge terminates a high area
-                       if (rising_edge_seen) {
-                               const int width = x - rising_edge_x;
-                               if (width > 0)
-                                       high_rects.emplace_back(rising_edge_x, high_offset,
-                                               width, signal_height);
-                               rising_edge_seen = false;
-                       }
-
-                       // Only rising edges start high areas
-                       if ((*i).second) {
-                               rising_edge_x = x;
-                               rising_edge_seen = true;
-                       }
-               }
-
-               if (show_sampling_points)
-                       while (sampling_point_sample < (*i).first) {
-                               const float y = (*i).second ? low_offset : high_offset;
-                               sampling_points.emplace_back(
-                                       QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
-                               sampling_point_sample++;
-                               sampling_point_x += pixels_per_sample;
-                       };
-       }
-
-       // Calculate the sample points from the last edge to the end of the trace
-       if (show_sampling_points)
-               while ((uint64_t)sampling_point_sample <= end_sample) {
-                       // Signal changed after the last edge, so the level is inverted
-                       const float y = (edges.cend() - 1)->second ? high_offset : low_offset;
-                       sampling_points.emplace_back(
-                               QRectF(sampling_point_x - (w / 2), y - (w / 2), w, w));
-                       sampling_point_sample++;
-                       sampling_point_x += pixels_per_sample;
-               };
-
-       if (fill_high_areas_) {
-               // Add last high rectangle if the signal is still high at the end of the trace
-               if (rising_edge_seen && (edges.cend() - 1)->second)
-                       high_rects.emplace_back(rising_edge_x, high_offset,
-                               last_sample_x - rising_edge_x, signal_height);
-
-               p.setPen(high_fill_color_);
-               p.setBrush(high_fill_color_);
-               p.drawRects((const QRectF*)(high_rects.data()), high_rects.size());
-       }
-
-       p.setPen(LogicSignal::EdgeColor);
-       p.drawLines(edge_lines, edge_count);
-       delete[] edge_lines;
-
-       // Paint the caps
-       const unsigned int max_cap_line_count = edges.size();
-       QLineF *const cap_lines = new QLineF[max_cap_line_count];
-
-       p.setPen(LogicSignal::HighColor);
-       paint_logic_caps(p, cap_lines, edges, true, samples_per_pixel,
-               pixels_offset, pp.left(), high_offset);
-       p.setPen(LogicSignal::LowColor);
-       paint_logic_caps(p, cap_lines, edges, false, samples_per_pixel,
-               pixels_offset, pp.left(), low_offset);
-
-       delete[] cap_lines;
-
-       // Paint the sampling points
-       if (show_sampling_points) {
-               p.setPen(SamplingPointColor);
-               p.drawRects(sampling_points.data(), sampling_points.size());
-       }
-}
-
-void AnalogSignal::paint_logic_caps(QPainter &p, QLineF *const lines,
-       vector< pair<int64_t, bool> > &edges, bool level,
-       double samples_per_pixel, double pixels_offset, float x_offset,
-       float y_offset)
-{
-       QLineF *line = lines;
-
-       for (auto i = edges.begin(); i != (edges.end() - 1); i++)
-               if ((*i).second == level) {
-                       *line++ = QLineF(
-                               ((*i).first / samples_per_pixel -
-                                       pixels_offset) + x_offset, y_offset,
-                               ((*(i+1)).first / samples_per_pixel -
-                                       pixels_offset) + x_offset, y_offset);
-               }
-
-       p.drawLines(lines, line - lines);
-}
-
 shared_ptr<pv::data::AnalogSegment> AnalogSignal::get_analog_segment_to_paint() const
 {
        shared_ptr<pv::data::AnalogSegment> segment;
@@ -719,7 +544,7 @@ shared_ptr<pv::data::AnalogSegment> AnalogSignal::get_analog_segment_to_paint()
                        segment = segments.back();
 
                if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
-                               (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
+                       (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
                        try {
                                segment = segments.at(current_segment_);
                        } catch (out_of_range&) {
@@ -731,30 +556,6 @@ shared_ptr<pv::data::AnalogSegment> AnalogSignal::get_analog_segment_to_paint()
        return segment;
 }
 
-shared_ptr<pv::data::LogicSegment> AnalogSignal::get_logic_segment_to_paint() const
-{
-       shared_ptr<pv::data::LogicSegment> segment;
-
-       const deque< shared_ptr<pv::data::LogicSegment> > &segments =
-               base_->logic_data()->logic_segments();
-
-       if (!segments.empty()) {
-               if (segment_display_mode_ == ShowLastSegmentOnly)
-                       segment = segments.back();
-
-               if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
-                               (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
-                       try {
-                               segment = segments.at(current_segment_);
-                       } catch (out_of_range&) {
-                               qDebug() << "Current logic segment out of range for signal" << base_->name() << ":" << current_segment_;
-                       }
-               }
-       }
-
-       return segment;
-}
-
 float AnalogSignal::get_resolution(int scale_index)
 {
        const float seq[] = {1.0f, 2.0f, 5.0f};
@@ -772,6 +573,17 @@ void AnalogSignal::update_scale()
        scale_ = div_height_ / resolution_;
 }
 
+void AnalogSignal::update_logic_level_offsets()
+{
+       const int signal_margin = QFontMetrics(QApplication::font()).height() / 2;
+
+       const int ph = min(pos_vdivs_, 1) * div_height_;
+       const int nh = min(neg_vdivs_, 1) * div_height_;
+
+       high_level_offset_ = -ph + signal_margin + 0.5f;
+       low_level_offset_  =  nh - signal_margin - 0.5f;
+}
+
 void AnalogSignal::update_conversion_widgets()
 {
        SignalBase::ConversionType conv_type = base_->get_conversion_type();
@@ -884,16 +696,18 @@ void AnalogSignal::perform_autoranging(bool keep_divs, bool force_update)
                }
        }
 
+       const bool showing_logic = (display_type_ == DisplayConverted) || (display_type_ == DisplayBoth);
+
        // If there is still no positive div when we need it, add one
        // (this can happen when pos_vdivs==neg_vdivs==0)
-       if ((max > 0) && (pos_vdivs_ == 0)) {
+       if (((max > 0) && (pos_vdivs_ == 0)) || showing_logic) {
                pos_vdivs_ = 1;
                owner_->extents_changed(false, true);
        }
 
        // If there is still no negative div when we need it, add one
        // (this can happen when pos_vdivs was 0 or 1 when trying to split)
-       if ((min < 0) && (neg_vdivs_ == 0)) {
+       if (((min < 0) && (neg_vdivs_ == 0)) || showing_logic) {
                neg_vdivs_ = 1;
                owner_->extents_changed(false, true);
        }
@@ -983,6 +797,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        pvdiv_sb_ = new QSpinBox(parent);
        pvdiv_sb_->setRange(0, MaximumVDivs);
        pvdiv_sb_->setValue(pos_vdivs_);
+       pvdiv_sb_->setEnabled(!autoranging_);
        connect(pvdiv_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_pos_vdivs_changed(int)));
        form->addRow(tr("Number of pos vertical divs"), pvdiv_sb_);
@@ -990,6 +805,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
        nvdiv_sb_ = new QSpinBox(parent);
        nvdiv_sb_->setRange(0, MaximumVDivs);
        nvdiv_sb_->setValue(neg_vdivs_);
+       nvdiv_sb_->setEnabled(!autoranging_);
        connect(nvdiv_sb_, SIGNAL(valueChanged(int)),
                this, SLOT(on_neg_vdivs_changed(int)));
        form->addRow(tr("Number of neg vertical divs"), nvdiv_sb_);
@@ -1005,6 +821,7 @@ void AnalogSignal::populate_popup_form(QWidget *parent, QFormLayout *form)
 
        // Add the vertical resolution
        resolution_cb_ = new QComboBox(parent);
+       resolution_cb_->setEnabled(!autoranging_);
 
        for (int i = MinScaleIndex; i < MaxScaleIndex; i++) {
                const QString label = QString("%1").arg(get_resolution(i));
@@ -1156,6 +973,8 @@ void AnalogSignal::on_pos_vdivs_changed(int vdivs)
                }
        }
 
+       update_logic_level_offsets();
+
        if (owner_) {
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
@@ -1187,6 +1006,8 @@ void AnalogSignal::on_neg_vdivs_changed(int vdivs)
                }
        }
 
+       update_logic_level_offsets();
+
        if (owner_) {
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
@@ -1197,6 +1018,7 @@ void AnalogSignal::on_neg_vdivs_changed(int vdivs)
 void AnalogSignal::on_div_height_changed(int height)
 {
        div_height_ = height;
+       update_logic_level_offsets();
        update_scale();
 
        if (owner_) {
@@ -1219,6 +1041,13 @@ void AnalogSignal::on_autoranging_changed(int state)
 {
        autoranging_ = (state == Qt::Checked);
 
+       if (pvdiv_sb_)
+               pvdiv_sb_->setEnabled(!autoranging_);
+       if (nvdiv_sb_)
+               nvdiv_sb_->setEnabled(!autoranging_);
+       if (resolution_cb_)
+               resolution_cb_->setEnabled(!autoranging_);
+
        if (autoranging_)
                perform_autoranging(false, true);
 
@@ -1240,6 +1069,11 @@ void AnalogSignal::on_conversion_changed(int index)
                base_->set_conversion_type(conv_type);
                update_conversion_widgets();
 
+               if (conv_type == SignalBase::ConversionType::NoConversion)
+                       on_display_type_changed(DisplayType::DisplayAnalog);
+               else
+                       on_display_type_changed(DisplayType::DisplayBoth);
+
                if (owner_)
                        owner_->row_item_appearance_changed(false, true);
        }
@@ -1267,13 +1101,20 @@ void AnalogSignal::on_conv_threshold_changed(int index)
                // https://txt2re.com/index-c++.php3?s=0.1V&1&-13
                QString re1 = "([+-]?\\d*[\\.,]?\\d*)"; // Float value
                QString re2 = "([a-zA-Z]*)"; // SI unit
-               QRegExp regex(re1 + re2);
-
                const QString text = conv_threshold_cb_->currentText();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               QRegularExpression regex(re1 + re2);
+               if (!regex.match(text).hasMatch())
+                       return;  // String doesn't match the regex
+
+               QStringList tokens = regex.match(text).capturedTexts();
+#else
+               QRegExp regex(re1 + re2);
                if (!regex.exactMatch(text))
                        return;  // String doesn't match the regex
 
                QStringList tokens = regex.capturedTexts();
+#endif
 
                // For now, we simply assume that the unit is volt without modifiers
                const double thr = tokens.at(1).toDouble();
@@ -1294,13 +1135,22 @@ void AnalogSignal::on_conv_threshold_changed(int index)
                QString re3 = "\\/"; // Forward slash, not captured
                QString re4 = "([+-]?\\d*[\\.,]?\\d*)"; // Float value
                QString re5 = "([a-zA-Z]*)"; // SI unit
+               const QString text = conv_threshold_cb_->currentText();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               QRegularExpression regex(re1 + re2 + re3 + re4 + re5);
+
+               if (!regex.match(text).hasMatch())
+                       return;  // String doesn't match the regex
+
+               QStringList tokens = regex.match(text).capturedTexts();
+#else
                QRegExp regex(re1 + re2 + re3 + re4 + re5);
 
-               const QString text = conv_threshold_cb_->currentText();
                if (!regex.exactMatch(text))
                        return;  // String doesn't match the regex
 
                QStringList tokens = regex.capturedTexts();
+#endif
 
                // For now, we simply assume that the unit is volt without modifiers
                const double low_thr = tokens.at(1).toDouble();
@@ -1330,8 +1180,21 @@ void AnalogSignal::on_delayed_conversion_starter()
 
 void AnalogSignal::on_display_type_changed(int index)
 {
+       const bool prev_showing_logic = (display_type_ == DisplayConverted) || (display_type_ == DisplayBoth);
+
        display_type_ = (DisplayType)(display_type_cb_->itemData(index).toInt());
 
+       const bool showing_logic = (display_type_ == DisplayConverted) || (display_type_ == DisplayBoth);
+
+       // If we show a logic trace, make sure we have at least one div for each
+       // polarity as that's where we paint it
+       if (showing_logic && !prev_showing_logic) {
+               if (pos_vdivs_ == 0)
+                       on_pos_vdivs_changed(1);
+               if (neg_vdivs_ == 0)
+                       on_neg_vdivs_changed(1);
+       }
+
        if (owner_)
                owner_->row_item_appearance_changed(false, true);
 }
index 4c42e579b662e596289b65828b9a3c6edf4e3f56..6fbcc24b5ae7cadc709f3ffe0985d01225bd43e3 100644 (file)
@@ -27,6 +27,7 @@
 #include <QSpinBox>
 
 #include <pv/views/trace/signal.hpp>
+#include <pv/views/trace/logicsignal.hpp>
 
 using std::pair;
 using std::shared_ptr;
@@ -42,14 +43,13 @@ class SignalBase;
 namespace views {
 namespace trace {
 
-class AnalogSignal : public Signal
+class AnalogSignal : public LogicSignal
 {
        Q_OBJECT
 
 private:
        static const QPen AxisPen;
        static const QColor GridMajorColor, GridMinorColor;
-       static const QColor SamplingPointColor;
        static const QColor SamplingPointColorLo;
        static const QColor SamplingPointColorNe;
        static const QColor SamplingPointColorHi;
@@ -74,8 +74,6 @@ private:
 public:
        AnalogSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
 
-       shared_ptr<pv::data::SignalData> data() const;
-
        virtual std::map<QString, QVariant> save_settings() const;
        virtual void restore_settings(std::map<QString, QVariant> settings);
 
@@ -83,28 +81,28 @@ public:
         * Computes the vertical extents of the contents of this row item.
         * @return A pair containing the minimum and maximum y-values.
         */
-       pair<int, int> v_extents() const;
+       virtual pair<int, int> v_extents() const;
 
        /**
         * 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..
         */
-       void paint_back(QPainter &p, ViewItemPaintParams &pp);
+       virtual void paint_back(QPainter &p, ViewItemPaintParams &pp);
 
        /**
         * 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, ViewItemPaintParams &pp);
+       virtual void paint_mid(QPainter &p, 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.
         */
-       void paint_fore(QPainter &p, ViewItemPaintParams &pp);
+       virtual void paint_fore(QPainter &p, ViewItemPaintParams &pp);
 
 private:
        void paint_grid(QPainter &p, int y, int left, int right);
@@ -119,15 +117,7 @@ private:
                int y, int left, const int64_t start, const int64_t end,
                const double pixels_offset, const double samples_per_pixel);
 
-       void paint_logic_mid(QPainter &p, ViewItemPaintParams &pp);
-
-       void paint_logic_caps(QPainter &p, QLineF *const lines,
-               vector< pair<int64_t, bool> > &edges,
-               bool level, double samples_per_pixel, double pixels_offset,
-               float x_offset, float y_offset);
-
        shared_ptr<pv::data::AnalogSegment> get_analog_segment_to_paint() const;
-       shared_ptr<pv::data::LogicSegment> get_logic_segment_to_paint() const;
 
        /**
         * Computes the scale factor from the scale index and vdiv settings.
@@ -135,6 +125,7 @@ private:
        float get_resolution(int scale_index);
 
        void update_scale();
+       virtual void update_logic_level_offsets();
 
        void update_conversion_widgets();
 
index 80eaba232b1d8157b2bbfacbb40ebe202e79670a..8462a3a604c6e2509f0389c5ffbec96ba91c5908 100644 (file)
@@ -72,7 +72,7 @@ QRectF Cursor::label_rect(const QRectF &rect) const
        const float x = get_x();
 
        QFontMetrics m(QApplication::font());
-       QSize text_size = m.boundingRect(get_text()).size();
+       QSize text_size = m.boundingRect(get_display_text()).size();
 
        const QSizeF label_size(
                text_size.width() + LabelPadding.width() * 2,
index 7d7d8e4d6477d2e9efbd2a5b84f1bd3f52ec9d79..ec7b75addd75180b9b26d288c4b1a20cb805fae9 100644 (file)
@@ -202,11 +202,13 @@ void CursorPair::paint_label(QPainter &p, const QRect &rect, bool hover)
 
        text_size_ = p.boundingRect(QRectF(), 0, text).size();
 
+       /* Currently, selecting the middle section between two cursors doesn't do
+        * anything, so don't highlight it when selected
        if (selected()) {
                p.setBrush(Qt::transparent);
                p.setPen(highlight_pen());
                p.drawRoundedRect(delta_rect, radius, radius);
-       }
+       } */
 
        p.setBrush(hover ? Cursor::FillColor.lighter() : Cursor::FillColor);
        p.setPen(Cursor::FillColor.darker());
index 409f1d282abc209d7753509b42f2c810abf2f5eb..63384911d7f515b160249e94daa3c23b65fdd743 100644 (file)
@@ -17,9 +17,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
-extern "C" {
 #include <libsigrokdecode/libsigrokdecode.h>
-}
 
 #include <limits>
 #include <mutex>
@@ -188,7 +186,16 @@ DecodeTrace::DecodeTrace(pv::Session &session,
                this, SLOT(on_decode_finished()));
        connect(decode_signal_.get(), SIGNAL(channels_updated()),
                this, SLOT(on_channels_updated()));
-
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&delete_mapper_, SIGNAL(mappedInt(int)),
+               this, SLOT(on_delete_decoder(int)));
+       connect(&show_hide_mapper_, SIGNAL(mappedInt(int)),
+               this, SLOT(on_show_hide_decoder(int)));
+       connect(&row_show_hide_mapper_, SIGNAL(mappedInt(int)),
+               this, SLOT(on_show_hide_row(int)));
+       connect(&class_show_hide_mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_show_hide_class(QObject*)));
+#else
        connect(&delete_mapper_, SIGNAL(mapped(int)),
                this, SLOT(on_delete_decoder(int)));
        connect(&show_hide_mapper_, SIGNAL(mapped(int)),
@@ -197,6 +204,7 @@ DecodeTrace::DecodeTrace(pv::Session &session,
                this, SLOT(on_show_hide_row(int)));
        connect(&class_show_hide_mapper_, SIGNAL(mapped(QWidget*)),
                this, SLOT(on_show_hide_class(QWidget*)));
+#endif
 
        connect(&delayed_trace_updater_, SIGNAL(timeout()),
                this, SLOT(on_delayed_trace_update()));
@@ -669,10 +677,19 @@ void DecodeTrace::mouse_left_press_event(const QMouseEvent* event)
                        continue;
 
                unsigned int y = get_row_y(&r);
-               if ((event->x() > 0) && (event->x() <= (int)(ArrowSize + 3 + r.title_width)) &&
-                       (event->y() > (int)(y - (default_row_height_ / 2))) &&
-                       (event->y() <= (int)(y + (default_row_height_ / 2)))) {
-
+               bool need_anim = true;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+               need_anim &= event->position().x() > 0;
+               need_anim &= event->position().x() <= (int)(ArrowSize + 3 + r.title_width);
+               need_anim &= event->position().y() > (int)(y - (default_row_height_ / 2));
+               need_anim &= event->position().y() <= (int)(y + (default_row_height_ / 2));
+#else
+               need_anim &= event->x() > 0;
+               need_anim &= event->x() <= (int)(ArrowSize + 3 + r.title_width);
+               need_anim &= event->y() > (int)(y - (default_row_height_ / 2));
+               need_anim &= event->y() <= (int)(y + (default_row_height_ / 2));
+#endif
+               if (need_anim) {
                        if (r.expanded) {
                                r.collapsing = true;
                                r.expanded = false;
@@ -1251,6 +1268,19 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
        QPalette header_palette = owner_->view()->palette();
        QPalette selector_palette = owner_->view()->palette();
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       if (GlobalSettings::current_theme_is_dark()) {
+               header_palette.setColor(QPalette::Window,
+                       QColor(255, 255, 255, ExpansionAreaHeaderAlpha));
+               selector_palette.setColor(QPalette::Window,
+                       QColor(255, 255, 255, ExpansionAreaAlpha));
+       } else {
+               header_palette.setColor(QPalette::Window,
+                       QColor(0, 0, 0, ExpansionAreaHeaderAlpha));
+               selector_palette.setColor(QPalette::Window,
+                       QColor(0, 0, 0, ExpansionAreaAlpha));
+       }
+#else
        if (GlobalSettings::current_theme_is_dark()) {
                header_palette.setColor(QPalette::Background,
                        QColor(255, 255, 255, ExpansionAreaHeaderAlpha));
@@ -1262,6 +1292,7 @@ void DecodeTrace::initialize_row_widgets(DecodeTraceRow* r, unsigned int row_id)
                selector_palette.setColor(QPalette::Background,
                        QColor(0, 0, 0, ExpansionAreaAlpha));
        }
+#endif
 
        const int w = m.boundingRect(r->decode_row->title()).width() + RowTitleMargin;
        r->title_width = w;
@@ -1604,7 +1635,11 @@ void DecodeTrace::on_show_hide_row(int row_id)
        owner_->row_item_appearance_changed(false, true);
 }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+void DecodeTrace::on_show_hide_class(QObject* sender)
+#else
 void DecodeTrace::on_show_hide_class(QWidget* sender)
+#endif
 {
        void* ann_class_ptr = sender->property("ann_class_ptr").value<void*>();
        assert(ann_class_ptr);
index 80c9cf750b341f155fe1a6b077fe60dca6339b5a..0ba52b357c8ed80e108b25559b5aa2559716c5cc 100644 (file)
@@ -278,7 +278,11 @@ private Q_SLOTS:
 
        void on_show_hide_decoder(int index);
        void on_show_hide_row(int row_id);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       void on_show_hide_class(QObject* sender);
+#else
        void on_show_hide_class(QWidget* sender);
+#endif
        void on_show_all_classes();
        void on_hide_all_classes();
        void on_row_container_resized(QWidget* sender);
index b0518b646d87c493d8c13de606570f8dccf92522..c356b15bf62bba31341b0f22f8a24a2ed5813dd1 100644 (file)
@@ -57,7 +57,11 @@ bool Flag::enabled() const
        return true;
 }
 
-QString Flag::get_text() const
+/**
+ * Returns the text used to display this flag item. This is not necessarily the
+ * name that the user has given it.
+ */
+QString Flag::get_display_text() const
 {
        QString s;
 
@@ -73,6 +77,14 @@ QString Flag::get_text() const
        return s;
 }
 
+/**
+ * Returns the text of this flag item, i.e. the user-editable name.
+ */
+QString Flag::get_text() const
+{
+       return text_;
+}
+
 void Flag::set_text(const QString &text)
 {
        text_ = text;
@@ -92,7 +104,7 @@ QRectF Flag::label_rect(const QRectF &rect) const
                const float x = get_x();
 
                QFontMetrics m(QApplication::font());
-               QSize text_size = m.boundingRect(get_text()).size();
+               QSize text_size = m.boundingRect(get_display_text()).size();
 
                const QSizeF label_size(
                        text_size.width() + LabelPadding.width() * 2,
index 4c4c977f0d19254bf17b199c6ede54e688a2d60a..eb4bf87c6008d9f84f737e1dd4a0086812ce47e7 100644 (file)
@@ -63,7 +63,13 @@ public:
        virtual bool enabled() const override;
 
        /**
-        * Gets the text to show in the marker.
+        * Gets the current text to show in the marker - this may be dynamic.
+        */
+       virtual QString get_display_text() const override;
+
+       /**
+        * Gets the default text used to show the marker - e.g. the user-editable
+        * name.
         */
        virtual QString get_text() const override;
 
index 350f495048f3344d66a3d367e86a38a57138d27b..732d46d2d9e82e86e933a71b6c41760951ae4227 100644 (file)
@@ -86,12 +86,8 @@ const char* LogicSignal::TriggerMarkerIcons[8] = {
 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<data::SignalBase> base) :
+LogicSignal::LogicSignal(pv::Session &session, shared_ptr<data::SignalBase> base) :
        Signal(session, base),
-       device_(device),
        trigger_types_(get_trigger_types()),
        trigger_none_(nullptr),
        trigger_rising_(nullptr),
@@ -100,8 +96,6 @@ LogicSignal::LogicSignal(
        trigger_low_(nullptr),
        trigger_change_(nullptr)
 {
-       shared_ptr<Trigger> trigger;
-
        GlobalSettings settings;
        signal_height_ = settings.value(GlobalSettings::Key_View_DefaultLogicHeight).toInt();
        show_sampling_points_ =
@@ -111,26 +105,18 @@ LogicSignal::LogicSignal(
        high_fill_color_ = QColor::fromRgba(settings.value(
                GlobalSettings::Key_View_FillSignalHighAreaColor).value<uint32_t>());
 
+       update_logic_level_offsets();
+
        /* 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()))
+       if (shared_ptr<Trigger> trigger = session_.session()->trigger())
                for (auto stage : trigger->stages())
                        for (auto match : stage->matches())
                                if (match->channel() == base_->channel())
                                        trigger_match_ = match->type();
 }
 
-shared_ptr<pv::data::SignalData> LogicSignal::data() const
-{
-       return base_->logic_data();
-}
-
-shared_ptr<pv::data::Logic> LogicSignal::logic_data() const
-{
-       return base_->logic_data();
-}
-
 std::map<QString, QVariant> LogicSignal::save_settings() const
 {
        std::map<QString, QVariant> result;
@@ -148,6 +134,8 @@ void LogicSignal::restore_settings(std::map<QString, QVariant> settings)
                signal_height_ = settings["trace_height"].toInt();
 
                if ((signal_height_ != old_height) && owner_) {
+                       update_logic_level_offsets();
+
                        // Call order is important, otherwise the lazy event handler won't work
                        owner_->extents_changed(false, true);
                        owner_->row_item_appearance_changed(false, true);
@@ -176,8 +164,9 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
        if (!base_->enabled())
                return;
 
-       const float low_offset = y + 0.5f;
-       const float high_offset = low_offset - signal_height_;
+       const float low_offset = y + low_level_offset_;
+       const float high_offset = y + high_level_offset_;
+       const float fill_height = low_offset - high_offset;
 
        shared_ptr<LogicSegment> segment = get_logic_segment_to_paint();
        if (!segment || (segment->get_sample_count() == 0))
@@ -203,7 +192,7 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
                (int64_t)0), last_sample);
 
        segment->get_subsampled_edges(edges, start_sample, end_sample,
-               samples_per_pixel / Oversampling, base_->index());
+               samples_per_pixel / Oversampling, base_->logic_bit_index());
        assert(edges.size() >= 2);
 
        const float first_sample_x =
@@ -250,8 +239,7 @@ void LogicSignal::paint_mid(QPainter &p, ViewItemPaintParams &pp)
                        if (rising_edge_seen) {
                                const int width = x - rising_edge_x;
                                if (width > 0)
-                                       high_rects.emplace_back(rising_edge_x, high_offset,
-                                               width, signal_height_);
+                                       high_rects.emplace_back(rising_edge_x, high_offset, width, fill_height);
                                rising_edge_seen = false;
                        }
 
@@ -413,12 +401,11 @@ shared_ptr<pv::data::LogicSegment> LogicSignal::get_logic_segment_to_paint() con
                base_->logic_data()->logic_segments();
 
        if (!segments.empty()) {
-               if (segment_display_mode_ == ShowLastSegmentOnly) {
+               if (segment_display_mode_ == ShowLastSegmentOnly)
                        segment = segments.back();
-               }
 
-       if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
-               (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
+               if ((segment_display_mode_ == ShowSingleSegmentOnly) ||
+                       (segment_display_mode_ == ShowLastCompleteSegmentOnly)) {
                        try {
                                segment = segments.at(current_segment_);
                        } catch (out_of_range&) {
@@ -466,10 +453,10 @@ void LogicSignal::init_trigger_actions(QWidget *parent)
 const vector<int32_t> LogicSignal::get_trigger_types() const
 {
        // We may not be associated with a device
-       if (!device_)
+       if (!session_.device())
                return vector<int32_t>();
 
-       const auto sr_dev = device_->device();
+       const auto sr_dev = session_.device()->device();
        if (sr_dev->config_check(ConfigKey::TRIGGER_MATCH, Capability::LIST)) {
                const Glib::VariantContainerBase gvar =
                        sr_dev->config_list(ConfigKey::TRIGGER_MATCH);
@@ -637,6 +624,12 @@ const QPixmap* LogicSignal::get_pixmap(const char *path)
        return pixmap_cache_.take(path);
 }
 
+void LogicSignal::update_logic_level_offsets()
+{
+       low_level_offset_ = 0.5f;
+       high_level_offset_ = low_level_offset_ - signal_height_;
+}
+
 void LogicSignal::on_setting_changed(const QString &key, const QVariant &value)
 {
        Signal::on_setting_changed(key, value);
@@ -669,6 +662,8 @@ void LogicSignal::on_signal_height_changed(int height)
        signal_height_ = height;
 
        if (owner_) {
+               update_logic_level_offsets();
+
                // Call order is important, otherwise the lazy event handler won't work
                owner_->extents_changed(false, true);
                owner_->row_item_appearance_changed(false, true);
index d316fd5de7947ca8934cd4b471b4cfb4ef14877c..eee6b9c4ce081f4b74d6ccf30ffe206e6aa6965b 100644 (file)
@@ -69,16 +69,10 @@ public:
        static const int TriggerMarkerPadding;
        static const char* TriggerMarkerIcons[8];
 
-       LogicSignal(pv::Session &session,
-               shared_ptr<devices::Device> device,
-               shared_ptr<data::SignalBase> base);
+       LogicSignal(pv::Session &session, shared_ptr<data::SignalBase> base);
 
        virtual ~LogicSignal() = default;
 
-       shared_ptr<pv::data::SignalData> data() const;
-
-       shared_ptr<pv::data::Logic> logic_data() const;
-
        virtual std::map<QString, QVariant> save_settings() const;
        virtual void restore_settings(std::map<QString, QVariant> settings);
 
@@ -86,14 +80,14 @@ public:
         * Computes the vertical extents of the contents of this row item.
         * @return A pair containing the minimum and maximum y-values.
         */
-       pair<int, int> v_extents() const;
+       virtual pair<int, int> v_extents() const;
 
        /**
         * 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, ViewItemPaintParams &pp);
+       virtual void paint_mid(QPainter &p, ViewItemPaintParams &pp);
 
        /**
         * Paints the foreground layer of the signal with a QPainter
@@ -111,7 +105,7 @@ public:
         */
        virtual vector<data::LogicSegment::EdgePair> get_nearest_level_changes(uint64_t sample_pos);
 
-private:
+protected:
        void paint_caps(QPainter &p, QLineF *const lines,
                vector< pair<int64_t, bool> > &edges,
                bool level, double samples_per_pixel, double pixels_offset,
@@ -131,18 +125,19 @@ private:
        static const QIcon* get_icon(const char *path);
        static const QPixmap* get_pixmap(const char *path);
 
-private Q_SLOTS:
+       virtual void update_logic_level_offsets();
+
+protected Q_SLOTS:
        void on_setting_changed(const QString &key, const QVariant &value);
 
        void on_trigger();
 
        void on_signal_height_changed(int height);
 
-private:
+protected:
        QColor high_fill_color_;
        bool show_sampling_points_, fill_high_areas_;
-
-       shared_ptr<pv::devices::Device> device_;
+       float high_level_offset_, low_level_offset_;  // y offsets relative to trace
 
        QSpinBox *signal_height_sb_;
 
index 537ffe5ef298ad29b1cc6dd00a9ab5bab1992fd5..347b41edfe7bcfb6afef775b58aa96568762fdb1 100644 (file)
@@ -48,10 +48,10 @@ void MarginWidget::show_popup(const shared_ptr<ViewItem> &item)
 {
        pv::widgets::Popup *const p = item->create_popup(this);
 
-       connect(p, SIGNAL(closed()), this, SLOT(on_popup_closed()));
-
-       if (p)
+       if (p) {
+               connect(p, SIGNAL(closed()), this, SLOT(on_popup_closed()));
                p->show();
+       }
 }
 
 void MarginWidget::contextMenuEvent(QContextMenuEvent *event)
index 2d242b9994da3e3c08d1003099b37d676521ddbb..665b1a4b83a0a7034e383e252841737b352dd700 100644 (file)
@@ -259,7 +259,11 @@ MathEditDialog::MathEditDialog(pv::Session &session,
        root_layout->addWidget(button_box);
 
        // Set tab width to 4 characters
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+       expr_edit_->setTabStopDistance(util::text_width(QFontMetrics(font()), "XXXX"));
+#else
        expr_edit_->setTabStopWidth(util::text_width(QFontMetrics(font()), "XXXX"));
+#endif
 
        connect(button_box, SIGNAL(accepted()), this, SLOT(accept()));
        connect(button_box, SIGNAL(rejected()), this, SLOT(reject()));
index 83ffed281b5f6768085d1a03ee77f2840a443487..64581459593a1fcc8148a4c4d2e14dee31164b64 100644 (file)
@@ -242,7 +242,11 @@ shared_ptr<ViewItem> Ruler::get_mouse_over_item(const QPoint &pt)
 
 void Ruler::mouseDoubleClickEvent(QMouseEvent *event)
 {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       hover_item_ = view_.add_flag(get_absolute_time_from_x_pos(event->pos().x()));
+#else
        hover_item_ = view_.add_flag(get_absolute_time_from_x_pos(event->x()));
+#endif
 }
 
 void Ruler::paintEvent(QPaintEvent*)
index d1bae5e74789e05c0372fff3c055b102549bdcdc..8840825c2fe2c08cba62cc646a162176b6a49b76 100644 (file)
@@ -51,7 +51,7 @@ const char *const ChannelNames[] = {
        "TX",
        "RX",
        "SDA",
-       "SCL"
+       "SCL",
        "SCLK",
        "MOSI",
        "MISO",
@@ -196,6 +196,7 @@ void Signal::on_name_changed(const QString &text)
 
 void Signal::on_disable()
 {
+       // For generated signals, "disable" means "remove"
        if (base_->is_generated())
                session_.remove_generated_signal(base_);
        else
index ca8eea2e4a85f3820de0c6b883bed9f8e69d6812..e10e93eac4d2863d200c530fbcb7ff4a07e72b7d 100644 (file)
@@ -70,8 +70,6 @@ public:
         */
        virtual void set_name(QString name);
 
-       virtual shared_ptr<pv::data::SignalData> data() const = 0;
-
        /**
         * Determines the closest level change (i.e. edge) to a given sample, which
         * is useful for e.g. the "snap to edge" functionality.
index b428f6027e7bf4331d5c9d4d408a3f56fe554a92..0f390f528004858f3a835fcb781bc5d1ad332b18 100644 (file)
@@ -88,7 +88,7 @@ QRectF TimeMarker::label_rect(const QRectF &rect) const
 {
        QFontMetrics m(QApplication::font());
        const QSizeF text_size(
-               max(m.boundingRect(get_text()).size().width(), ArrowSize),
+               max(m.boundingRect(get_display_text()).size().width(), ArrowSize),
                m.height());
        const QSizeF label_size(text_size + LabelPadding * 2);
        const float top = rect.height() - label_size.height() -
@@ -105,6 +105,11 @@ QRectF TimeMarker::hit_box_rect(const ViewItemPaintParams &pp) const
        return QRectF(x - h / 2.0f, pp.top(), h, pp.height());
 }
 
+QString TimeMarker::get_display_text() const
+{
+       return get_text();
+}
+
 void TimeMarker::set_text(const QString &text)
 {
        (void)text;
@@ -158,7 +163,7 @@ void TimeMarker::paint_label(QPainter &p, const QRect &rect, bool hover)
        p.drawPolygon(points, countof(points));
 
        p.setPen(select_text_color(color_));
-       p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_text());
+       p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, get_display_text());
 }
 
 void TimeMarker::paint_fore(QPainter &p, ViewItemPaintParams &pp)
@@ -179,6 +184,8 @@ pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
        popup->set_position(parent->mapToGlobal(
                drag_point(parent->rect())), Popup::Bottom);
 
+       connect(popup, SIGNAL(closed()), this, SLOT(on_popup_closed()));
+
        QFormLayout *const form = new QFormLayout(popup);
        popup->setLayout(form);
 
@@ -193,6 +200,13 @@ pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent)
        return popup;
 }
 
+void TimeMarker::on_popup_closed()
+{
+       GlobalSettings settings;
+       if (!settings.value(GlobalSettings::Key_View_KeepRulerItemSelected).toBool())
+               select(false);
+}
+
 void TimeMarker::on_value_changed(const pv::util::Timestamp& value)
 {
        set_time(view_.ruler()->get_absolute_time_from_ruler_time(value));
index 040a29ea78605fadcd5c2723ac37019b9691cca9..f67a0f17a1e36567bb050afa23d6ecedaccdbb2c 100644 (file)
@@ -95,7 +95,13 @@ public:
        QRectF hit_box_rect(const ViewItemPaintParams &pp) const override;
 
        /**
-        * Gets the text to show in the marker.
+        * Gets the current text to show in the marker - this may be dynamic.
+        */
+       virtual QString get_display_text() const;
+
+       /**
+        * Gets the default text used to show the marker - e.g. the user-editable
+        * name.
         */
        virtual QString get_text() const = 0;
 
@@ -122,6 +128,8 @@ public:
        virtual pv::widgets::Popup* create_popup(QWidget *parent) override;
 
 private Q_SLOTS:
+       void on_popup_closed();
+
        void on_value_changed(const pv::util::Timestamp& value);
 
 protected:
index 1f77598d609c04235dfb3d0e3282b693edf6ee32..4535a13efcb31f6e504d77963179e550c891349f 100644 (file)
@@ -442,7 +442,8 @@ void Trace::on_popup_closed()
 void Trace::on_nameedit_changed(const QString &name)
 {
        /* This event handler notifies SignalBase that the name changed */
-       base_->set_name(name);
+       if (!name.isEmpty())
+               base_->set_name(name);
 }
 
 void Trace::on_coloredit_changed(const QColor &color)
index 3cbca90934d68a8417e17d5feb6bc70fd79bb3a6..ecc97b6ee80153671c2a1cc4512a0530a99ea9c4 100644 (file)
@@ -138,7 +138,11 @@ QMenu* TraceGroup::create_header_context_menu(QWidget *parent)
        QMenu *const menu = new QMenu(parent);
 
        QAction *const ungroup = new QAction(tr("Ungroup"), this);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       ungroup->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_U));
+#else
        ungroup->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_U));
+#endif
        connect(ungroup, SIGNAL(triggered()), this, SLOT(on_ungroup()));
        menu->addAction(ungroup);
 
index 47cb96b2da3ce6c2125a39472928e8e1a63cb1b0..33d8b0a44aa7ed9530adb4b4349439d8f9cc1c49 100644 (file)
@@ -92,7 +92,7 @@ namespace views {
 namespace trace {
 
 const Timestamp View::MaxScale("1e9");
-const Timestamp View::MinScale("1e-12");
+const Timestamp View::MinScale("1e-14");
 
 const int View::MaxScrollValue = INT_MAX / 2;
 
@@ -359,7 +359,7 @@ void View::add_signalbase(const shared_ptr<data::SignalBase> signalbase)
 
        switch (signalbase->type()) {
        case SignalBase::LogicChannel:
-               signal = shared_ptr<Signal>(new LogicSignal(session_, session_.device(), signalbase));
+               signal = shared_ptr<Signal>(new LogicSignal(session_, signalbase));
                break;
 
        case SignalBase::AnalogChannel:
@@ -920,7 +920,7 @@ vector< shared_ptr<SignalData> > View::get_visible_data() const
        vector< shared_ptr<SignalData> > visible_data;
        for (const shared_ptr<Signal>& sig : signals_)
                if (sig->enabled())
-                       visible_data.push_back(sig->data());
+                       visible_data.push_back(sig->base()->data());
 
        return visible_data;
 }
@@ -933,9 +933,9 @@ pair<Timestamp, Timestamp> View::get_time_extents() const
        if (signals_.size() == 0)
                return make_pair(0, 0);
 
-       for (shared_ptr<Signal> s : signals_)
-               if (s->data() && (s->data()->segments().size() > 0))
-                       data.push_back(s->data());
+       for (const shared_ptr<Signal>& s : signals_)
+               if (s->base()->data() && (s->base()->data()->segments().size() > 0))
+                       data.push_back(s->base()->data());
 
        for (const shared_ptr<SignalData>& d : data) {
                const vector< shared_ptr<Segment> > segments = d->segments();
@@ -1502,7 +1502,7 @@ void View::determine_time_unit()
        if (time_unit_ == util::TimeUnit::Samples) {
                // Check all signals but...
                for (const shared_ptr<Signal>& signal : signals_) {
-                       const shared_ptr<SignalData> data = signal->data();
+                       const shared_ptr<SignalData> data = signal->base()->data();
 
                        // ...only check first segment of each
                        const vector< shared_ptr<Segment> > segments = data->segments();
@@ -1530,7 +1530,11 @@ bool View::eventFilter(QObject *object, QEvent *event)
                else if (object == ruler_)
                        hover_point_ = mouse_event->pos();
                else if (object == header_)
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       hover_point_ = QPoint(0, mouse_event->pos().y());
+#else
                        hover_point_ = QPoint(0, mouse_event->y());
+#endif
                else
                        hover_point_ = QPoint(-1, -1);
 
@@ -1594,10 +1598,22 @@ void View::contextMenuEvent(QContextMenuEvent *event)
        QPoint pos = event->pos() - QPoint(0, ruler_->sizeHint().height());
 
        const shared_ptr<ViewItem> r = viewport_->get_mouse_over_item(pos);
-       if (!r)
-               return;
 
-       QMenu *menu = r->create_view_context_menu(this, pos);
+       QMenu* menu = nullptr;
+
+       if (!r) {
+               context_menu_x_pos_ = pos.x();
+
+               // No view item under cursor, use generic menu
+               menu = new QMenu(this);
+
+               QAction *const create_marker_here = new QAction(tr("Create marker here"), this);
+               connect(create_marker_here, SIGNAL(triggered()), this, SLOT(on_create_marker_here()));
+               menu->addAction(create_marker_here);
+       } else {
+               menu = r->create_view_context_menu(this, pos);
+       }
+
        if (menu)
                menu->popup(event->globalPos());
 }
@@ -2067,6 +2083,13 @@ void View::on_segment_changed(int segment)
        }
 }
 
+void View::on_create_marker_here()
+{
+       const QPoint p = ruler_->mapFrom(this, QPoint(context_menu_x_pos_, 0));
+
+       add_flag(ruler_->get_absolute_time_from_x_pos(p.x()));
+}
+
 void View::on_settingViewTriggerIsZeroTime_changed(const QVariant new_value)
 {
        (void)new_value;
index f8506cf409ac1c73f9555a1bcfa0d72fd7cc1900..7fef8419e625232b7013eb4e4e84a7bbcae60af0 100644 (file)
@@ -452,6 +452,8 @@ private Q_SLOTS:
 
        void on_settingViewTriggerIsZeroTime_changed(const QVariant new_value);
 
+       void on_create_marker_here();
+
        virtual void perform_delayed_view_update();
 
        void process_sticky_events();
@@ -574,6 +576,9 @@ private:
        // These are used to determine whether the view was altered after acq started
        double scale_at_acq_start_;
        pv::util::Timestamp offset_at_acq_start_;
+
+       // X coordinate of mouse cursor where the user clicked to open a context menu
+       uint32_t context_menu_x_pos_;
 };
 
 } // namespace trace
index 0c73ec494adfbc72257252c59ead85d609a6ad24..e2a32aa20bdd4ec41dc6460b30b3f72a8e46f894 100644 (file)
@@ -54,6 +54,17 @@ Viewport::Viewport(View &parent) :
 {
        setAutoFillBackground(true);
        setBackgroundRole(QPalette::Base);
+
+       // Set up settings and event handlers
+       GlobalSettings settings;
+       allow_vertical_dragging_ = settings.value(GlobalSettings::Key_View_AllowVerticalDragging).toBool();
+
+       GlobalSettings::add_change_handler(this);
+}
+
+Viewport::~Viewport()
+{
+       GlobalSettings::remove_change_handler(this);
 }
 
 shared_ptr<ViewItem> Viewport::get_mouse_over_item(const QPoint &pt)
@@ -78,7 +89,9 @@ void Viewport::item_hover(const shared_ptr<ViewItem> &item, QPoint pos)
 void Viewport::drag()
 {
        drag_offset_ = view_.offset();
-       drag_v_offset_ = view_.owner_visual_v_offset();
+
+       if (allow_vertical_dragging_)
+               drag_v_offset_ = view_.owner_visual_v_offset();
 }
 
 void Viewport::drag_by(const QPoint &delta)
@@ -89,7 +102,8 @@ void Viewport::drag_by(const QPoint &delta)
        view_.set_scale_offset(view_.scale(),
                (*drag_offset_ - delta.x() * view_.scale()));
 
-       view_.set_v_offset(-drag_v_offset_ - delta.y());
+       if (allow_vertical_dragging_)
+               view_.set_v_offset(-drag_v_offset_ - delta.y());
 }
 
 void Viewport::drag_release()
@@ -110,12 +124,40 @@ vector< shared_ptr<ViewItem> > Viewport::items()
 
 bool Viewport::touch_event(QTouchEvent *event)
 {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       QList<QEventPoint> touchPoints = event->points();
+#else
        QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
+#endif
 
        if (touchPoints.count() != 2) {
                pinch_zoom_active_ = false;
                return false;
        }
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       if (event->device()->type() == QInputDevice::DeviceType::TouchPad) {
+               return false;
+       }
+
+       const QEventPoint &touchPoint0 = touchPoints.first();
+       const QEventPoint &touchPoint1 = touchPoints.last();
+
+       if (!pinch_zoom_active_ ||
+               (event->touchPointStates() & QEventPoint::Pressed)) {
+               pinch_offset0_ = (view_.offset() + view_.scale() * touchPoint0.position().x()).convert_to<double>();
+               pinch_offset1_ = (view_.offset() + view_.scale() * touchPoint1.position().x()).convert_to<double>();
+               pinch_zoom_active_ = true;
+       }
+
+       double w = touchPoint1.position().x() - touchPoint0.position().x();
+       if (abs(w) >= 1.0) {
+               const double scale =
+                       fabs((pinch_offset1_ - pinch_offset0_) / w);
+               double offset = pinch_offset0_ - touchPoint0.position().x() * scale;
+               if (scale > 0)
+                       view_.set_scale_offset(scale, offset);
+       }
+#else
        if (event->device()->type() == QTouchDevice::TouchPad) {
                return false;
        }
@@ -138,6 +180,7 @@ bool Viewport::touch_event(QTouchEvent *event)
                if (scale > 0)
                        view_.set_scale_offset(scale, offset);
        }
+#endif
 
        if (event->touchPointStates() & Qt::TouchPointReleased) {
                pinch_zoom_active_ = false;
@@ -148,7 +191,11 @@ bool Viewport::touch_event(QTouchEvent *event)
                } else {
                        // Update the mouse down fields so that continued
                        // dragging with the primary touch will work correctly
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+                       mouse_down_point_ = touchPoint0.position().toPoint();
+#else
                        mouse_down_point_ = touchPoint0.pos().toPoint();
+#endif
                        drag();
                }
        }
@@ -201,33 +248,70 @@ void Viewport::mouseDoubleClickEvent(QMouseEvent *event)
 {
        assert(event);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       if (event->buttons() & Qt::LeftButton)
+               view_.zoom(2.0, event->position().x());
+       else if (event->buttons() & Qt::RightButton)
+               view_.zoom(-2.0, event->position().x());
+#else
        if (event->buttons() & Qt::LeftButton)
                view_.zoom(2.0, event->x());
        else if (event->buttons() & Qt::RightButton)
                view_.zoom(-2.0, event->x());
+#endif
 }
 
 void Viewport::wheelEvent(QWheelEvent *event)
 {
        assert(event);
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       int delta = (event->angleDelta().x() != 0) ? event->angleDelta().x() : event->angleDelta().y();
+#else
+       int delta = event->delta();
+#endif
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       if (event->angleDelta().y() != 0) {
+#else
        if (event->orientation() == Qt::Vertical) {
+#endif
                if (event->modifiers() & Qt::ControlModifier) {
                        // Vertical scrolling with the control key pressed
                        // is intrepretted as vertical scrolling
                        view_.set_v_offset(-view_.owner_visual_v_offset() -
-                               (event->delta() * height()) / (8 * 120));
+                               (delta * height()) / (8 * 120));
+               } else if (event->modifiers() & Qt::ShiftModifier) {
+                       // Vertical scrolling with the shift key pressed
+                       // acts as horizontal scrolling like in Gimp
+                       // and Inkscape.
+                       view_.set_scale_offset(view_.scale(),
+                               - delta * view_.scale() + view_.offset());
                } else {
                        // Vertical scrolling is interpreted as zooming in/out
-                       view_.zoom(event->delta() / 120.0, event->x());
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+                       view_.zoom(delta / 120.0, event->position().x());
+#else
+                       view_.zoom(delta / 120.0, event->x());
+#endif
                }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
+       } else if (event->angleDelta().x() != 0) {
+#else
        } else if (event->orientation() == Qt::Horizontal) {
+#endif
                // Horizontal scrolling is interpreted as moving left/right
                view_.set_scale_offset(view_.scale(),
-                       event->delta() * view_.scale() + view_.offset());
+                       delta * view_.scale() + view_.offset());
        }
 }
 
+void Viewport::on_setting_changed(const QString &key, const QVariant &value)
+{
+       if (key == GlobalSettings::Key_View_AllowVerticalDragging)
+               allow_vertical_dragging_ = value.toBool();
+}
+
 } // namespace trace
 } // namespace views
 } // namespace pv
index 48f2d09c9d0c4c178535c089bdc86a7a064d26fc..8c82354ea8dcfb6f2e75ddeb8a34b35932087ef5 100644 (file)
@@ -26,6 +26,8 @@
 #include <QTimer>
 #include <QTouchEvent>
 
+#include <pv/globalsettings.hpp>
+
 #include "pv/util.hpp"
 #include "viewwidget.hpp"
 
@@ -42,12 +44,13 @@ namespace trace {
 
 class View;
 
-class Viewport : public ViewWidget
+class Viewport : public ViewWidget, public GlobalSettingsInterface
 {
        Q_OBJECT
 
 public:
        explicit Viewport(View &parent);
+       ~Viewport();
 
        /**
         * Gets the first view item which has a hit-box that contains @c pt .
@@ -92,15 +95,18 @@ private:
         */
        bool touch_event(QTouchEvent *event);
 
-private:
        void paintEvent(QPaintEvent *event);
 
        void mouseDoubleClickEvent(QMouseEvent *event);
+
        void wheelEvent(QWheelEvent *event);
 
+       void on_setting_changed(const QString &key, const QVariant &value);
+
 private:
        boost::optional<pv::util::Timestamp> drag_offset_;
        int drag_v_offset_;
+       bool allow_vertical_dragging_;
 
        double pinch_offset0_;
        double pinch_offset1_;
index eff962311cc549434e51dc48db45f6bda704c5bc..972b04ea6ed4cf8d99e3f9b83d192175ceea8269 100644 (file)
@@ -91,15 +91,12 @@ public:
        vector< shared_ptr<data::SignalBase> > signalbases() const;
 
        virtual void clear_signalbases();
-
        virtual void add_signalbase(const shared_ptr<data::SignalBase> signalbase);
        virtual void remove_signalbase(const shared_ptr<data::SignalBase> signalbase);
 
 #ifdef ENABLE_DECODE
        virtual void clear_decode_signals();
-
        virtual void add_decode_signal(shared_ptr<data::DecodeSignal> signal);
-
        virtual void remove_decode_signal(shared_ptr<data::DecodeSignal> signal);
 #endif
 
index 34f4852c9edf4f0381fe661efe273b6debe2685e..5cf99e5212690ee18e18b2fe08195d94db7d7041 100644 (file)
@@ -57,7 +57,11 @@ DecoderMenu::DecoderMenu(QWidget *parent, const char* input, bool first_level_de
        }
        g_slist_free(li);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)), this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)), this, SLOT(on_action(QObject*)));
+#endif
 }
 
 int DecoderMenu::decoder_name_cmp(const void *a, const void *b)
index d700b8b57cc0bdf471452ab6a8806e787c63f19c..2e76f689a61f448b068e149e2c1886a5f67992b8 100644 (file)
@@ -55,8 +55,13 @@ DeviceToolButton::DeviceToolButton(QWidget *parent,
        setDefaultAction(connect_action_);
        setMinimumWidth(QFontMetrics(font()).averageCharWidth() * 24);
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)),
                this, SLOT(on_action(QObject*)));
+#endif
 
        connect(&menu_, SIGNAL(hovered(QAction*)),
                this, SLOT(on_menu_hovered(QAction*)));
index 721affe058e9a359579b8caabf43485ffb99625b..bda669288f3731b9f3706fd9603de490eaee25a6 100644 (file)
@@ -75,8 +75,13 @@ ExportMenu::ExportMenu(QWidget *parent, shared_ptr<Context> context,
                connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)),
                this, SLOT(on_action(QObject*)));
+#endif
 }
 
 void ExportMenu::on_action(QObject *action)
index efd862f97b1fb9581307fe92e920d9903b50f2dc..73916700253087617a0539a44e6caa6579043840 100644 (file)
@@ -146,7 +146,13 @@ QSize FlowLayout::minimumSize() const
                        size.setHeight(h);
        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       int left, top, right, bottom;
+       getContentsMargins(&left, &top, &right, &bottom);
+       size += QSize(left + right, top + bottom);
+#else
        size += QSize(2 * margin(), 2 * margin());
+#endif
 
        return size;
 }
index b63256cb923cd36edaf89962975bc4e348147cfd..dcca307ecc731bb01e6946fd9f38d4c0ec17f4ac 100644 (file)
@@ -72,8 +72,13 @@ ImportMenu::ImportMenu(QWidget *parent, shared_ptr<Context> context,
                connect(action, SIGNAL(triggered()), &mapper_, SLOT(map()));
        }
 
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       connect(&mapper_, SIGNAL(mappedObject(QObject*)),
+               this, SLOT(on_action(QObject*)));
+#else
        connect(&mapper_, SIGNAL(mapped(QObject*)),
                this, SLOT(on_action(QObject*)));
+#endif
 }
 
 void ImportMenu::on_action(QObject *action)
index ec6d29c981c235b86451353ca620d9c5cf147868..bcfd8753c6b6dbe2c8e6da5a8032d75057b8bc50 100644 (file)
 #include <cassert>
 
 #include <QApplication>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QScreen>
+#else
 #include <QDesktopWidget>
+#endif
 #include <QLineEdit>
 #include <QScrollBar>
 #include <QStyle>
@@ -252,8 +256,12 @@ void Popup::reposition_widget()
 {
        QPoint o;
 
+#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
+       const QRect screen_rect = QApplication::screenAt(point_)->availableGeometry();
+#else
        const QRect screen_rect = QApplication::desktop()->availableGeometry(
                QApplication::desktop()->screenNumber(point_));
+#endif
 
        if (pos_ == Right || pos_ == Left)
                o.ry() = -height() / 2;
index e655526c0b3f7a1a12fabb5af0c9e2383177488d..0f1eb6d9ff2595f0f484c357655b9691ebbcbf92 100644 (file)
@@ -54,7 +54,11 @@ SweepTimingWidget::SweepTimingWidget(const char *suffix,
                this, SIGNAL(value_changed()));
 
        setLayout(&layout_);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       layout_.setContentsMargins(0, 0, 0, 0);
+#else
        layout_.setMargin(0);
+#endif
        layout_.addWidget(&list_);
        layout_.addWidget(&value_);
 
index 01424a5b7837654e50c36de5f3f4ce27c33ec8a1..b99a640a843055fa2d2271bcae9c67d6607f63a9 100644 (file)
 #include "timestampspinbox.hpp"
 
 #include <QLineEdit>
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+#include <QRegularExpression>
+#else
 #include <QRegExp>
+#endif
 
 namespace pv {
 namespace widgets {
@@ -93,10 +97,25 @@ void TimestampSpinBox::setValue(const pv::util::Timestamp& val)
 
 void TimestampSpinBox::on_editingFinished()
 {
-       QRegExp re(R"(\s*([-+]?)\s*([0-9]+\.?[0-9]*).*)");
+       static const auto re_pattern = R"(\s*([-+]?)\s*([0-9]+\.?[0-9]*).*)";
+
+       bool has_match;
+       QStringList captures;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+       QRegularExpression re(re_pattern);
+       has_match = re.match(text()).hasMatch();
+       if (has_match) {
+               captures = re.match(text()).capturedTexts();
+       }
+#else
+       QRegExp re(re_pattern);
+       has_match = re.exactMatch(text());
+       if (has_match) {
+               captures = re.capturedTexts();
+       }
+#endif
 
-       if (re.exactMatch(text())) {
-               QStringList captures = re.capturedTexts();
+       if (has_match) {
                captures.removeFirst(); // remove entire match
                QString str = captures.join("");
                setValue(pv::util::Timestamp(str.toStdString()));
index 554a59e5076fcd23219d7507e28e92567607206e..d5f282344af7f33fc17c0205aadf0f12aa6a0003 100644 (file)
@@ -2,5 +2,7 @@
        <qresource prefix="/">
                <file>l10n/de.qm</file>
                <file>l10n/es_MX.qm</file>
+               <file>l10n/ja_jp.qm</file>
+               <file>l10n/zh_cn.qm</file>
        </qresource>
 </RCC>