summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore11
-rw-r--r--Configure.mk195
-rw-r--r--Makefile362
-rw-r--r--Rules.mk252
-rw-r--r--callbacks-gtk.h2
-rw-r--r--color.h80
-rw-r--r--conversions.h16
-rw-r--r--device.h8
-rw-r--r--display-gtk.h30
-rw-r--r--display.h31
-rw-r--r--dive.c9
-rw-r--r--dive.h33
-rw-r--r--divelist-gtk.c46
-rw-r--r--divelist.c200
-rw-r--r--divelist.h16
-rw-r--r--download-dialog.c17
-rw-r--r--equipment.c46
-rw-r--r--gtk-gui.c261
-rw-r--r--helpers.h19
-rw-r--r--info-gtk.c8
-rw-r--r--info.c15
-rw-r--r--libdivecomputer.c4
-rw-r--r--libdivecomputer.h4
-rw-r--r--linux.c25
-rw-r--r--macos.c26
-rw-r--r--main.c153
-rwxr-xr-xpackaging/windows/mingw-make.sh2
-rw-r--r--packaging/windows/subsurface.resbin18056 -> 0 bytes
-rw-r--r--planner.c2
-rw-r--r--pref.h22
-rw-r--r--profile.c1138
-rw-r--r--profile.h112
-rw-r--r--qt-gui.cpp210
-rw-r--r--qt-ui/addcylinderdialog.cpp56
-rw-r--r--qt-ui/addcylinderdialog.h33
-rw-r--r--qt-ui/addcylinderdialog.ui303
-rw-r--r--qt-ui/addweightsystemdialog.cpp39
-rw-r--r--qt-ui/addweightsystemdialog.h30
-rw-r--r--qt-ui/addweightsystemdialog.ui109
-rw-r--r--qt-ui/divelistview.cpp132
-rw-r--r--qt-ui/divelistview.h40
-rw-r--r--qt-ui/globe.cpp113
-rw-r--r--qt-ui/globe.h34
-rw-r--r--qt-ui/maintab.cpp204
-rw-r--r--qt-ui/maintab.h46
-rw-r--r--qt-ui/maintab.ui885
-rw-r--r--qt-ui/mainwindow.cpp464
-rw-r--r--qt-ui/mainwindow.h89
-rw-r--r--qt-ui/mainwindow.ui383
-rw-r--r--qt-ui/modeldelegates.cpp49
-rw-r--r--qt-ui/modeldelegates.h15
-rw-r--r--qt-ui/models.cpp705
-rw-r--r--qt-ui/models.h126
-rw-r--r--qt-ui/plotareascene.cpp6
-rw-r--r--qt-ui/plotareascene.h6
-rw-r--r--qt-ui/profilegraphics.cpp1397
-rw-r--r--qt-ui/profilegraphics.h110
-rw-r--r--qt-ui/starwidget.cpp100
-rw-r--r--qt-ui/starwidget.h37
-rw-r--r--star.svg74
-rw-r--r--statistics.c18
-rw-r--r--statistics.h15
-rw-r--r--subsurface.qrc6
-rw-r--r--uemis-downloader.c4
-rw-r--r--webservice.c2
-rw-r--r--webservice.h8
-rw-r--r--windows.c21
67 files changed, 7310 insertions, 1704 deletions
diff --git a/.gitignore b/.gitignore
index 814241112..3f2085f8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,12 +3,16 @@
*.rej
*.exe
*.dmg
+*.moc
+*.moc.cpp
*.patch
+*.ui.h
*.xml
version.h
!dives/*.xml
*~
po/*.mo
+qt-ui/ui_*.h
/subsurface
/*.tar.gz
.dep/
@@ -19,3 +23,10 @@ Documentation/user-manual.pdf
Documentation/user-manual.text
packaging/windows/subsurface.nsi
packaging/macos/Info.plist
+config.cache
+*.qrc.cpp
+/subsurface.config
+/subsurface.creator
+/subsurface.creator.user
+/subsurface.files
+/subsurface.includes
diff --git a/Configure.mk b/Configure.mk
new file mode 100644
index 000000000..3d5e5c978
--- /dev/null
+++ b/Configure.mk
@@ -0,0 +1,195 @@
+# -*- Makefile -*-
+# This file contains the detection rules
+all:
+
+PKGCONFIG=pkg-config
+XML2CONFIG=xml2-config
+XSLCONFIG=xslt-config
+QMAKE=qmake
+MOC=moc
+UIC=uic
+
+CONFIGFILE = config.cache
+ifeq ($(CONFIGURING),1)
+
+# Detect the target system
+# Ask the compiler what OS it's producing files for
+UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd")
+
+# find libdivecomputer
+# First deal with the cross compile environment and with Mac.
+# For the native case, Linus doesn't want to trust pkg-config given
+# how young libdivecomputer still is - so we check the typical
+# subdirectories of /usr/local and /usr and then we give up. You can
+# override by simply setting it here
+#
+ifeq ($(CC), i686-w64-mingw32-gcc)
+# ok, we are cross building for Windows
+ LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer)
+ LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer)
+ RESFILE = packaging/windows/subsurface.res
+ LDFLAGS += -Wl,-subsystem,windows
+ LIBWINSOCK = -lwsock32
+else ifeq ($(UNAME), darwin)
+ LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer)
+ LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer)
+else
+libdc-local := $(wildcard /usr/local/lib/libdivecomputer.a)
+libdc-local64 := $(wildcard /usr/local/lib64/libdivecomputer.a)
+libdc-usr := $(wildcard /usr/lib/libdivecomputer.a)
+libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a)
+
+ifneq ($(LIBDCDEVEL),)
+ LIBDIVECOMPUTERDIR = ../libdivecomputer
+ LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
+ LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/src/.libs/libdivecomputer.a
+else ifneq ($(strip $(libdc-local)),)
+ LIBDIVECOMPUTERDIR = /usr/local
+ LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
+ LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
+else ifneq ($(strip $(libdc-local64)),)
+ LIBDIVECOMPUTERDIR = /usr/local
+ LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
+ LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a
+else ifneq ($(strip $(libdc-usr)),)
+ LIBDIVECOMPUTERDIR = /usr
+ LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
+ LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
+else ifneq ($(strip $(libdc-usr64)),)
+ LIBDIVECOMPUTERDIR = /usr
+ LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
+ LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a
+else
+$(error Cannot find libdivecomputer - please edit Makefile)
+endif
+endif
+
+# Libusb-1.0 is only required if libdivecomputer was built with it.
+# And libdivecomputer is only built with it if libusb-1.0 is
+# installed. So get libusb if it exists, but don't complain
+# about it if it doesn't.
+LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null)
+
+# Find qmake. Rules are:
+# - use qmake if it is in $PATH
+# [qmake -query QT_VERSION will fail if it's Qt 3's qmake]
+# - if that fails, try qmake-qt4
+# - if that fails, print an error
+# We specifically do not search for qmake-qt5 since that is not supposed
+# to exist.
+QMAKE = $(shell { qmake -query QT_VERSION >/dev/null 2>&1 && echo qmake; } || \
+ { qmake-qt4 -v >/dev/null 2>&1 && echo qmake-qt4; })
+ifeq ($(strip $(QMAKE)),)
+$(error Could not find qmake or qmake-qt4 in $$PATH or they failed)
+endif
+
+# Use qmake to find out which Qt version we are building for.
+QT_VERSION_MAJOR = $(shell $(QMAKE) -query QT_VERSION | cut -d. -f1)
+ifeq ($(QT_VERSION_MAJOR), 5)
+# QT_MODULES = Qt5Widgets Qt5Svg
+# QT_CORE = Qt5Core
+# QTBINDIR = $(shell $(QMAKE) -query QT_HOST_BINS)
+# # Tool paths are not stored in .pc files in Qt 5.0
+# MOC = $(QTBINDIR)/moc
+# UIC = $(QTBINDIR)/uic
+# RCC = $(QTBINDIR)/rcc
+# if qmake is qt5, try to get the qt4 one.
+ QMAKE = { qmake-qt4 -v >/dev/null 2>&1 && echo qmake-qt4; }
+#else
+endif
+
+ifeq ($(strip $(QMAKE)),)
+$(error Could not find qmake or qmake-qt4 in $$PATH for the Qt4 version they failed)
+endif
+
+ QT_MODULES = QtGui QtSvg
+ QT_CORE = QtCore
+ MOC = $(shell $(PKGCONFIG) --variable=moc_location QtCore)
+ UIC = $(shell $(PKGCONFIG) --variable=uic_location QtGui)
+ RCC = $(shell $(PKGCONFIG) --variable=rcc_location QtGui)
+#endif
+
+# we need GLIB2CFLAGS for gettext
+QTCXXFLAGS = $(shell $(PKGCONFIG) --cflags $(QT_MODULES)) $(GLIB2CFLAGS)
+LIBQT = $(shell $(PKGCONFIG) --libs $(QT_MODULES))
+ifneq ($(filter reduce_relocations, $(shell $(PKGCONFIG) --variable qt_config $(QT_CORE))), )
+ QTCXXFLAGS += -fPIE
+endif
+
+LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0)
+ifneq (,$(filter $(UNAME),linux kfreebsd gnu))
+ LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0)
+ GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0)
+else ifeq ($(UNAME), darwin)
+ LIBGTK += $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices
+ GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration)
+ GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler
+endif
+
+LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES)
+LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB)
+
+LIBXML2 = $(shell $(XML2CONFIG) --libs)
+LIBXSLT = $(shell $(XSLCONFIG) --libs)
+XML2CFLAGS = $(shell $(XML2CONFIG) --cflags)
+GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0)
+GTKCFLAGS += $(shell $(PKGCONFIG) --cflags gtk+-2.0)
+XSLCFLAGS = $(shell $(XSLCONFIG) --cflags)
+OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null)
+LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null)
+LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4)
+LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4)
+
+LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null)
+ZIPFLAGS = $(strip $(shell $(PKGCONFIG) --cflags libzip 2> /dev/null))
+
+LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null)
+SQLITE3FLAGS = $(strip $(shell $(PKGCONFIG) --cflags sqlite3))
+
+# Write the configure file
+all: configure
+configure $(CONFIGURE): Configure.mk
+ @echo "\
+ CONFIGURED = 1\\\
+ UNAME = $(UNAME)\\\
+ LIBDIVECOMPUTERDIR = $(LIBDIVECOMPUTERDIR)\\\
+ LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERCFLAGS)\\\
+ LIBDIVECOMPUTER = $(LIBDIVECOMPUTER)\\\
+ LIBWINSOCK = $(LIBWINSOCK)\\\
+ LDFLAGS = $(LDFLAGS)\\\
+ RESFILE = $(RESFILE)\\\
+ LIBQT = $(LIBQT)\\\
+ QTCXXFLAGS = $(QTCXXFLAGS)\\\
+ MOC = $(MOC)\\\
+ UIC = $(UIC)\\\
+ RCC = $(RCC)\\\
+ LIBGTK = $(LIBGTK)\\\
+ GTKCFLAGS = $(GTKCFLAGS)\\\
+ LIBGCONF2 = $(LIBGCONF2)\\\
+ GCONF2CFLAGS = $(GCONF2CFLAGS)\\\
+ GTK_MAC_BUNDLER = $(GTK_MAC_BUNDLER)\\\
+ LIBXML2 = $(LIBXML2)\\\
+ LIBXSLT = $(LIBXSLT)\\\
+ XML2CFLAGS = $(XML2CFLAGS)\\\
+ GLIB2CFLAGS = $(GLIB2CFLAGS)\\\
+ XSLCFLAGS = $(XSLCFLAGS)\\\
+ OSMGPSMAPFLAGS = $(OSMGPSMAPFLAGS)\\\
+ LIBOSMGPSMAP = $(LIBOSMGPSMAP)\\\
+ LIBSOUPCFLAGS = $(LIBSOUPCFLAGS)\\\
+ LIBSOUP = $(LIBSOUP)\\\
+ LIBZIP = $(LIBZIP)\\\
+ ZIPFLAGS = $(ZIPFLAGS)\\\
+ LIBSQLITE3 = $(LIBSQLITE3)\\\
+ SQLITE3FLAGS = $(SQLITE3FLAGS)\\\
+ " | tr '\\' '\n' > $(CONFIGFILE)
+
+else
+configure $(CONFIGFILE): Configure.mk
+ @test -e $(CONFIGFILE) && echo Reconfiguring.. || echo Configuring...
+ @$(MAKE) CONFIGURING=1 configure
+ @echo Done
+
+-include $(CONFIGFILE)
+endif
+
+.PHONY: configure all
diff --git a/Makefile b/Makefile
index e0689548d..0e15becd7 100644
--- a/Makefile
+++ b/Makefile
@@ -2,14 +2,14 @@ NAME = subsurface
CAPITALIZED_NAME = Subsurface
TARGET = $(NAME)
+include Configure.mk
VERSION=3.1
CC=gcc
CFLAGS=-Wall -Wno-pointer-sign -g $(CLCFLAGS) -DGSEAL_ENABLE
+CXX=g++
+CXXFLAGS=-Wall -g $(CLCXXFLAGS) -DQT_NO_KEYWORDS
INSTALL=install
-PKGCONFIG=pkg-config
-XML2CONFIG=xml2-config
-XSLCONFIG=xslt-config
# these locations seem to work for SuSE and Fedora
# prefix = $(HOME)
@@ -28,141 +28,86 @@ DESKTOPFILE = $(NAME).desktop
MANFILES = $(NAME).1
XSLTFILES = xslt/*.xslt xslt/*.xsl
-VERSION_FILE = version.h
-# There's only one line in $(VERSION_FILE); use the shell builtin `read'
-STORED_VERSION_STRING = \
- $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \
- read ignore ignore v <$(VERSION_FILE) && echo $$v))
-#" workaround editor syntax highlighting quirk
-
-UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win|gnu|kfreebsd")
-GET_VERSION = ./scripts/get-version
-VERSION_STRING := $(shell [ -d .git ] && $(GET_VERSION) linux || \
- echo "v$(VERSION)")
-# Mac Info.plist style with three numbers 1.2.3
-CFBUNDLEVERSION_STRING := $(shell [ -d .git ] && \
- $(GET_VERSION) darwin $(VERSION_STRING) || \
- echo "$(VERSION).0")
-# Windows .nsi style with four numbers 1.2.3.4
-PRODVERSION_STRING := $(shell [ -d .git ] && \
- $(GET_VERSION) win $(VERSION_STRING) || \
- echo "$(VERSION).0.0")
-
-# 'pretty' output (easy to spot warnings) by default
-# 'verbose' output (all the details) by calling with "make V=1"
-ifeq ($(V),1)
- PRETTYECHO=true
- COMPILE_PREFIX=
-else
- PRETTYECHO=echo
- COMPILE_PREFIX=@
+EXTRA_FLAGS = $(QTCXXFLAGS) $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \
+ $(LIBDIVECOMPUTERCFLAGS) \
+ $(LIBSOUPCFLAGS) $(GCONF2CFLAGS)
+
+HEADERS = \
+ qt-ui/addcylinderdialog.h \
+ qt-ui/addweightsystemdialog.h \
+ qt-ui/divelistview.h \
+ qt-ui/maintab.h \
+ qt-ui/mainwindow.h \
+ qt-ui/models.h \
+ qt-ui/plotareascene.h \
+ qt-ui/starwidget.h \
+ qt-ui/modeldelegates.h \
+ qt-ui/profilegraphics.h \
+ qt-ui/globe.h
+
+
+SOURCES = \
+ deco.c \
+ device.c \
+ dive.c \
+ divelist.c \
+ download-dialog.c \
+ equipment.c \
+ file.c \
+ info.c \
+ main.c \
+ parse-xml.c \
+ prefs.c \
+ profile.c \
+ save-xml.c \
+ sha1.c \
+ statistics.c \
+ time.c \
+ qt-gui.cpp \
+ qt-ui/addcylinderdialog.cpp \
+ qt-ui/addweightsystemdialog.cpp \
+ qt-ui/divelistview.cpp \
+ qt-ui/maintab.cpp \
+ qt-ui/mainwindow.cpp \
+ qt-ui/models.cpp \
+ qt-ui/plotareascene.cpp \
+ qt-ui/starwidget.cpp \
+ qt-ui/modeldelegates.cpp \
+ qt-ui/profilegraphics.cpp \
+ qt-ui/globe.cpp \
+ $(RESFILE)
+
+
+RESOURCES = $(NAME).qrc
+
+ifneq ($(SQLITE3FLAGS),)
+ EXTRA_FLAGS += -DSQLITE3 $(SQLITE3FLAGS)
endif
-
-# find libdivecomputer
-# First deal with the cross compile environment and with Mac.
-# For the native case, Linus doesn't want to trust pkg-config given
-# how young libdivecomputer still is - so we check the typical
-# subdirectories of /usr/local and /usr and then we give up. You can
-# override by simply setting it here
-#
-ifeq ($(CC), i686-w64-mingw32-gcc)
-# ok, we are cross building for Windows
- LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer)
- LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer)
- LIBGNUTLS = $(shell $(PKGCONFIG) --libs gnutls-extra) $(shell $(PKGCONFIG) --libs p11-kit-1)
- RESFILE = packaging/windows/$(NAME).res
- LDFLAGS += -Wl,-subsystem,windows
- LIBWINSOCK = -lwsock32
- TARGET = $(NAME).exe
-else ifeq ($(UNAME), darwin)
- LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer)
- LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer)
-else
-libdc-local := $(wildcard /usr/local/lib/libdivecomputer.a)
-libdc-local64 := $(wildcard /usr/local/lib64/libdivecomputer.a)
-libdc-usr := $(wildcard /usr/lib/libdivecomputer.a)
-libdc-usr64 := $(wildcard /usr/lib64/libdivecomputer.a)
-
-ifneq ($(LIBDCDEVEL),)
- LIBDIVECOMPUTERDIR = ../libdivecomputer
- LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
- LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/src/.libs/libdivecomputer.a
-else ifneq ($(strip $(libdc-local)),)
- LIBDIVECOMPUTERDIR = /usr/local
- LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
- LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
-else ifneq ($(strip $(libdc-local64)),)
- LIBDIVECOMPUTERDIR = /usr/local
- LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
- LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a
-else ifneq ($(strip $(libdc-usr)),)
- LIBDIVECOMPUTERDIR = /usr
- LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
- LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib/libdivecomputer.a
-else ifneq ($(strip $(libdc-usr64)),)
- LIBDIVECOMPUTERDIR = /usr
- LIBDIVECOMPUTERINCLUDES = -I$(LIBDIVECOMPUTERDIR)/include
- LIBDIVECOMPUTERARCHIVE = $(LIBDIVECOMPUTERDIR)/lib64/libdivecomputer.a
-else
-$(error Cannot find libdivecomputer - please edit Makefile)
+ifneq ($(ZIPFLAGS),)
+ EXTRA_FLAGS += -DLIBZIP $(ZIPFLAGS)
endif
+ifneq ($(strip $(LIBXSLT)),)
+ EXTRA_FLAGS += -DXSLT='"$(XSLTDIR)"' $(XSLCFLAGS)
endif
-
-# Libusb-1.0 is only required if libdivecomputer was built with it.
-# And libdivecomputer is only built with it if libusb-1.0 is
-# installed. So get libusb if it exists, but don't complain
-# about it if it doesn't.
-LIBUSB = $(shell $(PKGCONFIG) --libs libusb-1.0 2> /dev/null)
-
-LIBGTK = $(shell $(PKGCONFIG) --libs gtk+-2.0 glib-2.0)
-LIBDIVECOMPUTERCFLAGS = $(LIBDIVECOMPUTERINCLUDES)
-LIBDIVECOMPUTER = $(LIBDIVECOMPUTERARCHIVE) $(LIBUSB)
-
-LIBXML2 = $(shell $(XML2CONFIG) --libs)
-LIBXSLT = $(shell $(XSLCONFIG) --libs)
-XML2CFLAGS = $(shell $(XML2CONFIG) --cflags)
-GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0)
-GTKCFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0)
-CFLAGS += $(shell $(XSLCONFIG) --cflags)
-OSMGPSMAPFLAGS += $(shell $(PKGCONFIG) --cflags osmgpsmap 2> /dev/null)
-LIBOSMGPSMAP += $(shell $(PKGCONFIG) --libs osmgpsmap 2> /dev/null)
+ifeq ($(USE_GTK_UI),1)
ifneq ($(strip $(LIBOSMGPSMAP)),)
- GPSOBJ = gps.o
- CFLAGS += -DHAVE_OSM_GPS_MAP
-endif
-LIBSOUPCFLAGS = $(shell $(PKGCONFIG) --cflags libsoup-2.4)
-LIBSOUP = $(shell $(PKGCONFIG) --libs libsoup-2.4)
-
-LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null)
-ifneq ($(strip $(LIBZIP)),)
- ZIP = -DLIBZIP $(shell $(PKGCONFIG) --cflags libzip)
+ SOURCES += gps.c
+ EXTRA_FLAGS += -DHAVE_OSM_GPS_MAP $(OSMGPSMAPFLAGS)
endif
-
-LIBSQLITE3 = $(shell $(PKGCONFIG) --libs sqlite3 2> /dev/null)
-ifneq ($(strip $(LIBSQLITE3)),)
- SQLITE3 = -DSQLITE3 $(shell $(PKGCONFIG) --cflags sqlite3)
endif
ifneq (,$(filter $(UNAME),linux kfreebsd gnu))
- LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0)
- GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0)
- OSSUPPORT = linux
- OSSUPPORT_CFLAGS = $(GTKCFLAGS) $(GCONF2CFLAGS)
+ SOURCES += linux.c
else ifeq ($(UNAME), darwin)
- OSSUPPORT = macos
- OSSUPPORT_CFLAGS = $(GTKCFLAGS)
+ SOURCES += macos.c
MACOSXINSTALL = /Applications/$(CAPITALIZED_NAME).app
MACOSXFILES = packaging/macosx
MACOSXSTAGING = $(MACOSXFILES)/$(CAPITALIZED_NAME).app
INFOPLIST = $(MACOSXFILES)/Info.plist
INFOPLISTINPUT = $(INFOPLIST).in
- EXTRALIBS = $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation -framework CoreServices
- CFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration)
- LDFLAGS += -headerpad_max_install_names -sectcreate __TEXT __info_plist $(INFOPLIST)
- GTK_MAC_BUNDLER = ~/.local/bin/gtk-mac-bundler
+ LDFLAGS += -headerpad_max_install_names
else
- OSSUPPORT = windows
- OSSUPPORT_CFLAGS = $(GTKCFLAGS)
+ SOURCES += windows.c
WINDOWSSTAGING = ./packaging/windows
WINMSGDIRS=$(addprefix share/locale/,$(shell ls po/*.po | sed -e 's/po\/\(..\)_.*/\1\/LC_MESSAGES/'))
NSIINPUTFILE = $(WINDOWSSTAGING)/$(NAME).nsi.in
@@ -171,170 +116,15 @@ else
XSLTDIR = .\\xslt
endif
-ifneq ($(strip $(LIBXSLT)),)
- XSLT=-DXSLT='"$(XSLTDIR)"'
-endif
-
-LIBS = $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) $(LIBGNUTLS)
+LIBS = $(LIBQT) $(LIBXML2) $(LIBXSLT) $(LIBSQLITE3) $(LIBGCONF2) $(LIBDIVECOMPUTER) \
+ $(EXTRALIBS) $(LIBZIP) -lpthread -lm $(LIBOSMGPSMAP) $(LIBSOUP) $(LIBWINSOCK) -lmarblewidget
MSGLANGS=$(notdir $(wildcard po/*.po))
-MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/$(NAME).mo))
-
-GTKOBJS = info-gtk.o divelist-gtk.o planner-gtk.o statistics-gtk.o gtk-gui.o
-
-OBJS = main.o dive.o time.o profile.o info.o equipment.o divelist.o deco.o planner.o \
- parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o uemis-downloader.o \
- statistics.o file.o device.o download-dialog.o prefs.o \
- webservice.o sha1.o $(GPSOBJ) $(OSSUPPORT).o $(RESFILE) $(GTKOBJS)
-
-DEPS = $(wildcard .dep/*.dep)
-
-all: $(TARGET)
-
-$(TARGET): gen_version_file $(OBJS) $(MSGOBJS) $(INFOPLIST)
- @$(PRETTYECHO) ' LINK' $(TARGET)
- $(COMPILE_PREFIX)$(CC) $(LDFLAGS) -o $(TARGET) $(OBJS) $(LIBS)
-
-gen_version_file:
-ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING))
- $(info updating $(VERSION_FILE) to $(VERSION_STRING))
- @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE)
-endif
-
-install: all
- $(INSTALL) -d -m 755 $(BINDIR)
- $(INSTALL) $(NAME) $(BINDIR)
- $(INSTALL) -d -m 755 $(DESKTOPDIR)
- $(INSTALL) $(DESKTOPFILE) $(DESKTOPDIR)
- $(INSTALL) -d -m 755 $(ICONDIR)
- $(INSTALL) -m 644 $(ICONFILE) $(ICONDIR)
- @-if test -z "$(DESTDIR)"; then \
- $(gtk_update_icon_cache); \
- fi
- $(INSTALL) -d -m 755 $(MANDIR)
- $(INSTALL) -m 644 $(MANFILES) $(MANDIR)
- @-if test ! -z "$(XSLT)"; then \
- $(INSTALL) -d -m 755 $(DATADIR)/$(NAME); \
- $(INSTALL) -d -m 755 $(XSLTDIR); \
- $(INSTALL) -m 644 $(XSLTFILES) $(XSLTDIR); \
- fi
- for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
- $(INSTALL) -d $(prefix)/$$LOC; \
- $(INSTALL) -m 644 $$LOC/$(NAME).mo $(prefix)/$$LOC/$(NAME).mo; \
- done
-
-
-install-macosx: all
- $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources
- $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS
- $(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/$(NAME)-bin
- $(INSTALL) $(MACOSXFILES)/$(NAME).sh $(MACOSXINSTALL)/Contents/MacOS/$(NAME)
- $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/
- $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/
- $(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/
- $(INSTALL) $(MACOSXFILES)/$(CAPITALIZED_NAME).icns $(MACOSXINSTALL)/Contents/Resources/
- for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
- $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/$$LOC; \
- $(INSTALL) $$LOC/$(NAME).mo $(MACOSXINSTALL)/Contents/Resources/$$LOC/$(NAME).mo; \
- done
- @-if test ! -z "$(XSLT)"; then \
- $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/xslt; \
- $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXINSTALL)/Contents/Resources/xslt/; \
- fi
-
-
-create-macosx-bundle: all
- $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources
- $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/MacOS
- $(INSTALL) $(NAME) $(MACOSXSTAGING)/Contents/MacOS/
- $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXSTAGING)/Contents/
- $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXSTAGING)/Contents/
- $(INSTALL) $(ICONFILE) $(MACOSXSTAGING)/Contents/Resources/
- $(INSTALL) $(MACOSXFILES)/$(CAPITALIZED_NAME).icns $(MACOSXSTAGING)/Contents/Resources/
- for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
- $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/$$LOC; \
- $(INSTALL) $$LOC/$(NAME).mo $(MACOSXSTAGING)/Contents/Resources/$$LOC/$(NAME).mo; \
- done
- @-if test ! -z "$(XSLT)"; then \
- $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/xslt; \
- $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXSTAGING)/Contents/Resources/xslt/; \
- fi
- $(GTK_MAC_BUNDLER) packaging/macosx/$(NAME).bundle
-
-sign-macosx-bundle: all
- codesign -s "3A8CE62A483083EDEA5581A61E770EC1FA8BECE8" /Applications/$(CAPITALIZED_NAME).app/Contents/MacOS/$(NAME)-bin
-
-install-cross-windows: all
- $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/share/locale
- for MSG in $(WINMSGDIRS); do\
- $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$MSG;\
- $(INSTALL) $(CROSS_PATH)/$$MSG/* $(WINDOWSSTAGING)/$$MSG;\
- done
- for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
- $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$LOC; \
- $(INSTALL) $$LOC/$(NAME).mo $(WINDOWSSTAGING)/$$LOC/$(NAME).mo; \
- done
-
-create-windows-installer: all $(NSIFILE) install-cross-windows
- $(MAKENSIS) $(NSIFILE)
-
-$(NSIFILE): $(NSIINPUTFILE)
- $(shell cat $(NSIINPUTFILE) | sed -e 's/VERSIONTOKEN/$(VERSION_STRING)/;s/PRODVTOKEN/$(PRODVERSION_STRING)/' > $(NSIFILE))
-
-$(INFOPLIST): $(INFOPLISTINPUT)
- $(shell cat $(INFOPLISTINPUT) | sed -e 's/CFBUNDLEVERSION_TOKEN/$(CFBUNDLEVERSION_STRING)/' > $(INFOPLIST))
-
-# Transifex merge the translations
-update-po-files:
- tx pull -af
-
-push-pot:
- xgettext -o po/$(NAME)-new.pot -s -k_ -kN_ --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c
- tx push -s
-
-EXTRA_FLAGS = $(GTKCFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) \
- $(XSLT) $(ZIP) $(SQLITE3) $(LIBDIVECOMPUTERCFLAGS) \
- $(LIBSOUPCFLAGS) $(OSMGPSMAPFLAGS) $(GCONF2CFLAGS)
-
-%.o: %.c
- @$(PRETTYECHO) ' CC' $<
- @mkdir -p .dep
- $(COMPILE_PREFIX)$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $<
-
-share/locale/%.UTF-8/LC_MESSAGES/$(NAME).mo: po/%.po po/%.aliases
- mkdir -p $(dir $@)
- msgfmt -c -o $@ po/$*.po
- @-if test -s po/$*.aliases; then \
- for ALIAS in `cat po/$*.aliases`; do \
- mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \
- cp $@ share/locale/$$ALIAS/LC_MESSAGES; \
- done; \
- fi
-
-satellite.png: satellite.svg
- convert -transparent white -resize 11x16 -depth 8 $< $@
-
-# This should work, but it doesn't get the colors quite right - so I manually converted with Gimp
-# convert -colorspace RGB -transparent white -resize 256x256 $(NAME)-icon.svg $(NAME)-icon.png
-#
-# The following creates the pixbuf data in .h files with the basename followed by '_pixmap'
-# as name of the data structure
-%.h: %.png
- @$(PRETTYECHO) ' gdk-pixbuf-csource' $<
- $(COMPILE_PREFIX)gdk-pixbuf-csource --struct --name `echo $* | sed 's/-/_/g'`_pixbuf $< > $@
-
-doc:
- $(MAKE) -C Documentation doc
-
-clean:
- rm -f $(OBJS) *~ $(NAME) $(NAME).exe po/*~ po/$(NAME)-new.pot \
- $(VERSION_FILE)
- rm -rf share .dep
-release:
- @scripts/check-version -cr $(VERSION_STRING)
- git archive --prefix $(CAPITALIZED_NAME)-$(VERSION_STRING)/ \
- --output $(CAPITALIZED_NAME)-$(VERSION_STRING).tgz \
- v$(VERSION_STRING)
+# Add files to the following variables if the auto-detection based on the
+# filename fails
+OBJS_NEEDING_MOC =
+OBJS_NEEDING_UIC =
+HEADERS_NEEDING_MOC =
--include $(DEPS)
+include Rules.mk
diff --git a/Rules.mk b/Rules.mk
new file mode 100644
index 000000000..bf9346a48
--- /dev/null
+++ b/Rules.mk
@@ -0,0 +1,252 @@
+# -*- Makefile -*-
+# Rules for building and creating the version file
+
+VERSION_FILE = version.h
+# There's only one line in $(VERSION_FILE); use the shell builtin `read'
+STORED_VERSION_STRING = \
+ $(subst ",,$(shell [ ! -r $(VERSION_FILE) ] || \
+ read ignore ignore v <$(VERSION_FILE) && echo $$v))
+#" workaround editor syntax highlighting quirk
+
+GET_VERSION = ./scripts/get-version
+VERSION_STRING := $(shell $(GET_VERSION) linux || echo "v$(VERSION)")
+# Mac Info.plist style with three numbers 1.2.3
+CFBUNDLEVERSION_STRING := $(shell $(GET_VERSION) darwin $(VERSION_STRING) || \
+ echo "$(VERSION).0")
+# Windows .nsi style with four numbers 1.2.3.4
+PRODVERSION_STRING := $(shell $(GET_VERSION) win $(VERSION_STRING) || \
+ echo "$(VERSION).0.0")
+
+MSGOBJS=$(addprefix share/locale/,$(MSGLANGS:.po=.UTF-8/LC_MESSAGES/$(NAME).mo))
+
+ifeq ($(V),1)
+ PRETTYECHO=true
+ COMPILE_PREFIX=
+else
+ PRETTYECHO=echo
+ COMPILE_PREFIX=@
+endif
+
+C_SOURCES = $(filter %.c, $(SOURCES))
+CXX_SOURCES = $(filter %.cpp, $(SOURCES)) $(RESOURCES:.qrc=.qrc.cpp)
+OTHER_SOURCES = $(filter-out %.c %.cpp, $(SOURCES))
+OBJS = $(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o) $(OTHER_SOURCES)
+
+# Add the objects for the header files which define QObject subclasses
+HEADERS_NEEDING_MOC += $(shell grep -l -s 'Q_OBJECT' $(HEADERS))
+MOC_OBJS = $(HEADERS_NEEDING_MOC:.h=.moc.o)
+
+ALL_OBJS = $(OBJS) $(MOC_OBJS)
+
+# Files for using Qt Creator
+CREATOR_FILES = $(NAME).config $(NAME).creator $(NAME).files $(NAME).includes
+
+all: $(NAME)
+
+$(TARGET): gen_version_file $(ALL_OBJS) $(MSGOBJS) $(INFOPLIST)
+ @$(PRETTYECHO) ' LINK' $(TARGET)
+ $(COMPILE_PREFIX)$(CXX) $(LDFLAGS) -o $(TARGET) $(ALL_OBJS) $(LIBS)
+
+gen_version_file $(VERSION_FILE):
+ifneq ($(STORED_VERSION_STRING),$(VERSION_STRING))
+ $(info updating $(VERSION_FILE) to $(VERSION_STRING))
+ @echo \#define VERSION_STRING \"$(VERSION_STRING)\" >$(VERSION_FILE)
+endif
+
+install: all
+ $(INSTALL) -d -m 755 $(BINDIR)
+ $(INSTALL) $(NAME) $(BINDIR)
+ $(INSTALL) -d -m 755 $(DESKTOPDIR)
+ $(INSTALL) $(DESKTOPFILE) $(DESKTOPDIR)
+ $(INSTALL) -d -m 755 $(ICONDIR)
+ $(INSTALL) -m 644 $(ICONFILE) $(ICONDIR)
+ @-if test -z "$(DESTDIR)"; then \
+ $(gtk_update_icon_cache); \
+ fi
+ $(INSTALL) -d -m 755 $(MANDIR)
+ $(INSTALL) -m 644 $(MANFILES) $(MANDIR)
+ @-if test ! -z "$(XSLT)"; then \
+ $(INSTALL) -d -m 755 $(DATADIR)/$(NAME); \
+ $(INSTALL) -d -m 755 $(XSLTDIR); \
+ $(INSTALL) -m 644 $(XSLTFILES) $(XSLTDIR); \
+ fi
+ for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
+ $(INSTALL) -d $(prefix)/$$LOC; \
+ $(INSTALL) -m 644 $$LOC/$(NAME).mo $(prefix)/$$LOC/$(NAME).mo; \
+ done
+
+
+install-macosx: all
+ $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources
+ $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS
+ $(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/$(NAME)-bin
+ $(INSTALL) $(MACOSXFILES)/$(NAME).sh $(MACOSXINSTALL)/Contents/MacOS/$(NAME)
+ $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/
+ $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/
+ $(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/
+ $(INSTALL) $(MACOSXFILES)/$(CAPITALIZED_NAME).icns $(MACOSXINSTALL)/Contents/Resources/
+ for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
+ $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/$$LOC; \
+ $(INSTALL) $$LOC/$(NAME).mo $(MACOSXINSTALL)/Contents/Resources/$$LOC/$(NAME).mo; \
+ done
+ @-if test ! -z "$(XSLT)"; then \
+ $(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources/xslt; \
+ $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXINSTALL)/Contents/Resources/xslt/; \
+ fi
+
+
+create-macosx-bundle: all
+ $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources
+ $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/MacOS
+ $(INSTALL) $(NAME) $(MACOSXSTAGING)/Contents/MacOS/
+ $(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXSTAGING)/Contents/
+ $(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXSTAGING)/Contents/
+ $(INSTALL) $(ICONFILE) $(MACOSXSTAGING)/Contents/Resources/
+ $(INSTALL) $(MACOSXFILES)/$(CAPITALIZED_NAME).icns $(MACOSXSTAGING)/Contents/Resources/
+ for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
+ $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/$$LOC; \
+ $(INSTALL) $$LOC/$(NAME).mo $(MACOSXSTAGING)/Contents/Resources/$$LOC/$(NAME).mo; \
+ done
+ @-if test ! -z "$(XSLT)"; then \
+ $(INSTALL) -d -m 755 $(MACOSXSTAGING)/Contents/Resources/xslt; \
+ $(INSTALL) -m 644 $(XSLTFILES) $(MACOSXSTAGING)/Contents/Resources/xslt/; \
+ fi
+ $(GTK_MAC_BUNDLER) packaging/macosx/$(NAME).bundle
+
+sign-macosx-bundle: all
+ codesign -s "3A8CE62A483083EDEA5581A61E770EC1FA8BECE8" /Applications/$(CAPITALIZED_NAME).app/Contents/MacOS/$(NAME)-bin
+
+install-cross-windows: all
+ $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/share/locale
+ for MSG in $(WINMSGDIRS); do\
+ $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$MSG;\
+ $(INSTALL) $(CROSS_PATH)/$$MSG/* $(WINDOWSSTAGING)/$$MSG;\
+ done
+ for LOC in $(wildcard share/locale/*/LC_MESSAGES); do \
+ $(INSTALL) -d -m 755 $(WINDOWSSTAGING)/$$LOC; \
+ $(INSTALL) $$LOC/$(NAME).mo $(WINDOWSSTAGING)/$$LOC/$(NAME).mo; \
+ done
+
+create-windows-installer: all $(NSIFILE) install-cross-windows
+ $(MAKENSIS) $(NSIFILE)
+
+$(NSIFILE): $(NSIINPUTFILE)
+ $(shell cat $(NSIINPUTFILE) | sed -e 's/VERSIONTOKEN/$(VERSION_STRING)/;s/PRODVTOKEN/$(PRODVERSION_STRING)/' > $(NSIFILE))
+
+$(INFOPLIST): $(INFOPLISTINPUT)
+ $(shell cat $(INFOPLISTINPUT) | sed -e 's/CFBUNDLEVERSION_TOKEN/$(CFBUNDLEVERSION_STRING)/' > $(INFOPLIST))
+
+# Transifex merge the translations
+update-po-files:
+ xgettext -o po/$(NAME)-new.pot -s -k_ -kN_ -ktr --keyword=C_:1c,2 --add-comments="++GETTEXT" *.c qt-ui/*.cpp
+ tx push -s
+ tx pull -af
+
+MOCFLAGS = $(filter -I%, $(CXXFLAGS) $(EXTRA_FLAGS)) $(filter -D%, $(CXXFLAGS) $(EXTRA_FLAGS))
+
+%.o: %.c
+ @$(PRETTYECHO) ' CC' $<
+ @mkdir -p .dep/$(@D)
+ $(COMPILE_PREFIX)$(CC) $(CFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $<
+
+%.o: %.cpp
+ @$(PRETTYECHO) ' CXX' $<
+ @mkdir -p .dep/$(@D)
+ $(COMPILE_PREFIX)$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MD -MF .dep/$@.dep -c -o $@ $<
+
+# This rule is for running the moc on QObject subclasses defined in the .h
+# files.
+%.moc.cpp: %.h
+ @$(PRETTYECHO) ' MOC' $<
+ $(COMPILE_PREFIX)$(MOC) $(MOCFLAGS) $< -o $@
+
+# This rule is for running the moc on QObject subclasses defined in the .cpp
+# files; remember to #include "<file>.moc" at the end of the .cpp file, or
+# you'll get linker errors ("undefined vtable for...")
+%.moc: %.cpp
+ @$(PRETTYECHO) ' MOC' $<
+ $(COMPILE_PREFIX)$(MOC) -i $(MOCFLAGS) $< -o $@
+
+# This creates the Qt resource sources.
+%.qrc.cpp: %.qrc
+ @$(PRETTYECHO) ' RCC' $<
+ $(COMPILE_PREFIX)$(RCC) $< -o $@
+%.qrc:
+
+# This creates the ui headers.
+ui_%.h: %.ui
+ @$(PRETTYECHO) ' UIC' $<
+ $(COMPILE_PREFIX)$(UIC) $< -o $@
+
+# This forces the creation of ui headers with the wrong path
+# This is required because the -MG option to the compiler outputs
+# unknown files with no path prefix
+ui_%.h: qt-ui/%.ui
+ @$(PRETTYECHO) ' UIC' $<
+ $(COMPILE_PREFIX)$(UIC) $< -o qt-ui/$@
+
+share/locale/%.UTF-8/LC_MESSAGES/$(NAME).mo: po/%.po po/%.aliases
+ @$(PRETTYECHO) ' MSGFMT' $*.po
+ @mkdir -p $(dir $@)
+ $(COMPILE_PREFIX)msgfmt -c -o $@ po/$*.po
+ @-if test -s po/$*.aliases; then \
+ for ALIAS in `cat po/$*.aliases`; do \
+ mkdir -p share/locale/$$ALIAS/LC_MESSAGES; \
+ cp $@ share/locale/$$ALIAS/LC_MESSAGES; \
+ done; \
+ fi
+
+satellite.png: satellite.svg
+ convert -transparent white -resize 11x16 -depth 8 $< $@
+
+# This should work, but it doesn't get the colors quite right - so I manually converted with Gimp
+# convert -colorspace RGB -transparent white -resize 256x256 subsurface-icon.svg subsurface-icon.png
+#
+# The following creates the pixbuf data in .h files with the basename followed by '_pixmap'
+# as name of the data structure
+%.h: %.png
+ @echo ' gdk-pixbuf-csource' $<
+ @gdk-pixbuf-csource --struct --name `echo $* | sed 's/-/_/g'`_pixbuf $< > $@
+
+doc:
+ $(MAKE) -C Documentation doc
+
+clean:
+ rm -f $(ALL_OBJS) *~ $(NAME) $(NAME).exe po/*~ po/$(NAME)-new.pot \
+ $(VERSION_FILE) $(HEADERS_NEEDING_MOC:.h=.moc) *.moc qt-ui/*.moc qt-ui/ui_*.h
+ rm -f $(RESOURCES:.qrc=.qrc.cpp)
+ rm -rf share
+
+confclean: clean
+ rm -f $(CONFIGFILE)
+ rm -rf .dep
+
+distclean: confclean
+ rm -f $(CREATOR_FILES)
+
+release:
+ @scripts/check-version -cr $(VERSION_STRING)
+ git archive --prefix $(CAPITALIZED_NAME)-$(VERSION_STRING)/ \
+ --output $(CAPITALIZED_NAME)-$(VERSION_STRING).tgz \
+ v$(VERSION_STRING)
+
+.PHONY: creator-files
+creator-files: $(CREATOR_FILES)
+$(NAME).files: Makefile $(CONFIGFILE)
+ echo $(wildcard *.h) $(HEADERS) $(SOURCES) | tr ' ' '\n' | sort | uniq > $(NAME).files
+$(NAME).config: Makefile $(CONFIGFILE)
+ echo $(patsubst -D%,%,$(filter -D%, $(CXXFLAGS) $(CFLAGS) $(EXTRA_FLAGS))) | tr ' ' '\n' | sort | uniq > $(NAME).config
+$(NAME).includes: Makefile $(CONFIGFILE)
+ echo $$PWD > $(NAME).includes
+ echo $(patsubst -I%,%,$(filter -I%, $(CXXFLAGS) $(CFLAGS) $(EXTRA_FLAGS))) | tr ' ' '\n' | sort | uniq >> $(NAME).includes
+$(NAME).creator:
+ echo '[General]' > $(NAME).creator
+
+ifneq ($(CONFIGURED)$(CONFIGURING),)
+.dep/%.o.dep: %.cpp
+ @mkdir -p $(@D)
+ @$(CXX) $(CXXFLAGS) $(EXTRA_FLAGS) -MM -MG -MF $@ -MT $(<:.cpp=.o) -c $<
+endif
+
+DEPS = $(addprefix .dep/,$(C_SOURCES:.c=.o.dep) $(CXX_SOURCES:.cpp=.o.dep))
+-include $(DEPS)
diff --git a/callbacks-gtk.h b/callbacks-gtk.h
index 08c159b4d..568916f6c 100644
--- a/callbacks-gtk.h
+++ b/callbacks-gtk.h
@@ -9,7 +9,7 @@ static void name(GtkWidget *w, gpointer data) \
#define OPTIONCALLBACK(name, option) \
static void name(GtkWidget *w, gpointer data) \
{ \
- GtkWidget **entry = data; \
+ GtkWidget **entry = (GtkWidget**)data; \
option = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); \
update_screen(); \
if (entry) \
diff --git a/color.h b/color.h
index 5d88fb5f7..f2013bcc9 100644
--- a/color.h
+++ b/color.h
@@ -4,54 +4,56 @@
/* The colors are named by picking the closest match
from http://chir.ag/projects/name-that-color */
+#include <QColor>
+
// Greens
-#define CAMARONE1 { 0.0, 0.4, 0.0, 1 }
-#define FUNGREEN1 { 0.0, 0.4, 0.2, 1 }
-#define FUNGREEN1_HIGH_TRANS { 0.0, 0.4, 0.2, 0.25 }
-#define KILLARNEY1 { 0.2, 0.4, 0.2, 1 }
-#define APPLE1 { 0.2, 0.6, 0.2, 1 }
-#define APPLE1_MED_TRANS { 0.2, 0.6, 0.2, 0.5 }
-#define APPLE1_HIGH_TRANS { 0.2, 0.6, 0.2, 0.25 }
-#define LIMENADE1 { 0.4, 0.8, 0.0, 1 }
-#define ATLANTIS1 { 0.4, 0.8, 0.2, 1 }
-#define ATLANTIS2 { 0.6, 0.8, 0.2, 1 }
-#define RIOGRANDE1 { 0.8, 0.8, 0.0, 1 }
-#define EARLSGREEN1 { 0.8, 0.8, 0.2, 1 }
-#define FORESTGREEN1 { 0.1, 0.5, 0.1, 1 }
+#define CAMARONE1 QColor::fromRgbF( 0.0, 0.4, 0.0, 1 )
+#define FUNGREEN1 QColor::fromRgbF( 0.0, 0.4, 0.2, 1 )
+#define FUNGREEN1_HIGH_TRANS QColor::fromRgbF( 0.0, 0.4, 0.2, 0.25 )
+#define KILLARNEY1 QColor::fromRgbF( 0.2, 0.4, 0.2, 1 )
+#define APPLE1 QColor::fromRgbF( 0.2, 0.6, 0.2, 1 )
+#define APPLE1_MED_TRANS QColor::fromRgbF( 0.2, 0.6, 0.2, 0.5 )
+#define APPLE1_HIGH_TRANS QColor::fromRgbF( 0.2, 0.6, 0.2, 0.25 )
+#define LIMENADE1 QColor::fromRgbF( 0.4, 0.8, 0.0, 1 )
+#define ATLANTIS1 QColor::fromRgbF( 0.4, 0.8, 0.2, 1 )
+#define ATLANTIS2 QColor::fromRgbF( 0.6, 0.8, 0.2, 1 )
+#define RIOGRANDE1 QColor::fromRgbF( 0.8, 0.8, 0.0, 1 )
+#define EARLSGREEN1 QColor::fromRgbF( 0.8, 0.8, 0.2, 1 )
+#define FORESTGREEN1 QColor::fromRgbF( 0.1, 0.5, 0.1, 1 )
// Reds
-#define PERSIANRED1 { 0.8, 0.2, 0.2, 1 }
-#define TUSCANY1 { 0.8, 0.4, 0.2, 1 }
-#define PIRATEGOLD1 { 0.8, 0.5, 0.0, 1 }
-#define HOKEYPOKEY1 { 0.8, 0.6, 0.2, 1 }
-#define CINNABAR1 { 0.9, 0.3, 0.2, 1 }
-#define REDORANGE1 { 1.0, 0.2, 0.2, 1 }
-#define REDORANGE1_HIGH_TRANS { 1.0, 0.2, 0.2, 0.25 }
-#define REDORANGE1_MED_TRANS { 1.0, 0.2, 0.2, 0.5 }
-#define RED1_MED_TRANS { 1.0, 0.0, 0.0, 0.5 }
-#define RED1 { 1.0, 0.0, 0.0, 1 }
+#define PERSIANRED1 QColor::fromRgbF( 0.8, 0.2, 0.2, 1 )
+#define TUSCANY1 QColor::fromRgbF( 0.8, 0.4, 0.2, 1 )
+#define PIRATEGOLD1 QColor::fromRgbF( 0.8, 0.5, 0.0, 1 )
+#define HOKEYPOKEY1 QColor::fromRgbF( 0.8, 0.6, 0.2, 1 )
+#define CINNABAR1 QColor::fromRgbF( 0.9, 0.3, 0.2, 1 )
+#define REDORANGE1 QColor::fromRgbF( 1.0, 0.2, 0.2, 1 )
+#define REDORANGE1_HIGH_TRANS QColor::fromRgbF( 1.0, 0.2, 0.2, 0.25 )
+#define REDORANGE1_MED_TRANS QColor::fromRgbF( 1.0, 0.2, 0.2, 0.5 )
+#define RED1_MED_TRANS QColor::fromRgbF( 1.0, 0.0, 0.0, 0.5 )
+#define RED1 QColor::fromRgbF( 1.0, 0.0, 0.0, 1 )
// Monochromes
-#define BLACK1_LOW_TRANS { 0.0, 0.0, 0.0, 0.75 }
-#define BLACK1_HIGH_TRANS { 0.0, 0.0, 0.0, 0.25 }
-#define TUNDORA1_MED_TRANS { 0.3, 0.3, 0.3, 0.5 }
-#define MERCURY1_MED_TRANS { 0.9, 0.9, 0.9, 0.5 }
-#define CONCRETE1_LOWER_TRANS { 0.95, 0.95, 0.95, 0.9 }
-#define WHITE1_MED_TRANS { 1.0, 1.0, 1.0, 0.5 }
-#define WHITE1 { 1.0, 1.0, 1.0, 1 }
+#define BLACK1_LOW_TRANS QColor::fromRgbF( 0.0, 0.0, 0.0, 0.75 )
+#define BLACK1_HIGH_TRANS QColor::fromRgbF( 0.0, 0.0, 0.0, 0.25 )
+#define TUNDORA1_MED_TRANS QColor::fromRgbF( 0.3, 0.3, 0.3, 0.5 )
+#define MERCURY1_MED_TRANS QColor::fromRgbF( 0.9, 0.9, 0.9, 0.5 )
+#define CONCRETE1_LOWER_TRANS QColor::fromRgbF( 0.95, 0.95, 0.95, 0.9 )
+#define WHITE1_MED_TRANS QColor::fromRgbF( 1.0, 1.0, 1.0, 0.5 )
+#define WHITE1 QColor::fromRgbF( 1.0, 1.0, 1.0, 1 )
// Blues
-#define GOVERNORBAY2 { 0.2, 0.2, 0.7, 1 }
-#define GOVERNORBAY1_MED_TRANS { 0.2, 0.2, 0.8, 0.5 }
-#define ROYALBLUE2 { 0.2, 0.2, 0.9, 1 }
-#define ROYALBLUE2_LOW_TRANS { 0.2, 0.2, 0.9, 0.75 }
+#define GOVERNORBAY2 QColor::fromRgbF( 0.2, 0.2, 0.7, 1 )
+#define GOVERNORBAY1_MED_TRANS QColor::fromRgbF( 0.2, 0.2, 0.8, 0.5 )
+#define ROYALBLUE2 QColor::fromRgbF( 0.2, 0.2, 0.9, 1 )
+#define ROYALBLUE2_LOW_TRANS QColor::fromRgbF( 0.2, 0.2, 0.9, 0.75 )
// Yellows / BROWNS
-#define SPRINGWOOD1 { 0.95, 0.95, 0.9, 1 }
-#define BROOM1_LOWER_TRANS { 1.0, 1.0, 0.1, 0.9 }
-#define PEANUT { 0.5, 0.2, 0.1, 1.0 }
-#define PEANUT_MED_TRANS { 0.5, 0.2, 0.1, 0.5 }
+#define SPRINGWOOD1 QColor::fromRgbF( 0.95, 0.95, 0.9, 1 )
+#define BROOM1_LOWER_TRANS QColor::fromRgbF( 1.0, 1.0, 0.1, 0.9 )
+#define PEANUT QColor::fromRgbF( 0.5, 0.2, 0.1, 1.0 )
+#define PEANUT_MED_TRANS QColor::fromRgbF( 0.5, 0.2, 0.1, 0.5 )
// Magentas
-#define MEDIUMREDVIOLET1_HIGHER_TRANS { 0.7, 0.2, 0.7, 0.1 }
+#define MEDIUMREDVIOLET1_HIGHER_TRANS QColor::fromRgbF( 0.7, 0.2, 0.7, 0.1 )
#endif
diff --git a/conversions.h b/conversions.h
new file mode 100644
index 000000000..f59fcc96b
--- /dev/null
+++ b/conversions.h
@@ -0,0 +1,16 @@
+/*
+ * conversions.h
+ *
+ * Helpers to convert between different units
+ *
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void convert_volume_pressure(int ml, int mbar, double *v, double *p);
+int convert_pressure(int mbar, double *p);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/device.h b/device.h
index 8a306ef78..636eb73c6 100644
--- a/device.h
+++ b/device.h
@@ -1,6 +1,10 @@
#ifndef DEVICE_INFO_H
#define DEVICE_INFO_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct device_info {
const char *model;
uint32_t deviceid;
@@ -17,4 +21,8 @@ extern struct device_info *create_device_info(const char *model, uint32_t device
extern struct device_info *remove_device_info(const char *model, uint32_t deviceid);
extern struct device_info *head_of_device_info_list(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/display-gtk.h b/display-gtk.h
index 627988784..634f05cab 100644
--- a/display-gtk.h
+++ b/display-gtk.h
@@ -8,6 +8,10 @@
#include <gdk/gdkkeysyms-compat.h>
#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern GtkWidget *main_window;
/* we want a progress bar as part of the device_data_t - let's abstract this out */
@@ -35,15 +39,11 @@ extern void set_divelist_font(const char *);
extern void update_screen(void);
extern void download_dialog(GtkWidget *, gpointer);
-extern int is_default_dive_computer_device(const char *);
-extern int is_default_dive_computer(const char *, const char *);
+
extern void add_dive_cb(GtkWidget *, gpointer);
extern void update_progressbar(progressbar_t *progress, double value);
extern void update_progressbar_text(progressbar_t *progress, const char *text);
-extern const char *default_dive_computer_vendor;
-extern const char *default_dive_computer_product;
-extern const char *default_dive_computer_device;
// info.c
enum {
@@ -90,22 +90,6 @@ typedef gint (*sort_func_t)(GtkTreeModel *model,
GtkTreeIter *b,
gpointer user_data);
-#define ALIGN_LEFT 1
-#define ALIGN_RIGHT 2
-#define INVISIBLE 4
-#define UNSORTABLE 8
-#define EDITABLE 16
-
-#ifndef TEXT_SCALE
-#define TEXT_SCALE 1.0
-#endif
-
-#define DEPTH_TEXT_SIZE (10 * TEXT_SCALE)
-#define PRESSURE_TEXT_SIZE (10 * TEXT_SCALE)
-#define DC_TEXT_SIZE (10.5 * TEXT_SCALE)
-#define PP_TEXT_SIZE (11 * TEXT_SCALE)
-#define TEMP_TEXT_SIZE (12 * TEXT_SCALE)
-
extern GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title,
data_func_t data_func, unsigned int flags);
extern GtkTreeViewColumn *tree_view_column_add_pixbuf(GtkWidget *tree_view, data_func_t data_func, GtkTreeViewColumn *col);
@@ -115,4 +99,8 @@ GError *uemis_download(const char *path, progressbar_t *progress, GtkDialog *dia
/* from planner.c */
extern void input_plan(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/display.h b/display.h
index c61abcf90..b62ae3ba3 100644
--- a/display.h
+++ b/display.h
@@ -1,14 +1,18 @@
#ifndef DISPLAY_H
#define DISPLAY_H
-#include <cairo.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
#define SCALE_SCREEN 1.0
#define SCALE_PRINT (1.0 / get_screen_dpi())
extern void repaint_dive(void);
extern void do_print(void);
-extern gdouble get_screen_dpi(void);
+
+// Commented out because I don't know how to get the dpi on a paint device yet.
+extern double get_screen_dpi(void);
/* Plot info with smoothing, velocity indication
* and one-, two- and three-minute minimums and maximums */
@@ -20,11 +24,16 @@ struct plot_info {
int mintemp, maxtemp;
double endtempcoord;
double maxpp;
- gboolean has_ndl;
+ bool has_ndl;
struct plot_data *entry;
};
/*
+// I'm not sure if this is needed anymore - but keeping this here
+// so I wont break stuff trying to redo the well.
+*/
+
+/*
* Cairo scaling really is horribly horribly mis-designed.
*
* Which is sad, because I really like Cairo otherwise. But
@@ -34,8 +43,6 @@ struct plot_info {
*/
struct graphics_context {
int printer;
- cairo_t *cr;
- cairo_rectangle_t drawing_area;
double maxx, maxy;
double leftx, rightx;
double topy, bottomy;
@@ -49,7 +56,7 @@ extern void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t sc
extern struct divecomputer *select_dc(struct divecomputer *main);
extern void init_profile_background(struct graphics_context *gc);
extern void attach_tooltip(int x, int y, int w, int h, const char *text, struct event *event);
-extern void get_plot_details(struct graphics_context *gc, int time, char *buf, size_t bufsize);
+extern void get_plot_details(struct graphics_context *gc, int time, char *buf, int bufsize);
extern int x_to_time(double x);
extern int x_abs(double x);
@@ -57,7 +64,7 @@ struct options {
enum { PRETTY, TABLE, TWOPERPAGE } type;
int print_selected;
int color_selected;
- gboolean notes_up;
+ bool notes_up;
int profile_height, notes_height, tanks_height;
};
@@ -65,4 +72,14 @@ extern char zoomed_plot, dc_number;
extern unsigned int amount_selected;
+extern int is_default_dive_computer_device(const char *);
+extern int is_default_dive_computer(const char *, const char *);
+extern const char *default_dive_computer_vendor;
+extern const char *default_dive_computer_product;
+extern const char *default_dive_computer_device;
+
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/dive.c b/dive.c
index 34025d68c..2c2307e41 100644
--- a/dive.c
+++ b/dive.c
@@ -1810,6 +1810,15 @@ struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean pr
return res;
}
+int get_index_for_dive(struct dive *dive) {
+ int i;
+ struct dive *d;
+ for_each_dive(i, d)
+ if (d == dive)
+ return i;
+ return -1;
+}
+
struct dive *find_dive_including(timestamp_t when)
{
int i;
diff --git a/dive.h b/dive.h
index cdd3e2770..50e0dc4d4 100644
--- a/dive.h
+++ b/dive.h
@@ -13,6 +13,16 @@
#include "sha1.h"
+#ifdef __cplusplus
+extern "C" {
+#else
+#if __STDC_VERSION__ >= 199901L
+#include <stdbool.h>
+#else
+typedef int bool;
+#endif
+#endif
+
#define O2_IN_AIR 209 // permille
#define N2_IN_AIR 781
#define O2_DENSITY 1429 // mg/Liter
@@ -372,6 +382,8 @@ struct dive {
struct divecomputer dc;
};
+extern int get_index_for_dive(struct dive *dive);
+
static inline int dive_has_gps_location(struct dive *dive)
{
return dive->latitude.udeg || dive->longitude.udeg;
@@ -549,7 +561,7 @@ static inline struct divecomputer *get_dive_dc(struct dive *dive, int nr)
#define for_each_gps_location(_i,_x) \
for ((_i) = 0; ((_x) = get_gps_location(_i, &gps_location_table)) != NULL; (_i)++)
-static inline struct dive *get_dive_by_diveid(int diveid, int deviceid)
+static inline struct dive *get_dive_by_diveid(uint32_t diveid, uint32_t deviceid)
{
int i;
struct dive *dive;
@@ -609,7 +621,6 @@ extern struct sample *prepare_sample(struct divecomputer *dc);
extern void finish_sample(struct divecomputer *dc);
extern void sort_table(struct dive_table *table);
-extern void report_dives(gboolean imported, gboolean prefer_imported);
extern struct dive *fixup_dive(struct dive *dive);
extern unsigned int dc_airtemp(struct divecomputer *dc);
extern struct dive *merge_dives(struct dive *a, struct dive *b, int offset, gboolean prefer_downloaded);
@@ -622,6 +633,7 @@ extern void add_event(struct divecomputer *dc, int time, int type, int flags, in
/* UI related protopypes */
extern void init_ui(int *argcp, char ***argvp);
+extern void init_qt_ui(int *argcp, char ***argvp);
extern void run_ui(void);
extern void exit_ui(void);
@@ -717,11 +729,28 @@ void get_gas_string(int o2, int he, char *buf, int len);
struct event *get_next_event(struct event *event, char *name);
+
+/* this struct holds the information that
+ * describes the cylinders of air.
+ * it is a global variable initialized in equipment.c
+ * used to fill the combobox in the add/edit cylinder
+ * dialog
+ */
+
+struct tank_info {
+ const char *name;
+ int cuft, ml, psi, bar;
+};
+
#ifdef DEBUGFILE
extern char *debugfilename;
extern FILE *debugfile;
#endif
+#ifdef __cplusplus
+}
+#endif
+
#include "pref.h"
#endif /* DIVE_H */
diff --git a/divelist-gtk.c b/divelist-gtk.c
index 1a47feb51..79613e012 100644
--- a/divelist-gtk.c
+++ b/divelist-gtk.c
@@ -55,7 +55,6 @@ static struct DiveList dive_list;
#define TREESTORE(_dl) GTK_TREE_STORE((_dl).treemodel)
#define LISTSTORE(_dl) GTK_TREE_STORE((_dl).listmodel)
-short autogroup = FALSE;
static gboolean ignore_selection_changes = FALSE;
static gboolean in_set_cursor = FALSE;
static gboolean set_selected(GtkTreeModel *model, GtkTreePath *path,
@@ -255,7 +254,6 @@ static void date_data_func(GtkTreeViewColumn *col,
gpointer data)
{
int idx, nr;
- struct tm tm;
timestamp_t when;
/* this should be enought for most languages. if not increase the value. */
char *buffer;
@@ -263,11 +261,10 @@ static void date_data_func(GtkTreeViewColumn *col,
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, DIVE_DATE, &when, -1);
nr = gtk_tree_model_iter_n_children(model, iter);
- utc_mkdate(when, &tm);
if (idx < 0) {
- buffer = get_trip_date_string(&tm, nr);
+ buffer = get_trip_date_string(when, nr);
} else {
- buffer = get_dive_date_string(&tm);
+ buffer = get_dive_date_string(when);
}
g_object_set(renderer, "text", buffer, NULL);
free(buffer);
@@ -430,40 +427,24 @@ static gint nitrox_sort_func(GtkTreeModel *model,
return a_he - b_he;
}
-#define UTF8_ELLIPSIS "\xE2\x80\xA6"
-
static void nitrox_data_func(GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data)
{
- int idx, o2, he, o2low;
- char buffer[80];
+ int idx;
+ char *buffer;
struct dive *dive;
gtk_tree_model_get(model, iter, DIVE_INDEX, &idx, -1);
- if (idx < 0) {
- *buffer = '\0';
- goto exit;
+ if (idx >= 0 && (dive = get_dive(idx))) {
+ buffer = get_nitrox_string(dive);
+ g_object_set(renderer, "text", buffer, NULL);
+ free(buffer);
+ } else {
+ g_object_set(renderer, "text", "", NULL);
}
- dive = get_dive(idx);
- get_dive_gas(dive, &o2, &he, &o2low);
- o2 = (o2 + 5) / 10;
- he = (he + 5) / 10;
- o2low = (o2low + 5) / 10;
-
- if (he)
- snprintf(buffer, sizeof(buffer), "%d/%d", o2, he);
- else if (o2)
- if (o2 == o2low)
- snprintf(buffer, sizeof(buffer), "%d", o2);
- else
- snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
- else
- strcpy(buffer, _("air"));
-exit:
- g_object_set(renderer, "text", buffer, NULL);
}
/* Render the SAC data (integer value of "ml / min") */
@@ -804,7 +785,6 @@ static gint gtk_dive_nr_sort(GtkTreeModel *model,
return dive_nr_sort(idx_a, idx_b, when_a, when_b);
}
-
static struct divelist_column {
const char *header;
data_func_t data;
@@ -898,6 +878,12 @@ static void row_activated_cb(GtkTreeView *tree_view,
edit_dive_info(get_dive(index), FALSE);
}
+void report_dives(bool is_imported, bool prefer_imported)
+{
+ process_dives(is_imported, prefer_imported);
+ dive_list_update_dives();
+}
+
void add_dive_cb(GtkWidget *menuitem, gpointer data)
{
struct dive *dive;
diff --git a/divelist.c b/divelist.c
index fcbc7e59c..75f427892 100644
--- a/divelist.c
+++ b/divelist.c
@@ -50,6 +50,8 @@
static short dive_list_changed = FALSE;
+short autogroup = FALSE;
+
dive_trip_t *dive_trip_list;
unsigned int amount_selected;
@@ -69,6 +71,13 @@ void dump_selection(void)
}
#endif
+void set_autogroup(gboolean value)
+{
+ /* if we keep the UI paradigm, this needs to toggle
+ * the checkbox on the autogroup menu item */
+ autogroup = value;
+}
+
dive_trip_t *find_trip_by_idx(int idx)
{
dive_trip_t *trip = dive_trip_list;
@@ -131,7 +140,7 @@ int trip_has_selected_dives(dive_trip_t *trip)
return 0;
}
-/* Get the values as we want to show them. Whole feet. But meters with one decimal for
+/* Get the values as we want to show them. Whole feet. But meters with one decimal for
* values less than 20m, without decimals for larger values */
void get_depth_values(int depth, int *depth_int, int *depth_decimal, int *show_decimal)
{
@@ -557,38 +566,71 @@ void get_suit(struct dive *dive, char **str)
#define MAX_DATE_STRING 256
/* caller needs to free the string */
-char *get_dive_date_string(struct tm *tm) {
+char *get_dive_date_string(timestamp_t when) {
char *buffer = malloc(MAX_DATE_STRING);
- if (buffer)
+ if (buffer) {
+ struct tm tm;
+ utc_mkdate(when, &tm);
snprintf(buffer, MAX_DATE_STRING,
/*++GETTEXT 60 char buffer weekday, monthname, day of month, year, hour:min */
_("%1$s, %2$s %3$d, %4$d %5$02d:%6$02d"),
- weekday(tm->tm_wday),
- monthname(tm->tm_mon),
- tm->tm_mday, tm->tm_year + 1900,
- tm->tm_hour, tm->tm_min);
+ weekday(tm.tm_wday),
+ monthname(tm.tm_mon),
+ tm.tm_mday, tm.tm_year + 1900,
+ tm.tm_hour, tm.tm_min);
+ }
return buffer;
}
/* caller needs to free the string */
-char *get_trip_date_string(struct tm *tm, int nr) {
+char *get_trip_date_string(timestamp_t when, int nr) {
char *buffer = malloc(MAX_DATE_STRING);
- if (buffer)
+ if (buffer) {
+ struct tm tm;
+ utc_mkdate(when, &tm);
snprintf(buffer, MAX_DATE_STRING,
/*++GETTEXT 60 char buffer weekday, monthname, day of month, year, nr dives */
ngettext("Trip %1$s, %2$s %3$d, %4$d (%5$d dive)",
"Trip %1$s, %2$s %3$d, %4$d (%5$d dives)", nr),
- weekday(tm->tm_wday),
- monthname(tm->tm_mon),
- tm->tm_mday, tm->tm_year + 1900,
+ weekday(tm.tm_wday),
+ monthname(tm.tm_mon),
+ tm.tm_mday, tm.tm_year + 1900,
nr);
+ }
+ return buffer;
+}
+
+#define MAX_NITROX_STRING 80
+#define UTF8_ELLIPSIS "\xE2\x80\xA6"
+
+/* callers needs to free the string */
+char *get_nitrox_string(struct dive *dive)
+{
+ int o2, he, o2low;
+ char *buffer = malloc(MAX_NITROX_STRING);
+
+ if (buffer) {
+ get_dive_gas(dive, &o2, &he, &o2low);
+ o2 = (o2 + 5) / 10;
+ he = (he + 5) / 10;
+ o2low = (o2low + 5) / 10;
+
+ if (he)
+ snprintf(buffer, MAX_NITROX_STRING, "%d/%d", o2, he);
+ else if (o2)
+ if (o2 == o2low)
+ snprintf(buffer, MAX_NITROX_STRING, "%d", o2);
+ else
+ snprintf(buffer, MAX_NITROX_STRING, "%d" UTF8_ELLIPSIS "%d", o2low, o2);
+ else
+ strcpy(buffer, _("air"));
+ }
return buffer;
}
/*
* helper functions for dive_trip handling
*/
-
#ifdef DEBUG_TRIP
void dump_trip_list(void)
{
@@ -869,20 +911,23 @@ void merge_dive_index(int i, struct dive *a)
add_single_dive(i, res);
delete_single_dive(i+1);
delete_single_dive(i+1);
-
+#if USE_GTK_UI
dive_list_update_dives();
+#endif
mark_divelist_changed(TRUE);
}
void select_dive(int idx)
{
struct dive *dive = get_dive(idx);
- if (dive && !dive->selected) {
+ if (dive) {
/* never select an invalid dive that isn't displayed */
if (dive->dive_tags & DTAG_INVALID && !prefs.display_invalid_dives)
return;
- dive->selected = 1;
- amount_selected++;
+ if (!dive->selected) {
+ dive->selected = 1;
+ amount_selected++;
+ }
selected_dive = idx;
}
}
@@ -935,3 +980,124 @@ void remove_autogen_trips()
}
}
+/*
+ * When adding dives to the dive table, we try to renumber
+ * the new dives based on any old dives in the dive table.
+ *
+ * But we only do it if:
+ *
+ * - there are no dives in the dive table
+ *
+ * OR
+ *
+ * - the last dive in the old dive table was numbered
+ *
+ * - all the new dives are strictly at the end (so the
+ * "last dive" is at the same location in the dive table
+ * after re-sorting the dives.
+ *
+ * - none of the new dives have any numbers
+ *
+ * This catches the common case of importing new dives from
+ * a dive computer, and gives them proper numbers based on
+ * your old dive list. But it tries to be very conservative
+ * and not give numbers if there is *any* question about
+ * what the numbers should be - in which case you need to do
+ * a manual re-numbering.
+ */
+static void try_to_renumber(struct dive *last, int preexisting)
+{
+ int i, nr;
+
+ /*
+ * If the new dives aren't all strictly at the end,
+ * we're going to expect the user to do a manual
+ * renumbering.
+ */
+ if (preexisting && get_dive(preexisting-1) != last)
+ return;
+
+ /*
+ * If any of the new dives already had a number,
+ * we'll have to do a manual renumbering.
+ */
+ for (i = preexisting; i < dive_table.nr; i++) {
+ struct dive *dive = get_dive(i);
+ if (dive->number)
+ return;
+ }
+
+ /*
+ * Ok, renumber..
+ */
+ if (last)
+ nr = last->number;
+ else
+ nr = 0;
+ for (i = preexisting; i < dive_table.nr; i++) {
+ struct dive *dive = get_dive(i);
+ dive->number = ++nr;
+ }
+}
+
+void process_dives(bool is_imported, bool prefer_imported)
+{
+ int i;
+ int preexisting = dive_table.preexisting;
+ struct dive *last;
+
+ /* check if we need a nickname for the divecomputer for newly downloaded dives;
+ * since we know they all came from the same divecomputer we just check for the
+ * first one */
+ if (preexisting < dive_table.nr && dive_table.dives[preexisting]->downloaded)
+ set_dc_nickname(dive_table.dives[preexisting]);
+ else
+ /* they aren't downloaded, so record / check all new ones */
+ for (i = preexisting; i < dive_table.nr; i++)
+ set_dc_nickname(dive_table.dives[i]);
+
+ /* This does the right thing for -1: NULL */
+ last = get_dive(preexisting-1);
+
+ sort_table(&dive_table);
+
+ for (i = 1; i < dive_table.nr; i++) {
+ struct dive **pp = &dive_table.dives[i-1];
+ struct dive *prev = pp[0];
+ struct dive *dive = pp[1];
+ struct dive *merged;
+
+ /* only try to merge overlapping dives - or if one of the dives has
+ * zero duration (that might be a gps marker from the webservice) */
+ if (prev->duration.seconds && dive->duration.seconds &&
+ prev->when + prev->duration.seconds < dive->when)
+ continue;
+
+ merged = try_to_merge(prev, dive, prefer_imported);
+ if (!merged)
+ continue;
+
+ /* careful - we might free the dive that last points to. Oops... */
+ if (last == prev || last == dive)
+ last = merged;
+
+ /* Redo the new 'i'th dive */
+ i--;
+ add_single_dive(i, merged);
+ delete_single_dive(i+1);
+ delete_single_dive(i+1);
+ }
+ /* make sure no dives are still marked as downloaded */
+ for (i = 1; i < dive_table.nr; i++)
+ dive_table.dives[i]->downloaded = FALSE;
+
+ if (is_imported) {
+ /* If there are dives in the table, are they numbered */
+ if (!last || last->number)
+ try_to_renumber(last, preexisting);
+
+ /* did we add dives to the dive table? */
+ if (preexisting != dive_table.nr)
+ mark_divelist_changed(TRUE);
+ }
+}
diff --git a/divelist.h b/divelist.h
index 23e22190d..7a9d07967 100644
--- a/divelist.h
+++ b/divelist.h
@@ -1,8 +1,13 @@
#ifndef DIVELIST_H
#define DIVELIST_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct dive;
+extern void report_dives(bool imported, bool prefer_imported);
extern void dive_list_update_dives(void);
extern void update_dive_list_col_visibility(void);
extern void update_dive_list_units(void);
@@ -16,12 +21,13 @@ extern void select_prev_dive(void);
extern void show_and_select_dive(struct dive *dive);
extern double init_decompression(struct dive * dive);
extern void export_all_dives_uddf_cb();
-
extern void upload_all_dives_divelogs_cb();
/* divelist core logic functions */
-extern char *get_dive_date_string(struct tm *tm);
-extern char *get_trip_date_string(struct tm *tm, int nr);
+extern void process_dives(bool imported, bool prefer_imported);
+extern char *get_dive_date_string(timestamp_t when);
+extern char *get_trip_date_string(timestamp_t when, int nr);
+extern char *get_nitrox_string(struct dive *dive);
extern void clear_trip_indexes(void);
extern dive_trip_t *find_trip_by_idx(int idx);
extern int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b);
@@ -45,4 +51,8 @@ extern void dump_selection(void);
extern void dump_trip_list(void);
#endif
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/download-dialog.c b/download-dialog.c
index 6a55a327c..8f6220e31 100644
--- a/download-dialog.c
+++ b/download-dialog.c
@@ -3,19 +3,23 @@
#include "dive.h"
#include "divelist.h"
#include "display.h"
+#if USE_GTK_UI
#include "display-gtk.h"
#include "callbacks-gtk.h"
+#endif
#include "libdivecomputer.h"
const char *default_dive_computer_vendor;
const char *default_dive_computer_product;
const char *default_dive_computer_device;
+#if USE_GTK_UI
static gboolean force_download;
static gboolean prefer_downloaded;
OPTIONCALLBACK(force_toggle, force_download)
OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded)
+#endif
struct product {
const char *product;
@@ -38,6 +42,7 @@ struct mydescriptor {
struct vendor *dc_list;
+#if USE_GTK_UI
static void render_dc_vendor(GtkCellLayout *cell,
GtkCellRenderer *renderer,
GtkTreeModel *model,
@@ -63,6 +68,7 @@ static void render_dc_product(GtkCellLayout *cell,
product = dc_descriptor_get_product(descriptor);
g_object_set(renderer, "text", product, NULL);
}
+#endif
int is_default_dive_computer(const char *vendor, const char *product)
{
@@ -75,7 +81,7 @@ int is_default_dive_computer_device(const char *name)
return default_dive_computer_device && !strcmp(name, default_dive_computer_device);
}
-static void set_default_dive_computer(const char *vendor, const char *product)
+void set_default_dive_computer(const char *vendor, const char *product)
{
if (!vendor || !*vendor)
return;
@@ -93,7 +99,7 @@ static void set_default_dive_computer(const char *vendor, const char *product)
subsurface_set_conf("dive_computer_product", product);
}
-static void set_default_dive_computer_device(const char *name)
+void set_default_dive_computer_device(const char *name)
{
if (!name || !*name)
return;
@@ -105,6 +111,7 @@ static void set_default_dive_computer_device(const char *name)
subsurface_set_conf("dive_computer_device", name);
}
+#if USE_GTK_UI
static void dive_computer_selector_changed(GtkWidget *combo, gpointer data)
{
GtkWidget *import, *button;
@@ -161,10 +168,10 @@ static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog)
gtk_box_pack_start(GTK_BOX(vbox), info, FALSE, FALSE, 0);
return info;
}
-
+#endif
/* create a list of lists and keep the elements sorted */
-static void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor)
+void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor)
{
struct vendor *dcl = dc_list;
struct vendor **dclp = &dc_list;
@@ -207,6 +214,7 @@ static void add_dc(const char *vendor, const char *product, dc_descriptor_t *des
pl->descriptor = descriptor;
}
+#if USE_GTK_UI
/* fill the vendors and create and fill the respective product stores; return the longest product name
* and also the indices of the default vendor / product */
static int fill_computer_list(GtkListStore *vendorstore, GtkListStore ***productstore, int *vendor_index, int *product_index)
@@ -485,3 +493,4 @@ void update_progressbar_text(progressbar_t *progress, const char *text)
{
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->bar), text);
}
+#endif
diff --git a/equipment.c b/equipment.c
index 97388e79b..189cc617f 100644
--- a/equipment.c
+++ b/equipment.c
@@ -16,9 +16,14 @@
#include "dive.h"
#include "display.h"
+#if USE_GTK_UI
#include "display-gtk.h"
+#endif
#include "divelist.h"
+#include "conversions.h"
+#if USE_GTK_UI
+#include "display-gtk.h"
static GtkListStore *cylinder_model, *weightsystem_model;
enum {
@@ -68,9 +73,10 @@ struct ws_widget {
GtkSpinButton *weight;
int w_idx;
};
+#endif /* USE_GTK_UI */
/* we want bar - so let's not use our unit functions */
-static int convert_pressure(int mbar, double *p)
+int convert_pressure(int mbar, double *p)
{
int decimals = 1;
double pressure;
@@ -86,7 +92,7 @@ static int convert_pressure(int mbar, double *p)
return decimals;
}
-static void convert_volume_pressure(int ml, int mbar, double *v, double *p)
+void convert_volume_pressure(int ml, int mbar, double *v, double *p)
{
double volume, pressure;
@@ -108,7 +114,7 @@ static void convert_volume_pressure(int ml, int mbar, double *v, double *p)
*v = volume;
}
-static int convert_weight(int grams, double *m)
+int convert_weight(int grams, double *m)
{
int decimals = 1; /* not sure - do people do less than whole lbs/kg ? */
double weight;
@@ -121,6 +127,7 @@ static int convert_weight(int grams, double *m)
return decimals;
}
+#if USE_GTK_UI
static void set_cylinder_description(struct cylinder_widget *cylinder, const char *desc)
{
set_active_text(cylinder->description, desc);
@@ -452,6 +459,28 @@ static void show_weightsystem(weightsystem_t *ws, struct ws_widget *weightsystem
set_weight_description(weightsystem_widget, desc);
set_weight_weight_spinbutton(weightsystem_widget, ws->weight.grams);
}
+#else
+/* placeholders for a few functions that we need to redesign for the Qt UI */
+void add_cylinder_description(cylinder_type_t *type)
+{
+ const char *desc;
+
+ desc = type->description;
+ if (!desc)
+ return;
+ /* now do something with it... */
+}
+void add_weightsystem_description(weightsystem_t *weightsystem)
+{
+ const char *desc;
+
+ desc = weightsystem->description;
+ if (!desc)
+ return;
+ /* now do something with it... */
+}
+
+#endif /* USE_GTK_UI */
gboolean cylinder_nodata(cylinder_t *cyl)
{
@@ -516,6 +545,7 @@ gboolean weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2)
return TRUE;
}
+#if USE_GTK_UI
static void set_one_cylinder(void *_data, GtkListStore *model, GtkTreeIter *iter)
{
cylinder_t *cyl = _data;
@@ -726,6 +756,7 @@ static void fill_cylinder_info(struct cylinder_widget *cylinder, cylinder_t *cyl
/*
* Also, insert it into the model if it doesn't already exist
*/
+ // WARNING: GTK-Specific Code.
add_cylinder(cylinder, desc, ml, mbar);
}
@@ -784,16 +815,13 @@ static void record_weightsystem_changes(weightsystem_t *ws, struct ws_widget *we
ws->description = desc;
add_weightsystem_type(desc, grams, &iter);
}
-
+#endif /* USE_GTK_UI */
/*
* We hardcode the most common standard cylinders,
* we should pick up any other names from the dive
* logs directly.
*/
-static struct tank_info {
- const char *name;
- int cuft, ml, psi, bar;
-} tank_info[100] = {
+struct tank_info tank_info[100] = {
/* Need an empty entry for the no-cylinder case */
{ "", },
@@ -836,6 +864,7 @@ static struct tank_info {
{ NULL, }
};
+#if USE_GTK_UI
static void fill_tank_list(GtkListStore *store)
{
GtkTreeIter iter;
@@ -1682,3 +1711,4 @@ void clear_equipment_widgets()
gtk_list_store_clear(cylinder_list[W_IDX_PRIMARY].model);
gtk_list_store_clear(weightsystem_list[W_IDX_PRIMARY].model);
}
+#endif /* USE_GTK_UI */
diff --git a/gtk-gui.c b/gtk-gui.c
index be985a85a..666438c33 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -3,6 +3,11 @@
/* creates the window and overall layout
* divelist, dive info, equipment and printing are handled in their own source files
*/
+/*
+ * This is the former qt-gui.cpp - so it already contains some Qt related
+ * functions. It's renamed back to gtk-ui.c to keep the old Gtk code
+ * around for reference in case we still need it... all that has now been
+ * ripped out of qt-gui.cpp */
#include <libintl.h>
#include <glib/gi18n.h>
#include <stdio.h>
@@ -24,19 +29,52 @@
#include "webservice.h"
#include "version.h"
#include "libdivecomputer.h"
+#include "qt-ui/mainwindow.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk-pixbuf/gdk-pixdata.h>
-#include "subsurface-icon.h"
+
+#include <QApplication>
+#include <QFileDialog>
+#include <QFileInfo>
+#include <QStringList>
+#include <QTextCodec>
+#include <QTranslator>
#include <osm-gps-map-source.h>
+class Translator: public QTranslator
+{
+ Q_OBJECT
+
+public:
+ Translator(QObject *parent = 0);
+ ~Translator() {}
+
+ virtual QString translate(const char *context, const char *sourceText,
+ const char *disambiguation = NULL) const;
+};
+
+Translator::Translator(QObject *parent):
+ QTranslator(parent)
+{
+}
+
+QString Translator::translate(const char *context, const char *sourceText,
+ const char *disambiguation) const
+{
+ return gettext(sourceText);
+}
+
+static const GdkPixdata subsurface_icon_pixbuf = {};
+
GtkWidget *main_window;
GtkWidget *main_vbox;
GtkWidget *error_info_bar;
GtkWidget *error_label;
GtkWidget *vpane, *hpane;
GtkWidget *notebook;
+static QApplication *application = NULL;
int error_count;
const char *existing_filename;
@@ -93,6 +131,7 @@ static void on_info_bar_response(GtkWidget *widget, gint response,
void report_error(GError* error)
{
+ qDebug("Warning: Calling GTK-Specific Code.");
if (error == NULL)
{
return;
@@ -216,10 +255,12 @@ static void file_save(GtkWidget *w, gpointer data)
static gboolean ask_save_changes()
{
+ //WARNING: Porting to Qt
+ qDebug("This method is being ported to Qt, please, stop using it. ");
GtkWidget *dialog, *label, *content;
gboolean quit = TRUE;
dialog = gtk_dialog_new_with_buttons(_("Save Changes?"),
- GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_WINDOW(main_window), GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
GTK_STOCK_NO, GTK_RESPONSE_NO,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
@@ -254,6 +295,7 @@ static gboolean ask_save_changes()
static void file_close(GtkWidget *w, gpointer data)
{
+ qDebug("Calling an already ported-to-qt Gtk method");
if (unsaved_changes())
if (ask_save_changes() == FALSE)
return;
@@ -282,8 +324,12 @@ static void file_close(GtkWidget *w, gpointer data)
show_dive_info(NULL);
}
+//#####################################################################
+//###### ALREAADY PORTED TO Qt. DELETE ME WHEN NOT MORE USERFUL. #
+//#####################################################################
static void file_open(GtkWidget *w, gpointer data)
{
+ qDebug("Calling an already ported-to-qt Gtk method.");
GtkWidget *dialog;
GtkFileFilter *filter;
const char *current_default;
@@ -315,7 +361,7 @@ static void file_open(GtkWidget *w, gpointer data)
fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
GError *error = NULL;
- filename = fn_glist->data;
+ filename = (char *)fn_glist->data;
parse_file(filename, &error);
set_filename(filename, TRUE);
if (error != NULL)
@@ -605,16 +651,16 @@ void update_screen()
update_dive_list_col_visibility();
}
-UNITCALLBACK(set_meter, length, METERS)
-UNITCALLBACK(set_feet, length, FEET)
-UNITCALLBACK(set_bar, pressure, BAR)
-UNITCALLBACK(set_psi, pressure, PSI)
-UNITCALLBACK(set_liter, volume, LITER)
-UNITCALLBACK(set_cuft, volume, CUFT)
-UNITCALLBACK(set_celsius, temperature, CELSIUS)
-UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT)
-UNITCALLBACK(set_kg, weight, KG)
-UNITCALLBACK(set_lbs, weight, LBS)
+UNITCALLBACK(set_meter, length, units::METERS)
+UNITCALLBACK(set_feet, length, units::FEET)
+UNITCALLBACK(set_bar, pressure, units::BAR)
+UNITCALLBACK(set_psi, pressure, units::PSI)
+UNITCALLBACK(set_liter, volume, units::LITER)
+UNITCALLBACK(set_cuft, volume, units::CUFT)
+UNITCALLBACK(set_celsius, temperature, units::CELSIUS)
+UNITCALLBACK(set_fahrenheit, temperature, units::FAHRENHEIT)
+UNITCALLBACK(set_kg, weight, units::KG)
+UNITCALLBACK(set_lbs, weight, units::LBS)
OPTIONCALLBACK(otu_toggle, prefs.visible_cols.otu)
OPTIONCALLBACK(maxcns_toggle, prefs.visible_cols.maxcns)
@@ -664,7 +710,7 @@ static gboolean gfhigh_edit(GtkWidget *w, GdkEvent *event, gpointer _data)
static void event_toggle(GtkWidget *w, gpointer _data)
{
- gboolean *plot_ev = _data;
+ gboolean *plot_ev = (gboolean *)_data;
*plot_ev = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
}
@@ -707,7 +753,7 @@ static void pick_default_file(GtkWidget *w, GtkButton *button)
list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog));
if (g_slist_length(list) == 1)
- gtk_button_set_label(button, list->data);
+ gtk_button_set_label(button, (const gchar *)list->data);
g_slist_free(list);
}
@@ -720,7 +766,7 @@ static void pick_default_file(GtkWidget *w, GtkButton *button)
static GtkWidget * map_provider_widget()
{
- OsmGpsMapSource_t i;
+ int i;
#if GTK_CHECK_VERSION(2,24,0)
GtkWidget *combobox = gtk_combo_box_text_new();
@@ -772,28 +818,28 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
gtk_container_add(GTK_CONTAINER(frame), box);
create_radio(box, _("Depth:"),
- _("Meter"), set_meter, (prefs.units.length == METERS),
- _("Feet"), set_feet, (prefs.units.length == FEET),
+ _("Meter"), set_meter, (prefs.units.length == units::METERS),
+ _("Feet"), set_feet, (prefs.units.length == units::FEET),
NULL);
create_radio(box, _("Pressure:"),
- _("Bar"), set_bar, (prefs.units.pressure == BAR),
- _("PSI"), set_psi, (prefs.units.pressure == PSI),
+ _("Bar"), set_bar, (prefs.units.pressure == units::BAR),
+ _("PSI"), set_psi, (prefs.units.pressure == units::PSI),
NULL);
create_radio(box, _("Volume:"),
- _("Liter"), set_liter, (prefs.units.volume == LITER),
- _("CuFt"), set_cuft, (prefs.units.volume == CUFT),
+ _("Liter"), set_liter, (prefs.units.volume == units::LITER),
+ _("CuFt"), set_cuft, (prefs.units.volume == units::CUFT),
NULL);
create_radio(box, _("Temperature:"),
- _("Celsius"), set_celsius, (prefs.units.temperature == CELSIUS),
- _("Fahrenheit"), set_fahrenheit, (prefs.units.temperature == FAHRENHEIT),
+ _("Celsius"), set_celsius, (prefs.units.temperature == units::CELSIUS),
+ _("Fahrenheit"), set_fahrenheit, (prefs.units.temperature == units::FAHRENHEIT),
NULL);
create_radio(box, _("Weight:"),
- _("kg"), set_kg, (prefs.units.weight == KG),
- _("lbs"), set_lbs, (prefs.units.weight == LBS),
+ _("kg"), set_kg, (prefs.units.weight == units::KG),
+ _("lbs"), set_lbs, (prefs.units.weight == units::LBS),
NULL);
frame = gtk_frame_new(_("Show Columns"));
@@ -1072,14 +1118,14 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
prefs.default_filename = new_default;
}
/* get the map provider selected */
- OsmGpsMapSource_t i;
+ int i;
#if GTK_CHECK_VERSION(2,24,0)
char *provider = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(map_provider));
#else
char *provider = gtk_combo_box_get_active_text(GTK_COMBO_BOX(map_provider));
#endif
for (i = OSM_GPS_MAP_SOURCE_OPENSTREETMAP; i <= OSM_GPS_MAP_SOURCE_YAHOO_STREET; i++)
- if (!strcmp(provider,osm_gps_map_source_get_friendly_name(i))) {
+ if (!strcmp(provider,osm_gps_map_source_get_friendly_name((OsmGpsMapSource_t)i))) {
prefs.map_provider = i;
break;
}
@@ -1097,7 +1143,7 @@ static void preferences_dialog(GtkWidget *w, gpointer data)
static void create_toggle(const char* label, int *on, void *_data)
{
- GtkWidget *button, *table = _data;
+ GtkWidget *button, *table = GTK_WIDGET(_data);
int rows, cols, x, y;
static int count;
@@ -1413,7 +1459,7 @@ static void edit_dc_delete_rows(GtkTreeView *view)
selected_rows = gtk_tree_selection_get_selected_rows(selection, &model);
for (list = selected_rows; list; list = g_list_next(list)) {
- path = list->data;
+ path = (GtkTreePath *)list->data;
ref = gtk_tree_row_reference_new(model, path);
row_references = g_list_append(row_references, ref);
}
@@ -1509,7 +1555,7 @@ static void edit_dc_nicknames(GtkWidget *w, gpointer data)
dialog = gtk_dialog_new_with_buttons(_("Edit Dive Computer Nicknames"),
GTK_WINDOW(main_window),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
GTK_STOCK_DELETE,
SUB_RESPONSE_DELETE,
GTK_STOCK_CANCEL,
@@ -1577,7 +1623,7 @@ static void edit_dc_nicknames(GtkWidget *w, gpointer data)
if (res == SUB_RESPONSE_DELETE) {
confirm = gtk_dialog_new_with_buttons(_("Delete a dive computer information entry"),
GTK_WINDOW(dialog),
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GtkDialogFlags(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
GTK_STOCK_YES,
GTK_RESPONSE_YES,
GTK_STOCK_NO,
@@ -1773,8 +1819,143 @@ static gboolean notebook_tooltip (GtkWidget *widget, gint x, gint y,
}
}
+#if NEEDS_TO_MOVE_TO_QT_UI
+/* this appears to have moved - but it's very different in qt-ui */
+
+class MainWindow: public QMainWindow, private Ui::MainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = 0);
+ ~MainWindow() {}
+
+ void setCurrentFileName(const QString &fileName);
+
+private Q_SLOTS:
+ void on_actionNew_triggered() { on_actionClose_triggered(); }
+ void on_actionOpen_triggered();
+ void on_actionSave_triggered() { file_save(NULL, NULL); }
+ void on_actionSaveAs_triggered() { file_save_as(NULL, NULL); }
+ void on_actionClose_triggered();
+
+private:
+ QStringList fileNameFilters() const;
+
+private:
+ QString m_currentFileName;
+};
+
+MainWindow::MainWindow(QWidget *parent):
+ QMainWindow(parent)
+{
+ setupUi(this);
+}
+
+void MainWindow::setCurrentFileName(const QString &fileName)
+{
+ if (fileName == m_currentFileName) return;
+ m_currentFileName = fileName;
+
+ QString title = tr("Subsurface");
+ if (!m_currentFileName.isEmpty()) {
+ QFileInfo fileInfo(m_currentFileName);
+ title += " - " + fileInfo.fileName();
+ }
+ setWindowTitle(title);
+}
+
+void MainWindow::on_actionOpen_triggered()
+{
+ QString defaultFileName = prefs.default_filename;
+ QFileInfo fileInfo(defaultFileName);
+
+ QFileDialog dialog(this, tr("Open File"), fileInfo.path());
+ dialog.setFileMode(QFileDialog::ExistingFile);
+ dialog.selectFile(defaultFileName);
+ dialog.setNameFilters(fileNameFilters());
+ if (dialog.exec()) {
+ /* first, close the existing file, if any */
+ file_close(NULL, NULL);
+
+ /* we know there is only one filename */
+ QString fileName = dialog.selectedFiles().first();
+ GError *error = NULL;
+ parse_file(fileName.toUtf8().constData(), &error);
+ if (error != NULL) {
+ report_error(error);
+ g_error_free(error);
+ error = NULL;
+ } else {
+ setCurrentFileName(fileName);
+ }
+ report_dives(FALSE, FALSE);
+ }
+}
+
+void MainWindow::on_actionClose_triggered()
+{
+ if (unsaved_changes())
+ if (ask_save_changes() == FALSE)
+ return;
+
+ setCurrentFileName(QString());
+
+ /* free the dives and trips */
+ while (dive_table.nr)
+ delete_single_dive(0);
+ mark_divelist_changed(FALSE);
+
+ /* clear the selection and the statistics */
+ selected_dive = -1;
+ process_selected_dives();
+ clear_stats_widgets();
+ clear_events();
+ show_dive_stats(NULL);
+
+ /* clear the equipment page */
+ clear_equipment_widgets();
+
+ /* redraw the screen */
+ dive_list_update_dives();
+ show_dive_info(NULL);
+}
+
+QStringList MainWindow::fileNameFilters() const
+{
+ QStringList filters;
+
+ filters << "*.xml *.uddf *.udcf *.jlb"
+#ifdef LIBZIP
+ " *.sde *.dld"
+#endif
+#ifdef SQLITE3
+ " *.db"
+#endif
+ ;
+ return filters;
+}
+#endif /* NEEDS_TO_MOVE_TO_QT_UI */
+
+void init_qt_ui(int *argcp, char ***argvp)
+{
+ application->installTranslator(new Translator(application));
+ MainWindow *window = new MainWindow();
+ window->show();
+}
+
void init_ui(int *argcp, char ***argvp)
{
+ application = new QApplication(*argcp, *argvp);
+
+#if QT_VERSION < 0x050000
+ // ask QString in Qt 4 to interpret all char* as UTF-8,
+ // like Qt 5 does.
+ // 106 is "UTF-8", this is faster than lookup by name
+ // [http://www.iana.org/assignments/character-sets/character-sets.xml]
+ QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106));
+#endif
+
GtkWidget *win;
GtkWidget *nb_page;
GtkWidget *dive_list;
@@ -1900,11 +2081,12 @@ void init_ui(int *argcp, char ***argvp)
void run_ui(void)
{
- gtk_main();
+ application->exec();
}
void exit_ui(void)
{
+ delete application;
subsurface_close_conf();
if (existing_filename)
free((void *)existing_filename);
@@ -1924,7 +2106,8 @@ static int tooltips;
void attach_tooltip(int x, int y, int w, int h, const char *text, struct event *event)
{
cairo_rectangle_t *rect;
- tooltip_rects = realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t));
+ tooltip_rects = (tooltip_record_t *)
+ realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t));
rect = &tooltip_rects[tooltips].rect;
rect->x = x;
rect->y = y;
@@ -2044,7 +2227,7 @@ static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data)
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
GtkAllocation allocation;
- static struct graphics_context gc = { .printer = 0 };
+ static struct graphics_context gc = { 0 };
/* the drawing area gives TOTAL width * height - x,y is used as the topx/topy offset
* so effective drawing area is width-2x * height-2y */
@@ -2090,7 +2273,7 @@ static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer
static void add_gas_change_cb(GtkWidget *menuitem, gpointer data)
{
- double *x = data;
+ double *x = (double *)data;
int when = x_to_time(*x);
int cylnr = select_cylinder(current_dive, when);
if (cylnr >= 0) {
@@ -2130,7 +2313,7 @@ int confirm_dialog(int when, char *action_text, char *event_text)
static void add_bookmark_cb(GtkWidget *menuitem, gpointer data)
{
- double *x = data;
+ double *x = (double *)data;
int when = x_to_time(*x);
if (confirm_dialog(when, _("Add"), _("bookmark"))){
@@ -2158,7 +2341,7 @@ static struct event *event_at_x(double rel_x)
static void remove_event_cb(GtkWidget *menuitem, gpointer data)
{
- struct event *event = data;
+ struct event *event = (struct event *)data;
if (confirm_dialog(event->time.seconds, _("Remove"), _(event->name))){
struct event **ep = &current_dc->events;
while (ep && *ep != event)
@@ -2430,3 +2613,5 @@ gdouble get_screen_dpi(void)
gdouble dpi_h = floor((h / h_mm) * mm_per_inch);
return dpi_h;
}
+
+#include "qt-gui.moc"
diff --git a/helpers.h b/helpers.h
new file mode 100644
index 000000000..8e72d6a20
--- /dev/null
+++ b/helpers.h
@@ -0,0 +1,19 @@
+/*
+ * helpers.h
+ *
+ * header file for random helper functions of Subsurface
+ *
+ */
+#ifndef HELPER_H
+#define HELPER_H
+
+#include <QString>
+#include "dive.h"
+
+QString get_depth_string(depth_t depth, bool showunit);
+QString get_weight_string(weight_t weight, bool showunit);
+QString get_temperature_string(temperature_t temp, bool showunit);
+QString get_volume_string(volume_t volume, bool showunit);
+QString get_pressure_string(pressure_t pressure, bool showunit);
+
+#endif /* HELPER_H */
diff --git a/info-gtk.c b/info-gtk.c
index 61ed1c3b6..4ba9058cf 100644
--- a/info-gtk.c
+++ b/info-gtk.c
@@ -796,6 +796,14 @@ int edit_multi_dive_info(struct dive *single_dive)
return success;
}
+int edit_dive_info(struct dive *dive, gboolean newdive)
+{
+ if (!dive || (!newdive && !amount_selected))
+ return 0;
+
+ return edit_multi_dive_info(dive);
+}
+
static GtkWidget *frame_box(GtkWidget *vbox, const char *fmt, ...)
{
va_list ap;
diff --git a/info.c b/info.c
index 0abee1c1b..4e0c3cadb 100644
--- a/info.c
+++ b/info.c
@@ -448,10 +448,15 @@ void update_time_depth(struct dive *dive, struct dive *edited)
dive->dc.meandepth.mm = edited->dc.meandepth.mm;
}
-int edit_dive_info(struct dive *dive, gboolean newdive)
+void add_people(const char *string)
{
- if (!dive || (!newdive && !amount_selected))
- return 0;
-
- return edit_multi_dive_info(dive);
+ /* add names to the completion list for people */
+}
+void add_location(const char *string)
+{
+ /* add names to the completion list for locations */
+}
+void add_suit(const char *string)
+{
+ /* add names to the completion list for suits */
}
diff --git a/libdivecomputer.c b/libdivecomputer.c
index 9ad5df594..cc1c0be28 100644
--- a/libdivecomputer.c
+++ b/libdivecomputer.c
@@ -26,6 +26,7 @@ static double progress_bar_fraction = 0.0;
static int stoptime, stopdepth, ndl, po2, cns;
static gboolean in_deco, first_temp_is_air;
+#if USE_GTK_UI
static GError *error(const char *fmt, ...)
{
va_list args;
@@ -38,6 +39,7 @@ static GError *error(const char *fmt, ...)
va_end(args);
return error;
}
+#endif
static dc_status_t create_parser(device_data_t *devdata, dc_parser_t **parser)
{
@@ -708,6 +710,7 @@ static const char *do_libdivecomputer_import(device_data_t *data)
return err;
}
+#if USE_GTK_UI
static void *pthread_wrapper(void *_data)
{
device_data_t *data = _data;
@@ -772,3 +775,4 @@ GError *do_import(device_data_t *data)
return error(retval, data->vendor, data->product, data->devname);
return NULL;
}
+#endif
diff --git a/libdivecomputer.h b/libdivecomputer.h
index 2fd42c91a..639a69c73 100644
--- a/libdivecomputer.h
+++ b/libdivecomputer.h
@@ -18,10 +18,12 @@ typedef struct device_data_t {
unsigned int deviceid, diveid;
dc_device_t *device;
dc_context_t *context;
- progressbar_t progress;
int preexisting;
gboolean force_download;
+#if USE_GTK_UI
+ progressbar_t progress;
GtkDialog *dialog;
+#endif
} device_data_t;
extern GError *do_import(device_data_t *data);
diff --git a/linux.c b/linux.c
index 4add7bb01..33a846f9b 100644
--- a/linux.c
+++ b/linux.c
@@ -1,7 +1,10 @@
/* linux.c */
/* implements Linux specific functions */
#include "dive.h"
+#include "display.h"
+#if USE_GTK_UI
#include "display-gtk.h"
+#endif
#include <gconf/gconf-client.h>
#include <string.h>
@@ -9,7 +12,7 @@ const char system_divelist_default_font[] = "Sans 8";
GConfClient *gconf;
-static char *gconf_name(char *name)
+static char *gconf_name(const char *name)
{
static char buf[255] = "/apps/subsurface/";
@@ -23,32 +26,32 @@ void subsurface_open_conf(void)
gconf = gconf_client_get_default();
}
-void subsurface_unset_conf(char *name)
+void subsurface_unset_conf(const char *name)
{
gconf_client_unset(gconf, gconf_name(name), NULL);
}
-void subsurface_set_conf(char *name, const char *value)
+void subsurface_set_conf(const char *name, const char *value)
{
gconf_client_set_string(gconf, gconf_name(name), value, NULL);
}
-void subsurface_set_conf_bool(char *name, int value)
+void subsurface_set_conf_bool(const char *name, int value)
{
gconf_client_set_bool(gconf, gconf_name(name), value > 0, NULL);
}
-void subsurface_set_conf_int(char *name, int value)
+void subsurface_set_conf_int(const char *name, int value)
{
gconf_client_set_int(gconf, gconf_name(name), value , NULL);
}
-const void *subsurface_get_conf(char *name)
+const char *subsurface_get_conf(const char *name)
{
return gconf_client_get_string(gconf, gconf_name(name), NULL);
}
-int subsurface_get_conf_bool(char *name)
+int subsurface_get_conf_bool(const char *name)
{
GConfValue *val;
gboolean ret;
@@ -61,7 +64,7 @@ int subsurface_get_conf_bool(char *name)
return ret;
}
-int subsurface_get_conf_int(char *name)
+int subsurface_get_conf_int(const char *name)
{
int val = gconf_client_get_int(gconf, gconf_name(name), NULL);
if(!val)
@@ -80,6 +83,7 @@ void subsurface_close_conf(void)
/* this is a no-op */
}
+#if USE_GTK_UI
int subsurface_fill_device_list(GtkListStore *store)
{
int i = 0;
@@ -139,6 +143,7 @@ int subsurface_fill_device_list(GtkListStore *store)
}
return index;
}
+#endif /* USE_GTK_UI */
const char *subsurface_icon_name()
{
@@ -170,11 +175,13 @@ const char *subsurface_gettext_domainpath(char *argv0)
}
}
+#if USE_GTK_UI
void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
GtkWidget *vbox, GtkUIManager *ui_manager)
{
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
}
+#endif /* USE_GTK_UI */
void subsurface_command_line_init(gint *argc, gchar ***argv)
{
@@ -191,6 +198,7 @@ gboolean subsurface_os_feature_available(os_feature_t f)
return TRUE;
}
+#if USE_GTK_UI
gboolean subsurface_launch_for_uri(const char* uri)
{
GError *err = NULL;
@@ -202,3 +210,4 @@ gboolean subsurface_launch_for_uri(const char* uri)
}
return TRUE;
}
+#endif /* USE_GTK_UI */
diff --git a/macos.c b/macos.c
index aee4c73ca..b43849f87 100644
--- a/macos.c
+++ b/macos.c
@@ -2,7 +2,10 @@
/* implements Mac OS X specific functions */
#include <stdlib.h>
#include "dive.h"
+#include "display.h"
+#if USE_GTK_UI
#include "display-gtk.h"
+#endif /* USE_GTK_UI */
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <mach-o/dyld.h>
@@ -30,29 +33,29 @@ void subsurface_open_conf(void)
/* nothing at this time */
}
-void subsurface_unset_conf(char *name)
+void subsurface_unset_conf(const char *name)
{
CFPreferencesSetAppValue(CFSTR_VAR(name), NULL, SUBSURFACE_PREFERENCES);
}
-void subsurface_set_conf(char *name, const char *value)
+void subsurface_set_conf(const char *name, const char *value)
{
CFPreferencesSetAppValue(CFSTR_VAR(name), CFSTR_VAR(value), SUBSURFACE_PREFERENCES);
}
-void subsurface_set_conf_bool(char *name, int value)
+void subsurface_set_conf_bool(const char *name, int value)
{
CFPreferencesSetAppValue(CFSTR_VAR(name),
value ? kCFBooleanTrue : kCFBooleanFalse, SUBSURFACE_PREFERENCES);
}
-void subsurface_set_conf_int(char *name, int value)
+void subsurface_set_conf_int(const char *name, int value)
{
CFNumberRef numRef = CFNumberCreate(NULL, kCFNumberIntType, &value);
CFPreferencesSetAppValue(CFSTR_VAR(name), numRef, SUBSURFACE_PREFERENCES);
}
-const void *subsurface_get_conf(char *name)
+const char *subsurface_get_conf(const char *name)
{
CFPropertyListRef strpref;
@@ -62,7 +65,7 @@ const void *subsurface_get_conf(char *name)
return strdup(CFStringGetCStringPtr(strpref, kCFStringEncodingMacRoman));
}
-int subsurface_get_conf_bool(char *name)
+int subsurface_get_conf_bool(const char *name)
{
Boolean boolpref, exists;
@@ -72,7 +75,7 @@ int subsurface_get_conf_bool(char *name)
return boolpref;
}
-int subsurface_get_conf_int(char *name)
+int subsurface_get_conf_int(const char *name)
{
Boolean exists;
CFIndex value;
@@ -151,8 +154,11 @@ const char *subsurface_icon_name()
{
static char path[PATH_MAX];
+#if USE_GTK_UI
snprintf(path, sizeof(path), "%s/%s", gtkosx_application_get_resource_path(), ICON_NAME);
-
+#else
+ /* need Qt path */
+#endif
return path;
}
@@ -174,15 +180,18 @@ const char *subsurface_gettext_domainpath(char *argv0)
{
/* on a Mac we ignore the argv0 argument and instead use the resource_path
* to figure out where to find the translation files */
+#if USE_GTK_UI
static char buffer[PATH_MAX];
const char *resource_path = gtkosx_application_get_resource_path();
if (resource_path) {
snprintf(buffer, sizeof(buffer), "%s/share/locale", resource_path);
return buffer;
}
+#endif /* USE_GTK_UI */
return "./share/locale";
}
+#if USE_GTK_UI
static void show_main_window(GtkWidget *w, gpointer data)
{
gtk_widget_show(main_window);
@@ -230,6 +239,7 @@ void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
gtkosx_application_ready(osx_app);
}
+#endif /* UES_GTK_UI */
void subsurface_command_line_init(gint *argc, gchar ***argv)
{
diff --git a/main.c b/main.c
index a57e12981..510dbfa68 100644
--- a/main.c
+++ b/main.c
@@ -10,7 +10,10 @@
#include "dive.h"
#include "divelist.h"
+#ifdef USE_GTK_UI
#include <osm-gps-map.h>
+#endif
+
#ifdef DEBUGFILE
char *debugfilename;
FILE *debugfile;
@@ -36,7 +39,9 @@ struct preferences default_prefs = {
.calc_ceiling_3m_incr = FALSE,
.gflow = 0.30,
.gfhigh = 0.75,
+#ifdef USE_GTK_UI
.map_provider = OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID,
+#endif
};
/* random helper functions, used here or elsewhere */
@@ -77,137 +82,10 @@ const char *monthname(int mon)
}
/*
- * When adding dives to the dive table, we try to renumber
- * the new dives based on any old dives in the dive table.
- *
- * But we only do it if:
- *
- * - there are no dives in the dive table
- *
- * OR
- *
- * - the last dive in the old dive table was numbered
- *
- * - all the new dives are strictly at the end (so the
- * "last dive" is at the same location in the dive table
- * after re-sorting the dives.
- *
- * - none of the new dives have any numbers
- *
- * This catches the common case of importing new dives from
- * a dive computer, and gives them proper numbers based on
- * your old dive list. But it tries to be very conservative
- * and not give numbers if there is *any* question about
- * what the numbers should be - in which case you need to do
- * a manual re-numbering.
- */
-static void try_to_renumber(struct dive *last, int preexisting)
-{
- int i, nr;
-
- /*
- * If the new dives aren't all strictly at the end,
- * we're going to expect the user to do a manual
- * renumbering.
- */
- if (preexisting && get_dive(preexisting-1) != last)
- return;
-
- /*
- * If any of the new dives already had a number,
- * we'll have to do a manual renumbering.
- */
- for (i = preexisting; i < dive_table.nr; i++) {
- struct dive *dive = get_dive(i);
- if (dive->number)
- return;
- }
-
- /*
- * Ok, renumber..
- */
- if (last)
- nr = last->number;
- else
- nr = 0;
- for (i = preexisting; i < dive_table.nr; i++) {
- struct dive *dive = get_dive(i);
- dive->number = ++nr;
- }
-}
-
-/*
* track whether we switched to importing dives
*/
static gboolean imported = FALSE;
-/*
- * This doesn't really report anything at all. We just sort the
- * dives, the GUI does the reporting
- */
-void report_dives(gboolean is_imported, gboolean prefer_imported)
-{
- int i;
- int preexisting = dive_table.preexisting;
- struct dive *last;
-
- /* check if we need a nickname for the divecomputer for newly downloaded dives;
- * since we know they all came from the same divecomputer we just check for the
- * first one */
- if (preexisting < dive_table.nr && dive_table.dives[preexisting]->downloaded)
- set_dc_nickname(dive_table.dives[preexisting]);
- else
- /* they aren't downloaded, so record / check all new ones */
- for (i = preexisting; i < dive_table.nr; i++)
- set_dc_nickname(dive_table.dives[i]);
-
- /* This does the right thing for -1: NULL */
- last = get_dive(preexisting-1);
-
- sort_table(&dive_table);
-
- for (i = 1; i < dive_table.nr; i++) {
- struct dive **pp = &dive_table.dives[i-1];
- struct dive *prev = pp[0];
- struct dive *dive = pp[1];
- struct dive *merged;
-
- /* only try to merge overlapping dives - or if one of the dives has
- * zero duration (that might be a gps marker from the webservice) */
- if (prev->duration.seconds && dive->duration.seconds &&
- prev->when + prev->duration.seconds < dive->when)
- continue;
-
- merged = try_to_merge(prev, dive, prefer_imported);
- if (!merged)
- continue;
-
- /* careful - we might free the dive that last points to. Oops... */
- if (last == prev || last == dive)
- last = merged;
-
- /* Redo the new 'i'th dive */
- i--;
- add_single_dive(i, merged);
- delete_single_dive(i+1);
- delete_single_dive(i+1);
- }
- /* make sure no dives are still marked as downloaded */
- for (i = 1; i < dive_table.nr; i++)
- dive_table.dives[i]->downloaded = FALSE;
-
- if (is_imported) {
- /* If there are dives in the table, are they numbered */
- if (!last || last->number)
- try_to_renumber(last, preexisting);
-
- /* did we add dives to the dive table? */
- if (preexisting != dive_table.nr)
- mark_divelist_changed(TRUE);
- }
- dive_list_update_dives();
-}
-
static void parse_argument(const char *arg)
{
const char *p = arg+1;
@@ -222,7 +100,11 @@ static void parse_argument(const char *arg)
if (strcmp(arg,"--import") == 0) {
/* mark the dives so far as the base,
* everything after is imported */
+#if USE_GTK_UI
report_dives(FALSE, FALSE);
+#else
+ process_dives(FALSE, FALSE);
+#endif
imported = TRUE;
return;
}
@@ -242,6 +124,7 @@ static void parse_argument(const char *arg)
void update_dive(struct dive *new_dive)
{
+#if USE_GTK_UI
static struct dive *buffered_dive;
struct dive *old_dive = buffered_dive;
@@ -252,6 +135,7 @@ void update_dive(struct dive *new_dive)
show_dive_equipment(new_dive, W_IDX_PRIMARY);
show_dive_stats(new_dive);
buffered_dive = new_dive;
+#endif
}
void renumber_dives(int nr)
@@ -261,7 +145,9 @@ void renumber_dives(int nr)
for (i = 0; i < dive_table.nr; i++) {
struct dive *dive = dive_table.dives[i];
dive->number = nr + i;
+#if USE_GTK_UI
flush_divelist(dive);
+#endif
}
mark_divelist_changed(TRUE);
}
@@ -331,7 +217,7 @@ int main(int argc, char **argv)
subsurface_command_line_init(&argc, &argv);
parse_xml_init();
- init_ui(&argc, &argv);
+ init_ui(&argc, &argv); /* the gtk stuff is needed for parsing below */
for (i = 1; i < argc; i++) {
const char *a = argv[i];
@@ -352,7 +238,9 @@ int main(int argc, char **argv)
}
if (error != NULL)
{
+#if USE_GTK_UI
report_error(error);
+#endif
g_error_free(error);
error = NULL;
}
@@ -365,15 +253,20 @@ int main(int argc, char **argv)
sure we remember this as the filename in use */
set_filename(filename, FALSE);
}
+#if USE_GTK_UI
report_dives(imported, FALSE);
if (dive_table.nr == 0)
show_dive_info(NULL);
- run_ui();
- exit_ui();
+#else
+ process_dives(imported, FALSE);
+#endif
parse_xml_exit();
subsurface_command_line_exit(&argc, &argv);
+ init_qt_ui(&argc, &argv); /* qt bit delayed until dives are parsed */
+ run_ui();
+ exit_ui();
#ifdef DEBUGFILE
if (debugfile)
fclose(debugfile);
diff --git a/packaging/windows/mingw-make.sh b/packaging/windows/mingw-make.sh
index 6c2375114..8bb891752 100755
--- a/packaging/windows/mingw-make.sh
+++ b/packaging/windows/mingw-make.sh
@@ -11,7 +11,7 @@
rm packaging/windows/subsurface.nsi
export PATH=/usr/i686-w64-mingw32/sys-root/mingw/bin:$PATH
-make CC=i686-w64-mingw32-gcc \
+make CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ \
PKGCONFIG=i686-w64-mingw32-pkg-config \
PKG_CONFIG_PATH=/usr/i686-w64-mingw32/sys-root/mingw/lib/pkgconfig/ \
CROSS_PATH=/usr/i686-w64-mingw32/sys-root/mingw/ \
diff --git a/packaging/windows/subsurface.res b/packaging/windows/subsurface.res
deleted file mode 100644
index 616cf5ea9..000000000
--- a/packaging/windows/subsurface.res
+++ /dev/null
Binary files differ
diff --git a/planner.c b/planner.c
index 71273b3a1..a707f7552 100644
--- a/planner.c
+++ b/planner.c
@@ -699,9 +699,11 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, c
stopidx--;
}
add_plan_to_notes(diveplan, dive);
+#if USE_GTK_UI
/* now make the dive visible in the dive list */
report_dives(FALSE, FALSE);
show_and_select_dive(dive);
+#endif
error_exit:
free(stoplevels);
free(gaschanges);
diff --git a/pref.h b/pref.h
index 3c6b43c70..db26aee0d 100644
--- a/pref.h
+++ b/pref.h
@@ -1,6 +1,10 @@
#ifndef PREF_H
#define PREF_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct {
gboolean cylinder;
gboolean temperature;
@@ -44,13 +48,13 @@ extern struct preferences prefs, default_prefs;
#define PP_GRAPHS_ENABLED (prefs.pp_graphs.po2 || prefs.pp_graphs.pn2 || prefs.pp_graphs.phe)
extern void subsurface_open_conf(void);
-extern void subsurface_set_conf(char *name, const char *value);
-extern void subsurface_set_conf_bool(char *name, gboolean value);
-extern void subsurface_set_conf_int(char *name, int value);
-extern void subsurface_unset_conf(char *name);
-extern const void *subsurface_get_conf(char *name);
-extern int subsurface_get_conf_bool(char *name);
-extern int subsurface_get_conf_int(char *name);
+extern void subsurface_set_conf(const char *name, const char *value);
+extern void subsurface_set_conf_bool(const char *name, gboolean value);
+extern void subsurface_set_conf_int(const char *name, int value);
+extern void subsurface_unset_conf(const char *name);
+extern const char *subsurface_get_conf(const char *name);
+extern int subsurface_get_conf_bool(const char *name);
+extern int subsurface_get_conf_int(const char *name);
extern void subsurface_flush_conf(void);
extern void subsurface_close_conf(void);
@@ -60,4 +64,8 @@ extern const char *system_default_filename();
extern void load_preferences(void);
extern void save_preferences(void);
+#ifdef __cplusplus
+}
+#endif
+
#endif /* PREF_H */
diff --git a/profile.c b/profile.c
index e40ee5bad..e6ba6ad8d 100644
--- a/profile.c
+++ b/profile.c
@@ -6,9 +6,12 @@
#include "dive.h"
#include "display.h"
+#if USE_GTK_UI
#include "display-gtk.h"
+#endif
#include "divelist.h"
-#include "color.h"
+
+#include "profile.h"
#include "libdivecomputer/parser.h"
#include "libdivecomputer/version.h"
@@ -16,130 +19,13 @@ int selected_dive = -1; /* careful: 0 is a valid value */
char zoomed_plot = 0;
char dc_number = 0;
-static double plot_scale = SCALE_SCREEN;
+
static struct plot_data *last_pi_entry = NULL;
#define cairo_set_line_width_scaled(cr, w) \
cairo_set_line_width((cr), (w) * plot_scale);
-typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t;
-
-struct plot_data {
- unsigned int in_deco:1;
- unsigned int cylinderindex;
- int sec;
- /* pressure[0] is sensor pressure
- * pressure[1] is interpolated pressure */
- int pressure[2];
- int temperature;
- /* Depth info */
- int depth;
- int ceiling;
- int ndl;
- int stoptime;
- int stopdepth;
- int cns;
- int smoothed;
- double po2, pn2, phe;
- double mod, ead, end, eadd;
- velocity_t velocity;
- struct plot_data *min[3];
- struct plot_data *max[3];
- int avg[3];
-};
-
-#define SENSOR_PR 0
-#define INTERPOLATED_PR 1
-#define SENSOR_PRESSURE(_entry) (_entry)->pressure[SENSOR_PR]
-#define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR]
-#define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? : INTERPOLATED_PRESSURE(_entry))
-
-#define SAC_COLORS_START_IDX SAC_1
-#define SAC_COLORS 9
-#define VELOCITY_COLORS_START_IDX VELO_STABLE
-#define VELOCITY_COLORS 5
-
-typedef enum {
- /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
- SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9,
-
- /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */
- VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY,
-
- /* gas colors */
- PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES,
-
- /* Other colors */
- TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW,
- SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP,
- DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND,
- CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP
-} color_indice_t;
-
-typedef struct {
- /* media[0] is screen, media[1] is b/w printer media[2] is color printer */
- struct rgba {
- double r,g,b,a;
- } media[3];
-} color_t;
-
-/* [color indice] = {{screen color, b/w printer color, color printer}} printer & screen colours could be different */
-static const color_t profile_color[] = {
- [SAC_1] = {{FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1}},
- [SAC_2] = {{APPLE1, BLACK1_LOW_TRANS, APPLE1}},
- [SAC_3] = {{ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1}},
- [SAC_4] = {{ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2}},
- [SAC_5] = {{EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1}},
- [SAC_6] = {{HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1}},
- [SAC_7] = {{TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1}},
- [SAC_8] = {{CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1}},
- [SAC_9] = {{REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1}},
-
- [VELO_STABLE] = {{CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1}},
- [VELO_SLOW] = {{LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1}},
- [VELO_MODERATE] = {{RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1}},
- [VELO_FAST] = {{PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1}},
- [VELO_CRAZY] = {{RED1, BLACK1_LOW_TRANS, RED1}},
-
- [PO2] = {{APPLE1, BLACK1_LOW_TRANS, APPLE1}},
- [PO2_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}},
- [PN2] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}},
- [PN2_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}},
- [PHE] = {{PEANUT, BLACK1_LOW_TRANS, PEANUT}},
- [PHE_ALERT] = {{RED1, BLACK1_LOW_TRANS, RED1}},
- [PP_LINES] = {{BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS}},
-
- [TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS}},
- [ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS}},
- [ALERT_FG] = {{BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS}},
- [EVENTS] = {{REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1}},
- [SAMPLE_DEEP] = {{PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1}},
- [SAMPLE_SHALLOW] = {{PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1}},
- [SMOOTHED] = {{REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS}},
- [MINUTE] = {{MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS}},
- [TIME_GRID] = {{WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS}},
- [TIME_TEXT] = {{FORESTGREEN1, BLACK1_LOW_TRANS, FORESTGREEN1}},
- [DEPTH_GRID] = {{WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS}},
- [MEAN_DEPTH] = {{REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS}},
- [DEPTH_BOTTOM] = {{GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS}},
- [DEPTH_TOP] = {{MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS}},
- [TEMP_TEXT] = {{GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2}},
- [TEMP_PLOT] = {{ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS}},
- [SAC_DEFAULT] = {{WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1}},
- [BOUNDING_BOX] = {{WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS}},
- [PRESSURE_TEXT] = {{KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1}},
- [BACKGROUND] = {{SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1}},
- [CEILING_SHALLOW] = {{REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS}},
- [CEILING_DEEP] = {{RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS}},
- [CALC_CEILING_SHALLOW] = {{FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS}},
- [CALC_CEILING_DEEP] = {{APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS}},
-
-};
-
-/* Scale to 0,0 -> maxx,maxy */
-#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx)
-#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy)
-#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y)
+#if USE_GTK_UI
/* keep the last used gc around so we can invert the SCALEX calculation in
* order to calculate a time value for an x coordinate */
@@ -155,42 +41,7 @@ int x_abs(double x)
{
return x - last_gc.drawing_area.x;
}
-
-static void move_to(struct graphics_context *gc, double x, double y)
-{
- cairo_move_to(gc->cr, SCALE(gc, x, y));
-}
-
-static void line_to(struct graphics_context *gc, double x, double y)
-{
- cairo_line_to(gc->cr, SCALE(gc, x, y));
-}
-
-static void set_source_rgba(struct graphics_context *gc, color_indice_t c)
-{
- const color_t *col = &profile_color[c];
- struct rgba rgb = col->media[gc->printer];
- double r = rgb.r;
- double g = rgb.g;
- double b = rgb.b;
- double a = rgb.a;
-
- cairo_set_source_rgba(gc->cr, r, g, b, a);
-}
-
-void init_profile_background(struct graphics_context *gc)
-{
- set_source_rgba(gc, BACKGROUND);
-}
-
-static void pattern_add_color_stop_rgba(struct graphics_context *gc, cairo_pattern_t *pat, double o, color_indice_t c)
-{
- const color_t *col = &profile_color[c];
- struct rgba rgb = col->media[gc->printer];
- cairo_pattern_add_color_stop_rgba(pat, o, rgb.r, rgb.g, rgb.b, rgb.a);
-}
-
-#define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
+#endif /* USE_GTK_UI */
/* debugging tool - not normally used */
static void dump_pi (struct plot_info *pi)
@@ -215,6 +66,8 @@ static void dump_pi (struct plot_info *pi)
printf(" }\n");
}
+#define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
+
/*
* When showing dive profiles, we scale things to the
* current dive. However, we don't scale past less than
@@ -223,7 +76,7 @@ static void dump_pi (struct plot_info *pi)
* We also need to add 180 seconds at the end so the min/max
* plots correctly
*/
-static int get_maxtime(struct plot_info *pi)
+int get_maxtime(struct plot_info *pi)
{
int seconds = pi->maxtime;
if (zoomed_plot) {
@@ -246,7 +99,7 @@ static int get_maxtime(struct plot_info *pi)
/* get the maximum depth to which we want to plot
* take into account the additional verical space needed to plot
* partial pressure graphs */
-static int get_maxdepth(struct plot_info *pi)
+int get_maxdepth(struct plot_info *pi)
{
unsigned mm = pi->maxdepth;
int md;
@@ -262,61 +115,10 @@ static int get_maxdepth(struct plot_info *pi)
return md;
}
-typedef struct {
- double size;
- color_indice_t color;
- double hpos, vpos;
-} text_render_options_t;
-
-#define RIGHT (-1.0)
-#define CENTER (-0.5)
-#define LEFT (0.0)
-
-#define TOP (1)
-#define MIDDLE (0)
-#define BOTTOM (-1)
-
-static void plot_text(struct graphics_context *gc, const text_render_options_t *tro,
- double x, double y, const char *fmt, ...)
-{
- cairo_t *cr = gc->cr;
- cairo_font_extents_t fe;
- cairo_text_extents_t extents;
- double dx, dy;
- char buffer[256];
- va_list args;
-
- va_start(args, fmt);
- vsnprintf(buffer, sizeof(buffer), fmt, args);
- va_end(args);
-
- cairo_set_font_size(cr, tro->size * plot_scale);
- cairo_font_extents(cr, &fe);
- cairo_text_extents(cr, buffer, &extents);
- dx = tro->hpos * (extents.width + extents.x_bearing);
- dy = tro->vpos * (extents.height + fe.descent);
- move_to(gc, x, y);
- cairo_rel_move_to(cr, dx, dy);
-
- cairo_text_path(cr, buffer);
- set_source_rgba(gc, TEXT_BACKGROUND);
- cairo_stroke(cr);
-
- move_to(gc, x, y);
- cairo_rel_move_to(cr, dx, dy);
-
- set_source_rgba(gc, tro->color);
- cairo_show_text(cr, buffer);
-}
-
/* collect all event names and whether we display them */
-struct ev_select {
- char *ev_name;
- gboolean plot_ev;
-};
-static struct ev_select *ev_namelist;
-static int evn_allocated;
-static int evn_used;
+struct ev_select *ev_namelist;
+int evn_allocated;
+int evn_used;
int evn_foreach(void (*callback)(const char *, int *, void *), void *data)
{
@@ -357,145 +159,50 @@ void remember_event(const char *eventname)
evn_used++;
}
-static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event)
-{
- int i, depth = 0;
- int x,y;
- char buffer[256];
-
- /* is plotting this event disabled? */
- if (event->name) {
- for (i = 0; i < evn_used; i++) {
- if (! strcmp(event->name, ev_namelist[i].ev_name)) {
- if (ev_namelist[i].plot_ev)
- break;
- else
- return;
- }
- }
- }
- if (event->time.seconds < 30 && !strcmp(event->name, "gaschange"))
- /* a gas change in the first 30 seconds is the way of some dive computers
- * to tell us the gas that is used; let's not plot a marker for that */
- return;
-
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *data = pi->entry + i;
- if (event->time.seconds < data->sec)
- break;
- depth = data->depth;
- }
- /* draw a little triangular marker and attach tooltip */
- x = SCALEX(gc, event->time.seconds);
- y = SCALEY(gc, depth);
- set_source_rgba(gc, ALERT_BG);
- cairo_move_to(gc->cr, x-6, y+12);
- cairo_line_to(gc->cr, x+6, y+12);
- cairo_line_to(gc->cr, x , y);
- cairo_line_to(gc->cr, x-6, y+12);
- cairo_stroke_preserve(gc->cr);
- cairo_fill(gc->cr);
- set_source_rgba(gc, ALERT_FG);
- cairo_move_to(gc->cr, x, y+3);
- cairo_line_to(gc->cr, x, y+7);
- cairo_move_to(gc->cr, x, y+10);
- cairo_line_to(gc->cr, x, y+10);
- cairo_stroke(gc->cr);
- /* we display the event on screen - so translate */
- if (event->value) {
- if (event->name && !strcmp(event->name, "gaschange")) {
- unsigned int he = event->value >> 16;
- unsigned int o2 = event->value & 0xffff;
- if (he) {
- snprintf(buffer, sizeof(buffer), "%s:%u/%u",
- _(event->name), o2, he);
- } else {
- if (o2 == 21)
- snprintf(buffer, sizeof(buffer), "%s:%s",
- _(event->name), _("air"));
- else
- snprintf(buffer, sizeof(buffer), "%s:%u%% %s",
- _(event->name), o2, "O" UTF8_SUBSCRIPT_2);
- }
- } else if (event->name && !strcmp(event->name, "SP change")) {
- snprintf(buffer, sizeof(buffer), "%s:%0.1f", _(event->name), (double) event->value / 1000);
- } else {
- snprintf(buffer, sizeof(buffer), "%s:%d", _(event->name), event->value);
- }
- } else if (event->name && !strcmp(event->name, "SP change")) {
- snprintf(buffer, sizeof(buffer), _("Bailing out to OC"));
- } else {
- snprintf(buffer, sizeof(buffer), "%s%s", _(event->name),
- event->flags == SAMPLE_FLAGS_BEGIN ? C_("Starts with space!"," begin") :
- event->flags == SAMPLE_FLAGS_END ? C_("Starts with space!", " end") : "");
- }
- attach_tooltip(x-6, y, 12, 12, buffer, event);
-}
-
-static void plot_events(struct graphics_context *gc, struct plot_info *pi, struct divecomputer *dc)
+int setup_temperature_limits(struct graphics_context *gc)
{
- struct event *event = dc->events;
-
- if (gc->printer)
- return;
+ int maxtime, mintemp, maxtemp, delta;
- while (event) {
- plot_one_event(gc, pi, event);
- event = event->next;
- }
-}
+ struct plot_info *pi = &gc->pi;
+ /* Get plot scaling limits */
+ maxtime = get_maxtime(pi);
+ mintemp = pi->mintemp;
+ maxtemp = pi->maxtemp;
-static void render_depth_sample(struct graphics_context *gc, struct plot_data *entry, const text_render_options_t *tro)
-{
- int sec = entry->sec, decimals;
- double d;
+ gc->leftx = 0; gc->rightx = maxtime;
+ /* Show temperatures in roughly the lower third, but make sure the scale
+ is at least somewhat reasonable */
+ delta = maxtemp - mintemp;
+ if (delta < 3000) /* less than 3K in fluctuation */
+ delta = 3000;
+ gc->topy = maxtemp + delta*2;
- d = get_depth_units(entry->depth, &decimals, NULL);
+ if (PP_GRAPHS_ENABLED)
+ gc->bottomy = mintemp - delta * 2;
+ else
+ gc->bottomy = mintemp - delta / 3;
- plot_text(gc, tro, sec, entry->depth, "%.*f", decimals, d);
+ pi->endtempcoord = SCALEY(gc, pi->mintemp);
+ return maxtemp && maxtemp >= mintemp;
}
-static void plot_text_samples(struct graphics_context *gc, struct plot_info *pi)
+void setup_pp_limits(struct graphics_context *gc)
{
- static const text_render_options_t deep = {14, SAMPLE_DEEP, CENTER, TOP};
- static const text_render_options_t shallow = {14, SAMPLE_SHALLOW, CENTER, BOTTOM};
- int i;
- int last = -1;
-
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
-
- if (entry->depth < 2000)
- continue;
-
- if ((entry == entry->max[2]) && entry->depth != last) {
- render_depth_sample(gc, entry, &deep);
- last = entry->depth;
- }
+ int maxdepth;
- if ((entry == entry->min[2]) && entry->depth != last) {
- render_depth_sample(gc, entry, &shallow);
- last = entry->depth;
- }
+ gc->leftx = 0;
+ gc->rightx = get_maxtime(&gc->pi);
- if (entry->depth != last)
- last = -1;
- }
+ /* the maxdepth already includes extra vertical space - and if
+ * we use 1.5 times the corresponding pressure as maximum partial
+ * pressure the graph seems to look fine*/
+ maxdepth = get_maxdepth(&gc->pi);
+ gc->topy = 1.5 * (maxdepth + 10000) / 10000.0 * SURFACE_PRESSURE / 1000;
+ gc->bottomy = -gc->topy / 20;
}
-static void plot_depth_text(struct graphics_context *gc, struct plot_info *pi)
-{
- int maxtime, maxdepth;
-
- /* Get plot scaling limits */
- maxtime = get_maxtime(pi);
- maxdepth = get_maxdepth(pi);
-
- gc->leftx = 0; gc->rightx = maxtime;
- gc->topy = 0; gc->bottomy = maxdepth;
- plot_text_samples(gc, pi);
-}
+#if 0
static void plot_smoothed_profile(struct graphics_context *gc, struct plot_info *pi)
{
@@ -540,496 +247,30 @@ static void plot_minmax_profile(struct graphics_context *gc, struct plot_info *p
plot_minmax_profile_minute(gc, pi, 0);
}
-static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi)
-{
- int i, maxdepth, marker;
- static const text_render_options_t tro = {DEPTH_TEXT_SIZE, SAMPLE_DEEP, RIGHT, MIDDLE};
-
- /* Depth markers: every 30 ft or 10 m*/
- maxdepth = get_maxdepth(pi);
- gc->topy = 0; gc->bottomy = maxdepth;
+#endif /* USE_GTK_UI */
- switch (prefs.units.length) {
- case METERS: marker = 10000; break;
- case FEET: marker = 9144; break; /* 30 ft */
- }
- set_source_rgba(gc, DEPTH_GRID);
- /* don't write depth labels all the way to the bottom as
- * there may be other graphs below the depth plot (like
- * partial pressure graphs) where this would look out
- * of place - so we only make sure that we print the next
- * marker below the actual maxdepth of the dive */
- for (i = marker; i <= pi->maxdepth + marker; i += marker) {
- double d = get_depth_units(i, NULL, NULL);
- plot_text(gc, &tro, -0.002, i, "%.0f", d);
- }
-}
-
-static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi)
+int get_cylinder_pressure_range(struct graphics_context *gc)
{
- int maxdepth;
-
gc->leftx = 0;
- gc->rightx = get_maxtime(pi);
-
- /* the maxdepth already includes extra vertical space - and if
- * we use 1.5 times the corresponding pressure as maximum partial
- * pressure the graph seems to look fine*/
- maxdepth = get_maxdepth(pi);
- gc->topy = 1.5 * (maxdepth + 10000) / 10000.0 * SURFACE_PRESSURE / 1000;
- gc->bottomy = -gc->topy / 20;
-}
-
-static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi)
-{
- double pp, dpp, m;
- int hpos;
- static const text_render_options_t tro = {PP_TEXT_SIZE, PP_LINES, LEFT, MIDDLE};
-
- setup_pp_limits(gc, pi);
- pp = floor(pi->maxpp * 10.0) / 10.0 + 0.2;
- dpp = pp > 4 ? 1.0 : 0.5;
- hpos = pi->entry[pi->nr - 1].sec;
- set_source_rgba(gc, PP_LINES);
- for (m = 0.0; m <= pp; m += dpp) {
- move_to(gc, 0, m);
- line_to(gc, hpos, m);
- cairo_stroke(gc->cr);
- plot_text(gc, &tro, hpos + 30, m, "%.1f", m);
- }
-}
-
-static void plot_pp_gas_profile(struct graphics_context *gc, struct plot_info *pi)
-{
- int i;
- struct plot_data *entry;
-
- setup_pp_limits(gc, pi);
-
- if (prefs.pp_graphs.pn2) {
- set_source_rgba(gc, PN2);
- entry = pi->entry;
- move_to(gc, entry->sec, entry->pn2);
- for (i = 1; i < pi->nr; i++) {
- entry++;
- if (entry->pn2 < prefs.pp_graphs.pn2_threshold)
- line_to(gc, entry->sec, entry->pn2);
- else
- move_to(gc, entry->sec, entry->pn2);
- }
- cairo_stroke(gc->cr);
-
- set_source_rgba(gc, PN2_ALERT);
- entry = pi->entry;
- move_to(gc, entry->sec, entry->pn2);
- for (i = 1; i < pi->nr; i++) {
- entry++;
- if (entry->pn2 >= prefs.pp_graphs.pn2_threshold)
- line_to(gc, entry->sec, entry->pn2);
- else
- move_to(gc, entry->sec, entry->pn2);
- }
- cairo_stroke(gc->cr);
- }
- if (prefs.pp_graphs.phe) {
- set_source_rgba(gc, PHE);
- entry = pi->entry;
- move_to(gc, entry->sec, entry->phe);
- for (i = 1; i < pi->nr; i++) {
- entry++;
- if (entry->phe < prefs.pp_graphs.phe_threshold)
- line_to(gc, entry->sec, entry->phe);
- else
- move_to(gc, entry->sec, entry->phe);
- }
- cairo_stroke(gc->cr);
-
- set_source_rgba(gc, PHE_ALERT);
- entry = pi->entry;
- move_to(gc, entry->sec, entry->phe);
- for (i = 1; i < pi->nr; i++) {
- entry++;
- if (entry->phe >= prefs.pp_graphs.phe_threshold)
- line_to(gc, entry->sec, entry->phe);
- else
- move_to(gc, entry->sec, entry->phe);
- }
- cairo_stroke(gc->cr);
- }
- if (prefs.pp_graphs.po2) {
- set_source_rgba(gc, PO2);
- entry = pi->entry;
- move_to(gc, entry->sec, entry->po2);
- for (i = 1; i < pi->nr; i++) {
- entry++;
- if (entry->po2 < prefs.pp_graphs.po2_threshold)
- line_to(gc, entry->sec, entry->po2);
- else
- move_to(gc, entry->sec, entry->po2);
- }
- cairo_stroke(gc->cr);
-
- set_source_rgba(gc, PO2_ALERT);
- entry = pi->entry;
- move_to(gc, entry->sec, entry->po2);
- for (i = 1; i < pi->nr; i++) {
- entry++;
- if (entry->po2 >= prefs.pp_graphs.po2_threshold)
- line_to(gc, entry->sec, entry->po2);
- else
- move_to(gc, entry->sec, entry->po2);
- }
- cairo_stroke(gc->cr);
- }
-}
-
-static void plot_depth_profile(struct graphics_context *gc, struct plot_info *pi)
-{
- int i, incr;
- cairo_t *cr = gc->cr;
- int sec, depth;
- struct plot_data *entry;
- int maxtime, maxdepth, marker, maxline;
- int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 };
-
- /* Get plot scaling limits */
- maxtime = get_maxtime(pi);
- maxdepth = get_maxdepth(pi);
-
- gc->maxtime = maxtime;
-
- /* Time markers: at most every 10 seconds, but no more than 12 markers.
- * We start out with 10 seconds and increment up to 30 minutes,
- * depending on the dive time.
- * This allows for 6h dives - enough (I hope) for even the craziest
- * divers - but just in case, for those 8h depth-record-breaking dives,
- * we double the interval if this still doesn't get us to 12 or fewer
- * time markers */
- i = 0;
- while (maxtime / increments[i] > 12 && i < 7)
- i++;
- incr = increments[i];
- while (maxtime / incr > 12)
- incr *= 2;
-
- gc->leftx = 0; gc->rightx = maxtime;
- gc->topy = 0; gc->bottomy = 1.0;
-
- last_gc = *gc;
-
- set_source_rgba(gc, TIME_GRID);
- cairo_set_line_width_scaled(gc->cr, 2);
-
- for (i = incr; i < maxtime; i += incr) {
- move_to(gc, i, 0);
- line_to(gc, i, 1);
- }
- cairo_stroke(cr);
-
- /* now the text on the time markers */
- text_render_options_t tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP};
- if (maxtime < 600) {
- /* Be a bit more verbose with shorter dives */
- for (i = incr; i < maxtime; i += incr)
- plot_text(gc, &tro, i, 1, "%02d:%02d", i/60, i%60);
- } else {
- /* Only render the time on every second marker for normal dives */
- for (i = incr; i < maxtime; i += 2 * incr)
- plot_text(gc, &tro, i, 1, "%d", i/60);
- }
- /* Depth markers: every 30 ft or 10 m*/
- gc->leftx = 0; gc->rightx = 1.0;
- gc->topy = 0; gc->bottomy = maxdepth;
- switch (prefs.units.length) {
- case METERS: marker = 10000; break;
- case FEET: marker = 9144; break; /* 30 ft */
- }
- maxline = MAX(pi->maxdepth + marker, maxdepth * 2 / 3);
- set_source_rgba(gc, DEPTH_GRID);
- for (i = marker; i < maxline; i += marker) {
- move_to(gc, 0, i);
- line_to(gc, 1, i);
- }
- cairo_stroke(cr);
-
- gc->leftx = 0; gc->rightx = maxtime;
-
- /* Show mean depth */
- if (! gc->printer) {
- set_source_rgba(gc, MEAN_DEPTH);
- move_to(gc, 0, pi->meandepth);
- line_to(gc, pi->entry[pi->nr - 1].sec, pi->meandepth);
- cairo_stroke(cr);
- }
-
- /*
- * These are good for debugging text placement etc,
- * but not for actual display..
- */
- if (0) {
- plot_smoothed_profile(gc, pi);
- plot_minmax_profile(gc, pi);
- }
-
- /* Do the depth profile for the neat fill */
- gc->topy = 0; gc->bottomy = maxdepth;
-
- cairo_pattern_t *pat;
- pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale);
- pattern_add_color_stop_rgba (gc, pat, 1, DEPTH_BOTTOM);
- pattern_add_color_stop_rgba (gc, pat, 0, DEPTH_TOP);
-
- cairo_set_source(gc->cr, pat);
- cairo_pattern_destroy(pat);
- cairo_set_line_width_scaled(gc->cr, 2);
-
- entry = pi->entry;
- move_to(gc, 0, 0);
- for (i = 0; i < pi->nr; i++, entry++)
- line_to(gc, entry->sec, entry->depth);
-
- /* Show any ceiling we may have encountered */
- for (i = pi->nr - 1; i >= 0; i--, entry--) {
- if (entry->ndl) {
- /* non-zero NDL implies this is a safety stop, no ceiling */
- line_to(gc, entry->sec, 0);
- } else if (entry->stopdepth < entry->depth) {
- line_to(gc, entry->sec, entry->stopdepth);
- } else {
- line_to(gc, entry->sec, entry->depth);
- }
- }
- cairo_close_path(gc->cr);
- cairo_fill(gc->cr);
-
- /* if the user wants the deco ceiling more visible, do that here (this
- * basically draws over the background that we had allowed to shine
- * through so far) */
- if (prefs.profile_red_ceiling) {
- pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale);
- pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW);
- pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP);
- cairo_set_source(gc->cr, pat);
- cairo_pattern_destroy(pat);
- entry = pi->entry;
- move_to(gc, 0, 0);
- for (i = 0; i < pi->nr; i++, entry++) {
- if (entry->ndl == 0 && entry->stopdepth) {
- if (entry->ndl == 0 && entry->stopdepth < entry->depth) {
- line_to(gc, entry->sec, entry->stopdepth);
- } else {
- line_to(gc, entry->sec, entry->depth);
- }
- } else {
- line_to(gc, entry->sec, 0);
- }
- }
- cairo_close_path(gc->cr);
- cairo_fill(gc->cr);
- }
- /* finally, plot the calculated ceiling over all this */
- if (prefs.profile_calc_ceiling) {
- pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale);
- pattern_add_color_stop_rgba (gc, pat, 0, CALC_CEILING_SHALLOW);
- pattern_add_color_stop_rgba (gc, pat, 1, CALC_CEILING_DEEP);
- cairo_set_source(gc->cr, pat);
- cairo_pattern_destroy(pat);
- entry = pi->entry;
- move_to(gc, 0, 0);
- for (i = 0; i < pi->nr; i++, entry++) {
- if (entry->ceiling)
- line_to(gc, entry->sec, entry->ceiling);
- else
- line_to(gc, entry->sec, 0);
- }
- line_to(gc, (entry-1)->sec, 0); /* make sure we end at 0 */
- cairo_close_path(gc->cr);
- cairo_fill(gc->cr);
- }
- /* next show where we have been bad and crossed the dc's ceiling */
- pat = cairo_pattern_create_linear (0.0, 0.0, 0.0, 256.0 * plot_scale);
- pattern_add_color_stop_rgba (gc, pat, 0, CEILING_SHALLOW);
- pattern_add_color_stop_rgba (gc, pat, 1, CEILING_DEEP);
- cairo_set_source(gc->cr, pat);
- cairo_pattern_destroy(pat);
- entry = pi->entry;
- move_to(gc, 0, 0);
- for (i = 0; i < pi->nr; i++, entry++)
- line_to(gc, entry->sec, entry->depth);
-
- for (i = pi->nr - 1; i >= 0; i--, entry--) {
- if (entry->ndl == 0 && entry->stopdepth > entry->depth) {
- line_to(gc, entry->sec, entry->stopdepth);
- } else {
- line_to(gc, entry->sec, entry->depth);
- }
- }
- cairo_close_path(gc->cr);
- cairo_fill(gc->cr);
-
- /* Now do it again for the velocity colors */
- entry = pi->entry;
- for (i = 1; i < pi->nr; i++) {
- entry++;
- sec = entry->sec;
- /* we want to draw the segments in different colors
- * representing the vertical velocity, so we need to
- * chop this into short segments */
- depth = entry->depth;
- set_source_rgba(gc, VELOCITY_COLORS_START_IDX + entry->velocity);
- move_to(gc, entry[-1].sec, entry[-1].depth);
- line_to(gc, sec, depth);
- cairo_stroke(cr);
- }
-}
-
-static int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi)
-{
- int maxtime, mintemp, maxtemp, delta;
-
- /* Get plot scaling limits */
- maxtime = get_maxtime(pi);
- mintemp = pi->mintemp;
- maxtemp = pi->maxtemp;
-
- gc->leftx = 0; gc->rightx = maxtime;
- /* Show temperatures in roughly the lower third, but make sure the scale
- is at least somewhat reasonable */
- delta = maxtemp - mintemp;
- if (delta < 3000) /* less than 3K in fluctuation */
- delta = 3000;
- gc->topy = maxtemp + delta*2;
-
- if (PP_GRAPHS_ENABLED)
- gc->bottomy = mintemp - delta * 2;
- else
- gc->bottomy = mintemp - delta / 3;
-
- pi->endtempcoord = SCALEY(gc, pi->mintemp);
- return maxtemp && maxtemp >= mintemp;
-}
-
-static void plot_single_temp_text(struct graphics_context *gc, int sec, int mkelvin)
-{
- double deg;
- const char *unit;
- static const text_render_options_t tro = {TEMP_TEXT_SIZE, TEMP_TEXT, LEFT, TOP};
-
- deg = get_temp_units(mkelvin, &unit);
-
- plot_text(gc, &tro, sec, mkelvin, "%.2g%s", deg, unit);
-}
-
-static void plot_temperature_text(struct graphics_context *gc, struct plot_info *pi)
-{
- int i;
- int last = -300, sec = 0;
- int last_temperature = 0, last_printed_temp = 0;
-
- if (!setup_temperature_limits(gc, pi))
- return;
-
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry+i;
- int mkelvin = entry->temperature;
- sec = entry->sec;
-
- if (!mkelvin)
- continue;
- last_temperature = mkelvin;
- /* don't print a temperature
- * if it's been less than 5min and less than a 2K change OR
- * if it's been less than 2min OR if the change from the
- * last print is less than .4K (and therefore less than 1F */
- if (((sec < last + 300) && (abs(mkelvin - last_printed_temp) < 2000)) ||
- (sec < last + 120) ||
- (abs(mkelvin - last_printed_temp) < 400))
- continue;
- last = sec;
- plot_single_temp_text(gc,sec,mkelvin);
- last_printed_temp = mkelvin;
- }
- /* it would be nice to print the end temperature, if it's
- * different or if the last temperature print has been more
- * than a quarter of the dive back */
- if ((abs(last_temperature - last_printed_temp) > 500) ||
- ((double)last / (double)sec < 0.75))
- plot_single_temp_text(gc, sec, last_temperature);
-}
-
-static void plot_temperature_profile(struct graphics_context *gc, struct plot_info *pi)
-{
- int i;
- cairo_t *cr = gc->cr;
- int last = 0;
-
- if (!setup_temperature_limits(gc, pi))
- return;
-
- cairo_set_line_width_scaled(gc->cr, 2);
- set_source_rgba(gc, TEMP_PLOT);
- for (i = 0; i < pi->nr; i++) {
- struct plot_data *entry = pi->entry + i;
- int mkelvin = entry->temperature;
- int sec = entry->sec;
- if (!mkelvin) {
- if (!last)
- continue;
- mkelvin = last;
- }
- if (last)
- line_to(gc, sec, mkelvin);
- else
- move_to(gc, sec, mkelvin);
- last = mkelvin;
- }
- cairo_stroke(cr);
-}
-
-/* gets both the actual start and end pressure as well as the scaling factors */
-static int get_cylinder_pressure_range(struct graphics_context *gc, struct plot_info *pi)
-{
- gc->leftx = 0;
- gc->rightx = get_maxtime(pi);
+ gc->rightx = get_maxtime(&gc->pi);
if (PP_GRAPHS_ENABLED)
- gc->bottomy = -pi->maxpressure * 0.75;
+ gc->bottomy = -gc->pi.maxpressure * 0.75;
else
gc->bottomy = 0;
- gc->topy = pi->maxpressure * 1.5;
- if (!pi->maxpressure)
+ gc->topy = gc->pi.maxpressure * 1.5;
+ if (!gc->pi.maxpressure)
return FALSE;
- while (pi->endtempcoord <= SCALEY(gc, pi->minpressure - (gc->topy) * 0.1))
- gc->bottomy -= gc->topy * 0.1;
+ while (gc->pi.endtempcoord <= SCALEY(gc, gc->pi.minpressure - (gc->topy) * 0.1))
+ gc->bottomy -= gc->topy * 0.1 * gc->maxy/abs(gc->maxy);
return TRUE;
}
-/* set the color for the pressure plot according to temporary sac rate
- * as compared to avg_sac; the calculation simply maps the delta between
- * sac and avg_sac to indexes 0 .. (SAC_COLORS - 1) with everything
- * more than 6000 ml/min below avg_sac mapped to 0 */
-
-static void set_sac_color(struct graphics_context *gc, int sac, int avg_sac)
-{
- int sac_index = 0;
- int delta = sac - avg_sac + 7000;
-
- if (!gc->printer) {
- sac_index = delta / 2000;
- if (sac_index < 0)
- sac_index = 0;
- if (sac_index > SAC_COLORS - 1)
- sac_index = SAC_COLORS - 1;
- set_source_rgba(gc, SAC_COLORS_START_IDX + sac_index);
- } else {
- set_source_rgba(gc, SAC_DEFAULT);
- }
-}
/* Get local sac-rate (in ml/min) between entry1 and entry2 */
-static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive)
+int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive)
{
int index = entry1->cylinderindex;
cylinder_t *cyl;
@@ -1059,137 +300,6 @@ static int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, str
return airuse / atm * 60 / duration;
}
-/* calculate the current SAC in ml/min and convert to int */
-#define GET_LOCAL_SAC(_entry1, _entry2, _dive) \
- get_local_sac(_entry1, _entry2, _dive)
-
-#define SAC_WINDOW 45 /* sliding window in seconds for current SAC calculation */
-
-static void plot_cylinder_pressure(struct graphics_context *gc, struct plot_info *pi,
- struct dive *dive, struct divecomputer *dc)
-{
- int i;
- int last = -1, last_index = -1;
- int lift_pen = FALSE;
- int first_plot = TRUE;
- int sac = 0;
- struct plot_data *last_entry = NULL;
-
- if (!get_cylinder_pressure_range(gc, pi))
- return;
-
- cairo_set_line_width_scaled(gc->cr, 2);
-
- for (i = 0; i < pi->nr; i++) {
- int mbar;
- struct plot_data *entry = pi->entry + i;
-
- mbar = GET_PRESSURE(entry);
- if (entry->cylinderindex != last_index) {
- lift_pen = TRUE;
- last_entry = NULL;
- }
- if (!mbar) {
- lift_pen = TRUE;
- continue;
- }
- if (!last_entry) {
- last = i;
- last_entry = entry;
- sac = GET_LOCAL_SAC(entry, pi->entry + i + 1, dive);
- } else {
- int j;
- sac = 0;
- for (j = last; j < i; j++)
- sac += GET_LOCAL_SAC(pi->entry + j, pi->entry + j + 1, dive);
- sac /= (i - last);
- if (entry->sec - last_entry->sec >= SAC_WINDOW) {
- last++;
- last_entry = pi->entry + last;
- }
- }
- set_sac_color(gc, sac, dive->sac);
- if (lift_pen) {
- if (!first_plot && entry->cylinderindex == last_index) {
- /* if we have a previous event from the same tank,
- * draw at least a short line */
- int prev_pr;
- prev_pr = GET_PRESSURE(entry - 1);
- move_to(gc, (entry-1)->sec, prev_pr);
- line_to(gc, entry->sec, mbar);
- } else {
- first_plot = FALSE;
- move_to(gc, entry->sec, mbar);
- }
- lift_pen = FALSE;
- } else {
- line_to(gc, entry->sec, mbar);
- }
- cairo_stroke(gc->cr);
- move_to(gc, entry->sec, mbar);
- last_index = entry->cylinderindex;
- }
-}
-
-static void plot_pressure_value(struct graphics_context *gc, int mbar, int sec,
- int xalign, int yalign)
-{
- int pressure;
- const char *unit;
-
- pressure = get_pressure_units(mbar, &unit);
- text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, xalign, yalign};
- plot_text(gc, &tro, sec, mbar, "%d %s", pressure, unit);
-}
-
-static void plot_cylinder_pressure_text(struct graphics_context *gc, struct plot_info *pi)
-{
- int i;
- int mbar, cyl;
- int seen_cyl[MAX_CYLINDERS] = { FALSE, };
- int last_pressure[MAX_CYLINDERS] = { 0, };
- int last_time[MAX_CYLINDERS] = { 0, };
- struct plot_data *entry;
-
- if (!get_cylinder_pressure_range(gc, pi))
- return;
-
- cyl = -1;
- for (i = 0; i < pi->nr; i++) {
- entry = pi->entry + i;
- mbar = GET_PRESSURE(entry);
-
- if (!mbar)
- continue;
- if (cyl != entry->cylinderindex) {
- cyl = entry->cylinderindex;
- if (!seen_cyl[cyl]) {
- plot_pressure_value(gc, mbar, entry->sec, LEFT, BOTTOM);
- seen_cyl[cyl] = TRUE;
- }
- }
- last_pressure[cyl] = mbar;
- last_time[cyl] = entry->sec;
- }
-
- for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) {
- if (last_time[cyl]) {
- plot_pressure_value(gc, last_pressure[cyl], last_time[cyl], CENTER, TOP);
- }
- }
-}
-
-static void plot_deco_text(struct graphics_context *gc, struct plot_info *pi)
-{
- if (prefs.profile_calc_ceiling) {
- float x = gc->leftx + (gc->rightx - gc->leftx) / 2;
- float y = gc->topy = 1.0;
- text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, CENTER, -0.2};
- gc->bottomy = 0.0;
- plot_text(gc, &tro, x, y, "GF %.0f/%.0f", prefs.gflow * 100, prefs.gfhigh * 100);
- }
-}
-
static void analyze_plot_info_minmax_minute(struct plot_data *entry, struct plot_data *first, struct plot_data *last, int index)
{
struct plot_data *p = entry;
@@ -1259,6 +369,7 @@ static velocity_t velocity(int speed)
return v;
}
+
static struct plot_info *analyze_plot_info(struct plot_info *pi)
{
int i;
@@ -1585,7 +696,7 @@ static void check_gas_change_events(struct dive *dive, struct divecomputer *dc,
set_cylinder_index(pi, i, cylinderindex, ~0u);
}
-static void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc)
+void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc)
{
struct plot_info *pi;
int maxdepth;
@@ -1941,7 +1052,7 @@ static void calculate_deco_information(struct dive *dive, struct divecomputer *d
* sides, so that you can do end-points without having to worry
* about it.
*/
-static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc)
+struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc)
{
struct plot_info *pi;
@@ -1974,19 +1085,6 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
return analyze_plot_info(pi);
}
-static void plot_set_scale(scale_mode_t scale)
-{
- switch (scale) {
- default:
- case SC_SCREEN:
- plot_scale = SCALE_SCREEN;
- break;
- case SC_PRINT:
- plot_scale = SCALE_PRINT;
- break;
- }
-}
-
/* make sure you pass this the FIRST dc - it just walks the list */
static int nr_dcs(struct divecomputer *main)
{
@@ -2015,125 +1113,7 @@ struct divecomputer *select_dc(struct divecomputer *main)
return main;
}
-void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t scale)
-{
- struct plot_info *pi;
- struct divecomputer *dc = &dive->dc;
- cairo_rectangle_t *drawing_area = &gc->drawing_area;
- const char *nickname;
-
- plot_set_scale(scale);
-
- if (!dc->samples) {
- static struct sample fake[4];
- static struct divecomputer fakedc;
- fakedc = dive->dc;
- fakedc.sample = fake;
- fakedc.samples = 4;
-
- /* The dive has no samples, so create a few fake ones. This assumes an
- ascent/descent rate of 9 m/min, which is just below the limit for FAST. */
- int duration = dive->dc.duration.seconds;
- int maxdepth = dive->dc.maxdepth.mm;
- int asc_desc_time = dive->dc.maxdepth.mm*60/9000;
- if (asc_desc_time * 2 >= duration)
- asc_desc_time = duration / 2;
- fake[1].time.seconds = asc_desc_time;
- fake[1].depth.mm = maxdepth;
- fake[2].time.seconds = duration - asc_desc_time;
- fake[2].depth.mm = maxdepth;
- fake[3].time.seconds = duration * 1.00;
- fakedc.events = dc->events;
- dc = &fakedc;
- }
-
- /*
- * Set up limits that are independent of
- * the dive computer
- */
- calculate_max_limits(dive, dc, gc);
-
- /* shift the drawing area so we have a nice margin around it */
- cairo_translate(gc->cr, drawing_area->x, drawing_area->y);
- cairo_set_line_width_scaled(gc->cr, 1);
- cairo_set_line_cap(gc->cr, CAIRO_LINE_CAP_ROUND);
- cairo_set_line_join(gc->cr, CAIRO_LINE_JOIN_ROUND);
-
- /*
- * We don't use "cairo_translate()" because that doesn't
- * scale line width etc. But the actual scaling we need
- * do set up ourselves..
- *
- * Snif. What a pity.
- */
- gc->maxx = (drawing_area->width - 2*drawing_area->x);
- gc->maxy = (drawing_area->height - 2*drawing_area->y);
-
- dc = select_dc(dc);
-
- /* This is per-dive-computer. Right now we just do the first one */
- pi = create_plot_info(dive, dc, gc);
-
- /* Depth profile */
- plot_depth_profile(gc, pi);
- plot_events(gc, pi, dc);
-
- /* Temperature profile */
- plot_temperature_profile(gc, pi);
-
- /* Cylinder pressure plot */
- plot_cylinder_pressure(gc, pi, dive, dc);
-
- /* Text on top of all graphs.. */
- plot_temperature_text(gc, pi);
- plot_depth_text(gc, pi);
- plot_cylinder_pressure_text(gc, pi);
- plot_deco_text(gc, pi);
-
- /* Bounding box last */
- gc->leftx = 0; gc->rightx = 1.0;
- gc->topy = 0; gc->bottomy = 1.0;
-
- set_source_rgba(gc, BOUNDING_BOX);
- cairo_set_line_width_scaled(gc->cr, 1);
- move_to(gc, 0, 0);
- line_to(gc, 0, 1);
- line_to(gc, 1, 1);
- line_to(gc, 1, 0);
- cairo_close_path(gc->cr);
- cairo_stroke(gc->cr);
-
- /* Put the dive computer name in the lower left corner */
- nickname = get_dc_nickname(dc->model, dc->deviceid);
- if (!nickname || *nickname == '\0')
- nickname = dc->model;
- if (nickname) {
- static const text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE};
- plot_text(gc, &computer, 0, 1, "%s", nickname);
- }
-
- if (PP_GRAPHS_ENABLED) {
- plot_pp_gas_profile(gc, pi);
- plot_pp_text(gc, pi);
- }
-
- /* now shift the translation back by half the margin;
- * this way we can draw the vertical scales on both sides */
- cairo_translate(gc->cr, -drawing_area->x / 2.0, 0);
- gc->maxx += drawing_area->x;
- gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0;
- gc->rightx = 1.0 - gc->leftx;
-
- plot_depth_scale(gc, pi);
-
- if (gc->printer) {
- free(pi->entry);
- last_pi_entry = pi->entry = NULL;
- pi->nr = 0;
- }
-}
-
-static void plot_string(struct plot_data *entry, char *buf, size_t bufsize,
+static void plot_string(struct plot_data *entry, char *buf, int bufsize,
int depth, int pressure, int temp, gboolean has_ndl)
{
int pressurevalue, mod, ead, end, eadd;
@@ -2217,7 +1197,7 @@ static void plot_string(struct plot_data *entry, char *buf, size_t bufsize,
free(buf2);
}
-void get_plot_details(struct graphics_context *gc, int time, char *buf, size_t bufsize)
+void get_plot_details(struct graphics_context *gc, int time, char *buf, int bufsize)
{
struct plot_info *pi = &gc->pi;
int pressure = 0, temp = 0;
diff --git a/profile.h b/profile.h
new file mode 100644
index 000000000..2b2c7bff5
--- /dev/null
+++ b/profile.h
@@ -0,0 +1,112 @@
+#ifndef PROFILE_H
+#define PROFILE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "dive.h"
+
+typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t;
+
+struct divecomputer;
+struct graphics_context;
+struct plot_info;
+struct plot_data {
+ unsigned int in_deco:1;
+ int cylinderindex;
+ int sec;
+ /* pressure[0] is sensor pressure
+ * pressure[1] is interpolated pressure */
+ int pressure[2];
+ int temperature;
+ /* Depth info */
+ int depth;
+ int ceiling;
+ int ndl;
+ int stoptime;
+ int stopdepth;
+ int cns;
+ int smoothed;
+ double po2, pn2, phe;
+ double mod, ead, end, eadd;
+ velocity_t velocity;
+ struct plot_data *min[3];
+ struct plot_data *max[3];
+ int avg[3];
+};
+
+void calculate_max_limits(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc);
+struct plot_info *create_plot_info(struct dive *dive, struct divecomputer *dc, struct graphics_context *gc);
+int setup_temperature_limits(struct graphics_context *gc);
+int get_cylinder_pressure_range(struct graphics_context *gc);
+
+struct ev_select {
+ char *ev_name;
+ bool plot_ev;
+};
+
+/*
+ * When showing dive profiles, we scale things to the
+ * current dive. However, we don't scale past less than
+ * 30 minutes or 90 ft, just so that small dives show
+ * up as such unless zoom is enabled.
+ * We also need to add 180 seconds at the end so the min/max
+ * plots correctly
+ */
+int get_maxtime(struct plot_info *pi);
+
+/* get the maximum depth to which we want to plot
+ * take into account the additional verical space needed to plot
+ * partial pressure graphs */
+int get_maxdepth(struct plot_info *pi);
+
+int get_local_sac(struct plot_data *entry1, struct plot_data *entry2, struct dive *dive);
+
+void setup_pp_limits(struct graphics_context *gc);
+
+
+#define ALIGN_LEFT 1
+#define ALIGN_RIGHT 2
+#define INVISIBLE 4
+#define UNSORTABLE 8
+#define EDITABLE 16
+
+#ifndef TEXT_SCALE
+#define TEXT_SCALE 1.0
+#endif
+
+#define DEPTH_TEXT_SIZE (10 * TEXT_SCALE)
+#define PRESSURE_TEXT_SIZE (10 * TEXT_SCALE)
+#define DC_TEXT_SIZE (10.5 * TEXT_SCALE)
+#define PP_TEXT_SIZE (11 * TEXT_SCALE)
+#define TEMP_TEXT_SIZE (12 * TEXT_SCALE)
+
+#define RIGHT (-1.0)
+#define CENTER (-0.5)
+#define LEFT (0.0)
+
+#define TOP (1)
+#define MIDDLE (0)
+#define BOTTOM (-1)
+
+#define SCALEXGC(x) (((x) - gc.leftx) / (gc.rightx - gc.leftx) * gc.maxx)
+#define SCALEYGC(y) (((y) - gc.topy) / (gc.bottomy - gc.topy) * gc.maxy)
+#define SCALEGC(x,y) SCALEXGC(x),SCALEYGC(y)
+
+#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx)
+#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy)
+#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y)
+
+#define SENSOR_PR 0
+#define INTERPOLATED_PR 1
+#define SENSOR_PRESSURE(_entry) (_entry)->pressure[SENSOR_PR]
+#define INTERPOLATED_PRESSURE(_entry) (_entry)->pressure[INTERPOLATED_PR]
+#define GET_PRESSURE(_entry) (SENSOR_PRESSURE(_entry) ? SENSOR_PRESSURE(_entry) : INTERPOLATED_PRESSURE(_entry))
+
+#define SAC_WINDOW 45 /* sliding window in seconds for current SAC calculation */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/qt-gui.cpp b/qt-gui.cpp
new file mode 100644
index 000000000..e3a8ad5aa
--- /dev/null
+++ b/qt-gui.cpp
@@ -0,0 +1,210 @@
+/* qt-gui.cpp */
+/* Qt UI implementation */
+#include <libintl.h>
+#include <glib/gi18n.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ctype.h>
+
+#include "dive.h"
+#include "divelist.h"
+#include "display.h"
+#include "uemis.h"
+#include "device.h"
+#include "webservice.h"
+#include "version.h"
+#include "libdivecomputer.h"
+#include "qt-ui/mainwindow.h"
+
+#include <QApplication>
+#include <QFileDialog>
+#include <QFileInfo>
+#include <QStringList>
+#include <QTextCodec>
+#include <QTranslator>
+#include <QSettings>
+#include <QDesktopWidget>
+
+class Translator: public QTranslator
+{
+ Q_OBJECT
+
+public:
+ Translator(QObject *parent = 0);
+ ~Translator() {}
+
+ virtual QString translate(const char *context, const char *sourceText,
+ const char *disambiguation = NULL) const;
+};
+
+Translator::Translator(QObject *parent):
+ QTranslator(parent)
+{
+}
+
+QString Translator::translate(const char *context, const char *sourceText,
+ const char *disambiguation) const
+{
+ return gettext(sourceText);
+}
+
+static QApplication *application = NULL;
+
+int error_count;
+const char *existing_filename;
+
+void init_qt_ui(int *argcp, char ***argvp)
+{
+ application->installTranslator(new Translator(application));
+ MainWindow *window = new MainWindow();
+ window->show();
+}
+
+void init_ui(int *argcp, char ***argvp)
+{
+ QVariant v;
+ application = new QApplication(*argcp, *argvp);
+
+#if QT_VERSION < 0x050000
+ // ask QString in Qt 4 to interpret all char* as UTF-8,
+ // like Qt 5 does.
+ // 106 is "UTF-8", this is faster than lookup by name
+ // [http://www.iana.org/assignments/character-sets/character-sets.xml]
+ QTextCodec::setCodecForCStrings(QTextCodec::codecForMib(106));
+#endif
+
+ QSettings settings("hohndel.org","subsurface");
+ settings.beginGroup("GeneralSettings");
+ v = settings.value(QString("default_filename"));
+ if (v.isValid()) {
+ QString name = v.toString();
+ prefs.default_filename = strdup(name.toUtf8());
+ }
+ settings.endGroup();
+
+#if 0
+ subsurface_open_conf();
+
+ load_preferences();
+
+ /* these still need to be handled in QSettings */
+ default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor");
+ default_dive_computer_product = subsurface_get_conf("dive_computer_product");
+ default_dive_computer_device = subsurface_get_conf("dive_computer_device");
+#endif
+ return;
+}
+
+void run_ui(void)
+{
+ application->exec();
+}
+
+void exit_ui(void)
+{
+ delete application;
+#if 0
+ subsurface_close_conf();
+#endif
+ if (existing_filename)
+ free((void *)existing_filename);
+ if (default_dive_computer_device)
+ free((void *)default_dive_computer_device);
+}
+
+void set_filename(const char *filename, gboolean force)
+{
+ if (!force && existing_filename)
+ return;
+ free((void *)existing_filename);
+ if (filename)
+ existing_filename = strdup(filename);
+ else
+ existing_filename = NULL;
+}
+
+const char *get_dc_nickname(const char *model, uint32_t deviceid)
+{
+ struct device_info *known = get_device_info(model, deviceid);
+ if (known) {
+ if (known->nickname && *known->nickname)
+ return known->nickname;
+ else
+ return known->model;
+ }
+ return NULL;
+}
+
+void set_dc_nickname(struct dive *dive)
+{
+ /* needs Qt implementation */
+}
+
+QString get_depth_string(depth_t depth, bool showunit)
+{
+ if (prefs.units.length == units::METERS) {
+ double meters = depth.mm / 1000.0;
+ return QString("%1%2").arg(meters, 0, 'f', meters >= 20.0 ? 0 : 1 ).arg(showunit ? _("m") : "");
+ } else {
+ double feet = mm_to_feet(depth.mm);
+ return QString("%1%2").arg(feet, 0, 'f', 1). arg(showunit ? _("ft") : "");
+ }
+}
+
+QString get_weight_string(weight_t weight, bool showunit)
+{
+ if (prefs.units.weight == units::KG) {
+ double kg = weight.grams / 1000.0;
+ return QString("%1%2").arg(kg, 0, 'f', kg >= 20.0 ? 0 : 1 ).arg(showunit ? _("kg") : "");
+ } else {
+ double lbs = grams_to_lbs(weight.grams);
+ return QString("%1%2").arg(lbs, 0, 'f', lbs >= 40.0 ? 0 : 1 ).arg(showunit ? _("lbs") : "");
+ }
+}
+
+QString get_temperature_string(temperature_t temp, bool showunit)
+{
+ if (prefs.units.temperature == units::CELSIUS) {
+ double celsius = mkelvin_to_C(temp.mkelvin);
+ return QString("%1%2%3").arg(celsius, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE): "")
+ .arg(showunit ? _("C") : "");
+ } else {
+ double fahrenheit = mkelvin_to_F(temp.mkelvin);
+ return QString("%1%2%3").arg(fahrenheit, 0, 'f', 1).arg(showunit ? (UTF8_DEGREE): "")
+ .arg(showunit ? _("F") : "");
+ }
+}
+
+QString get_volume_string(volume_t volume, bool showunit)
+{
+ if (prefs.units.volume == units::LITER) {
+ double liter = volume.mliter / 1000.0;
+ return QString("%1%2").arg(liter, 0, 'f', liter >= 40.0 ? 0 : 1 ).arg(showunit ? _("l") : "");
+ } else {
+ double cuft = ml_to_cuft(volume.mliter);
+ return QString("%1%2").arg(cuft, 0, 'f', cuft >= 20.0 ? 0 : (cuft >= 2.0 ? 1 : 2)).arg(showunit ? _("cuft") : "");
+ }
+}
+
+QString get_pressure_string(pressure_t pressure, bool showunit)
+{
+ if (prefs.units.pressure == units::BAR) {
+ double bar = pressure.mbar / 1000.0;
+ return QString("%1%2").arg(bar, 0, 'f', 1).arg(showunit ? _("bar") : "");
+ } else {
+ double psi = mbar_to_PSI(pressure.mbar);
+ return QString("%1%2").arg(psi, 0, 'f', 0).arg(showunit ? _("psi") : "");
+ }
+}
+
+double get_screen_dpi()
+{
+ QDesktopWidget *mydesk = application->desktop();
+ return mydesk->physicalDpiX();
+}
+#include "qt-gui.moc"
diff --git a/qt-ui/addcylinderdialog.cpp b/qt-ui/addcylinderdialog.cpp
new file mode 100644
index 000000000..5b91617ed
--- /dev/null
+++ b/qt-ui/addcylinderdialog.cpp
@@ -0,0 +1,56 @@
+/*
+ * addcylinderdialog.cpp
+ *
+ * classes for the add cylinder dialog of Subsurface
+ *
+ */
+#include "addcylinderdialog.h"
+#include "ui_addcylinderdialog.h"
+#include <QComboBox>
+#include <QDoubleSpinBox>
+#include "../conversions.h"
+#include "models.h"
+
+AddCylinderDialog::AddCylinderDialog(QWidget *parent) : ui(new Ui::AddCylinderDialog())
+, tankInfoModel(new TankInfoModel())
+{
+ ui->setupUi(this);
+ ui->cylinderType->setModel(tankInfoModel);
+}
+
+void AddCylinderDialog::setCylinder(cylinder_t *cylinder)
+{
+ double volume, pressure;
+ int index;
+
+ currentCylinder = cylinder;
+ convert_volume_pressure(cylinder->type.size.mliter, cylinder->type.workingpressure.mbar, &volume, &pressure);
+
+ index = ui->cylinderType->findText(QString(cylinder->type.description));
+ ui->cylinderType->setCurrentIndex(index);
+ ui->size->setValue(volume);
+ ui->pressure->setValue(pressure);
+
+ ui->o2percent->setValue(cylinder->gasmix.o2.permille / 10.0);
+ ui->hepercent->setValue(cylinder->gasmix.he.permille / 10.0);
+
+ convert_pressure(cylinder->start.mbar, &pressure);
+ ui->start->setValue(pressure);
+
+ convert_pressure(cylinder->end.mbar, &pressure);
+ ui->end->setValue(pressure);
+}
+
+void AddCylinderDialog::updateCylinder()
+{
+ QByteArray description = ui->cylinderType->currentText().toLocal8Bit();
+
+ currentCylinder->type.description = description.data();
+ currentCylinder->type.size.mliter = ui->size->value();
+ currentCylinder->type.workingpressure.mbar = ui->pressure->value();
+ currentCylinder->gasmix.o2.permille = ui->o2percent->value();
+ currentCylinder->gasmix.he.permille = ui->hepercent->value();
+ currentCylinder->start.mbar = ui->start->value();
+ currentCylinder->end.mbar = ui->end->value();
+}
+
diff --git a/qt-ui/addcylinderdialog.h b/qt-ui/addcylinderdialog.h
new file mode 100644
index 000000000..fc68faa72
--- /dev/null
+++ b/qt-ui/addcylinderdialog.h
@@ -0,0 +1,33 @@
+/*
+ * addcylinderdialog.h
+ *
+ * header file for the add cylinder dialog of Subsurface
+ *
+ */
+#ifndef ADDCYLINDERDIALOG_H
+#define ADDCYLINDERDIALOG_H
+
+#include <QDialog>
+#include "../dive.h"
+
+namespace Ui{
+ class AddCylinderDialog;
+}
+
+class TankInfoModel;
+
+class AddCylinderDialog : public QDialog{
+ Q_OBJECT
+public:
+ explicit AddCylinderDialog(QWidget* parent = 0);
+ void setCylinder(cylinder_t *cylinder);
+ void updateCylinder();
+
+private:
+ Ui::AddCylinderDialog *ui;
+ cylinder_t *currentCylinder;
+ TankInfoModel *tankInfoModel;
+};
+
+
+#endif
diff --git a/qt-ui/addcylinderdialog.ui b/qt-ui/addcylinderdialog.ui
new file mode 100644
index 000000000..8bb0428b7
--- /dev/null
+++ b/qt-ui/addcylinderdialog.ui
@@ -0,0 +1,303 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddCylinderDialog</class>
+ <widget class="QDialog" name="AddCylinderDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>408</width>
+ <height>298</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="2">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Cylinder</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Type</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="cylinderType">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Size</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="size">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Pressure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="pressure">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>Pressure</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_2">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Start</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>End</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QSpinBox" name="start">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QSpinBox" name="end">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="checkBox">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Gas Mix</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout_3">
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>O2%</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QDoubleSpinBox" name="o2percent">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>He%</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QDoubleSpinBox" name="hepercent">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="checkBox_2">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AddCylinderDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>269</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>260</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AddCylinderDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>290</x>
+ <y>269</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>260</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>checkBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>start</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>216</x>
+ <y>46</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>280</x>
+ <y>66</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>checkBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>end</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>226</x>
+ <y>48</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>268</x>
+ <y>100</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>checkBox_2</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>o2percent</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>214</x>
+ <y>165</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>260</x>
+ <y>190</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>checkBox_2</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>hepercent</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>228</x>
+ <y>165</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>262</x>
+ <y>216</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/qt-ui/addweightsystemdialog.cpp b/qt-ui/addweightsystemdialog.cpp
new file mode 100644
index 000000000..48a399de9
--- /dev/null
+++ b/qt-ui/addweightsystemdialog.cpp
@@ -0,0 +1,39 @@
+/*
+ * addweightsystemdialog.cpp
+ *
+ * classes for the add weightsystem dialog of Subsurface
+ *
+ */
+#include "addweightsystemdialog.h"
+#include "ui_addweightsystemdialog.h"
+#include <QComboBox>
+#include <QDoubleSpinBox>
+#include "../conversions.h"
+#include "models.h"
+
+AddWeightsystemDialog::AddWeightsystemDialog(QWidget *parent) : ui(new Ui::AddWeightsystemDialog())
+{
+ ui->setupUi(this);
+ currentWeightsystem = NULL;
+}
+
+void AddWeightsystemDialog::setWeightsystem(weightsystem_t *ws)
+{
+ currentWeightsystem = ws;
+
+ ui->description->insert(QString(ws->description));
+ if (get_units()->weight == units::KG)
+ ui->weight->setValue(ws->weight.grams / 1000);
+ else
+ ui->weight->setValue(grams_to_lbs(ws->weight.grams));
+}
+
+void AddWeightsystemDialog::updateWeightsystem()
+{
+ currentWeightsystem->description = strdup(ui->description->text().toUtf8().data());
+ if (get_units()->weight == units::KG)
+ currentWeightsystem->weight.grams = ui->weight->value() * 1000;
+ else
+ currentWeightsystem->weight.grams = lbs_to_grams(ui->weight->value());
+}
+
diff --git a/qt-ui/addweightsystemdialog.h b/qt-ui/addweightsystemdialog.h
new file mode 100644
index 000000000..e99dc08d8
--- /dev/null
+++ b/qt-ui/addweightsystemdialog.h
@@ -0,0 +1,30 @@
+/*
+ * addweightsystemdialog.h
+ *
+ * header file for the add weightsystem dialog of Subsurface
+ *
+ */
+#ifndef ADDWEIGHTSYSTEMDIALOG_H
+#define ADDWEIGHTSYSTEMDIALOG_H
+
+#include <QDialog>
+#include "../dive.h"
+
+namespace Ui{
+ class AddWeightsystemDialog;
+}
+
+class AddWeightsystemDialog : public QDialog{
+ Q_OBJECT
+public:
+ explicit AddWeightsystemDialog(QWidget* parent = 0);
+ void setWeightsystem(weightsystem_t *ws);
+ void updateWeightsystem();
+
+private:
+ Ui::AddWeightsystemDialog *ui;
+ weightsystem_t *currentWeightsystem;
+};
+
+
+#endif
diff --git a/qt-ui/addweightsystemdialog.ui b/qt-ui/addweightsystemdialog.ui
new file mode 100644
index 000000000..11e60d562
--- /dev/null
+++ b/qt-ui/addweightsystemdialog.ui
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddWeightsystemDialog</class>
+ <widget class="QDialog" name="AddWeightsystemDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>408</width>
+ <height>186</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" rowspan="2">
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Weightsystem</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
+ </property>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Description</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Weight</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QSpinBox" name="weight">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="inputMethodHints">
+ <set>Qt::ImhFormattedNumbersOnly</set>
+ </property>
+ <property name="accelerated">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="description"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AddWeightsystemDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>269</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>260</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AddWeightsystemDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>290</x>
+ <y>269</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>260</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp
new file mode 100644
index 000000000..5c8a93ff0
--- /dev/null
+++ b/qt-ui/divelistview.cpp
@@ -0,0 +1,132 @@
+/*
+ * divelistview.cpp
+ *
+ * classes for the divelist of Subsurface
+ *
+ */
+#include "divelistview.h"
+#include "models.h"
+#include "modeldelegates.h"
+#include <QApplication>
+#include <QHeaderView>
+#include <QDebug>
+#include <QKeyEvent>
+#include <QSortFilterProxyModel>
+
+
+DiveListView::DiveListView(QWidget *parent) : QTreeView(parent), mouseClickSelection(false)
+{
+ setUniformRowHeights(true);
+ setItemDelegateForColumn(TreeItemDT::RATING, new StarWidgetsDelegate());
+ QSortFilterProxyModel *model = new QSortFilterProxyModel(this);
+ setModel(model);
+}
+
+void DiveListView::reload()
+{
+ QSortFilterProxyModel *m = qobject_cast<QSortFilterProxyModel*>(model());
+ QAbstractItemModel *oldModel = m->sourceModel();
+ if (oldModel)
+ oldModel->deleteLater();
+ m->setSourceModel(new DiveTripModel(this));
+ sortByColumn(0, Qt::DescendingOrder);
+ QModelIndex firstDiveOrTrip = m->index(0,0);
+ if (firstDiveOrTrip.isValid()){
+ if (m->index(0,0, firstDiveOrTrip).isValid())
+ setCurrentIndex(m->index(0,0, firstDiveOrTrip));
+ else
+ setCurrentIndex(firstDiveOrTrip);
+ }
+}
+
+void DiveListView::setModel(QAbstractItemModel* model)
+{
+ QTreeView::setModel(model);
+}
+
+void DiveListView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command)
+{
+ if (mouseClickSelection)
+ QTreeView::setSelection(rect, command);
+}
+
+void DiveListView::mousePressEvent(QMouseEvent* event)
+{
+ mouseClickSelection = true;
+ QTreeView::mousePressEvent(event);
+}
+
+void DiveListView::mouseReleaseEvent(QMouseEvent* event)
+{
+ mouseClickSelection = false;
+ QTreeView::mouseReleaseEvent(event);
+}
+
+void DiveListView::keyPressEvent(QKeyEvent* event)
+{
+ if(event->modifiers())
+ mouseClickSelection = true;
+ QTreeView::keyPressEvent(event);
+}
+
+void DiveListView::keyReleaseEvent(QKeyEvent* event)
+{
+ mouseClickSelection = false;
+ QWidget::keyReleaseEvent(event);
+}
+
+void DiveListView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
+{
+ if (!current.isValid())
+ return;
+ const QAbstractItemModel *model = current.model();
+ int selectedDive = 0;
+ struct dive *dive = (struct dive*) model->data(current, TreeItemDT::DIVE_ROLE).value<void*>();
+ if (!dive) { // it's a trip! select first child.
+ dive = (struct dive*) model->data(current.child(0,0), TreeItemDT::DIVE_ROLE).value<void*>();
+ selectedDive = get_divenr(dive);
+ }else{
+ selectedDive = get_divenr(dive);
+ }
+ if (selectedDive == selected_dive)
+ return;
+ Q_EMIT currentDiveChanged(selectedDive);
+}
+
+void DiveListView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
+{
+ QList<QModelIndex> parents;
+ Q_FOREACH(const QModelIndex& index, deselected.indexes()) {
+ const QAbstractItemModel *model = index.model();
+ struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value<void*>();
+ if (!dive) { // it's a trip!
+ if (model->rowCount(index)) {
+ expand(index); // leave this - even if it looks like it shouldn't be here. looks like I've found a Qt bug.
+ // the subselection is removed, but the painting is not. this cleans the area.
+ }
+ } else if (!parents.contains(index.parent())) {
+ parents.push_back(index.parent());
+ }
+ }
+
+ Q_FOREACH(const QModelIndex& index, selected.indexes()) {
+ const QAbstractItemModel *model = index.model();
+ struct dive *dive = (struct dive*) model->data(index, TreeItemDT::DIVE_ROLE).value<void*>();
+ if (!dive) { // it's a trip!
+ if (model->rowCount(index)) {
+ QItemSelection selection;
+ selection.select(index.child(0,0), index.child(model->rowCount(index) -1 , 0));
+ selectionModel()->select(selection, QItemSelectionModel::Select | QItemSelectionModel::Rows);
+ selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select | QItemSelectionModel::NoUpdate);
+ if (!isExpanded(index)) {
+ expand(index);
+ }
+ }
+ } else if (!parents.contains(index.parent())) {
+ parents.push_back(index.parent());
+ }
+ }
+
+ Q_FOREACH(const QModelIndex& index, parents)
+ expand(index);
+}
diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h
new file mode 100644
index 000000000..09830b7b5
--- /dev/null
+++ b/qt-ui/divelistview.h
@@ -0,0 +1,40 @@
+/*
+ * divelistview.h
+ *
+ * header file for the dive list of Subsurface
+ *
+ */
+#ifndef DIVELISTVIEW_H
+#define DIVELISTVIEW_H
+
+/*! A view subclass for use with dives
+
+ Note: calling this a list view might be misleading?
+
+
+*/
+
+#include <QTreeView>
+
+class DiveListView : public QTreeView
+{
+ Q_OBJECT
+public:
+ DiveListView(QWidget *parent = 0);
+ void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
+ void currentChanged(const QModelIndex& current, const QModelIndex& previous);
+ void setModel(QAbstractItemModel* model);
+ void mousePressEvent(QMouseEvent* event);
+ void mouseReleaseEvent(QMouseEvent* event);
+ void keyPressEvent(QKeyEvent* event);
+ void keyReleaseEvent(QKeyEvent*);
+ void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command);
+ void reload();
+
+Q_SIGNALS:
+ void currentDiveChanged(int divenr);
+private:
+ bool mouseClickSelection;
+};
+
+#endif // DIVELISTVIEW_H
diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp
new file mode 100644
index 000000000..93d1ab7c4
--- /dev/null
+++ b/qt-ui/globe.cpp
@@ -0,0 +1,113 @@
+#include "globe.h"
+#include "../dive.h"
+
+#include <QDebug>
+
+#include <marble/AbstractFloatItem.h>
+#include <marble/GeoDataPlacemark.h>
+#include <marble/GeoDataDocument.h>
+#include <marble/MarbleModel.h>
+#include <marble/GeoDataTreeModel.h>
+
+#include <QMouseEvent>
+#include <QMessageBox>
+
+GlobeGPS::GlobeGPS(QWidget* parent) : MarbleWidget(parent), loadedDives(0)
+{
+ setMapThemeId("earth/bluemarble/bluemarble.dgml");
+ setProjection( Marble::Spherical );
+
+ setAnimationsEnabled(true);
+ setShowClouds( false );
+ setShowBorders( false );
+ setShowPlaces( false );
+ setShowCrosshairs( false );
+ setShowGrid( false );
+ setShowOverviewMap(false);
+ setShowScaleBar(false);
+
+ Q_FOREACH( AbstractFloatItem * floatItem, floatItems() ){
+ if ( floatItem && floatItem->nameId() == "compass" ) {
+ floatItem->setPosition( QPoint( 10, 10 ) );
+ floatItem->setContentSize( QSize( 50, 50 ) );
+ }
+ }
+}
+
+void GlobeGPS::reload()
+{
+ if (loadedDives){
+ model()->treeModel()->removeDocument(loadedDives);
+ delete loadedDives;
+ }
+ if (editingDiveCoords){
+ editingDiveCoords = 0;
+ }
+
+ loadedDives = new GeoDataDocument;
+
+ int idx = 0;
+ struct dive *dive;
+ for_each_dive(idx, dive) {
+ if (dive_has_gps_location(dive)) {
+ // don't add dive locations twice.
+ if( diveLocations.contains( QString(dive->location)))
+ continue;
+
+ GeoDataPlacemark *place = new GeoDataPlacemark( dive->location );
+ place->setCoordinate(dive->longitude.udeg / 1000000.0,dive->latitude.udeg / 1000000.0 , 0, GeoDataCoordinates::Degree );
+ loadedDives->append( place );
+ }
+ }
+ model()->treeModel()->addDocument( loadedDives );
+}
+
+void GlobeGPS::centerOn(dive* dive)
+{
+ qreal longitude = dive->longitude.udeg / 1000000.0;
+ qreal latitude = dive->latitude.udeg / 1000000.0;
+
+ if (!longitude || !latitude){
+ prepareForGetDiveCoordinates(dive);
+ return;
+ }
+
+ centerOn(longitude,latitude, true);
+}
+
+void GlobeGPS::prepareForGetDiveCoordinates(dive* dive)
+{
+ QMessageBox::warning(parentWidget(),
+ tr("This dive has no location!"),
+ tr("Move the planet to the desired position, then \n double-click to set the new location of this dive."));
+
+ editingDiveCoords = dive;
+}
+
+void GlobeGPS::changeDiveGeoPosition(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
+{
+ // convert to degrees if in radian.
+ if (unit == GeoDataCoordinates::Radian){
+ lon = lon * 180 / M_PI;
+ lat = lat * 180 / M_PI;
+ }
+
+ if (!editingDiveCoords){
+ return;
+ }
+
+ editingDiveCoords->latitude.udeg = (int) lat * 1000000.0;
+ editingDiveCoords->longitude.udeg = (int) lon * 1000000.0;
+ centerOn(lon, lat, true);
+ reload();
+ editingDiveCoords = 0;
+}
+
+void GlobeGPS::mousePressEvent(QMouseEvent* event)
+{
+ qreal lat, lon;
+ if (editingDiveCoords && geoCoordinates(event->pos().x(), event->pos().y(), lon,lat, GeoDataCoordinates::Radian)){
+ changeDiveGeoPosition(lon, lat, GeoDataCoordinates::Radian);
+ }
+}
+
diff --git a/qt-ui/globe.h b/qt-ui/globe.h
new file mode 100644
index 000000000..5f207a502
--- /dev/null
+++ b/qt-ui/globe.h
@@ -0,0 +1,34 @@
+#ifndef GLOBE_H
+#define GLOBE_H
+
+#include <marble/MarbleWidget.h>
+#include <marble/GeoDataCoordinates.h>
+
+#include <QHash>
+
+using namespace Marble;
+struct dive;
+
+class GlobeGPS : public MarbleWidget{
+ Q_OBJECT
+ void prepareForGetDiveCoordinates(struct dive* dive);
+public:
+ using MarbleWidget::centerOn;
+ GlobeGPS(QWidget *parent);
+ void reload();
+ void centerOn(struct dive* dive);
+
+protected:
+ virtual void mousePressEvent(QMouseEvent* event);
+
+private:
+ GeoDataDocument *loadedDives;
+ QStringList diveLocations;
+ struct dive* editingDiveCoords;
+
+public Q_SLOTS:
+ void changeDiveGeoPosition(qreal lon,qreal lat,GeoDataCoordinates::Unit);
+
+};
+
+#endif
diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp
new file mode 100644
index 000000000..70cb3caea
--- /dev/null
+++ b/qt-ui/maintab.cpp
@@ -0,0 +1,204 @@
+/*
+ * maintab.cpp
+ *
+ * classes for the "notebook" area of the main window of Subsurface
+ *
+ */
+#include "maintab.h"
+#include "ui_maintab.h"
+#include "addcylinderdialog.h"
+#include "addweightsystemdialog.h"
+#include "../helpers.h"
+#include "../statistics.h"
+
+#include <QLabel>
+
+MainTab::MainTab(QWidget *parent) : QTabWidget(parent),
+ ui(new Ui::MainTab()),
+ weightModel(new WeightModel()),
+ cylindersModel(new CylindersModel())
+{
+ ui->setupUi(this);
+ ui->cylinders->setModel(cylindersModel);
+ ui->weights->setModel(weightModel);
+
+ /* example of where code is more concise than Qt designer */
+ QList<QObject *> infoTabWidgets = ui->infoTab->children();
+ Q_FOREACH( QObject* obj, infoTabWidgets ){
+ QLabel* label = qobject_cast<QLabel *>(obj);
+ if (label)
+ label->setAlignment(Qt::AlignHCenter);
+ }
+ QList<QObject *> statisticsTabWidgets = ui->statisticsTab->children();
+ Q_FOREACH( QObject* obj, statisticsTabWidgets ){
+ QLabel* label = qobject_cast<QLabel *>(obj);
+ if (label)
+ label->setAlignment(Qt::AlignHCenter);
+ }
+}
+
+void MainTab::clearEquipment()
+{
+}
+
+void MainTab::clearInfo()
+{
+ ui->sacText->clear();
+ ui->otuText->clear();
+ ui->oxygenHeliumText->clear();
+ ui->gasUsedText->clear();
+ ui->dateText->clear();
+ ui->diveTimeText->clear();
+ ui->surfaceIntervalText->clear();
+ ui->maximumDepthText->clear();
+ ui->averageDepthText->clear();
+ ui->visibilityText->clear();
+ ui->waterTemperatureText->clear();
+ ui->airTemperatureText->clear();
+ ui->airPressureText->clear();
+}
+
+void MainTab::clearStats()
+{
+ ui->maximumDepthAllText->clear();
+ ui->minimumDepthAllText->clear();
+ ui->averageDepthAllText->clear();
+ ui->maximumSacAllText->clear();
+ ui->minimumSacAllText->clear();
+ ui->averageSacAllText->clear();
+ ui->divesAllText->clear();
+ ui->maximumTemperatureAllText->clear();
+ ui->minimumTemperatureAllText->clear();
+ ui->averageTemperatureAllText->clear();
+ ui->totalTimeAllText->clear();
+ ui->averageTimeAllText->clear();
+ ui->longestAllText->clear();
+ ui->shortestAllText->clear();
+}
+
+#define UPDATE_TEXT(d, field) \
+ if (!d || !d->field) \
+ ui->field->setText(""); \
+ else \
+ ui->field->setText(d->field)
+
+
+void MainTab::updateDiveInfo(int dive)
+{
+ // So, this is what happens now:
+ // Every tab should be populated from this method,
+ // it will be called whenever a new dive is selected
+ // I'm already populating the 'notes' box
+ // to show how it can be done.
+ // If you are unsure about the name of something,
+ // open the file maintab.ui on the designer
+ // click on the item and check its objectName,
+ // the access is ui->objectName from here on.
+ volume_t sacVal;
+ struct dive *d = get_dive(dive);
+ UPDATE_TEXT(d, notes);
+ UPDATE_TEXT(d, location);
+ UPDATE_TEXT(d, suit);
+ UPDATE_TEXT(d, divemaster);
+ UPDATE_TEXT(d, buddy);
+ /* infoTab */
+ if (d) {
+ ui->rating->setCurrentStars(d->rating);
+ ui->maximumDepthText->setText(get_depth_string(d->maxdepth, TRUE));
+ ui->averageDepthText->setText(get_depth_string(d->meandepth, TRUE));
+ ui->otuText->setText(QString("%1").arg(d->otu));
+ ui->waterTemperatureText->setText(get_temperature_string(d->watertemp, TRUE));
+ ui->airTemperatureText->setText(get_temperature_string(d->airtemp, TRUE));
+ ui->gasUsedText->setText(get_volume_string(get_gas_used(d), TRUE));
+ if ((sacVal.mliter = d->sac) > 0)
+ ui->sacText->setText(get_volume_string(sacVal, TRUE).append("/min"));
+ else
+ ui->sacText->clear();
+ if (d->surface_pressure.mbar)
+ /* this is ALWAYS displayed in mbar */
+ ui->airPressureText->setText(QString("%1mbar").arg(d->surface_pressure.mbar));
+ else
+ ui->airPressureText->clear();
+ } else {
+ ui->rating->setCurrentStars(0);
+ ui->sacText->clear();
+ ui->otuText->clear();
+ ui->oxygenHeliumText->clear();
+ ui->dateText->clear();
+ ui->diveTimeText->clear();
+ ui->surfaceIntervalText->clear();
+ ui->maximumDepthText->clear();
+ ui->averageDepthText->clear();
+ ui->visibilityText->clear();
+ ui->waterTemperatureText->clear();
+ ui->airTemperatureText->clear();
+ ui->gasUsedText->clear();
+ ui->airPressureText->clear();
+ }
+ /* statisticsTab*/
+ /* we can access the stats_selection struct, but how do we ensure the relevant dives are selected
+ * if we don't use the gtk widget to drive this?
+ * Maybe call process_selected_dives? Or re-write to query our Qt list view.
+ */
+// qDebug("max temp %u",stats_selection.max_temp);
+// qDebug("min temp %u",stats_selection.min_temp);
+}
+
+void MainTab::on_addCylinder_clicked()
+{
+ if (cylindersModel->rowCount() >= MAX_CYLINDERS)
+ return;
+
+ AddCylinderDialog dialog(this);
+ cylinder_t *newCylinder = (cylinder_t*) malloc(sizeof(cylinder_t));
+ newCylinder->type.description = "";
+
+ dialog.setCylinder(newCylinder);
+ int result = dialog.exec();
+ if (result == QDialog::Rejected){
+ return;
+ }
+
+ dialog.updateCylinder();
+ cylindersModel->add(newCylinder);
+}
+
+void MainTab::on_editCylinder_clicked()
+{
+}
+
+void MainTab::on_delCylinder_clicked()
+{
+}
+
+void MainTab::on_addWeight_clicked()
+{
+ if (weightModel->rowCount() >= MAX_WEIGHTSYSTEMS)
+ return;
+
+ AddWeightsystemDialog dialog(this);
+ weightsystem_t newWeightsystem;
+ newWeightsystem.description = "";
+ newWeightsystem.weight.grams = 0;
+
+ dialog.setWeightsystem(&newWeightsystem);
+ int result = dialog.exec();
+ if (result == QDialog::Rejected)
+ return;
+
+ dialog.updateWeightsystem();
+ weightModel->add(&newWeightsystem);
+}
+
+void MainTab::on_editWeight_clicked()
+{
+}
+
+void MainTab::on_delWeight_clicked()
+{
+}
+
+void MainTab::reload()
+{
+ cylindersModel->update();
+}
diff --git a/qt-ui/maintab.h b/qt-ui/maintab.h
new file mode 100644
index 000000000..e09781362
--- /dev/null
+++ b/qt-ui/maintab.h
@@ -0,0 +1,46 @@
+/*
+ * maintab.h
+ *
+ * header file for the main tab of Subsurface
+ *
+ */
+#ifndef MAINTAB_H
+#define MAINTAB_H
+
+#include <QTabWidget>
+#include <QDialog>
+
+#include "models.h"
+
+namespace Ui
+{
+ class MainTab;
+}
+
+class MainTab : public QTabWidget
+{
+ Q_OBJECT
+public:
+ MainTab(QWidget *parent);
+ void clearStats();
+ void clearInfo();
+ void clearEquipment();
+ void reload();
+
+public Q_SLOTS:
+ void on_addCylinder_clicked();
+ void on_editCylinder_clicked();
+ void on_delCylinder_clicked();
+ void on_addWeight_clicked();
+ void on_editWeight_clicked();
+ void on_delWeight_clicked();
+
+ void updateDiveInfo(int dive);
+
+private:
+ Ui::MainTab *ui;
+ WeightModel *weightModel;
+ CylindersModel *cylindersModel;
+};
+
+#endif
diff --git a/qt-ui/maintab.ui b/qt-ui/maintab.ui
new file mode 100644
index 000000000..70f88caec
--- /dev/null
+++ b/qt-ui/maintab.ui
@@ -0,0 +1,885 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainTab</class>
+ <widget class="QTabWidget" name="MainTab">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>325</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>TabWidget</string>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="notesTab">
+ <attribute name="title">
+ <string>Dive Notes</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Location</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLineEdit" name="location"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>Divemaster</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Buddy</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLineEdit" name="divemaster"/>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="buddy"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_14">
+ <property name="text">
+ <string>Rating</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="label_19">
+ <property name="text">
+ <string>Suit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="suit"/>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="label_16">
+ <property name="text">
+ <string>Notes</string>
+ </property>
+ </widget>
+ </item>
+ <item row="7" column="0" colspan="2">
+ <widget class="QTextEdit" name="notes"/>
+ </item>
+ <item row="5" column="0">
+ <widget class="StarWidget" name="rating" native="true"/>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="equipmentTab">
+ <attribute name="title">
+ <string>Equipment</string>
+ </attribute>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="0" column="0">
+ <widget class="QSplitter" name="splitter_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Cylinders</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QTableView" name="cylinders"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QPushButton" name="editCylinder">
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addCylinder">
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="delCylinder">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QGroupBox" name="groupBox_3">
+ <property name="title">
+ <string>Weight</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QTableView" name="weights"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QPushButton" name="editWeight">
+ <property name="text">
+ <string>Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="addWeight">
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="delWeight">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="infoTab">
+ <attribute name="title">
+ <string>Dive Info</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="diveInfoUpperGridLayout">
+ <property name="horizontalSpacing">
+ <number>10</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>15</number>
+ </property>
+ <property name="margin">
+ <number>10</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="sacLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>SAC</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="outLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>OTU</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="oxygenHeliumLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>0²/He</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="gasUsedLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Gas Used</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="gasUsedText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="sacText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="otuText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="oxygenHeliumText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="diveInfoLowerGridLayout">
+ <property name="margin">
+ <number>10</number>
+ </property>
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="dateLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Date</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="maximumDepthText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="visibilityText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="visibilityLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Visibility</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="waterTempertureLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Water Temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLabel" name="airTemperatureText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="diveTimeText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="surfaceIntervalText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QLabel" name="airPressureLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Air Pressure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="averageDepthLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ave. Depth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="maximumDepthLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Max. Depth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLabel" name="airTempertatureLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Air Temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="averageDepthText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QLabel" name="airPressureText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="waterTemperatureText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="dateText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="surfaceIntervalLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Interval</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="diveTimeLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Dive Time</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="statisticsTab">
+ <attribute name="title">
+ <string>Stats</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="statsUpperGridLayout">
+ <property name="margin">
+ <number>10</number>
+ </property>
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="maximumDepthAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Max. Depth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="minimumDepthAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Min. Depth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="averageDepthAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ave. Depth</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="maximumDepthAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="minimumDepthAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="averageDepthAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="maximumSacAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Max. SAC</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="minimumSacAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Min. SAC</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="averageSacAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ave. SAC</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="maximumSacAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="minimumSacAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="averageSacAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="statsLowerGridLayout">
+ <property name="margin">
+ <number>10</number>
+ </property>
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="divesAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Dives</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="maximumTemperatureLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Max. Temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="minimumTemperatureAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Min. Temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="averageTemperatureAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ave. Temp.</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="divesAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLabel" name="maximumTemperatureAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QLabel" name="minimumTemperatureAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QLabel" name="averageTemperatureAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="totalTimeAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Total Time</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLabel" name="averageTimeAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ave. Time</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2">
+ <widget class="QLabel" name="longestAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Longest</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3">
+ <widget class="QLabel" name="shortestAllLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Shortest</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="totalTimeAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLabel" name="averageTimeAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="2">
+ <widget class="QLabel" name="longestAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="3">
+ <widget class="QLabel" name="shortestAllText">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>StarWidget</class>
+ <extends>QWidget</extends>
+ <header>starwidget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
new file mode 100644
index 000000000..c5a4e5ce3
--- /dev/null
+++ b/qt-ui/mainwindow.cpp
@@ -0,0 +1,464 @@
+/*
+ * mainwindow.cpp
+ *
+ * classes for the main UI window in Subsurface
+ */
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+
+#include <QVBoxLayout>
+#include <QFileDialog>
+#include <QMessageBox>
+#include <QtDebug>
+#include <QDateTime>
+#include <QSettings>
+#include <QCloseEvent>
+#include <QApplication>
+#include <QFontMetrics>
+
+#include "divelistview.h"
+#include "starwidget.h"
+
+#include "glib.h"
+#include "../dive.h"
+#include "../divelist.h"
+#include "../pref.h"
+#include "modeldelegates.h"
+#include "models.h"
+
+MainWindow::MainWindow() : ui(new Ui::MainWindow())
+{
+ ui->setupUi(this);
+ readSettings();
+ setWindowIcon(QIcon(":subsurface-icon"));
+ connect(ui->ListWidget, SIGNAL(currentDiveChanged(int)), this, SLOT(current_dive_changed(int)));
+ ui->ProfileWidget->setFocusProxy(ui->ListWidget);
+ ui->ListWidget->reload();
+ ui->ListWidget->setFocus();
+ ui->widget->reload();
+}
+
+void MainWindow::current_dive_changed(int divenr)
+{
+ select_dive(divenr);
+ ui->widget->centerOn(get_dive(selected_dive));
+ redrawProfile();
+ ui->InfoWidget->updateDiveInfo(divenr);
+}
+
+void MainWindow::redrawProfile()
+{
+ ui->ProfileWidget->plot(get_dive(selected_dive));
+}
+
+void MainWindow::on_actionNew_triggered()
+{
+ qDebug("actionNew");
+}
+
+void MainWindow::on_actionOpen_triggered()
+{
+ QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::homePath(), filter());
+ if (filename.isEmpty())
+ return;
+
+ // Needed to convert to char*
+ QByteArray fileNamePtr = filename.toLocal8Bit();
+
+ on_actionClose_triggered();
+
+ GError *error = NULL;
+ parse_file(fileNamePtr.data(), &error);
+ set_filename(fileNamePtr.data(), TRUE);
+
+ if (error != NULL) {
+ QMessageBox::warning(this, "Error", error->message);
+ g_error_free(error);
+ error = NULL;
+ }
+ process_dives(FALSE, FALSE);
+
+ ui->InfoWidget->reload();
+ ui->widget->reload();
+ ui->ListWidget->reload();
+ ui->ListWidget->setFocus();
+}
+
+void MainWindow::on_actionSave_triggered()
+{
+ qDebug("actionSave");
+}
+
+void MainWindow::on_actionSaveAs_triggered()
+{
+ qDebug("actionSaveAs");
+}
+void MainWindow::on_actionClose_triggered()
+{
+ if (unsaved_changes() && (askSaveChanges() == FALSE))
+ return;
+
+ /* free the dives and trips */
+ while (dive_table.nr)
+ delete_single_dive(0);
+
+ /* clear the selection and the statistics */
+ selected_dive = -1;
+
+ //WARNING: Port this to Qt.
+ //process_selected_dives();
+
+ ui->InfoWidget->clearStats();
+ ui->InfoWidget->clearInfo();
+ ui->InfoWidget->clearEquipment();
+ ui->ProfileWidget->clear();
+ ui->ListWidget->reload();
+
+ clear_events();
+#if USE_GTK_UI
+ show_dive_stats(NULL);
+
+ /* redraw the screen */
+ //WARNING: Port this to Qt.
+ dive_list_update_dives();
+
+ // WARNING? Port this to Qt.
+ show_dive_info(NULL);
+#endif /* USE_GTK_UI */
+}
+
+void MainWindow::on_actionImport_triggered()
+{
+ qDebug("actionImport");
+}
+
+void MainWindow::on_actionExportUDDF_triggered()
+{
+ qDebug("actionExportUDDF");
+}
+
+void MainWindow::on_actionPrint_triggered()
+{
+ qDebug("actionPrint");
+}
+
+void MainWindow::on_actionPreferences_triggered()
+{
+ qDebug("actionPreferences");
+}
+
+void MainWindow::on_actionQuit_triggered()
+{
+ if (unsaved_changes() && (askSaveChanges() == FALSE))
+ return;
+ writeSettings();
+ QApplication::quit();
+}
+
+void MainWindow::on_actionDownloadDC_triggered()
+{
+ qDebug("actionDownloadDC");
+}
+
+void MainWindow::on_actionDownloadWeb_triggered()
+{
+ qDebug("actionDownloadWeb");}
+
+void MainWindow::on_actionEditDeviceNames_triggered()
+{
+ qDebug("actionEditDeviceNames");}
+
+void MainWindow::on_actionAddDive_triggered()
+{
+ qDebug("actionAddDive");
+}
+
+void MainWindow::on_actionRenumber_triggered()
+{
+ qDebug("actionRenumber");
+}
+
+void MainWindow::on_actionAutoGroup_triggered()
+{
+ qDebug("actionAutoGroup");
+}
+
+void MainWindow::on_actionToggleZoom_triggered()
+{
+ qDebug("actionToggleZoom");
+}
+
+void MainWindow::on_actionYearlyStatistics_triggered()
+{
+ qDebug("actionYearlyStatistics");
+}
+
+void MainWindow::on_actionViewList_triggered()
+{
+ ui->InfoWidget->setVisible(false);
+ ui->ListWidget->setVisible(true);
+ ui->ProfileWidget->setVisible(false);
+}
+
+void MainWindow::on_actionViewProfile_triggered()
+{
+ ui->InfoWidget->setVisible(false);
+ ui->ListWidget->setVisible(false);
+ ui->ProfileWidget->setVisible(true);
+}
+
+void MainWindow::on_actionViewInfo_triggered()
+{
+ ui->InfoWidget->setVisible(true);
+ ui->ListWidget->setVisible(false);
+ ui->ProfileWidget->setVisible(false);
+}
+
+void MainWindow::on_actionViewAll_triggered()
+{
+ ui->InfoWidget->setVisible(true);
+ ui->ListWidget->setVisible(true);
+ ui->ProfileWidget->setVisible(true);
+}
+
+void MainWindow::on_actionPreviousDC_triggered()
+{
+ dc_number--;
+ redrawProfile();
+}
+
+void MainWindow::on_actionNextDC_triggered()
+{
+ dc_number++;
+ redrawProfile();
+}
+
+void MainWindow::on_actionSelectEvents_triggered()
+{
+ qDebug("actionSelectEvents");
+}
+
+void MainWindow::on_actionInputPlan_triggered()
+{
+ qDebug("actionInputPlan");
+}
+
+void MainWindow::on_actionAboutSubsurface_triggered()
+{
+ qDebug("actionAboutSubsurface");
+}
+
+void MainWindow::on_actionUserManual_triggered()
+{
+ qDebug("actionUserManual");
+}
+
+QString MainWindow::filter()
+{
+ QString f;
+ f += "ALL ( *.xml *.XML *.uddf *.udcf *.UDFC *.jlb *.JLB ";
+#ifdef LIBZIP
+ f += "*.sde *.SDE *.dld *.DLD ";
+#endif
+#ifdef SQLITE3
+ f += "*.db";
+#endif
+ f += ");;";
+
+ f += "XML (*.xml *.XML);;";
+ f += "UDDF (*.uddf);;";
+ f += "UDCF (*.udcf *.UDCF);;";
+ f += "JLB (*.jlb *.JLB);;";
+
+#ifdef LIBZIP
+ f += "SDE (*.sde *.SDE);;";
+ f += "DLD (*.dld *.DLD);;";
+#endif
+#ifdef SQLITE3
+ f += "DB (*.db)";
+#endif
+
+ return f;
+}
+
+bool MainWindow::askSaveChanges()
+{
+ QString message = ! existing_filename ? tr("You have unsaved changes\nWould you like to save those before closing the datafile?")
+ : tr("You have unsaved changes to file: %1 \nWould you like to save those before closing the datafile?").arg(existing_filename);
+
+ if (QMessageBox::question(this, tr("Save Changes?"), message) == QMessageBox::Ok) {
+ // WARNING: Port.
+ // file_save(NULL,NULL);
+ return true;
+ }
+ return false;
+}
+
+#define GET_UNIT(v, name, field, f, t) \
+ v = settings.value(QString(name)); \
+ if (v.isValid()) \
+ prefs.units.field = (v.toInt() == (t)) ? (t) : (f)
+
+#define GET_BOOL(v, name, field) \
+ v = settings.value(QString(name)); \
+ if (v.isValid() && v.toInt()) \
+ field = TRUE; \
+ else \
+ field = FALSE
+
+void MainWindow::readSettings()
+{
+ int i;
+ QVariant v;
+ QSettings settings("hohndel.org","subsurface");
+
+ settings.beginGroup("MainWindow");
+ QSize sz = settings.value("size").value<QSize>();
+ resize(sz);
+ ui->mainSplitter->restoreState(settings.value("mainSplitter").toByteArray());
+ ui->infoProfileSplitter->restoreState(settings.value("infoProfileSplitter").toByteArray());
+ settings.endGroup();
+
+ settings.beginGroup("ListWidget");
+ /* if no width are set, use the calculated width for each column;
+ * for that to work we need to temporarily expand all rows */
+ ui->ListWidget->expandAll();
+ for (i = TreeItemDT::NR; i < TreeItemDT::COLUMNS; i++) {
+ QVariant width = settings.value(QString("colwidth%1").arg(i));
+ if (width.isValid())
+ ui->ListWidget->setColumnWidth(i, width.toInt());
+ else
+ ui->ListWidget->resizeColumnToContents(i);
+ }
+ ui->ListWidget->collapseAll();
+ ui->ListWidget->scrollTo(ui->ListWidget->model()->index(0,0), QAbstractItemView::PositionAtCenter);
+
+ settings.endGroup();
+ settings.beginGroup("Units");
+ GET_UNIT(v, "feet", length, units::METERS, units::FEET);
+ GET_UNIT(v, "psi", pressure, units::BAR, units::PSI);
+ GET_UNIT(v, "cuft", volume, units::LITER, units::CUFT);
+ GET_UNIT(v, "fahrenheit", temperature, units::CELSIUS, units::FAHRENHEIT);
+ GET_UNIT(v, "lbs", weight, units::KG, units::LBS);
+ settings.endGroup();
+ settings.beginGroup("DisplayListColumns");
+ GET_BOOL(v, "CYLINDER", prefs.visible_cols.cylinder);
+ GET_BOOL(v, "TEMPERATURE", prefs.visible_cols.temperature);
+ GET_BOOL(v, "TOTALWEIGHT", prefs.visible_cols.totalweight);
+ GET_BOOL(v, "SUIT", prefs.visible_cols.suit);
+ GET_BOOL(v, "NITROX", prefs.visible_cols.nitrox);
+ GET_BOOL(v, "OTU", prefs.visible_cols.otu);
+ GET_BOOL(v, "MAXCNS", prefs.visible_cols.maxcns);
+ GET_BOOL(v, "SAC", prefs.visible_cols.sac);
+ GET_BOOL(v, "po2graph", prefs.pp_graphs.po2);
+ GET_BOOL(v, "pn2graph", prefs.pp_graphs.pn2);
+ GET_BOOL(v, "phegraph", prefs.pp_graphs.phe);
+ settings.endGroup();
+ settings.beginGroup("TecDetails");
+ v = settings.value(QString("po2threshold"));
+ if (v.isValid())
+ prefs.pp_graphs.po2_threshold = v.toDouble();
+ v = settings.value(QString("pn2threshold"));
+ if (v.isValid())
+ prefs.pp_graphs.pn2_threshold = v.toDouble();
+ v = settings.value(QString("phethreshold"));
+ if (v.isValid())
+ prefs.pp_graphs.phe_threshold = v.toDouble();
+ GET_BOOL(v, "mod", prefs.mod);
+ v = settings.value(QString("modppO2"));
+ if (v.isValid())
+ prefs.mod_ppO2 = v.toDouble();
+ GET_BOOL(v, "ead", prefs.ead);
+ GET_BOOL(v, "redceiling", prefs.profile_red_ceiling);
+ GET_BOOL(v, "calcceiling", prefs.profile_calc_ceiling);
+ GET_BOOL(v, "calcceiling3m", prefs.calc_ceiling_3m_incr);
+ v = settings.value(QString("gflow"));
+ if (v.isValid())
+ prefs.gflow = v.toInt() / 100.0;
+ v = settings.value(QString("gfhigh"));
+ if (v.isValid())
+ prefs.gfhigh = v.toInt() / 100.0;
+ set_gf(prefs.gflow, prefs.gfhigh);
+ settings.endGroup();
+
+#if ONCE_WE_CAN_SET_FONTS
+ settings.beginGroup("Display");
+ v = settings.value(QString("divelist_font"));
+ if (v.isValid())
+ /* I don't think this is right */
+ prefs.divelist_font = strdup(v.toString);
+#endif
+
+#if ONCE_WE_HAVE_MAPS
+ v = settings.value(QString_int("map_provider"));
+ if(v.isValid())
+ prefs.map_provider = v.toInt();
+#endif
+}
+
+#define SAVE_VALUE(name, field) \
+ if (prefs.field != default_prefs.field) \
+ settings.setValue(name, prefs.field)
+
+void MainWindow::writeSettings()
+{
+ int i;
+ QSettings settings("hohndel.org","subsurface");
+
+ settings.beginGroup("MainWindow");
+ settings.setValue("size",size());
+ settings.setValue("mainSplitter", ui->mainSplitter->saveState());
+ settings.setValue("infoProfileSplitter", ui->infoProfileSplitter->saveState());
+ settings.endGroup();
+
+ settings.beginGroup("ListWidget");
+ for (i = TreeItemDT::NR; i < TreeItemDT::COLUMNS; i++)
+ settings.setValue(QString("colwidth%1").arg(i), ui->ListWidget->columnWidth(i));
+ settings.endGroup();
+ settings.beginGroup("Units");
+ SAVE_VALUE("feet", units.length);
+ SAVE_VALUE("psi", units.pressure);
+ SAVE_VALUE("cuft", units.volume);
+ SAVE_VALUE("fahrenheit", units.temperature);
+ SAVE_VALUE("lbs", units.weight);
+ settings.endGroup();
+ settings.beginGroup("DisplayListColumns");
+ SAVE_VALUE("TEMPERATURE", visible_cols.temperature);
+ SAVE_VALUE("TOTALWEIGHT", visible_cols.totalweight);
+ SAVE_VALUE("SUIT", visible_cols.suit);
+ SAVE_VALUE("CYLINDER", visible_cols.cylinder);
+ SAVE_VALUE("NITROX", visible_cols.nitrox);
+ SAVE_VALUE("SAC", visible_cols.sac);
+ SAVE_VALUE("OTU", visible_cols.otu);
+ SAVE_VALUE("MAXCNS", visible_cols.maxcns);
+ settings.endGroup();
+ settings.beginGroup("TecDetails");
+ SAVE_VALUE("po2graph", pp_graphs.po2);
+ SAVE_VALUE("pn2graph", pp_graphs.pn2);
+ SAVE_VALUE("phegraph", pp_graphs.phe);
+ SAVE_VALUE("po2threshold", pp_graphs.po2_threshold);
+ SAVE_VALUE("pn2threshold", pp_graphs.pn2_threshold);
+ SAVE_VALUE("phethreshold", pp_graphs.phe_threshold);
+ SAVE_VALUE("mod", mod);
+ SAVE_VALUE("modppO2", mod_ppO2);
+ SAVE_VALUE("ead", ead);
+ SAVE_VALUE("redceiling", profile_red_ceiling);
+ SAVE_VALUE("calcceiling", profile_calc_ceiling);
+ SAVE_VALUE("calcceiling3m", calc_ceiling_3m_incr);
+ SAVE_VALUE("gflow", gflow);
+ SAVE_VALUE("gfhigh", gfhigh);
+ settings.endGroup();
+ settings.beginGroup("GeneralSettings");
+ SAVE_VALUE("default_filename", default_filename);
+ settings.endGroup();
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ if (unsaved_changes() && (askSaveChanges() == FALSE)) {
+ event->ignore();
+ return;
+ }
+ event->accept();
+ writeSettings();
+}
diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h
new file mode 100644
index 000000000..60f0d4609
--- /dev/null
+++ b/qt-ui/mainwindow.h
@@ -0,0 +1,89 @@
+/*
+ * mainwindow.h
+ *
+ * header file for the main window of Subsurface
+ *
+ */
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QModelIndex>
+#include <QAction>
+
+class QSortFilterProxyModel;
+class DiveTripModel;
+
+namespace Ui
+{
+ class MainWindow;
+}
+
+class DiveInfo;
+class DiveNotes;
+class Stats;
+class Equipment;
+class QItemSelection;
+
+class MainWindow : public QMainWindow
+{
+Q_OBJECT
+public:
+ MainWindow();
+
+private Q_SLOTS:
+
+ /* file menu action */
+ void on_actionNew_triggered();
+ void on_actionOpen_triggered();
+ void on_actionSave_triggered();
+ void on_actionSaveAs_triggered();
+ void on_actionClose_triggered();
+ void on_actionImport_triggered();
+ void on_actionExportUDDF_triggered();
+ void on_actionPrint_triggered();
+ void on_actionPreferences_triggered();
+ void on_actionQuit_triggered();
+
+ /* log menu actions */
+ void on_actionDownloadDC_triggered();
+ void on_actionDownloadWeb_triggered();
+ void on_actionEditDeviceNames_triggered();
+ void on_actionAddDive_triggered();
+ void on_actionRenumber_triggered();
+ void on_actionAutoGroup_triggered();
+ void on_actionToggleZoom_triggered();
+ void on_actionYearlyStatistics_triggered();
+
+ /* view menu actions */
+ void on_actionViewList_triggered();
+ void on_actionViewProfile_triggered();
+ void on_actionViewInfo_triggered();
+ void on_actionViewAll_triggered();
+ void on_actionPreviousDC_triggered();
+ void on_actionNextDC_triggered();
+
+ /* other menu actions */
+ void on_actionSelectEvents_triggered();
+ void on_actionInputPlan_triggered();
+ void on_actionAboutSubsurface_triggered();
+ void on_actionUserManual_triggered();
+
+ void current_dive_changed(int divenr);
+
+protected:
+ void closeEvent(QCloseEvent *);
+
+private:
+ Ui::MainWindow *ui;
+ QAction *actionNextDive;
+ QAction *actionPreviousDive;
+
+ QString filter();
+ bool askSaveChanges();
+ void readSettings();
+ void writeSettings();
+ void redrawProfile();
+};
+
+#endif
diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui
new file mode 100644
index 000000000..ab0cd5f49
--- /dev/null
+++ b/qt-ui/mainwindow.ui
@@ -0,0 +1,383 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>763</width>
+ <height>548</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QSplitter" name="mainSplitter">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <widget class="QSplitter" name="infoProfileSplitter">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="MainTab" name="InfoWidget" native="true"/>
+ <widget class="ProfileGraphicsView" name="ProfileWidget"/>
+ </widget>
+ <widget class="QSplitter" name="globeListSplitter">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <widget class="DiveListView" name="ListWidget">
+ <property name="styleSheet">
+ <string notr="true"> QTreeView {
+ show-decoration-selected: 1;
+ }
+
+ QTreeView::item {
+ border: 1px solid #d9d9d9;
+ border-top-color: transparent;
+ border-bottom-color: transparent;
+ padding: 2px;
+ }
+
+ QTreeView::item:hover {
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1);
+ border: 1px solid #bfcde4;
+ }
+
+ QTreeView::item:selected {
+ border: 1px solid #567dbc;
+ }
+
+ QTreeView::item:selected:active{
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6ea1f1, stop: 1 #567dbc);
+ }
+
+ QTreeView::item:selected:!active {
+ background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6b9be8, stop: 1 #577fbf);
+ }
+
+</string>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="animated">
+ <bool>true</bool>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="GlobeGPS" name="widget" native="true"/>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QMenuBar" name="menubar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>763</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <widget class="QMenu" name="menuFile">
+ <property name="title">
+ <string>File</string>
+ </property>
+ <addaction name="actionNew"/>
+ <addaction name="actionOpen"/>
+ <addaction name="actionSave"/>
+ <addaction name="actionSaveAs"/>
+ <addaction name="actionClose"/>
+ <addaction name="separator"/>
+ <addaction name="actionImport"/>
+ <addaction name="actionExportUDDF"/>
+ <addaction name="separator"/>
+ <addaction name="actionPrint"/>
+ <addaction name="separator"/>
+ <addaction name="actionPreferences"/>
+ <addaction name="separator"/>
+ <addaction name="actionQuit"/>
+ </widget>
+ <widget class="QMenu" name="menuLog">
+ <property name="title">
+ <string>Log</string>
+ </property>
+ <addaction name="actionDownloadDC"/>
+ <addaction name="actionDownloadWeb"/>
+ <addaction name="actionEditDeviceNames"/>
+ <addaction name="separator"/>
+ <addaction name="actionAddDive"/>
+ <addaction name="separator"/>
+ <addaction name="actionRenumber"/>
+ <addaction name="actionAutoGroup"/>
+ <addaction name="actionToggleZoom"/>
+ <addaction name="actionYearlyStatistics"/>
+ </widget>
+ <widget class="QMenu" name="menuView">
+ <property name="title">
+ <string>View</string>
+ </property>
+ <addaction name="actionViewList"/>
+ <addaction name="actionViewProfile"/>
+ <addaction name="actionViewInfo"/>
+ <addaction name="actionViewAll"/>
+ <addaction name="actionPreviousDC"/>
+ <addaction name="actionNextDC"/>
+ </widget>
+ <widget class="QMenu" name="menuFilter">
+ <property name="title">
+ <string>Filter</string>
+ </property>
+ <addaction name="actionSelectEvents"/>
+ </widget>
+ <widget class="QMenu" name="menuPlanner">
+ <property name="title">
+ <string>Planner</string>
+ </property>
+ <addaction name="actionInputPlan"/>
+ </widget>
+ <widget class="QMenu" name="menuHelp">
+ <property name="title">
+ <string>Help</string>
+ </property>
+ <addaction name="actionAboutSubsurface"/>
+ <addaction name="actionUserManual"/>
+ </widget>
+ <addaction name="menuFile"/>
+ <addaction name="menuLog"/>
+ <addaction name="menuView"/>
+ <addaction name="menuFilter"/>
+ <addaction name="menuPlanner"/>
+ <addaction name="menuHelp"/>
+ </widget>
+ <action name="actionNew">
+ <property name="text">
+ <string>New</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+N</string>
+ </property>
+ </action>
+ <action name="actionOpen">
+ <property name="text">
+ <string>Open</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+O</string>
+ </property>
+ </action>
+ <action name="actionSave">
+ <property name="text">
+ <string>Save</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+S</string>
+ </property>
+ </action>
+ <action name="actionSaveAs">
+ <property name="text">
+ <string>Save as</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Shift+S</string>
+ </property>
+ </action>
+ <action name="actionClose">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+W</string>
+ </property>
+ </action>
+ <action name="actionImport">
+ <property name="text">
+ <string>Import Files</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+I</string>
+ </property>
+ </action>
+ <action name="actionExportUDDF">
+ <property name="text">
+ <string>Export UDDF</string>
+ </property>
+ </action>
+ <action name="actionPrint">
+ <property name="text">
+ <string>Print</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+P</string>
+ </property>
+ </action>
+ <action name="actionPreferences">
+ <property name="text">
+ <string>Preferences</string>
+ </property>
+ </action>
+ <action name="actionQuit">
+ <property name="text">
+ <string>Quit</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+Q</string>
+ </property>
+ </action>
+ <action name="actionDownloadDC">
+ <property name="text">
+ <string>Download from Dive computer</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+D</string>
+ </property>
+ </action>
+ <action name="actionDownloadWeb">
+ <property name="text">
+ <string>Download from Web Service</string>
+ </property>
+ </action>
+ <action name="actionEditDeviceNames">
+ <property name="text">
+ <string>Edit Device Names</string>
+ </property>
+ </action>
+ <action name="actionAddDive">
+ <property name="text">
+ <string>Add Dive</string>
+ </property>
+ </action>
+ <action name="actionRenumber">
+ <property name="text">
+ <string>Renumber</string>
+ </property>
+ </action>
+ <action name="actionAutoGroup">
+ <property name="text">
+ <string>Auto Group</string>
+ </property>
+ </action>
+ <action name="actionToggleZoom">
+ <property name="text">
+ <string>Toggle Zoom</string>
+ </property>
+ </action>
+ <action name="actionYearlyStatistics">
+ <property name="text">
+ <string>Yearly Statistics</string>
+ </property>
+ </action>
+ <action name="actionViewList">
+ <property name="text">
+ <string>View List</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+1</string>
+ </property>
+ </action>
+ <action name="actionViewProfile">
+ <property name="text">
+ <string>View Profile</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+2</string>
+ </property>
+ </action>
+ <action name="actionViewInfo">
+ <property name="text">
+ <string>View Info</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+3</string>
+ </property>
+ </action>
+ <action name="actionViewAll">
+ <property name="text">
+ <string>View All</string>
+ </property>
+ <property name="shortcut">
+ <string>Ctrl+4</string>
+ </property>
+ </action>
+ <action name="actionPreviousDC">
+ <property name="text">
+ <string>Prev DC</string>
+ </property>
+ <property name="shortcut">
+ <string>Left</string>
+ </property>
+ </action>
+ <action name="actionNextDC">
+ <property name="text">
+ <string>Next DC</string>
+ </property>
+ <property name="shortcut">
+ <string>Right</string>
+ </property>
+ </action>
+ <action name="actionSelectEvents">
+ <property name="text">
+ <string>Select Events</string>
+ </property>
+ </action>
+ <action name="actionInputPlan">
+ <property name="text">
+ <string>Input Plan</string>
+ </property>
+ </action>
+ <action name="actionAboutSubsurface">
+ <property name="text">
+ <string>About Subsurface</string>
+ </property>
+ </action>
+ <action name="actionUserManual">
+ <property name="text">
+ <string>User Manual</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>MainTab</class>
+ <extends>QWidget</extends>
+ <header>maintab.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>DiveListView</class>
+ <extends>QTreeView</extends>
+ <header>divelistview.h</header>
+ </customwidget>
+ <customwidget>
+ <class>ProfileGraphicsView</class>
+ <extends>QGraphicsView</extends>
+ <header>profilegraphics.h</header>
+ </customwidget>
+ <customwidget>
+ <class>GlobeGPS</class>
+ <extends>QWidget</extends>
+ <header>globe.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp
new file mode 100644
index 000000000..be47198e2
--- /dev/null
+++ b/qt-ui/modeldelegates.cpp
@@ -0,0 +1,49 @@
+#include "modeldelegates.h"
+#include "../dive.h"
+#include "../divelist.h"
+#include "starwidget.h"
+#include "models.h"
+
+#include <QtDebug>
+#include <QPainter>
+#include <QSortFilterProxyModel>
+#include <QStyle>
+#include <QStyleOption>
+
+StarWidgetsDelegate::StarWidgetsDelegate(QWidget* parent):
+ QStyledItemDelegate(parent),
+ parentWidget(parent)
+{
+
+}
+
+void StarWidgetsDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+ QStyledItemDelegate::paint(painter, option, index);
+
+ if (!index.isValid())
+ return;
+
+ QVariant value = index.model()->data(index, TreeItemDT::STAR_ROLE);
+
+ if (!value.isValid())
+ return;
+
+ int rating = value.toInt();
+
+ painter->save();
+ painter->setRenderHint(QPainter::Antialiasing, true);
+
+ for(int i = 0; i < rating; i++)
+ painter->drawPixmap(option.rect.x() + i * IMG_SIZE + SPACING, option.rect.y(), StarWidget::starActive());
+
+ for(int i = rating; i < TOTALSTARS; i++)
+ painter->drawPixmap(option.rect.x() + i * IMG_SIZE + SPACING, option.rect.y(), StarWidget::starInactive());
+
+ painter->restore();
+}
+
+QSize StarWidgetsDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+ return QSize(IMG_SIZE * TOTALSTARS + SPACING * (TOTALSTARS-1), IMG_SIZE);
+}
diff --git a/qt-ui/modeldelegates.h b/qt-ui/modeldelegates.h
new file mode 100644
index 000000000..5f90a3061
--- /dev/null
+++ b/qt-ui/modeldelegates.h
@@ -0,0 +1,15 @@
+#ifndef MODELDELEGATES_H
+#define MODELDELEGATES_H
+
+#include <QStyledItemDelegate>
+
+class StarWidgetsDelegate : public QStyledItemDelegate {
+ Q_OBJECT
+public:
+ explicit StarWidgetsDelegate(QWidget* parent = 0);
+ virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
+ virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
+private:
+ QWidget *parentWidget;
+};
+#endif
diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp
new file mode 100644
index 000000000..9bc8db0bb
--- /dev/null
+++ b/qt-ui/models.cpp
@@ -0,0 +1,705 @@
+/*
+ * models.cpp
+ *
+ * classes for the equipment models of Subsurface
+ *
+ */
+#include "models.h"
+#include <QCoreApplication>
+#include <QDebug>
+#include <QColor>
+#include <QBrush>
+
+extern struct tank_info tank_info[100];
+
+CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent)
+{
+}
+
+QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ QVariant ret;
+ if (orientation == Qt::Vertical)
+ return ret;
+
+ if (role == Qt::DisplayRole) {
+ switch(section) {
+ case TYPE:
+ ret = tr("Type");
+ break;
+ case SIZE:
+ ret = tr("Size");
+ break;
+ case MAXPRESS:
+ ret = tr("MaxPress");
+ break;
+ case START:
+ ret = tr("Start");
+ break;
+ case END:
+ ret = tr("End");
+ break;
+ case O2:
+ ret = tr("O2%");
+ break;
+ case HE:
+ ret = tr("He%");
+ break;
+ }
+ }
+ return ret;
+}
+
+int CylindersModel::columnCount(const QModelIndex& parent) const
+{
+ return 7;
+}
+
+QVariant CylindersModel::data(const QModelIndex& index, int role) const
+{
+ QVariant ret;
+ if (!index.isValid() || index.row() >= MAX_CYLINDERS)
+ return ret;
+
+ cylinder_t& cyl = current_dive->cylinder[index.row()];
+
+ if (role == Qt::DisplayRole) {
+ switch(index.column()) {
+ case TYPE:
+ ret = QString(cyl.type.description);
+ break;
+ case SIZE:
+ ret = cyl.type.size.mliter;
+ break;
+ case MAXPRESS:
+ ret = cyl.type.workingpressure.mbar;
+ break;
+ case START:
+ ret = cyl.start.mbar;
+ break;
+ case END:
+ ret = cyl.end.mbar;
+ break;
+ case O2:
+ ret = cyl.gasmix.o2.permille;
+ break;
+ case HE:
+ ret = cyl.gasmix.he.permille;
+ break;
+ }
+ }
+ return ret;
+}
+
+int CylindersModel::rowCount(const QModelIndex& parent) const
+{
+ return usedRows[current_dive];
+}
+
+void CylindersModel::add(cylinder_t* cyl)
+{
+ if (usedRows[current_dive] >= MAX_CYLINDERS) {
+ free(cyl);
+ return;
+ }
+
+ int row = usedRows[current_dive];
+
+ cylinder_t& cylinder = current_dive->cylinder[row];
+
+ cylinder.end.mbar = cyl->end.mbar;
+ cylinder.start.mbar = cyl->start.mbar;
+
+ beginInsertRows(QModelIndex(), row, row);
+ usedRows[current_dive]++;
+ endInsertRows();
+}
+
+void CylindersModel::update()
+{
+ if (usedRows[current_dive] > 0) {
+ beginRemoveRows(QModelIndex(), 0, usedRows[current_dive]-1);
+ endRemoveRows();
+ }
+ if (usedRows[current_dive] > 0) {
+ beginInsertRows(QModelIndex(), 0, usedRows[current_dive]-1);
+ endInsertRows();
+ }
+}
+
+void CylindersModel::clear()
+{
+ if (usedRows[current_dive] > 0) {
+ beginRemoveRows(QModelIndex(), 0, usedRows[current_dive]-1);
+ usedRows[current_dive] = 0;
+ endRemoveRows();
+ }
+}
+
+void WeightModel::clear()
+{
+ if (usedRows[current_dive] > 0) {
+ beginRemoveRows(QModelIndex(), 0, usedRows[current_dive]-1);
+ usedRows[current_dive] = 0;
+ endRemoveRows();
+ }
+}
+
+int WeightModel::columnCount(const QModelIndex& parent) const
+{
+ return 2;
+}
+
+QVariant WeightModel::data(const QModelIndex& index, int role) const
+{
+ QVariant ret;
+ if (!index.isValid() || index.row() >= MAX_WEIGHTSYSTEMS)
+ return ret;
+
+ weightsystem_t *ws = &current_dive->weightsystem[index.row()];
+
+ if (role == Qt::DisplayRole) {
+ switch(index.column()) {
+ case TYPE:
+ ret = QString(ws->description);
+ break;
+ case WEIGHT:
+ if (get_units()->weight == units::KG) {
+ int gr = ws->weight.grams % 1000;
+ int kg = ws->weight.grams / 1000;
+ ret = QString("%1.%2").arg(kg).arg((unsigned) gr / 100);
+ } else {
+ ret = QString("%1").arg((unsigned)(grams_to_lbs(ws->weight.grams)));
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+int WeightModel::rowCount(const QModelIndex& parent) const
+{
+ return usedRows[current_dive];
+}
+
+QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ QVariant ret;
+ if (orientation == Qt::Vertical)
+ return ret;
+
+ if (role == Qt::DisplayRole) {
+ switch(section) {
+ case TYPE:
+ ret = tr("Type");
+ break;
+ case WEIGHT:
+ ret = tr("Weight");
+ break;
+ }
+ }
+ return ret;
+}
+
+void WeightModel::add(weightsystem_t* weight)
+{
+ if (usedRows[current_dive] >= MAX_WEIGHTSYSTEMS) {
+ free(weight);
+ return;
+ }
+
+ int row = usedRows[current_dive];
+
+ weightsystem_t *ws = &current_dive->weightsystem[row];
+
+ ws->description = weight->description;
+ ws->weight.grams = weight->weight.grams;
+
+ beginInsertRows(QModelIndex(), row, row);
+ usedRows[current_dive]++;
+ endInsertRows();
+}
+
+void WeightModel::update()
+{
+}
+
+void TankInfoModel::add(const QString& description)
+{
+ // When the user `creates` a new one on the combobox.
+ // for now, empty till dirk cleans the GTK code.
+}
+
+void TankInfoModel::clear()
+{
+}
+
+int TankInfoModel::columnCount(const QModelIndex& parent) const
+{
+ return 3;
+}
+
+QVariant TankInfoModel::data(const QModelIndex& index, int role) const
+{
+ QVariant ret;
+ if (!index.isValid()) {
+ return ret;
+ }
+ struct tank_info *info = &tank_info[index.row()];
+
+ int ml = info->ml;
+
+ int bar = ((info->psi) ? psi_to_bar(info->psi) : info->bar) * 1000 + 0.5;
+
+ if (info->cuft) {
+ double airvolume = cuft_to_l(info->cuft) * 1000.0;
+ ml = airvolume / bar_to_atm(bar) + 0.5;
+ }
+ if (role == Qt::DisplayRole) {
+ switch(index.column()) {
+ case BAR:
+ ret = bar;
+ break;
+ case ML:
+ ret = ml;
+ break;
+ case DESCRIPTION:
+ ret = QString(info->name);
+ break;
+ }
+ }
+ return ret;
+}
+
+QVariant TankInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ QVariant ret;
+
+ if (orientation != Qt::Horizontal)
+ return ret;
+
+ if (role == Qt::DisplayRole) {
+ switch(section) {
+ case BAR:
+ ret = tr("Bar");
+ break;
+ case ML:
+ ret = tr("Ml");
+ break;
+ case DESCRIPTION:
+ ret = tr("Description");
+ break;
+ }
+ }
+ return ret;
+}
+
+int TankInfoModel::rowCount(const QModelIndex& parent) const
+{
+ return rows+1;
+}
+
+TankInfoModel::TankInfoModel() : QAbstractTableModel(), rows(-1)
+{
+ struct tank_info *info = tank_info;
+ for (info = tank_info; info->name; info++, rows++);
+
+ if (rows > -1) {
+ beginInsertRows(QModelIndex(), 0, rows);
+ endInsertRows();
+ }
+}
+
+void TankInfoModel::update()
+{
+ if(rows > -1) {
+ beginRemoveRows(QModelIndex(), 0, rows);
+ endRemoveRows();
+ }
+ struct tank_info *info = tank_info;
+ for (info = tank_info; info->name; info++, rows++);
+
+ if (rows > -1) {
+ beginInsertRows(QModelIndex(), 0, rows);
+ endInsertRows();
+ }
+}
+
+
+/*! A DiveItem for use with a DiveTripModel
+ *
+ * A simple class which wraps basic stats for a dive (e.g. duration, depth) and
+ * tidies up after it's children. This is done manually as we don't inherit from
+ * QObject.
+ *
+*/
+
+TreeItemDT::~TreeItemDT()
+{
+ qDeleteAll(children);
+}
+
+int TreeItemDT::row() const
+{
+ if (parent)
+ return parent->children.indexOf(const_cast<TreeItemDT*>(this));
+
+ return 0;
+}
+
+QVariant TreeItemDT::data(int column, int role) const
+{
+ QVariant ret;
+ switch (column) {
+ case NR:
+ ret = tr("#");
+ break;
+ case DATE:
+ ret = tr("Date");
+ break;
+ case RATING:
+ ret = UTF8_BLACKSTAR;
+ break;
+ case DEPTH:
+ ret = (get_units()->length == units::METERS) ? tr("m") : tr("ft");
+ break;
+ case DURATION:
+ ret = tr("min");
+ break;
+ case TEMPERATURE:
+ ret = QString("%1%2").arg(UTF8_DEGREE).arg( (get_units()->temperature == units::CELSIUS) ? "C" : "F");
+ break;
+ case TOTALWEIGHT:
+ ret = (get_units()->weight == units::KG) ? tr("kg") : tr("lbs");
+ break;
+ case SUIT:
+ ret = tr("Suit");
+ break;
+ case CYLINDER:
+ ret = tr("Cyl");
+ break;
+ case NITROX:
+ ret = QString("O%1%").arg(UTF8_SUBSCRIPT_2);
+ break;
+ case SAC:
+ ret = tr("SAC");
+ break;
+ case OTU:
+ ret = tr("OTU");
+ break;
+ case MAXCNS:
+ ret = tr("maxCNS");
+ break;
+ case LOCATION:
+ ret = tr("Location");
+ break;
+ }
+ return ret;
+}
+
+struct TripItem : public TreeItemDT {
+ virtual QVariant data(int column, int role) const;
+ dive_trip_t* trip;
+};
+
+QVariant TripItem::data(int column, int role) const
+{
+ QVariant ret;
+
+ if (role == Qt::DisplayRole) {
+ switch (column) {
+ case LOCATION:
+ ret = QString(trip->location);
+ break;
+ case DATE:
+ ret = QString(get_trip_date_string(trip->when, trip->nrdives));
+ break;
+ }
+ }
+
+ return ret;
+}
+
+struct DiveItem : public TreeItemDT {
+ virtual QVariant data(int column, int role) const;
+ struct dive* dive;
+
+ QString displayDuration() const;
+ QString displayDepth() const;
+ QString displayTemperature() const;
+ QString displayWeight() const;
+ QString displaySac() const;
+ int weight() const;
+};
+
+QVariant DiveItem::data(int column, int role) const
+{
+ QVariant retVal;
+
+ switch (role) {
+ case Qt::TextAlignmentRole:
+ switch (column) {
+ case DATE: /* fall through */
+ case SUIT: /* fall through */
+ case LOCATION:
+ retVal = Qt::AlignLeft;
+ break;
+ default:
+ retVal = Qt::AlignRight;
+ break;
+ }
+ break;
+ case Qt::DisplayRole:
+ switch (column) {
+ case NR:
+ retVal = dive->number;
+ break;
+ case DATE:
+ retVal = QString(get_dive_date_string(dive->when));
+ break;
+ case DEPTH:
+ retVal = displayDepth();
+ break;
+ case DURATION:
+ retVal = displayDuration();
+ break;
+ case TEMPERATURE:
+ retVal = displayTemperature();
+ break;
+ case TOTALWEIGHT:
+ retVal = displayWeight();
+ break;
+ case SUIT:
+ retVal = QString(dive->suit);
+ break;
+ case CYLINDER:
+ retVal = QString(dive->cylinder[0].type.description);
+ break;
+ case NITROX:
+ retVal = QString(get_nitrox_string(dive));
+ break;
+ case SAC:
+ retVal = displaySac();
+ break;
+ case OTU:
+ retVal = dive->otu;
+ break;
+ case MAXCNS:
+ retVal = dive->maxcns;
+ break;
+ case LOCATION:
+ retVal = QString(dive->location);
+ break;
+ }
+ break;
+ }
+
+ if(role == STAR_ROLE){
+ retVal = dive->rating;
+ }
+
+ if(role == DIVE_ROLE){
+ retVal = QVariant::fromValue<void*>(dive);
+ }
+
+ return retVal;
+}
+
+QString DiveItem::displayDepth() const
+{
+ const int scale = 1000;
+ QString fract, str;
+ if (get_units()->length == units::METERS) {
+ fract = QString::number((unsigned)(dive->maxdepth.mm % scale) / 10);
+ str = QString("%1.%2").arg((unsigned)(dive->maxdepth.mm / scale)).arg(fract, 2, QChar('0'));
+ }
+ if (get_units()->length == units::FEET) {
+ str = QString::number(mm_to_feet(dive->maxdepth.mm),'f',0);
+ }
+ return str;
+}
+
+QString DiveItem::displayDuration() const
+{
+ int hrs, mins, secs;
+ secs = dive->duration.seconds % 60;
+ mins = dive->duration.seconds / 60;
+ hrs = mins / 60;
+ mins -= hrs * 60;
+
+ QString displayTime;
+ if (hrs)
+ displayTime = QString("%1:%2:").arg(hrs).arg(mins, 2, 10, QChar('0'));
+ else
+ displayTime = QString("%1:").arg(mins);
+ displayTime += QString("%1").arg(secs, 2, 10, QChar('0'));
+ return displayTime;
+}
+
+QString DiveItem::displayTemperature() const
+{
+ QString str;
+
+ if (!dive->watertemp.mkelvin)
+ return str;
+
+ if (get_units()->temperature == units::CELSIUS)
+ str = QString::number(mkelvin_to_C(dive->watertemp.mkelvin), 'f', 1);
+ else
+ str = QString::number(mkelvin_to_F(dive->watertemp.mkelvin), 'f', 1);
+
+ return str;
+}
+
+QString DiveItem::displaySac() const
+{
+ QString str;
+
+ if (get_units()->volume == units::LITER)
+ str = QString::number(dive->sac / 1000, 'f', 1);
+ else
+ str = QString::number(ml_to_cuft(dive->sac), 'f', 2);
+
+ return str;
+}
+
+QString DiveItem::displayWeight() const
+{
+ QString str;
+
+ if (get_units()->weight == units::KG) {
+ int gr = weight() % 1000;
+ int kg = weight() / 1000;
+ str = QString("%1.%2").arg(kg).arg((unsigned)(gr) / 100);
+ } else {
+ str = QString("%1").arg((unsigned)(grams_to_lbs(weight())));
+ }
+
+ return str;
+}
+
+int DiveItem::weight() const
+{
+ weight_t tw = { total_weight(dive) };
+ return tw.grams;
+}
+
+
+DiveTripModel::DiveTripModel(QObject* parent) :
+ QAbstractItemModel(parent)
+{
+ rootItem = new TreeItemDT();
+ setupModelData();
+}
+
+DiveTripModel::~DiveTripModel()
+{
+ delete rootItem;
+}
+
+int DiveTripModel::columnCount(const QModelIndex& parent) const
+{
+ if (parent.isValid())
+ return static_cast<TreeItemDT*>(parent.internalPointer())->columnCount();
+ else
+ return rootItem->columnCount();
+}
+
+QVariant DiveTripModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+
+ TreeItemDT* item = static_cast<TreeItemDT*>(index.internalPointer());
+
+ return item->data(index.column(), role);
+}
+
+Qt::ItemFlags DiveTripModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return 0;
+
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+}
+
+QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation,
+ int role) const
+{
+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
+ return rootItem->data(section, role);
+
+ return QVariant();
+}
+
+QModelIndex DiveTripModel::index(int row, int column, const QModelIndex& parent)
+const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ TreeItemDT* parentItem = (!parent.isValid()) ? rootItem : static_cast<TreeItemDT*>(parent.internalPointer());
+
+ TreeItemDT* childItem = parentItem->children[row];
+
+ return (childItem) ? createIndex(row, column, childItem) : QModelIndex();
+}
+
+QModelIndex DiveTripModel::parent(const QModelIndex& index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ TreeItemDT* childItem = static_cast<TreeItemDT*>(index.internalPointer());
+ TreeItemDT* parentItem = childItem->parent;
+
+ if (parentItem == rootItem || !parentItem)
+ return QModelIndex();
+
+ return createIndex(parentItem->row(), 0, parentItem);
+}
+
+int DiveTripModel::rowCount(const QModelIndex& parent) const
+{
+ TreeItemDT* parentItem;
+
+ if (!parent.isValid())
+ parentItem = rootItem;
+ else
+ parentItem = static_cast<TreeItemDT*>(parent.internalPointer());
+
+ int amount = parentItem->children.count();
+
+ return amount;
+}
+
+void DiveTripModel::setupModelData()
+{
+ int i = dive_table.nr;
+
+ while (--i >= 0) {
+ struct dive* dive = get_dive(i);
+ update_cylinder_related_info(dive);
+ dive_trip_t* trip = dive->divetrip;
+
+ DiveItem* diveItem = new DiveItem();
+ diveItem->dive = dive;
+
+ if (!trip) {
+ diveItem->parent = rootItem;
+ rootItem->children.push_back(diveItem);
+ continue;
+ }
+ if (!trips.keys().contains(trip)) {
+ TripItem* tripItem = new TripItem();
+ tripItem->trip = trip;
+ tripItem->parent = rootItem;
+ tripItem->children.push_back(diveItem);
+ trips[trip] = tripItem;
+ rootItem->children.push_back(tripItem);
+ continue;
+ }
+ TripItem* tripItem = trips[trip];
+ tripItem->children.push_back(diveItem);
+ }
+}
diff --git a/qt-ui/models.h b/qt-ui/models.h
new file mode 100644
index 000000000..ac533fd71
--- /dev/null
+++ b/qt-ui/models.h
@@ -0,0 +1,126 @@
+/*
+ * models.h
+ *
+ * header file for the equipment models of Subsurface
+ *
+ */
+#ifndef MODELS_H
+#define MODELS_H
+
+#include <QAbstractTableModel>
+#include <QCoreApplication>
+
+#include "../dive.h"
+#include "../divelist.h"
+
+/* Encapsulates the tank_info global variable
+ * to show on Qt's Model View System.*/
+class TankInfoModel : public QAbstractTableModel {
+Q_OBJECT
+public:
+ enum Column { DESCRIPTION, ML, BAR};
+ TankInfoModel();
+
+ /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const;
+ /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+
+ void add(const QString& description);
+ void clear();
+ void update();
+private:
+ int rows;
+};
+
+/* Encapsulation of the Cylinder Model, that presents the
+ * Current cylinders that are used on a dive. */
+class CylindersModel : public QAbstractTableModel {
+Q_OBJECT
+public:
+ enum Column {TYPE, SIZE, MAXPRESS, START, END, O2, HE};
+
+ explicit CylindersModel(QObject* parent = 0);
+ /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const;
+ /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+
+ void add(cylinder_t *cyl);
+ void clear();
+ void update();
+private:
+ /* Since the dive doesn't stores the number of cylinders that
+ * it has (max 8) and since I don't want to make a
+ * model-for-each-dive, let's hack this here instead. */
+ QMap<struct dive *, int> usedRows;
+};
+
+/* Encapsulation of the Weight Model, that represents
+ * the current weights on a dive. */
+class WeightModel : public QAbstractTableModel {
+Q_OBJECT
+public:
+ enum Column {TYPE, WEIGHT};
+ /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ /*reimp*/ int columnCount(const QModelIndex& parent = QModelIndex()) const;
+ /*reimp*/ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ /*reimp*/ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+
+ void add(weightsystem_t *weight);
+ void clear();
+ void update();
+private:
+ /* Remember the number of rows in a dive */
+ QMap<struct dive *, int> usedRows;
+};
+
+/*! An AbstractItemModel for recording dive trip information such as a list of dives.
+*
+*/
+
+struct TreeItemDT {
+ Q_DECLARE_TR_FUNCTIONS ( TreeItemDT );
+public:
+ enum Column {NR, DATE, RATING, DEPTH, DURATION, TEMPERATURE, TOTALWEIGHT,
+ SUIT, CYLINDER, NITROX, SAC, OTU, MAXCNS, LOCATION, DIVE, COLUMNS };
+
+ enum ExtraRoles{STAR_ROLE = Qt::UserRole + 1, DIVE_ROLE};
+
+ virtual ~TreeItemDT();
+ int columnCount() const {
+ return COLUMNS;
+ };
+
+ virtual QVariant data ( int column, int role ) const;
+ int row() const;
+ QList<TreeItemDT *> children;
+ TreeItemDT *parent;
+};
+
+struct TripItem;
+
+class DiveTripModel : public QAbstractItemModel
+{
+ Q_OBJECT
+
+public:
+ DiveTripModel(QObject *parent = 0);
+ ~DiveTripModel();
+
+ /*reimp*/ Qt::ItemFlags flags(const QModelIndex &index) const;
+ /*reimp*/ QVariant data(const QModelIndex &index, int role) const;
+ /*reimp*/ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ /*reimp*/ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ /*reimp*/ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ /*reimp*/ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
+ /*reimp*/ QModelIndex parent(const QModelIndex &child) const;
+
+private:
+ void setupModelData();
+
+ TreeItemDT *rootItem;
+ QMap<dive_trip_t*, TripItem*> trips;
+};
+
+#endif
diff --git a/qt-ui/plotareascene.cpp b/qt-ui/plotareascene.cpp
new file mode 100644
index 000000000..a728040f5
--- /dev/null
+++ b/qt-ui/plotareascene.cpp
@@ -0,0 +1,6 @@
+/*
+ * plotareascene.cpp
+ *
+ * classes for profile plot area scene of Subsurface
+ *
+ */
diff --git a/qt-ui/plotareascene.h b/qt-ui/plotareascene.h
new file mode 100644
index 000000000..a5b07d1be
--- /dev/null
+++ b/qt-ui/plotareascene.h
@@ -0,0 +1,6 @@
+/*
+ * plotareascene.h
+ *
+ * header file for the profile plot area scene of Subsurface
+ *
+ */
diff --git a/qt-ui/profilegraphics.cpp b/qt-ui/profilegraphics.cpp
new file mode 100644
index 000000000..637c7fe95
--- /dev/null
+++ b/qt-ui/profilegraphics.cpp
@@ -0,0 +1,1397 @@
+#include "profilegraphics.h"
+
+#include <QGraphicsScene>
+#include <QResizeEvent>
+#include <QGraphicsLineItem>
+#include <QPen>
+#include <QBrush>
+#include <QDebug>
+#include <QLineF>
+#include <QSettings>
+#include <QIcon>
+#include <QPropertyAnimation>
+#include <QGraphicsSceneHoverEvent>
+#include <QMouseEvent>
+
+#include "../color.h"
+#include "../display.h"
+#include "../dive.h"
+#include "../profile.h"
+
+#include <libdivecomputer/parser.h>
+#include <libdivecomputer/version.h>
+
+#define SAC_COLORS_START_IDX SAC_1
+#define SAC_COLORS 9
+#define VELOCITY_COLORS_START_IDX VELO_STABLE
+#define VELOCITY_COLORS 5
+
+static struct graphics_context last_gc;
+static double plot_scale = SCALE_SCREEN;
+
+typedef enum {
+ /* SAC colors. Order is important, the SAC_COLORS_START_IDX define above. */
+ SAC_1, SAC_2, SAC_3, SAC_4, SAC_5, SAC_6, SAC_7, SAC_8, SAC_9,
+
+ /* Velocity colors. Order is still important, ref VELOCITY_COLORS_START_IDX. */
+ VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY,
+
+ /* gas colors */
+ PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES,
+
+ /* Other colors */
+ TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW,
+ SMOOTHED, MINUTE, TIME_GRID, TIME_TEXT, DEPTH_GRID, MEAN_DEPTH, DEPTH_TOP,
+ DEPTH_BOTTOM, TEMP_TEXT, TEMP_PLOT, SAC_DEFAULT, BOUNDING_BOX, PRESSURE_TEXT, BACKGROUND,
+ CEILING_SHALLOW, CEILING_DEEP, CALC_CEILING_SHALLOW, CALC_CEILING_DEEP
+} color_indice_t;
+
+
+#define COLOR(x, y, z) QVector<QColor>() << x << y << z;
+/* profile_color[color indice] = COLOR(screen color, b/w printer color, color printer}} printer & screen colours could be different */
+QMap<color_indice_t, QVector<QColor> > profile_color;
+void fill_profile_color()
+{
+ profile_color[SAC_1] = COLOR(FUNGREEN1, BLACK1_LOW_TRANS, FUNGREEN1);
+ profile_color[SAC_2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1);
+ profile_color[SAC_3] = COLOR(ATLANTIS1, BLACK1_LOW_TRANS, ATLANTIS1);
+ profile_color[SAC_4] = COLOR(ATLANTIS2, BLACK1_LOW_TRANS, ATLANTIS2);
+ profile_color[SAC_5] = COLOR(EARLSGREEN1, BLACK1_LOW_TRANS, EARLSGREEN1);
+ profile_color[SAC_6] = COLOR(HOKEYPOKEY1, BLACK1_LOW_TRANS, HOKEYPOKEY1);
+ profile_color[SAC_7] = COLOR(TUSCANY1, BLACK1_LOW_TRANS, TUSCANY1);
+ profile_color[SAC_8] = COLOR(CINNABAR1, BLACK1_LOW_TRANS, CINNABAR1);
+ profile_color[SAC_9] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1);
+
+ profile_color[VELO_STABLE] = COLOR(CAMARONE1, BLACK1_LOW_TRANS, CAMARONE1);
+ profile_color[VELO_SLOW] = COLOR(LIMENADE1, BLACK1_LOW_TRANS, LIMENADE1);
+ profile_color[VELO_MODERATE] = COLOR(RIOGRANDE1, BLACK1_LOW_TRANS, RIOGRANDE1);
+ profile_color[VELO_FAST] = COLOR(PIRATEGOLD1, BLACK1_LOW_TRANS, PIRATEGOLD1);
+ profile_color[VELO_CRAZY] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
+
+ profile_color[PO2] = COLOR(APPLE1, BLACK1_LOW_TRANS, APPLE1);
+ profile_color[PO2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
+ profile_color[PN2] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS);
+ profile_color[PN2_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
+ profile_color[PHE] = COLOR(PEANUT, BLACK1_LOW_TRANS, PEANUT);
+ profile_color[PHE_ALERT] = COLOR(RED1, BLACK1_LOW_TRANS, RED1);
+ profile_color[PP_LINES] = COLOR(BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS);
+
+ profile_color[TEXT_BACKGROUND] = COLOR(CONCRETE1_LOWER_TRANS, WHITE1, CONCRETE1_LOWER_TRANS);
+ profile_color[ALERT_BG] = COLOR(BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS, BROOM1_LOWER_TRANS);
+ profile_color[ALERT_FG] = COLOR(BLACK1_LOW_TRANS, BLACK1_LOW_TRANS, BLACK1_LOW_TRANS);
+ profile_color[EVENTS] = COLOR(REDORANGE1, BLACK1_LOW_TRANS, REDORANGE1);
+ profile_color[SAMPLE_DEEP] = COLOR(PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1);
+ profile_color[SAMPLE_SHALLOW] = COLOR(PERSIANRED1, BLACK1_LOW_TRANS, PERSIANRED1);
+ profile_color[SMOOTHED] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_LOW_TRANS, REDORANGE1_HIGH_TRANS);
+ profile_color[MINUTE] = COLOR(MEDIUMREDVIOLET1_HIGHER_TRANS, BLACK1_LOW_TRANS, MEDIUMREDVIOLET1_HIGHER_TRANS);
+ profile_color[TIME_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS);
+ profile_color[TIME_TEXT] = COLOR(FORESTGREEN1, BLACK1_LOW_TRANS, FORESTGREEN1);
+ profile_color[DEPTH_GRID] = COLOR(WHITE1, BLACK1_HIGH_TRANS, TUNDORA1_MED_TRANS);
+ profile_color[MEAN_DEPTH] = COLOR(REDORANGE1_MED_TRANS, BLACK1_LOW_TRANS, REDORANGE1_MED_TRANS);
+ profile_color[DEPTH_BOTTOM] = COLOR(GOVERNORBAY1_MED_TRANS, BLACK1_HIGH_TRANS, GOVERNORBAY1_MED_TRANS);
+ profile_color[DEPTH_TOP] = COLOR(MERCURY1_MED_TRANS, WHITE1_MED_TRANS, MERCURY1_MED_TRANS);
+ profile_color[TEMP_TEXT] = COLOR(GOVERNORBAY2, BLACK1_LOW_TRANS, GOVERNORBAY2);
+ profile_color[TEMP_PLOT] = COLOR(ROYALBLUE2_LOW_TRANS, BLACK1_LOW_TRANS, ROYALBLUE2_LOW_TRANS);
+ profile_color[SAC_DEFAULT] = COLOR(WHITE1, BLACK1_LOW_TRANS, FORESTGREEN1);
+ profile_color[BOUNDING_BOX] = COLOR(WHITE1, BLACK1_LOW_TRANS, TUNDORA1_MED_TRANS);
+ profile_color[PRESSURE_TEXT] = COLOR(KILLARNEY1, BLACK1_LOW_TRANS, KILLARNEY1);
+ profile_color[BACKGROUND] = COLOR(SPRINGWOOD1, BLACK1_LOW_TRANS, SPRINGWOOD1);
+ profile_color[CEILING_SHALLOW] = COLOR(REDORANGE1_HIGH_TRANS, BLACK1_HIGH_TRANS, REDORANGE1_HIGH_TRANS);
+ profile_color[CEILING_DEEP] = COLOR(RED1_MED_TRANS, BLACK1_HIGH_TRANS, RED1_MED_TRANS);
+ profile_color[CALC_CEILING_SHALLOW] = COLOR(FUNGREEN1_HIGH_TRANS, BLACK1_HIGH_TRANS, FUNGREEN1_HIGH_TRANS);
+ profile_color[CALC_CEILING_DEEP] = COLOR(APPLE1_HIGH_TRANS, BLACK1_HIGH_TRANS, APPLE1_HIGH_TRANS);
+
+}
+#undef COLOR
+
+struct text_render_options{
+ double size;
+ color_indice_t color;
+ double hpos, vpos;
+};
+
+extern struct ev_select *ev_namelist;
+extern int evn_allocated;
+extern int evn_used;
+
+ProfileGraphicsView::ProfileGraphicsView(QWidget* parent) : QGraphicsView(parent), toolTip(0) , dive(0)
+{
+ gc.printer = false;
+ setScene(new QGraphicsScene());
+ setBackgroundBrush(QColor("#F3F3E6"));
+ scene()->installEventFilter(this);
+
+ setRenderHint(QPainter::Antialiasing);
+ setRenderHint(QPainter::HighQualityAntialiasing);
+ setRenderHint(QPainter::SmoothPixmapTransform);
+
+ defaultPen.setJoinStyle(Qt::RoundJoin);
+ defaultPen.setCapStyle(Qt::RoundCap);
+ defaultPen.setWidth(2);
+ defaultPen.setCosmetic(true);
+
+ setHorizontalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
+ setVerticalScrollBarPolicy ( Qt::ScrollBarAlwaysOff );
+
+ fill_profile_color();
+}
+
+void ProfileGraphicsView::wheelEvent(QWheelEvent* event)
+{
+ if (!toolTip)
+ return;
+
+ setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
+
+ // Scale the view / do the zoom
+ QPoint toolTipPos = mapFromScene(toolTip->pos());
+ double scaleFactor = 1.15;
+ if(event->delta() > 0 && zoomLevel <= 10) {
+ scale(scaleFactor, scaleFactor);
+ zoomLevel++;
+ } else if (zoomLevel >= 0) {
+ // Zooming out
+ scale(1.0 / scaleFactor, 1.0 / scaleFactor);
+ zoomLevel--;
+ }
+ toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y());
+}
+
+void ProfileGraphicsView::mouseMoveEvent(QMouseEvent* event)
+{
+ if (!toolTip)
+ return;
+
+ toolTip->refresh(&gc, mapToScene(event->pos()));
+
+ QPoint toolTipPos = mapFromScene(toolTip->pos());
+
+ double dx = sceneRect().x();
+ double dy = sceneRect().y();
+
+ ensureVisible(event->pos().x() + dx, event->pos().y() + dy, 1, 1);
+
+ toolTip->setPos(mapToScene(toolTipPos).x(), mapToScene(toolTipPos).y());
+
+ if (zoomLevel < 0)
+ QGraphicsView::mouseMoveEvent(event);
+}
+
+bool ProfileGraphicsView::eventFilter(QObject* obj, QEvent* event)
+{
+ // This will "Eat" the default tooltip behavior.
+ if (event->type() == QEvent::GraphicsSceneHelp) {
+ event->ignore();
+ return true;
+ }
+ return QGraphicsView::eventFilter(obj, event);
+}
+
+static void plot_set_scale(scale_mode_t scale)
+{
+ switch (scale) {
+ default:
+ case SC_SCREEN:
+ plot_scale = SCALE_SCREEN;
+ break;
+ case SC_PRINT:
+ plot_scale = SCALE_PRINT;
+ break;
+ }
+}
+
+void ProfileGraphicsView::showEvent(QShowEvent* event)
+{
+ // Program just opened,
+ // but the dive was not ploted.
+ // force a replot by modifying the dive
+ // hold by the view, and issuing a plot.
+ if (dive){
+ dive = 0;
+ plot(get_dive(selected_dive));
+ }
+}
+
+void ProfileGraphicsView::clear()
+{
+ scene()->clear();
+ resetTransform();
+ zoomLevel = 0;
+ toolTip = 0;
+}
+
+void ProfileGraphicsView::plot(struct dive *d)
+{
+ if (dive == d)
+ return;
+
+ clear();
+ dive = d;
+
+ if(!isVisible() || !dive){
+ return;
+ }
+
+ scene()->setSceneRect(0,0, viewport()->width()-50, viewport()->height()-50);
+
+ QSettings s;
+ s.beginGroup("ProfileMap");
+ QPointF toolTipPos = s.value("tooltip_position", QPointF(0,0)).toPointF();
+ s.endGroup();
+
+ toolTip = new ToolTipItem();
+ toolTip->setPos(toolTipPos);
+
+ scene()->addItem(toolTip);
+
+ struct divecomputer *dc = &dive->dc;
+
+
+ // Fix this for printing / screen later.
+ // plot_set_scale(scale_mode_t);
+
+ if (!dc->samples) {
+ static struct sample fake[4];
+ static struct divecomputer fakedc;
+ fakedc = dive->dc;
+ fakedc.sample = fake;
+ fakedc.samples = 4;
+
+ /* The dive has no samples, so create a few fake ones. This assumes an
+ ascent/descent rate of 9 m/min, which is just below the limit for FAST. */
+ int duration = dive->dc.duration.seconds;
+ int maxdepth = dive->dc.maxdepth.mm;
+ int asc_desc_time = dive->dc.maxdepth.mm*60/9000;
+ if (asc_desc_time * 2 >= duration)
+ asc_desc_time = duration / 2;
+ fake[1].time.seconds = asc_desc_time;
+ fake[1].depth.mm = maxdepth;
+ fake[2].time.seconds = duration - asc_desc_time;
+ fake[2].depth.mm = maxdepth;
+ fake[3].time.seconds = duration * 1.00;
+ fakedc.events = dc->events;
+ dc = &fakedc;
+ }
+
+ /*
+ * Set up limits that are independent of
+ * the dive computer
+ */
+ calculate_max_limits(dive, dc, &gc);
+
+ QRectF profile_grid_area = scene()->sceneRect();
+ gc.maxx = (profile_grid_area.width() - 2 * profile_grid_area.x());
+ gc.maxy = (profile_grid_area.height() - 2 * profile_grid_area.y());
+
+ dc = select_dc(dc);
+
+ /* This is per-dive-computer. Right now we just do the first one */
+ gc.pi = *create_plot_info(dive, dc, &gc);
+
+ /* Depth profile */
+ plot_depth_profile();
+
+ plot_events(dc);
+
+ /* Temperature profile */
+ plot_temperature_profile();
+
+ /* Cylinder pressure plot */
+ plot_cylinder_pressure(dive, dc);
+
+ /* Text on top of all graphs.. */
+ plot_temperature_text();
+
+ plot_depth_text();
+
+ plot_cylinder_pressure_text();
+
+ plot_deco_text();
+
+ /* Bounding box */
+ QPen pen = defaultPen;
+ pen.setColor(profile_color[TIME_GRID].at(0));
+ QGraphicsRectItem *rect = new QGraphicsRectItem(profile_grid_area);
+ rect->setPen(pen);
+ scene()->addItem(rect);
+
+ /* Put the dive computer name in the lower left corner */
+ QString nick(get_dc_nickname(dc->model, dc->deviceid));
+ if (nick.isEmpty())
+ nick = QString(dc->model);
+
+ if (nick.isEmpty())
+ nick = tr("unknown divecomputer");
+
+ gc.leftx = 0; gc.rightx = 1.0;
+ gc.topy = 0; gc.bottomy = 1.0;
+
+ text_render_options_t computer = {DC_TEXT_SIZE, TIME_TEXT, LEFT, MIDDLE};
+ diveComputer = plot_text(&computer, QPointF(gc.leftx, gc.bottomy), nick);
+ // The Time ruler should be right after the DiveComputer:
+ timeMarkers->setPos(0, diveComputer->y());
+
+ if (PP_GRAPHS_ENABLED) {
+ plot_pp_gas_profile();
+ plot_pp_text();
+ }
+
+
+ /* now shift the translation back by half the margin;
+ * this way we can draw the vertical scales on both sides */
+ //cairo_translate(gc->cr, -drawing_area->x / 2.0, 0);
+
+ //gc->maxx += drawing_area->x;
+ //gc->leftx = -(drawing_area->x / drawing_area->width) / 2.0;
+ //gc->rightx = 1.0 - gc->leftx;
+
+ plot_depth_scale();
+
+#if 0
+ if (gc->printer) {
+ free(pi->entry);
+ last_pi_entry = pi->entry = NULL;
+ pi->nr = 0;
+ }
+#endif
+
+ QRectF r = scene()->itemsBoundingRect();
+ scene()->setSceneRect(r.x() - 15, r.y() -15, r.width() + 30, r.height() + 30);
+ if(zoomLevel == 0){
+ fitInView(sceneRect());
+ }
+}
+
+void ProfileGraphicsView::plot_depth_scale()
+{
+ int i, maxdepth, marker;
+ static text_render_options_t tro = {DEPTH_TEXT_SIZE, SAMPLE_DEEP, RIGHT, MIDDLE};
+
+ /* Depth markers: every 30 ft or 10 m*/
+ maxdepth = get_maxdepth(&gc.pi);
+ gc.topy = 0; gc.bottomy = maxdepth;
+
+ switch (prefs.units.length) {
+ case units::METERS: marker = 10000; break;
+ case units::FEET: marker = 9144; break; /* 30 ft */
+ }
+
+ QColor c(profile_color[DEPTH_GRID].first());
+
+ /* don't write depth labels all the way to the bottom as
+ * there may be other graphs below the depth plot (like
+ * partial pressure graphs) where this would look out
+ * of place - so we only make sure that we print the next
+ * marker below the actual maxdepth of the dive */
+ depthMarkers = new QGraphicsRectItem();
+ for (i = marker; i <= gc.pi.maxdepth + marker; i += marker) {
+ double d = get_depth_units(i, NULL, NULL);
+ plot_text(&tro, QPointF(-0.002, i), QString::number(d), depthMarkers);
+ }
+ scene()->addItem(depthMarkers);
+ depthMarkers->setPos(depthMarkers->pos().x() - 10, 0);
+}
+
+void ProfileGraphicsView::plot_pp_text()
+{
+ double pp, dpp, m;
+ int hpos;
+ static text_render_options_t tro = {PP_TEXT_SIZE, PP_LINES, LEFT, MIDDLE};
+
+ setup_pp_limits(&gc);
+ pp = floor(gc.pi.maxpp * 10.0) / 10.0 + 0.2;
+ dpp = pp > 4 ? 1.0 : 0.5;
+ hpos = gc.pi.entry[gc.pi.nr - 1].sec;
+ QColor c = profile_color[PP_LINES].first();
+
+ for (m = 0.0; m <= pp; m += dpp) {
+ QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, m), SCALEGC(hpos, m));
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ plot_text(&tro, QPointF(hpos + 30, m), QString::number(m));
+ }
+}
+
+void ProfileGraphicsView::plot_pp_gas_profile()
+{
+ int i;
+ struct plot_data *entry;
+ struct plot_info *pi = &gc.pi;
+
+ setup_pp_limits(&gc);
+ QColor c;
+ QPointF from, to;
+ //if (prefs.pp_graphs.pn2) {
+ c = profile_color[PN2].first();
+ entry = pi->entry;
+ from = QPointF(SCALEGC(entry->sec, entry->pn2));
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ if (entry->pn2 < prefs.pp_graphs.pn2_threshold){
+ to = QPointF(SCALEGC(entry->sec, entry->pn2));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(entry->sec, entry->pn2));
+ }
+ }
+
+ c = profile_color[PN2_ALERT].first();
+ entry = pi->entry;
+ from = QPointF(SCALEGC(entry->sec, entry->pn2));
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ if (entry->pn2 >= prefs.pp_graphs.pn2_threshold){
+ to = QPointF(SCALEGC(entry->sec, entry->pn2));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(entry->sec, entry->pn2));
+ }
+ }
+ //}
+
+ //if (prefs.pp_graphs.phe) {
+ c = profile_color[PHE].first();
+ entry = pi->entry;
+
+ from = QPointF(SCALEGC(entry->sec, entry->phe));
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ if (entry->phe < prefs.pp_graphs.phe_threshold){
+ to = QPointF(SCALEGC(entry->sec, entry->phe));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(entry->sec, entry->phe));
+ }
+ }
+
+ c = profile_color[PHE_ALERT].first();
+ entry = pi->entry;
+ from = QPointF(SCALEGC(entry->sec, entry->phe));
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ if (entry->phe >= prefs.pp_graphs.phe_threshold){
+ to = QPointF(SCALEGC(entry->sec, entry->phe));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(entry->sec, entry->phe));
+ }
+ }
+ //}
+ //if (prefs.pp_graphs.po2) {
+ c = profile_color[PO2].first();
+ entry = pi->entry;
+ from = QPointF(SCALEGC(entry->sec, entry->po2));
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ if (entry->po2 < prefs.pp_graphs.po2_threshold){
+ to = QPointF(SCALEGC(entry->sec, entry->po2));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(entry->sec, entry->po2));
+ }
+ }
+
+ c = profile_color[PO2_ALERT].first();
+ entry = pi->entry;
+ from = QPointF(SCALEGC(entry->sec, entry->po2));
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ if (entry->po2 >= prefs.pp_graphs.po2_threshold){
+ to = QPointF(SCALEGC(entry->sec, entry->po2));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ item->setPen(QPen(c));
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(entry->sec, entry->po2));
+ }
+ }
+ //}
+}
+
+void ProfileGraphicsView::plot_deco_text()
+{
+ if (prefs.profile_calc_ceiling) {
+ float x = gc.leftx + (gc.rightx - gc.leftx) / 2;
+ float y = gc.topy = 1.0;
+ static text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, CENTER, -0.2};
+ gc.bottomy = 0.0;
+ plot_text(&tro, QPointF(x, y), QString("GF %1/%2").arg(prefs.gflow * 100).arg(prefs.gfhigh * 100));
+ }
+}
+
+void ProfileGraphicsView::plot_cylinder_pressure_text()
+{
+ int i;
+ int mbar, cyl;
+ int seen_cyl[MAX_CYLINDERS] = { FALSE, };
+ int last_pressure[MAX_CYLINDERS] = { 0, };
+ int last_time[MAX_CYLINDERS] = { 0, };
+ struct plot_data *entry;
+ struct plot_info *pi = &gc.pi;
+
+ if (!get_cylinder_pressure_range(&gc))
+ return;
+
+ cyl = -1;
+ for (i = 0; i < pi->nr; i++) {
+ entry = pi->entry + i;
+ mbar = GET_PRESSURE(entry);
+
+ if (!mbar)
+ continue;
+ if (cyl != entry->cylinderindex) {
+ cyl = entry->cylinderindex;
+ if (!seen_cyl[cyl]) {
+ plot_pressure_value(mbar, entry->sec, LEFT, BOTTOM);
+ seen_cyl[cyl] = TRUE;
+ }
+ }
+ last_pressure[cyl] = mbar;
+ last_time[cyl] = entry->sec;
+ }
+
+ for (cyl = 0; cyl < MAX_CYLINDERS; cyl++) {
+ if (last_time[cyl]) {
+ plot_pressure_value(last_pressure[cyl], last_time[cyl], CENTER, TOP);
+ }
+ }
+}
+
+void ProfileGraphicsView::plot_pressure_value(int mbar, int sec, double xalign, double yalign)
+{
+ int pressure;
+ const char *unit;
+
+ pressure = get_pressure_units(mbar, &unit);
+ static text_render_options_t tro = {PRESSURE_TEXT_SIZE, PRESSURE_TEXT, xalign, yalign};
+ plot_text(&tro, QPointF(sec, mbar), QString("%1 %2").arg(pressure).arg(unit));
+}
+
+void ProfileGraphicsView::plot_depth_text()
+{
+ int maxtime, maxdepth;
+
+ /* Get plot scaling limits */
+ maxtime = get_maxtime(&gc.pi);
+ maxdepth = get_maxdepth(&gc.pi);
+
+ gc.leftx = 0; gc.rightx = maxtime;
+ gc.topy = 0; gc.bottomy = maxdepth;
+
+ plot_text_samples();
+}
+
+void ProfileGraphicsView::plot_text_samples()
+{
+ static text_render_options_t deep = {14, SAMPLE_DEEP, CENTER, TOP};
+ static text_render_options_t shallow = {14, SAMPLE_SHALLOW, CENTER, BOTTOM};
+ int i;
+ int last = -1;
+
+ struct plot_info* pi = &gc.pi;
+
+ for (i = 0; i < pi->nr; i++) {
+ struct plot_data *entry = pi->entry + i;
+
+ if (entry->depth < 2000)
+ continue;
+
+ if ((entry == entry->max[2]) && entry->depth / 100 != last) {
+ plot_depth_sample(entry, &deep);
+ last = entry->depth / 100;
+ }
+
+ if ((entry == entry->min[2]) && entry->depth / 100 != last) {
+ plot_depth_sample(entry, &shallow);
+ last = entry->depth / 100;
+ }
+
+ if (entry->depth != last)
+ last = -1;
+ }
+}
+
+void ProfileGraphicsView::plot_depth_sample(struct plot_data *entry,text_render_options_t *tro)
+{
+ int sec = entry->sec, decimals;
+ double d;
+
+ d = get_depth_units(entry->depth, &decimals, NULL);
+
+ plot_text(tro, QPointF(sec, entry->depth), QString("%1").arg(d, 0, 'f', 1));
+}
+
+
+void ProfileGraphicsView::plot_temperature_text()
+{
+ int i;
+ int last = -300, sec = 0;
+ int last_temperature = 0, last_printed_temp = 0;
+ plot_info *pi = &gc.pi;
+
+ if (!setup_temperature_limits(&gc))
+ return;
+
+ for (i = 0; i < pi->nr; i++) {
+ struct plot_data *entry = pi->entry+i;
+ int mkelvin = entry->temperature;
+ sec = entry->sec;
+
+ if (!mkelvin)
+ continue;
+ last_temperature = mkelvin;
+ /* don't print a temperature
+ * if it's been less than 5min and less than a 2K change OR
+ * if it's been less than 2min OR if the change from the
+ * last print is less than .4K (and therefore less than 1F) */
+ if (((sec < last + 300) && (abs(mkelvin - last_printed_temp) < 2000)) ||
+ (sec < last + 120) ||
+ (abs(mkelvin - last_printed_temp) < 400))
+ continue;
+ last = sec;
+ if (mkelvin > 200000)
+ plot_single_temp_text(sec,mkelvin);
+ last_printed_temp = mkelvin;
+ }
+ /* it would be nice to print the end temperature, if it's
+ * different or if the last temperature print has been more
+ * than a quarter of the dive back */
+ if (last_temperature > 200000 &&
+ ((abs(last_temperature - last_printed_temp) > 500) || ((double)last / (double)sec < 0.75)))
+ plot_single_temp_text(sec, last_temperature);
+}
+
+void ProfileGraphicsView::plot_single_temp_text(int sec, int mkelvin)
+{
+ double deg;
+ const char *unit;
+ static text_render_options_t tro = {TEMP_TEXT_SIZE, TEMP_TEXT, LEFT, TOP};
+ deg = get_temp_units(mkelvin, &unit);
+ plot_text(&tro, QPointF(sec, mkelvin), QString("%1%2").arg(deg, 0, 'f', 1).arg(unit)); //"%.2g%s"
+}
+
+void ProfileGraphicsView::plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc)
+{
+ int i;
+ int last = -1, last_index = -1;
+ int lift_pen = FALSE;
+ int first_plot = TRUE;
+ int sac = 0;
+ struct plot_data *last_entry = NULL;
+
+ if (!get_cylinder_pressure_range(&gc))
+ return;
+
+ QPointF from, to;
+ for (i = 0; i < gc.pi.nr; i++) {
+ int mbar;
+ struct plot_data *entry = gc.pi.entry + i;
+
+ mbar = GET_PRESSURE(entry);
+ if (entry->cylinderindex != last_index) {
+ lift_pen = TRUE;
+ last_entry = NULL;
+ }
+ if (!mbar) {
+ lift_pen = TRUE;
+ continue;
+ }
+ if (!last_entry) {
+ last = i;
+ last_entry = entry;
+ sac = get_local_sac(entry, gc.pi.entry + i + 1, dive);
+ } else {
+ int j;
+ sac = 0;
+ for (j = last; j < i; j++)
+ sac += get_local_sac(gc.pi.entry + j, gc.pi.entry + j + 1, dive);
+ sac /= (i - last);
+ if (entry->sec - last_entry->sec >= SAC_WINDOW) {
+ last++;
+ last_entry = gc.pi.entry + last;
+ }
+ }
+
+ QColor c = get_sac_color(sac, dive->sac);
+
+ if (lift_pen) {
+ if (!first_plot && entry->cylinderindex == last_index) {
+ /* if we have a previous event from the same tank,
+ * draw at least a short line */
+ int prev_pr;
+ prev_pr = GET_PRESSURE(entry - 1);
+
+ QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC((entry-1)->sec, prev_pr), SCALEGC(entry->sec, mbar));
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ } else {
+ first_plot = FALSE;
+ from = QPointF(SCALEGC(entry->sec, mbar));
+ }
+ lift_pen = FALSE;
+ } else {
+ to = QPointF(SCALEGC(entry->sec, mbar));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ }
+
+
+ from = QPointF(SCALEGC(entry->sec, mbar));
+ last_index = entry->cylinderindex;
+ }
+}
+
+
+/* set the color for the pressure plot according to temporary sac rate
+ * as compared to avg_sac; the calculation simply maps the delta between
+ * sac and avg_sac to indexes 0 .. (SAC_COLORS - 1) with everything
+ * more than 6000 ml/min below avg_sac mapped to 0 */
+QColor ProfileGraphicsView::get_sac_color(int sac, int avg_sac)
+{
+ int sac_index = 0;
+ int delta = sac - avg_sac + 7000;
+
+ if (!gc.printer) {
+ sac_index = delta / 2000;
+ if (sac_index < 0)
+ sac_index = 0;
+ if (sac_index > SAC_COLORS - 1)
+ sac_index = SAC_COLORS - 1;
+ return profile_color[ (color_indice_t) (SAC_COLORS_START_IDX + sac_index)].first();
+ }
+ return profile_color[SAC_DEFAULT].first();
+}
+
+void ProfileGraphicsView::plot_events(struct divecomputer *dc)
+{
+ struct event *event = dc->events;
+
+// if (gc->printer){
+// return;
+// }
+
+ while (event) {
+ plot_one_event(event);
+ event = event->next;
+ }
+}
+
+void ProfileGraphicsView::plot_one_event(struct event *ev)
+{
+ int i, depth = 0;
+ struct plot_info *pi = &gc.pi;
+
+ /* is plotting of this event disabled? */
+ if (ev->name) {
+ for (i = 0; i < evn_used; i++) {
+ if (! strcmp(ev->name, ev_namelist[i].ev_name)) {
+ if (ev_namelist[i].plot_ev)
+ break;
+ else
+ return;
+ }
+ }
+ }
+
+ if (ev->time.seconds < 30 && !strcmp(ev->name, "gaschange"))
+ /* a gas change in the first 30 seconds is the way of some dive computers
+ * to tell us the gas that is used; let's not plot a marker for that */
+ return;
+
+ for (i = 0; i < pi->nr; i++) {
+ struct plot_data *data = pi->entry + i;
+ if (ev->time.seconds < data->sec)
+ break;
+ depth = data->depth;
+ }
+
+ /* draw a little triangular marker and attach tooltip */
+
+ int x = SCALEXGC(ev->time.seconds);
+ int y = SCALEYGC(depth);
+
+ EventItem *item = new EventItem();
+ item->setPos(x, y);
+ scene()->addItem(item);
+
+ /* we display the event on screen - so translate */
+ QString name = tr(ev->name);
+ if (ev->value) {
+ if (ev->name && name == "gaschange") {
+ unsigned int he = ev->value >> 16;
+ unsigned int o2 = ev->value & 0xffff;
+
+ name += ": ";
+ name += (he) ? QString("%1/%2").arg(o2, he)
+ : (o2 == 21) ? name += tr("air")
+ : QString("%1% %2").arg(o2).arg("O" UTF8_SUBSCRIPT_2);
+
+ } else if (ev->name && !strcmp(ev->name, "SP change")) {
+ name += QString(":%1").arg((double) ev->value / 1000);
+ } else {
+ name += QString(":%1").arg(ev->value);
+ }
+ } else if (ev->name && name == "SP change") {
+ name += tr("Bailing out to OC");
+ } else {
+ name += ev->flags == SAMPLE_FLAGS_BEGIN ? tr("Starts with space!"," begin") :
+ ev->flags == SAMPLE_FLAGS_END ? tr("Starts with space!", " end") : "";
+ }
+
+ //item->setToolTipController(toolTip);
+ //item->addToolTip(name);
+ item->setToolTip(name);
+}
+
+void ProfileGraphicsView::plot_depth_profile()
+{
+ int i, incr;
+ int sec, depth;
+ struct plot_data *entry;
+ int maxtime, maxdepth, marker, maxline;
+ int increments[8] = { 10, 20, 30, 60, 5*60, 10*60, 15*60, 30*60 };
+
+ /* Get plot scaling limits */
+ maxtime = get_maxtime(&gc.pi);
+ maxdepth = get_maxdepth(&gc.pi);
+
+ gc.maxtime = maxtime;
+
+ /* Time markers: at most every 10 seconds, but no more than 12 markers.
+ * We start out with 10 seconds and increment up to 30 minutes,
+ * depending on the dive time.
+ * This allows for 6h dives - enough (I hope) for even the craziest
+ * divers - but just in case, for those 8h depth-record-breaking dives,
+ * we double the interval if this still doesn't get us to 12 or fewer
+ * time markers */
+ i = 0;
+ while (maxtime / increments[i] > 12 && i < 7)
+ i++;
+ incr = increments[i];
+ while (maxtime / incr > 12)
+ incr *= 2;
+
+ gc.leftx = 0; gc.rightx = maxtime;
+ gc.topy = 0; gc.bottomy = 1.0;
+
+ last_gc = gc;
+
+ QColor c = profile_color[TIME_GRID].at(0);
+ for (i = incr; i < maxtime; i += incr) {
+ QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(i, 0), SCALEGC(i, 1));
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ }
+
+ timeMarkers = new QGraphicsRectItem();
+ /* now the text on the time markers */
+ struct text_render_options tro = {DEPTH_TEXT_SIZE, TIME_TEXT, CENTER, TOP};
+ if (maxtime < 600) {
+ /* Be a bit more verbose with shorter dives */
+ for (i = incr; i < maxtime; i += incr)
+ plot_text(&tro, QPointF(i, 0), QString("%1:%2").arg(i/60).arg(i%60), timeMarkers);
+ } else {
+ /* Only render the time on every second marker for normal dives */
+ for (i = incr; i < maxtime; i += 2 * incr)
+ plot_text(&tro, QPointF(i, 0), QString("%1").arg(QString::number(i/60)), timeMarkers);
+ }
+ timeMarkers->setPos(0,0);
+ scene()->addItem(timeMarkers);
+
+ /* Depth markers: every 30 ft or 10 m*/
+ gc.leftx = 0; gc.rightx = 1.0;
+ gc.topy = 0; gc.bottomy = maxdepth;
+ switch (prefs.units.length) {
+ case units::METERS:
+ marker = 10000;
+ break;
+ case units::FEET:
+ marker = 9144;
+ break; /* 30 ft */
+ }
+ maxline = MAX(gc.pi.maxdepth + marker, maxdepth * 2 / 3);
+
+ c = profile_color[DEPTH_GRID].at(0);
+
+ for (i = marker; i < maxline; i += marker) {
+ QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, i), SCALEGC(1, i));
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ }
+
+ gc.leftx = 0; gc.rightx = maxtime;
+ c = profile_color[MEAN_DEPTH].at(0);
+
+ /* Show mean depth */
+ if (! gc.printer) {
+ QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(0, gc.pi.meandepth),
+ SCALEGC(gc.pi.entry[gc.pi.nr - 1].sec, gc.pi.meandepth));
+ QPen pen(defaultPen);
+ pen.setColor(c);
+ item->setPen(pen);
+ scene()->addItem(item);
+ }
+
+#if 0
+ /*
+ * These are good for debugging text placement etc,
+ * but not for actual display..
+ */
+ if (0) {
+ plot_smoothed_profile(gc, pi);
+ plot_minmax_profile(gc, pi);
+ }
+#endif
+
+ /* Do the depth profile for the neat fill */
+ gc.topy = 0; gc.bottomy = maxdepth;
+
+ entry = gc.pi.entry;
+
+ QPolygonF p;
+ QLinearGradient pat(0.0,0.0,0.0,scene()->height());
+ QGraphicsPolygonItem *neatFill = NULL;
+
+ p.append(QPointF(SCALEGC(0, 0)));
+ for (i = 0; i < gc.pi.nr; i++, entry++)
+ p.append(QPointF(SCALEGC(entry->sec, entry->depth)));
+
+ /* Show any ceiling we may have encountered */
+ for (i = gc.pi.nr - 1; i >= 0; i--, entry--) {
+ if (entry->ndl) {
+ /* non-zero NDL implies this is a safety stop, no ceiling */
+ p.append(QPointF(SCALEGC(entry->sec, 0)));
+ } else if (entry->stopdepth < entry->depth) {
+ p.append(QPointF(SCALEGC(entry->sec, entry->stopdepth)));
+ } else {
+ p.append(QPointF(SCALEGC(entry->sec, entry->depth)));
+ }
+ }
+ pat.setColorAt(1, profile_color[DEPTH_BOTTOM].first());
+ pat.setColorAt(0, profile_color[DEPTH_TOP].first());
+
+ neatFill = new QGraphicsPolygonItem();
+ neatFill->setPolygon(p);
+ neatFill->setBrush(QBrush(pat));
+ neatFill->setPen(QPen(QBrush(Qt::transparent),0));
+ scene()->addItem(neatFill);
+
+
+ /* if the user wants the deco ceiling more visible, do that here (this
+ * basically draws over the background that we had allowed to shine
+ * through so far) */
+ // TODO: port the prefs.profile_red_ceiling to QSettings
+
+ //if (prefs.profile_red_ceiling) {
+ p.clear();
+ pat.setColorAt(0, profile_color[CEILING_SHALLOW].first());
+ pat.setColorAt(1, profile_color[CEILING_DEEP].first());
+
+ entry = gc.pi.entry;
+ p.append(QPointF(SCALEGC(0, 0)));
+ for (i = 0; i < gc.pi.nr; i++, entry++) {
+ if (entry->ndl == 0 && entry->stopdepth) {
+ if (entry->ndl == 0 && entry->stopdepth < entry->depth) {
+ p.append(QPointF(SCALEGC(entry->sec, entry->stopdepth)));
+ } else {
+ p.append(QPointF(SCALEGC(entry->sec, entry->depth)));
+ }
+ } else {
+ p.append(QPointF(SCALEGC(entry->sec, 0)));
+ }
+ }
+
+ neatFill = new QGraphicsPolygonItem();
+ neatFill->setBrush(QBrush(pat));
+ neatFill->setPolygon(p);
+ neatFill->setPen(QPen(QBrush(Qt::NoBrush),0));
+ scene()->addItem(neatFill);
+ //}
+
+ /* finally, plot the calculated ceiling over all this */
+ // TODO: Port the profile_calc_ceiling to QSettings
+ // if (prefs.profile_calc_ceiling) {
+
+ pat.setColorAt(0, profile_color[CALC_CEILING_SHALLOW].first());
+ pat.setColorAt(1, profile_color[CALC_CEILING_DEEP].first());
+
+ entry = gc.pi.entry;
+ p.clear();
+ p.append(QPointF(SCALEGC(0, 0)));
+ for (i = 0; i < gc.pi.nr; i++, entry++) {
+ if (entry->ceiling)
+ p.append(QPointF(SCALEGC(entry->sec, entry->ceiling)));
+ else
+ p.append(QPointF(SCALEGC(entry->sec, 0)));
+ }
+ p.append(QPointF(SCALEGC((entry-1)->sec, 0)));
+ neatFill = new QGraphicsPolygonItem();
+ neatFill->setPolygon(p);
+ neatFill->setPen(QPen(QBrush(Qt::NoBrush),0));
+ neatFill->setBrush(pat);
+ scene()->addItem(neatFill);
+ //}
+ /* next show where we have been bad and crossed the dc's ceiling */
+ pat.setColorAt(0, profile_color[CEILING_SHALLOW].first());
+ pat.setColorAt(1, profile_color[CEILING_DEEP].first());
+
+ entry = gc.pi.entry;
+ p.clear();
+ p.append(QPointF(SCALEGC(0, 0)));
+ for (i = 0; i < gc.pi.nr; i++, entry++)
+ p.append(QPointF(SCALEGC(entry->sec, entry->depth)));
+
+ for (i = gc.pi.nr - 1; i >= 0; i--, entry--) {
+ if (entry->ndl == 0 && entry->stopdepth > entry->depth) {
+ p.append(QPointF(SCALEGC(entry->sec, entry->stopdepth)));
+ } else {
+ p.append(QPointF(SCALEGC(entry->sec, entry->depth)));
+ }
+ }
+
+ neatFill = new QGraphicsPolygonItem();
+ neatFill->setPolygon(p);
+ neatFill->setPen(QPen(QBrush(Qt::NoBrush),0));
+ neatFill->setBrush(QBrush(pat));
+ scene()->addItem(neatFill);
+
+ /* Now do it again for the velocity colors */
+ entry = gc.pi.entry;
+ for (i = 1; i < gc.pi.nr; i++) {
+ entry++;
+ sec = entry->sec;
+ /* we want to draw the segments in different colors
+ * representing the vertical velocity, so we need to
+ * chop this into short segments */
+ depth = entry->depth;
+ QGraphicsLineItem *item = new QGraphicsLineItem(SCALEGC(entry[-1].sec, entry[-1].depth), SCALEGC(sec, depth));
+ QPen pen(defaultPen);
+ pen.setColor(profile_color[ (color_indice_t) (VELOCITY_COLORS_START_IDX + entry->velocity)].first());
+ item->setPen(pen);
+ scene()->addItem(item);
+ }
+}
+
+QGraphicsSimpleTextItem *ProfileGraphicsView::plot_text(text_render_options_t *tro,const QPointF& pos, const QString& text, QGraphicsItem *parent)
+{
+ QFontMetrics fm(font());
+
+ double dx = tro->hpos * (fm.width(text));
+ double dy = tro->vpos * (fm.height());
+
+ QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text, parent);
+ QPointF point(SCALEGC(pos.x(), pos.y())); // This is neded because of the SCALE macro.
+
+ item->setPos(point.x() + dx, point.y() + dy);
+ item->setBrush(QBrush(profile_color[tro->color].first()));
+ item->setFlag(QGraphicsItem::ItemIgnoresTransformations);
+
+ if(!parent)
+ scene()->addItem(item);
+ return item;
+}
+
+void ProfileGraphicsView::resizeEvent(QResizeEvent *event)
+{
+ plot(dive);
+}
+
+void ProfileGraphicsView::plot_temperature_profile()
+{
+ int last = 0;
+
+ if (!setup_temperature_limits(&gc))
+ return;
+
+ QPointF from;
+ QPointF to;
+ QColor color = profile_color[TEMP_PLOT].first();
+
+ for (int i = 0; i < gc.pi.nr; i++) {
+ struct plot_data *entry = gc.pi.entry + i;
+ int mkelvin = entry->temperature;
+ int sec = entry->sec;
+ if (!mkelvin) {
+ if (!last)
+ continue;
+ mkelvin = last;
+ }
+ if (last) {
+ to = QPointF(SCALEGC(sec, mkelvin));
+ QGraphicsLineItem *item = new QGraphicsLineItem(from.x(), from.y(), to.x(), to.y());
+ QPen pen(defaultPen);
+ pen.setColor(color);
+ item->setPen(pen);
+ scene()->addItem(item);
+ from = to;
+ }
+ else{
+ from = QPointF(SCALEGC(sec, mkelvin));
+ }
+ last = mkelvin;
+ }
+}
+
+void ToolTipItem::addToolTip(const QString& toolTip, const QIcon& icon)
+{
+ QGraphicsPixmapItem *iconItem = 0;
+ double yValue = title->boundingRect().height() + SPACING;
+ Q_FOREACH(ToolTip t, toolTips) {
+ yValue += t.second->boundingRect().height();
+ }
+ if (!icon.isNull()) {
+ iconItem = new QGraphicsPixmapItem(icon.pixmap(ICON_SMALL,ICON_SMALL), this);
+ iconItem->setPos(SPACING, yValue);
+ }
+
+ QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem(toolTip, this);
+ textItem->setPos(SPACING + ICON_SMALL + SPACING, yValue);
+ textItem->setBrush(QBrush(Qt::white));
+ textItem->setFlag(ItemIgnoresTransformations);
+ toolTips[toolTip] = qMakePair(iconItem, textItem);
+ expand();
+}
+
+void ToolTipItem::removeToolTip(const QString& toolTip)
+{
+ ToolTip toBeRemoved = toolTips[toolTip];
+ delete toBeRemoved.first;
+ delete toBeRemoved.second;
+ toolTips.remove(toolTip);
+
+ int toolTipIndex = 0;
+
+ // We removed a toolTip, let's move the others to the correct location
+ Q_FOREACH(ToolTip t, toolTips) {
+ double yValue = title->boundingRect().height() + SPACING + toolTipIndex * ICON_SMALL + SPACING;
+
+ // Icons can be null.
+ if (t.first)
+ t.first->setPos(SPACING, yValue);
+
+ t.second->setPos(SPACING + ICON_SMALL + SPACING, yValue);
+ toolTipIndex++;
+ }
+
+ expand();
+}
+
+void ToolTipItem::refresh(struct graphics_context *gc, QPointF pos)
+{
+ clear();
+ int time = (pos.x() * gc->maxtime) / scene()->sceneRect().width();
+ char buffer[500];
+ get_plot_details(gc, time, buffer, 500);
+ addToolTip(QString(buffer));
+
+ QList<QGraphicsItem*> items = scene()->items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder, transform());
+ Q_FOREACH(QGraphicsItem *item, items) {
+ if (!item->toolTip().isEmpty())
+ addToolTip(item->toolTip());
+ }
+
+}
+
+void ToolTipItem::clear()
+{
+ Q_FOREACH(ToolTip t, toolTips) {
+ delete t.first;
+ delete t.second;
+ }
+ toolTips.clear();
+ expand();
+}
+
+void ToolTipItem::setRect(const QRectF& r)
+{
+
+ // qDeleteAll(childItems());
+ delete background;
+
+ rectangle = r;
+ setBrush(QBrush(Qt::white));
+ setPen(QPen(Qt::black, 0.5));
+
+ // Creates a 2pixels border
+ QPainterPath border;
+ border.addRoundedRect(-4, -4, rectangle.width() + 8, rectangle.height() + 10, 3, 3);
+ border.addRoundedRect(-1, -1, rectangle.width() + 3, rectangle.height() + 4, 3, 3);
+ setPath(border);
+
+ QPainterPath bg;
+ bg.addRoundedRect(-1, -1, rectangle.width() + 3, rectangle.height() + 4, 3, 3);
+
+ QColor c = QColor(Qt::black);
+ c.setAlpha(155);
+
+ QGraphicsPathItem *b = new QGraphicsPathItem(bg, this);
+ b->setFlag(ItemStacksBehindParent);
+ b->setFlags(ItemIgnoresTransformations);
+ b->setBrush(c);
+ b->setPen(QPen(QBrush(Qt::transparent), 0));
+ b->setZValue(-10);
+ background = b;
+
+ updateTitlePosition();
+}
+
+
+void ToolTipItem::collapse()
+{
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "rect");
+ animation->setDuration(100);
+ animation->setStartValue(boundingRect());
+ animation->setEndValue(QRect(0, 0, ICON_SMALL, ICON_SMALL));
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+}
+
+void ToolTipItem::expand()
+{
+
+ if (!title){
+ return;
+ }
+
+ QRectF nextRectangle;
+ double width = 0, height = title->boundingRect().height() + SPACING;
+ Q_FOREACH(ToolTip t, toolTips) {
+ if (t.second->boundingRect().width() > width)
+ width = t.second->boundingRect().width();
+ height += t.second->boundingRect().height();
+ }
+ /* Left padding, Icon Size, space, right padding */
+ width += SPACING + ICON_SMALL + SPACING + SPACING;
+
+ if (width < title->boundingRect().width() + SPACING*2)
+ width = title->boundingRect().width() + SPACING*2;
+
+ if(height < ICON_SMALL)
+ height = ICON_SMALL;
+
+ nextRectangle.setWidth(width);
+ nextRectangle.setHeight(height);
+
+ QPropertyAnimation *animation = new QPropertyAnimation(this, "rect");
+ animation->setDuration(100);
+ animation->setStartValue(rectangle);
+ animation->setEndValue(nextRectangle);
+ animation->start(QAbstractAnimation::DeleteWhenStopped);
+
+}
+
+ToolTipItem::ToolTipItem(QGraphicsItem* parent): QGraphicsPathItem(parent), background(0)
+{
+ title = new QGraphicsSimpleTextItem(tr("Information"), this);
+ separator = new QGraphicsLineItem(this);
+
+ setFlag(ItemIgnoresTransformations);
+ setFlag(ItemIsMovable);
+
+ updateTitlePosition();
+ setZValue(99);
+}
+
+void ToolTipItem::updateTitlePosition()
+{
+ if (rectangle.width() < title->boundingRect().width() + SPACING*4) {
+ QRectF newRect = rectangle;
+ newRect.setWidth(title->boundingRect().width() + SPACING*4);
+ newRect.setHeight(newRect.height() ? newRect.height() : ICON_SMALL);
+ setRect(newRect);
+ }
+
+ title->setPos(boundingRect().width()/2 - title->boundingRect().width()/2 -1, 0);
+ title->setFlag(ItemIgnoresTransformations);
+ title->setPen(QPen(Qt::white, 1));
+ title->setBrush(Qt::white);
+
+ if (toolTips.size() > 0) {
+ double x1 = 0;
+ double y1 = title->pos().y() + SPACING/2 + title->boundingRect().height();
+ double x2 = boundingRect().width() - 10;
+ double y2 = y1;
+
+ separator->setLine(x1, y1, x2, y2);
+ separator->setFlag(ItemIgnoresTransformations);
+ separator->setPen(QPen(Qt::white));
+ separator->show();
+ } else {
+ separator->hide();
+ }
+}
+
+EventItem::EventItem(QGraphicsItem* parent): QGraphicsPolygonItem(parent)
+{
+ setFlag(ItemIgnoresTransformations);
+ setFlag(ItemIsFocusable);
+ setAcceptHoverEvents(true);
+
+ QPolygonF poly;
+ poly.push_back(QPointF(-8, 16));
+ poly.push_back(QPointF(8, 16));
+ poly.push_back(QPointF(0, 0));
+ poly.push_back(QPointF(-8, 16));
+
+ QPen defaultPen ;
+ defaultPen.setJoinStyle(Qt::RoundJoin);
+ defaultPen.setCapStyle(Qt::RoundCap);
+ defaultPen.setWidth(2);
+ defaultPen.setCosmetic(true);
+
+ QPen pen = defaultPen;
+ pen.setBrush(QBrush(profile_color[ALERT_BG].first()));
+
+ setPolygon(poly);
+ setBrush(QBrush(profile_color[ALERT_BG].first()));
+ setPen(pen);
+
+ QGraphicsLineItem *line = new QGraphicsLineItem(0,5,0,10, this);
+ line->setPen(QPen(Qt::black, 2));
+
+ QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(-1, 12, 2,2, this);
+ ball->setBrush(QBrush(Qt::black));
+
+}
diff --git a/qt-ui/profilegraphics.h b/qt-ui/profilegraphics.h
new file mode 100644
index 000000000..453b8cf03
--- /dev/null
+++ b/qt-ui/profilegraphics.h
@@ -0,0 +1,110 @@
+#ifndef PROFILEGRAPHICS_H
+#define PROFILEGRAPHICS_H
+
+#include "../display.h"
+#include <QGraphicsView>
+#include <QGraphicsItem>
+#include <QIcon>
+
+struct text_render_options;
+struct graphics_context;
+struct plot_info;
+typedef struct text_render_options text_render_options_t;
+
+/* To use a tooltip, simply ->setToolTip on the QGraphicsItem that you want
+ * or, if it's a "global" tooltip, set it on the mouseMoveEvent of the ProfileGraphicsView.
+ */
+class ToolTipItem :public QObject, public QGraphicsPathItem
+{
+ Q_OBJECT
+ void updateTitlePosition();
+ Q_PROPERTY(QRectF rect READ boundingRect WRITE setRect)
+
+public:
+ enum Status{COLLAPSED, EXPANDED};
+ enum {ICON_SMALL = 16, ICON_MEDIUM = 24, ICON_BIG = 32, SPACING=4};
+
+ explicit ToolTipItem(QGraphicsItem* parent = 0);
+
+ void collapse();
+ void expand();
+ void clear();
+ void addToolTip(const QString& toolTip, const QIcon& icon = QIcon());
+ void removeToolTip(const QString& toolTip);
+ void refresh(struct graphics_context* gc, QPointF pos);
+
+public Q_SLOTS:
+ void setRect(const QRectF& rect);
+
+private:
+ typedef QPair<QGraphicsPixmapItem*, QGraphicsSimpleTextItem*> ToolTip;
+ QMap<QString, ToolTip > toolTips;
+ QGraphicsPathItem *background;
+ QGraphicsLineItem *separator;
+ QGraphicsSimpleTextItem *title;
+
+ QRectF rectangle;
+};
+
+class EventItem : public QGraphicsPolygonItem
+{
+public:
+ explicit EventItem(QGraphicsItem* parent = 0);
+
+private:
+ ToolTipItem *controller;
+ QString text;
+ QIcon icon;
+};
+
+class ProfileGraphicsView : public QGraphicsView
+{
+Q_OBJECT
+public:
+ ProfileGraphicsView(QWidget* parent = 0);
+ void plot(struct dive *d);
+ bool eventFilter(QObject* obj, QEvent* event);
+ void clear();
+
+protected:
+ void resizeEvent(QResizeEvent *event);
+ void mouseMoveEvent(QMouseEvent* event);
+ void wheelEvent(QWheelEvent* event);
+ void showEvent(QShowEvent* event);
+
+private:
+ void plot_depth_profile();
+ QGraphicsSimpleTextItem* plot_text(text_render_options_t *tro, const QPointF& pos, const QString &text, QGraphicsItem *parent = 0);
+ void plot_events(struct divecomputer *dc);
+ void plot_one_event(struct event *event);
+ void plot_temperature_profile();
+ void plot_cylinder_pressure(struct dive *dive, struct divecomputer *dc);
+ void plot_temperature_text();
+ void plot_single_temp_text(int sec, int mkelvin);
+ void plot_depth_text();
+ void plot_text_samples();
+ void plot_depth_sample(struct plot_data *entry, text_render_options_t *tro);
+ void plot_cylinder_pressure_text();
+ void plot_pressure_value(int mbar, int sec, double xalign, double yalign);
+ void plot_deco_text();
+ void plot_pp_gas_profile();
+ void plot_pp_text();
+ void plot_depth_scale();
+
+ QColor get_sac_color(int sac, int avg_sac);
+
+ QPen defaultPen;
+ QBrush defaultBrush;
+ ToolTipItem *toolTip;
+ graphics_context gc;
+ struct dive *dive;
+ int zoomLevel;
+
+ // Top Level Items.
+ QGraphicsItem* profileGrid;
+ QGraphicsItem* timeMarkers;
+ QGraphicsItem* depthMarkers;
+ QGraphicsItem* diveComputer;
+};
+
+#endif
diff --git a/qt-ui/starwidget.cpp b/qt-ui/starwidget.cpp
new file mode 100644
index 000000000..4d1fa066c
--- /dev/null
+++ b/qt-ui/starwidget.cpp
@@ -0,0 +1,100 @@
+#include "starwidget.h"
+#include <QSvgRenderer>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QDebug>
+#include <QMouseEvent>
+
+QPixmap* StarWidget::activeStar = 0;
+QPixmap* StarWidget::inactiveStar = 0;
+
+QPixmap StarWidget::starActive()
+{
+ return (*activeStar);
+}
+
+QPixmap StarWidget::starInactive()
+{
+ return (*inactiveStar);
+}
+
+int StarWidget::currentStars() const
+{
+ return current;
+}
+
+void StarWidget::mouseReleaseEvent(QMouseEvent* event)
+{
+ int starClicked = event->pos().x() / IMG_SIZE + 1;
+ if (starClicked > TOTALSTARS)
+ starClicked = TOTALSTARS;
+
+ if (current == starClicked)
+ current -= 1;
+ else
+ current = starClicked;
+
+ update();
+}
+
+void StarWidget::paintEvent(QPaintEvent* event)
+{
+ QPainter p(this);
+
+ for(int i = 0; i < current; i++)
+ p.drawPixmap(i * IMG_SIZE + SPACING, 0, starActive());
+
+ for(int i = current; i < TOTALSTARS; i++)
+ p.drawPixmap(i * IMG_SIZE + SPACING, 0, starInactive());
+}
+
+void StarWidget::setCurrentStars(int value)
+{
+ current = value;
+ update();
+ Q_EMIT valueChanged(current);
+}
+
+StarWidget::StarWidget(QWidget* parent, Qt::WindowFlags f):
+ QWidget(parent, f),
+ current(0)
+{
+ if(!activeStar){
+ activeStar = new QPixmap();
+ QSvgRenderer render(QString(":star"));
+ QPixmap renderedStar(IMG_SIZE, IMG_SIZE);
+
+ renderedStar.fill(Qt::transparent);
+ QPainter painter(&renderedStar);
+
+ render.render(&painter, QRectF(0, 0, IMG_SIZE, IMG_SIZE));
+ (*activeStar) = renderedStar;
+ }
+ if(!inactiveStar){
+ inactiveStar = new QPixmap();
+ (*inactiveStar) = grayImage(activeStar);
+ }
+}
+
+QPixmap StarWidget::grayImage(QPixmap* coloredImg)
+{
+ QImage img = coloredImg->toImage();
+ for (int i = 0; i < img.width(); ++i) {
+ for (int j = 0; j < img.height(); ++j) {
+ QRgb rgb = img.pixel(i, j);
+ if (!rgb)
+ continue;
+
+ QColor c(rgb);
+ int gray = (c.red() + c.green() + c.blue()) / 3;
+ img.setPixel(i, j, qRgb(gray, gray, gray));
+ }
+ }
+
+ return QPixmap::fromImage(img);
+}
+
+QSize StarWidget::sizeHint() const
+{
+ return QSize(IMG_SIZE * TOTALSTARS + SPACING * (TOTALSTARS-1), IMG_SIZE);
+}
diff --git a/qt-ui/starwidget.h b/qt-ui/starwidget.h
new file mode 100644
index 000000000..d92be5a98
--- /dev/null
+++ b/qt-ui/starwidget.h
@@ -0,0 +1,37 @@
+#ifndef STARWIDGET_H
+#define STARWIDGET_H
+
+#include <QWidget>
+
+enum StarConfig {SPACING = 2, IMG_SIZE = 16, TOTALSTARS = 5};
+
+class StarWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit StarWidget(QWidget* parent = 0, Qt::WindowFlags f = 0);
+ int currentStars() const;
+
+ /*reimp*/ QSize sizeHint() const;
+
+ static QPixmap starActive();
+ static QPixmap starInactive();
+
+Q_SIGNALS:
+ void valueChanged(int stars);
+
+public Q_SLOTS:
+ void setCurrentStars(int value);
+
+protected:
+ /*reimp*/ void mouseReleaseEvent(QMouseEvent* );
+ /*reimp*/ void paintEvent(QPaintEvent* );
+
+private:
+ int current;
+ static QPixmap* activeStar;
+ static QPixmap* inactiveStar;
+ QPixmap grayImage(QPixmap *coloredImg);
+};
+
+#endif // STARWIDGET_H
diff --git a/star.svg b/star.svg
new file mode 100644
index 000000000..e4345eb48
--- /dev/null
+++ b/star.svg
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="32px"
+ height="32px"
+ id="svg15725"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="New document 4">
+ <defs
+ id="defs15727" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.197802"
+ inkscape:cx="16"
+ inkscape:cy="16.044652"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1884"
+ inkscape:window-height="1058"
+ inkscape:window-x="-5"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata15730">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="star"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="path15735"
+ sodipodi:sides="5"
+ sodipodi:cx="15.628067"
+ sodipodi:cy="15.74681"
+ sodipodi:r1="16.150806"
+ sodipodi:r2="7.9237795"
+ sodipodi:arg1="2.5689263"
+ sodipodi:arg2="3.1972448"
+ inkscape:flatsided="false"
+ inkscape:rounded="0"
+ inkscape:randomized="0"
+ d="M 2.0539742,24.498527 7.716555,15.306062 3.110064,5.5415099 13.602452,8.0863163 21.465608,0.68787049 22.28768,11.453112 l 9.466189,5.192062 -9.984319,4.108479 -2.012731,10.607316 -6.99272,-8.226063 z"
+ inkscape:transform-center-x="0.46164082"
+ inkscape:transform-center-y="-1.2197792"
+ transform="matrix(0.86268816,-0.34553411,0.34553411,0.86268816,-2.9571926,8.9732054)" />
+ </g>
+</svg>
diff --git a/statistics.c b/statistics.c
index a60c6e404..d7f8371fa 100644
--- a/statistics.c
+++ b/statistics.c
@@ -28,8 +28,6 @@ stats_t stats_selection;
stats_t *stats_monthly = NULL;
stats_t *stats_yearly = NULL;
-
-
static void process_temperatures(struct dive *dp, stats_t *stats)
{
int min_temp, mean_temp, max_temp = 0;
@@ -275,3 +273,19 @@ void get_selected_dives_text(char *buffer, int size)
}
}
}
+
+volume_t get_gas_used(struct dive *dive)
+{
+ int idx;
+ volume_t gas_used = { 0 };
+ for (idx = 0; idx < MAX_CYLINDERS; idx++) {
+ cylinder_t *cyl = &dive->cylinder[idx];
+ pressure_t start, end;
+
+ start = cyl->start.mbar ? cyl->start : cyl->sample_start;
+ end = cyl->end.mbar ?cyl->sample_end : cyl->sample_end;
+ if (start.mbar && end.mbar)
+ gas_used.mliter += gas_volume(cyl, start) - gas_volume(cyl, end);
+ }
+ return gas_used;
+}
diff --git a/statistics.h b/statistics.h
index d2709ee93..732a287e1 100644
--- a/statistics.h
+++ b/statistics.h
@@ -4,6 +4,14 @@
* core logic functions called from statistics UI
* common types and variables
*/
+
+#ifndef STATISTICS_H
+#define STATISTICS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct {
int period;
duration_t total_time;
@@ -31,3 +39,10 @@ extern char *get_time_string(int seconds, int maxdays);
extern char *get_minutes(int seconds);
extern void process_all_dives(struct dive *dive, struct dive **prev_dive);
extern void get_selected_dives_text(char *buffer, int size);
+extern volume_t get_gas_used(struct dive *dive);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/subsurface.qrc b/subsurface.qrc
new file mode 100644
index 000000000..e1939f28e
--- /dev/null
+++ b/subsurface.qrc
@@ -0,0 +1,6 @@
+ <!DOCTYPE RCC><RCC version="1.0">
+ <qresource>
+ <file alias="star">star.svg</file>
+ <file alias="subsurface-icon">subsurface-icon.png</file>
+ </qresource>
+ </RCC>
diff --git a/uemis-downloader.c b/uemis-downloader.c
index d33f08b8c..53d4f6876 100644
--- a/uemis-downloader.c
+++ b/uemis-downloader.c
@@ -922,14 +922,18 @@ GError *uemis_download(const char *mountpath, progressbar_t *progress,
if (!import_thread_cancelled) {
int result;
g_timeout_add(100, timeout_func, dialog);
+#if USE_GTK_UI
update_progressbar(args.progress, progress_bar_fraction);
update_progressbar_text(args.progress, progress_bar_text);
+#endif
result = gtk_dialog_run(dialog);
if (result == GTK_RESPONSE_CANCEL)
import_thread_cancelled = TRUE;
} else {
+#if USE_GTK_UI
update_progressbar(args.progress, progress_bar_fraction);
update_progressbar_text(args.progress, _("Cancelled, exiting cleanly..."));
+#endif
usleep(100000);
}
}
diff --git a/webservice.c b/webservice.c
index 9c990284c..f4f8baeb4 100644
--- a/webservice.c
+++ b/webservice.c
@@ -164,7 +164,9 @@ static void download_dialog_response_cb(GtkDialog *d, gint response, gpointer da
/* now merge the data in the gps_location table into the dive_table */
if (merge_locations_into_dives()) {
mark_divelist_changed(TRUE);
+#if USE_GTK_UI
dive_list_update_dives();
+#endif
}
/* store last entered uid in config */
subsurface_set_conf("webservice_uid", gtk_entry_get_text(GTK_ENTRY(state->uid)));
diff --git a/webservice.h b/webservice.h
index e1eb0ce47..c1951acd1 100644
--- a/webservice.h
+++ b/webservice.h
@@ -1,3 +1,11 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern void webservice_download_dialog(void);
extern gboolean webservice_request_user_xml(const gchar *, gchar **, guint *, guint *);
extern int divelogde_upload(char *fn, char **error);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/windows.c b/windows.c
index f06ffc7a8..d6cb531ae 100644
--- a/windows.c
+++ b/windows.c
@@ -1,7 +1,10 @@
/* windows.c */
/* implements Windows specific functions */
#include "dive.h"
+#include "display.h"
+#if USE_GTK_UI
#include "display-gtk.h"
+#endif
#include <windows.h>
#include <shlobj.h>
@@ -20,12 +23,12 @@ void subsurface_open_conf(void)
printf("CreateKey Software\\subsurface failed %ld\n", success);
}
-void subsurface_unset_conf(char *name)
+void subsurface_unset_conf(const char *name)
{
RegDeleteValue(hkey, (LPCTSTR)name);
}
-void subsurface_set_conf(char *name, const char *value)
+void subsurface_set_conf(const char *name, const char *value)
{
/* since we are using the pointer 'value' as both an actual
* pointer to the string setting and as a way to pass the
@@ -52,17 +55,17 @@ void subsurface_set_conf(char *name, const char *value)
free(wname);
}
-void subsurface_set_conf_int(char *name, int value)
+void subsurface_set_conf_int(const char *name, int value)
{
RegSetValueEx(hkey, (LPCTSTR)name, 0, REG_DWORD, (const BYTE *)&value, 4);
}
-void subsurface_set_conf_bool(char *name, int value)
+void subsurface_set_conf_bool(const char *name, int value)
{
subsurface_set_conf_int(name, value);
}
-const void *subsurface_get_conf(char *name)
+const char *subsurface_get_conf(const char *name)
{
const int csize = 64;
int blen = 0;
@@ -100,7 +103,7 @@ const void *subsurface_get_conf(char *name)
return utf8_string;
}
-int subsurface_get_conf_int(char *name)
+int subsurface_get_conf_int(const char *name)
{
DWORD value = -1, len = 4;
LONG ret = RegQueryValueEx(hkey, (LPCTSTR)TEXT(name), NULL, NULL,
@@ -110,7 +113,7 @@ int subsurface_get_conf_int(char *name)
return value;
}
-int subsurface_get_conf_bool(char *name)
+int subsurface_get_conf_bool(const char *name)
{
int ret = subsurface_get_conf_int(name);
if (ret == -1)
@@ -128,6 +131,7 @@ void subsurface_close_conf(void)
RegCloseKey(hkey);
}
+#if USE_GTK_UI
int subsurface_fill_device_list(GtkListStore *store)
{
const int bufdef = 512;
@@ -194,6 +198,7 @@ int subsurface_fill_device_list(GtkListStore *store)
}
return index;
}
+#endif /* USE_GTK_UI */
const char *subsurface_icon_name()
{
@@ -231,11 +236,13 @@ const char *subsurface_gettext_domainpath(char *argv0)
return "./share/locale";
}
+#if USE_GTK_UI
void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
GtkWidget *vbox, GtkUIManager *ui_manager)
{
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
}
+#endif /* USE_GTK_UI */
/* barely documented API */
extern int __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *);