]> git.tdb.fi Git - ext/subsurface.git/commitdiff
Merge branch 'weight' of git://subsurface.hohndel.org/subsurface
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 24 Mar 2012 04:07:53 +0000 (21:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 24 Mar 2012 04:07:53 +0000 (21:07 -0700)
Pull weight management from Dirk Hohndel:
 "This is the fifth or sixth version of this code, I'm begining to lose
  track.  I still struggle with the balance between code duplication and
  unnecessary indirectness and complexity.  Maybe I'm just not finding
  the right level of abstraction.  Maybe I'm just trying too hard.

  The code here is reasonably well tested.  Works for me :-)

  It can import DivingLog xml files with weight systems and correctly
  parses those.  It obviously can read and write weight systems in its
  own file format.  It adds a KG/lbs unit default (and correctly stores
  that).

  The thing I still worry about is the code in equipment.c.  You'll see
  that I tried to abstract things in a way that weight systems and
  cylinders share quite a bit of code - but there's more very similar
  code that isn't shared as my attempts to do so turned into ugly and
  hard to read code.  It always felt like trying to write C++ in C..."

* 'weight' of git://subsurface.hohndel.org/subsurface:
  Add weight system tracking

Fix up some trivial conflicts due to various renaming of globals and
simplification in function interfaces.

26 files changed:
Documentation/Makefile [new file with mode: 0644]
Documentation/user-manual.txt
Makefile
cochran.c [new file with mode: 0644]
display-gtk.h
dive.c
dive.h
divelist.c
equipment.c
file.c [new file with mode: 0644]
file.h [new file with mode: 0644]
gtk-gui.c
info.c
libdivecomputer.c
linux.c
macos.c
main.c
packaging/macosx/Info.plist
packaging/macosx/subsurface.sh [deleted file]
packaging/windows/subsurface.nsi
parse-xml.c
profile.c
statistics.c
uemis.c
windows.c
xslt/jdivelog2subsurface.xslt

diff --git a/Documentation/Makefile b/Documentation/Makefile
new file mode 100644 (file)
index 0000000..273eae5
--- /dev/null
@@ -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
index 181360140896e67cb1e5c1ada0548cc128357208..75fb3a338c8762ca88b59ed7267926b0f6b71b07 100644 (file)
@@ -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
index f7c88399975c65d9c57be33bce20f9643708e548..20dff4c857f11d5d9286245e9d43c8a5d6b63aa0 100644 (file)
--- 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 (file)
index 0000000..360f2eb
--- /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);
+}
index b20495731e50036b2b4b774d953675e8950af9e1..f12e4299661d2d7795db8e6a477f2310f960d8fa 100644 (file)
@@ -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 3fa09bd10c05982a9972c0db8a58a76314543f59..9f57aed58146554969901198ac761fc888904e18 100644 (file)
--- 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 bfaa076b1eba08085a6329da989a230dd79d7e87..39f203738833519c594c50032146d13f867c49a2 100644 (file)
--- 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 */
index f664cde49cf6f0c8f725a8d3f681402a277a7abc..2054ca017ca0b41b8d6642e22ec09aa2e223f687 100644 (file)
@@ -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();
 
index 579c4554207041a7030c8d7d08c940e37db1efc9..abfb0e7b5979bba41b9af71b1d5391d610ed4359 100644 (file)
@@ -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 (file)
index 0000000..aff1d51
--- /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 (file)
index 0000000..df7e5ea
--- /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
index 371a0308fdb014c925d8e679f58b878ca765394d..7d0e95c43ecdf5b8885ff44dd4a1693b75b744c6 100644 (file)
--- 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 e53af015eb71d4ab63da397c1489fd3bb84ae024..6a4a296b004013d7b77ba9bb746b79225385f6d7 100644 (file)
--- 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 {
index 9d4c1065a3817ce046798448b7c7a39a2a10be2a..4ff41486db453fda6d76f0e3c9a27a2dd7f15588 100644 (file)
@@ -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 ac5cf55f938b778afef9bd99ddf70b5f5ffd5551..f0ab2841caa48ed6dc4a5377a5c6fd5536d3e97d 100644 (file)
--- 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 25f946c81db9a52abad2547286e3f12c3b75e5ad..f6c4bd6c097c8ce14cd5ed73539cf99f42e9429e 100644 (file)
--- 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 d77fcd2d97adbf4c6eac1410bddc4424635f01d9..eae4ee2399412a1b329f1e765f77cba519735c04 100644 (file)
--- 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)
                {
index 4ee12438b4eee8fcce7e70d4e3ecbc15e2b76b3e..f3ac10cdd06c3692115f65621ef3dbd76725c52c 100644 (file)
@@ -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 (executable)
index ee9427c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-cd `dirname $0`/../Resources
-../MacOS/subsurface &
-exit 0
index 09a6eb0e6ac11198ef9ef252744359adffec113f..280991f69abc3ed04b4171cc5841edad041ef833 100644 (file)
@@ -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"
index 34afdb9f690dc22fa83d6f08a94aaaddd13f1ce4..e920a11f6f0d798b18900ccb23317c95c8ec5162 100644 (file)
@@ -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
index 822e30c0e11e5c217e84235578cfde08b6445fdb..137ed6f88778c2dea942c2dd377b43dea11497d2 100644 (file)
--- 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]);
index f8f9d0e24549ca10c568eb85fcc2b957ea14a814..65efdf145dea8d17d55152777d5169b5667998a1 100644 (file)
@@ -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 28302b1c0c75ae955d9ae29baf91fcf74056d676..06ef5b4231c6f85e6474d182b04a5cf7911d8f2b 100644 (file)
--- 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++;
        }
index 8a81737b039082ea4b551534b6072e740abb4b8d..46b6951bd87bd8c7a9d6ad43020b994d557d263d 100644 (file)
--- 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);
+}
index 73b4590fbb6b163c54f0f500917161ea0f8e6905..e2f40c560c5d7fcc4eb135781835d8cbecc39cc6 100644 (file)
 
       <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"/>