summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2013-05-17 22:01:41 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2013-05-17 22:01:41 -0700
commitf3f7bf51fa1dbe9cdb859e1a45b20c108613275b (patch)
treea28814b4d3d59ff26b41ecfe71670c03fe2ce71d
parent082ec43eeabe92c7d65d3ab0f189e8fb43016f35 (diff)
parent56dbb7c2ff697a393f5051e2b5363bd4c0f2bb6e (diff)
downloadsubsurface-f3f7bf51fa1dbe9cdb859e1a45b20c108613275b.tar.gz
Merge branch 'Qt'
After the 3.1 release it is time to shift the focus on the Qt effort - and the best way to do this is to merge the changes in the Qt branch into master. Linus was extremely nice and did a merge for me. I decided to do my own merge instead (which by accident actually based on a different version of the Qt branch) and then used his merge to double check what I was doing. I resolved a few things differently but overall what we did was very much the same (and I say this with pride since Linus is a professional git merger) Here's his merge commit message: This is a rough and tumble merge of the Qt branch into 'master', trying to sort out the conflicts as best as I could. There were two major kinds of conflicts: - the Makefile changes, in particular the split of the single Makefile into Rules.mk and Configure.mk, along with the obvious Qt build changes themselves. Those changes conflicted with some of the updates done in mainline wrt "release" targets and some helper macros ($(NAME) etc). Resolved by largely taking the Qt branch versions, and then editing in the most obvious parts of the Makefile updates from mainline. NOTE! The script/get_version shell script was made to just fail silently on not finding a git repository, which avoided having to take some particularly ugly Makefile changes. - Various random updates in mainline to support things like dive tags. The conflicts were mainly to the gtk GUI parts, which obviously looked different afterwards. I fixed things up to look like the newer code, but since the gtk files themselves are actually dead in the Qt branch, this is largely irrelevant. NOTE! This does *NOT* introduce the equivalent Qt functionality. The fields are there in the code now, but there's no Qt UI for the whole dive tag stuff etc. This seems to compile for me (although I have to force "QMAKE=qmake-qt4" on f19), and results in a Linux binary that seems to work, but it is otherwise largely untested. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-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 *);