summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/Makefile10
-rw-r--r--Documentation/user-manual.txt612
-rw-r--r--Makefile30
-rw-r--r--cochran.c224
-rw-r--r--display-gtk.h20
-rw-r--r--dive.c121
-rw-r--r--dive.h8
-rw-r--r--divelist.c52
-rw-r--r--equipment.c23
-rw-r--r--file.c136
-rw-r--r--file.h11
-rw-r--r--gtk-gui.c88
-rw-r--r--info.c6
-rw-r--r--libdivecomputer.c4
-rw-r--r--linux.c14
-rw-r--r--macos.c119
-rw-r--r--main.c6
-rw-r--r--packaging/macosx/Info.plist4
-rwxr-xr-xpackaging/macosx/subsurface.sh5
-rw-r--r--packaging/windows/subsurface.nsi5
-rw-r--r--parse-xml.c273
-rw-r--r--profile.c5
-rw-r--r--statistics.c289
-rw-r--r--uemis.c2
-rw-r--r--windows.c14
-rw-r--r--xslt/jdivelog2subsurface.xslt99
26 files changed, 1542 insertions, 638 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
new file mode 100644
index 000000000..273eae521
--- /dev/null
+++ b/Documentation/Makefile
@@ -0,0 +1,10 @@
+ASCIIDOC=asciidoc
+BROWSER=firefox
+
+doc: user-manual.html
+
+show: user-manual.html
+ $(BROWSER) user-manual.html
+
+user-manual.html: user-manual.txt
+ $(ASCIIDOC) user-manual.txt
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 181360140..75fb3a338 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -1,19 +1,23 @@
-Subsurface 1.2
-
-User Manual
-
-Version 0.0.4
-
-Autor documentation: Jacco van Koll <jko@haringstad.com>
+Subsurface 1.2 User Manual
+==========================
+Jacco van_Koll <jko@haringstad.com>
+v0.0.7, January 2012
+:Author Initials: JKO
+:toc:
+:icons:
+:numbered:
+:website: http://subsurface.hohndel.org
Scope of this document is the usage of the program.
Please read the build manual for instructions how to build the
-software and (if needed) it´s dependencies.
+software and (if needed) its dependencies.
Audience: Fun Divers, Tec Divers, Professional Divers
-1. Introduction:
+[[S_Introduction]]
+Introduction:
+-------------
Subsurface was started because of a lack of viable dive log software
on Linux. It turns out that the resulting software was easily ported
@@ -24,18 +28,22 @@ already very usable for divers with supported dive-computers.
In this manual the Suunto Viper will be used for all examples.
-2. Requirements
+[[S_Requirements]]
+Requirements
+------------
Before you are able to import information from your divecomputer into
Subsurface, you need some preparation. Do you have the following:
- 1. Your Divecomputer - Compatible with libdivecomputer (see list in Appendix A)
+ 1. Your Divecomputer - Compatible with libdivecomputer (see list in <<AppendixA,Appendix A>>)
2. Communication interface - Cable to connect your divecomputer to your PC/Laptop/Netbook
3. Working installation of Subsurface
4. If needed, the manual of your divecomputer
-3. Start using the program:
+[[S_StartUsing]]
+Start using the program
+-----------------------
When you start the program for the first time, it shows no information
at all. This is because the program does not automatically load the
@@ -45,12 +53,14 @@ There is a menu, containing 'File', 'Log', 'Filter' and Help.
The screen is devided in 3 area's:
- Area with 3 tabs: Dive Notes, Equipment, Info & Stats
- Area next to the 3 tabs, which will contain the dive profile
- Area with the dives (usually called dive list), which can be sorted by number, date, etc.
+ - Area with 3 tabs: Dive Notes, Equipment, Info & Stats
+ - Area next to the 3 tabs which will contain the dive profile
+ - Area with the dives (usually called dive list) which can be sorted by number, date, etc.
-4. Import new dives
+[[S_ImportNewDives]]
+Import new dives from your divecomputer
+---------------------------------------
Before you start fiddeling around with your divecomputer, note that
there are divecomputers that consume more power when they are in the
@@ -62,40 +72,42 @@ be recharged when connected to the USB port.
Now it is time to hook up your divecomputer to your Linux system:
- Make sure that your OS has the required drivers installed
+ - Make sure that your OS has the required drivers installed
- On Linux this means you need to have the correct kernel
- module loaded. Most distributions will do this automatically
- for you.
+ * On Linux this means you need to have the correct kernel
+ module loaded. Most distributions will do this automatically
+ for you.
- On Windows, the OS should offer to download the correct
- driver when you connect to the USB port.
+ * On Windows, the OS should offer to download the correct
+ driver when you connect to the USB port.
- On a Mac you at times have to manually hunt for the correct
- driver. For example the correct driver for the Mares Puck
- devices can be found as Mac_OSX_VCP_Driver.zip at
- http://www.silabs.com/support/pages/support.aspx?ProductFamily=USB+Bridges
+ * On a Mac you at times have to manually hunt for the correct
+ driver. For example the correct driver for the Mares Puck
+ devices can be found as Mac_OSX_VCP_Driver.zip at
+ http://www.silabs.com/support/pages/support.aspx?ProductFamily=USB+Bridges
- Connect your interface cable to a free USB port
+ - Connect your interface cable to a free USB port
- Put your divecomputer into PC Communication mode. (For Suunto Viper, press Mode - 1 Memory - 3 TR-PC)
+ - Put your divecomputer into PC Communication mode. (For Suunto Viper, press Mode - 1 Memory - 3 TR-PC)
(You should consult the manual of your specific divecomputer for your brand and type)
- Go in Subsurface to 'File - Import'
- Within the popup, under Dive computer, choose your brand and type. Here we choose Suunto Vyper.
- Change the devicename under which your interface is connected.
- On Linux, default is /dev/ttyUSB0
- On Windows, default is COM3
- On Mac, default is ... specific to the dive computer
+ - Go in Subsurface to 'File - Import'
+ * Within the popup, under Dive computer, choose your brand and type. Here we choose Suunto Vyper.
+ * Change the devicename under which your interface is connected.
+ ** On Linux, default is /dev/ttyUSB0
+ ** On Windows, default is COM3
+ ** On Mac, default is ... specific to the dive computer
- Click the 'OK' button.
+ * Click the 'OK' button.
- Now watch how your data is retrieved from your divecomputer!
- Depending on your type of computer and/or number of dives, this
- could take some time. Please be patient.
+ - Now watch how your data is retrieved from your divecomputer!
+ Depending on your type of computer and/or number of dives, this
+ could take some time. Please be patient.
-5. Viewing and completing your logs
+[[S_ViewingLogs]]
+Viewing and completing your logs
+--------------------------------
When all data from your divecomputer is transferred, you will see a
listing of your dives in Area 3.
@@ -104,99 +116,110 @@ An example:
On Sunday Oct 23, 2011 you made a dive.
In the log line of this dive, you see the following information:
-
- #: 12 Dive number
- Date: Sun, Oct 23, 2011 10:50 Date and time of your dive
- *: Your rating (none at this time)
- m: 12.8 Your maximum depth in meters
- min: 31:20 Your dive-time in minutes and seconds
- Deg. C: 13.0 Lowest water temperature during your dive
- Cyl: Your used cylinder (none at this time)
- O2%: air What type of mixture
- SAC: SAC (none at this time)
- Location: Where you performed your dive (empty)
-
- As you can see, some information is already there because it is
- retrieved from your divecomputer. Some information is waiting for
- you to be added. By double clicking on this dive, you can view and
- complete the log.
-
-
-6. Edit the dive info
+[width="70%",cols="<5%,10%,<20%,<65%",grid="none",frame="none",style="monospaced"]
+|===============================================================================
+|| # | 12 | Dive number
+|| Date | Sun, Oct 23, 2011 10:50 | Date and time of your dive
+|| * | | Your rating (none at this time)
+|| m | 12.8 | Your maximum depth in meters
+|| min | 31:20 | Your dive-time in minutes and seconds
+|| Deg. C | 13.0 | Lowest water temperature during your dive
+|| Cyl | | Your used cylinder (none at this time)
+|| O2% | air | What type of mixture
+|| SAC | | SAC (none at this time)
+|| Location | | Where you performed your dive (empty)
+|===============================================================================
+
+As you can see, some information is already there because it is
+retrieved from your divecomputer. Some information is waiting for
+you to be added. By double clicking on this dive, you can view and
+complete the log.
+
+
+[[S_EditDiveInfo]]
+Edit the dive info
+------------------
When you double click on the dive log line, the editor window
opens. Now you can add information that is missing. Let start with
completing the example:
-You double clicked on dive #12, as described in 5. Viewing and
-completing your logs. The Dive Info window pops up and you will see
+You double clicked on dive #12, as described in <<S_ViewingLogs,Viewing and
+completing your logs>>. The Dive Info window pops up and you will see
the following:
-
- Location: An input where you can enter your new location, or you can choose with the pull-down previous locations
- Dive Master: An input where you can enter the name of your Dive Master, or you can choose with the pull-down a previous name
- Buddy: An input where you can enter het name of you Buddy, or you can choose with the pull-down a previous name
- Rating: A pull-down where you can rate your dive.
- Notes: A free input where you can enter information about your dive. What you've seen, etc.
+[horizontal]
+ *Location*:: An input where you can enter your new location, or you can choose with the pull-down previous locations
+ *Dive Master*:: An input where you can enter the name of your Dive Master, or you can choose with the pull-down a previous name
+ *Buddy*:: An input where you can enter het name of you Buddy, or you can choose with the pull-down a previous name
+ *Rating*:: A pull-down where you can rate your dive.
+ *Notes*:: A free input where you can enter information about your dive. What you've seen, etc.
In this example we use the following information:
-
- Location: Oostvoornse Meer
- Dive Master: S. de Vries
- Buddy: S. de Vries
- Rating: 3 stars
- Notes: First dive here. Good visibility. Did see the concrete poles, some crab and fish. Very nice and easy dive.
- Made movie with 'headcam'.
+[horizontal]
+ *Location*:: Oostvoornse Meer
+ *Dive Master*:: S. de Vries
+ *Buddy*:: S. de Vries
+ *Rating*:: 3 stars
+ *Notes*:: First dive here. Good visibility. Did see the concrete poles, some crab and fish. Very nice and easy dive. +
+ Made movie with headcam.
Now don't press ok yet!
+[[S_EditEquipmentInfo]]
+Edit equipment info
+-------------------
-7. Edit equipment info
-
-You also want to edit your Cylinder information. And in the previous
-chapter, this was not edited. There is still another item to edit in
-the Dive Info screen:
+You also want to edit your Cylinder information. And in the
+<<S_EditDiveInfo, previous chapter>>, this was not edited. There is
+still another item to edit in the Dive Info screen:
- Cylinder: A double-click fieldset. Here you can edit your Cylinder information
+ - Cylinder: A double-click fieldset. Here you can edit your Cylinder information
So, when you double click on the cylinder info, you get another
popup. This popup gives you the following:
- Cylinder: Pull-down where you can choose your Cylinder, or add your own
- Size: The volume if not 'filled'
- Pressure: The maximum pressure of this Cylinder
- Optional:
- Start Pressure: What was the pressure starting the dive
- End Pressure: What was the pressure ending the dive
- Nitrox: What was the percentage of blend
+ - Cylinder: Pull-down where you can choose your Cylinder, or add your own
+ - Size: The volume if not `filled'
+ - Pressure: The maximum pressure of this Cylinder
+ - Optional:
+ * Start Pressure: What was the pressure starting the dive
+ * End Pressure: What was the pressure ending the dive
+ * Nitrox: What was the percentage of blend
Now we are going to enter the data:
- Cylinder: 15.0 l
- Size: 15.0
- Pressure: 220
+ - Cylinder: 15.0 l
+ - Size: 15.0
+ - Pressure: 220
- Now tick the option for Start & End pressure
+Now tick the option for Start & End pressure
- Start Pressure: 180
- End Pressure: 60
- Press Ok
+ - Start Pressure: 180
+ - End Pressure: 60
+ - Press Ok
Now your dive information for this dive is complete. You can now press
ok in the Dive Info screen and view the results.
-
-8. Adding equipment info
+[[S_AddingEquipment]]
+Adding equipment info
+---------------------
In Area with the 3 tabs there is the tab Equipment. With this tab, you
can add Cylinders. We are going to add an additional Cylinder:
- In the main screen, click on the Equipment tab. This shows your Cylinder you added in 7.
- Now press the Add button and the Cylinder popup comes back.
- Just like you added your Cylinder information in 7. Edit equipment info, you add your cylinder
- information for the second Cylinder. Fill in all the information about this Cylinder and press OK.
+ - In the main screen, click on the Equipment tab. This shows your
+ Cylinder you added in 7.
+
+ - Now press the Add button and the Cylinder popup comes back.
+ - Just like you added your Cylinder information in 7. Edit equipment
+ info, you add your cylinder information for the second Cylinder.
+ Fill in all the information about this Cylinder and press OK.
-9. View info & Stats
+[[S_ViewInfoStats]]
+View info & Stats
+-----------------
After adding all the information, you can use the tab Info &
Stats. This tab will provide you with all the (statistical and
@@ -204,31 +227,32 @@ calculated) information regarding your dive.
The information contains:
- Dive Info:
-
- Date: Date and time of your dive
- Dive Time: Duration of your dive
- Surf Intv: Interval between previous dive and this dive
- Max Depth: Maximum depth of this dive
- Avg Depth: The average depth of this dive
- Water Temp: Lowest temperature of the water
- SAC: The amount of Surface Air Consumption liters per minute
- OTU: The Oxygen Toxicity Units of this dive
- O2/He: Amount of Oxygen/Helium
- Gas Used: The total volume of gas used during this dive
-
- Statistics:
-
- Total time: Total time of all your dives together, calculated
- Avg Time: The average divetime of your dives, calculated
- Max Depth: The maximum depth of all your dives
- Avg Depth: The average depth of all your dives, calculated
- Max SAC: Highest of Surface Air Consumption of all your dives
- Min SAC: Lowest of Surface Air Consumption of all your dives
- Avg SAC: Average Surface Air Consuption of all your dives, calculated
-
-
-10. Setting up preferences
+ - Dive Info:
+
+ ** Date: Date and time of your dive
+ ** Dive Time: Duration of your dive
+ ** Surf Intv: Interval between previous dive and this dive
+ ** Max Depth: Maximum depth of this dive
+ ** Avg Depth: The average depth of this dive
+ ** Water Temp: Lowest temperature of the water
+ ** SAC: The amount of Surface Air Consumption liters per minute
+ ** OTU: The Oxygen Toxicity Units of this dive
+ ** O2/He: Amount of Oxygen/Helium
+ ** Gas Used: The total volume of gas used during this dive
+
+ - Statistics:
+
+ ** Total time: Total time of all your dives together, calculated
+ ** Avg Time: The average divetime of your dives, calculated
+ ** Max Depth: The maximum depth of all your dives
+ ** Avg Depth: The average depth of all your dives, calculated
+ ** Max SAC: Highest of Surface Air Consumption of all your dives
+ ** Min SAC: Lowest of Surface Air Consumption of all your dives
+ ** Avg SAC: Average Surface Air Consuption of all your dives, calculated
+
+[[S_SettingUpPreferences]]
+Setting up preferences
+----------------------
Subsurface has the ability to modify the preferences you want. By
using menu 'File - Preferences' you will be presented a popup with the
@@ -236,19 +260,19 @@ using menu 'File - Preferences' you will be presented a popup with the
words, use Metric or Imperial.
You can set the following options:
- Depth: Your diving depth in Meters or Feet
- Pressure: The pressure of your tank(s) in Bar/Ato or PSI (Pressure Square Inch)
- Volume: The volume of your tank(s) in Liter or CuFt (Cubic Feet) (At sea-level pressure)
- Temperature: The temperature of the water in Celcius or Fahrenheit
+ - Depth: Your diving depth in Meters or Feet
+ - Pressure: The pressure of your tank(s) in Bar/Ato or PSI (Pressure Square Inch)
+ - Volume: The volume of your tank(s) in Liter or CuFt (Cubic Feet) (At sea-level pressure)
+ - Temperature: The temperature of the water in Celcius or Fahrenheit
In the main screen, you did see in Area 3, some information. In the
Columns options, you can enable/disable options you would like to show
there:
- Show Temp: Shows the temperature of your dive
- Show Cyl: Shows the cylinder(s) of your dive
- Show O2%: Shows the O2% of your dive
- Show SAC: Shows the SAC of your dive (Surface Air Consumption)
- Show OTU: Shows the OTU of your dive (Oxygen Toxicity Units)
+ - Show Temp: Shows the temperature of your dive
+ - Show Cyl: Shows the cylinder(s) of your dive
+ - Show O2%: Shows the O2% of your dive
+ - Show SAC: Shows the SAC of your dive (Surface Air Consumption)
+ - Show OTU: Shows the OTU of your dive (Oxygen Toxicity Units)
And, you can change the font usage of the program.
@@ -256,39 +280,41 @@ I will give an example here:
I am a diver in The Netherlands, using the Metric System. Therefor, I
go to the menu File, choose Preferences here. In the Units section, I
-use the folowing:
+use the following:
- Depth: Meter
- Pressure: Bar
- Volume: Liter
- Temperature: Celcius
+ - Depth: Meter
+ - Pressure: Bar
+ - Volume: Liter
+ - Temperature: Celcius
I would like to see the:
- Temperature
- Show Cyl
- Show O2%
- Show SAC
+
+ - Temperature
+ - Show Cyl
+ - Show O2%
+ - Show SAC
As a beginning diver, I don't need to track my OTUs. So I leave this
one not enabled.
Clicking OK on the dialog stores these settings.
-
-11. How to find the Device Name
+[[S_HowFindDeviceName]]
+How to find the Device Name
+---------------------------
When you connect your divecomputer by using an USB connector, most of the
-time, the default of ´/dev/ttyUSB0' should work. But if you have other
+time, the default of '/dev/ttyUSB0' should work. But if you have other
Serial to USB devices, this can be different because '/dev/ttyUSB0' is
already in use.
One of the ways to find out what your dive name is:
- Disconnect your usb cable of your dive computer
- Open a terminal
- Type the command: 'dmesg' and press enter
- Plug in your usb cable of your divecomputer
- Type the command: 'dmesg' and press enter
+ - Disconnect your usb cable of your dive computer
+ - Open a terminal
+ - Type the command: 'dmesg' and press enter
+ - Plug in your usb cable of your divecomputer
+ - Type the command: 'dmesg' and press enter
Within your terminal you should see a message similair to this one:
@@ -313,45 +339,303 @@ detected and is connected to 'ttyUSB3'. Now you use this information in
the import settings as '/dev/ttyUSB3'. Your divecomputer interface is
connected and you should be able to import your dives.
+[[S_ImportingDivesJDivelog]]
+Importing dives from JDivelog
+-----------------------------
+
+Maybe you have been using JDivelog and you have a lot of dives logged in
+this program. You don't have to type all information by hand into
+Subsurface, because you can import your divelogs from JDivelog.
-Appendix A
+JDivelog stores its information into files with the extension of .jlb.
+These .jlb contain all the information that has been stored, except your
+images in xml format.
+
+By using the menu 'File - Import' you get the popup, like described in
+<<S_ImportNewDives, chapter 4>>, Importing new dives. Within this
+popup there is the option to import existing files which are already
+on your computer. To import your JDivelog file(s) do the following:
+
+ - Open 'File - Import' on the menu
+ - Use the file locator under XML file name
+ - Browse your directories to the location where your *.jlb file is
+ - Select your existing *.jlb file and click 'open'
+ - Click the OK button in the popup
+
+After a few moments, you see your existing logs in Subsurface. Now you can
+edit your dives like explained in <<S_EditDiveInfo, chapter 6>>.
+
+Information that is imported from JDivelog into the location field:
+
+ - Extended dive location information
+
+Information that is merged into the location or notes field:
+
+ - Used amount of weight
+ - Used type of suit
+ - Used type of gloves
+ - Type of dive
+ - Dive activity
+
+Alternatively, you can start subsurface with the --import command line
+which will have the same effect:
+
+ subsurface MyDives.xml --import JDivelogDives.jlb
+
+will open your divelog (assuming that's called MyDives.xml) and then
+import the dives from JdivelogDives.jlb. You can now save the combined
+divelog back as MyDives.xml.
+
+Subsurface will similarly import xml exports from DivingLog as well as
+Suunto DiveManager.
+
+When importing dives subsurface tries to detect multiple records for
+the same dive and merges the information as best as it can. So as long
+as there are no time zone issues (or other reasons that would cause the
+beginning time of the dives to be substantially different) subsurface
+will not create duplicate entries.
+
+[[S_ImportingDivesSuunto]]
+Importing dives from Suunto Divemanager 3.*
+-------------------------------------------
+
+Before you can start importing dives from Suunto Divemanager, you first
+have to export the dives you want to import. Subsurface does not import
+directly from the Suunto Divemanager log files. The following procedures
+unpacking instructions for Linux and Windows.
+
+Export from Suunto Divemanager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ - Start Suunto Divemanager and login with the name containing the logs
+ - Do not start the import wizard to import dives from your computer.
+ - In the navigation tree on the left side of the program-window, select your dives.
+ - Within the list of dives, select the dives you would like to import later:
+ * To select certain dives: hold ctrl and point & click the dive
+ * To select all dives: Select the first dive, hold down shift and select the last dive
+ - With the dives marked, use the program menu 'File - Export'
+ - The export popup will show
+ - Within this popup, there is one field called Export Path.
+ * Click the button browse next to the field Export Path
+ ** A file-manager like window pops up
+ ** Navigate to the directory where you want to store the Divelog.SDE file
+ ** Optional change the name of the file you want to save
+ ** Click 'Save'
+ * You are back in the Export popup. Press the button 'Export'
+ - Your dives are now exported to the file Divelogs.SDE.
+
+Unpacking the Divelogs.SDE on Windows
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Renaming your file to a .zip:
+
+ - Use the filemanager (explorer) and navigate to your Divelogs.SDE file
+ - Right click on the Divelogs.SDE file and choose 'Rename'
+ * Change the name into Divelogs.SDE.zip
+ * Press enter when done. A warning popup shows:
+
+ The file could be unusable when changing the extension. Are you sure:
+ Press OK.
+
+ * Your filemanager will show now the filename Divelogs.SDE.zip
+
+When you double click your Divelogs.SDE.zip file, your preferred archiving
+tool will start and show you the list of xml files that are in the zip
+archive. Select all the xml files and extract them to a place where you
+can find them later in the process.
+
+Unpacking the Divelogs.SDE on Linux
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The assumption is that you have exported your Divelogs.SDE on a Windows
+system. You have to transfer the file to a location where you can read it
+from within your Linux environment. You can use file-transfer, shared
+storage or an USB storage device to do this.
+The example uses an USB storage:
+
+ - Insert your USB storage into your Windows computer
+ - Use the filemanager (explorer) to navigate to the location where your Divelogs.SDE file is located
+ - Copy the file to your USB storage:
+ * Select the file by 1 click
+ * Press Ctrl+c
+ * Navigate to your USB Storage
+ * Press Ctrl+v
+ - Disconnect your USB storage by right clicking your USB storage in the explorer and choose Eject
+ - Insert your USB storage into your Linux computer
+ - Use your favourite filemanager to navigate to your USB storage
+ - Copy the file to /tmp by:
+ * Right click on the file
+ * select copy
+ * navigate to /tmp
+ * press Ctrl+v or use the menu 'Edit - Paste'
+ - The file is now transfered to /tmp
+
+Now the file is in /tmp, we can extract the xml files from it. You can do
+this by hand, or use the example script in <<AppendixB,Appendix B>>.
+
+To extract the xml files, we need to open a terminal and use the following
+commands:
+
+ cd /tmp
+ mkdir suunto
+ cd suunto
+ unzip ../Divelogs.SDE
+
+Your divelogs have now been extracted from the Divelogs.SDE file and you
+can import them with the command:
+
+ subsurface *.xml
+
+And with the menu 'File - Save' you can save your dives into the
+Subsurface format.
+
+[[S_Menu]]
+The menu and sub-menus
+----------------------
+
+Within Subsurface, there are several menu and sub-menu options. All of
+those will be described here with their function.
+
+The file menu
+~~~~~~~~~~~~~
+
+The file menu is used for the following menu options:
+
+ - Open:: Open your saved Subsurface xml file(s)
+ - Save:: Save your current divelogs or changes you made to your divelogs
+ - Print:: Print your current divelog profiles and information about the dive
+ - Import:: Import your dives from your divecomputer, JDivelogs or Suunto Divemanager
+ - Preferences:: Set your preferences as described in <<S_SettingUpPreferences,chapter 10>>
+ - Quit:: Quit the program
+
+The Log menu
+~~~~~~~~~~~~
+
+Within the Log menu, there are only 2 sub-items:
+
+ - Renumber:: This option provides you with a popup. Within this
+ popup you can choose what the first number of your dives should be
+ for this set of dives.
+ - View:: This is a submenu containing:
+ * List:: Show only the list of dives you have made
+ * Profile:: Show only the dive profile of the selected dive
+ * Info:: Show only the 3 tab information screen
+ * Three:: Show the 'default' 3 screen setup
+
+The Filter menu
+~~~~~~~~~~~~~~~
+
+This menu gives you the choice to enable or disable Events for the
+selected divelog(s). At this time, you can enable or disable ascent.
+When you enable ascent for your dives, within the dive profile, a yellow
+marker with exclamation sign (!) will show on the points where you have
+ascented.
+
+The Help menu
+~~~~~~~~~~~~~
+
+The Help menu shows only the About, which contains the version and author
+information and License button.
+
+
+[[AppendixA]]
+Appendix A: Supported Dive Computers
+------------------------------------
The use of libdivecomputer provides the support for divecomputers. Within
the list of computers in the 'File - import' menu, you will see a listing
of divecomputers. This list is covering a compatible set. Please check
your users manual to check if your computer will be supported.
- Supported divecomputers:
+ Supported divecomputers::
- Atomics:
+ Atomics::
Cobalt
- Cressi:
+
+ Cressi::
Edi
- Mares:
+
+ Mares::
Icon HD
Nemo
Puck
- Oceanic:
+ Air
+
+ Oceanic::
Veo250
VT Pro
- OSTC: *
+
+ OSTC::
DR5
2N
- Reefnet:
+
+ Reefnet::
Sensus
Sensus Pro
Sensus Ultra
- Suunto:
+
+ Suunto::
+ Cobra
+ 2
+ 3
+ D3
D9
+ D4
+ D4i
+ D6
+ D6i
+ D9tx
Eon
+ Gekko
+ HelO2
+ Mosquito
Solution
- Viper
- Viper Air
- Uwatec:
+ Alpha
+ Nitrox/Vario
+ Stinger
+ Vyper
+ 2
+ Air
+ Vytec
+ DS
+ Zoop
+
+ Uwatec::
Aladin
Memo Mouse
Smart
- Zeagle:
+
+ Zeagle::
N2iTiON 3
* OSTC computers are listed in the pull-down menu as OSTC. All 3 types are supported.
+
+
+[[AppendixB]]
+Appendix B: Suunto Export Unpacking Script
+------------------------------------------
+
+ #!/bin/bash
+ #
+ # Small basic example script to unpack Suunto Export files
+ # for the use with Subsurface
+ #
+
+ echo -n "Enter the directory where you stored your Suunto Divemanager export file: "
+ read SuuntoExportDir
+
+ echo -n "Enter the name of your Suunto Divemanager export file: "
+ read SuuntoExportFile
+
+ echo "You have entered: $SuuntoExportDir/$SuuntoExportFile"
+
+ cd $SuuntoExportDir
+
+ if [ -e ./$SuuntoExportFile ]; then
+ mkdir SuuntoXML
+ cd SuuntoXML
+ unzip ../$SuuntoExportFile
+ subsurface *.xml
+ else
+ echo "Nothing found! Try again!"
+ fi
diff --git a/Makefile b/Makefile
index f7c883999..20dff4c85 100644
--- a/Makefile
+++ b/Makefile
@@ -36,13 +36,13 @@ UNAME := $(shell $(CC) -dumpmachine 2>&1 | grep -E -o "linux|darwin|win")
#
ifeq ($(CC), i686-w64-mingw32-gcc)
# ok, we are cross building for Windows
- LIBDIVECOMPUTERINCLUDES = `$(PKGCONFIG) --cflags libdivecomputer`
- LIBDIVECOMPUTERARCHIVE = `$(PKGCONFIG) --libs libdivecomputer`
+ LIBDIVECOMPUTERINCLUDES = $(shell $(PKGCONFIG) --cflags libdivecomputer)
+ LIBDIVECOMPUTERARCHIVE = $(shell $(PKGCONFIG) --libs libdivecomputer)
RESFILE = packaging/windows/subsurface.res
LDFLAGS += -Wl,-subsystem,windows
else ifeq ($(UNAME), darwin)
- LIBDIVECOMPUTERINCLUDES = `$(PKGCONFIG) --cflags libdivecomputer`
- LIBDIVECOMPUTERARCHIVE = `$(PKGCONFIG) --libs libdivecomputer`
+ 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)
@@ -87,6 +87,11 @@ GLIB2CFLAGS = $(shell $(PKGCONFIG) --cflags glib-2.0)
GTK2CFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-2.0)
CFLAGS += $(shell $(XSLCONFIG) --cflags)
+LIBZIP = $(shell $(PKGCONFIG) --libs libzip 2> /dev/null)
+ifneq ($(strip $(LIBZIP)),)
+ ZIP = -DLIBZIP $(shell $(PKGCONFIG) --cflags libzip)
+endif
+
ifeq ($(UNAME), linux)
LIBGCONF2 = $(shell $(PKGCONFIG) --libs gconf-2.0)
GCONF2CFLAGS = $(shell $(PKGCONFIG) --cflags gconf-2.0)
@@ -97,7 +102,8 @@ else ifeq ($(UNAME), darwin)
OSSUPPORT_CFLAGS = $(GTK2CFLAGS)
MACOSXINSTALL = /Applications/Subsurface.app
MACOSXFILES = packaging/macosx
- EXTRALIBS = -framework CoreFoundation
+ EXTRALIBS = $(shell $(PKGCONFIG) --libs gtk-mac-integration) -framework CoreFoundation
+ CFLAGS += $(shell $(PKGCONFIG) --cflags gtk-mac-integration)
else
OSSUPPORT = windows
OSSUPPORT_CFLAGS = $(GTK2CFLAGS)
@@ -110,11 +116,11 @@ ifneq ($(strip $(LIBXSLT)),)
endif
endif
-LIBS = $(LIBXML2) $(LIBXSLT) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) -lpthread
+LIBS = $(LIBXML2) $(LIBXSLT) $(LIBGTK) $(LIBGCONF2) $(LIBDIVECOMPUTER) $(EXTRALIBS) $(LIBZIP) -lpthread -lm
OBJS = main.o dive.o profile.o info.o equipment.o divelist.o \
parse-xml.o save-xml.o libdivecomputer.o print.o uemis.o \
- gtk-gui.o statistics.o $(OSSUPPORT).o $(RESFILE)
+ gtk-gui.o statistics.o file.o cochran.o $(OSSUPPORT).o $(RESFILE)
$(NAME): $(OBJS)
$(CC) $(LDFLAGS) -o $(NAME) $(OBJS) $(LIBS)
@@ -141,12 +147,17 @@ install-macosx: $(NAME)
$(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/Resources
$(INSTALL) -d -m 755 $(MACOSXINSTALL)/Contents/MacOS
$(INSTALL) $(NAME) $(MACOSXINSTALL)/Contents/MacOS/
- $(INSTALL) $(MACOSXFILES)/subsurface.sh $(MACOSXINSTALL)/Contents/MacOS/
$(INSTALL) $(MACOSXFILES)/PkgInfo $(MACOSXINSTALL)/Contents/
$(INSTALL) $(MACOSXFILES)/Info.plist $(MACOSXINSTALL)/Contents/
$(INSTALL) $(ICONFILE) $(MACOSXINSTALL)/Contents/Resources/
$(INSTALL) $(MACOSXFILES)/Subsurface.icns $(MACOSXINSTALL)/Contents/Resources/
+file.o: file.c dive.h file.h
+ $(CC) $(CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) $(XSLT) $(ZIP) -c file.c
+
+cochran.o: cochran.c dive.h file.h
+ $(CC) $(CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) $(XSLT) $(ZIP) -c cochran.c
+
parse-xml.o: parse-xml.c dive.h
$(CC) $(CFLAGS) $(GLIB2CFLAGS) $(XML2CFLAGS) $(XSLT) -c parse-xml.c
@@ -194,5 +205,8 @@ uemis.o: uemis.c dive.h uemis.h
$(OSSUPPORT).o: $(OSSUPPORT).c display-gtk.h
$(CC) $(CFLAGS) $(OSSUPPORT_CFLAGS) -c $(OSSUPPORT).c
+doc:
+ $(MAKE) -C Documentation doc
+
clean:
rm -f $(OBJS) *~ $(NAME)
diff --git a/cochran.c b/cochran.c
new file mode 100644
index 000000000..360f2eb7c
--- /dev/null
+++ b/cochran.c
@@ -0,0 +1,224 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "dive.h"
+#include "file.h"
+
+#define DON
+
+/*
+ * The Cochran file format is designed to be annoying to read. It's roughly:
+ *
+ * 0x00000: room for 65534 4-byte words, giving the starting offsets
+ * of the dives themselves.
+ *
+ * 0x3fff8: the size of the file + 1
+ * 0x3ffff: 0 (high 32 bits of filesize? Bogus: the offsets into the file
+ * are 32-bit, so it can't be a large file anyway)
+ *
+ * 0x40000: "block 0": the decoding block. The first byte is some random
+ * value (0x46 in the files I have access to), the next 200+ bytes or so
+ * are the "scrambling array" that needs to be added into the file
+ * contents to make sense of them.
+ *
+ * The descrambling array seems to be of some random size which is likely
+ * determinable from the array somehow, the two test files I have it as
+ * 230 bytes and 234 bytes respectively.
+ */
+static unsigned int partial_decode(unsigned int start, unsigned int end,
+ const unsigned char *decode, unsigned offset, unsigned mod,
+ const unsigned char *buf, unsigned int size, unsigned char *dst)
+{
+ unsigned i, sum = 0;
+
+ for (i = start ; i < end; i++) {
+ unsigned char d = decode[offset++];
+ if (i >= size)
+ break;
+ if (offset == mod)
+ offset = 0;
+ d += buf[i];
+ if (dst)
+ dst[i] = d;
+ sum += d;
+ }
+ return sum;
+}
+
+/*
+ * The decode buffer size can be figured out by simply trying our the
+ * decode: we expect that the scrambled contents are largely random, and
+ * thus tend to have half the bits set. Summing over the bytes is going
+ * to give an average of 0x80 per byte.
+ *
+ * The decoded array is mostly full of zeroes, so the sum is lower.
+ *
+ * Works for me.
+ */
+static int figure_out_modulus(const unsigned char *decode, const unsigned char *dive, unsigned int size)
+{
+ int mod, best = -1;
+ unsigned int min = ~0u;
+
+ if (size < 0x1000)
+ return best;
+
+ for (mod = 50; mod < 300; mod++) {
+ unsigned int sum;
+
+ sum = partial_decode(0, 0x0fff, decode, 1, mod, dive, size, NULL);
+ if (sum < min) {
+ min = sum;
+ best = mod;
+ }
+ }
+ return best;
+}
+
+#define hexchar(n) ("0123456789abcdef"[(n)&15])
+
+static int show_line(unsigned offset, const unsigned char *data, unsigned size, int show_empty)
+{
+ unsigned char bits;
+ int i, off;
+ char buffer[120];
+
+ if (size > 16)
+ size = 16;
+
+ bits = 0;
+ memset(buffer, ' ', sizeof(buffer));
+ off = sprintf(buffer, "%06x ", offset);
+ for (i = 0; i < size; i++) {
+ char *hex = buffer + off + 3*i;
+ char *asc = buffer + off + 50 + i;
+ unsigned char byte = data[i];
+
+ hex[0] = hexchar(byte>>4);
+ hex[1] = hexchar(byte);
+ bits |= byte;
+ if (byte < 32 || byte > 126)
+ byte = '.';
+ asc[0] = byte;
+ asc[1] = 0;
+ }
+
+ if (bits) {
+ puts(buffer);
+ return 1;
+ }
+ if (show_empty)
+ puts("...");
+ return 0;
+}
+
+static void cochran_debug_write(const char *filename, const unsigned char *data, unsigned size)
+{
+ int i, show = 1;
+
+ for (i = 0; i < size; i += 16)
+ show = show_line(i, data + i, size - i, show);
+}
+
+static void parse_cochran_header(const char *filename,
+ const unsigned char *decode, unsigned mod,
+ const unsigned char *in, unsigned size)
+{
+ char *buf = malloc(size);
+
+ /* Do the "null decode" using a one-byte decode array of '\0' */
+ partial_decode(0 , 0x0b14, "", 0, 1, in, size, buf);
+
+ /*
+ * The header scrambling is different form the dive
+ * scrambling. Oh yay!
+ */
+ partial_decode(0x010e, 0x0b14, decode, 0, mod, in, size, buf);
+ partial_decode(0x0b14, 0x1b14, decode, 0, mod, in, size, buf);
+ partial_decode(0x1b14, 0x2b14, decode, 0, mod, in, size, buf);
+ partial_decode(0x2b14, 0x3b14, decode, 0, mod, in, size, buf);
+ partial_decode(0x3b14, 0x5414, decode, 0, mod, in, size, buf);
+ partial_decode(0x5414, size, decode, 0, mod, in, size, buf);
+
+ printf("\n%s, header\n\n", filename);
+ cochran_debug_write(filename, buf, size);
+
+ free(buf);
+}
+
+static void parse_cochran_dive(const char *filename, int dive,
+ const unsigned char *decode, unsigned mod,
+ const unsigned char *in, unsigned size)
+{
+ char *buf = malloc(size);
+#ifdef DON
+ unsigned int offset = 0x4a14;
+#else
+ unsigned int offset = 0x4b14;
+#endif
+
+ /*
+ * The scrambling has odd boundaries. I think the boundaries
+ * match some data structure size, but I don't know. They were
+ * discovered the same way we dynamically discover the decode
+ * size: automatically looking for least random output.
+ *
+ * The boundaries are also this confused "off-by-one" thing,
+ * the same way the file size is off by one. It's as if the
+ * cochran software forgot to write one byte at the beginning.
+ */
+ partial_decode(0 , 0x0fff, decode, 1, mod, in, size, buf);
+ partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf);
+ partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf);
+ partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf);
+
+ /*
+ * This is not all the descrambling you need - the above are just
+ * what appears to be the fixed-size blocks. The rest is also
+ * scrambled, but there seems to be size differences in the data,
+ * so this just descrambles part of it:
+ */
+ partial_decode(0x48ff, offset, decode, 0, mod, in, size, buf);
+ partial_decode(offset, size, decode, 0, mod, in, size, buf);
+
+ printf("\n%s, dive %d\n\n", filename, dive);
+ cochran_debug_write(filename, buf, size);
+
+ free(buf);
+}
+
+int try_to_open_cochran(const char *filename, struct memblock *mem, GError **error)
+{
+ unsigned int i;
+ unsigned int mod;
+ unsigned int *offsets, dive1, dive2;
+ unsigned char *decode = mem->buffer + 0x40001;
+
+ if (mem->size < 0x40000)
+ return 0;
+ offsets = mem->buffer;
+ dive1 = offsets[0];
+ dive2 = offsets[1];
+ if (dive1 < 0x40000 || dive2 < dive1 || dive2 > mem->size)
+ return 0;
+
+ mod = figure_out_modulus(decode, mem->buffer + dive1, dive2 - dive1);
+
+ parse_cochran_header(filename, decode, mod, mem->buffer + 0x40000, dive1 - 0x40000);
+
+ for (i = 0; i < 65534; i++) {
+ dive1 = offsets[i];
+ dive2 = offsets[i+1];
+ if (dive2 < dive1)
+ break;
+ if (dive2 > mem->size)
+ break;
+ parse_cochran_dive(filename, i+1, decode, mod, mem->buffer + dive1, dive2 - dive1);
+ }
+
+ exit(0);
+}
diff --git a/display-gtk.h b/display-gtk.h
index b20495731..f12e42996 100644
--- a/display-gtk.h
+++ b/display-gtk.h
@@ -27,12 +27,25 @@ typedef enum {
#define BOOL_TO_PTR(_cond) ((_cond) ? (void *)1 : NULL)
#define PTR_TO_BOOL(_ptr) ((_ptr) != NULL)
+#if defined __APPLE__
+#define CTRLCHAR "<Meta>"
+#define PREFERENCE_ACCEL "<Meta>comma"
+#else
+#define CTRLCHAR "<Control>"
+#define PREFERENCE_ACCEL NULL
+#endif
+
extern void subsurface_open_conf(void);
extern void subsurface_set_conf(char *name, pref_type_t type, const void *value);
extern const void *subsurface_get_conf(char *name, pref_type_t type);
extern void subsurface_close_conf(void);
extern const char *subsurface_USB_name(void);
+extern const char *subsurface_icon_name(void);
+extern void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
+ GtkWidget *vbox, GtkUIManager *ui_manager);
+
+extern const char *divelist_font;
extern visible_cols_t visible_cols;
@@ -48,11 +61,16 @@ extern GtkWidget *dive_profile_widget(void);
extern GtkWidget *dive_info_frame(void);
extern GtkWidget *extended_dive_info_widget(void);
extern GtkWidget *equipment_widget(void);
-extern GtkWidget *stats_widget(void);
+extern GtkWidget *single_stats_widget(void);
+extern GtkWidget *total_stats_widget(void);
extern GtkWidget *cylinder_list_widget(void);
extern GtkWidget *dive_list_create(void);
+unsigned int amount_selected;
+
+extern void process_selected_dives(GList *, GtkTreeModel *);
+
typedef void (*data_func_t)(GtkTreeViewColumn *col,
GtkCellRenderer *renderer,
GtkTreeModel *model,
diff --git a/dive.c b/dive.c
index 3fa09bd10..9f57aed58 100644
--- a/dive.c
+++ b/dive.c
@@ -160,7 +160,7 @@ struct sample *prepare_sample(struct dive **divep)
return NULL;
}
-void finish_sample(struct dive *dive, struct sample *sample)
+void finish_sample(struct dive *dive)
{
dive->samples++;
}
@@ -234,6 +234,122 @@ static int same_rounded_pressure(pressure_t a, pressure_t b)
return abs(a.mbar - b.mbar) <= 500;
}
+static void sanitize_gasmix(struct gasmix *mix)
+{
+ unsigned int o2, he;
+
+ o2 = mix->o2.permille;
+ he = mix->he.permille;
+
+ /* Regular air: leave empty */
+ if (!he) {
+ if (!o2)
+ return;
+ /* 20.9% or 21% O2 is just air */
+ if (o2 >= 209 && o2 <= 210) {
+ mix->o2.permille = 0;
+ return;
+ }
+ }
+
+ /* Sane mix? */
+ if (o2 <= 1000 && he <= 1000 && o2+he <= 1000)
+ return;
+ fprintf(stderr, "Odd gasmix: %d O2 %d He\n", o2, he);
+ memset(mix, 0, sizeof(*mix));
+}
+
+/*
+ * See if the size/workingpressure looks like some standard cylinder
+ * size, eg "AL80".
+ */
+static void match_standard_cylinder(cylinder_type_t *type)
+{
+ double cuft;
+ int psi, len;
+ const char *fmt;
+ char buffer[20], *p;
+
+ /* Do we already have a cylinder description? */
+ if (type->description)
+ return;
+
+ cuft = ml_to_cuft(type->size.mliter);
+ cuft *= to_ATM(type->workingpressure);
+ psi = to_PSI(type->workingpressure);
+
+ switch (psi) {
+ case 2300 ... 2500: /* 2400 psi: LP tank */
+ fmt = "LP%d";
+ break;
+ case 2600 ... 2700: /* 2640 psi: LP+10% */
+ fmt = "LP%d";
+ break;
+ case 2900 ... 3100: /* 3000 psi: ALx tank */
+ fmt = "AL%d";
+ break;
+ case 3400 ... 3500: /* 3442 psi: HP tank */
+ fmt = "HP%d";
+ break;
+ case 3700 ... 3850: /* HP+10% */
+ fmt = "HP%d+";
+ break;
+ default:
+ return;
+ }
+ len = snprintf(buffer, sizeof(buffer), fmt, (int) (cuft+0.5));
+ p = malloc(len+1);
+ if (!p)
+ return;
+ memcpy(p, buffer, len+1);
+ type->description = p;
+}
+
+
+/*
+ * There are two ways to give cylinder size information:
+ * - total amount of gas in cuft (depends on working pressure and physical size)
+ * - physical size
+ *
+ * where "physical size" is the one that actually matters and is sane.
+ *
+ * We internally use physical size only. But we save the workingpressure
+ * so that we can do the conversion if required.
+ */
+static void sanitize_cylinder_type(cylinder_type_t *type)
+{
+ double volume_of_air, atm, volume;
+
+ /* If we have no working pressure, it had *better* be just a physical size! */
+ if (!type->workingpressure.mbar)
+ return;
+
+ /* No size either? Nothing to go on */
+ if (!type->size.mliter)
+ return;
+
+ if (input_units.volume == CUFT) {
+ /* confusing - we don't really start from ml but millicuft !*/
+ volume_of_air = cuft_to_l(type->size.mliter);
+ atm = to_ATM(type->workingpressure); /* working pressure in atm */
+ volume = volume_of_air / atm; /* milliliters at 1 atm: "true size" */
+ type->size.mliter = volume + 0.5;
+ }
+
+ /* Ok, we have both size and pressure: try to match a description */
+ match_standard_cylinder(type);
+}
+
+static void sanitize_cylinder_info(struct dive *dive)
+{
+ int i;
+
+ for (i = 0; i < MAX_CYLINDERS; i++) {
+ sanitize_gasmix(&dive->cylinder[i].gasmix);
+ sanitize_cylinder_type(&dive->cylinder[i].type);
+ }
+}
+
struct dive *fixup_dive(struct dive *dive)
{
int i,j;
@@ -246,6 +362,7 @@ struct dive *fixup_dive(struct dive *dive)
int lasttemp = 0, lastpressure = 0;
int pressure_delta[MAX_CYLINDERS] = {INT_MAX, };
+ sanitize_cylinder_info(dive);
for (i = 0; i < dive->samples; i++) {
struct sample *sample = dive->sample + i;
int time = sample->time.seconds;
@@ -373,7 +490,7 @@ static struct dive *add_sample(struct sample *sample, int time, struct dive *div
return NULL;
*p = *sample;
p->time.seconds = time;
- finish_sample(dive, p);
+ finish_sample(dive);
return dive;
}
diff --git a/dive.h b/dive.h
index bfaa076b1..39f203738 100644
--- a/dive.h
+++ b/dive.h
@@ -272,9 +272,11 @@ static inline struct dive *get_dive(unsigned int nr)
}
extern void parse_xml_init(void);
-extern void parse_xml_file(const char *filename, GError **error);
+extern void parse_xml_buffer(const char *url, const char *buf, int size, GError **error);
extern void set_filename(const char *filename);
+extern void parse_file(const char *filename, GError **error);
+
#ifdef XSLT
extern xmlDoc *test_xslt_transforms(xmlDoc *doc);
#endif
@@ -299,7 +301,7 @@ extern struct dive *alloc_dive(void);
extern void record_dive(struct dive *dive);
extern struct sample *prepare_sample(struct dive **divep);
-extern void finish_sample(struct dive *dive, struct sample *sample);
+extern void finish_sample(struct dive *dive);
extern void report_dives(gboolean imported);
extern struct dive *fixup_dive(struct dive *dive);
@@ -344,4 +346,6 @@ const char *monthname(int mon);
#define FIVE_STARS UTF8_BLACKSTAR UTF8_BLACKSTAR UTF8_BLACKSTAR UTF8_BLACKSTAR UTF8_BLACKSTAR
extern const char *star_strings[];
+#define AIR_PERMILLE 209
+
#endif /* DIVE_H */
diff --git a/divelist.c b/divelist.c
index f664cde49..2054ca017 100644
--- a/divelist.c
+++ b/divelist.c
@@ -73,6 +73,7 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
return;
case 1:
/* just pick that dive as selected */
+ amount_selected = 1;
path = g_list_nth_data(selected_dives, 0);
if (gtk_tree_model_get_iter(model, &iter, path)) {
gtk_tree_model_get_value(model, &iter, DIVE_INDEX, &value);
@@ -86,6 +87,9 @@ static void selection_cb(GtkTreeSelection *selection, GtkTreeModel *model)
* is the most intuitive solution.
* I do however want to keep around which dives have
* been selected */
+ amount_selected = g_list_length(selected_dives);
+ process_selected_dives(selected_dives, model);
+ repaint_dive();
return;
}
}
@@ -230,7 +234,7 @@ static void temperature_data_func(GtkTreeViewColumn *col,
* - Nitrox trumps air (even if hypoxic)
* These are the same rules as the inter-dive sorting rules.
*/
-static void get_dive_gas(struct dive *dive, int *o2, int *he, int *o2low)
+static void get_dive_gas(struct dive *dive, int *o2_p, int *he_p, int *o2low_p)
{
int i;
int maxo2 = -1, maxhe = -1, mino2 = 1000;
@@ -244,7 +248,7 @@ static void get_dive_gas(struct dive *dive, int *o2, int *he, int *o2low)
if (cylinder_none(cyl))
continue;
if (!o2)
- o2 = 209;
+ o2 = AIR_PERMILLE;
if (o2 < mino2)
mino2 = o2;
if (he > maxhe)
@@ -258,11 +262,11 @@ newmax:
maxo2 = o2;
}
/* All air? Show/sort as "air"/zero */
- if (!maxhe && maxo2 == 209 && mino2 == maxo2)
+ if (!maxhe && maxo2 == AIR_PERMILLE && mino2 == maxo2)
maxo2 = mino2 = 0;
- *o2 = maxo2;
- *he = maxhe;
- *o2low = mino2;
+ *o2_p = maxo2;
+ *he_p = maxhe;
+ *o2low_p = mino2;
}
static gint nitrox_sort_func(GtkTreeModel *model,
@@ -292,6 +296,8 @@ 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,
@@ -315,7 +321,7 @@ static void nitrox_data_func(GtkTreeViewColumn *col,
if (o2 == o2low)
snprintf(buffer, sizeof(buffer), "%d", o2);
else
- snprintf(buffer, sizeof(buffer), "%d-%d", o2low, o2);
+ snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
else
strcpy(buffer, "air");
@@ -390,8 +396,10 @@ static int calculate_otu(struct dive *dive)
struct sample *sample = dive->sample + i;
struct sample *psample = sample - 1;
t = sample->time.seconds - psample->time.seconds;
- po2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille / 1000.0 *
- (sample->depth.mm + 10000) / 10000.0;
+ int o2 = dive->cylinder[sample->cylinderindex].gasmix.o2.permille;
+ if (!o2)
+ o2 = AIR_PERMILLE;
+ po2 = o2 / 1000.0 * (sample->depth.mm + 10000) / 10000.0;
if (po2 >= 0.5)
otu += pow(po2 - 0.5, 0.83) * t / 30.0;
}
@@ -621,7 +629,7 @@ static struct divelist_column {
sort_func_t sort;
unsigned int flags;
int *visible;
-} column[] = {
+} dl_column[] = {
[DIVE_NR] = { "#", NULL, NULL, ALIGN_RIGHT | UNSORTABLE },
[DIVE_DATE] = { "Date", date_data_func, NULL, ALIGN_LEFT },
[DIVE_RATING] = { UTF8_BLACKSTAR, star_data_func, NULL, ALIGN_LEFT },
@@ -638,7 +646,7 @@ static struct divelist_column {
static GtkTreeViewColumn *divelist_column(struct DiveList *dl, struct divelist_column *col)
{
- int index = col - &column[0];
+ int index = col - &dl_column[0];
const char *title = col->header;
data_func_t data_func = col->data;
sort_func_t sort_func = col->sort;
@@ -705,17 +713,17 @@ GtkWidget *dive_list_create(void)
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
gtk_widget_set_size_request(dive_list.tree_view, 200, 200);
- dive_list.nr = divelist_column(&dive_list, column + DIVE_NR);
- dive_list.date = divelist_column(&dive_list, column + DIVE_DATE);
- dive_list.stars = divelist_column(&dive_list, column + DIVE_RATING);
- dive_list.depth = divelist_column(&dive_list, column + DIVE_DEPTH);
- dive_list.duration = divelist_column(&dive_list, column + DIVE_DURATION);
- dive_list.temperature = divelist_column(&dive_list, column + DIVE_TEMPERATURE);
- dive_list.cylinder = divelist_column(&dive_list, column + DIVE_CYLINDER);
- dive_list.nitrox = divelist_column(&dive_list, column + DIVE_NITROX);
- dive_list.sac = divelist_column(&dive_list, column + DIVE_SAC);
- dive_list.otu = divelist_column(&dive_list, column + DIVE_OTU);
- dive_list.location = divelist_column(&dive_list, column + DIVE_LOCATION);
+ dive_list.nr = divelist_column(&dive_list, dl_column + DIVE_NR);
+ dive_list.date = divelist_column(&dive_list, dl_column + DIVE_DATE);
+ dive_list.stars = divelist_column(&dive_list, dl_column + DIVE_RATING);
+ dive_list.depth = divelist_column(&dive_list, dl_column + DIVE_DEPTH);
+ dive_list.duration = divelist_column(&dive_list, dl_column + DIVE_DURATION);
+ dive_list.temperature = divelist_column(&dive_list, dl_column + DIVE_TEMPERATURE);
+ dive_list.cylinder = divelist_column(&dive_list, dl_column + DIVE_CYLINDER);
+ dive_list.nitrox = divelist_column(&dive_list, dl_column + DIVE_NITROX);
+ dive_list.sac = divelist_column(&dive_list, dl_column + DIVE_SAC);
+ dive_list.otu = divelist_column(&dive_list, dl_column + DIVE_OTU);
+ dive_list.location = divelist_column(&dive_list, dl_column + DIVE_LOCATION);
fill_dive_list();
diff --git a/equipment.c b/equipment.c
index 579c45542..abfb0e7b5 100644
--- a/equipment.c
+++ b/equipment.c
@@ -81,9 +81,8 @@ static int convert_pressure(int mbar, double *p)
return decimals;
}
-static int convert_volume_pressure(int ml, int mbar, double *v, double *p)
+static void convert_volume_pressure(int ml, int mbar, double *v, double *p)
{
- int decimals = 1;
double volume, pressure;
volume = ml / 1000.0;
@@ -95,13 +94,11 @@ static int convert_volume_pressure(int ml, int mbar, double *v, double *p)
if (output_units.pressure == PSI) {
pressure = mbar_to_PSI(mbar);
- decimals = 0;
} else
pressure = mbar / 1000.0;
}
*v = volume;
*p = pressure;
- return decimals;
}
static int convert_weight(int grams, double *m)
@@ -405,7 +402,7 @@ static void show_cylinder(cylinder_t *cyl, struct cylinder_widget *cylinder)
o2 = cyl->gasmix.o2.permille / 10.0;
he = cyl->gasmix.he.permille / 10.0;
if (!o2)
- o2 = 21.0;
+ o2 = AIR_PERMILLE / 10.0;
gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->o2), o2);
gtk_spin_button_set_value(GTK_SPIN_BUTTON(cylinder->he), he);
}
@@ -448,7 +445,7 @@ int weightsystem_none(void *_data)
return !ws->weight.grams && !ws->description;
}
-static void set_one_cylinder(int index, void *_data, GtkListStore *model, GtkTreeIter *iter)
+static void set_one_cylinder(void *_data, GtkListStore *model, GtkTreeIter *iter)
{
cylinder_t *cyl = _data;
unsigned int start, end;
@@ -466,7 +463,7 @@ static void set_one_cylinder(int index, void *_data, GtkListStore *model, GtkTre
-1);
}
-static void set_one_weightsystem(int index, void *_data, GtkListStore *model, GtkTreeIter *iter)
+static void set_one_weightsystem(void *_data, GtkListStore *model, GtkTreeIter *iter)
{
weightsystem_t *ws = _data;
@@ -494,7 +491,7 @@ static void show_equipment(struct dive *dive, int max,
struct equipment_list *equipment_list,
void*(*ptr_function)(struct dive*, int),
int(*none_function)(void *),
- void(*set_one_function)(int, void*, GtkListStore*, GtkTreeIter *))
+ void(*set_one_function)(void*, GtkListStore*, GtkTreeIter *))
{
int i, used;
void *data;
@@ -518,7 +515,7 @@ static void show_equipment(struct dive *dive, int max,
for (i = 0; i < used; i++) {
data = ptr_function(dive, i);
gtk_list_store_append(model, &iter);
- set_one_function(i, data, model, &iter);
+ set_one_function(data, model, &iter);
}
}
@@ -1073,7 +1070,7 @@ static void edit_cb(GtkButton *button, GtkTreeView *tree_view)
if (!edit_cylinder_dialog(index, &cyl))
return;
- set_one_cylinder(index, &cyl, model, &iter);
+ set_one_cylinder(&cyl, model, &iter);
repaint_dive();
}
@@ -1089,7 +1086,7 @@ static void add_cb(GtkButton *button, GtkTreeView *tree_view)
return;
gtk_list_store_append(model, &iter);
- set_one_cylinder(index, &cyl, model, &iter);
+ set_one_cylinder(&cyl, model, &iter);
selection = gtk_tree_view_get_selection(tree_view);
gtk_tree_selection_select_iter(selection, &iter);
@@ -1153,7 +1150,7 @@ static void ws_edit_cb(GtkButton *button, GtkTreeView *tree_view)
if (!edit_weightsystem_dialog(index, &ws))
return;
- set_one_weightsystem(index, &ws, model, &iter);
+ set_one_weightsystem(&ws, model, &iter);
repaint_dive();
}
@@ -1169,7 +1166,7 @@ static void ws_add_cb(GtkButton *button, GtkTreeView *tree_view)
return;
gtk_list_store_append(model, &iter);
- set_one_weightsystem(index, &ws, model, &iter);
+ set_one_weightsystem(&ws, model, &iter);
selection = gtk_tree_view_get_selection(tree_view);
gtk_tree_selection_select_iter(selection, &iter);
diff --git a/file.c b/file.c
new file mode 100644
index 000000000..aff1d51f2
--- /dev/null
+++ b/file.c
@@ -0,0 +1,136 @@
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "dive.h"
+#include "file.h"
+
+static int readfile(const char *filename, struct memblock *mem)
+{
+ int ret, fd = open(filename, O_RDONLY);
+ struct stat st;
+ char *buf;
+
+ mem->buffer = NULL;
+ mem->size = 0;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+ ret = fstat(fd, &st);
+ if (ret < 0)
+ goto out;
+ ret = -EINVAL;
+ if (!S_ISREG(st.st_mode))
+ goto out;
+ ret = 0;
+ if (!st.st_size)
+ goto out;
+ buf = malloc(st.st_size+1);
+ ret = -1;
+ errno = ENOMEM;
+ if (!buf)
+ goto out;
+ mem->buffer = buf;
+ mem->size = st.st_size;
+ ret = read(fd, buf, mem->size);
+ if (ret < 0)
+ goto free;
+ buf[ret] = 0;
+ if (ret == mem->size)
+ goto out;
+ errno = EIO;
+ ret = -1;
+free:
+ free(mem->buffer);
+ mem->buffer = NULL;
+ mem->size = 0;
+out:
+ close(fd);
+ return ret;
+}
+
+#ifdef LIBZIP
+#include <zip.h>
+
+static void suunto_read(struct zip_file *file, GError **error)
+{
+ int size = 1024, n, read = 0;
+ char *mem = malloc(size);
+
+ while ((n = zip_fread(file, mem+read, size-read)) > 0) {
+ read += n;
+ size = read * 3 / 2;
+ mem = realloc(mem, size);
+ }
+ parse_xml_buffer("SDE file", mem, read, error);
+ free(mem);
+}
+#endif
+
+static int try_to_open_suunto(const char *filename, struct memblock *mem, GError **error)
+{
+ int success = 0;
+#ifdef LIBZIP
+ /* Grr. libzip needs to re-open the file, it can't take a buffer */
+ struct zip *zip = zip_open(filename, ZIP_CHECKCONS, NULL);
+
+ if (zip) {
+ int index;
+ for (index = 0; ;index++) {
+ struct zip_file *file = zip_fopen_index(zip, index, 0);
+ if (!file)
+ break;
+ suunto_read(file, error);
+ zip_fclose(file);
+ success++;
+ }
+ zip_close(zip);
+ }
+#endif
+ return success;
+}
+
+static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, GError **error)
+{
+ /* Suunto Dive Manager files: SDE */
+ if (!strcasecmp(fmt, "SDE"))
+ return try_to_open_suunto(filename, mem, error);
+
+ /* Truly nasty intentionally obfuscated Cochran Anal software */
+ if (!strcasecmp(fmt, "CAN"))
+ return try_to_open_cochran(filename, mem, error);
+
+ return 0;
+}
+
+static void parse_file_buffer(const char *filename, struct memblock *mem, GError **error)
+{
+ char *fmt = strrchr(filename, '.');
+ if (fmt && open_by_filename(filename, fmt+1, mem, error))
+ return;
+
+ parse_xml_buffer(filename, mem->buffer, mem->size, error);
+}
+
+void parse_file(const char *filename, GError **error)
+{
+ struct memblock mem;
+
+ if (readfile(filename, &mem) < 0) {
+ fprintf(stderr, "Failed to read '%s'.\n", filename);
+ if (error) {
+ *error = g_error_new(g_quark_from_string("subsurface"),
+ DIVE_ERROR_PARSE,
+ "Failed to read '%s'",
+ filename);
+ }
+ return;
+ }
+
+ parse_file_buffer(filename, &mem, error);
+ free(mem.buffer);
+}
diff --git a/file.h b/file.h
new file mode 100644
index 000000000..df7e5eaf9
--- /dev/null
+++ b/file.h
@@ -0,0 +1,11 @@
+#ifndef FILE_H
+#define FILE_H
+
+struct memblock {
+ void *buffer;
+ size_t size;
+};
+
+extern int try_to_open_cochran(const char *filename, struct memblock *mem, GError **error);
+
+#endif
diff --git a/gtk-gui.c b/gtk-gui.c
index 371a0308f..7d0e95c43 100644
--- a/gtk-gui.c
+++ b/gtk-gui.c
@@ -7,6 +7,7 @@
#include <string.h>
#include <stdlib.h>
#include <time.h>
+#include <unistd.h>
#include "dive.h"
#include "divelist.h"
@@ -22,7 +23,6 @@ GtkWidget *error_label;
GtkWidget *vpane, *hpane;
int error_count;
-#define DIVELIST_DEFAULT_FONT "Sans 8"
const char *divelist_font;
struct units output_units;
@@ -107,14 +107,14 @@ static void file_open(GtkWidget *w, gpointer data)
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
- GSList *filenames;
+ GSList *filenames, *fn_glist;
char *filename;
- filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
+ filenames = fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
GError *error = NULL;
while(filenames != NULL) {
filename = filenames->data;
- parse_xml_file(filename, &error);
+ parse_file(filename, &error);
if (error != NULL)
{
report_error(error);
@@ -125,7 +125,7 @@ static void file_open(GtkWidget *w, gpointer data)
g_free(filename);
filenames = g_slist_next(filenames);
}
- g_slist_free(filenames);
+ g_slist_free(fn_glist);
report_dives(FALSE);
}
gtk_widget_destroy(dialog);
@@ -245,7 +245,7 @@ GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char
return col;
}
-static void create_radio(GtkWidget *vbox, const char *name, ...)
+static void create_radio(GtkWidget *vbox, const char *w_name, ...)
{
va_list args;
GtkRadioButton *group = NULL;
@@ -254,10 +254,10 @@ static void create_radio(GtkWidget *vbox, const char *name, ...)
box = gtk_hbox_new(TRUE, 10);
gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0);
- label = gtk_label_new(name);
+ label = gtk_label_new(w_name);
gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
- va_start(args, name);
+ va_start(args, w_name);
for (;;) {
int enabled;
const char *name;
@@ -531,11 +531,7 @@ static void about_dialog(GtkWidget *w, gpointer data)
GdkPixbuf *logo = NULL;
if (need_icon) {
-#if defined __linux__ || defined __APPLE__
- GtkWidget *image = gtk_image_new_from_file("subsurface.svg");
-#elif defined WIN32
- GtkWidget *image = gtk_image_new_from_file("subsurface.ico");
-#endif
+ GtkWidget *image = gtk_image_new_from_file(subsurface_icon_name());
if (image) {
logo = gtk_image_get_pixbuf(GTK_IMAGE(image));
@@ -585,19 +581,19 @@ static GtkActionEntry menu_items[] = {
{ "ViewMenuAction", GTK_STOCK_FILE, "View", NULL, NULL, NULL},
{ "FilterMenuAction", GTK_STOCK_FILE, "Filter", NULL, NULL, NULL},
{ "HelpMenuAction", GTK_STOCK_HELP, "Help", NULL, NULL, NULL},
- { "OpenFile", GTK_STOCK_OPEN, NULL, "<control>O", NULL, G_CALLBACK(file_open) },
- { "SaveFile", GTK_STOCK_SAVE, NULL, "<control>S", NULL, G_CALLBACK(file_save) },
- { "Print", GTK_STOCK_PRINT, NULL, "<control>P", NULL, G_CALLBACK(do_print) },
+ { "OpenFile", GTK_STOCK_OPEN, NULL, CTRLCHAR "O", NULL, G_CALLBACK(file_open) },
+ { "SaveFile", GTK_STOCK_SAVE, NULL, CTRLCHAR "S", NULL, G_CALLBACK(file_save) },
+ { "Print", GTK_STOCK_PRINT, NULL, CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
{ "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
- { "Preferences", NULL, "Preferences", NULL, NULL, G_CALLBACK(preferences_dialog) },
+ { "Preferences", NULL, "Preferences", PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
{ "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) },
{ "SelectEvents", NULL, "SelectEvents", NULL, NULL, G_CALLBACK(selectevents_dialog) },
- { "Quit", GTK_STOCK_QUIT, NULL, "<control>Q", NULL, G_CALLBACK(quit) },
+ { "Quit", GTK_STOCK_QUIT, NULL, CTRLCHAR "Q", NULL, G_CALLBACK(quit) },
{ "About", GTK_STOCK_ABOUT, NULL, NULL, NULL, G_CALLBACK(about_dialog) },
- { "ViewList", NULL, "List", "<control>1", NULL, G_CALLBACK(view_list) },
- { "ViewProfile", NULL, "Profile", "<control>2", NULL, G_CALLBACK(view_profile) },
- { "ViewInfo", NULL, "Info", "<control>3", NULL, G_CALLBACK(view_info) },
- { "ViewThree", NULL, "Three", "<control>4", NULL, G_CALLBACK(view_three) },
+ { "ViewList", NULL, "List", CTRLCHAR "1", NULL, G_CALLBACK(view_list) },
+ { "ViewProfile", NULL, "Profile", CTRLCHAR "2", NULL, G_CALLBACK(view_profile) },
+ { "ViewInfo", NULL, "Info", CTRLCHAR "3", NULL, G_CALLBACK(view_info) },
+ { "ViewThree", NULL, "Three", CTRLCHAR "4", NULL, G_CALLBACK(view_three) },
};
static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
@@ -634,12 +630,11 @@ static const gchar* ui_string = " \
</ui> \
";
-static GtkWidget *get_menubar_menu(GtkWidget *window)
+static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
{
GtkActionGroup *action_group = gtk_action_group_new("Menu");
gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
- GtkUIManager *ui_manager = gtk_ui_manager_new();
gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
GError* error = 0;
gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
@@ -659,15 +654,14 @@ void init_ui(int *argcp, char ***argvp)
{
GtkWidget *win;
GtkWidget *notebook;
- GtkWidget *dive_info;
+ GtkWidget *nb_page;
GtkWidget *dive_list;
- GtkWidget *equipment;
- GtkWidget *stats;
GtkWidget *menubar;
GtkWidget *vbox;
GdkScreen *screen;
GtkIconTheme *icon_theme=NULL;
GtkSettings *settings;
+ GtkUIManager *ui_manager;
gtk_init(argcp, argvp);
settings = gtk_settings_get_default();
@@ -695,9 +689,6 @@ void init_ui(int *argcp, char ***argvp)
divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
- if (!divelist_font)
- divelist_font = DIVELIST_DEFAULT_FONT;
-
error_info_bar = NULL;
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_set_application_name ("subsurface");
@@ -712,12 +703,11 @@ void init_ui(int *argcp, char ***argvp)
gtk_window_set_default_icon_name ("subsurface");
}
}
- if (need_icon)
-#if defined __linux__ || defined __APPLE__
- gtk_window_set_icon_from_file(GTK_WINDOW(win), "subsurface.svg", NULL);
-#elif defined WIN32
- gtk_window_set_icon_from_file(GTK_WINDOW(win), "subsurface.ico", NULL);
-#endif
+ if (need_icon) {
+ const char *icon_name = subsurface_icon_name();
+ if (!access(icon_name, R_OK))
+ gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL);
+ }
g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL);
g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL);
main_window = win;
@@ -726,8 +716,10 @@ void init_ui(int *argcp, char ***argvp)
gtk_container_add(GTK_CONTAINER(win), vbox);
main_vbox = vbox;
- menubar = get_menubar_menu(win);
- gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
+ ui_manager = gtk_ui_manager_new();
+ menubar = get_menubar_menu(win, ui_manager);
+
+ subsurface_ui_setup(settings, menubar, vbox, ui_manager);
vpane = gtk_vpaned_new();
gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3);
@@ -751,16 +743,20 @@ void init_ui(int *argcp, char ***argvp)
gtk_paned_add2(GTK_PANED(hpane), dive_profile);
/* Frame for extended dive info */
- dive_info = extended_dive_info_widget();
- gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_info, gtk_label_new("Dive Notes"));
+ nb_page = extended_dive_info_widget();
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Notes"));
/* Frame for dive equipment */
- equipment = equipment_widget();
- gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment"));
+ nb_page = equipment_widget();
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Equipment"));
+
+ /* Frame for single dive statistics */
+ nb_page = single_stats_widget();
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Dive Info"));
- /* Frame for dive statistics */
- stats = stats_widget();
- gtk_notebook_append_page(GTK_NOTEBOOK(notebook), stats, gtk_label_new("Info & Stats"));
+ /* Frame for total dive statistics */
+ nb_page = total_stats_widget();
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new("Stats"));
gtk_widget_set_app_paintable(win, TRUE);
gtk_widget_show_all(win);
@@ -975,7 +971,7 @@ static GtkWidget *xml_file_selector(GtkWidget *vbox, GtkWidget *main_dialog)
static void do_import_file(gpointer data, gpointer user_data)
{
GError *error = NULL;
- parse_xml_file(data, &error);
+ parse_file(data, &error);
if (error != NULL)
{
diff --git a/info.c b/info.c
index e53af015e..6a4a296b0 100644
--- a/info.c
+++ b/info.c
@@ -258,13 +258,13 @@ void add_location(const char *string)
static int get_rating(const char *string)
{
- int rating = 0;
+ int rating_val = 0;
int i;
for (i = 0; i <= 5; i++)
if (!strcmp(star_strings[i],string))
- rating = i;
- return rating;
+ rating_val = i;
+ return rating_val;
}
struct dive_info {
diff --git a/libdivecomputer.c b/libdivecomputer.c
index 9d4c1065a..4ff41486d 100644
--- a/libdivecomputer.c
+++ b/libdivecomputer.c
@@ -109,7 +109,7 @@ static int parse_gasmixes(struct dive *dive, parser_t *parser, int ngases)
he = gasmix.helium * 1000 + 0.5;
/* Ignore bogus data - libdivecomputer does some crazy stuff */
- if (o2 < 210 || o2 >= 1000)
+ if (o2 <= AIR_PERMILLE || o2 >= 1000)
o2 = 0;
if (he < 0 || he >= 800 || o2+he >= 1000)
he = 0;
@@ -175,7 +175,7 @@ sample_cb(parser_sample_type_t type, parser_sample_value_t value, void *userdata
case SAMPLE_TYPE_TIME:
sample = prepare_sample(divep);
sample->time.seconds = value.time;
- finish_sample(*divep, sample);
+ finish_sample(*divep);
break;
case SAMPLE_TYPE_DEPTH:
sample->depth.mm = value.depth * 1000 + 0.5;
diff --git a/linux.c b/linux.c
index ac5cf55f9..f0ab2841c 100644
--- a/linux.c
+++ b/linux.c
@@ -2,6 +2,7 @@
/* implements Linux specific functions */
#include "display-gtk.h"
#include <gconf/gconf-client.h>
+#define DIVELIST_DEFAULT_FONT "Sans 8"
GConfClient *gconf;
@@ -51,3 +52,16 @@ const char *subsurface_USB_name()
{
return "/dev/ttyUSB0";
}
+
+const char *subsurface_icon_name()
+{
+ return "subsurface.svg";
+}
+
+void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
+ GtkWidget *vbox, GtkUIManager *ui_manager)
+{
+ if (!divelist_font)
+ divelist_font = DIVELIST_DEFAULT_FONT;
+ gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
+}
diff --git a/macos.c b/macos.c
index 25f946c81..f6c4bd6c0 100644
--- a/macos.c
+++ b/macos.c
@@ -2,10 +2,10 @@
/* implements Mac OS X specific functions */
#include "display-gtk.h"
#include <CoreFoundation/CoreFoundation.h>
+#include <mach-o/dyld.h>
+#include "gtkosxapplication.h"
-static CFURLRef fileURL;
-static CFPropertyListRef propertyList;
-static CFMutableDictionaryRef dict = NULL;
+static GtkOSXApplication *osx_app;
/* macos defines CFSTR to create a CFString object from a constant,
* but no similar macros if a C string variable is supposed to be
@@ -15,61 +15,45 @@ static CFMutableDictionaryRef dict = NULL;
(_var), kCFStringEncodingMacRoman, \
kCFAllocatorNull)
+#define SUBSURFACE_PREFERENCES CFSTR("org.hohndel.subsurface")
+#define ICON_NAME "Subsurface.icns"
+#define UI_FONT "Arial Unicode MS 12"
+#define DIVELIST_MAC_DEFAULT_FONT "Arial Unicode MS 9"
+
void subsurface_open_conf(void)
{
- CFStringRef errorString;
- CFDataRef resourceData;
- Boolean status;
- SInt32 errorCode;
-
- fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
- CFSTR("subsurface.pref"),// file path name
- kCFURLPOSIXPathStyle, // interpret as POSIX path
- false ); // is it a directory?
-
- status = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault,
- fileURL, &resourceData,
- NULL, NULL, &errorCode);
- if (status) {
- propertyList = CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
- resourceData, kCFPropertyListImmutable,
- &errorString);
- CFRelease(resourceData);
- }
+ /* nothing at this time */
}
void subsurface_set_conf(char *name, pref_type_t type, const void *value)
{
- if (!dict)
- dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
- &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
switch (type) {
case PREF_BOOL:
- CFDictionarySetValue(dict, CFSTR_VAR(name), value == NULL ? CFSTR("0") : CFSTR("1"));
+ CFPreferencesSetAppValue(CFSTR_VAR(name),
+ value == NULL ? kCFBooleanFalse : kCFBooleanTrue, SUBSURFACE_PREFERENCES);
break;
case PREF_STRING:
- CFDictionarySetValue(dict, CFSTR_VAR(name), CFSTR_VAR(value));
+ CFPreferencesSetAppValue(CFSTR_VAR(name), CFSTR_VAR(value), SUBSURFACE_PREFERENCES);
}
}
+
const void *subsurface_get_conf(char *name, pref_type_t type)
{
- CFStringRef dict_entry;
-
- /* if no settings exist, we return the value for FALSE */
- if (!propertyList)
- return NULL;
+ Boolean boolpref;
+ CFPropertyListRef strpref;
switch (type) {
case PREF_BOOL:
- dict_entry = CFDictionaryGetValue(propertyList, CFSTR_VAR(name));
- if (dict_entry && ! CFStringCompare(CFSTR("1"), dict_entry, 0))
+ boolpref = CFPreferencesGetAppBooleanValue(CFSTR_VAR(name), SUBSURFACE_PREFERENCES, FALSE);
+ if (boolpref)
return (void *) 1;
else
return NULL;
case PREF_STRING:
- return CFStringGetCStringPtr(CFDictionaryGetValue(propertyList,
- CFSTR_VAR(name)), kCFStringEncodingMacRoman);
+ strpref = CFPreferencesCopyAppValue(CFSTR_VAR(name), SUBSURFACE_PREFERENCES);
+ if (!strpref)
+ return NULL;
+ return CFStringGetCStringPtr(strpref, kCFStringEncodingMacRoman);
}
/* we shouldn't get here, but having this line makes the compiler happy */
return NULL;
@@ -77,20 +61,59 @@ const void *subsurface_get_conf(char *name, pref_type_t type)
void subsurface_close_conf(void)
{
- Boolean status;
- SInt32 errorCode;
- CFDataRef xmlData;
-
- propertyList = dict;
- dict = NULL;
- xmlData = CFPropertyListCreateXMLData(kCFAllocatorDefault, propertyList);
- status = CFURLWriteDataAndPropertiesToResource (fileURL, xmlData, NULL, &errorCode);
- // some error handling - but really, what can we do?
- CFRelease(xmlData);
- CFRelease(propertyList);
+ int ok = CFPreferencesAppSynchronize(SUBSURFACE_PREFERENCES);
+ if (!ok)
+ fprintf(stderr,"Could not save preferences\n");
}
const char *subsurface_USB_name()
{
return "/dev/tty.SLAB_USBtoUART";
}
+
+const char *subsurface_icon_name()
+{
+ static char path[1024];
+
+ snprintf(path, 1024, "%s/%s", quartz_application_get_resource_path(), ICON_NAME);
+
+ return path;
+}
+
+void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
+ GtkWidget *vbox, GtkUIManager *ui_manager)
+{
+ GtkWidget *menu_item, *sep;
+
+ if (!divelist_font)
+ divelist_font = DIVELIST_MAC_DEFAULT_FONT;
+ g_object_set(G_OBJECT(settings), "gtk-font-name", UI_FONT, NULL);
+
+ osx_app = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
+ gtk_widget_hide (menubar);
+ gtk_osxapplication_set_menu_bar(osx_app, GTK_MENU_SHELL(menubar));
+
+ sep = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Separator3");
+ gtk_widget_destroy(sep);
+ sep = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Separator2");
+ gtk_widget_destroy(sep);
+
+ menu_item = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Quit");
+ gtk_widget_hide (menu_item);
+ menu_item = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/Help/About");
+ gtk_osxapplication_insert_app_menu_item(osx_app, menu_item, 0);
+
+ sep = gtk_separator_menu_item_new();
+ g_object_ref(sep);
+ gtk_osxapplication_insert_app_menu_item (osx_app, sep, 1);
+
+ menu_item = gtk_ui_manager_get_widget(ui_manager, "/MainMenu/FileMenu/Preferences");
+ gtk_osxapplication_insert_app_menu_item(osx_app, menu_item, 2);
+
+ sep = gtk_separator_menu_item_new();
+ g_object_ref(sep);
+ gtk_osxapplication_insert_app_menu_item (osx_app, sep, 3);
+
+ gtk_osxapplication_set_use_quartz_accelerators(osx_app, TRUE);
+ gtk_osxapplication_ready(osx_app);
+}
diff --git a/main.c b/main.c
index d77fcd2d9..eae4ee239 100644
--- a/main.c
+++ b/main.c
@@ -101,7 +101,7 @@ 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 imported)
+void report_dives(gboolean is_imported)
{
int i;
int preexisting = dive_table.preexisting;
@@ -135,7 +135,7 @@ void report_dives(gboolean imported)
i--;
}
- if (imported) {
+ if (is_imported) {
/* Was the previous dive table state numbered? */
if (last && last->number)
try_to_renumber(last, preexisting);
@@ -226,7 +226,7 @@ int main(int argc, char **argv)
continue;
}
GError *error = NULL;
- parse_xml_file(a, &error);
+ parse_file(a, &error);
if (error != NULL)
{
diff --git a/packaging/macosx/Info.plist b/packaging/macosx/Info.plist
index 4ee12438b..f3ac10cdd 100644
--- a/packaging/macosx/Info.plist
+++ b/packaging/macosx/Info.plist
@@ -13,8 +13,8 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleExecutable</key>
- <string>subsurface.sh</string>
+ <string>subsurface</string>
<key>CFBundleIdentifier</key>
- <string>torvalds.subsurface</string>
+ <string>org.hohndel.subsurface</string>
</dict>
</plist>
diff --git a/packaging/macosx/subsurface.sh b/packaging/macosx/subsurface.sh
deleted file mode 100755
index ee9427c37..000000000
--- a/packaging/macosx/subsurface.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-cd `dirname $0`/../Resources
-../MacOS/subsurface &
-exit 0
diff --git a/packaging/windows/subsurface.nsi b/packaging/windows/subsurface.nsi
index 09a6eb0e6..280991f69 100644
--- a/packaging/windows/subsurface.nsi
+++ b/packaging/windows/subsurface.nsi
@@ -80,11 +80,14 @@ file /oname=libpangoft2-1.0-0.dll dll\libpangoft2-1.0-0.dll
file /oname=libpangowin32-1.0-0.dll dll\libpangowin32-1.0-0.dll
file /oname=libpixman-1-0.dll dll\libpixman-1-0.dll
file /oname=libpng15-15.dll dll\libpng15-15.dll
-file /oname=libtiff-3.dll dll\libtiff-3.dll
+file /oname=libtiff-5.dll dll\libtiff-5.dll
file /oname=libxml2-2.dll dll\libxml2-2.dll
file /oname=libxslt-1.dll dll\libxslt-1.dll
file /oname=pthreadGC2.dll dll\pthreadGC2.dll
file /oname=zlib1.dll dll\zlib1.dll
+file /oname=libusb-1.0.dll dll\libusb-1.0.dll
+file /oname=SuuntoSDM.xslt ../../xslt/SuuntoSDM.xslt
+file /oname=jdivelog2subsurface.xslt ../../xslt/jdivelog2subsurface.xslt
sectionEnd
section "uninstall"
diff --git a/parse-xml.c b/parse-xml.c
index 34afdb9f6..e920a11f6 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -97,16 +97,16 @@ const struct units IMPERIAL_units = {
/*
* Dive info as it is being built up..
*/
-static struct dive *dive;
-static struct sample *sample;
+static struct dive *cur_dive;
+static struct sample *cur_sample;
static struct {
int active;
duration_t time;
int type, flags, value;
const char *name;
-} event;
-static struct tm tm;
-static int cylinder_index, ws_index;
+} cur_event;
+static struct tm cur_tm;
+static int cur_cylinder_index, cur_ws_index;
static enum import_source {
UNKNOWN,
@@ -152,22 +152,22 @@ static void divedate(char *buffer, void *_when)
time_t *when = _when;
int success = 0;
- success = tm.tm_sec | tm.tm_min | tm.tm_hour;
+ success = cur_tm.tm_sec | cur_tm.tm_min | cur_tm.tm_hour;
if (sscanf(buffer, "%d.%d.%d", &d, &m, &y) == 3) {
- tm.tm_year = y;
- tm.tm_mon = m-1;
- tm.tm_mday = d;
+ cur_tm.tm_year = y;
+ cur_tm.tm_mon = m-1;
+ cur_tm.tm_mday = d;
} else if (sscanf(buffer, "%d-%d-%d", &y, &m, &d) == 3) {
- tm.tm_year = y;
- tm.tm_mon = m-1;
- tm.tm_mday = d;
+ cur_tm.tm_year = y;
+ cur_tm.tm_mon = m-1;
+ cur_tm.tm_mday = d;
} else {
fprintf(stderr, "Unable to parse date '%s'\n", buffer);
success = 0;
}
if (success)
- *when = utc_mktime(&tm);
+ *when = utc_mktime(&cur_tm);
free(buffer);
}
@@ -178,11 +178,11 @@ static void divetime(char *buffer, void *_when)
time_t *when = _when;
if (sscanf(buffer, "%d:%d:%d", &h, &m, &s) >= 2) {
- tm.tm_hour = h;
- tm.tm_min = m;
- tm.tm_sec = s;
- if (tm.tm_year)
- *when = utc_mktime(&tm);
+ cur_tm.tm_hour = h;
+ cur_tm.tm_min = m;
+ cur_tm.tm_sec = s;
+ if (cur_tm.tm_year)
+ *when = utc_mktime(&cur_tm);
}
free(buffer);
}
@@ -196,13 +196,13 @@ static void divedatetime(char *buffer, void *_when)
if (sscanf(buffer, "%d-%d-%d %d:%d:%d",
&y, &m, &d, &hr, &min, &sec) == 6) {
- tm.tm_year = y;
- tm.tm_mon = m-1;
- tm.tm_mday = d;
- tm.tm_hour = hr;
- tm.tm_min = min;
- tm.tm_sec = sec;
- *when = utc_mktime(&tm);
+ cur_tm.tm_year = y;
+ cur_tm.tm_mon = m-1;
+ cur_tm.tm_mday = d;
+ cur_tm.tm_hour = hr;
+ cur_tm.tm_min = min;
+ cur_tm.tm_sec = sec;
+ *when = utc_mktime(&cur_tm);
}
free(buffer);
}
@@ -398,7 +398,7 @@ static void gasmix(char *buffer, void *_fraction)
/* libdivecomputer does negative percentages. */
if (*buffer == '-')
return;
- if (cylinder_index < MAX_CYLINDERS)
+ if (cur_cylinder_index < MAX_CYLINDERS)
percent(buffer, _fraction);
}
@@ -595,8 +595,8 @@ static void eventtime(char *buffer, void *_duration)
{
duration_t *duration = _duration;
sampletime(buffer, duration);
- if (sample)
- duration->seconds += sample->time.seconds;
+ if (cur_sample)
+ duration->seconds += cur_sample->time.seconds;
}
static void try_to_fill_event(const char *name, char *buf)
@@ -604,17 +604,17 @@ static void try_to_fill_event(const char *name, char *buf)
int len = strlen(name);
start_match("event", name, buf);
- if (MATCH(".event", utf8_string, &event.name))
+ if (MATCH(".event", utf8_string, &cur_event.name))
return;
- if (MATCH(".name", utf8_string, &event.name))
+ if (MATCH(".name", utf8_string, &cur_event.name))
return;
- if (MATCH(".time", eventtime, &event.time))
+ if (MATCH(".time", eventtime, &cur_event.time))
return;
- if (MATCH(".type", get_index, &event.type))
+ if (MATCH(".type", get_index, &cur_event.type))
return;
- if (MATCH(".flags", get_index, &event.flags))
+ if (MATCH(".flags", get_index, &cur_event.flags))
return;
- if (MATCH(".value", get_index, &event.value))
+ if (MATCH(".value", get_index, &cur_event.value))
return;
nonmatch("event", name, buf);
}
@@ -826,7 +826,7 @@ static int uemis_gas_template;
static int uemis_cylinder_index(void *_cylinder)
{
cylinder_t *cylinder = _cylinder;
- unsigned int index = cylinder - dive->cylinder;
+ unsigned int index = cylinder - cur_dive->cylinder;
if (index > 6) {
fprintf(stderr, "Uemis cylinder pointer calculations broken\n");
@@ -858,14 +858,14 @@ static void uemis_cylindersize(char *buffer, void *_cylinder)
{
int index = uemis_cylinder_index(_cylinder);
if (index >= 0)
- cylindersize(buffer, &dive->cylinder[index].type.size);
+ cylindersize(buffer, &cur_dive->cylinder[index].type.size);
}
static void uemis_percent(char *buffer, void *_cylinder)
{
int index = uemis_cylinder_index(_cylinder);
if (index >= 0)
- percent(buffer, &dive->cylinder[index].gasmix.o2);
+ percent(buffer, &cur_dive->cylinder[index].gasmix.o2);
}
static int uemis_dive_match(struct dive **divep, const char *name, int len, char *buf)
@@ -1048,27 +1048,27 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf)
return;
if (MATCH(".rating", get_index, &dive->rating))
return;
- if (MATCH(".cylinder.size", cylindersize, &dive->cylinder[cylinder_index].type.size))
+ if (MATCH(".cylinder.size", cylindersize, &dive->cylinder[cur_cylinder_index].type.size))
return;
- if (MATCH(".cylinder.workpressure", pressure, &dive->cylinder[cylinder_index].type.workingpressure))
+ if (MATCH(".cylinder.workpressure", pressure, &dive->cylinder[cur_cylinder_index].type.workingpressure))
return;
- if (MATCH(".cylinder.description", utf8_string, &dive->cylinder[cylinder_index].type.description))
+ if (MATCH(".cylinder.description", utf8_string, &dive->cylinder[cur_cylinder_index].type.description))
return;
- if (MATCH(".cylinder.start", pressure, &dive->cylinder[cylinder_index].start))
+ if (MATCH(".cylinder.start", pressure, &dive->cylinder[cur_cylinder_index].start))
return;
- if (MATCH(".cylinder.end", pressure, &dive->cylinder[cylinder_index].end))
+ if (MATCH(".cylinder.end", pressure, &dive->cylinder[cur_cylinder_index].end))
return;
- if (MATCH(".weightsystem.description", utf8_string, &dive->weightsystem[ws_index].description))
+ if (MATCH(".weightsystem.description", utf8_string, &dive->weightsystem[cur_ws_index].description))
return;
- if (MATCH(".weightsystem.weight", weight, &dive->weightsystem[ws_index].weight))
+ if (MATCH(".weightsystem.weight", weight, &dive->weightsystem[cur_ws_index].weight))
return;
- if (MATCH("weight", weight, &dive->weightsystem[ws_index].weight))
+ if (MATCH("weight", weight, &dive->weightsystem[cur_ws_index].weight))
return;
- if (MATCH(".o2", gasmix, &dive->cylinder[cylinder_index].gasmix.o2))
+ if (MATCH(".o2", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.o2))
return;
- if (MATCH(".n2", gasmix_nitrogen, &dive->cylinder[cylinder_index].gasmix))
+ if (MATCH(".n2", gasmix_nitrogen, &dive->cylinder[cur_cylinder_index].gasmix))
return;
- if (MATCH(".he", gasmix, &dive->cylinder[cylinder_index].gasmix.he))
+ if (MATCH(".he", gasmix, &dive->cylinder[cur_cylinder_index].gasmix.he))
return;
nonmatch("dive", name, buf);
@@ -1082,150 +1082,35 @@ static void try_to_fill_dive(struct dive **divep, const char *name, char *buf)
*/
static void dive_start(void)
{
- if (dive)
+ if (cur_dive)
return;
- dive = alloc_dive();
- memset(&tm, 0, sizeof(tm));
-}
-
-static void sanitize_gasmix(struct gasmix *mix)
-{
- unsigned int o2, he;
-
- o2 = mix->o2.permille;
- he = mix->he.permille;
-
- /* Regular air: leave empty */
- if (!he) {
- if (!o2)
- return;
- /* 20.9% or 21% O2 is just air */
- if (o2 >= 209 && o2 <= 210) {
- mix->o2.permille = 0;
- return;
- }
- }
-
- /* Sane mix? */
- if (o2 <= 1000 && he <= 1000 && o2+he <= 1000)
- return;
- fprintf(stderr, "Odd gasmix: %d O2 %d He\n", o2, he);
- memset(mix, 0, sizeof(*mix));
-}
-
-/*
- * See if the size/workingpressure looks like some standard cylinder
- * size, eg "AL80".
- */
-static void match_standard_cylinder(cylinder_type_t *type)
-{
- double cuft;
- int psi, len;
- const char *fmt;
- char buffer[20], *p;
-
- /* Do we already have a cylinder description? */
- if (type->description)
- return;
-
- cuft = ml_to_cuft(type->size.mliter);
- cuft *= to_ATM(type->workingpressure);
- psi = to_PSI(type->workingpressure);
-
- switch (psi) {
- case 2300 ... 2500: /* 2400 psi: LP tank */
- fmt = "LP%d";
- break;
- case 2600 ... 2700: /* 2640 psi: LP+10% */
- fmt = "LP%d";
- break;
- case 2900 ... 3100: /* 3000 psi: ALx tank */
- fmt = "AL%d";
- break;
- case 3400 ... 3500: /* 3442 psi: HP tank */
- fmt = "HP%d";
- break;
- case 3700 ... 3850: /* HP+10% */
- fmt = "HP%d+";
- break;
- default:
- return;
- }
- len = snprintf(buffer, sizeof(buffer), fmt, (int) (cuft+0.5));
- p = malloc(len+1);
- if (!p)
- return;
- memcpy(p, buffer, len+1);
- type->description = p;
-}
-
-
-/*
- * There are two ways to give cylinder size information:
- * - total amount of gas in cuft (depends on working pressure and physical size)
- * - physical size
- *
- * where "physical size" is the one that actually matters and is sane.
- *
- * We internally use physical size only. But we save the workingpressure
- * so that we can do the conversion if required.
- */
-static void sanitize_cylinder_type(cylinder_type_t *type)
-{
- double volume_of_air, atm, volume;
-
- /* If we have no working pressure, it had *better* be just a physical size! */
- if (!type->workingpressure.mbar)
- return;
-
- /* No size either? Nothing to go on */
- if (!type->size.mliter)
- return;
-
- if (input_units.volume == CUFT) {
- /* confusing - we don't really start from ml but millicuft !*/
- volume_of_air = cuft_to_l(type->size.mliter);
- atm = to_ATM(type->workingpressure); /* working pressure in atm */
- volume = volume_of_air / atm; /* milliliters at 1 atm: "true size" */
- type->size.mliter = volume + 0.5;
- }
-
- /* Ok, we have both size and pressure: try to match a description */
- match_standard_cylinder(type);
-}
-
-static void sanitize_cylinder_info(struct dive *dive)
-{
- int i;
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- sanitize_gasmix(&dive->cylinder[i].gasmix);
- sanitize_cylinder_type(&dive->cylinder[i].type);
- }
+ cur_dive = alloc_dive();
+ memset(&cur_tm, 0, sizeof(cur_tm));
}
static void dive_end(void)
{
- if (!dive)
+ if (!cur_dive)
return;
- sanitize_cylinder_info(dive);
- record_dive(dive);
- dive = NULL;
- cylinder_index = 0;
- ws_index = 0;
+ record_dive(cur_dive);
+ cur_dive = NULL;
+ cur_cylinder_index = 0;
+ cur_ws_index = 0;
}
static void event_start(void)
{
- memset(&event, 0, sizeof(event));
- event.active = 1;
+ memset(&cur_event, 0, sizeof(cur_event));
+ cur_event.active = 1;
}
static void event_end(void)
{
- if (event.name && strcmp(event.name, "surface") != 0)
- add_event(dive, event.time.seconds, event.type, event.flags, event.value, event.name);
- event.active = 0;
+ if (cur_event.name && strcmp(cur_event.name, "surface") != 0)
+ add_event(cur_dive, cur_event.time.seconds,
+ cur_event.type, cur_event.flags,
+ cur_event.value, cur_event.name);
+ cur_event.active = 0;
}
static void cylinder_start(void)
@@ -1234,7 +1119,7 @@ static void cylinder_start(void)
static void cylinder_end(void)
{
- cylinder_index++;
+ cur_cylinder_index++;
}
static void ws_start(void)
@@ -1243,21 +1128,21 @@ static void ws_start(void)
static void ws_end(void)
{
- ws_index++;
+ cur_ws_index++;
}
static void sample_start(void)
{
- sample = prepare_sample(&dive);
+ cur_sample = prepare_sample(&cur_dive);
}
static void sample_end(void)
{
- if (!dive)
+ if (!cur_dive)
return;
- finish_sample(dive, sample);
- sample = NULL;
+ finish_sample(cur_dive);
+ cur_sample = NULL;
}
static void entry(const char *name, int size, const char *raw)
@@ -1268,16 +1153,16 @@ static void entry(const char *name, int size, const char *raw)
return;
memcpy(buf, raw, size);
buf[size] = 0;
- if (event.active) {
+ if (cur_event.active) {
try_to_fill_event(name, buf);
return;
}
- if (sample) {
- try_to_fill_sample(sample, name, buf);
+ if (cur_sample) {
+ try_to_fill_sample(cur_sample, name, buf);
return;
}
- if (dive) {
- try_to_fill_dive(&dive, name, buf);
+ if (cur_dive) {
+ try_to_fill_dive(&cur_dive, name, buf);
return;
}
}
@@ -1455,25 +1340,25 @@ static void reset_all(void)
import_source = UNKNOWN;
}
-void parse_xml_file(const char *filename, GError **error)
+void parse_xml_buffer(const char *url, const char *buffer, int size, GError **error)
{
xmlDoc *doc;
- doc = xmlReadFile(filename, NULL, 0);
+ doc = xmlReadMemory(buffer, size, url, NULL, 0);
if (!doc) {
- fprintf(stderr, "Failed to parse '%s'.\n", filename);
+ fprintf(stderr, "Failed to parse '%s'.\n", url);
if (error != NULL)
{
*error = g_error_new(g_quark_from_string("subsurface"),
DIVE_ERROR_PARSE,
"Failed to parse '%s'",
- filename);
+ url);
}
return;
}
/* we assume that the last (or only) filename passed as argument is a
* great filename to use as default when saving the dives */
- set_filename(filename);
+ set_filename(url);
reset_all();
dive_start();
#ifdef XSLT
diff --git a/profile.c b/profile.c
index 822e30c0e..137ed6f88 100644
--- a/profile.c
+++ b/profile.c
@@ -1014,8 +1014,7 @@ static void dump_pr_track(pr_track_t **track_pr)
}
}
-static void fill_missing_tank_pressures(struct dive *dive, struct plot_info *pi,
- pr_track_t **track_pr)
+static void fill_missing_tank_pressures(struct plot_info *pi, pr_track_t **track_pr)
{
pr_track_t *list = NULL;
pr_track_t *nlist = NULL;
@@ -1338,7 +1337,7 @@ static struct plot_info *create_plot_info(struct dive *dive, int nr_samples, str
pi->meandepth = dive->meandepth.mm;
if (missing_pr) {
- fill_missing_tank_pressures(dive, pi, track_pr);
+ fill_missing_tank_pressures(pi, track_pr);
}
for (cyl = 0; cyl < MAX_CYLINDERS; cyl++)
list_free(track_pr[cyl]);
diff --git a/statistics.c b/statistics.c
index f8f9d0e24..65efdf145 100644
--- a/statistics.c
+++ b/statistics.c
@@ -29,38 +29,101 @@ typedef struct {
*sac,
*otu,
*o2he,
- *gas_used,
- *total_time,
+ *gas_used;
+} single_stat_widget_t;
+
+static single_stat_widget_t single_w;
+
+typedef struct {
+ GtkWidget *total_time,
*avg_time,
+ *shortest_time,
+ *longest_time,
*max_overall_depth,
+ *min_overall_depth,
*avg_overall_depth,
*min_sac,
*avg_sac,
- *max_sac;
-} info_stat_widget_t;
+ *max_sac,
+ *selection_size,
+ *max_temp,
+ *avg_temp,
+ *min_temp;
+} total_stats_widget_t;
-static info_stat_widget_t info_stat_w;
+static total_stats_widget_t stats_w;
typedef struct {
duration_t total_time;
/* avg_time is simply total_time / nr -- let's not keep this */
+ duration_t shortest_time;
+ duration_t longest_time;
depth_t max_depth;
+ depth_t min_depth;
depth_t avg_depth;
volume_t max_sac;
volume_t min_sac;
volume_t avg_sac;
-} info_stat_t;
+ int max_temp;
+ int min_temp;
+ unsigned int combined_temp;
+ unsigned int combined_count;
+ unsigned int selection_size;
+} stats_t;
-static info_stat_t info_stat;
+static stats_t stats;
+static stats_t stats_selection;
+
+
+static void process_dive(struct dive *dp, stats_t *stats)
+{
+ int old_tt, sac_time = 0;
+ const char *unit;
+
+ old_tt = stats->total_time.seconds;
+ stats->total_time.seconds += dp->duration.seconds;
+ if (dp->duration.seconds > stats->longest_time.seconds)
+ stats->longest_time.seconds = dp->duration.seconds;
+ if (stats->shortest_time.seconds == 0 || dp->duration.seconds < stats->shortest_time.seconds)
+ stats->shortest_time.seconds = dp->duration.seconds;
+ if (dp->maxdepth.mm > stats->max_depth.mm)
+ stats->max_depth.mm = dp->maxdepth.mm;
+ if (stats->min_depth.mm == 0 || dp->maxdepth.mm < stats->min_depth.mm)
+ stats->min_depth.mm = dp->maxdepth.mm;
+ stats->avg_depth.mm = (1.0 * old_tt * stats->avg_depth.mm +
+ dp->duration.seconds * dp->meandepth.mm) / stats->total_time.seconds;
+ if (dp->sac > 2800) { /* less than .1 cuft/min (2800ml/min) is bogus */
+ int old_sac_time = sac_time;
+ sac_time += dp->duration.seconds;
+ stats->avg_sac.mliter = (1.0 * old_sac_time * stats->avg_sac.mliter +
+ dp->duration.seconds * dp->sac) / sac_time ;
+ if (dp->sac > stats->max_sac.mliter)
+ stats->max_sac.mliter = dp->sac;
+ if (stats->min_sac.mliter == 0 || dp->sac < stats->min_sac.mliter)
+ stats->min_sac.mliter = dp->sac;
+ }
+ if (dp->watertemp.mkelvin) {
+ if (stats->min_temp == 0 || dp->watertemp.mkelvin < stats->min_temp)
+ stats->min_temp = dp->watertemp.mkelvin;
+ if (dp->watertemp.mkelvin > stats->max_temp)
+ stats->max_temp = dp->watertemp.mkelvin;
+ stats->combined_temp += get_temp_units(dp->watertemp.mkelvin, &unit);
+ stats->combined_count++;
+ }
+}
static void process_all_dives(struct dive *dive, struct dive **prev_dive)
{
int idx;
struct dive *dp;
- int old_tt, sac_time = 0;
*prev_dive = NULL;
- memset(&info_stat, 0, sizeof(info_stat));
+ memset(&stats, 0, sizeof(stats));
+ if (dive_table.nr > 0) {
+ stats.shortest_time.seconds = dive_table.dives[0]->duration.seconds;
+ stats.min_depth.mm = dive_table.dives[0]->maxdepth.mm;
+ stats.selection_size = dive_table.nr;
+ }
/* this relies on the fact that the dives in the dive_table
* are in chronological order */
for (idx = 0; idx < dive_table.nr; idx++) {
@@ -70,22 +133,28 @@ static void process_all_dives(struct dive *dive, struct dive **prev_dive)
if (idx > 0)
*prev_dive = dive_table.dives[idx-1];
}
- old_tt = info_stat.total_time.seconds;
- info_stat.total_time.seconds += dp->duration.seconds;
- if (dp->maxdepth.mm > info_stat.max_depth.mm)
- info_stat.max_depth.mm = dp->maxdepth.mm;
- info_stat.avg_depth.mm = (1.0 * old_tt * info_stat.avg_depth.mm +
- dp->duration.seconds * dp->meandepth.mm) / info_stat.total_time.seconds;
- if (dp->sac > 2800) { /* less than .1 cuft/min (2800ml/min) is bogus */
- int old_sac_time = sac_time;
- sac_time += dp->duration.seconds;
- info_stat.avg_sac.mliter = (1.0 * old_sac_time * info_stat.avg_sac.mliter +
- dp->duration.seconds * dp->sac) / sac_time ;
- if (dp->sac > info_stat.max_sac.mliter)
- info_stat.max_sac.mliter = dp->sac;
- if (info_stat.min_sac.mliter == 0 || dp->sac < info_stat.min_sac.mliter)
- info_stat.min_sac.mliter = dp->sac;
+ process_dive(dp, &stats);
+ }
+}
+
+void process_selected_dives(GList *selected_dives, GtkTreeModel *model)
+{
+ struct dive *dp;
+ unsigned int i;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ memset(&stats_selection, 0, sizeof(stats_selection));
+ stats_selection.selection_size = amount_selected;
+
+ for (i = 0; i < amount_selected; ++i) {
+ GValue value = {0, };
+ path = g_list_nth_data(selected_dives, i);
+ if (gtk_tree_model_get_iter(model, &iter, path)) {
+ gtk_tree_model_get_value(model, &iter, 0, &value);
+ dp = get_dive(g_value_get_int(&value));
}
+ process_dive(dp, &stats_selection);
}
}
@@ -117,7 +186,7 @@ static char * get_time_string(int seconds, int maxdays)
return buf;
}
-void show_dive_stats(struct dive *dive)
+static void show_single_dive_stats(struct dive *dive)
{
char buf[80];
double value;
@@ -137,28 +206,28 @@ void show_dive_stats(struct dive *dive)
tm->tm_mday, tm->tm_year + 1900,
tm->tm_hour, tm->tm_min);
- set_label(info_stat_w.date, buf);
- set_label(info_stat_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60);
+ set_label(single_w.date, buf);
+ set_label(single_w.dive_time, "%d min", (dive->duration.seconds + 30) / 60);
if (prev_dive)
- set_label(info_stat_w.surf_intv,
+ set_label(single_w.surf_intv,
get_time_string(dive->when - (prev_dive->when + prev_dive->duration.seconds), 4));
else
- set_label(info_stat_w.surf_intv, "unknown");
+ set_label(single_w.surf_intv, "unknown");
value = get_depth_units(dive->maxdepth.mm, &decimals, &unit);
- set_label(info_stat_w.max_depth, "%.*f %s", decimals, value, unit);
+ set_label(single_w.max_depth, "%.*f %s", decimals, value, unit);
value = get_depth_units(dive->meandepth.mm, &decimals, &unit);
- set_label(info_stat_w.avg_depth, "%.*f %s", decimals, value, unit);
+ set_label(single_w.avg_depth, "%.*f %s", decimals, value, unit);
if (dive->watertemp.mkelvin) {
value = get_temp_units(dive->watertemp.mkelvin, &unit);
- set_label(info_stat_w.water_temp, "%.1f %s", value, unit);
+ set_label(single_w.water_temp, "%.1f %s", value, unit);
} else
- set_label(info_stat_w.water_temp, "");
+ set_label(single_w.water_temp, "");
value = get_volume_units(dive->sac, &decimals, &unit);
if (value > 0) {
- set_label(info_stat_w.sac, "%.*f %s/min", decimals, value, unit);
+ set_label(single_w.sac, "%.*f %s/min", decimals, value, unit);
} else
- set_label(info_stat_w.sac, "");
- set_label(info_stat_w.otu, "%d", dive->otu);
+ set_label(single_w.sac, "");
+ set_label(single_w.otu, "%d", dive->otu);
offset = 0;
gas_used = 0;
buf[0] = '\0';
@@ -171,7 +240,7 @@ void show_dive_stats(struct dive *dive)
end = cyl->end.mbar ? : cyl->sample_end.mbar;
if (!cylinder_none(cyl)) {
/* 0% O2 strangely means air, so 21% - I don't like that at all */
- int o2 = cyl->gasmix.o2.permille ? : 209;
+ int o2 = cyl->gasmix.o2.permille ? : AIR_PERMILLE;
if (offset > 0) {
snprintf(buf+offset, 80-offset, ", ");
offset += 2;
@@ -185,25 +254,61 @@ void show_dive_stats(struct dive *dive)
if (cyl->type.size.mliter && start && end)
gas_used += cyl->type.size.mliter / 1000.0 * (start - end);
}
- set_label(info_stat_w.o2he, buf);
+ set_label(single_w.o2he, buf);
if (gas_used) {
value = get_volume_units(gas_used, &decimals, &unit);
- set_label(info_stat_w.gas_used, "%.*f %s", decimals, value, unit);
+ set_label(single_w.gas_used, "%.*f %s", decimals, value, unit);
} else
- set_label(info_stat_w.gas_used, "");
- /* and now do the statistics */
- set_label(info_stat_w.total_time, get_time_string(info_stat.total_time.seconds, 0));
- set_label(info_stat_w.avg_time, get_time_string(info_stat.total_time.seconds / dive_table.nr, 0));
- value = get_depth_units(info_stat.max_depth.mm, &decimals, &unit);
- set_label(info_stat_w.max_overall_depth, "%.*f %s", decimals, value, unit);
- value = get_depth_units(info_stat.avg_depth.mm, &decimals, &unit);
- set_label(info_stat_w.avg_overall_depth, "%.*f %s", decimals, value, unit);
- value = get_volume_units(info_stat.max_sac.mliter, &decimals, &unit);
- set_label(info_stat_w.max_sac, "%.*f %s/min", decimals, value, unit);
- value = get_volume_units(info_stat.min_sac.mliter, &decimals, &unit);
- set_label(info_stat_w.min_sac, "%.*f %s/min", decimals, value, unit);
- value = get_volume_units(info_stat.avg_sac.mliter, &decimals, &unit);
- set_label(info_stat_w.avg_sac, "%.*f %s/min", decimals, value, unit);
+ set_label(single_w.gas_used, "");
+}
+
+static void show_total_dive_stats(struct dive *dive)
+{
+ double value;
+ int decimals;
+ const char *unit;
+ stats_t *stats_ptr;
+
+ if (amount_selected < 2)
+ stats_ptr = &stats;
+ else
+ stats_ptr = &stats_selection;
+
+ set_label(stats_w.selection_size, "%d", stats_ptr->selection_size);
+ if (stats_ptr->min_temp) {
+ value = get_temp_units(stats_ptr->min_temp, &unit);
+ set_label(stats_w.min_temp, "%.1f %s", value, unit);
+ }
+ if (stats_ptr->combined_temp && stats_ptr->combined_count)
+ set_label(stats_w.avg_temp, "%.1f %s", stats_ptr->combined_temp / (stats_ptr->combined_count * 1.0), unit);
+ if (stats_ptr->max_temp) {
+ value = get_temp_units(stats_ptr->max_temp, &unit);
+ set_label(stats_w.max_temp, "%.1f %s", value, unit);
+ }
+ set_label(stats_w.total_time, get_time_string(stats_ptr->total_time.seconds, 0));
+ set_label(stats_w.avg_time, get_time_string(stats_ptr->total_time.seconds / stats_ptr->selection_size, 0));
+ set_label(stats_w.longest_time, get_time_string(stats_ptr->longest_time.seconds, 0));
+ set_label(stats_w.shortest_time, get_time_string(stats_ptr->shortest_time.seconds, 0));
+ value = get_depth_units(stats_ptr->max_depth.mm, &decimals, &unit);
+ set_label(stats_w.max_overall_depth, "%.*f %s", decimals, value, unit);
+ value = get_depth_units(stats_ptr->min_depth.mm, &decimals, &unit);
+ set_label(stats_w.min_overall_depth, "%.*f %s", decimals, value, unit);
+ value = get_depth_units(stats_ptr->avg_depth.mm, &decimals, &unit);
+ set_label(stats_w.avg_overall_depth, "%.*f %s", decimals, value, unit);
+ value = get_volume_units(stats_ptr->max_sac.mliter, &decimals, &unit);
+ set_label(stats_w.max_sac, "%.*f %s/min", decimals, value, unit);
+ value = get_volume_units(stats_ptr->min_sac.mliter, &decimals, &unit);
+ set_label(stats_w.min_sac, "%.*f %s/min", decimals, value, unit);
+ value = get_volume_units(stats_ptr->avg_sac.mliter, &decimals, &unit);
+ set_label(stats_w.avg_sac, "%.*f %s/min", decimals, value, unit);
+}
+
+void show_dive_stats(struct dive *dive)
+{
+ /* they have to be called in this order, as 'total' depends on
+ * calculations done in 'single' */
+ show_single_dive_stats(dive);
+ show_total_dive_stats(dive);
}
void flush_dive_stats_changes(struct dive *dive)
@@ -224,64 +329,88 @@ static GtkWidget *new_info_label_in_frame(GtkWidget *box, const char *label)
return label_widget;
}
-GtkWidget *stats_widget(void)
+GtkWidget *total_stats_widget(void)
{
-
- GtkWidget *vbox, *hbox, *infoframe, *statsframe, *framebox;
+ GtkWidget *vbox, *hbox, *statsframe, *framebox;
vbox = gtk_vbox_new(FALSE, 3);
- infoframe = gtk_frame_new("Dive Info");
- gtk_box_pack_start(GTK_BOX(vbox), infoframe, TRUE, FALSE, 3);
+ statsframe = gtk_frame_new("Statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), statsframe, TRUE, FALSE, 3);
framebox = gtk_vbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(infoframe), framebox);
+ gtk_container_add(GTK_CONTAINER(statsframe), framebox);
/* first row */
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
-
- info_stat_w.date = new_info_label_in_frame(hbox, "Date");
- info_stat_w.dive_time = new_info_label_in_frame(hbox, "Dive Time");
- info_stat_w.surf_intv = new_info_label_in_frame(hbox, "Surf Intv");
+ stats_w.selection_size = new_info_label_in_frame(hbox, "Dives");
+ stats_w.max_temp = new_info_label_in_frame(hbox, "Max Temp");
+ stats_w.min_temp = new_info_label_in_frame(hbox, "Min Temp");
+ stats_w.avg_temp = new_info_label_in_frame(hbox, "Avg Temp");
/* second row */
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
- info_stat_w.max_depth = new_info_label_in_frame(hbox, "Max Depth");
- info_stat_w.avg_depth = new_info_label_in_frame(hbox, "Avg Depth");
- info_stat_w.water_temp = new_info_label_in_frame(hbox, "Water Temp");
+ stats_w.total_time = new_info_label_in_frame(hbox, "Total Time");
+ stats_w.avg_time = new_info_label_in_frame(hbox, "Avg Time");
+ stats_w.longest_time = new_info_label_in_frame(hbox, "Longest Dive");
+ stats_w.shortest_time = new_info_label_in_frame(hbox, "Shortest Dive");
/* third row */
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
- info_stat_w.sac = new_info_label_in_frame(hbox, "SAC");
- info_stat_w.otu = new_info_label_in_frame(hbox, "OTU");
- info_stat_w.o2he = new_info_label_in_frame(hbox, "O" UTF8_SUBSCRIPT_2 " / He");
- info_stat_w.gas_used = new_info_label_in_frame(hbox, "Gas Used");
+ stats_w.max_overall_depth = new_info_label_in_frame(hbox, "Max Depth");
+ stats_w.min_overall_depth = new_info_label_in_frame(hbox, "Min Depth");
+ stats_w.avg_overall_depth = new_info_label_in_frame(hbox, "Avg Depth");
- statsframe = gtk_frame_new("Statistics");
- gtk_box_pack_start(GTK_BOX(vbox), statsframe, TRUE, FALSE, 3);
+ /* fourth row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ stats_w.max_sac = new_info_label_in_frame(hbox, "Max SAC");
+ stats_w.min_sac = new_info_label_in_frame(hbox, "Min SAC");
+ stats_w.avg_sac = new_info_label_in_frame(hbox, "Avg SAC");
+
+ return vbox;
+}
+
+GtkWidget *single_stats_widget(void)
+{
+ GtkWidget *vbox, *hbox, *infoframe, *framebox;
+
+ vbox = gtk_vbox_new(FALSE, 3);
+
+ infoframe = gtk_frame_new("Dive Info");
+ gtk_box_pack_start(GTK_BOX(vbox), infoframe, TRUE, FALSE, 3);
framebox = gtk_vbox_new(FALSE, 3);
- gtk_container_add(GTK_CONTAINER(statsframe), framebox);
+ gtk_container_add(GTK_CONTAINER(infoframe), framebox);
/* first row */
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
- info_stat_w.total_time = new_info_label_in_frame(hbox, "Total Time");
- info_stat_w.avg_time = new_info_label_in_frame(hbox, "Avg Time");
- info_stat_w.max_overall_depth = new_info_label_in_frame(hbox, "Max Depth");
- info_stat_w.avg_overall_depth = new_info_label_in_frame(hbox, "Avg Depth");
+ single_w.date = new_info_label_in_frame(hbox, "Date");
+ single_w.dive_time = new_info_label_in_frame(hbox, "Dive Time");
+ single_w.surf_intv = new_info_label_in_frame(hbox, "Surf Intv");
/* second row */
hbox = gtk_hbox_new(FALSE, 3);
gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
- info_stat_w.max_sac = new_info_label_in_frame(hbox, "Max SAC");
- info_stat_w.min_sac = new_info_label_in_frame(hbox, "Min SAC");
- info_stat_w.avg_sac = new_info_label_in_frame(hbox, "Avg SAC");
+ single_w.max_depth = new_info_label_in_frame(hbox, "Max Depth");
+ single_w.avg_depth = new_info_label_in_frame(hbox, "Avg Depth");
+ single_w.water_temp = new_info_label_in_frame(hbox, "Water Temp");
+
+ /* third row */
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(framebox), hbox, TRUE, FALSE, 3);
+
+ single_w.sac = new_info_label_in_frame(hbox, "SAC");
+ single_w.otu = new_info_label_in_frame(hbox, "OTU");
+ single_w.o2he = new_info_label_in_frame(hbox, "O" UTF8_SUBSCRIPT_2 " / He");
+ single_w.gas_used = new_info_label_in_frame(hbox, "Gas Used");
return vbox;
}
diff --git a/uemis.c b/uemis.c
index 28302b1c0..06ef5b423 100644
--- a/uemis.c
+++ b/uemis.c
@@ -231,7 +231,7 @@ void uemis_parse_divelog_binary(char *base64, void *datap) {
sample->cylinderindex = u_sample->active_tank;
sample->cylinderpressure.mbar= u_sample->tank_pressure * 10;
uemis_event(dive, sample, u_sample);
- finish_sample(dive, sample);
+ finish_sample(dive);
i += 0x25;
u_sample++;
}
diff --git a/windows.c b/windows.c
index 8a81737b0..46b6951bd 100644
--- a/windows.c
+++ b/windows.c
@@ -2,6 +2,7 @@
/* implements Windows specific functions */
#include "display-gtk.h"
#include <windows.h>
+#define DIVELIST_DEFAULT_FONT "Sans 8"
static HKEY hkey;
@@ -83,3 +84,16 @@ const char *subsurface_USB_name()
{
return "COM3";
}
+
+const char *subsurface_icon_name()
+{
+ return "subsurface.ico";
+}
+
+void subsurface_ui_setup(GtkSettings *settings, GtkWidget *menubar,
+ GtkWidget *vbox, GtkUIManager *ui_manager)
+{
+ if (!divelist_font)
+ divelist_font = DIVELIST_DEFAULT_FONT;
+ gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
+}
diff --git a/xslt/jdivelog2subsurface.xslt b/xslt/jdivelog2subsurface.xslt
index 73b4590fb..e2f40c560 100644
--- a/xslt/jdivelog2subsurface.xslt
+++ b/xslt/jdivelog2subsurface.xslt
@@ -66,13 +66,10 @@
<notes>
<xsl:if test="DiveActivity != ''">
- <xsl:value-of select="DiveActivity"/>
+Diveactivity: <xsl:value-of select="DiveActivity"/>
</xsl:if>
- <xsl:if test="Comment != ''">
- <xsl:if test="DiveActivity != ''">
- <xsl:value-of select="': '"/>
- </xsl:if>
- <xsl:value-of select="Comment"/>
+ <xsl:if test="DiveType != ''">
+Divetype: <xsl:value-of select="DiveType"/>
</xsl:if>
<xsl:if test="Equipment/Visibility != ''">
Visibility: <xsl:value-of select="Equipment/Visibility"/>
@@ -86,41 +83,51 @@ Gloves: <xsl:value-of select="Equipment/Gloves"/>
<xsl:if test="Equipment/Weight != ''">
Weight: <xsl:value-of select="Equipment/Weight"/>
</xsl:if>
+ <xsl:if test="Comment != ''">
+Comment: <xsl:value-of select="Comment"/>
+ </xsl:if>
</notes>
<!-- cylinder -->
- <xsl:variable name="o2">
- <xsl:choose>
- <xsl:when test="DIVE/GASES/MIX/O2 != ''">
- <xsl:value-of select="concat(DIVE/GASES/MIX/O2*100, '%')"/>
- </xsl:when>
- <xsl:otherwise>21.0%</xsl:otherwise>
- </xsl:choose>
- </xsl:variable>
- <xsl:variable name="size">
- <xsl:choose>
- <xsl:when test="Equipment/Tanks/Tank/MIX/TANK/TANKVOLUME != ''">
- <xsl:value-of select="concat(Equipment/Tanks/Tank/MIX/TANK/TANKVOLUME * 1000, ' l')"/>
- </xsl:when>
- <xsl:otherwise>0 l</xsl:otherwise>
- </xsl:choose>
- </xsl:variable>
- <xsl:variable name="start">
- <xsl:variable name="number" select="Equipment/Tanks/Tank/MIX/TANK/PSTART"/>
- <xsl:call-template name="pressure">
- <xsl:with-param name="number" select="$number"/>
- <xsl:with-param name="units" select="$units"/>
- </xsl:call-template>
- </xsl:variable>
- <xsl:variable name="end">
- <xsl:variable name="number" select="Equipment/Tanks/Tank/MIX/TANK/PEND"/>
- <xsl:call-template name="pressure">
- <xsl:with-param name="number" select="$number"/>
- <xsl:with-param name="units" select="$units"/>
- </xsl:call-template>
- </xsl:variable>
-
- <cylinder o2="{$o2}" size="{$size}" start="{$start}" end="{$end}"/>
+ <xsl:for-each select="Equipment/Tanks/Tank">
+ <cylinder>
+ <xsl:attribute name="o2">
+ <xsl:choose>
+ <xsl:when test="MIX/O2 != ''">
+ <xsl:value-of select="concat(MIX/O2*100, '%')"/>
+ </xsl:when>
+ <xsl:otherwise>21.0%</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:if test="MIX/HE != '0.0'">
+ <xsl:attribute name="he">
+ <xsl:value-of select="concat(MIX/HE*100, '%')"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:attribute name="size">
+ <xsl:choose>
+ <xsl:when test="MIX/TANK/TANKVOLUME != ''">
+ <xsl:value-of select="concat(MIX/TANK/TANKVOLUME * 1000, ' l')"/>
+ </xsl:when>
+ <xsl:otherwise>0 l</xsl:otherwise>
+ </xsl:choose>
+ </xsl:attribute>
+ <xsl:attribute name="start">
+ <xsl:variable name="number" select="MIX/TANK/PSTART"/>
+ <xsl:call-template name="pressure">
+ <xsl:with-param name="number" select="$number"/>
+ <xsl:with-param name="units" select="$units"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ <xsl:attribute name="end">
+ <xsl:variable name="number" select="MIX/TANK/PEND"/>
+ <xsl:call-template name="pressure">
+ <xsl:with-param name="number" select="$number"/>
+ <xsl:with-param name="units" select="$units"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </cylinder>
+ </xsl:for-each>
<!-- end cylinder -->
<!-- DELTA is the sample interval -->
@@ -156,6 +163,22 @@ Weight: <xsl:value-of select="Equipment/Weight"/>
</xsl:for-each>
<!-- end events -->
+ <!-- gas change -->
+ <xsl:for-each select="DIVE/SAMPLES/SWITCH">
+ <event name="gaschange">
+ <xsl:attribute name="time">
+ <xsl:call-template name="timeConvert">
+ <xsl:with-param name="timeSec" select="count(preceding-sibling::D) * $delta"/>
+ <xsl:with-param name="units" select="'si'"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ <xsl:attribute name="value">
+ <xsl:value-of select="ancestor::DIVE/GASES/MIX[MIXNAME=current()]/O2 * 100" />
+ </xsl:attribute>
+ </event>
+ </xsl:for-each>
+ <!-- end gas change -->
+
<!-- dive sample - all the depth and temp readings -->
<xsl:for-each select="DIVE/SAMPLES/D">
<xsl:variable name="timeSec" select="(position() - 1) * $delta"/>