diff options
116 files changed, 2213 insertions, 1123 deletions
diff --git a/Documentation/images/AddDive1.jpg b/Documentation/images/AddDive1.jpg Binary files differindex 592ed66db..d70e330a5 100644 --- a/Documentation/images/AddDive1.jpg +++ b/Documentation/images/AddDive1.jpg diff --git a/Documentation/images/Ceilings.png b/Documentation/images/Ceilings.png Binary files differindex b3eae8c04..55e53abf1 100644 --- a/Documentation/images/Ceilings.png +++ b/Documentation/images/Ceilings.png diff --git a/Documentation/images/Ceilings2.jpg b/Documentation/images/Ceilings2.jpg Binary files differnew file mode 100644 index 000000000..66a818417 --- /dev/null +++ b/Documentation/images/Ceilings2.jpg diff --git a/Documentation/images/CylinderDataEntry1.jpg b/Documentation/images/CylinderDataEntry1.jpg Binary files differindex fd7a95287..ca7105ac7 100644 --- a/Documentation/images/CylinderDataEntry1.jpg +++ b/Documentation/images/CylinderDataEntry1.jpg diff --git a/Documentation/images/CylinderDataEntry2.jpg b/Documentation/images/CylinderDataEntry2.jpg Binary files differindex b3cc0e8d3..72a630ed7 100644 --- a/Documentation/images/CylinderDataEntry2.jpg +++ b/Documentation/images/CylinderDataEntry2.jpg diff --git a/Documentation/images/CylinderDataEntry3.jpg b/Documentation/images/CylinderDataEntry3.jpg Binary files differindex a0f50e2f3..7b67cc347 100644 --- a/Documentation/images/CylinderDataEntry3.jpg +++ b/Documentation/images/CylinderDataEntry3.jpg diff --git a/Documentation/images/DiveProfile1.jpg b/Documentation/images/DiveProfile1.jpg Binary files differindex 0cf224555..af5f9cc4b 100644 --- a/Documentation/images/DiveProfile1.jpg +++ b/Documentation/images/DiveProfile1.jpg diff --git a/Documentation/images/DiveProfile2.jpg b/Documentation/images/DiveProfile2.jpg Binary files differindex 94b828cd4..ddaa3596a 100644 --- a/Documentation/images/DiveProfile2.jpg +++ b/Documentation/images/DiveProfile2.jpg diff --git a/Documentation/images/DiveProfile3.jpg b/Documentation/images/DiveProfile3.jpg Binary files differindex b3eb773db..160118d8c 100644 --- a/Documentation/images/DiveProfile3.jpg +++ b/Documentation/images/DiveProfile3.jpg diff --git a/Documentation/images/DiveProfile4.jpg b/Documentation/images/DiveProfile4.jpg Binary files differindex a015333a4..01af348af 100644 --- a/Documentation/images/DiveProfile4.jpg +++ b/Documentation/images/DiveProfile4.jpg diff --git a/Documentation/images/MeasuringBar.png b/Documentation/images/MeasuringBar.png Binary files differindex 8340d3716..4484bce03 100644 --- a/Documentation/images/MeasuringBar.png +++ b/Documentation/images/MeasuringBar.png diff --git a/Documentation/images/Profile2.png b/Documentation/images/Profile2.png Binary files differindex 7acbbdfb7..689a2c8cb 100644 --- a/Documentation/images/Profile2.png +++ b/Documentation/images/Profile2.png diff --git a/Documentation/images/WeightsDataEntry1.jpg b/Documentation/images/WeightsDataEntry1.jpg Binary files differindex b3aa1b785..456d913dd 100644 --- a/Documentation/images/WeightsDataEntry1.jpg +++ b/Documentation/images/WeightsDataEntry1.jpg diff --git a/Documentation/images/WeightsDataEntry2.jpg b/Documentation/images/WeightsDataEntry2.jpg Binary files differindex 5da906448..5b487ff10 100644 --- a/Documentation/images/WeightsDataEntry2.jpg +++ b/Documentation/images/WeightsDataEntry2.jpg diff --git a/Documentation/images/WeightsDataEntry3.jpg b/Documentation/images/WeightsDataEntry3.jpg Binary files differindex 4531e6b90..31e0bbda3 100644 --- a/Documentation/images/WeightsDataEntry3.jpg +++ b/Documentation/images/WeightsDataEntry3.jpg diff --git a/Documentation/images/icons/EAD.jpg b/Documentation/images/icons/EAD.jpg Binary files differnew file mode 100644 index 000000000..dfc43e7bb --- /dev/null +++ b/Documentation/images/icons/EAD.jpg diff --git a/Documentation/images/icons/He.jpg b/Documentation/images/icons/He.jpg Binary files differnew file mode 100644 index 000000000..d2c7618d5 --- /dev/null +++ b/Documentation/images/icons/He.jpg diff --git a/Documentation/images/icons/MOD.jpg b/Documentation/images/icons/MOD.jpg Binary files differnew file mode 100644 index 000000000..9ba0b5871 --- /dev/null +++ b/Documentation/images/icons/MOD.jpg diff --git a/Documentation/images/icons/N2.jpg b/Documentation/images/icons/N2.jpg Binary files differnew file mode 100644 index 000000000..cec39eb40 --- /dev/null +++ b/Documentation/images/icons/N2.jpg diff --git a/Documentation/images/icons/NDL.jpg b/Documentation/images/icons/NDL.jpg Binary files differnew file mode 100644 index 000000000..10435b7e7 --- /dev/null +++ b/Documentation/images/icons/NDL.jpg diff --git a/Documentation/images/icons/O2.jpg b/Documentation/images/icons/O2.jpg Binary files differnew file mode 100644 index 000000000..ec8488a60 --- /dev/null +++ b/Documentation/images/icons/O2.jpg diff --git a/Documentation/images/icons/SAC.jpg b/Documentation/images/icons/SAC.jpg Binary files differnew file mode 100644 index 000000000..9bfff3dcf --- /dev/null +++ b/Documentation/images/icons/SAC.jpg diff --git a/Documentation/images/icons/cceiling.jpg b/Documentation/images/icons/cceiling.jpg Binary files differnew file mode 100644 index 000000000..6c7c52e77 --- /dev/null +++ b/Documentation/images/icons/cceiling.jpg diff --git a/Documentation/images/icons/ceiling1.jpg b/Documentation/images/icons/ceiling1.jpg Binary files differnew file mode 100644 index 000000000..7d8e0bc2f --- /dev/null +++ b/Documentation/images/icons/ceiling1.jpg diff --git a/Documentation/images/icons/ceiling2.jpg b/Documentation/images/icons/ceiling2.jpg Binary files differnew file mode 100644 index 000000000..f2e1c42f3 --- /dev/null +++ b/Documentation/images/icons/ceiling2.jpg diff --git a/Documentation/images/icons/ceiling3.jpg b/Documentation/images/icons/ceiling3.jpg Binary files differnew file mode 100644 index 000000000..96a7cea1a --- /dev/null +++ b/Documentation/images/icons/ceiling3.jpg diff --git a/Documentation/images/icons/ruler.jpg b/Documentation/images/icons/ruler.jpg Binary files differnew file mode 100644 index 000000000..fe5b7cd7c --- /dev/null +++ b/Documentation/images/icons/ruler.jpg diff --git a/Documentation/images/icons/scale.jpg b/Documentation/images/icons/scale.jpg Binary files differnew file mode 100644 index 000000000..02d45e6ae --- /dev/null +++ b/Documentation/images/icons/scale.jpg diff --git a/Documentation/images/main_window.jpg b/Documentation/images/main_window.jpg Binary files differindex 363ee17b2..c12f2651f 100644 --- a/Documentation/images/main_window.jpg +++ b/Documentation/images/main_window.jpg diff --git a/Documentation/user-manual.html.git b/Documentation/user-manual.html.git index 8ff3e0431..fc367eb04 100644 --- a/Documentation/user-manual.html.git +++ b/Documentation/user-manual.html.git @@ -410,11 +410,38 @@ asciidoc.install(3); <div class="paragraph"><p><span class="big">USER MANUAL</span></p></div>
<div class="paragraph"><p><strong>Manual authors</strong>: Willem Ferguson, Jacco van Koll, Dirk Hohndel, Reinout Hoornweg,
Linus Torvalds, Miika Turkia, Amit Chaudhuri, Jan Schubert, Salvador Cuñat</p></div>
-<div class="paragraph"><p><span class="blue"><em>Version 4.0.1, January 2014</em></span></p></div>
+<div class="paragraph"><p><span class="blue"><em>Version 4.1, April 2014</em></span></p></div>
<div class="paragraph"><p>Welcome as a user of <em>Subsurface</em>, an advanced dive logging programme with
extensive infrastructure to describe, organise, interpret and print scuba
-and free dives.
-<em>Subsurface</em> binaries are available for Windows PCs (Win XP or later), Intel
+and free dives. <em>Subsurface</em> offers many advantages above other similar
+software solutions:</p></div>
+<div class="ulist"><ul>
+<li>
+<p>
+Do you use two different dive computer brands, each with its own proprietary
+ software for downloading dive logs? Do you dive with rebreathers as well
+ as open circuit? Do you use a Reefnet Sensus time-depth recorder
+ in conjunction with a dive computer? <em>Subsurface</em> offers a standard
+ interface for downloading dive logs from all these different pieces of
+ equipment and to store and analyse the dive logs within a unified system.
+</p>
+</li>
+<li>
+<p>
+Do you use more than one operating system? <em>Subsurface</em> is fully compatible
+ with Mac, Linux and Microsoft, allowing you to access your dive log on each
+ of your operating systems in a unified way.
+</p>
+</li>
+<li>
+<p>
+Do you use Linux or Mac and your dive computer has only Microsoft-based software
+ for downloading dive information (e.g. Mares)? <em>Subsurface</em> provides a way of
+ storing and anaysing your dive logs on other operating systems.
+</p>
+</li>
+</ul></div>
+<div class="paragraph"><p><em>Subsurface</em> binaries are available for Windows PCs (Win XP or later), Intel
based Macs (OS/X) and many Linux distributions. <em>Subsurface</em> can be built for
many more hardware platforms and software environments where Qt and
libdivecomputer are available.</p></div>
@@ -435,30 +462,31 @@ Divers</p></div> <div class="sect1">
<h2 id="S_StartUsing">1. Start Using the Program</h2>
<div class="sectionbody">
-<div class="paragraph"><p>The <em>Subsurface</em> window is usually divided into four panels and has a <strong>Main
+<div class="paragraph"><p>The <em>Subsurface</em> window is usually divided into four panels with a <strong>Main
Menu</strong> (File Import Log View Filter Help) at the top of the window (for Windows
and Linux) or the top of the screen (for Mac and Ubuntu Unity). The four panels are:</p></div>
<div class="paragraph"><p>1) The <strong>Dive List</strong> to the bottom left containing a list of all the dives in the
user’s
dive log. A dive can be selected and highlighted on the dive list by clicking on
-it. In most situations the cursor up/down keys can be used to switch
-between dives.</p></div>
+it. In most situations the up/down keys can be used to switch
+between dives. The Dive List is an important tool for manipulating a dive log.</p></div>
<div class="paragraph"><p>2) The <strong>Dive Map</strong> to the bottom right, showing the user’s dive sites on a world
map
-and centred on the site of the last selected dive.</p></div>
+and centred on the site of the last dive selected in the <strong>Dive List</strong>.</p></div>
<div class="paragraph"><p>3) The <strong>Dive Info</strong> to the top left, giving more detailed information on the
-selected dive, including some statistics for the selected dive or for all
+dive selected in the <strong>Dive List</strong>, including some statistics for the selected dive or for all
highlighted dive(s).</p></div>
<div class="paragraph"><p>4) The <strong>Dive Profile</strong> to the top right, showing a graphical dive profile of the
-highlighted dive in the dive list.</p></div>
+selected dive in the <strong>Dive List</strong>.</p></div>
<div class="paragraph"><p>The dividers can be dragged between panels in order to change the size of any of
the panels. <em>Subsurface</em> remembers the position of the dividers, so the next
time <em>Subsurface</em> starts it uses the positions of the dividers when the program
was executed previously.</p></div>
-<div class="paragraph"><p>If one dive is selected, the dive location, detailed information and profile of
+<div class="paragraph"><p>If a dive is selected in the <strong>Dive List</strong>, the dive location, detailed information
+and profile of
the <em>selected dive</em> are shown in the respective panels. On the other hand, if
one highlights more than one dive the last highlighted dive is the <em>selected
-dive</em>, but summary data of all <em>highlighted dives</em> is shown in the <strong>Stats</strong> tab
+dive</em>, but summary data of all <em>highlighted dives</em> are shown in the <strong>Stats</strong> tab
of the <strong>Dive Info</strong> panel (maximum, minimum and average depths, durations, water
temperatures and SAC; total time and number of dives selected).</p></div>
<div class="imageblock" id="S_ViewPanels" style="text-align:center;">
@@ -467,7 +495,7 @@ temperatures and SAC; total time and number of dives selected).</p></div> </div>
</div>
<div class="paragraph"><p>The user can determine which of the four panels are displayed by selecting the
-<strong>View</strong> option on the main menu. This option gives the user several choices of
+<strong>View</strong> option on the main menu. This feature gives the user several choices of
display:</p></div>
<div class="paragraph"><p><strong>All</strong>: show all four of the panels as in the screenshot above.</p></div>
<div class="paragraph"><p><strong>Divelist</strong>: Show only the Dive List.</p></div>
@@ -476,11 +504,11 @@ display:</p></div> all highlighted dives.</p></div>
<div class="paragraph"><p><strong>Globe</strong>: Show only the world map, centred on the last selected dive.</p></div>
<div class="paragraph"><p>Like many other functions that can be accessed via the Main Menu, these options
-can be triggered using keyboard shortcuts as well. The shortcuts for a
+can be triggered using keyboard shortcuts. The shortcuts for a
particular system
-are shown with an underline in the menu entries. Since different Operating
+are shown with an underline in the main menu entries. Since different Operating
Systems and the user chosen language may cause <em>Subsurface</em> to use different
-shortcut keys they will not be listed here in the user manual.</p></div>
+shortcut keys they are not listed here in the user manual.</p></div>
<div class="paragraph"><p>When the program is started for the first time, it shows no information at all.
This is because the program doesn’t have any dive information available. In the
following sections, the procedures to create a new logbook will be described.</p></div>
@@ -490,13 +518,13 @@ following sections, the procedures to create a new logbook will be described.</p <h2 id="S_NewLogbook">2. Creating a new logbook</h2>
<div class="sectionbody">
<div class="paragraph"><p>Select <em>File → New Logbook</em> from the main menu. All existing dive data are
-cleared so that new information can be added. If there is unsaved data in an
-open logbook, <em>Subsurface</em> the user will be asked if the open logbook will be
+cleared so that new information can be added. If there are unsaved data in an
+open logbook, the user is asked whether the open logbook should be
saved before a new logbook is created.</p></div>
</div>
</div>
<div class="sect1">
-<h2 id="S_GetInformation">3. How to obtain dive information to store in the user’s logbook</h2>
+<h2 id="S_GetInformation">3. How to store dive information in the user’s logbook</h2>
<div class="sectionbody">
<div class="paragraph"><p>There are several ways in which dive information can be added to a logbook:</p></div>
<div class="olist arabic"><ol class="arabic">
@@ -553,14 +581,14 @@ fields are visible:</p></div> <img src="images/AddDive2.jpg" alt="FIGURE: The Dive Notes tab" />
</div>
</div>
-<div class="paragraph"><p>The <strong>Starttime</strong> field reflects the date and the time of the dive. By clicking
+<div class="paragraph"><p>The <strong>Start time</strong> field reflects the date and the time of the dive. By clicking
the down-arrow on the right of that field a calendar will be displayed from
which
one can choose the correct date. The time values (hour and minutes) can also be
edited directly by clicking on each of them in the text box and by overtyping the
information displayed.</p></div>
<div class="paragraph"><p><strong>Air and water temperatures</strong>: the air and water temperatures during the
-dive can be typed directly on the fields to the right of the Starttime.
+dive can be typed directly on the fields to the right of the Start time.
Temperature units are not needed, as they will be automatically supplied by
<em>Subsurface</em>. Only the numerical value must be
typed by the user (The units selected in the <em>Preferences</em>
@@ -577,7 +605,7 @@ here. These can come from three sources:</p></div> One can find the coordinates on the world map in the bottom right hand
part
of the Subsurface window. The map displays a green bar indicating "No location
-data - Move the map double-click to set the location". Upon a double-click
+data - Move the map and double-click to set the dive location". Upon a double-click
at the appropriate place, the green bar disappears and the coordinates are
stored.
</p>
@@ -607,9 +635,9 @@ Decimal degrees, e.g. 30.22496 , 30.821798</code></pre> <div class="paragraph"><p>Southern hemisphere latitudes are given with a <strong>S</strong>, e.g. S30°, or with a
negative value, e.g. -30.22496. Similarly western longitudes are given with a
<strong>W</strong>, e.g. W07°, or with a negative value, e.g. -7.34323.</p></div>
-<div class="paragraph"><p>Please note that GPS coordinates of a dive site a linked to the Location
-name - so adding coordinates to dives that don’t have a location set will
-caused unexpected behavior (as Subsurface will think that all of these
+<div class="paragraph"><p>Please note that GPS coordinates of a dive site are linked to the Location
+name - so adding coordinates to dives that does not have a location description
+will cause unexpected behavior (Subsurface will think that all of these
dives have the same location and try to keep their GPS coordinates the
same.</p></div>
<div class="paragraph"><p><strong>Divemaster</strong>: The name of the dive master or dive guide for this dive can be
@@ -618,13 +646,12 @@ Again, this field offers auto completion based on the list of dive masters in the current logbook.</p></div>
<div class="paragraph"><p><strong>Buddy</strong>: In this field one can enter the name(s) of the buddy / buddies
(separated by commas) who accompanied the user on the dive. Auto completion
-based on the list of buddies in the current logbook is offered.</p></div>
-<div class="paragraph"><p><strong>Suit</strong>: The type of diving suit that was used for the dive can be entered here.
-Just as with the
-other items, auto completion of the suit description is available.</p></div>
-<div class="paragraph"><p><strong>Rating</strong>: In this field, users can provide a subjective overall rating of the
+is offered based on the list of buddies in the current logbook.</p></div>
+<div class="paragraph"><p><strong>Suit</strong>: The type of diving suit used for the dive can be entered here.
+As with the other items, auto completion of the suit description is available.</p></div>
+<div class="paragraph"><p><strong>Rating</strong>: In this field, provide a subjective overall rating of the
dive on a 5-point scale by clicking the appropriate star on the rating scale.</p></div>
-<div class="paragraph"><p><strong>Visibility</strong>: As with the previous item, users can provide a rating of
+<div class="paragraph"><p><strong>Visibility</strong>: As with the previous item, provide a rating of
visibility during the dive on a 5-point scale by clicking the appropriate star.</p></div>
<div class="paragraph"><p><strong>Tags</strong>: Tags that describe the type of dive performed may
be entered here (separated by commas). Examples of common tags are boat, drift,
@@ -668,25 +695,26 @@ like this:</p></div> dive.
The dark dustbin icon on the left allows one to delete information for a
particular cylinder.</p></div>
-<div class="paragraph"><p>The user starts by selecting a cylinder type on the left-hand side of the
-table. To select a cylinder, the user must click in the <strong>cylinder type</strong> box.
-This brings up a list button that can be used to display a dropdown list of
+<div class="paragraph"><p>Start by selecting a cylinder type on the left-hand side of the
+table. To select a cylinder, click in the <strong>Type</strong> box.
+This brings up a button that can be used to display a dropdown list of
cylinders:</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
<img src="images/CylinderDataEntry2.jpg" alt="FIGURE:The cylinder drop-down list button" />
</div>
</div>
-<div class="paragraph"><p>This drop-down list can then be used to select the cylinder type that was used
-for this dive. The
+<div class="paragraph"><p>The drop-down list can be used to select the cylinder type used
+for the dive or the user may start typing in the box which shows the
+available options for the entered characters. The
<strong>Size</strong> of the cylinder as well as its working pressure (<strong>WorkPress</strong>) will
-automatically be shown in the dialogue.</p></div>
-<div class="paragraph"><p>Next, the user must indicate the starting pressure and the ending pressure of
+automatically be shown in the dialogue. If a cylinder is not shown in the dropdown list, type the name and description of that cylinder into the <strong>Type</strong> field.</p></div>
+<div class="paragraph"><p>Next, indicate the starting pressure and the ending pressure of
the
gas used during the dive. The unit of pressure (metric/imperial) corresponds
-to the setting in the preferences.</p></div>
-<div class="paragraph"><p>Finally, the user should type in the gas mixture used. If air
-was used, a value of 21% can be entered on this field, or the field might be
+to the setting in the <em>Preferences</em>.</p></div>
+<div class="paragraph"><p>Finally, type in the gas mixture used in the <strong>O2%</strong> field. If air
+was used, a value of 21% can be entered on this field, or it might be
left blank. If nitrox or trimix were used, their percentages of oxygen and/or
helium must be specified.
Any inappropriate fields should be left empty. After typing the information for
@@ -701,7 +729,7 @@ dive made using two cylinders (air and EAN50):</p></div> </div>
</div>
<div class="paragraph"><p><strong>Weights</strong>: Information about the weight system used during a dive can be entered
-using a dialogue very similar to that of the cylinder information. If the user
+using a dialogue very similar to that for the cylinder information. If the user
clicks the + button on the top right of the weights dialogue, the table looks
like this:</p></div>
<div class="imageblock" style="text-align:center;">
@@ -716,7 +744,8 @@ through a down-arrow:</p></div> <img src="images/WeightsDataEntry2.jpg" alt="FIGURE: Weights type drop-down list button" />
</div>
</div>
-<div class="paragraph"><p>This drop-down list can then be used to select the type of weight system. In
+<div class="paragraph"><p>The drop-down list can then be used to select the type of weight system or the user may start
+typing in the box which shows the available options for the entered characters. In
the <strong>Weight</strong>
field, the weight used during the dive must be typed. After typing the
information
@@ -733,7 +762,7 @@ with two types of weights: integrated and a weight belt:</p></div> </div>
</div>
<div class="paragraph"><p>There’s NO need to click the <em>Save</em> button before the dive
-profile has beeb completed.</p></div>
+profile has been completed.</p></div>
</div>
<div class="sect3">
<h4 id="_creating_a_dive_profile">3.1.3. Creating a Dive Profile</h4>
@@ -748,20 +777,21 @@ dive being described:</p></div> </div>
</div>
<div class="paragraph"><p><em>Modifying the dive profile</em>: When the cursor is moved around the dive profile,
-its position is indicated by two red lines as shown below. The depth and time
+its position is indicated by two colored lines (red and green) as shown below.
+The depth and time
that the cursor represents are indicated, respectively on the left hand and
bottom axes. The units (metric/imperial) on the axes are determined by the
-<strong>preference</strong> settings. The dive profile itself comprises several
+<strong>Preference</strong> settings. The dive profile itself comprises several
line segments demarcated by waypoints (white dots on the profile, as shown
above). The default dive depth is 15 m.
If the dive depth was 21 m then the user needs to drag the appropriate waypoints
downwards to represent 21 m. To add a waypoint, double-click on
-any line segment.
-To remove a waypoint, right-click on it and choose "Remove this point" from the
-context menu. The user will also need to drag the waypoints to represent an
+any line segment. To move an additional waypoint, drag it.
+To remove this waypoint, right-click on it and choose "Remove this point" from the
+context menu. The user needs to drag the waypoints to represent an
accurate
time duration for the dive. Below is a dive profile that represents a dive
-to 21 m for 31 min, followed by a 3 minute safety stop at 5 m.</p></div>
+to 21 m for 31 min, followed by a 5 minute safety stop at 5 m.</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
<img src="images/DiveProfile2.jpg" alt="FIGURE: Edited dive profile" />
@@ -771,20 +801,20 @@ to 21 m for 31 min, followed by a 3 minute safety stop at 5 m.</p></div> along the line segments of the dive profile. This defaults to the first gas
mixture specified in the <strong>Equipment</strong> tab, which was air in the case of the
profile illustrated above. The gas mixtures of segments of the dive profile can
-be edited. This is done by clicking on the particular
+be edited. This is done by right-clicking on the particular
waypoint and selecting the appropriate gas from the context menu. Changing
the gas for a waypoint affects the gas shown in the segment <em>to the left</em> of
that
waypoint. Note that only the gases defined in the <strong>Equipment</strong> tab appear in the
-context menu:</p></div>
+context menu.</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
<img src="images/DiveProfile3.jpg" alt="FIGURE: Gas composition context menu" />
</div>
</div>
-<div class="paragraph"><p>Below is the profile of a dive to 21 m for 31 min with a switch from air to
-EAN40 on the ascent. In this case the first cylinder in the <strong>Equipment</strong> tab
-contained air and the second cylinder contained EAN40.</p></div>
+<div class="paragraph"><p>Below is the profile of a dive to 21 m for 31 min for which an extra waypoint was added at 18 m on the ascent and with a switch from air to
+EAN50 at 18 m. In this case the first cylinder in the <strong>Equipment</strong> tab
+contained air and the second cylinder contained EAN50.</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
<img src="images/DiveProfile4.jpg" alt="FIGURE: Completed dive profile" />
@@ -800,7 +830,7 @@ on the top right hand of the Dive Notes tab. If the <em>Save</em> button is clic the dive data
are saved in the current logbook. If the <em>Cancel</em> button is clicked, the newly
entered
-dive data are discarded. When exiting <em>Subsurface</em> the user will be prompted
+dive data are discarded. When exiting <em>Subsurface</em>, the user will be prompted
once more to save the logbook with the new dive(s).</p></div>
</div>
</div>
@@ -827,9 +857,9 @@ PC-Communication mode. <strong>This could drain the dive computer’s batter therefore
recommend that the user checks if the dive computer is charged when
connected to the USB port of a PC. For example, several Suunto and Mares dive
-computers do not recharge through the USB connection. The users should refer to
+computers do not recharge through the USB connection. Users should refer to
the dive computer’s manual
-if they are unsure whether the dive computer will recharge or drain its batteries
+if they are unsure whether the dive computer recharges its batteries
while connected to the USB port.</td>
</tr></table>
</div>
@@ -879,13 +909,13 @@ though these dives have already been imported to <em>Subsurface</em>. For that r the download process faster on most dive computers and also saves battery power
of the dive computer (at least for those not charging while connected via USB).
If, for some reason, the user wishes to import ALL dives from the dive computer,
-even though some may already be in the logbook, then the the check box labeled
-<em>Force download of all dives</em> can be ticked.</p></div>
+even though some may already be in the logbook, then check the the check box labeled
+<em>Force download of all dives</em>.</p></div>
<div class="ulist"><ul>
<li>
<p>
The dialogue has two drop-down lists, <strong>Vendor</strong> and <strong>Dive Computer</strong>. On the
-<strong>vendor</strong> drop-down list the user must select the make of the computer, e.g.
+<strong>vendor</strong> drop-down list select the make of the computer, e.g.
Suunto, Oceanic,
Uwatec, Mares. On the <strong>Dive Computer</strong> drop-down list, the model name of
the dive computer must be selected, e.g. D4 (Suunto), Veo200 (Oceanic), or Puck
@@ -950,10 +980,10 @@ progress bar at the bottom of the dialogue (for some dive computers the progress information could be inaccurate as we cannot determine how much
downloadable data there is until all data have been downloaded). When the
download of the dive information is complete, all the imported dives appear
-in the Dive List, sorted by date and time. Disconnect and
+in the <strong>Dive List</strong>, sorted by date and time. Disconnect and
switch off the dive
computer to conserve its battery power.
-If a particular dive is selected, the Dive Profile panel shows an informative
+If a particular dive is selected, the <strong>Dive Profile</strong> panel shows an informative
graph of dive depth against time for that particular dive.
</p>
</li>
@@ -982,7 +1012,7 @@ software? Has it worked before, or is this the first time the cable is being use 4) Consult <strong>Appendix A</strong> and make sure that the correct Mount Point
was specified (see above).
5) On Unix-like operating systems, does the user have write permission to the
-USB port? If not, users should consult <strong>Appendix A</strong>.</td>
+USB port? If not, consult <strong>Appendix A</strong>.</td>
</tr></table>
</div>
<div class="paragraph"><p>If the <em>Subsurface</em> computer does not recognise the USB adaptor by
@@ -997,13 +1027,14 @@ two check boxes checked in the download dialogue discussed above:</p></div> <pre><code>Save libdivecomputer logfile
Save libdivecomputer dumpfile</code></pre>
</div></div>
-<div class="paragraph"><p><strong>Important</strong>: These check boxes are only used when problems are ancountered
+<div class="paragraph"><p><strong>Important</strong>: These check boxes are only used when problems are encountered
during the download process: under normal circumstances they should not be checked.
When checking these boxes, the user is prompted to select a folder to
save the information to. The default folder is the one in which the <em>Subsurface</em>
-dive log is kept. <strong>Important:</strong> After downloading with the above checkboxes
-checked, no dives are shown in the
-<strong>Dive List</strong> but two files are created in the folder selected above:</p></div>
+dive log is kept.</p></div>
+<div class="paragraph"><p><strong>Important:</strong> <em>After downloading with the above checkboxes
+checked, no dives are added to the
+<strong>Dive List</strong> but two files are created in the folder selected above</em>:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>subsurface.log
@@ -1011,7 +1042,7 @@ subsurface.bin</code></pre> </div></div>
<div class="paragraph"><p>These files should be send to the <em>Subsurface</em> mail list:
<em>subsurface@hohndel.org</em> with a
-request for the files to be analysed. One should provide the dive computer
+request for the files to be analysed. Provide the dive computer
make and model
as well as contextual information about the dives recorded on the dive computer.</p></div>
</div></div>
@@ -1019,14 +1050,14 @@ as well as contextual information about the dives recorded on the dive computer. <div class="sect3">
<h4 id="S_EditDiveInfo">3.2.2. Updating the dive information imported from the dive computer.</h4>
<div class="paragraph"><p>The information from the dive computer is not complete and more
-details must be added in order to have a more complete record of the dives. To
+details must be added in order to have a more full record of the dives. To
do this,
the <strong>Dive Notes</strong> and the <strong>Equipment</strong> tabs on the top left hand of the
<em>Subsurface</em> window should be used.</p></div>
</div>
<div class="sect3">
<h4 id="_dive_notes_2">3.2.3. Dive Notes</h4>
-<div class="paragraph"><p>The date and time of the dive, gas mixture and perhaps water temperature is
+<div class="paragraph"><p>The date and time of the dive, gas mixture and (often) water temperature is
shown as obtained from the dive computer, but the user needs to add some
additional information by hand in order to have a more complete dive record.
The message in a blue box at
@@ -1038,14 +1069,14 @@ visible:</p></div> <img src="images/AddDive3.jpg" alt="FIGURE: The Dive Notes tab" />
</div>
</div>
-<div class="paragraph"><p>The <strong>Starttime</strong> field reflects the date and the time of the dive, as supplied by
+<div class="paragraph"><p>The <strong>Start time</strong> field reflects the date and the time of the dive, as supplied by
the dive computer. It should therefore not be necessary to edit this, but one
could make changes by clicking the down-arrow on the right of that field to
display a calendar from which the correct date can be chosen. The hour and
minutes values can also be edited by clicking on each of them in the text box
and by overtyping the information displayed.</p></div>
<div class="paragraph"><p><strong>Air/water temperatures</strong>: Air and water temperatures during the dive are shown
-in these fields to the right of the Starttime. Many dive computers supply water
+in these fields to the right of the Start time. Many dive computers supply water
temperature information and this field may therefore not require further
editing. If
editing is required, only a value is required. The units of temperature will be
@@ -1053,10 +1084,7 @@ automatically supplied by <em>Subsurface</em> (according to the <em>Preferences</em>, metric or imperial units will
be used).</p></div>
<div class="paragraph"><p><strong>Location</strong>: In this field one should type in text that describes the site
-where the dive was performed, e.g. "Tihany, Lake Balaton, Hungary". the required drivers to interact with the download cable and connected dive
-computer.</p></div>
-<div class="paragraph"><p>If all the above points are in order and there is a failure to download the
-dive
+where the dive was performed, e.g. "Tihany, Lake Balaton, Hungary".
Auto completion of location names will
make this easier when a user frequently dives at the same sites.</p></div>
<div class="paragraph"><p><strong>Coordinates</strong>: The geographic coordinates of the dive site should be entered
@@ -1066,8 +1094,8 @@ here. These can come from three sources:</p></div> <p>
The user can find the coordinates on the world map in the bottom right hand
part
-of the Subsurface window. The map displays a green bar indicating "No location
-data - move the map and double-click to set the location". Double-click
+of the Subsurface window. The map displays a green bar indicating "Move the map
+and double-click to set the dive location". Double-click
at the appropriate place, the green bar disappears and the coordinates are
stored.
</p>
@@ -1095,11 +1123,11 @@ Decimal degrees, e.g. 30.22496 , 30.821798</code></pre> </li>
</ol></div>
<div class="paragraph"><p>Southern hemisphere latitudes are given with a <strong>S</strong>, e.g. S30°, or with a
-negative value, e.g. -30.22496. Similarly western longitudes are given with a
+negative value, e.g. -30.22496. Similarly, western longitudes are given with a
<strong>W</strong>, e.g. W07°, or with a negative value, e.g. -7.34323.</p></div>
<div class="paragraph"><p><strong>Divemaster</strong>: The name of the dive master or dive guide for this dive should be
-entered in this field.
-This field offers auto completion based on the list of dive masters in
+entered in this field
+which offers auto completion based on the list of dive masters in
the current logbook.</p></div>
<div class="paragraph"><p><strong>Buddy</strong>: In this field, one enters the name(s) of the buddy / buddies
(separated with commas) who accompanied him/her on the
@@ -1166,14 +1194,15 @@ cylinders:</p></div> </div>
</div>
<div class="paragraph"><p>The drop-down list can then be used to select the cylinder type that was used
-for this dive. The
+for this dive or the user may start typing in the box which shows the available
+options for the entered characters. The
<strong>Size</strong> of the cylinder as well as its working pressure (<strong>WorkPress</strong>) will
automatically be shown in the dialogue.</p></div>
<div class="paragraph"><p>Next one must indicate the starting pressure and the ending pressure of the
specified gas during the dive. The unit of pressure (metric/imperial)
-corresponds to the settings chosen in the preferences.</p></div>
-<div class="paragraph"><p>Finally, the user must type in the gas mixture used. If air was used, the
-value of 21% can be entered here or this field can be left blank. If nitrox or
+corresponds to the settings chosen in the <em>Preferences</em>.</p></div>
+<div class="paragraph"><p>Finally, the user must provide the gas mixture used. If air was used, the
+value of 21% can be entered or this field can be left blank. If nitrox or
trimix were used, their percentages of oxygen and/or helium should be entered.
Any inappropriate fields should be left empty. After typing the
information for the cylinder,
@@ -1205,7 +1234,9 @@ down-arrow:</p></div> </div>
</div>
<div class="paragraph"><p>The drop-down list can then be used to select the type of weight system
-used during the dive. In the <strong>Weight</strong>
+used during the dive or the user may start typing in the box
+which shows the available options for the entered characters.
+In the <strong>Weight</strong>
field, type in the amount of weight used during the dive. After
specifying the weight
system, the user can either press <em>ENTER</em> on the keyboard or click outside the
@@ -1214,7 +1245,7 @@ It is possible to enter information for more than one weight system by adding an additional system
using the + button on the top right hand. Weight systems can be deleted using
the dustbin icon on the left hand. Here is an example of information for a dive
-with two types of weights: integrated and a weight belt:</p></div>
+with two types of weights: integrated as well as a weight belt:</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
<img src="images/WeightsDataEntry3.jpg" alt="FIGURE: A completed weights information table" />
@@ -1224,7 +1255,7 @@ with two types of weights: integrated and a weight belt:</p></div> <div class="sect3">
<h4 id="_saving_the_updated_dive_information">3.2.5. Saving the updated dive information</h4>
<div class="paragraph"><p>The information entered in the <strong>Dive Notes</strong> tab and the <strong>Equipment</strong> tab can be
-saved with all the other information of the dives in the user’s logbook by
+saved by
using the
two buttons on the top right hand of the <strong>Dive Notes</strong> tab. If the <em>Save</em> button
is clicked,
@@ -1237,13 +1268,13 @@ that the new data should be saved.</p></div> </div>
<div class="sect2">
<h3 id="_importing_dive_information_from_other_digital_data_sources_or_other_data_formats">3.3. Importing dive information from other digital data sources or other data formats</h3>
-<div class="paragraph" id="S_ImportingAlienDiveLogs"><p>If a user has been diving for some time, it’s possible that several dives were
-logged using other dive log software. All this information needs not
-be typed by hand into <em>Subsurface</em>, because these divelogs can probably be
+<div class="paragraph" id="S_ImportingAlienDiveLogs"><p>If a user has been diving for some time, it is possible that several dives were
+logged using other dive log software. This information does not need retyping
+because these divelogs can probably be
imported onto <em>Subsurface</em>. <em>Subsurface</em> will import divelogs from a range of
other
dive log software. While some software is supported natively, for others the
-user will have to
+user has to
export the logbook(s) to an intermediate format so that they can then be imported
by <em>Subsurface</em>.
Currently, <em>Subsurface</em> supports importing CSV log files from several sources.
@@ -1252,7 +1283,7 @@ preconfigured, but because the import is flexible, users can configure their own imports.
Manually kept log files (e.g. in spreadsheet) can also be imported by
configuring the CSV import.
-<em>Subsurface</em> can also import UDDF and UDCF files that are used by some divelog
+<em>Subsurface</em> can also import UDDF and UDCF files used by some divelog
software and some divecomputers, like the Heinrichs & Weikamp DR5. Finally,
for some divelog software like Mares Dive Organiser it is currently suggested to
import the logbooks first into a webservice like <em>divelogs.de</em> and then import
@@ -1280,12 +1311,12 @@ available, as in dialogue <strong>B</strong>, above. Currently these are:</p></d <div class="ulist"><ul>
<li>
<p>
-XML-formatted dive logs
+XML-formatted dive logs (Divinglog 5.0, MacDive and several other dive log systems)
</p>
</li>
<li>
<p>
-UDDF-formatted dive logs
+UDDF-formatted dive logs (e.g. Kenozoooid)
</p>
</li>
<li>
@@ -1305,27 +1336,12 @@ Suunto Dive Manager (DM3 and DM4) </li>
<li>
<p>
-MacDive (XML)
-</p>
-</li>
-<li>
-<p>
-DivingLog 5.0 (XML)
-</p>
-</li>
-<li>
-<p>
-Kenozooid (UDDF)
-</p>
-</li>
-<li>
-<p>
CSV (text-based and spreadsheet-based) dive logs.
</p>
</li>
</ul></div>
<div class="paragraph"><p>Selecting the appropriate file in the file list of the dialogue opens
-the imported dive log in the <em>Subsurface</em> <strong>Dive List</strong>. A few other formats, not
+the imported dive log in the <em>Subsurface</em> <strong>Dive List</strong>. Some other formats, not
accessible through the Import dialogue are also supported, as explained below.</p></div>
</div>
<div class="sect3">
@@ -1334,15 +1350,14 @@ accessible through the Import dialogue are also supported, as explained below.</ multi-platform applications, these dive logs cannot be
directly imported into
<em>Subsurface</em>. Mares dive logs need to be imported using a three-step process,
-using <em>www.divelogs.de</em> as a mechanism to extract the dive information from
-the file.</p></div>
+using <em>www.divelogs.de</em> as a mechanism to extract the dive log information.</p></div>
<div class="ulist"><ul>
<li>
<p>
The dive log data from Mares Dive Organiser need to be exported to the user’s
desktop, using
- a <em>.sdf</em> file name extension. Refer to <a href="#Mares_Export">Appendix
-C</a> for more information.
+ a <em>.sdf</em> file name extension. Refer to <a href="#Mares_Export">Appendix C</a> for more
+information.
</p>
</li>
<li>
@@ -1372,7 +1387,7 @@ brings up a dialogue box (see figure on left [<strong>A</strong>] below). Enter user-ID and password for <em>divelogs.de</em> into the appropriate fields and then
select
the <em>Download</em> button. Download from <em>divelogs.de</em> starts immediately,
-displaying a progress bar in the dialogue box. At the and of the download, the
+displaying a progress bar in the dialogue box. At the end of the download, the
success status is indicated (see figure on the right [<strong>B</strong>], below). The
<em>Apply</em> button should then be selected, after which the imported dives appear in the
<em>Subsurface</em> <strong>Dive List</strong> panel.</p></div>
@@ -1386,8 +1401,8 @@ success status is indicated (see figure on the right [<strong>B</strong>], below <h4 id="S_ImportingCSV">3.3.4. Importing dives in CSV format</h4>
<div class="paragraph"><p>Sometimes dive computers export dive information as files with
<em>comma-separated values</em> (.CSV). For example, the APD Inspiration and Evolution
-closed circuit rebreather (CCR) systems export dive information in CSV format
-that normally contains information about a single dive only. These
+closed circuit rebreather (CCR) systems export dive information in a CSV
+formatted file that normally contains information for a single dive only. These
files can easily be imported into <em>Subsurface</em>.
CSV files are normally organised into
a single line that provides the headers of the data columns, followed by the
@@ -1424,14 +1439,14 @@ data? This field separator should be either a comma (,) or a TAB character. This can be determined by opening the file with a text editor. If it is
comma-delimited, then the comma
characters between the values are clearly visible. If no commas are evident and
-the numbers are aligned,
+the numbers are aligned in columns,
the file is probably TAB-delimited (i.e. it uses a TAB as a field separator, as
in the above example).
</p>
</li>
<li>
<p>
-Which data columns need to be imported into <em>Subsurface</em>?. The Dive Time and
+Which data columns need to be imported into <em>Subsurface</em>? The Dive Time and
Depth columns are always required. Open the file using a text editor and note
the titles of the columns to be imported and their column positions. For
instance for the above example:
@@ -1448,7 +1463,7 @@ ppO2: column 4</code></pre> <div class="paragraph"><p>Armed with this information, importing the data into <em>Subsurface</em> is
straightforward. Select
<em>Import→Import Log Files</em> from the main menu. In the resulting file
-selection menu, select oa CSV files, after which a common configuration dialog
+selection menu, select <em>CSV files</em>, after which a common configuration dialog
appears for all the
files with a CSV extension:</p></div>
<div class="imageblock" style="text-align:center;">
@@ -1459,7 +1474,7 @@ files with a CSV extension:</p></div> <div class="paragraph"><p>There are pre-configured definitions for some dive computers, e.g. the APD
rebreathers. If the user’s dive computer is on this list, it should be selected
using the dropdown
-box labeled <em>Pre-configured imports</em>. Finally the <em>OK</em> should be clicked and
+box labeled <em>Pre-configured imports</em>. Finally <em>OK</em> should be clicked and
the dive will be imported and listed in the <strong>Dive List</strong> tab of <em>Subsurface</em>.</p></div>
<div class="paragraph"><p>If the dive computer is not on the pre-configured list, the user must
select the <em>Field
@@ -1470,7 +1485,7 @@ variables. For each data column used for import, the user must check the appropriate check box
and indicate in which column these data are found. For instance, the image above
corresponds to the dialogue that would apply to the CSV data set described above
-the image. Having performed the column specification, select the <em>OK</em> button
+the image. After completing the column specification, select the <em>OK</em> button
and the dive will be imported and listed in the <strong>Dive List</strong> tab of <em>Subsurface</em>.</p></div>
</div>
<div class="sect3">
@@ -1496,18 +1511,19 @@ the data are in clear columns, the file
is probably TAB-delimited (i.e. it uses a TAB as a field separator, as in the
above example).
-A recommended field separator for the export is tab, as commas might be used in
+A recommended field separator for the export is tab, as commas might be part of
the
-field values themselves.
+field values themselves. Therefore the use of an appropriate field separator
+in very important.
</p>
</li>
<li>
<p>
-Which columns need to be imported into <em>Subsurface</em>?. We do not
+Which columns need to be imported into <em>Subsurface</em>? We do not
currently have any mandatory input fields, but some, e.g. dive duration
are crucial for the log file to make any sense. Possible options
can be seen in the image below and one should include all the
-fields available in both your log file and in the Subsurface
+fields available in both your log file and in the <em>Subsurface</em>
import.
</p>
</li>
@@ -1515,16 +1531,16 @@ import. <p>
Units used for depth, weight and temperature. We consider depth to be
either feet or meters, weight kilograms or pounds and temperature either
-Celsius or Fahrenheit. However, the users can only select <em>Metric</em> or
-<em>Imperial</em> in the Preferences tab of <em>Subsurface</em>.. No mixture of unit s
-ystems is allowed for the different fields.
+Celsius or Fahrenheit. However, the users can select <em>Metric</em> or
+<em>Imperial</em> in the <em>Preferences</em> tab of <em>Subsurface</em>. No mixture of unit
+systems is allowed for the different fields.
</p>
</li>
</ol></div>
<div class="paragraph"><p>Importing manually kept CSV log files is quite straight forward, but
there might be many fields and counting the field numbers is error
prone. Therefore validation of the data to be imported is critical.</p></div>
-<div class="paragraph"><p>To import the dives, one must select <em>Import→Import Log Files</em> from the menu
+<div class="paragraph"><p>To import the dives, select <em>Import→Import Log Files</em> from the menu
bar. If the CSV option in the dropdown list is selected and the file list
includes file names ending with .CSV, one can select the
<em>Manual dives</em> tab that will bring up the following configuration dialog:</p></div>
@@ -1539,17 +1555,19 @@ appear in the <strong>Dive List</strong> area of <em>Subsurface</em>.</p></div> </div>
</div>
<div class="sect2">
-<h3 id="S_Companion">3.4. Importing GPS coordinates with the Subsurface Companion app for mobile</h3>
-<div class="paragraph"><p>phones
-If the user has an Android device with GPS, the coordinates for the diving
-location can be obtained and automatically passed to Subsurface
-divelog. To do this, one needs to:</p></div>
+<h3 id="S_Companion">3.4. Importing GPS coordinates with the <strong>Subsurface Companion App</strong> for mobile phones</h3>
+<div class="paragraph"><p>If the user has an Android device with GPS, the coordinates for the diving
+location can be obtained and automatically passed to the <em>Subsurface</em>
+divelog. This takes place when the Companion App stores the dive locations on
+a dedicated Internet-based file server. <em>Subsurface</em>, in turn, can collect
+the localities from the file server.</p></div>
+<div class="paragraph"><p>To do this, one needs to:</p></div>
<div class="ulist"><ul>
<li>
<p>
Register on the <a href="http://api.hohndel.org/login/">Subsurface companion web page</a>.
A confirmation mail with instructions and a personal <strong>DIVERID</strong> will be send together with
-a long number that gives access to the companion app capabilities.
+a long number that gives access to the file server and Companion App capabilities.
</p>
</li>
<li>
@@ -1587,14 +1605,14 @@ option (see below). </li>
</ul></div>
<div class="paragraph"><p>Now one is ready to get a dive position and send it to the server. The Android
-display will look like the left hand image (A) below, but without any dive.</p></div>
-<div class="paragraph"><p>Touching the "+" icon on the top right to add a new dive site. Users will be
-prompted for a place name (or asked to activate the GPS if it was turned off).
+display will look like the left hand image (<strong>A</strong>) below, but without any dive.</p></div>
+<div class="paragraph"><p>Touch the "+" icon on the top right to add a new dive site. Users will be
+prompted for a place name (or asked to activate the GPS if it is turned off).
The main screen shows a list of dive locations, each with a name, date and
time. Some dives may have an arrow-up icon on the selection box to the left (see
image B in the middle, below) indicating that they require upload to the server.</p></div>
<div class="paragraph"><p>There are several ways to send dives to the server; the easiest is by simply
-selecting the dive. See middle image below (B):</p></div>
+selecting the dive. See middle image below (<strong>B</strong>):</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
<img src="images/Companion.jpg" alt="FIGURE: Screen shots (A-C) of companion app" />
@@ -1613,10 +1631,10 @@ it deletes the dive location(s).</td> <div class="paragraph"><p>The new dive points are now stored on the server and can be downloaded to the
<em>Subsurface</em> dive log whenever users upload or add dives to <em>Subsurface</em>.
After a dive trip using the Companion app, all dive locations are ready to be
-saved on a Subsurface dive log (see below).</p></div>
+saved on a <em>Subsurface</em> dive log (see below).</p></div>
<div class="sect4">
<h5 id="_settings_on_the_companion_app">Settings on the Companion app</h5>
-<div class="paragraph"><p>Selecting the <em>Settings</em> menu option results in the right hand image above ©.</p></div>
+<div class="paragraph"><p>Selecting the <em>Settings</em> menu option results in the right hand image above (<strong>C</strong>).</p></div>
</div>
<div class="sect4">
<h5 id="_server_and_account">Server and account</h5>
@@ -1734,7 +1752,7 @@ of another registered diver.</p></div> </div>
</div>
<div class="sect3">
-<h4 id="_downloading_dive_locations_to_a_subsurface_divelog">3.4.2. Downloading dive locations to a Subsurface divelog</h4>
+<h4 id="_downloading_dive_locations_to_the_em_subsurface_em_divelog">3.4.2. Downloading dive locations to the <em>Subsurface</em> divelog</h4>
<div class="paragraph"><p>Download dive(s) from a dive computer or enter them manually into
<em>Subsurface</em> before obtaining the GPS coordinates from the server. The download
dialog can be reached via <em>Ctrl+G</em> or from the <em>Subsurface</em> Main Menu <em>Import
@@ -1756,7 +1774,7 @@ date-times of the uploaded GPS localities.</p></div> <td class="icon">
<img src="images/icons/info.jpg" alt="Note" />
</td>
-<td class="content">Features, issues and tips._ Since <em>Subsurface</em> matches GPS locations from the
+<td class="content"><em>Features, issues and tips.</em> Since <em>Subsurface</em> matches GPS locations from the
Android device and dive information from the dive computer based on date-time
data, automatic assignment of GPS data to dives is dependent on agreement of
date and time between these two devices. If there is a large difference between
@@ -1813,7 +1831,10 @@ water temperature and surface air consumption (SAC).</p></div> </div>
</div>
<div class="paragraph"><p>Of all the panels in <em>Subsurface</em>, the Dive Profile contains the most detailed
-information about each dive. The main item in the Dive profile is the graph of
+information about each dive. The Dive Profile has a <strong>button bar</strong> on the left hand side
+that allows control over several display options. The functions of these
+buttons are described below. The main item in the Dive
+Profile is the graph of
dive depth as a function of time. In addition to the obvious information of
the depth it also shows the ascent and descent rates compared to the recommended
speed of going up or down in the water column. This information is given using
@@ -1863,6 +1884,48 @@ cellspacing="0" cellpadding="4"> <div class="paragraph"><p>The profile also includes depth readings for the peaks and troughs in the graph.
Thus, users should see the depth of the deepest point and other peaks. Mean depth
is marked with a horizontal red line.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/scale.jpg" alt="Note" />
+</td>
+<td class="content">In some cases the dive profile does not fill the whole area of the <strong>Dive Profile</strong>
+panel. Clicking the <strong>Scale</strong> button in the toolbar on the left of the dive profile
+frequently increases the size of the dive profile to fill the area of the panel efficiently.</td>
+</tr></table>
+</div>
+<div class="paragraph"><p><strong>Water temperature</strong> is displayed with its own blue line with temperature values
+placed adjacent to significant changes.</p></div>
+<div class="paragraph"><p>The dive profile can include graphs of the <strong>partial pressures</strong>
+of O2, N2, and He during the dive (see figure above) as well as a calculated and dive computer
+reported deco ceilings (only visible for deep, long, or repetitive dives). Partial pressures of oxygen are indicated in green, those of nitrogen in black, and those of helium in dark red. These
+partial pressure graphs are shown below the profile data.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/O2.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button allows display of the partial pressure of <strong>oxygen</strong> during the
+dive. This is depicted below the dive depth and water temperature graphs.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/N2.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button allows display of the partial pressure of <strong>nitrogen</strong> during the dive.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/He.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button allows display of the partial pressure of <strong>helium</strong> during the dive.
+This is only of importance to divers using Trimix, Helitrox or similar breathing gasses.</td>
+</tr></table>
+</div>
<div class="paragraph"><p>The <strong>air consumption</strong> graph displays the tank pressure and its change during the
dive. The air consumption takes depth into account so that even when manually
entering the start and end pressures the graph is not a straight line.
@@ -1877,8 +1940,6 @@ times of increased normalized air consumption while dark green reflects times when the diver was using less gas than average. The colour coding is obviously
only possible when a tank sensor is connected and tank pressure readings during
the dive are available.</p></div>
-<div class="paragraph"><p><strong>Water temperature</strong> is displayed with its own blue line with temperature values
-placed adjacent to significant changes.</p></div>
<div class="paragraph"><p>It is possible to <strong>zoom</strong> into the profile graph. This is done either by using
the scroll wheel / scroll gesture of your mouse or trackpad. By default
<em>Subsurface</em> always shows a profile area large enough for at least 30 minutes
@@ -1891,50 +1952,94 @@ that free divers clearly won’t care about.</p></div> <img src="images/MeasuringBar.png" alt="FIGURE: Measuring Bar" />
</div>
</div>
-<div class="paragraph"><p>Measurements of <strong>depth differences</strong> can be achieved by using the button with
-two vertical bars at the right of the dive profile panel (users should refer to
-the above figure,
-bottom right). The measurement is done by dragging the red dots to the two points
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/ruler.jpg" alt="Note" />
+</td>
+<td class="content">Measurements of <strong>depth or time differences</strong> can be achieved by using the
+<strong>ruler button</strong> on the left of the dive profile panel.
+The measurement is done by dragging the red dots to the two points
on the dive profile that the user wishes to measure. Information is then given
-along the line connecting the
-two red dots.</p></div>
-<div class="paragraph"><p>The dive profile can include further information that is typically more
-interesting for tec divers. Enabling these is described in the section entitled
-<em>Setting up Preferences</em>. Basically, users can include graphs of the <strong>partial
-pressures</strong>
-of O2, N2, and He during the dive as well as a calculated and dive computer
-reported deco ceilings (only visible for deep, long, or repetitive dives). The
-partial pressure graphs are added below the profile data, the calculated ceiling
-is shown as a green overlay on top of the dive profile. Above the profile the
-currently used gradient factors are shown (e.g. GF 35/75). Please note that
-these are NOT the gradient factors in use by the dive computer in question,
-but the gradient factors used by <em>Subsurface</em> to calculate deco obligations
-during the dive.</p></div>
-<div class="paragraph"><p>The graph can also include the dive computer reported <strong>ceiling</strong> (or more
-precisely, the first deco stop that the dive computer recorded – users are reminded that not
+in the horizontal white area underneath the
+two red dots.</td>
+</tr></table>
+</div>
+<div class="paragraph"><p>The profile can also include the dive computer reported <strong>ceiling</strong> (more
+precisely, the deepest deco stop that the dive computer calculated for each particular moment in time) as a red overlay on the dive profile. Ascent ceilings arise when a direct ascent to the surface increases
+the risk of a diver suffering from decompression sickness (DCS) and it is necessary to either ascend
+slower or to perform decompression stop(s) before ascending to the surface. Not
all dive computers record this information and make it available for download;
for example all of the Suunto dive computers fail to make this very useful data
-available to divelog software). Because of the differences in used algorithms
-and amount of data available (and factors taken into consideration) at the time
-of the calculation it is unlikely that both of these are the same; this can be
-true even if the same algorithm and <em>gradient factors</em> (see below) are used.
+available to divelog software. <em>Subsurface</em> also calculates ceilings independently,
+shown as a green overlay on the dive profile.
+Because of the differences in algorithms used
+and amount of data available (and other factors taken into consideration at the time
+of the calculation) it is unlikely that ceilings from dive computers and from <em>Subsurface</em> are the same, even if the same algorithm and <em>gradient factors</em> (see below) are used.
It is also quite common that <em>Subsurface</em> calculates a ceiling for
non-decompression dives when the dive computer stayed in non-deco mode during
the whole dive (represented by the <span class="green">dark green</span> section in the profile
at the beginning of this section). This is caused by the fact that
<em>Subsurface’s</em>
-calculations describe the deco obligation at that point in time during a dive,
+calculations describe the deco obligation at each moment during a dive,
while dive computers usually take the upcoming ascent into account. During the
-ascent some excess Nitrogen (and possibly Helium) are already breathed off so
+ascent some excess nitrogen (and possibly helium) are already breathed off so
even
though the diver technically encountered a ceiling at depth, the dive still does
not require an explicit deco stop. This feature allows dive computers to offer
-longer non-stop bottom time.</p></div>
-<div class="paragraph"><p>For a more detailed explanation of <em>gradient factors</em>, use the following links:</p></div>
+longer non-stop bottom times.</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/cceiling.jpg" alt="Note" />
+</td>
+<td class="content">If the dive computer itself calculates a ceiling and makes it available to
+<em>Subsurface</em> during upload of dives, this can be
+shown as a red area by checking <strong>Dive computer reported ceiling</strong> button on the Profile Panel.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/ceiling1.jpg" alt="Note" />
+</td>
+<td class="content">If the <strong>Calculated ceiling</strong> button on the Profile Panel is clicked, then a ceiling, calculated by <em>Subsurface</em>, is shown in green if it exists for
+a particular dive (<strong>A</strong> in figure below). This setting can be modified in two ways:</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/ceiling2.jpg" alt="Note" />
+</td>
+<td class="content">If, in addition, the <strong>show all tissues</strong> button on the Profile Panel is clicked, the ceiling is shown for the tissue
+compartments following the BĂĽhlmann model (<strong>B</strong> in figure below).</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/ceiling3.jpg" alt="Note" />
+</td>
+<td class="content">If, in addition, the <strong>3m increments</strong> button on the Profile Panel is clicked, then the ceiling is indicated in 3 m increments
+(<strong>C</strong> in figure below).</td>
+</tr></table>
+</div>
+<div class="imageblock" style="text-align:center;">
+<div class="content">
+<img src="images/Ceilings2.jpg" alt="Figure: Ceiling with 3m resolution" />
+</div>
+</div>
+<div class="paragraph"><p>Gradient Factor settings strongly affect the calculated ceilings and their depths.
+For more information about Gradient factors, see the section on <a href="#S_GradientFactors">Gradient Factor Preference settings</a>. The
+currently used gradient factors (e.g. GF 35/75) are shown above the depth profile if the appropriate toolbar buttons are activated.
+<strong>N.B.:</strong> The indicated gradient factors are NOT the gradient factors in use by the dive computer,
+but those used by <em>Subsurface</em> to calculate deco obligations
+during the dive. For more information external to this manual see:</p></div>
<div class="ulist"><ul>
<li>
<p>
-<a href="http://www.tek-dive.com/portal/upload/M-Values.pdf">Understanding M-values, by Erik Baker, 1988, <em>Immersed</em> Vol. 3, No. 3.</a>
+<a href="http://www.tek-dive.com/portal/upload/M-Values.pdf">Understanding M-values by Erik Baker, <em>Immersed</em> Vol. 3, No. 3.</a>
</p>
</li>
<li>
@@ -1964,7 +2069,7 @@ restored to view by selecting Unhide all events from the context menu.</p></div> profile. Normally the Information Box is located to the top left of the <strong>Dive
Profile</strong> panel. If the mouse points outside of the <strong>Dive Profile</strong> panel, then
only the top line of the Information Box is visible (see left-hand part of
-figure (A) below). The Information Box can be moved around in the <strong>Dive Profile</strong>
+figure (<strong>A</strong>) below). The Information Box can be moved around in the <strong>Dive Profile</strong>
panel by click-dragging it with the mouse so that it is not obstructing
important detail. The position of the Information Box is saved and used again
during subsequent dive analyses.</p></div>
@@ -1973,9 +2078,9 @@ during subsequent dive analyses.</p></div> <img src="images/InfoBox2.jpg" alt="Figure: Information Box" />
</div>
</div>
-<div class="paragraph"><p>The moment the mouse points inside the <strong>Dive Profile</strong> panel, it expands and
+<div class="paragraph"><p>The moment the mouse points inside the <strong>Dive Profile</strong> panel, the information box expands and
shows many data items. In this situation, the data reflect the time point along
-the dive profile indicated by the mouse (see right-hand part of figure (B) above
+the dive profile indicated by the mouse cursor (see right-hand part of figure (<strong>B</strong>) above
where the Information Box reflects the situation at the position of the cursor
[arrow] in that image). Therefore, moving the cursor in the horizontal
direction allows the Information Box to show information for any point along the
@@ -1984,10 +2089,73 @@ In this mode, the Information Box gives extensive statistics about depth, gas and ceiling characteristics of the particular dive. These include: Time period
into the dive (indicated by a @), depth, cylinder pressure (P), temperature,
ascent/descent rate, surface air consumption (SAC), oxygen partial pressure,
-maximum operating depth, effective air depth (EAD), effective narcotic depth
+maximum operating depth, equivalent air depth (EAD), equivalent narcotic depth
(END), equivalent air density depth (EADD), decompression requirements at that
instant in time (Deco), time to surface (TTS), the calculated ceiling, as well
as the calculated ceiling for several BĂĽhlmann tissue compartments.</p></div>
+<div class="paragraph"><p>The user has control over the display of several statstics, represented as four
+buttons on the left of the profile panel. These are:</p></div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/MOD.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button causes the Information Box to display the <strong>Maximum Operating Depth
+(MOD)</strong> of the dive, given the
+gas mixture used. MOD is dependent on the oxygen concentration in the breathing gas.
+For air (21% oxygen) it is around 57 m. Below the MOD there is a markedly increased
+risk of exposure to the dangers associated with oxygen toxicity.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/NDL.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button causes the Information Box to display the <strong>No-deco Limit (NDL)</strong> or the
+<strong>Total Time to Surface (TTS)</strong>. NDL is the time duration that a diver can continue with a
+dive, given the present depth, that does not require decompression (that is, before an
+ascent ceiling appears). Once one has exceeded the NDL and decompression is required (that
+is, there is an ascent ceiling above the diver, then TTS gives the number of minutes
+required before the diver can surface. TTS includes ascent time as well as decompression
+time.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/SAC.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button causes the Information Box to display the <strong>Surface Air Consumption (SAC)</strong>.
+SAC is an indication of the surface-normalised respiration rate of a diver. The value of SAC
+is less than the real
+respiration rate because a diver at 10m uses breathing gas at a rate roughly double that of
+the equivalent rate at the surface. SAC gives an indication of breathing gas consumption rate
+independent of the depth of the dive so that the respiratory rates of different dives
+can be compared. The units for SAC is litres/min or cub ft/min.</td>
+</tr></table>
+</div>
+<div class="admonitionblock">
+<table><tr>
+<td class="icon">
+<img src="images/icons/EAD.jpg" alt="Note" />
+</td>
+<td class="content">Clicking this button displays the <strong>Equivalent Air Depth (EAD)</strong> for
+nitrox dives as well as the <strong>Equivalent
+Narcotic Depth (END)</strong> for trimix dives. These are numbers of
+importance to divers who use breathing gases other than air. Their
+values are dependent on the composition of the breathing gas. The EAD
+is the depth of a hypothetical air dive that has the same partial
+pressure of nitrogen as the current depth of the nitrox dive at
+hand. A nitrox dive leads to the same decompression obligation as an
+air dive to the depth equalling the EAD. The END is the depth of a
+hypothetical air dive that has the same sum of partial pressures of
+the narcotic gases nitrogen and oxygen as the current trimix dive. A
+trimix diver can expect the same narcotic effect as a diver breathing
+air diving at a depth equalling the END.</td>
+</tr></table>
+</div>
+<div class="paragraph"><p>Figure (<strong>B</strong>) above shows an information box with a nearly complete set of data.</p></div>
</div>
</div>
</div>
@@ -2012,8 +2180,8 @@ a dive or a group of dives and then right-clicking.</p></div> numbers) to recent dives (having the highest sequence numbers). The numbering
of the dives is not always consistent. For instance, when non-recent dives are
added to the dive list the numbering does not automatically follow on because
-of the dives that are more recent in date/time than the newly-added dive that
-has an older date/time. Therefore, one may sometimes need to renumber the dives.
+of the dives that are more recent in date/time than the newly-added dive with
+an older date/time. Therefore, one may sometimes need to renumber the dives.
This is performed by selecting (from the Main Menu) <em>Log → Renumber</em>. Users are
given a choice with respect to the lowest sequence number to be used.
Completing this operation results in new sequence numbers (based on date/time)
@@ -2033,28 +2201,30 @@ list comprising five dive trips (<strong>B</strong>, on the right):</p></div> </div>
</div>
<div class="paragraph"><p>Grouping into trips allows a rapid way of accessing individual dives without
-having to scan long lists of dives. In order to group the dives in a dive list,
-(from the Main Menu) users must select <em>Log → Autogroup</em>. The <strong>Dive List</strong> panel now shows
-only the titles for the trips.</p></div>
+having to scan a long lists of dives. In order to group the dives in a dive list,
+(from the Main Menu) users must select <em>Log → Autogroup</em>. The <strong>Dive List</strong> panel
+now shows only the titles for the trips.</p></div>
<div class="sect3">
-<h4 id="_viewing_the_dives_during_a_particular_trip">5.3.1. Viewing the dives during a particular trip</h4>
-<div class="paragraph"><p>Once when the dives have been grouped into trips, users can expand one or more
-trips by clicking the arrow-head on the left of each trip title. This expands
-the selected trip, revealing the individual dives performed during the trip.</p></div>
-</div>
-<div class="sect3">
-<h4 id="_editing_the_title_and_associated_information_for_a_particular_trip">5.3.2. Editing the title and associated information for a particular trip</h4>
+<h4 id="_editing_the_title_and_associated_information_for_a_particular_trip">5.3.1. Editing the title and associated information for a particular trip</h4>
<div class="paragraph"><p>Normally, in the dive list, minimal information is included in the trip title.
More information about a trip can be added by selecting its trip title from
-the dive list. This shows a <strong>Trip Notes</strong> tab in the <strong>Dive Notes</strong> panel. Here
+the <strong>Dive list</strong>. This shows a <strong>Trip Notes</strong> tab in the <strong>Dive Notes</strong> panel. Here
users can add or edit information about the date/time, the trip location and any
other general comments about the trip as a whole (e.g. the dive company that was
-dived with, the general weather during the trip, etc.). After entering this
-information, users should select <strong>Save</strong> from the buttons at the top right of the <strong>Trip Notes</strong>
+dived with, the general weather and surface conditions during the trip, etc.).
+After entering this
+information, users should select <strong>Save</strong> from the buttons at the top right
+of the <strong>Trip Notes</strong>
tab. The trip title in the <strong>Dive List</strong> panel should now reflect some of the
edited information.</p></div>
</div>
<div class="sect3">
+<h4 id="_viewing_the_dives_during_a_particular_trip">5.3.2. Viewing the dives during a particular trip</h4>
+<div class="paragraph"><p>Once when the dives have been grouped into trips, users can expand one or more
+trips by clicking the arrow-head on the left of each trip title. This expands
+the selected trip, revealing the individual dives performed during the trip.</p></div>
+</div>
+<div class="sect3">
<h4 id="_collapsing_or_expanding_dive_information_for_different_trips">5.3.3. Collapsing or expanding dive information for different trips</h4>
<div class="paragraph"><p>If a user right-clicks after selecting a particular trip in the dive list, the
resulting context menu allows several possibilities to expand or collapse dives
@@ -2074,7 +2244,8 @@ top 3 dives; trip 2: bottom 2 dives) by selecting and right-clicking the top three dives. The resulting context menu allows the user to create a new trip by
choosing the option <strong>Create new trip above</strong>. The top three dives are then
grouped
-into a separate trip. The figures bellow show the selection and context menu on the left (A) and
+into a separate trip. The figures bellow shows the selection and context menu
+on the left (A) and
the completed action on the right (B):</p></div>
<div class="imageblock" style="text-align:center;">
<div class="content">
@@ -2096,18 +2267,17 @@ calibration dives of the dive computer or dives of extremely short duration.</p> <div class="sect3">
<h4 id="_unlink_a_dive_from_a_trip">5.4.2. Unlink a dive from a trip</h4>
<div class="paragraph"><p>Users can unlink dives from the trip to which they belong. In order to do this,
-users should select and right-click
-the relevant dives to bring up the context menu. Then the option <strong>Remove dive(s)
-from trip</strong> should be selected.
-The dives that have been unlinked now appear immediately above the trip to
+select and right-click
+the relevant dives to bring up the context menu. Then select the option <strong>Remove dive(s)
+from trip</strong>. The dive(s) now appear immediately above the trip to
which they belonged.</p></div>
</div>
<div class="sect3">
<h4 id="_add_a_dive_to_the_trip_immediately_above">5.4.3. Add a dive to the trip immediately above</h4>
<div class="paragraph"><p>Selected dives can be moved from the trip to which they belong and placed within
-the trip immediately above the currently active trip. To do this, users must select
+the trip immediately above the currently active trip. To do this, select
and right-click
-the dives to bring up the context menu, and then select <strong>Add dive(s) to trip
+the dive(s) to bring up the context menu, and then select <strong>Add dive(s) to trip
immediately above</strong>.</p></div>
</div>
<div class="sect3">
@@ -2153,20 +2323,22 @@ below shows the depth profile two such dives that were merged:</p></div> </li>
<li>
<p>
-Universal Dive Data format (UDDF). User should refer to <a href="http://uddf.org">http://uddf.org</a> for more information.
+Universal Dive Data format (UDDF). Refer to <a href="http://uddf.org">http://uddf.org</a> for more information.
UDDF is a generic format that enables communication among many dive computers
and computer programs.
</p>
</li>
</ul></div>
-<div class="paragraph"><p>In order to save the WHOLE dive log (i.e. all trips and dives), <strong>File</strong> should be selected
-from the Main menu. To save in <em>Subsurface</em> XML format, users should select <em>File → Save
-as</em>. To save in UDDF format, the <em>File → Export UDDF</em> option should be selected.</p></div>
-<div class="paragraph"><p>In order to save only one or more dives or one or two trips, users can select the
+<div class="paragraph"><p>In order to save the WHOLE dive log (i.e. all trips and dives), select <strong>File</strong>
+from the Main menu. To save in <em>Subsurface</em> XML format, select <em>File → Save as</em>.
+To save in UDDF format, select <em>File → Export UDDF</em>.</p></div>
+<div class="paragraph"><p>In order to save only one or more dives or one or two trips, select the
appropriate dives or trips in the <strong>Dive List</strong> panel and then right-click the
selected dives to bring up the context menu. To save in <em>Subsurface</em> XML
-format, users should select <em>Save as</em> from the context menu. To save in UDDF format, users should select
+format, select <em>Save as</em> from the context menu. To save in UDDF format, select
<em>Export as UDDF</em> from the context menu.</p></div>
+<div class="paragraph"><p>Export to other formats can be achieved through third party facilities, for
+instance <em>www.divelogs.de</em>.</p></div>
</div>
</div>
<div class="sect1">
@@ -2258,7 +2430,7 @@ the output for one particular page.</p></div> </div>
</div>
<div class="sect1">
-<h2 id="S_Preferences">8. Setting user <strong>Preferences</strong> for <em>Subsurface</em></h2>
+<h2 id="S_Preferences">8. Setting user <em>Preferences</em> for <em>Subsurface</em></h2>
<div class="sectionbody">
<div class="paragraph"><p>There are several settings within <em>Subsurface</em> that the users can specify. These
are found when selecting <em>File→Preferences</em>. The settings are performed in
@@ -2324,7 +2496,7 @@ independently, with some units in the metric system and other in the imperial.</ <img src="images/Preferences3.jpg" alt="FIGURE: Preferences Graph page" />
</div>
</div>
-<div class="paragraph"><p>This panel allows two type of selections:</p></div>
+<div class="paragraph" id="S_GradientFactors"><p>This panel allows two type of selections:</p></div>
<div class="ulist"><ul>
<li>
<p>
@@ -2343,44 +2515,6 @@ figure below). </li>
<li>
<p>
-Ceiling: Ascent ceilings arise when a direct ascent to the surface increases
-the risk of a diver suffering from decompression sickness (DCS) and it is necessary to either ascend
-slower or to perform decompression stop(s) before ascending to the surface.
-<em>Subsurface</em> can indicate these ceilings above which the diver should not ascend
-at a particular point in time (see the green-shaded areas in the figure in the
-section on <a href="#S_DiveProfile">Dive Profiles</a>) and in the figure below:
-</p>
-<div class="ulist"><ul>
-<li>
-<p>
-If the <strong>Calculated ceiling</strong> option is checked, then a ceiling is shown if it exists for
-a particular dive (<strong>A</strong> in figure below)
-</p>
-</li>
-<li>
-<p>
-If the <strong>show all tissues</strong> option is checked, the ceiling is shown for the tissue
-compartments following the BĂĽhlmann model (<strong>B</strong> in figure below).
-</p>
-</li>
-<li>
-<p>
-If the <strong>3m increments</strong> option is checked, then the ceiling is indicated in 3 m increments
-(<strong>C</strong> in figure below).
-</p>
-</li>
-<li>
-<p>
-If the dive computer itself calculates a ceiling and makes it available to
-<em>Subsurface</em> during upload of dives, this can be
-shown by checking <strong>Dive computer reported ceiling</strong> and it can be drawn in red by
-checking <strong>Draw ceiling red</strong>.
-</p>
-</li>
-</ul></div>
-</li>
-<li>
-<p>
If a <em>Maximum operating depth (MOD)</em> or an <em>Equivalent air depth (EAD)</em>
applies to a dive profile, these can be shown by checking the appropriate boxes.
</p>
@@ -2395,16 +2529,9 @@ appropriate box. </li>
</ul></div>
</li>
-</ul></div>
-<div class="imageblock" style="text-align:center;">
-<div class="content">
-<img src="images/Ceilings.png" alt="Figure: Ceiling with 3m resolution" />
-</div>
-</div>
-<div class="ulist"><ul>
<li>
<p>
-<strong>Misc</strong>: Here users can set the <em>gradient factors</em> used while diving. GF_Low is
+<strong>Misc</strong>: <strong>Gradient Factors:</strong> Here users can set the <em>gradient factors</em> used while diving. GF_Low is
the gradient factor at deep and GF_High is used just below the surface.
At intermediate depths gradient factors between GF_Low and GF_High are used.
Gradient factors add conservatism to the nitrogen exposure during a dive, in a
@@ -2654,25 +2781,29 @@ computer prefers (e.g. bluetooth, USB, infrared).</td> <div class="ulist"><ul>
<li>
<p>
-On Linux this means users need to have the correct kernel
- module loaded. Most distributions will do this automatically.
+On Linux users need to have the correct kernel
+ module loaded. Most distributions will do this automatically, so the
+ user does not need to load drivers.
</p>
</li>
<li>
<p>
On Windows, the OS should offer to download the correct
- driver once the user connects the dive computer to the USB port.
+ driver once the user connects the dive computer to the USB port and
+ operating system sees the equipment for the first time.
</p>
</li>
<li>
<p>
On a Mac users sometimes 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
+ devices or any other dive computer using a USB-to-serial interface
+ based on the Silicon Labs CP2101 or similar chip can be found as
+ <em>Mac_OSX_VCP_Driver.zip</em> at the
+<a href="http://www.silabs.com/support/pages/document-library.aspx?p=Interface&f=USB%20Bridges&pn=CP2101">Silicon Labs document and software repository</a>.
</p>
</li>
</ul></div>
-<div class="paragraph"><p><a href="http://www.silabs.com/support/pages/support.aspx?ProductFamily=USB+Bridges">http://www.silabs.com/support/pages/support.aspx?ProductFamily=USB+Bridges</a></p></div>
</div>
<div class="sect2">
<h3 id="S_HowFindDeviceName">10.2. How to Find the Device Name for USB devices and set its write permission</h3>
@@ -2692,7 +2823,7 @@ instructions on ways to find out what the device name is:</td> <div class="paragraph"><div class="title">On Windows:</div><p>Simply try COM1, COM2, etc. The drop down list should contain all connected COM
devices.</p></div>
<div class="paragraph"><div class="title">On MacOS:</div><p>The drop down box should find all connected dive computers.</p></div>
-<div class="paragraph"><div class="title">On Linux:</div><p>Try the following:</p></div>
+<div class="paragraph"><div class="title">On Linux:</div><p>There is a definitive way to find the port:</p></div>
<div class="ulist"><ul>
<li>
<p>
@@ -2720,7 +2851,7 @@ Type the command: <em>dmesg</em> and press enter </p>
</li>
</ul></div>
-<div class="paragraph"><p>Within the terminal, users should see a message similar to this one:</p></div>
+<div class="paragraph"><p>A message similar to this one should appear:</p></div>
<div class="literalblock">
<div class="content">
<pre><code>usb 2-1.1: new full speed USB device number 14 using ehci_hcd
@@ -2739,11 +2870,11 @@ usb 2-1.1: FTDI USB Serial Device converter now attached to ttyUSB3 usbcore: registered new interface driver ftdi_sio
ftdi_sio: v1.6.0:USB FTDI Serial Converters Driver</code></pre>
</div></div>
-<div class="paragraph"><p>Users can see that in the third line from the bottom, the USB adapter is
-detected and is connected to <code>ttyUSB3</code>. This information can now be used in
-the import settings as <code>/dev/ttyUSB3</code>. This directs Subsurface to the correct
+<div class="paragraph"><p>The third line from the bottom shows that the FTDI USB adapter is
+detected and connected to <code>ttyUSB3</code>. This information can now be used in
+the import settings as <code>/dev/ttyUSB3</code> which directs Subsurface to the correct
USB port.</p></div>
-<div class="paragraph"><p>Ensuring you have write permission to the USB serial port:</p></div>
+<div class="paragraph"><p>Ensuring that the user has write permission to the USB serial port:</p></div>
<div class="paragraph"><p>On Unix-like operating systems the USB ports can only be accessed by users who
are members
of the <code>dialout</code> group. If one is not root, one may not be a member of
@@ -2752,8 +2883,8 @@ will not be able to use the USB port. Let us assume one’s username is <em> <div class="ulist"><ul>
<li>
<p>
-As root, type: <code>usermod -a -G dialout johnB</code> (Ubuntu users: <code>sudo -a -G
-dialout johnB</code>)
+As root, type: <code>usermod -a -G dialout johnB</code> (Ubuntu users: <code>sudo usermod
+-a -G dialout johnB</code>)
This makes johnB a member of the <code>dialout</code> group.
</p>
</li>
@@ -2782,77 +2913,75 @@ dives.</p></div> <td class="content">For dive computers communicating through bluetooth like the Heinrichs
Weikamp Frog or the Shearwater Predator and Petrel there is a
different procedure to get the devices name to communicate with
-<em>Subsurface</em>. In general it consists of these steps:</td>
+<em>Subsurface</em>. Follow these steps:</td>
</tr></table>
</div>
<div class="ulist"><ul>
<li>
<p>
-enable bluetooth on your computer
+<strong>For the dive computer, after enabling Bluetooth, ensure it is in Upload mode.</strong>
</p>
</li>
+</ul></div>
+<div class="paragraph"><p>For Bluetooth pairing of the dive computer, refer to the
+manufacturer’s user guide. When using a Shearwater Predator/Petrel, select
+<em>Dive Log → Upload Log</em> and wait for the <em>Wait PC</em> message.</p></div>
+<div class="ulist"><ul>
<li>
<p>
-pairing the device
+<strong>Pair the <em>Subsurface</em> computer with the dive computer.</strong>
</p>
</li>
</ul></div>
-<div class="paragraph"><p>Do not forget to set your divecomputer in Bluetooth or upload mode before
-Paring and Downloading logs. If you use a Shearwater Predator/Petrel just select
-<em>Dive Log → Upload Log</em> and wait until you see the <em>Wait PC</em> message.</p></div>
-<div class="paragraph"><div class="title">On Windows:</div><p>Bluetooth is most likely already enabled. For pairing the device choose
-Control Panel→Bluetooth Devices→Add Wireless Device
+<div class="paragraph"><div class="title">On Windows:</div><p>Bluetooth is most likely already enabled. For pairing with the dive computer choose
+<em>Control Panel→Bluetooth Devices→Add Wireless Device</em>.
This should bring up a dialog showing your dive computer (in Bluetooth mode) and
-allowing to pair it. For bluetooth pairing of your dive computer refer to the
-manufacturer’s user guide. The dive computer should then show up in the list of
-Bluetooth devices and you may then right click on it and choose Properties→COM
-Ports to identify the port used for your dive computer. If there are several
+allowing pairing. Right click on it and choose <em>Properties→COM
+Ports</em> to identify the port used for your dive computer. If there are several
ports listed, use the one saying "Outgoing" instead of "Incoming".</p></div>
-<div class="paragraph"><p>For downloading to <em>Subsurface</em>, the drop down list should contain this COM
-port already. If not, enter it manually.</p></div>
-<div class="paragraph"><p>Note: If you have issues downloading from your dive computer in other software
-afterwards try to remove the pairing with your dive computer.</p></div>
+<div class="paragraph"><p>For downloading to <em>Subsurface</em>, the <em>Subsurface</em> drop-down list should contain
+this COM port already. If not, enter it manually.</p></div>
+<div class="paragraph"><p>Note: If there are issues afterwards downloading from the dive computer using
+other software, remove the existing pairing with the dive computer.</p></div>
<div class="paragraph"><div class="title">On MacOS:</div><p>Click on the Bluetooth symbol in the menu bar and select <em>Set up
-Bluetooth Device…</em>. Make sure that your dive computer is in upload
-mode; it should then show up in the list of devices. Select it and go
+Bluetooth Device…</em>. The dive computer should then show up in the list of devices. Select it and go
through the pairing process. This step should only be needed once for
initial setup.</p></div>
-<div class="paragraph"><p>Once the pairing is completed the correct device will be shown in the
-<em>Device or Mount Point</em> drop down in the <em>Subsurface</em> <strong>Import</strong> dialog.</p></div>
-<div class="paragraph"><div class="title">On Linux</div><p>Ensure bluetooth is enabled on the <em>Subsurface</em> computer.
-On most common distributions this should be true out of the box. If not then
-depending on your system, running <code>initd</code> or <code>systemd</code>. This might be different
-and also involve loading modules specific to your hardware. In case your system
-is
-running <code>systemd</code>, manually run <code>sudo systemctl start bluetooth.service</code> to
-enable
-it, in case of <code>initd</code>, run something like <code>sudo rc.config start bluetoothd</code> or
-<code>sudo /etc/init.d/bluetooth start</code>.</p></div>
-<div class="paragraph"><p>Pairing should be straight forward. Using Gnome3 for instance will show a
-bluetooth icon in the upper right corner of the desktop where one selects <em>Set
+<div class="paragraph"><p>Once the pairing is completed the correct device is shown in the
+<em>Device or Mount Point</em> drop-down in the <em>Subsurface</em> <strong>Import</strong> dialog.</p></div>
+<div class="paragraph"><div class="title">On Linux</div><p>Ensure Bluetooth is enabled on the <em>Subsurface</em> computer.
+On most common distributions this should be true out of the box and
+pairing should be straight forward. For instance, Gnome3 shows a
+Bluetooth icon in the upper right corner of the desktop where one selects <em>Set
up New Device</em>. This should show a dialog where one can select the
-dive computer (in bluetooth mode) and pair it. For issues with PIN
-setting try manually setting <em>0000</em>.</p></div>
+dive computer (which already should be in Bluetooth mode) and pair it.
+If a PIN is required, try manually setting <em>0000</em>.</p></div>
+<div class="paragraph"><p>In the rare cases where the above is not true, then
+depending on your system, try <code>initd</code> or <code>systemd</code>. This might be different
+and also involve loading modules specific to your hardware. In case your system
+is running <code>systemd</code>, manually run <code>systemctl start bluetooth.service</code> to
+enable it, in case of <code>initd</code>, run something like <code>rc.config start bluetoothd</code> or
+<code>/etc/init.d/bluetooth start</code>.</p></div>
<div class="paragraph"><p>One may also use a manual approach by using such commands:</p></div>
<div class="ulist"><ul>
<li>
<p>
-<code>sudo hciconfig</code> - shows the bluetooth devices available on your
+<code>hciconfig</code> shows the Bluetooth devices available on your
computer (not dive computer), most likely one will see a hci0, if not
-try <em>sudo hcitool -a</em> to see inactive devices and try to run <em>sudo
-hciconfig hci0 up</em> to bring them up
+try <code>hcitool -a</code> to see inactive devices and run <code>sudo
+hciconfig hci0 up</code> to bring them up.
</p>
</li>
<li>
<p>
-<code>sudo hcitool scanning</code>- use this to get a list of bluetooth enabled
+<code>hcitool scanning</code> gets a list of bluetooth enabled
client devices, look for the dive computer and remember the MAC
-address shown there
+address are shown there
</p>
</li>
<li>
<p>
-<code>sudo bluez-simple-agent hci0 10:00:E8:C4:BE:C4</code> - this will pair
+<code>bluez-simple-agent hci0 10:00:E8:C4:BE:C4</code> pairs
the dive computer with the bluetooth stack of the <em>Subsurface</em> computer, copy/paste
the MAC address from the output of <em>hcitool scanning</em>
</p>
@@ -2863,23 +2992,22 @@ manually by running:</p></div> <div class="ulist"><ul>
<li>
<p>
-<code>sudo rfcomm bind /dev/rfcomm0 10:00:E8:C4:BE:C4</code> - bind the dive
+<code>rfcomm bind /dev/rfcomm0 10:00:E8:C4:BE:C4</code> binds the dive
computer to a communication device in the desktop computer, in case rfcomm is
-already taken just use rfcomm1 or up, please copy/paste the MAC address
-from the output of <em>hcitool scanning</em>, the MAC shown in here will not
+already taken use rfcomm1 or up. IMPORTANT: Copy/paste the MAC address
+from the output of <code>hcitool scanning</code>, the MAC address shown above will not
work.
</p>
</li>
</ul></div>
-<div class="paragraph"><p>For downloading dives in Subsurface one then has to specify <code>/dev/rfcomm0</code>
-as device name to use.</p></div>
+<div class="paragraph"><p>For downloading dives in <em>Subsurface</em> specify the device name connected to the MAC
+address in the last step above, e.g. <em>/dev/rfcomm0</em>.</p></div>
</div>
</div>
</div>
<div class="sect1">
-<h2 id="_appendix_b_dive_computer_specific_information_for_importing_dive">11. APPENDIX B: Dive Computer specific information for importing dive</h2>
+<h2 id="_appendix_b_dive_computer_specific_information_for_importing_dive_information">11. APPENDIX B: Dive Computer specific information for importing dive information.</h2>
<div class="sectionbody">
-<div class="paragraph"><p>information.</p></div>
<div class="sect2">
<h3 id="S_ImportUemis">11.1. Import from a Uemis Zurich</h3>
<div class="admonitionblock">
@@ -2887,12 +3015,15 @@ as device name to use.</p></div> <td class="icon">
<img src="images/icons/iumis.jpg" alt="Note" />
</td>
-<td class="content">Things are very similar to a normal USB-connected dive computer when
-downloading dives from a Uemis Zurich
-dive computer (one of the ones that recharge when
-connected to the USB port). The main difference is that one does not enter a
+<td class="content"><em>Subsurface</em> downloads the information
+stored on the SDA (the built-in file system of the Uemis) including
+information about dive spots and
+equipment. Buddy information is not yet downloadable.
+Things are very similar to a normal USB-connected dive computer
+(the Uemis is one of those that recharge when connected to the USB port).
+The main difference is that one does not enter a
device name, but instead the location where the UEMISSDA file system is
-mounted once you connect the dive computer. On Windows this is a drive letter (
+mounted once connected to the dive computer. On Windows this is a drive letter (
often <em>E:</em> or <em>F:</em>), on a Mac this is
<em>/Volumes/UEMISSDA</em> and on Linux systems this differs depending on the
distribution. On Fedora it usually is
@@ -2900,13 +3031,13 @@ distribution. On Fedora it usually is should suggest the correct location in the drop down list.</td>
</tr></table>
</div>
-<div class="paragraph"><p>Once onehas selected this as device name one can download the
+<div class="paragraph"><p>After selecting the above device name, download the
dives from the Uemis Zurich. One technical issue with the Uemis Zurich
-download implementation (this is a firmware limitation, not a
+download implementation (this is a Uemis firmware limitation, not a
<em>Subsurface</em> issue) is that one cannot download more than about 40-50
dives without running out of memory on the SDA. This will usually only
-happen the very first time one downloads dives from the Uemis Zurich -
-normally when downloading at the end of a day or even after a dive
+happen the very first time one downloads dives from the Uemis Zurich.
+Normally when downloading at the end of a day or even after a dive
trip, the capacity is sufficient. If <em>Subsurface</em> displays an error
that the dive computer ran out of space the solution is straight
forward. Disconnect the SDA, turn it off and on again, and reconnect
@@ -2914,9 +3045,6 @@ it. You can now retry (or start a new download session) and the download will continue where it stopped previously. One
may have to do this more than once, depending on how many dives are
stored on the dive computer.</p></div>
-<div class="paragraph"><p>At this point <em>Subsurface</em> downloads most of the information
-stored on the SDA, including information about dive spots and
-equipment. Buddy information is not yet downloadable.</p></div>
</div>
<div class="sect2">
<h3 id="S_ImportingDR5">11.2. Importing dives from Heinrichs Weikamp DR5</h3>
@@ -2930,38 +3058,37 @@ for every dive. Mark all the dives you’d like to import or open.
Note: The DR5 does not seem to store gradient factors nor deco information, so
for <em>Subsurface</em> it is not possible to display them. Adjust the gradient
-factors in the Tec Settings in <em>Subsurface</em> to generate a deco overlay in the _
-Subsurface_ <strong>Dive Profile</strong> panel
-to get deco displayed but please note that the deco calculated by <em>Subsurface</em>
-will most likely differ from the one displayed on the DR5.</td>
+factors in the <em>Tec Settings</em> in <em>Subsurface</em> to generate a deco overlay in the
+<em>Subsurface</em> <strong>Dive Profile</strong> panel but please note that the deco calculated by
+<em>Subsurface</em> will most likely differ from the one displayed on the DR5.</td>
</tr></table>
</div>
</div>
<div class="sect2">
-<h3 id="_import_from_shearwater_predator_using_bluetooth">11.3. Import from Shearwater Predator using bluetooth</h3>
+<h3 id="_import_from_shearwater_predator_using_bluetooth">11.3. Import from Shearwater Predator using Bluetooth</h3>
<div class="admonitionblock">
<table><tr>
<td class="icon">
<img src="images/icons/predator.jpg" alt="Note" />
</td>
-<td class="content">Using a Shearwater Predator you may be able to pair Bluetooth but then encounter
+<td class="content">Using a Shearwater Predator one may be able to pair Bluetooth but then encounter
issues when downloading, showing errors like <em>Slip RX: unexp. SLIP END</em> on the
Predator.
-This might also be seen, when using other dive log software and operating
-systems than Linux. We have no detailed idea about the source and how to fix
+This might also arise when using other dive log software and operating
+systems other than Linux. We have no detailed idea about the source and how to fix
this, but it is reported to be solved sometimes by one of these steps:</td>
</tr></table>
</div>
<div class="ulist"><ul>
<li>
<p>
-use the bluetooth dongle which came with the Shearwater Predator instead of
- the built-in one of your computer
+use the Bluetooth dongle which came with the Shearwater Predator instead of
+ the built-in one of the <em>Subsurface</em> computer
</p>
</li>
<li>
<p>
-switch to different bluetooth drivers for your hardware
+switch to different Bluetooth drivers for the same hardware
</p>
</li>
<li>
@@ -3061,7 +3188,7 @@ The export pop-up will show </li>
<li>
<p>
-Within this pop-up, there is one field called Export Path.
+Within this pop-up, there is one field called <em>Export Path</em>.
</p>
<div class="ulist"><ul>
<li>
@@ -3106,9 +3233,9 @@ The dives are now exported to the file Divelogs.SDE. </li>
</ul></div>
<div class="paragraph"><p><strong>Divemanager 4 (DM4):</strong></p></div>
-<div class="paragraph"><p>To import divelog from <em>Suunto DM4</em>, one needs to locate the DM4 database
+<div class="paragraph"><p>To export divelog from <em>Suunto DM4</em>, one needs to locate the DM4 database
where the dives are stored. the user can either look for the original
-database or take a backup of the dives. Both methods are described here.</p></div>
+database or make a backup of the dives. Both methods are described here.</p></div>
<div class="paragraph"><p>Locating the Suunto DM4 database:</p></div>
<div class="ulist"><ul>
<li>
@@ -3180,7 +3307,7 @@ The dives are now exported to the file DM4.bak <img src="images/icons/mareslogo.jpg" alt="Note" />
</td>
<td class="content">Mares Dive Organiser is a Microsoft application. The dive log is kept as a
-Microsoft SQL Compact Edition data base with a .SDF filename extension. The
+Microsoft SQL Compact Edition data base with a <em>.sdf</em> filename extension. The
data base includes all Dive Organiser-registered divers on the particular
computer and all Mares dive computers used. The safest way to obtain a copy
of the dive data base is to export the information to another compatible format
@@ -3221,8 +3348,8 @@ Extract the <em>.sdf</em> file from the zipped folder to your Desktop. indication on the preferences set on one’s system. So in order for
<em>Subsurface</em> to be able to successfully import XML files from DivingLog
one first needs to ensure that DivingLog is configured
-to use the Metric system (one can easily change this in <em>File →
-Preferences → Units and Language</em> by clicking the <em>Metric</em>
+to use the Metric system (one can easily change this within Diving Log by
+selecting <em>File → Preferences → Units and Language</em> by clicking the <em>Metric</em>
button). Then do the following:</td>
</tr></table>
</div>
@@ -3251,7 +3378,9 @@ Click on the export button and select the filename <div class="sectionbody">
<div class="sect2">
<h3 id="_subsurface_appears_to_miscalculate_gas_consumption_and_sac">13.1. Subsurface appears to miscalculate gas consumption and SAC</h3>
-<div class="paragraph" id="SAC_CALCULATION"><p>Not really. What happens is that subsurface actually calculates gas
+<div class="paragraph" id="SAC_CALCULATION"><p><em>Question</em>: I dived with a 12.2 l tank, starting with 220 bar and ending with 100 bar, and I calculate a different SAC compared what <em>Subsurface</em> calculates. Is <em>Subsurface</em>
+miscalculating?</p></div>
+<div class="paragraph"><p><em>Answer</em>: Not really. What happens is that <em>Subsurface</em> actually calculates gas
consumption differently - and better - than you expect.
In particular, it takes the incompressibility of the gas into account.
Traditionally, Gas consumption and SAC should be:
@@ -3271,15 +3400,8 @@ Remember: one ATM is ~1.013 bar, so without the compressibility, your gas use is <div class="paragraph"><p>which is about 1445, not 1464. So there was 19 l too much in your simple
calculation that ignored the difference between 1 bar and one ATM.
The compressibility does show up above 200 bar, and takes that 1445 down
-about eight liters more, so you really did use only about 1437 l of air at surface pressure.
-The math details can be seen in dive.c:</p></div>
-<div class="paragraph"><p><code>surface_volume_multiplier().</code></p></div>
-<div class="paragraph"><p>The "if (bar > 200) bar = .." part is the compressibility - it’s an approximation,
-but it’s a reasonably good one, and closer to reality than not doing it.
-You can get the numbers you expect if you remove that, and turn the function into just:</p></div>
-<div class="paragraph"><p><code>return pressure.mbar / 1000.0;</code></p></div>
-<div class="paragraph"><p>but that would actually be wrong.
-So be happy: your SAC really is better than your calculations indicated.
+about eight liters more, so you really did use only about 1437 l of air at surface pressure.</p></div>
+<div class="paragraph"><p>So be happy: your SAC really is better than your calculations indicated.
Or be sad: your cylinder contains less air than you thought it did.
And as mentioned, the "contains less air than you thought it did" really
starts becoming much more noticeable at high pressure. A 400 bar really does not
@@ -3302,7 +3424,7 @@ you may stay in the water for a long time, but spend most of it at the surface. <div id="footnotes"><hr /></div>
<div id="footer">
<div id="footer-text">
-Last updated 2014-02-22 08:52:15 PST
+Last updated 2014-03-31 11:51:15 PDT
</div>
</div>
</body>
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 156f20342..0dc711638 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -3,8 +3,8 @@ // :author: Manual authors: Jacco van Koll, Dirk Hohndel, Reinout Hoornweg, // Linus Torvalds, Miika Turkia, Amit Chaudhuri, Jan Schubert, Willem // Ferguson, Salvador Cuñat -// :revnumber: 4.0.1 -// :revdate: December 2013 +// :revnumber: 4.1 +// :revdate: April 2014 :icons: :toc: :toc-placement: manual @@ -18,12 +18,27 @@ image::images/Subsurface4Banner.png["Banner",align="center"] *Manual authors*: Willem Ferguson, Jacco van Koll, Dirk Hohndel, Reinout Hoornweg, Linus Torvalds, Miika Turkia, Amit Chaudhuri, Jan Schubert, Salvador Cuñat -[blue]#_Version 4.0.1, January 2014_# +[blue]#_Version 4.1, April 2014_# Welcome as a user of _Subsurface_, an advanced dive logging programme with extensive infrastructure to describe, organise, interpret and print scuba -and free dives. +and free dives. _Subsurface_ offers many advantages above other similar +software solutions: + + - Do you use two different dive computer brands, each with its own proprietary + software for downloading dive logs? Do you dive with rebreathers as well + as open circuit? Do you use a Reefnet Sensus time-depth recorder + in conjunction with a dive computer? _Subsurface_ offers a standard + interface for downloading dive logs from all these different pieces of + equipment and to store and analyse the dive logs within a unified system. + - Do you use more than one operating system? _Subsurface_ is fully compatible + with Mac, Linux and Microsoft, allowing you to access your dive log on each + of your operating systems in a unified way. + - Do you use Linux or Mac and your dive computer has only Microsoft-based software + for downloading dive information (e.g. Mares)? _Subsurface_ provides a way of + storing and anaysing your dive logs on other operating systems. + _Subsurface_ binaries are available for Windows PCs (Win XP or later), Intel based Macs (OS/X) and many Linux distributions. _Subsurface_ can be built for many more hardware platforms and software environments where Qt and @@ -47,36 +62,37 @@ toc::[] Start Using the Program ----------------------- -The _Subsurface_ window is usually divided into four panels and has a *Main +The _Subsurface_ window is usually divided into four panels with a *Main Menu* (File Import Log View Filter Help) at the top of the window (for Windows and Linux) or the top of the screen (for Mac and Ubuntu Unity). The four panels are: 1) The *Dive List* to the bottom left containing a list of all the dives in the user's dive log. A dive can be selected and highlighted on the dive list by clicking on -it. In most situations the cursor up/down keys can be used to switch -between dives. +it. In most situations the up/down keys can be used to switch +between dives. The Dive List is an important tool for manipulating a dive log. 2) The *Dive Map* to the bottom right, showing the user's dive sites on a world map -and centred on the site of the last selected dive. +and centred on the site of the last dive selected in the *Dive List*. 3) The *Dive Info* to the top left, giving more detailed information on the -selected dive, including some statistics for the selected dive or for all +dive selected in the *Dive List*, including some statistics for the selected dive or for all highlighted dive(s). 4) The *Dive Profile* to the top right, showing a graphical dive profile of the -highlighted dive in the dive list. +selected dive in the *Dive List*. The dividers can be dragged between panels in order to change the size of any of the panels. _Subsurface_ remembers the position of the dividers, so the next time _Subsurface_ starts it uses the positions of the dividers when the program was executed previously. -If one dive is selected, the dive location, detailed information and profile of +If a dive is selected in the *Dive List*, the dive location, detailed information +and profile of the _selected dive_ are shown in the respective panels. On the other hand, if one highlights more than one dive the last highlighted dive is the _selected -dive_, but summary data of all _highlighted dives_ is shown in the *Stats* tab +dive_, but summary data of all _highlighted dives_ are shown in the *Stats* tab of the *Dive Info* panel (maximum, minimum and average depths, durations, water temperatures and SAC; total time and number of dives selected). @@ -85,7 +101,7 @@ temperatures and SAC; total time and number of dives selected). image::images/main_window.jpg["The Main Window",align="center"] The user can determine which of the four panels are displayed by selecting the -*View* option on the main menu. This option gives the user several choices of +*View* option on the main menu. This feature gives the user several choices of display: *All*: show all four of the panels as in the screenshot above. @@ -100,11 +116,11 @@ all highlighted dives. *Globe*: Show only the world map, centred on the last selected dive. Like many other functions that can be accessed via the Main Menu, these options -can be triggered using keyboard shortcuts as well. The shortcuts for a +can be triggered using keyboard shortcuts. The shortcuts for a particular system -are shown with an underline in the menu entries. Since different Operating +are shown with an underline in the main menu entries. Since different Operating Systems and the user chosen language may cause _Subsurface_ to use different -shortcut keys they will not be listed here in the user manual. +shortcut keys they are not listed here in the user manual. When the program is started for the first time, it shows no information at all. This is because the program doesn't have any dive information available. In the @@ -114,12 +130,12 @@ following sections, the procedures to create a new logbook will be described. Creating a new logbook ---------------------- Select _File -> New Logbook_ from the main menu. All existing dive data are -cleared so that new information can be added. If there is unsaved data in an -open logbook, _Subsurface_ the user will be asked if the open logbook will be +cleared so that new information can be added. If there are unsaved data in an +open logbook, the user is asked whether the open logbook should be saved before a new logbook is created. [[S_GetInformation]] -== How to obtain dive information to store in the user's logbook +== How to store dive information in the user's logbook There are several ways in which dive information can be added to a logbook: @@ -163,7 +179,7 @@ fields are visible: image::images/AddDive2.jpg["FIGURE: The Dive Notes tab",align="center"] -The *Starttime* field reflects the date and the time of the dive. By clicking +The *Start time* field reflects the date and the time of the dive. By clicking the down-arrow on the right of that field a calendar will be displayed from which one can choose the correct date. The time values (hour and minutes) can also be @@ -171,7 +187,7 @@ edited directly by clicking on each of them in the text box and by overtyping th information displayed. *Air and water temperatures*: the air and water temperatures during the -dive can be typed directly on the fields to the right of the Starttime. +dive can be typed directly on the fields to the right of the Start time. Temperature units are not needed, as they will be automatically supplied by _Subsurface_. Only the numerical value must be typed by the user (The units selected in the 'Preferences' @@ -188,7 +204,7 @@ here. These can come from three sources: a. One can find the coordinates on the world map in the bottom right hand part of the Subsurface window. The map displays a green bar indicating "No location -data - Move the map double-click to set the location". Upon a double-click +data - Move the map and double-click to set the dive location". Upon a double-click at the appropriate place, the green bar disappears and the coordinates are stored. @@ -209,9 +225,9 @@ Southern hemisphere latitudes are given with a *S*, e.g. S30°, or with a negative value, e.g. -30.22496. Similarly western longitudes are given with a *W*, e.g. W07°, or with a negative value, e.g. -7.34323. -Please note that GPS coordinates of a dive site a linked to the Location -name - so adding coordinates to dives that don't have a location set will -caused unexpected behavior (as Subsurface will think that all of these +Please note that GPS coordinates of a dive site are linked to the Location +name - so adding coordinates to dives that does not have a location description +will cause unexpected behavior (Subsurface will think that all of these dives have the same location and try to keep their GPS coordinates the same. @@ -222,16 +238,15 @@ the current logbook. *Buddy*: In this field one can enter the name(s) of the buddy / buddies (separated by commas) who accompanied the user on the dive. Auto completion -based on the list of buddies in the current logbook is offered. +is offered based on the list of buddies in the current logbook. -*Suit*: The type of diving suit that was used for the dive can be entered here. -Just as with the -other items, auto completion of the suit description is available. +*Suit*: The type of diving suit used for the dive can be entered here. +As with the other items, auto completion of the suit description is available. -*Rating*: In this field, users can provide a subjective overall rating of the +*Rating*: In this field, provide a subjective overall rating of the dive on a 5-point scale by clicking the appropriate star on the rating scale. -*Visibility*: As with the previous item, users can provide a rating of +*Visibility*: As with the previous item, provide a rating of visibility during the dive on a 5-point scale by clicking the appropriate star. *Tags*: Tags that describe the type of dive performed may @@ -273,25 +288,26 @@ dive. The dark dustbin icon on the left allows one to delete information for a particular cylinder. -The user starts by selecting a cylinder type on the left-hand side of the -table. To select a cylinder, the user must click in the *cylinder type* box. -This brings up a list button that can be used to display a dropdown list of +Start by selecting a cylinder type on the left-hand side of the +table. To select a cylinder, click in the *Type* box. +This brings up a button that can be used to display a dropdown list of cylinders: image::images/CylinderDataEntry2.jpg["FIGURE:The cylinder drop-down list button",align="center"] -This drop-down list can then be used to select the cylinder type that was used -for this dive. The +The drop-down list can be used to select the cylinder type used +for the dive or the user may start typing in the box which shows the +available options for the entered characters. The *Size* of the cylinder as well as its working pressure (*WorkPress*) will -automatically be shown in the dialogue. +automatically be shown in the dialogue. If a cylinder is not shown in the dropdown list, type the name and description of that cylinder into the *Type* field. -Next, the user must indicate the starting pressure and the ending pressure of +Next, indicate the starting pressure and the ending pressure of the gas used during the dive. The unit of pressure (metric/imperial) corresponds -to the setting in the preferences. +to the setting in the _Preferences_. -Finally, the user should type in the gas mixture used. If air -was used, a value of 21% can be entered on this field, or the field might be +Finally, type in the gas mixture used in the *O2%* field. If air +was used, a value of 21% can be entered on this field, or it might be left blank. If nitrox or trimix were used, their percentages of oxygen and/or helium must be specified. Any inappropriate fields should be left empty. After typing the information for @@ -304,7 +320,7 @@ dive made using two cylinders (air and EAN50): image::images/CylinderDataEntry3.jpg["FIGURE: a completed cylinder dive information table",align="center"] *Weights*: Information about the weight system used during a dive can be entered -using a dialogue very similar to that of the cylinder information. If the user +using a dialogue very similar to that for the cylinder information. If the user clicks the + button on the top right of the weights dialogue, the table looks like this: @@ -315,7 +331,8 @@ through a down-arrow: image::images/WeightsDataEntry2.jpg["FIGURE: Weights type drop-down list button",align="center"] -This drop-down list can then be used to select the type of weight system. In +The drop-down list can then be used to select the type of weight system or the user may start +typing in the box which shows the available options for the entered characters. In the *Weight* field, the weight used during the dive must be typed. After typing the information @@ -330,7 +347,7 @@ with two types of weights: integrated and a weight belt: image::images/WeightsDataEntry3.jpg["FIGURE: A completed weights information table",align="center"] There's NO need to click the _Save_ button before the dive -profile has beeb completed. +profile has been completed. ==== Creating a Dive Profile @@ -343,20 +360,21 @@ dive being described: image::images/DiveProfile1.jpg["FIGURE: Initial dive profile",align="center"] _Modifying the dive profile_: When the cursor is moved around the dive profile, -its position is indicated by two red lines as shown below. The depth and time +its position is indicated by two colored lines (red and green) as shown below. +The depth and time that the cursor represents are indicated, respectively on the left hand and bottom axes. The units (metric/imperial) on the axes are determined by the -*preference* settings. The dive profile itself comprises several +*Preference* settings. The dive profile itself comprises several line segments demarcated by waypoints (white dots on the profile, as shown above). The default dive depth is 15 m. If the dive depth was 21 m then the user needs to drag the appropriate waypoints downwards to represent 21 m. To add a waypoint, double-click on -any line segment. -To remove a waypoint, right-click on it and choose "Remove this point" from the -context menu. The user will also need to drag the waypoints to represent an +any line segment. To move an additional waypoint, drag it. +To remove this waypoint, right-click on it and choose "Remove this point" from the +context menu. The user needs to drag the waypoints to represent an accurate time duration for the dive. Below is a dive profile that represents a dive -to 21 m for 31 min, followed by a 3 minute safety stop at 5 m. +to 21 m for 31 min, followed by a 5 minute safety stop at 5 m. image::images/DiveProfile2.jpg["FIGURE: Edited dive profile",align="center"] @@ -364,18 +382,18 @@ _Specifying the gas composition:_ The gas composition used is clearly indicated along the line segments of the dive profile. This defaults to the first gas mixture specified in the *Equipment* tab, which was air in the case of the profile illustrated above. The gas mixtures of segments of the dive profile can -be edited. This is done by clicking on the particular +be edited. This is done by right-clicking on the particular waypoint and selecting the appropriate gas from the context menu. Changing the gas for a waypoint affects the gas shown in the segment _to the left_ of that waypoint. Note that only the gases defined in the *Equipment* tab appear in the -context menu: +context menu. image::images/DiveProfile3.jpg["FIGURE: Gas composition context menu",align="center"] -Below is the profile of a dive to 21 m for 31 min with a switch from air to -EAN40 on the ascent. In this case the first cylinder in the *Equipment* tab -contained air and the second cylinder contained EAN40. +Below is the profile of a dive to 21 m for 31 min for which an extra waypoint was added at 18 m on the ascent and with a switch from air to +EAN50 at 18 m. In this case the first cylinder in the *Equipment* tab +contained air and the second cylinder contained EAN50. image::images/DiveProfile4.jpg["FIGURE: Completed dive profile",align="center"] @@ -388,7 +406,7 @@ on the top right hand of the Dive Notes tab. If the _Save_ button is clicked, the dive data are saved in the current logbook. If the _Cancel_ button is clicked, the newly entered -dive data are discarded. When exiting _Subsurface_ the user will be prompted +dive data are discarded. When exiting _Subsurface_, the user will be prompted once more to save the logbook with the new dive(s). @@ -414,9 +432,9 @@ PC-Communication mode. **This could drain the dive computer's battery**. We therefore recommend that the user checks if the dive computer is charged when connected to the USB port of a PC. For example, several Suunto and Mares dive -computers do not recharge through the USB connection. The users should refer to +computers do not recharge through the USB connection. Users should refer to the dive computer's manual -if they are unsure whether the dive computer will recharge or drain its batteries +if they are unsure whether the dive computer recharges its batteries while connected to the USB port. To import dive information from a dive computer to a computer with @@ -454,11 +472,11 @@ _Subsurface_ only imports dives that have not been uploaded before. This makes the download process faster on most dive computers and also saves battery power of the dive computer (at least for those not charging while connected via USB). If, for some reason, the user wishes to import ALL dives from the dive computer, -even though some may already be in the logbook, then the the check box labeled -_Force download of all dives_ can be ticked. +even though some may already be in the logbook, then check the the check box labeled +_Force download of all dives_. - The dialogue has two drop-down lists, *Vendor* and *Dive Computer*. On the -*vendor* drop-down list the user must select the make of the computer, e.g. +*vendor* drop-down list select the make of the computer, e.g. Suunto, Oceanic, Uwatec, Mares. On the *Dive Computer* drop-down list, the model name of the dive computer must be selected, e.g. D4 (Suunto), Veo200 (Oceanic), or Puck @@ -505,10 +523,10 @@ progress bar at the bottom of the dialogue (for some dive computers the progress information could be inaccurate as we cannot determine how much downloadable data there is until all data have been downloaded). When the download of the dive information is complete, all the imported dives appear -in the Dive List, sorted by date and time. Disconnect and +in the *Dive List*, sorted by date and time. Disconnect and switch off the dive computer to conserve its battery power. -If a particular dive is selected, the Dive Profile panel shows an informative +If a particular dive is selected, the *Dive Profile* panel shows an informative graph of dive depth against time for that particular dive. @@ -530,7 +548,7 @@ software? Has it worked before, or is this the first time the cable is being use 4) Consult *Appendix A* and make sure that the correct Mount Point was specified (see above). 5) On Unix-like operating systems, does the user have write permission to the -USB port? If not, users should consult *Appendix A*. +USB port? If not, consult *Appendix A*. If the _Subsurface_ computer does not recognise the USB adaptor by showing an appropriate device name next to the Mount Point, then there is a @@ -543,20 +561,22 @@ two check boxes checked in the download dialogue discussed above: Save libdivecomputer logfile Save libdivecomputer dumpfile -*Important*: These check boxes are only used when problems are ancountered +*Important*: These check boxes are only used when problems are encountered during the download process: under normal circumstances they should not be checked. When checking these boxes, the user is prompted to select a folder to save the information to. The default folder is the one in which the _Subsurface_ -dive log is kept. *Important:* After downloading with the above checkboxes -checked, no dives are shown in the -*Dive List* but two files are created in the folder selected above: +dive log is kept. + +*Important:* _After downloading with the above checkboxes +checked, no dives are added to the +*Dive List* but two files are created in the folder selected above_: subsurface.log subsurface.bin These files should be send to the _Subsurface_ mail list: _subsurface@hohndel.org_ with a -request for the files to be analysed. One should provide the dive computer +request for the files to be analysed. Provide the dive computer make and model as well as contextual information about the dives recorded on the dive computer. **** @@ -566,14 +586,14 @@ as well as contextual information about the dives recorded on the dive computer. ==== Updating the dive information imported from the dive computer. The information from the dive computer is not complete and more -details must be added in order to have a more complete record of the dives. To +details must be added in order to have a more full record of the dives. To do this, the *Dive Notes* and the *Equipment* tabs on the top left hand of the _Subsurface_ window should be used. ==== Dive Notes -The date and time of the dive, gas mixture and perhaps water temperature is +The date and time of the dive, gas mixture and (often) water temperature is shown as obtained from the dive computer, but the user needs to add some additional information by hand in order to have a more complete dive record. The message in a blue box at @@ -583,7 +603,7 @@ visible: image::images/AddDive3.jpg["FIGURE: The Dive Notes tab",align="center"] -The *Starttime* field reflects the date and the time of the dive, as supplied by +The *Start time* field reflects the date and the time of the dive, as supplied by the dive computer. It should therefore not be necessary to edit this, but one could make changes by clicking the down-arrow on the right of that field to display a calendar from which the correct date can be chosen. The hour and @@ -591,20 +611,16 @@ minutes values can also be edited by clicking on each of them in the text box and by overtyping the information displayed. *Air/water temperatures*: Air and water temperatures during the dive are shown -in these fields to the right of the Starttime. Many dive computers supply water +in these fields to the right of the Start time. Many dive computers supply water temperature information and this field may therefore not require further editing. If editing is required, only a value is required. The units of temperature will be automatically supplied by -_Subsurface_ (according to the 'Preferences', metric or imperial units will +_Subsurface_ (according to the _Preferences_, metric or imperial units will be used). *Location*: In this field one should type in text that describes the site -where the dive was performed, e.g. "Tihany, Lake Balaton, Hungary". the required drivers to interact with the download cable and connected dive -computer. - -If all the above points are in order and there is a failure to download the -dive +where the dive was performed, e.g. "Tihany, Lake Balaton, Hungary". Auto completion of location names will make this easier when a user frequently dives at the same sites. @@ -613,8 +629,8 @@ here. These can come from three sources: a. The user can find the coordinates on the world map in the bottom right hand part -of the Subsurface window. The map displays a green bar indicating "No location -data - move the map and double-click to set the location". Double-click +of the Subsurface window. The map displays a green bar indicating "Move the map +and double-click to set the dive location". Double-click at the appropriate place, the green bar disappears and the coordinates are stored. @@ -632,12 +648,12 @@ four formats with latitude followed by longitude: Decimal degrees, e.g. 30.22496 , 30.821798 Southern hemisphere latitudes are given with a *S*, e.g. S30°, or with a -negative value, e.g. -30.22496. Similarly western longitudes are given with a +negative value, e.g. -30.22496. Similarly, western longitudes are given with a *W*, e.g. W07°, or with a negative value, e.g. -7.34323. *Divemaster*: The name of the dive master or dive guide for this dive should be -entered in this field. -This field offers auto completion based on the list of dive masters in +entered in this field +which offers auto completion based on the list of dive masters in the current logbook. *Buddy*: In this field, one enters the name(s) of the buddy / buddies @@ -704,16 +720,17 @@ cylinders: image::images/CylinderDataEntry2.jpg["FIGURE: The cylinder drop-down list button",align="center"] The drop-down list can then be used to select the cylinder type that was used -for this dive. The +for this dive or the user may start typing in the box which shows the available +options for the entered characters. The *Size* of the cylinder as well as its working pressure (*WorkPress*) will automatically be shown in the dialogue. Next one must indicate the starting pressure and the ending pressure of the specified gas during the dive. The unit of pressure (metric/imperial) -corresponds to the settings chosen in the preferences. +corresponds to the settings chosen in the _Preferences_. -Finally, the user must type in the gas mixture used. If air was used, the -value of 21% can be entered here or this field can be left blank. If nitrox or +Finally, the user must provide the gas mixture used. If air was used, the +value of 21% can be entered or this field can be left blank. If nitrox or trimix were used, their percentages of oxygen and/or helium should be entered. Any inappropriate fields should be left empty. After typing the information for the cylinder, @@ -739,7 +756,9 @@ down-arrow: image::images/WeightsDataEntry2.jpg["FIGURE:Weights type drop-down list button",align="center"] The drop-down list can then be used to select the type of weight system -used during the dive. In the *Weight* +used during the dive or the user may start typing in the box +which shows the available options for the entered characters. +In the *Weight* field, type in the amount of weight used during the dive. After specifying the weight system, the user can either press _ENTER_ on the keyboard or click outside the @@ -748,14 +767,14 @@ It is possible to enter information for more than one weight system by adding an additional system using the + button on the top right hand. Weight systems can be deleted using the dustbin icon on the left hand. Here is an example of information for a dive -with two types of weights: integrated and a weight belt: +with two types of weights: integrated as well as a weight belt: image::images/WeightsDataEntry3.jpg["FIGURE: A completed weights information table",align="center"] ==== Saving the updated dive information The information entered in the *Dive Notes* tab and the *Equipment* tab can be -saved with all the other information of the dives in the user's logbook by +saved by using the two buttons on the top right hand of the *Dive Notes* tab. If the _Save_ button is clicked, @@ -769,13 +788,13 @@ that the new data should be saved. [[S_ImportingAlienDiveLogs]] -If a user has been diving for some time, it's possible that several dives were -logged using other dive log software. All this information needs not -be typed by hand into _Subsurface_, because these divelogs can probably be +If a user has been diving for some time, it is possible that several dives were +logged using other dive log software. This information does not need retyping +because these divelogs can probably be imported onto _Subsurface_. _Subsurface_ will import divelogs from a range of other dive log software. While some software is supported natively, for others the -user will have to +user has to export the logbook(s) to an intermediate format so that they can then be imported by _Subsurface_. Currently, _Subsurface_ supports importing CSV log files from several sources. @@ -784,7 +803,7 @@ preconfigured, but because the import is flexible, users can configure their own imports. Manually kept log files (e.g. in spreadsheet) can also be imported by configuring the CSV import. -_Subsurface_ can also import UDDF and UDCF files that are used by some divelog +_Subsurface_ can also import UDDF and UDCF files used by some divelog software and some divecomputers, like the Heinrichs & Weikamp DR5. Finally, for some divelog software like Mares Dive Organiser it is currently suggested to import the logbooks first into a webservice like _divelogs.de_ and then import @@ -811,18 +830,15 @@ Towards the bottom right is a dropdown selector with a default label of _Dive Log Files_ which gives access to the different types of direct imports available, as in dialogue *B*, above. Currently these are: - - XML-formatted dive logs - - UDDF-formatted dive logs + - XML-formatted dive logs (Divinglog 5.0, MacDive and several other dive log systems) + - UDDF-formatted dive logs (e.g. Kenozoooid) - UDCF-formatted dive logs - JDiveLog - Suunto Dive Manager (DM3 and DM4) - - MacDive (XML) - - DivingLog 5.0 (XML) - - Kenozooid (UDDF) - CSV (text-based and spreadsheet-based) dive logs. Selecting the appropriate file in the file list of the dialogue opens -the imported dive log in the _Subsurface_ *Dive List*. A few other formats, not +the imported dive log in the _Subsurface_ *Dive List*. Some other formats, not accessible through the Import dialogue are also supported, as explained below. ==== Importing from Mares Dive Organiser V2.1 @@ -831,13 +847,12 @@ Since Mares utilise proprietory Microsoft software not compatible with multi-platform applications, these dive logs cannot be directly imported into _Subsurface_. Mares dive logs need to be imported using a three-step process, -using _www.divelogs.de_ as a mechanism to extract the dive information from -the file. +using _www.divelogs.de_ as a mechanism to extract the dive log information. - The dive log data from Mares Dive Organiser need to be exported to the user's desktop, using - a _.sdf_ file name extension. Refer to xref:Mares_Export[Appendix -C] for more information. + a _.sdf_ file name extension. Refer to xref:Mares_Export[Appendix C] for more +information. - Data should then be imported into _www.divelogs.de_. One needs to create a user account in _www.divelogs.de_, log into that web site, then @@ -858,7 +873,7 @@ brings up a dialogue box (see figure on left [*A*] below). Enter a user-ID and password for _divelogs.de_ into the appropriate fields and then select the _Download_ button. Download from _divelogs.de_ starts immediately, -displaying a progress bar in the dialogue box. At the and of the download, the +displaying a progress bar in the dialogue box. At the end of the download, the success status is indicated (see figure on the right [*B*], below). The _Apply_ button should then be selected, after which the imported dives appear in the _Subsurface_ *Dive List* panel. @@ -871,8 +886,8 @@ image::images/Divelogs1.jpg["FIGURE:Download from Divelogs.de",align="center"] Sometimes dive computers export dive information as files with _comma-separated values_ (.CSV). For example, the APD Inspiration and Evolution -closed circuit rebreather (CCR) systems export dive information in CSV format -that normally contains information about a single dive only. These +closed circuit rebreather (CCR) systems export dive information in a CSV +formatted file that normally contains information for a single dive only. These files can easily be imported into _Subsurface_. CSV files are normally organised into a single line that provides the headers of the data columns, followed by the @@ -906,11 +921,11 @@ data? This field separator should be either a comma (,) or a TAB character. This can be determined by opening the file with a text editor. If it is comma-delimited, then the comma characters between the values are clearly visible. If no commas are evident and -the numbers are aligned, +the numbers are aligned in columns, the file is probably TAB-delimited (i.e. it uses a TAB as a field separator, as in the above example). -b. Which data columns need to be imported into _Subsurface_?. The Dive Time and +b. Which data columns need to be imported into _Subsurface_? The Dive Time and Depth columns are always required. Open the file using a text editor and note the titles of the columns to be imported and their column positions. For instance for the above example: @@ -923,7 +938,7 @@ instance for the above example: Armed with this information, importing the data into _Subsurface_ is straightforward. Select _Import->Import Log Files_ from the main menu. In the resulting file -selection menu, select oa CSV files, after which a common configuration dialog +selection menu, select _CSV files_, after which a common configuration dialog appears for all the files with a CSV extension: @@ -933,7 +948,7 @@ image::images/Import_CSV1.jpg["FIGURE: CSV download dialogue",align="center"] There are pre-configured definitions for some dive computers, e.g. the APD rebreathers. If the user's dive computer is on this list, it should be selected using the dropdown -box labeled _Pre-configured imports_. Finally the _OK_ should be clicked and +box labeled _Pre-configured imports_. Finally _OK_ should be clicked and the dive will be imported and listed in the *Dive List* tab of _Subsurface_. If the dive computer is not on the pre-configured list, the user must @@ -945,7 +960,7 @@ variables. For each data column used for import, the user must check the appropriate check box and indicate in which column these data are found. For instance, the image above corresponds to the dialogue that would apply to the CSV data set described above -the image. Having performed the column specification, select the _OK_ button +the image. After completing the column specification, select the _OK_ button and the dive will be imported and listed in the *Dive List* tab of _Subsurface_. @@ -972,28 +987,29 @@ the data are in clear columns, the file is probably TAB-delimited (i.e. it uses a TAB as a field separator, as in the above example). -A recommended field separator for the export is tab, as commas might be used in +A recommended field separator for the export is tab, as commas might be part of the -field values themselves. +field values themselves. Therefore the use of an appropriate field separator +in very important. -b. Which columns need to be imported into _Subsurface_?. We do not +b. Which columns need to be imported into _Subsurface_? We do not currently have any mandatory input fields, but some, e.g. dive duration are crucial for the log file to make any sense. Possible options can be seen in the image below and one should include all the -fields available in both your log file and in the Subsurface +fields available in both your log file and in the _Subsurface_ import. c. Units used for depth, weight and temperature. We consider depth to be either feet or meters, weight kilograms or pounds and temperature either -Celsius or Fahrenheit. However, the users can only select _Metric_ or -_Imperial_ in the Preferences tab of _Subsurface_.. No mixture of unit s -ystems is allowed for the different fields. +Celsius or Fahrenheit. However, the users can select _Metric_ or +_Imperial_ in the _Preferences_ tab of _Subsurface_. No mixture of unit +systems is allowed for the different fields. Importing manually kept CSV log files is quite straight forward, but there might be many fields and counting the field numbers is error prone. Therefore validation of the data to be imported is critical. -To import the dives, one must select _Import->Import Log Files_ from the menu +To import the dives, select _Import->Import Log Files_ from the menu bar. If the CSV option in the dropdown list is selected and the file list includes file names ending with .CSV, one can select the _Manual dives_ tab that will bring up the following configuration dialog: @@ -1006,15 +1022,19 @@ appear in the *Dive List* area of _Subsurface_. [[S_Companion]] -=== Importing GPS coordinates with the Subsurface Companion app for mobile -phones +=== Importing GPS coordinates with the *Subsurface Companion App* for mobile phones + If the user has an Android device with GPS, the coordinates for the diving -location can be obtained and automatically passed to Subsurface -divelog. To do this, one needs to: +location can be obtained and automatically passed to the _Subsurface_ +divelog. This takes place when the Companion App stores the dive locations on +a dedicated Internet-based file server. _Subsurface_, in turn, can collect +the localities from the file server. + +To do this, one needs to: - Register on the http://api.hohndel.org/login/[Subsurface companion web page]. A confirmation mail with instructions and a personal *DIVERID* will be send together with -a long number that gives access to the companion app capabilities. +a long number that gives access to the file server and Companion App capabilities. - Download the app from https://play.google.com/store/apps/details?id=org.subsurface[Google Play Store] @@ -1037,16 +1057,16 @@ this DIVERID and does not ask for it again unless one uses the _Disconnect_ menu option (see below). Now one is ready to get a dive position and send it to the server. The Android -display will look like the left hand image (A) below, but without any dive. +display will look like the left hand image (*A*) below, but without any dive. -Touching the "+" icon on the top right to add a new dive site. Users will be -prompted for a place name (or asked to activate the GPS if it was turned off). +Touch the "+" icon on the top right to add a new dive site. Users will be +prompted for a place name (or asked to activate the GPS if it is turned off). The main screen shows a list of dive locations, each with a name, date and time. Some dives may have an arrow-up icon on the selection box to the left (see image B in the middle, below) indicating that they require upload to the server. There are several ways to send dives to the server; the easiest is by simply -selecting the dive. See middle image below (B): +selecting the dive. See middle image below (*B*): image::images/Companion.jpg["FIGURE: Screen shots (A-C) of companion app",align="center"] @@ -1060,11 +1080,11 @@ it deletes the dive location(s). The new dive points are now stored on the server and can be downloaded to the _Subsurface_ dive log whenever users upload or add dives to _Subsurface_. After a dive trip using the Companion app, all dive locations are ready to be -saved on a Subsurface dive log (see below). +saved on a _Subsurface_ dive log (see below). ===== Settings on the Companion app -Selecting the _Settings_ menu option results in the right hand image above (C). +Selecting the _Settings_ menu option results in the right hand image above (*C*). ===== Server and account @@ -1137,7 +1157,7 @@ of another registered diver. This option sends all locations stored in the Android device to the server. -==== Downloading dive locations to a Subsurface divelog +==== Downloading dive locations to the _Subsurface_ divelog Download dive(s) from a dive computer or enter them manually into _Subsurface_ before obtaining the GPS coordinates from the server. The download @@ -1156,7 +1176,7 @@ date-times of the uploaded GPS localities. [icon="images/icons/info.jpg"] [NOTE] -Features, issues and tips._ Since _Subsurface_ matches GPS locations from the +_Features, issues and tips._ Since _Subsurface_ matches GPS locations from the Android device and dive information from the dive computer based on date-time data, automatic assignment of GPS data to dives is dependent on agreement of date and time between these two devices. If there is a large difference between @@ -1201,7 +1221,10 @@ water temperature and surface air consumption (SAC). image::images/Profile2.png["Typical dive profile",align="center"] Of all the panels in _Subsurface_, the Dive Profile contains the most detailed -information about each dive. The main item in the Dive profile is the graph of +information about each dive. The Dive Profile has a *button bar* on the left hand side +that allows control over several display options. The functions of these +buttons are described below. The main item in the Dive +Profile is the graph of dive depth as a function of time. In addition to the obvious information of the depth it also shows the ascent and descent rates compared to the recommended speed of going up or down in the water column. This information is given using @@ -1219,6 +1242,34 @@ The profile also includes depth readings for the peaks and troughs in the graph. Thus, users should see the depth of the deepest point and other peaks. Mean depth is marked with a horizontal red line. +[icon="images/icons/scale.jpg"] +[NOTE] +In some cases the dive profile does not fill the whole area of the *Dive Profile* +panel. Clicking the *Scale* button in the toolbar on the left of the dive profile +frequently increases the size of the dive profile to fill the area of the panel efficiently. + +*Water temperature* is displayed with its own blue line with temperature values +placed adjacent to significant changes. + +The dive profile can include graphs of the *partial pressures* +of O2, N2, and He during the dive (see figure above) as well as a calculated and dive computer +reported deco ceilings (only visible for deep, long, or repetitive dives). Partial pressures of oxygen are indicated in green, those of nitrogen in black, and those of helium in dark red. These +partial pressure graphs are shown below the profile data. + +[icon="images/icons/O2.jpg"] +[NOTE] +Clicking this button allows display of the partial pressure of *oxygen* during the +dive. This is depicted below the dive depth and water temperature graphs. + +[icon="images/icons/N2.jpg"] +[NOTE] +Clicking this button allows display of the partial pressure of *nitrogen* during the dive. + +[icon="images/icons/He.jpg"] +[NOTE] +Clicking this button allows display of the partial pressure of *helium* during the dive. +This is only of importance to divers using Trimix, Helitrox or similar breathing gasses. + The *air consumption* graph displays the tank pressure and its change during the dive. The air consumption takes depth into account so that even when manually entering the start and end pressures the graph is not a straight line. @@ -1234,9 +1285,6 @@ when the diver was using less gas than average. The colour coding is obviously only possible when a tank sensor is connected and tank pressure readings during the dive are available. -*Water temperature* is displayed with its own blue line with temperature values -placed adjacent to significant changes. - It is possible to *zoom* into the profile graph. This is done either by using the scroll wheel / scroll gesture of your mouse or trackpad. By default _Subsurface_ always shows a profile area large enough for at least 30 minutes @@ -1247,53 +1295,72 @@ that free divers clearly won’t care about. image::images/MeasuringBar.png["FIGURE: Measuring Bar",align="center"] -Measurements of *depth differences* can be achieved by using the button with -two vertical bars at the right of the dive profile panel (users should refer to -the above figure, -bottom right). The measurement is done by dragging the red dots to the two points +[icon="images/icons/ruler.jpg"] +[NOTE] +Measurements of *depth or time differences* can be achieved by using the +*ruler button* on the left of the dive profile panel. +The measurement is done by dragging the red dots to the two points on the dive profile that the user wishes to measure. Information is then given -along the line connecting the +in the horizontal white area underneath the two red dots. -The dive profile can include further information that is typically more -interesting for tec divers. Enabling these is described in the section entitled -_Setting up Preferences_. Basically, users can include graphs of the *partial -pressures* -of O2, N2, and He during the dive as well as a calculated and dive computer -reported deco ceilings (only visible for deep, long, or repetitive dives). The -partial pressure graphs are added below the profile data, the calculated ceiling -is shown as a green overlay on top of the dive profile. Above the profile the -currently used gradient factors are shown (e.g. GF 35/75). Please note that -these are NOT the gradient factors in use by the dive computer in question, -but the gradient factors used by _Subsurface_ to calculate deco obligations -during the dive. - -The graph can also include the dive computer reported *ceiling* (or more -precisely, the first deco stop that the dive computer recorded – users are reminded that not +The profile can also include the dive computer reported *ceiling* (more +precisely, the deepest deco stop that the dive computer calculated for each particular moment in time) as a red overlay on the dive profile. Ascent ceilings arise when a direct ascent to the surface increases +the risk of a diver suffering from decompression sickness (DCS) and it is necessary to either ascend +slower or to perform decompression stop(s) before ascending to the surface. Not all dive computers record this information and make it available for download; for example all of the Suunto dive computers fail to make this very useful data -available to divelog software). Because of the differences in used algorithms -and amount of data available (and factors taken into consideration) at the time -of the calculation it is unlikely that both of these are the same; this can be -true even if the same algorithm and _gradient factors_ (see below) are used. +available to divelog software. _Subsurface_ also calculates ceilings independently, +shown as a green overlay on the dive profile. +Because of the differences in algorithms used +and amount of data available (and other factors taken into consideration at the time +of the calculation) it is unlikely that ceilings from dive computers and from _Subsurface_ are the same, even if the same algorithm and _gradient factors_ (see below) are used. It is also quite common that _Subsurface_ calculates a ceiling for non-decompression dives when the dive computer stayed in non-deco mode during the whole dive (represented by the [green]#dark green# section in the profile at the beginning of this section). This is caused by the fact that _Subsurface’s_ -calculations describe the deco obligation at that point in time during a dive, +calculations describe the deco obligation at each moment during a dive, while dive computers usually take the upcoming ascent into account. During the -ascent some excess Nitrogen (and possibly Helium) are already breathed off so +ascent some excess nitrogen (and possibly helium) are already breathed off so even though the diver technically encountered a ceiling at depth, the dive still does not require an explicit deco stop. This feature allows dive computers to offer -longer non-stop bottom time. +longer non-stop bottom times. + +[icon="images/icons/cceiling.jpg"] +[NOTE] +If the dive computer itself calculates a ceiling and makes it available to +_Subsurface_ during upload of dives, this can be +shown as a red area by checking *Dive computer reported ceiling* button on the Profile Panel. -For a more detailed explanation of _gradient factors_, use the following links: +[icon="images/icons/ceiling1.jpg"] +[NOTE] +If the *Calculated ceiling* button on the Profile Panel is clicked, then a ceiling, calculated by _Subsurface_, is shown in green if it exists for +a particular dive (*A* in figure below). This setting can be modified in two ways: + +[icon="images/icons/ceiling2.jpg"] +[NOTE] +If, in addition, the *show all tissues* button on the Profile Panel is clicked, the ceiling is shown for the tissue +compartments following the BĂĽhlmann model (*B* in figure below). + +[icon="images/icons/ceiling3.jpg"] +[NOTE] +If, in addition, the *3m increments* button on the Profile Panel is clicked, then the ceiling is indicated in 3 m increments +(*C* in figure below). + +image::images/Ceilings2.jpg["Figure: Ceiling with 3m resolution",align="center"] -- link:http://www.tek-dive.com/portal/upload/M-Values.pdf[Understanding M-values, by Erik Baker, 1988, _Immersed_ Vol. 3, No. 3.] +Gradient Factor settings strongly affect the calculated ceilings and their depths. +For more information about Gradient factors, see the section on xref:S_GradientFactors[Gradient Factor Preference settings]. The +currently used gradient factors (e.g. GF 35/75) are shown above the depth profile if the appropriate toolbar buttons are activated. +*N.B.:* The indicated gradient factors are NOT the gradient factors in use by the dive computer, +but those used by _Subsurface_ to calculate deco obligations +during the dive. For more information external to this manual see: -- link:http://www.rebreatherworld.com/general-and-new-to-rebreather-articles/5037-gradient-factors-for-dummies.html[Gradient factors for dummies, by Kevin Watts] + ** http://www.tek-dive.com/portal/upload/M-Values.pdf[Understanding M-values by Erik Baker, _Immersed_ Vol. 3, No. 3.] + + ** link:http://www.rebreatherworld.com/general-and-new-to-rebreather-articles/5037-gradient-factors-for-dummies.html[Gradient factors for dummies, by Kevin Watts] === The Dive Profile context menu @@ -1316,16 +1383,16 @@ The Information box displays a large range of information pertaining to the dive profile. Normally the Information Box is located to the top left of the *Dive Profile* panel. If the mouse points outside of the *Dive Profile* panel, then only the top line of the Information Box is visible (see left-hand part of -figure (A) below). The Information Box can be moved around in the *Dive Profile* +figure (*A*) below). The Information Box can be moved around in the *Dive Profile* panel by click-dragging it with the mouse so that it is not obstructing important detail. The position of the Information Box is saved and used again during subsequent dive analyses. image::images/InfoBox2.jpg["Figure: Information Box",align="center"] -The moment the mouse points inside the *Dive Profile* panel, it expands and +The moment the mouse points inside the *Dive Profile* panel, the information box expands and shows many data items. In this situation, the data reflect the time point along -the dive profile indicated by the mouse (see right-hand part of figure (B) above +the dive profile indicated by the mouse cursor (see right-hand part of figure (*B*) above where the Information Box reflects the situation at the position of the cursor [arrow] in that image). Therefore, moving the cursor in the horizontal direction allows the Information Box to show information for any point along the @@ -1334,11 +1401,59 @@ In this mode, the Information Box gives extensive statistics about depth, gas and ceiling characteristics of the particular dive. These include: Time period into the dive (indicated by a @), depth, cylinder pressure (P), temperature, ascent/descent rate, surface air consumption (SAC), oxygen partial pressure, -maximum operating depth, effective air depth (EAD), effective narcotic depth +maximum operating depth, equivalent air depth (EAD), equivalent narcotic depth (END), equivalent air density depth (EADD), decompression requirements at that instant in time (Deco), time to surface (TTS), the calculated ceiling, as well as the calculated ceiling for several BĂĽhlmann tissue compartments. +The user has control over the display of several statstics, represented as four +buttons on the left of the profile panel. These are: + +[icon="images/icons/MOD.jpg"] +[NOTE] +Clicking this button causes the Information Box to display the *Maximum Operating Depth +(MOD)* of the dive, given the +gas mixture used. MOD is dependent on the oxygen concentration in the breathing gas. +For air (21% oxygen) it is around 57 m. Below the MOD there is a markedly increased +risk of exposure to the dangers associated with oxygen toxicity. + +[icon="images/icons/NDL.jpg"] +[NOTE] +Clicking this button causes the Information Box to display the *No-deco Limit (NDL)* or the +*Total Time to Surface (TTS)*. NDL is the time duration that a diver can continue with a +dive, given the present depth, that does not require decompression (that is, before an +ascent ceiling appears). Once one has exceeded the NDL and decompression is required (that +is, there is an ascent ceiling above the diver, then TTS gives the number of minutes +required before the diver can surface. TTS includes ascent time as well as decompression +time. + +[icon="images/icons/SAC.jpg"] +[NOTE] +Clicking this button causes the Information Box to display the *Surface Air Consumption (SAC)*. +SAC is an indication of the surface-normalised respiration rate of a diver. The value of SAC +is less than the real +respiration rate because a diver at 10m uses breathing gas at a rate roughly double that of +the equivalent rate at the surface. SAC gives an indication of breathing gas consumption rate +independent of the depth of the dive so that the respiratory rates of different dives +can be compared. The units for SAC is litres/min or cub ft/min. + +[icon="images/icons/EAD.jpg"] +[NOTE] +Clicking this button displays the *Equivalent Air Depth (EAD)* for +nitrox dives as well as the *Equivalent +Narcotic Depth (END)* for trimix dives. These are numbers of +importance to divers who use breathing gases other than air. Their +values are dependent on the composition of the breathing gas. The EAD +is the depth of a hypothetical air dive that has the same partial +pressure of nitrogen as the current depth of the nitrox dive at +hand. A nitrox dive leads to the same decompression obligation as an +air dive to the depth equalling the EAD. The END is the depth of a +hypothetical air dive that has the same sum of partial pressures of +the narcotic gases nitrogen and oxygen as the current trimix dive. A +trimix diver can expect the same narcotic effect as a diver breathing +air diving at a depth equalling the END. + +Figure (*B*) above shows an information box with a nearly complete set of data. == Organising the logbook (Manipulating groups of dives) @@ -1359,8 +1474,8 @@ Dives are normally numbered incrementally from non-recent dives (low sequence numbers) to recent dives (having the highest sequence numbers). The numbering of the dives is not always consistent. For instance, when non-recent dives are added to the dive list the numbering does not automatically follow on because -of the dives that are more recent in date/time than the newly-added dive that -has an older date/time. Therefore, one may sometimes need to renumber the dives. +of the dives that are more recent in date/time than the newly-added dive with +an older date/time. Therefore, one may sometimes need to renumber the dives. This is performed by selecting (from the Main Menu) _Log -> Renumber_. Users are given a choice with respect to the lowest sequence number to be used. Completing this operation results in new sequence numbers (based on date/time) @@ -1379,27 +1494,30 @@ list comprising five dive trips (*B*, on the right): image::images/Group2.jpg["Figure: Grouping dives",align="center"] Grouping into trips allows a rapid way of accessing individual dives without -having to scan long lists of dives. In order to group the dives in a dive list, -(from the Main Menu) users must select _Log -> Autogroup_. The *Dive List* panel now shows -only the titles for the trips. - -==== Viewing the dives during a particular trip -Once when the dives have been grouped into trips, users can expand one or more -trips by clicking the arrow-head on the left of each trip title. This expands -the selected trip, revealing the individual dives performed during the trip. +having to scan a long lists of dives. In order to group the dives in a dive list, +(from the Main Menu) users must select _Log -> Autogroup_. The *Dive List* panel +now shows only the titles for the trips. ==== Editing the title and associated information for a particular trip Normally, in the dive list, minimal information is included in the trip title. More information about a trip can be added by selecting its trip title from -the dive list. This shows a *Trip Notes* tab in the *Dive Notes* panel. Here +the *Dive list*. This shows a *Trip Notes* tab in the *Dive Notes* panel. Here users can add or edit information about the date/time, the trip location and any other general comments about the trip as a whole (e.g. the dive company that was -dived with, the general weather during the trip, etc.). After entering this -information, users should select *Save* from the buttons at the top right of the *Trip Notes* +dived with, the general weather and surface conditions during the trip, etc.). +After entering this +information, users should select *Save* from the buttons at the top right +of the *Trip Notes* tab. The trip title in the *Dive List* panel should now reflect some of the edited information. +==== Viewing the dives during a particular trip + +Once when the dives have been grouped into trips, users can expand one or more +trips by clicking the arrow-head on the left of each trip title. This expands +the selected trip, revealing the individual dives performed during the trip. + ==== Collapsing or expanding dive information for different trips If a user right-clicks after selecting a particular trip in the dive list, the @@ -1420,7 +1538,8 @@ top 3 dives; trip 2: bottom 2 dives) by selecting and right-clicking the top three dives. The resulting context menu allows the user to create a new trip by choosing the option *Create new trip above*. The top three dives are then grouped -into a separate trip. The figures bellow show the selection and context menu on the left (A) and +into a separate trip. The figures bellow shows the selection and context menu +on the left (A) and the completed action on the right (B): image::images/SplitDive3a.jpg["FIGURE: Split a trip into 2 trips",align="center"] @@ -1438,18 +1557,17 @@ calibration dives of the dive computer or dives of extremely short duration. ==== Unlink a dive from a trip Users can unlink dives from the trip to which they belong. In order to do this, -users should select and right-click -the relevant dives to bring up the context menu. Then the option *Remove dive(s) -from trip* should be selected. -The dives that have been unlinked now appear immediately above the trip to +select and right-click +the relevant dives to bring up the context menu. Then select the option *Remove dive(s) +from trip*. The dive(s) now appear immediately above the trip to which they belonged. ==== Add a dive to the trip immediately above Selected dives can be moved from the trip to which they belong and placed within -the trip immediately above the currently active trip. To do this, users must select +the trip immediately above the currently active trip. To do this, select and right-click -the dives to bring up the context menu, and then select *Add dive(s) to trip +the dive(s) to bring up the context menu, and then select *Add dive(s) to trip immediately above*. ==== Shift the start time of dive(s) @@ -1484,20 +1602,22 @@ A dive log can be saved in two formats: * _Subsurface_ XML format. This is the native format used by _Subsurface_. -* Universal Dive Data format (UDDF). User should refer to http://uddf.org for more information. +* Universal Dive Data format (UDDF). Refer to http://uddf.org for more information. UDDF is a generic format that enables communication among many dive computers and computer programs. -In order to save the WHOLE dive log (i.e. all trips and dives), *File* should be selected -from the Main menu. To save in _Subsurface_ XML format, users should select _File -> Save -as_. To save in UDDF format, the _File -> Export UDDF_ option should be selected. +In order to save the WHOLE dive log (i.e. all trips and dives), select *File* +from the Main menu. To save in _Subsurface_ XML format, select _File -> Save as_. +To save in UDDF format, select _File -> Export UDDF_. -In order to save only one or more dives or one or two trips, users can select the +In order to save only one or more dives or one or two trips, select the appropriate dives or trips in the *Dive List* panel and then right-click the selected dives to bring up the context menu. To save in _Subsurface_ XML -format, users should select _Save as_ from the context menu. To save in UDDF format, users should select +format, select _Save as_ from the context menu. To save in UDDF format, select _Export as UDDF_ from the context menu. +Export to other formats can be achieved through third party facilities, for +instance _www.divelogs.de_. [[S_PrintDivelog]] == Printing a dive log @@ -1558,7 +1678,7 @@ image::images/Printpreview.jpg["FIGURE: Print preview page",align="center"] [[S_Preferences]] -== Setting user *Preferences* for _Subsurface_ +== Setting user _Preferences_ for _Subsurface_ There are several settings within _Subsurface_ that the users can specify. These are found when selecting _File->Preferences_. The settings are performed in @@ -1600,6 +1720,7 @@ independently, with some units in the metric system and other in the imperial. === Graph image::images/Preferences3.jpg["FIGURE: Preferences Graph page",align="center"] +[[S_GradientFactors]] This panel allows two type of selections: * *Show*: Here users can specify the amount of information shown as part of @@ -1610,27 +1731,6 @@ panel. If any of the graphs go above this threshold level, the graph is highlighted in red, indication that the threshold value has been exceeded (see figure below). -** Ceiling: Ascent ceilings arise when a direct ascent to the surface increases -the risk of a diver suffering from decompression sickness (DCS) and it is necessary to either ascend -slower or to perform decompression stop(s) before ascending to the surface. -_Subsurface_ can indicate these ceilings above which the diver should not ascend -at a particular point in time (see the green-shaded areas in the figure in the -section on <<S_DiveProfile,Dive Profiles>>) and in the figure below: - -*** If the *Calculated ceiling* option is checked, then a ceiling is shown if it exists for -a particular dive (*A* in figure below) - -*** If the *show all tissues* option is checked, the ceiling is shown for the tissue -compartments following the BĂĽhlmann model (*B* in figure below). - -*** If the *3m increments* option is checked, then the ceiling is indicated in 3 m increments -(*C* in figure below). - -*** If the dive computer itself calculates a ceiling and makes it available to -_Subsurface_ during upload of dives, this can be -shown by checking *Dive computer reported ceiling* and it can be drawn in red by -checking *Draw ceiling red*. - ** If a _Maximum operating depth (MOD)_ or an _Equivalent air depth (EAD)_ applies to a dive profile, these can be shown by checking the appropriate boxes. @@ -1639,9 +1739,8 @@ this can be specified in the appropriate text box. In addition, if a _no-decompression limit (NDL)_ is to be shown in the *info* box, then users must check the appropriate box. -image::images/Ceilings.png["Figure: Ceiling with 3m resolution",align="center"] -* *Misc*: Here users can set the _gradient factors_ used while diving. GF_Low is +* *Misc*: *Gradient Factors:* Here users can set the _gradient factors_ used while diving. GF_Low is the gradient factor at deep and GF_High is used just below the surface. At intermediate depths gradient factors between GF_Low and GF_High are used. Gradient factors add conservatism to the nitrogen exposure during a dive, in a @@ -1733,17 +1832,20 @@ The operating system of the desktop computer needs the appropriate drivers in order to communicate with the dive computer in whichever way the dive computer prefers (e.g. bluetooth, USB, infrared). - * On Linux this means users need to have the correct kernel - module loaded. Most distributions will do this automatically. + * On Linux users need to have the correct kernel + module loaded. Most distributions will do this automatically, so the + user does not need to load drivers. * On Windows, the OS should offer to download the correct - driver once the user connects the dive computer to the USB port. + driver once the user connects the dive computer to the USB port and + operating system sees the equipment for the first time. * On a Mac users sometimes 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 + devices or any other dive computer using a USB-to-serial interface + based on the Silicon Labs CP2101 or similar chip can be found as + _Mac_OSX_VCP_Driver.zip_ at the +http://www.silabs.com/support/pages/document-library.aspx?p=Interface&f=USB%20Bridges&pn=CP2101[Silicon Labs document and software repository]. [[S_HowFindDeviceName]] === How to Find the Device Name for USB devices and set its write permission @@ -1767,7 +1869,7 @@ The drop down box should find all connected dive computers. .On Linux: -Try the following: +There is a definitive way to find the port: - Disconnect the USB cable from the dive computer - Open a terminal @@ -1775,7 +1877,7 @@ Try the following: - Plug in the USB cable of the dive computer - Type the command: 'dmesg' and press enter -Within the terminal, users should see a message similar to this one: +A message similar to this one should appear: usb 2-1.1: new full speed USB device number 14 using ehci_hcd usbcore: registered new interface driver usbserial @@ -1793,12 +1895,12 @@ Within the terminal, users should see a message similar to this one: usbcore: registered new interface driver ftdi_sio ftdi_sio: v1.6.0:USB FTDI Serial Converters Driver -Users can see that in the third line from the bottom, the USB adapter is -detected and is connected to +ttyUSB3+. This information can now be used in -the import settings as +/dev/ttyUSB3+. This directs Subsurface to the correct +The third line from the bottom shows that the FTDI USB adapter is +detected and connected to +ttyUSB3+. This information can now be used in +the import settings as +/dev/ttyUSB3+ which directs Subsurface to the correct USB port. -Ensuring you have write permission to the USB serial port: +Ensuring that the user has write permission to the USB serial port: On Unix-like operating systems the USB ports can only be accessed by users who are members @@ -1806,8 +1908,8 @@ of the +dialout+ group. If one is not root, one may not be a member of that group and will not be able to use the USB port. Let us assume one's username is 'johnB'. - - As root, type: +usermod -a -G dialout johnB+ (Ubuntu users: +sudo -a -G -dialout johnB+) + - As root, type: +usermod -a -G dialout johnB+ (Ubuntu users: +sudo usermod +-a -G dialout johnB+) This makes johnB a member of the +dialout+ group. - Type: +id johnB+ This lists all the groups that johnB belongs to and verifies that @@ -1827,116 +1929,114 @@ dives. For dive computers communicating through bluetooth like the Heinrichs Weikamp Frog or the Shearwater Predator and Petrel there is a different procedure to get the devices name to communicate with -_Subsurface_. In general it consists of these steps: +_Subsurface_. Follow these steps: + + * *For the dive computer, after enabling Bluetooth, ensure it is in Upload mode.* - * enable bluetooth on your computer - * pairing the device +For Bluetooth pairing of the dive computer, refer to the +manufacturer's user guide. When using a Shearwater Predator/Petrel, select +_Dive Log -> Upload Log_ and wait for the _Wait PC_ message. -Do not forget to set your divecomputer in Bluetooth or upload mode before -Paring and Downloading logs. If you use a Shearwater Predator/Petrel just select -_Dive Log -> Upload Log_ and wait until you see the _Wait PC_ message. + * *Pair the _Subsurface_ computer with the dive computer.* .On Windows: -Bluetooth is most likely already enabled. For pairing the device choose -Control Panel->Bluetooth Devices->Add Wireless Device +Bluetooth is most likely already enabled. For pairing with the dive computer choose +_Control Panel->Bluetooth Devices->Add Wireless Device_. This should bring up a dialog showing your dive computer (in Bluetooth mode) and -allowing to pair it. For bluetooth pairing of your dive computer refer to the -manufacturer's user guide. The dive computer should then show up in the list of -Bluetooth devices and you may then right click on it and choose Properties->COM -Ports to identify the port used for your dive computer. If there are several +allowing pairing. Right click on it and choose _Properties->COM +Ports_ to identify the port used for your dive computer. If there are several ports listed, use the one saying "Outgoing" instead of "Incoming". -For downloading to _Subsurface_, the drop down list should contain this COM -port already. If not, enter it manually. +For downloading to _Subsurface_, the _Subsurface_ drop-down list should contain +this COM port already. If not, enter it manually. -Note: If you have issues downloading from your dive computer in other software -afterwards try to remove the pairing with your dive computer. +Note: If there are issues afterwards downloading from the dive computer using +other software, remove the existing pairing with the dive computer. .On MacOS: -Click on the Bluetooth symbol in the menu bar and select 'Set up -Bluetooth Device...'. Make sure that your dive computer is in upload -mode; it should then show up in the list of devices. Select it and go +Click on the Bluetooth symbol in the menu bar and select _Set up +Bluetooth Device..._. The dive computer should then show up in the list of devices. Select it and go through the pairing process. This step should only be needed once for initial setup. -Once the pairing is completed the correct device will be shown in the -'Device or Mount Point' drop down in the _Subsurface_ *Import* dialog. +Once the pairing is completed the correct device is shown in the +'Device or Mount Point' drop-down in the _Subsurface_ *Import* dialog. .On Linux -Ensure bluetooth is enabled on the _Subsurface_ computer. -On most common distributions this should be true out of the box. If not then -depending on your system, running +initd+ or +systemd+. This might be different -and also involve loading modules specific to your hardware. In case your system -is -running +systemd+, manually run +sudo systemctl start bluetooth.service+ to -enable -it, in case of +initd+, run something like +sudo rc.config start bluetoothd+ or -+sudo /etc/init.d/bluetooth start+. - -Pairing should be straight forward. Using Gnome3 for instance will show a -bluetooth icon in the upper right corner of the desktop where one selects 'Set +Ensure Bluetooth is enabled on the _Subsurface_ computer. +On most common distributions this should be true out of the box and +pairing should be straight forward. For instance, Gnome3 shows a +Bluetooth icon in the upper right corner of the desktop where one selects 'Set up New Device'. This should show a dialog where one can select the -dive computer (in bluetooth mode) and pair it. For issues with PIN -setting try manually setting '0000'. +dive computer (which already should be in Bluetooth mode) and pair it. +If a PIN is required, try manually setting '0000'. + +In the rare cases where the above is not true, then +depending on your system, try +initd+ or +systemd+. This might be different +and also involve loading modules specific to your hardware. In case your system +is running +systemd+, manually run +systemctl start bluetooth.service+ to +enable it, in case of +initd+, run something like +rc.config start bluetoothd+ or ++/etc/init.d/bluetooth start+. One may also use a manual approach by using such commands: - * +sudo hciconfig+ - shows the bluetooth devices available on your + * +hciconfig+ shows the Bluetooth devices available on your computer (not dive computer), most likely one will see a hci0, if not -try 'sudo hcitool -a' to see inactive devices and try to run 'sudo -hciconfig hci0 up' to bring them up +try +hcitool -a+ to see inactive devices and run +sudo +hciconfig hci0 up+ to bring them up. - * +sudo hcitool scanning+- use this to get a list of bluetooth enabled + * +hcitool scanning+ gets a list of bluetooth enabled client devices, look for the dive computer and remember the MAC -address shown there +address are shown there - * +sudo bluez-simple-agent hci0 10:00:E8:C4:BE:C4+ - this will pair + * +bluez-simple-agent hci0 10:00:E8:C4:BE:C4+ pairs the dive computer with the bluetooth stack of the _Subsurface_ computer, copy/paste the MAC address from the output of 'hcitool scanning' Unfortunately on Linux binding to a communication device has to be done manually by running: - * +sudo rfcomm bind /dev/rfcomm0 10:00:E8:C4:BE:C4+ - bind the dive + * +rfcomm bind /dev/rfcomm0 10:00:E8:C4:BE:C4+ binds the dive computer to a communication device in the desktop computer, in case rfcomm is -already taken just use rfcomm1 or up, please copy/paste the MAC address -from the output of 'hcitool scanning', the MAC shown in here will not +already taken use rfcomm1 or up. IMPORTANT: Copy/paste the MAC address +from the output of +hcitool scanning+, the MAC address shown above will not work. -For downloading dives in Subsurface one then has to specify +/dev/rfcomm0+ -as device name to use. - +For downloading dives in _Subsurface_ specify the device name connected to the MAC +address in the last step above, e.g. _/dev/rfcomm0_. -== APPENDIX B: Dive Computer specific information for importing dive -information. +== APPENDIX B: Dive Computer specific information for importing dive information. [[S_ImportUemis]] === Import from a Uemis Zurich [icon="images/icons/iumis.jpg"] [NOTE] -Things are very similar to a normal USB-connected dive computer when -downloading dives from a Uemis Zurich -dive computer (one of the ones that recharge when -connected to the USB port). The main difference is that one does not enter a +_Subsurface_ downloads the information +stored on the SDA (the built-in file system of the Uemis) including +information about dive spots and +equipment. Buddy information is not yet downloadable. +Things are very similar to a normal USB-connected dive computer +(the Uemis is one of those that recharge when connected to the USB port). +The main difference is that one does not enter a device name, but instead the location where the UEMISSDA file system is -mounted once you connect the dive computer. On Windows this is a drive letter ( +mounted once connected to the dive computer. On Windows this is a drive letter ( often 'E:' or 'F:'), on a Mac this is '/Volumes/UEMISSDA' and on Linux systems this differs depending on the distribution. On Fedora it usually is '/var/run/media/<your_username>/UEMISSDA'. In all cases _Subsurface_ should suggest the correct location in the drop down list. -Once onehas selected this as device name one can download the +After selecting the above device name, download the dives from the Uemis Zurich. One technical issue with the Uemis Zurich -download implementation (this is a firmware limitation, not a +download implementation (this is a Uemis firmware limitation, not a _Subsurface_ issue) is that one cannot download more than about 40-50 dives without running out of memory on the SDA. This will usually only -happen the very first time one downloads dives from the Uemis Zurich - -normally when downloading at the end of a day or even after a dive +happen the very first time one downloads dives from the Uemis Zurich. +Normally when downloading at the end of a day or even after a dive trip, the capacity is sufficient. If _Subsurface_ displays an error that the dive computer ran out of space the solution is straight forward. Disconnect the SDA, turn it off and on again, and reconnect @@ -1945,10 +2045,6 @@ download will continue where it stopped previously. One may have to do this more than once, depending on how many dives are stored on the dive computer. -At this point _Subsurface_ downloads most of the information -stored on the SDA, including information about dive spots and -equipment. Buddy information is not yet downloadable. - [[S_ImportingDR5]] === Importing dives from Heinrichs Weikamp DR5 @@ -1960,25 +2056,24 @@ for every dive. Mark all the dives you'd like to import or open. Note: The DR5 does not seem to store gradient factors nor deco information, so for _Subsurface_ it is not possible to display them. Adjust the gradient -factors in the Tec Settings in _Subsurface_ to generate a deco overlay in the _ -Subsurface_ *Dive Profile* panel -to get deco displayed but please note that the deco calculated by _Subsurface_ -will most likely differ from the one displayed on the DR5. +factors in the _Tec Settings_ in _Subsurface_ to generate a deco overlay in the +_Subsurface_ *Dive Profile* panel but please note that the deco calculated by +_Subsurface_ will most likely differ from the one displayed on the DR5. -=== Import from Shearwater Predator using bluetooth +=== Import from Shearwater Predator using Bluetooth [icon="images/icons/predator.jpg"] [NOTE] -Using a Shearwater Predator you may be able to pair Bluetooth but then encounter +Using a Shearwater Predator one may be able to pair Bluetooth but then encounter issues when downloading, showing errors like _Slip RX: unexp. SLIP END_ on the Predator. -This might also be seen, when using other dive log software and operating -systems than Linux. We have no detailed idea about the source and how to fix +This might also arise when using other dive log software and operating +systems other than Linux. We have no detailed idea about the source and how to fix this, but it is reported to be solved sometimes by one of these steps: - * use the bluetooth dongle which came with the Shearwater Predator instead of - the built-in one of your computer - * switch to different bluetooth drivers for your hardware + * use the Bluetooth dongle which came with the Shearwater Predator instead of + the built-in one of the _Subsurface_ computer + * switch to different Bluetooth drivers for the same hardware * switch off WiFi while using Bluetooth @@ -2020,9 +2115,9 @@ dives. * 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' + - With the dives marked, use the program menu _File -> Export_ - The export pop-up will show - - Within this pop-up, there is one field called Export Path. + - Within this pop-up, there is one field called 'Export Path'. * Click the browse button next to the field Export Path ** A file-manager like window pops up ** Navigate to the directory or storing the @@ -2034,9 +2129,9 @@ Divelog.SDE file *Divemanager 4 (DM4):* -To import divelog from 'Suunto DM4', one needs to locate the DM4 database +To export divelog from 'Suunto DM4', one needs to locate the DM4 database where the dives are stored. the user can either look for the original -database or take a backup of the dives. Both methods are described here. +database or make a backup of the dives. Both methods are described here. Locating the Suunto DM4 database: @@ -2063,7 +2158,7 @@ Backing up Suunto DM4: [icon="images/icons/mareslogo.jpg"] [NOTE] Mares Dive Organiser is a Microsoft application. The dive log is kept as a -Microsoft SQL Compact Edition data base with a .SDF filename extension. The +Microsoft SQL Compact Edition data base with a '.sdf' filename extension. The data base includes all Dive Organiser-registered divers on the particular computer and all Mares dive computers used. The safest way to obtain a copy of the dive data base is to export the information to another compatible format @@ -2087,8 +2182,8 @@ Unfortunately DivingLog XML files give us no indication on the preferences set on one's system. So in order for _Subsurface_ to be able to successfully import XML files from DivingLog one first needs to ensure that DivingLog is configured -to use the Metric system (one can easily change this in 'File -> -Preferences -> Units and Language' by clicking the 'Metric' +to use the Metric system (one can easily change this within Diving Log by +selecting 'File -> Preferences -> Units and Language' by clicking the 'Metric' button). Then do the following: - In Divinglog open the 'File -> Export -> XML' menu @@ -2100,7 +2195,10 @@ button). Then do the following: === Subsurface appears to miscalculate gas consumption and SAC [[SAC_CALCULATION]] -Not really. What happens is that subsurface actually calculates gas +'Question': I dived with a 12.2 l tank, starting with 220 bar and ending with 100 bar, and I calculate a different SAC compared what _Subsurface_ calculates. Is _Subsurface_ +miscalculating? + +'Answer': Not really. What happens is that _Subsurface_ actually calculates gas consumption differently - and better - than you expect. In particular, it takes the incompressibility of the gas into account. Traditionally, Gas consumption and SAC should be: @@ -2126,17 +2224,7 @@ which is about 1445, not 1464. So there was 19 l too much in your simple calculation that ignored the difference between 1 bar and one ATM. The compressibility does show up above 200 bar, and takes that 1445 down about eight liters more, so you really did use only about 1437 l of air at surface pressure. -The math details can be seen in dive.c: - -+surface_volume_multiplier().+ - -The "if (bar > 200) bar = .." part is the compressibility - it's an approximation, -but it's a reasonably good one, and closer to reality than not doing it. -You can get the numbers you expect if you remove that, and turn the function into just: - -+return pressure.mbar / 1000.0;+ -but that would actually be wrong. So be happy: your SAC really is better than your calculations indicated. Or be sad: your cylinder contains less air than you thought it did. And as mentioned, the "contains less air than you thought it did" really diff --git a/android.cpp b/android.cpp new file mode 100644 index 000000000..15de9e65d --- /dev/null +++ b/android.cpp @@ -0,0 +1,81 @@ +/* implements Android specific functions */ +#include "dive.h" +#include "display.h" +#include <string.h> +#include <sys/types.h> +#include <dirent.h> +#include <fcntl.h> + +#include <QtAndroidExtras/QtAndroidExtras> +#include <QtAndroidExtras/QAndroidJniObject> + +extern "C" { + +const char system_divelist_default_font[] = "Roboto"; +const int system_divelist_default_font_size = 8; + +const char *system_default_filename(void) +{ + /* Replace this when QtCore/QStandardPaths getExternalStorageDirectory landed */ + QAndroidJniObject externalStorage = QAndroidJniObject::callStaticObjectMethod("android/os/Environment", "getExternalStorageDirectory", "()Ljava/io/File;"); + QAndroidJniObject externalStorageAbsolute = externalStorage.callObjectMethod( "getAbsolutePath", "()Ljava/lang/String;" ); + QString system_default_filename = externalStorageAbsolute.toString()+"/subsurface.xml"; + QAndroidJniEnvironment env; + if (env->ExceptionCheck()) { + // FIXME: Handle exception here. + env->ExceptionClear(); + return strdup("/sdcard/subsurface.xml"); + } + return strdup(system_default_filename.toUtf8().data()); +} + +int enumerate_devices (device_callback_t callback, void *userdata) +{ + /* FIXME: we need to enumerate in some other way on android */ + /* qtserialport maybee? */ + return -1; +} + +/* NOP wrappers to comform with windows.c */ +int subsurface_rename(const char *path, const char *newpath) +{ + return rename(path, newpath); +} + +int subsurface_open(const char *path, int oflags, mode_t mode) +{ + return open(path, oflags, mode); +} + +FILE *subsurface_fopen(const char *path, const char *mode) +{ + return fopen(path, mode); +} + +void *subsurface_opendir(const char *path) +{ + return (void *)opendir(path); +} + +struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp) +{ + return zip_open(path, flags, errorp); +} + +int subsurface_zip_close(struct zip *zip) +{ + return zip_close(zip); +} + +/* win32 console */ +void subsurface_console_init(bool dedicated) +{ + /* NOP */ +} + +void subsurface_console_exit(void) +{ + /* NOP */ +} + +} @@ -21,6 +21,7 @@ struct plot_info { int minpressure, maxpressure; int minhr, maxhr; int mintemp, maxtemp; + enum {AIR, NITROX, TRIMIX} dive_type; double endtempcoord; double maxpp; bool has_ndl; @@ -32,7 +33,7 @@ typedef enum { SC_PRINT } scale_mode_t; -extern struct divecomputer *select_dc(struct divecomputer *main); +extern struct divecomputer *select_dc(struct dive *); struct options { enum { @@ -46,7 +47,7 @@ struct options { int profile_height, notes_height, tanks_height; }; -extern char dc_number; +extern unsigned int dc_number; extern unsigned int amount_selected; @@ -2176,3 +2176,15 @@ void shift_times(const timestamp_t amount) dive->when += amount; } } + +timestamp_t get_times() +{ + int i; + struct dive *dive; + + for_each_dive(i, dive) { + if (dive->selected) + break; + } + return dive->when; +} @@ -600,13 +600,30 @@ static inline struct dive *get_dive(int nr) return dive_table.dives[nr]; } +static inline unsigned int number_of_computers(struct dive *dive) +{ + unsigned int total_number = 0; + struct divecomputer *dc = &dive->dc; + + if (!dive) + return 1; + + do { + total_number++; + dc = dc->next; + } while (dc); + return total_number; +} + static inline struct divecomputer *get_dive_dc(struct dive *dive, int nr) { - struct divecomputer *dc = NULL; - if (nr >= 0) - dc = &dive->dc; - while (nr-- > 0) + struct divecomputer *dc = &dive->dc; + + while (nr-- > 0) { dc = dc->next; + if (!dc) + return &dive->dc; + } return dc; } @@ -661,6 +678,7 @@ extern "C" { #endif extern int report_error(const char *fmt, ...); +extern const char *get_error_string(void); extern struct dive *find_dive_including(timestamp_t when); extern bool dive_within_time_range(struct dive *dive, timestamp_t when, timestamp_t offset); @@ -670,26 +688,31 @@ struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset); extern int match_one_dc(struct divecomputer *a, struct divecomputer *b); extern void parse_xml_init(void); -extern void parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, const char **params, char **error); +extern void parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, const char **params); extern void parse_xml_exit(void); extern void set_filename(const char *filename, bool force); -extern int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table, char **error); -extern int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table, char **error); +extern int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table); +extern int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *buf, int size, struct dive_table *table); -extern void parse_file(const char *filename, char **error); -extern void parse_csv_file(const char *filename, int time, int depth, int temp, int po2f, int cnsf, int stopdepthf, int sepidx, const char *csvtemplate, int units, char **error); -extern void parse_manual_file(const char *filename, int separator_index, int units, int number, int date, int time, int duration, int location, int gps, int maxdepth, int meandepth, int buddy, int notes, int weight, int tags, char **error); +extern int parse_file(const char *filename); +extern int parse_csv_file(const char *filename, int time, int depth, int temp, int po2f, int cnsf, int stopdepthf, int sepidx, const char *csvtemplate, int units); +extern int parse_manual_file(const char *filename, int separator_index, int units, int number, int date, int time, int duration, int location, int gps, int maxdepth, int meandepth, int buddy, int notes, int weight, int tags); -extern void save_dives(const char *filename); -extern void save_dives_logic(const char *filename, bool select_only); -extern void save_dive(FILE *f, struct dive *dive); -extern void export_dives_uddf(const char *filename, const bool selected); +extern int save_dives(const char *filename); +extern int save_dives_logic(const char *filename, bool select_only); +extern int save_dive(FILE *f, struct dive *dive); +extern int export_dives_uddf(const char *filename, const bool selected); +struct git_oid; struct git_repository; +#define dummy_git_repository ((git_repository *) 3ul) /* Random bogus pointer, not NULL */ extern struct git_repository *is_git_repository(const char *filename, const char **branchp); extern int git_save_dives(struct git_repository *, const char *, bool select_only); extern int git_load_dives(struct git_repository *, const char *); +extern const char *saved_git_id; +extern void clear_git_id(void); +extern void set_git_id(const struct git_oid *); extern int subsurface_rename(const char *path, const char *newpath); extern int subsurface_open(const char *path, int oflags, mode_t mode); @@ -697,8 +720,11 @@ extern FILE *subsurface_fopen(const char *path, const char *mode); extern void *subsurface_opendir(const char *path); extern struct zip *subsurface_zip_open_readonly(const char *path, int flags, int *errorp); extern int subsurface_zip_close(struct zip *zip); +extern void subsurface_console_init(bool dedicated); +extern void subsurface_console_exit(void); extern void shift_times(const timestamp_t amount); +extern timestamp_t get_times(); extern xsltStylesheetPtr get_stylesheet(const char *name); @@ -815,7 +841,7 @@ struct divedatapoint *plan_add_segment(struct diveplan *diveplan, int duration, void get_gas_string(int o2, int he, char *buf, int len); struct divedatapoint *create_dp(int time_incr, int depth, int o2, int he, int po2); void dump_plan(struct diveplan *diveplan); -void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco, const char **error_string_p); +void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco); void delete_single_dive(int idx); struct event *get_next_event(struct event *event, char *name); diff --git a/dives/test32.xml b/dives/test32.xml new file mode 100644 index 000000000..d3839f9aa --- /dev/null +++ b/dives/test32.xml @@ -0,0 +1,37 @@ +<divelog program='subsurface' version='2'> +<settings> +<divecomputerid model='Reefnet Sensus Ultra' deviceid='fbad0400' nickname='Sensus'/> +</settings> +<dives> +<dive number='1' date='2014-03-15' time='21:44:06' duration='52:20 min'> + <location>At Home</location> + <notes>Simple test dive to show the pp graph (in this case O2 only) being drawn incorrectly due to multiple gas switches to a hyperoxic gas (thus triggering the pO2 threshold setting). + +Fixed in commit aa0cd792bbe3e5c2dbaaaaff77688f4ee96c694c</notes> + <cylinder size='24.0 l' workpressure='232.0 bar' description='D12 232 bar' o2='32.0%' /> + <cylinder size='5.547 l' workpressure='206.843 bar' description='AL40' o2='100.0%' /> + <divecomputer model='manually added dive'> + <depth max='15.0 m' mean='9.908 m' /> + <event time='12:00 min' type='25' value='100' name='gaschange' /> + <event time='19:00 min' type='25' value='32' name='gaschange' /> + <event time='38:00 min' type='25' value='100' name='gaschange' /> + <sample time='0:00 min' depth='0.0 m' /> + <sample time='1:00 min' depth='15.0 m' /> + <sample time='4:00 min' depth='15.0 m' /> + <sample time='9:00 min' depth='7.0 m' /> + <sample time='12:00 min' depth='7.0 m' /> + <sample time='19:00 min' depth='15.0 m' /> + <sample time='24:00 min' depth='15.0 m' /> + <sample time='29:00 min' depth='15.0 m' /> + <sample time='34:00 min' depth='7.0 m' /> + <sample time='38:00 min' depth='5.0 m' /> + <sample time='41:00 min' depth='7.0 m' /> + <sample time='44:00 min' depth='9.0 m' /> + <sample time='46:00 min' depth='5.0 m' /> + <sample time='48:00 min' depth='4.0 m' /> + <sample time='51:00 min' depth='6.0 m' /> + <sample time='52:20 min' depth='0.0 m' /> + </divecomputer> +</dive> +</dives> +</divelog> @@ -62,7 +62,7 @@ out: } -static void zip_read(struct zip_file *file, char **error, const char *filename) +static void zip_read(struct zip_file *file, const char *filename) { int size = 1024, n, read = 0; char *mem = malloc(size); @@ -73,11 +73,11 @@ static void zip_read(struct zip_file *file, char **error, const char *filename) mem = realloc(mem, size); } mem[read] = 0; - parse_xml_buffer(filename, mem, read, &dive_table, NULL, error); + parse_xml_buffer(filename, mem, read, &dive_table, NULL); free(mem); } -static int try_to_open_zip(const char *filename, struct memblock *mem, char **error) +static int try_to_open_zip(const char *filename, struct memblock *mem) { int success = 0; /* Grr. libzip needs to re-open the file, it can't take a buffer */ @@ -89,7 +89,7 @@ static int try_to_open_zip(const char *filename, struct memblock *mem, char **er struct zip_file *file = zip_fopen_index(zip, index, 0); if (!file) break; - zip_read(file, error, filename); + zip_read(file, filename); zip_fclose(file); success++; } @@ -98,19 +98,12 @@ static int try_to_open_zip(const char *filename, struct memblock *mem, char **er return success; } -static int try_to_xslt_open_csv(const char *filename, struct memblock *mem, char **error, const char *tag) +static int try_to_xslt_open_csv(const char *filename, struct memblock *mem, const char *tag) { char *buf; - if (readfile(filename, mem) < 0) { - if (error) { - int len = strlen(translate("gettextFromC", "Failed to read '%s'")) + strlen(filename); - *error = malloc(len); - snprintf(*error, len, translate("gettextFromC", "Failed to read '%s'"), filename); - } - - return 1; - } + if (readfile(filename, mem) < 0) + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); /* Surround the CSV file content with XML tags to enable XSLT * parsing @@ -132,8 +125,7 @@ static int try_to_xslt_open_csv(const char *filename, struct memblock *mem, char free(starttag); free(endtag); free(buf); - *error = strdup("Memory allocation failed in __func__\n"); - return 1; + return report_error("Memory allocation failed in %s", __func__); } sprintf(starttag, "<%s>", tag); @@ -148,10 +140,8 @@ static int try_to_xslt_open_csv(const char *filename, struct memblock *mem, char free(starttag); free(endtag); } else { - /* we can atleast try to strdup a error... */ - *error = strdup("realloc failed in __func__\n"); free(mem->buffer); - return 1; + return report_error("realloc failed in %s", __func__); } return 0; @@ -163,7 +153,7 @@ int db_test_func(void *param, int columns, char **data, char **column) } -static int try_to_open_db(const char *filename, struct memblock *mem, char **error) +static int try_to_open_db(const char *filename, struct memblock *mem) { sqlite3 *handle; char dm4_test[] = "select count(*) from sqlite_master where type='table' and name='Dive' and sql like '%ProfileBlob%'"; @@ -180,7 +170,7 @@ static int try_to_open_db(const char *filename, struct memblock *mem, char **err /* Testing if DB schema resembles Suunto DM4 database format */ retval = sqlite3_exec(handle, dm4_test, &db_test_func, 0, NULL); if (!retval) { - retval = parse_dm4_buffer(handle, filename, mem->buffer, mem->size, &dive_table, error); + retval = parse_dm4_buffer(handle, filename, mem->buffer, mem->size, &dive_table); sqlite3_close(handle); return retval; } @@ -188,7 +178,7 @@ static int try_to_open_db(const char *filename, struct memblock *mem, char **err /* Testing if DB schema resembles Shearwater database format */ retval = sqlite3_exec(handle, shearwater_test, &db_test_func, 0, NULL); if (!retval) { - retval = parse_shearwater_buffer(handle, filename, mem->buffer, mem->size, &dive_table, error); + retval = parse_shearwater_buffer(handle, filename, mem->buffer, mem->size, &dive_table); sqlite3_close(handle); return retval; } @@ -321,11 +311,11 @@ static int try_to_open_csv(const char *filename, struct memblock *mem, enum csv_ return 1; } -static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, char **error) +static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem) { /* Suunto Dive Manager files: SDE, ZIP; divelogs.de files: DLD */ if (!strcasecmp(fmt, "SDE") || !strcasecmp(fmt, "ZIP") || !strcasecmp(fmt, "DLD")) - return try_to_open_zip(filename, mem, error); + return try_to_open_zip(filename, mem); /* CSV files */ if (!strcasecmp(fmt, "CSV")) @@ -334,7 +324,7 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo #if ONCE_COCHRAN_IS_SUPPORTED /* Truly nasty intentionally obfuscated Cochran Anal software */ if (!strcasecmp(fmt, "CAN")) - return try_to_open_cochran(filename, mem, error); + return try_to_open_cochran(filename, mem); #endif /* Cochran export comma-separated-value files */ @@ -348,19 +338,19 @@ static int open_by_filename(const char *filename, const char *fmt, struct memblo return 0; } -static void parse_file_buffer(const char *filename, struct memblock *mem, char **error) +static void parse_file_buffer(const char *filename, struct memblock *mem) { char *fmt = strrchr(filename, '.'); - if (fmt && open_by_filename(filename, fmt + 1, mem, error)) + if (fmt && open_by_filename(filename, fmt + 1, mem)) return; if (!mem->size || !mem->buffer) return; - parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, NULL, error); + parse_xml_buffer(filename, mem->buffer, mem->size, &dive_table, NULL); } -void parse_file(const char *filename, char **error) +int parse_file(const char *filename) { struct git_repository *git; const char *branch; @@ -369,37 +359,32 @@ void parse_file(const char *filename, char **error) git = is_git_repository(filename, &branch); if (git && !git_load_dives(git, branch)) - return; + return 0; if (readfile(filename, &mem) < 0) { /* we don't want to display an error if this was the default file */ if (prefs.default_filename && !strcmp(filename, prefs.default_filename)) - return; - - if (error) { - int len = strlen(translate("gettextFromC", "Failed to read '%s'")) + strlen(filename); - *error = malloc(len); - snprintf(*error, len, translate("gettextFromC", "Failed to read '%s'"), filename); - } + return 0; - return; + return report_error(translate("gettextFromC", "Failed to read '%s'"), filename); } fmt = strrchr(filename, '.'); if (fmt && (!strcasecmp(fmt + 1, "DB") || !strcasecmp(fmt + 1, "BAK"))) { - if (!try_to_open_db(filename, &mem, error)) { + if (!try_to_open_db(filename, &mem)) { free(mem.buffer); - return; + return 0; } } - parse_file_buffer(filename, &mem, error); + parse_file_buffer(filename, &mem); free(mem.buffer); + return 0; } #define MAXCOLDIGITS 3 #define MAXCOLS 100 -void parse_csv_file(const char *filename, int timef, int depthf, int tempf, int po2f, int cnsf, int stopdepthf, int sepidx, const char *csvtemplate, int unitidx, char **error) +int parse_csv_file(const char *filename, int timef, int depthf, int tempf, int po2f, int cnsf, int stopdepthf, int sepidx, const char *csvtemplate, int unitidx) { struct memblock mem; int pnr = 0; @@ -417,13 +402,9 @@ void parse_csv_file(const char *filename, int timef, int depthf, int tempf, int char curdate[9]; char curtime[6]; - if (timef >= MAXCOLS || depthf >= MAXCOLS || tempf >= MAXCOLS || po2f >= MAXCOLS || cnsf >= MAXCOLS || stopdepthf >= MAXCOLS) { - int len = strlen(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d")) + MAXCOLDIGITS; - *error = malloc(len); - snprintf(*error, len, translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS); + if (timef >= MAXCOLS || depthf >= MAXCOLS || tempf >= MAXCOLS || po2f >= MAXCOLS || cnsf >= MAXCOLS || stopdepthf >= MAXCOLS) + return report_error(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS); - return; - } snprintf(timebuf, MAXCOLDIGITS, "%d", timef); snprintf(depthbuf, MAXCOLDIGITS, "%d", depthf); snprintf(tempbuf, MAXCOLDIGITS, "%d", tempf); @@ -463,16 +444,17 @@ void parse_csv_file(const char *filename, int timef, int depthf, int tempf, int params[pnr++] = NULL; if (filename == NULL) - return; + return report_error("No CSV filename"); - if (try_to_xslt_open_csv(filename, &mem, error, csvtemplate)) - return; + if (try_to_xslt_open_csv(filename, &mem, csvtemplate)) + return -1; - parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params, error); + parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); free(mem.buffer); + return 0; } -void parse_manual_file(const char *filename, int sepidx, int units, int numberf, int datef, int timef, int durationf, int locationf, int gpsf, int maxdepthf, int meandepthf, int buddyf, int notesf, int weightf, int tagsf, char **error) +int parse_manual_file(const char *filename, int sepidx, int units, int numberf, int datef, int timef, int durationf, int locationf, int gpsf, int maxdepthf, int meandepthf, int buddyf, int notesf, int weightf, int tagsf) { struct memblock mem; int pnr = 0; @@ -496,13 +478,9 @@ void parse_manual_file(const char *filename, int sepidx, int units, int numberf, char curdate[9]; char curtime[6]; - if (numberf >= MAXCOLS || datef >= MAXCOLS || timef >= MAXCOLS || durationf >= MAXCOLS || locationf >= MAXCOLS || gpsf >= MAXCOLS || maxdepthf >= MAXCOLS || meandepthf >= MAXCOLS || buddyf >= MAXCOLS || notesf >= MAXCOLS || weightf >= MAXCOLS || tagsf >= MAXCOLS) { - int len = strlen(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d")) + MAXCOLDIGITS; - *error = malloc(len); - snprintf(*error, len, translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS); + if (numberf >= MAXCOLS || datef >= MAXCOLS || timef >= MAXCOLS || durationf >= MAXCOLS || locationf >= MAXCOLS || gpsf >= MAXCOLS || maxdepthf >= MAXCOLS || meandepthf >= MAXCOLS || buddyf >= MAXCOLS || notesf >= MAXCOLS || weightf >= MAXCOLS || tagsf >= MAXCOLS) + return report_error(translate("gettextFromC", "Maximum number of supported columns on CSV import is %d"), MAXCOLS); - return; - } snprintf(numberbuf, MAXCOLDIGITS, "%d", numberf); snprintf(datebuf, MAXCOLDIGITS, "%d", datef); snprintf(timebuf, MAXCOLDIGITS, "%d", timef); @@ -560,11 +538,12 @@ void parse_manual_file(const char *filename, int sepidx, int units, int numberf, params[pnr++] = NULL; if (filename == NULL) - return; + return report_error("No manual CSV filename"); - if (try_to_xslt_open_csv(filename, &mem, error, "manualCSV")) - return; + if (try_to_xslt_open_csv(filename, &mem, "manualCSV")) + return -1; - parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params, error); + parse_xml_buffer(filename, mem.buffer, mem.size, &dive_table, (const char **)params); free(mem.buffer); + return 0; } diff --git a/icons/3x3.png b/icons/3x3.png Binary files differnew file mode 100644 index 000000000..81018bb3d --- /dev/null +++ b/icons/3x3.png diff --git a/icons/clock.png b/icons/clock.png Binary files differnew file mode 100644 index 000000000..66a00040e --- /dev/null +++ b/icons/clock.png diff --git a/icons/ead.png b/icons/ead.png Binary files differnew file mode 100644 index 000000000..20234ce9e --- /dev/null +++ b/icons/ead.png diff --git a/icons/gas.png b/icons/gas.png Binary files differnew file mode 100644 index 000000000..d70902e7b --- /dev/null +++ b/icons/gas.png diff --git a/icons/he.png b/icons/he.png Binary files differnew file mode 100644 index 000000000..6a0f5e312 --- /dev/null +++ b/icons/he.png diff --git a/icons/icon-NDLTTS.png b/icons/icon-NDLTTS.png Binary files differdeleted file mode 100644 index 1df3e63b1..000000000 --- a/icons/icon-NDLTTS.png +++ /dev/null diff --git a/icons/icon-ceiling-3m.png b/icons/icon-ceiling-3m.png Binary files differdeleted file mode 100644 index 6570526fc..000000000 --- a/icons/icon-ceiling-3m.png +++ /dev/null diff --git a/icons/icon-ceiling-calculated.png b/icons/icon-ceiling-calculated.png Binary files differdeleted file mode 100644 index 0d2edd234..000000000 --- a/icons/icon-ceiling-calculated.png +++ /dev/null diff --git a/icons/icon-ceiling-dc.png b/icons/icon-ceiling-dc.png Binary files differdeleted file mode 100644 index 04fa57f16..000000000 --- a/icons/icon-ceiling-dc.png +++ /dev/null diff --git a/icons/icon-ead.png b/icons/icon-ead.png Binary files differdeleted file mode 100644 index 6b054d691..000000000 --- a/icons/icon-ead.png +++ /dev/null diff --git a/icons/icon-lung.png b/icons/icon-lung.png Binary files differdeleted file mode 100644 index 3d3326d4f..000000000 --- a/icons/icon-lung.png +++ /dev/null diff --git a/icons/icon-mod.png b/icons/icon-mod.png Binary files differdeleted file mode 100644 index 0e9af1383..000000000 --- a/icons/icon-mod.png +++ /dev/null diff --git a/icons/icon-phegraph.png b/icons/icon-phegraph.png Binary files differdeleted file mode 100644 index 1c80dc872..000000000 --- a/icons/icon-phegraph.png +++ /dev/null diff --git a/icons/icon-pn2graph.png b/icons/icon-pn2graph.png Binary files differdeleted file mode 100644 index 71b3c8002..000000000 --- a/icons/icon-pn2graph.png +++ /dev/null diff --git a/icons/icon-po2graph.png b/icons/icon-po2graph.png Binary files differdeleted file mode 100644 index eb496c8b7..000000000 --- a/icons/icon-po2graph.png +++ /dev/null diff --git a/icons/limit.png b/icons/limit.png Binary files differnew file mode 100644 index 000000000..f26fa019c --- /dev/null +++ b/icons/limit.png diff --git a/icons/mod.png b/icons/mod.png Binary files differnew file mode 100644 index 000000000..42d4d2ae4 --- /dev/null +++ b/icons/mod.png diff --git a/icons/moreinfo.png b/icons/moreinfo.png Binary files differnew file mode 100644 index 000000000..a26703565 --- /dev/null +++ b/icons/moreinfo.png diff --git a/icons/n2.png b/icons/n2.png Binary files differnew file mode 100644 index 000000000..de168f338 --- /dev/null +++ b/icons/n2.png diff --git a/icons/o2.png b/icons/o2.png Binary files differnew file mode 100644 index 000000000..c200de54a --- /dev/null +++ b/icons/o2.png diff --git a/icons/pc.png b/icons/pc.png Binary files differnew file mode 100644 index 000000000..5030b6609 --- /dev/null +++ b/icons/pc.png diff --git a/icons/ss.png b/icons/ss.png Binary files differnew file mode 100644 index 000000000..604aa526f --- /dev/null +++ b/icons/ss.png diff --git a/libdivecomputer.c b/libdivecomputer.c index 309605e56..d6f2d60b1 100644 --- a/libdivecomputer.c +++ b/libdivecomputer.c @@ -124,7 +124,9 @@ static int parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_parser_t dive->cylinder[i].gasmix.o2.permille = o2; dive->cylinder[i].gasmix.he.permille = he; - if (!get_tanksize(devdata, data, dive->cylinder, i)) + /* for the first tank, if there is no tanksize available from the + * dive computer, fill in the default tank information (if set) */ + if (!get_tanksize(devdata, data, dive->cylinder, i) && i == 0) fill_default_cylinder(&dive->cylinder[i]); } return DC_STATUS_SUCCESS; @@ -9,7 +9,8 @@ #include <stdio.h> #include <fcntl.h> -const char system_divelist_default_font[] = "Sans 8"; +const char system_divelist_default_font[] = "Sans"; +const int system_divelist_default_font_size = 8; const char *system_default_filename(void) { @@ -130,3 +131,14 @@ int subsurface_zip_close(struct zip *zip) { return zip_close(zip); } + +/* win32 console */ +void subsurface_console_init(bool dedicated) +{ + /* NOP */ +} + +void subsurface_console_exit(void) +{ + /* NOP */ +} diff --git a/load-git.c b/load-git.c index 5d55245db..4040c1677 100644 --- a/load-git.c +++ b/load-git.c @@ -14,6 +14,8 @@ #include "device.h" #include "membuffer.h" +const char *saved_git_id = NULL; + struct keyword_action { const char *keyword; void (*fn)(char *, struct membuffer *, void *); @@ -1198,19 +1200,36 @@ static int load_dives_from_tree(git_repository *repo, git_tree *tree) return 0; } +void clear_git_id(void) +{ + saved_git_id = NULL; +} + +void set_git_id(const struct git_oid * id) +{ + static char git_id_buffer[GIT_OID_HEXSZ+1]; + + git_oid_tostr(git_id_buffer, sizeof(git_id_buffer), id); + saved_git_id = git_id_buffer; +} + static int do_git_load(git_repository *repo, const char *branch) { int ret; - git_reference *ref; - git_object *tree; - - ret = git_branch_lookup(&ref, repo, branch, GIT_BRANCH_LOCAL); - if (ret) - return report_error("Unable to look up branch '%s'", branch); - if (git_reference_peel(&tree, ref, GIT_OBJ_TREE)) - return report_error("Could not look up tree of branch '%s'", branch); - ret = load_dives_from_tree(repo, (git_tree *) tree); - git_object_free(tree); + git_object *object; + git_commit *commit; + git_tree *tree; + + if (git_revparse_single(&object, repo, branch)) + return report_error("Unable to look up revision '%s'", branch); + if (git_object_peel((git_object **)&commit, object, GIT_OBJ_COMMIT)) + return report_error("Revision '%s' is not a valid commit", branch); + if (git_commit_tree(&tree, commit)) + return report_error("Could not look up tree of commit in branch '%s'", branch); + ret = load_dives_from_tree(repo, tree); + if (!ret) + set_git_id(git_commit_id(commit)); + git_object_free((git_object *)tree); return ret; } @@ -1224,7 +1243,11 @@ static int do_git_load(git_repository *repo, const char *branch) */ int git_load_dives(struct git_repository *repo, const char *branch) { - int ret = do_git_load(repo, branch); + int ret; + + if (repo == dummy_git_repository) + return report_error("Unable to open git repository at '%s'", branch); + ret = do_git_load(repo, branch); git_repository_free(repo); free((void *)branch); finish_active_dive(); @@ -25,7 +25,8 @@ #define ICON_NAME "Subsurface.icns" #define UI_FONT "Arial 12" -const char system_divelist_default_font[] = "Arial 10"; +const char system_divelist_default_font[] = "Arial"; +const int system_divelist_default_font_size = 10; const char *system_default_filename(void) { @@ -111,3 +112,14 @@ int subsurface_zip_close(struct zip *zip) { return zip_close(zip); } + +/* win32 console */ +void subsurface_console_init(bool dedicated) +{ + /* NOP */ +} + +void subsurface_console_exit(void) +{ + /* NOP */ +} @@ -11,6 +11,7 @@ #include "qt-ui/diveplanner.h" #include <QStringList> +#include <git2.h> QTranslator *qtTranslator, *ssrfTranslator; @@ -19,16 +20,15 @@ int main(int argc, char **argv) int i; bool no_filenames = true; - setup_system_prefs(); - prefs = default_prefs; - fill_profile_color(); - init_ui(&argc, &argv); - parse_xml_init(); - taglist_init_global(); - + init_qt(&argc, &argv); QStringList files; QStringList importedFiles; QStringList arguments = QCoreApplication::arguments(); + + bool dedicated_console = arguments.length() > 1 && + (arguments.at(1) == QString("--win32console")); + subsurface_console_init(dedicated_console); + for (i = 1; i < arguments.length(); i++) { QString a = arguments.at(i); if (a.at(0) == '-') { @@ -42,18 +42,27 @@ int main(int argc, char **argv) files.push_back(a); } } + git_threads_init(); + setup_system_prefs(); + prefs = default_prefs; + fill_profile_color(); + parse_xml_init(); + taglist_init_global(); + init_ui(); if (no_filenames) { QString defaultFile(prefs.default_filename); if (!defaultFile.isEmpty()) files.push_back(QString(prefs.default_filename)); } - parse_xml_exit(); + MainWindow *m = MainWindow::instance(); - m->setLoadedWithFiles( !files.isEmpty() || !importedFiles.isEmpty()); + m->setLoadedWithFiles(!files.isEmpty() || !importedFiles.isEmpty()); m->loadFiles(files); m->importFiles(importedFiles); if (!quit) run_ui(); exit_ui(); + parse_xml_exit(); + subsurface_console_exit(); return 0; } diff --git a/parse-xml.c b/parse-xml.c index d3ff03d2c..483187a51 100644 --- a/parse-xml.c +++ b/parse-xml.c @@ -20,36 +20,13 @@ int verbose, quit; int metric = 1; -static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params, char **error); +static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params); /* the dive table holds the overall dive list; target table points at * the table we are currently filling */ struct dive_table dive_table; struct dive_table *target_table = NULL; -static void parser_error(char **error, const char *fmt, ...) -{ - va_list args; - char *tmp; - - if (!error) - return; - - tmp = malloc(1024); - va_start(args, fmt); - vsnprintf(tmp, 1024, fmt, args); - va_end(args); - - if (*error) { - int len = strlen(*error) + strlen(tmp) + 1; - *error = realloc(*error, len); - strncat(*error, tmp, strlen(tmp)); - free(tmp); - } else { - *error = tmp; - } -} - /* * Add a dive into the dive_table array */ @@ -1611,7 +1588,7 @@ const char *preprocess_divelog_de(const char *buffer) } void parse_xml_buffer(const char *url, const char *buffer, int size, - struct dive_table *table, const char **params, char **error) + struct dive_table *table, const char **params) { xmlDoc *doc; const char *res = preprocess_divelog_de(buffer); @@ -1622,13 +1599,12 @@ void parse_xml_buffer(const char *url, const char *buffer, int size, free((char *)res); if (!doc) { - fprintf(stderr, translate("gettextFromC", "Failed to parse '%s'.\n"), url); - parser_error(error, translate("gettextFromC", "Failed to parse '%s'"), url); + report_error(translate("gettextFromC", "Failed to parse '%s'"), url); return; } reset_all(); dive_start(); - doc = test_xslt_transforms(doc, params, error); + doc = test_xslt_transforms(doc, params); traverse(xmlDocGetRootElement(doc)); dive_end(); xmlFreeDoc(doc); @@ -1870,7 +1846,7 @@ extern int dm4_dive(void *param, int columns, char **data, char **column) } int parse_dm4_buffer(sqlite3 *handle, const char *url, const char *buffer, int size, - struct dive_table *table, char **error) + struct dive_table *table) { int retval; char *err = NULL; @@ -2019,7 +1995,7 @@ extern int shearwater_dive(void *param, int columns, char **data, char **column) int parse_shearwater_buffer(sqlite3 *handle, const char *url, const char *buffer, int size, - struct dive_table *table, char **error) + struct dive_table *table) { int retval; char *err = NULL; @@ -2068,7 +2044,7 @@ static struct xslt_files { { NULL, } }; -static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params, char **error) +static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params) { struct xslt_files *info = xslt_files; xmlDoc *transformed; @@ -2099,7 +2075,7 @@ static xmlDoc *test_xslt_transforms(xmlDoc *doc, const char **params, char **err xmlSubstituteEntitiesDefault(1); xslt = get_stylesheet(info->file); if (xslt == NULL) { - parser_error(error, translate("gettextFromC", "Can't open stylesheet %s"), info->file); + report_error(translate("gettextFromC", "Can't open stylesheet %s"), info->file); return doc; } transformed = xsltApplyStylesheet(xslt, doc, params); @@ -100,14 +100,13 @@ void get_gas_string(int o2, int he, char *text, int len) } /* returns the tissue tolerance at the end of this (partial) dive */ -double tissue_at_end(struct dive *dive, char **cached_datap, const char **error_string_p) +double tissue_at_end(struct dive *dive, char **cached_datap) { struct divecomputer *dc; struct sample *sample, *psample; int i, j, t0, t1, gasidx, lastdepth; int o2, he; double tissue_tolerance; - static char buf[200]; if (!dive) return 0.0; @@ -129,8 +128,7 @@ double tissue_at_end(struct dive *dive, char **cached_datap, const char **error_ t1 = sample->time.seconds; get_gas_from_events(&dive->dc, t0, &o2, &he); if ((gasidx = get_gasidx(dive, o2, he)) == -1) { - snprintf(buf, sizeof(buf), translate("gettextFromC", "Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); - *error_string_p = buf; + report_error(translate("gettextFromC", "Can't find gas %d/%d"), (o2 + 5) / 10, (he + 5) / 10); gasidx = 0; } if (i > 0) @@ -147,7 +145,7 @@ double tissue_at_end(struct dive *dive, char **cached_datap, const char **error_ } /* how many seconds until we can ascend to the next stop? */ -static int time_at_last_depth(struct dive *dive, int o2, int he, unsigned int next_stop, char **cached_data_p, const char **error_string_p) +static int time_at_last_depth(struct dive *dive, int o2, int he, unsigned int next_stop, char **cached_data_p) { int depth, gasidx; double surface_pressure, tissue_tolerance; @@ -157,7 +155,7 @@ static int time_at_last_depth(struct dive *dive, int o2, int he, unsigned int ne if (!dive) return 0; surface_pressure = dive->dc.surface_pressure.mbar / 1000.0; - tissue_tolerance = tissue_at_end(dive, cached_data_p, error_string_p); + tissue_tolerance = tissue_at_end(dive, cached_data_p); sample = &dive->dc.sample[dive->dc.samples - 1]; depth = sample->depth.mm; gasidx = get_gasidx(dive, o2, he); @@ -173,21 +171,22 @@ static int time_at_last_depth(struct dive *dive, int o2, int he, unsigned int ne return wait; } +/* if a default cylinder is set, use that */ void fill_default_cylinder(cylinder_t *cyl) { - const char *cyl_name = prefs.default_cylinder != NULL ? prefs.default_cylinder : "AL80"; + const char *cyl_name = prefs.default_cylinder; struct tank_info_t *ti = tank_info; - struct tank_info_t *al80 = NULL; + if (!cyl_name) + return; while (ti->name != NULL) { if (strcmp(ti->name, cyl_name) == 0) break; - if (strcmp(ti->name, "AL80") == 0) - al80 = ti; ti++; } if (ti->name == NULL) - ti = al80; + /* didn't find it */ + return; cyl->type.description = strdup(ti->name); if (ti->ml) { cyl->type.size.mliter = ti->ml; @@ -223,7 +222,7 @@ static int add_gas(struct dive *dive, int o2, int he) return i; } -struct dive *create_dive_from_plan(struct diveplan *diveplan, const char **error_string) +struct dive *create_dive_from_plan(struct diveplan *diveplan) { struct dive *dive; struct divedatapoint *dp; @@ -233,7 +232,6 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan, const char **error int oldpo2 = 0; int lasttime = 0; - *error_string = NULL; if (!diveplan || !diveplan->dp) return NULL; #if DEBUG_PLAN & 4 @@ -321,7 +319,7 @@ struct dive *create_dive_from_plan(struct diveplan *diveplan, const char **error gas_error_exit: free(dive); - *error_string = translate("gettextFromC", "Too many gas mixes"); + report_error(translate("gettextFromC", "Too many gas mixes")); return NULL; } @@ -588,7 +586,7 @@ static void add_plan_to_notes(struct diveplan *diveplan, struct dive *dive) } #endif -void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco, const char **error_string_p) +void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, bool add_deco) { struct dive *dive; struct sample *sample; @@ -605,7 +603,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b diveplan->surface_pressure = SURFACE_PRESSURE; if (*divep) delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan, error_string_p); + *divep = dive = create_dive_from_plan(diveplan); if (!dive) return; record_dive(dive); @@ -624,13 +622,13 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b plan_add_segment(diveplan, transitiontime, 0, o2, he, po2, false); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan, error_string_p); + *divep = dive = create_dive_from_plan(diveplan); if (dive) record_dive(dive); return; } - tissue_tolerance = tissue_at_end(dive, cached_datap, error_string_p); + tissue_tolerance = tissue_at_end(dive, cached_datap); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); #if DEBUG_PLAN & 4 printf("gas %d/%d\n", o2, he); @@ -666,7 +664,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b plan_add_segment(diveplan, transitiontime, stoplevels[stopidx], o2, he, po2, false); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan, error_string_p); + *divep = dive = create_dive_from_plan(diveplan); if (!dive) goto error_exit; record_dive(dive); @@ -687,12 +685,12 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b #endif gi--; } - wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap, error_string_p); + wait_time = time_at_last_depth(dive, o2, he, stoplevels[stopidx - 1], cached_datap); /* typically deco plans are done in one minute increments; we may want to * make this configurable at some point */ wait_time = ((wait_time + 59) / 60) * 60; #if DEBUG_PLAN & 2 - tissue_tolerance = tissue_at_end(dive, cached_datap, error_string_p); + tissue_tolerance = tissue_at_end(dive, cached_datap); ceiling = deco_allowed_depth(tissue_tolerance, diveplan->surface_pressure / 1000.0, dive, 1); printf("waittime %d:%02d at depth %5.2lfm; ceiling %5.2lfm\n", FRACTION(wait_time, 60), stoplevels[stopidx] / 1000.0, ceiling / 1000.0); @@ -707,7 +705,7 @@ void plan(struct diveplan *diveplan, char **cached_datap, struct dive **divep, b plan_add_segment(diveplan, transitiontime, stoplevels[stopidx - 1], o2, he, po2, false); /* re-create the dive */ delete_single_dive(dive_table.nr - 1); - *divep = dive = create_dive_from_plan(diveplan, error_string_p); + *divep = dive = create_dive_from_plan(diveplan); if (!dive) goto error_exit; record_dive(dive); @@ -39,6 +39,7 @@ struct preferences { struct units units; short show_sac; bool display_unused_tanks; + bool show_average_depth; bool zoomed_plot; bool text_label_with_units; }; @@ -64,6 +65,7 @@ extern void subsurface_flush_conf(void); extern void subsurface_close_conf(void); extern const char system_divelist_default_font[]; +extern const int system_divelist_default_font_size; extern const char *system_default_filename(); extern void load_preferences(void); @@ -16,7 +16,7 @@ #include "membuffer.h" int selected_dive = -1; /* careful: 0 is a valid value */ -char dc_number = 0; +unsigned int dc_number = 0; static struct plot_data *last_pi_entry_new = NULL; @@ -1130,16 +1130,19 @@ static void calculate_gas_information_new(struct dive *dive, struct plot_info *p amb_pressure = depth_to_mbar(entry->depth, dive) / 1000.0; fo2 = get_o2(&dive->cylinder[cylinderindex].gasmix); fhe = get_he(&dive->cylinder[cylinderindex].gasmix); - double ratio = (double)fhe / (1000.0 - fo2); if (entry->po2) { /* we have an O2 partial pressure in the sample - so this * is likely a CC dive... use that instead of the value * from the cylinder info */ - double po2 = entry->po2 > amb_pressure ? amb_pressure : entry->po2; - entry->po2 = po2; - entry->phe = (amb_pressure - po2) * ratio; - entry->pn2 = amb_pressure - po2 - entry->phe; + if (entry->po2 >= amb_pressure || fo2 == 1000) { + entry->po2 = amb_pressure; + entry->phe = 0; + entry->pn2 = 0; + } else { + entry->phe = (amb_pressure - entry->po2) * (double)fhe / (1000 - fo2); + entry->pn2 = amb_pressure - entry->po2 - entry->phe; + } } else { entry->po2 = fo2 / 1000.0 * amb_pressure; entry->phe = fhe / 1000.0 * amb_pressure; @@ -1148,13 +1151,11 @@ static void calculate_gas_information_new(struct dive *dive, struct plot_info *p /* Calculate MOD, EAD, END and EADD based on partial pressures calculated before * so there is no difference in calculating between OC and CC - * EAD takes O2 + N2 (air) into account - * END just uses N2 */ + * END takes O2 + N2 (air) into account ("Narcotic" for trimix dives) + * EAD just uses N2 ("Air" for nitrox dives) */ entry->mod = (prefs.mod_ppO2 / fo2 * 1000 - 1) * 10000; - entry->ead = (entry->depth + 10000) * - (entry->po2 + (amb_pressure - entry->po2) * (1 - ratio)) / amb_pressure - 10000; - entry->end = (entry->depth + 10000) * - (amb_pressure - entry->po2) * (1 - ratio) / amb_pressure / N2_IN_AIR * 1000 - 10000; + entry->end = (entry->depth + 10000) * (1000 - fhe) / 1000.0 - 10000; + entry->ead = (entry->depth + 10000) * (1000 - fo2 - fhe) / (double)N2_IN_AIR - 10000; entry->eadd = (entry->depth + 10000) * (entry->po2 / amb_pressure * O2_DENSITY + entry->pn2 / amb_pressure * N2_DENSITY + @@ -1179,9 +1180,19 @@ static void calculate_gas_information_new(struct dive *dive, struct plot_info *p */ void create_plot_info_new(struct dive *dive, struct divecomputer *dc, struct plot_info *pi) { + int o2, he, o2low; init_decompression(dive); if (last_pi_entry_new) /* Create the new plot data */ free((void *)last_pi_entry_new); + get_dive_gas(dive, &o2, &he, &o2low); + if (he > 0) { + pi->dive_type = TRIMIX; + } else { + if (o2) + pi->dive_type = NITROX; + else + pi->dive_type = AIR; + } last_pi_entry_new = populate_plot_entries(dive, dc, pi); check_gas_change_events(dive, dc, pi); /* Populate the gas index from the gas change events */ setup_gas_sensor_pressure(dive, dc, pi); /* Try to populate our gas pressure knowledge */ @@ -1193,35 +1204,19 @@ void create_plot_info_new(struct dive *dive, struct divecomputer *dc, struct plo analyze_plot_info(pi); } -/* make sure you pass this the FIRST dc - it just walks the list */ -static int nr_dcs(struct divecomputer *main) +struct divecomputer *select_dc(struct dive *dive) { - int i = 1; - struct divecomputer *dc = main; + unsigned int max = number_of_computers(dive); + unsigned int i = dc_number; - while ((dc = dc->next) != NULL) - i++; - return i; -} + /* Reset 'dc_number' if we've switched dives and it is now out of range */ + if (i >= max) + dc_number = i = 0; -struct divecomputer *select_dc(struct divecomputer *main) -{ - int i = dc_number; - struct divecomputer *dc = main; - - while (i < 0) - i += nr_dcs(main); - do { - if (--i < 0) - return dc; - } while ((dc = dc->next) != NULL); - - /* If we switched dives to one with fewer DC's, reset the dive computer counter */ - dc_number = 0; - return main; + return get_dive_dc(dive, i); } -static void plot_string(struct plot_data *entry, struct membuffer *b, bool has_ndl) +static void plot_string(struct plot_info *pi, struct plot_data *entry, struct membuffer *b, bool has_ndl) { int pressurevalue, mod, ead, end, eadd; const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit; @@ -1257,11 +1252,21 @@ static void plot_string(struct plot_data *entry, struct membuffer *b, bool has_n mod = (int)get_depth_units(entry->mod, NULL, &depth_unit); put_format(b, translate("gettextFromC", "MOD: %d%s\n"), mod, depth_unit); } + eadd = (int)get_depth_units(entry->eadd, NULL, &depth_unit); if (prefs.ead) { - ead = (int)get_depth_units(entry->ead, NULL, &depth_unit); - end = (int)get_depth_units(entry->end, NULL, &depth_unit); - eadd = (int)get_depth_units(entry->eadd, NULL, &depth_unit); - put_format(b, translate("gettextFromC", "EAD: %d%s\nEND: %d%s\nEADD: %d%s\n"), ead, depth_unit, end, depth_unit, eadd, depth_unit); + switch (pi->dive_type) { + case NITROX: + ead = (int)get_depth_units(entry->ead, NULL, &depth_unit); + put_format(b, translate("gettextFromC", "EAD: %d%s\nEADD: %d%s\n"), ead, depth_unit, eadd, depth_unit); + break; + case TRIMIX: + end = (int)get_depth_units(entry->end, NULL, &depth_unit); + put_format(b, translate("gettextFromC", "END: %d%s\nEADD: %d%s\n"), end, depth_unit, eadd, depth_unit); + break; + case AIR: + /* nothing */ + break; + } } if (entry->stopdepth) { depthvalue = get_depth_units(entry->stopdepth, NULL, &depth_unit); @@ -1334,7 +1339,7 @@ void get_plot_details_new(struct plot_info *pi, int time, struct membuffer *mb) break; } if (entry) - plot_string(entry, mb, pi->has_ndl); + plot_string(pi, entry, mb, pi->has_ndl); } /* Compare two plot_data entries and writes the results into a string */ diff --git a/qt-gui.cpp b/qt-gui.cpp index 2b16c2d77..a3b8b44b6 100644 --- a/qt-gui.cpp +++ b/qt-gui.cpp @@ -78,12 +78,14 @@ static QString decodeUtf8(const QByteArray &fname) } #endif -void init_ui(int *argcp, char ***argvp) +void init_qt(int *argcp, char ***argvp) { - QVariant v; - application = new QApplication(*argcp, *argvp); +} +void init_ui(void) +{ + QVariant v; // tell Qt to use system proxies // note: on Linux, "system" == "environment variables" QNetworkProxyFactory::setUseSystemConfiguration(true); @@ -155,8 +157,6 @@ void init_ui(int *argcp, char ***argvp) window->setTitle(MWTF_FILENAME); else window->setTitle(MWTF_DEFAULT); - - return; } void run_ui(void) @@ -1,8 +1,8 @@ #ifndef QT_GUI_H #define QT_GUI_H -void init_ui(int *argcp, char ***argvp); -void init_qt_ui(int *argcp, char ***argvp, char *errormessage); +void init_qt(int *argcp, char ***argvp); +void init_ui(void); void run_ui(void); void exit_ui(void); diff --git a/qt-ui/divelistview.cpp b/qt-ui/divelistview.cpp index ab4e301ad..d9f5eb93b 100644 --- a/qt-ui/divelistview.cpp +++ b/qt-ui/divelistview.cpp @@ -582,14 +582,26 @@ void DiveListView::newTripAbove() restoreSelection(); } +void DiveListView::addToTripBelow() +{ + addToTrip(true); +} + void DiveListView::addToTripAbove() { + addToTrip(false); +} + +void DiveListView::addToTrip(bool below) +{ int idx, delta = (currentOrder == Qt::AscendingOrder) ? -1 : +1; dive_trip_t *trip = NULL; struct dive *pd = NULL; struct dive *d = (struct dive *)contextMenuIndex.data(DiveTripModel::DIVE_ROLE).value<void *>(); if (!d) // shouldn't happen as we only are setting up this action if this is a dive return; + if (below) // Should we add to the trip below instead? + delta *= -1; rememberSelection(); if (d->selected) { // we are right-clicking on one of possibly many selected dive(s) // find the top selected dive, depending on the list order @@ -714,6 +726,7 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event) popup.addAction(tr("remove dive(s) from trip"), this, SLOT(removeFromTrip())); popup.addAction(tr("create new trip above"), this, SLOT(newTripAbove())); popup.addAction(tr("add dive(s) to trip immediately above"), this, SLOT(addToTripAbove())); + popup.addAction(tr("add dive(s) to trip immediately below"), this, SLOT(addToTripBelow())); } if (trip) { popup.addAction(tr("merge trip with trip above"), this, SLOT(mergeTripAbove())); diff --git a/qt-ui/divelistview.h b/qt-ui/divelistview.h index f395720bd..3079cddab 100644 --- a/qt-ui/divelistview.h +++ b/qt-ui/divelistview.h @@ -46,6 +46,7 @@ slots: void mergeTripBelow(); void newTripAbove(); void addToTripAbove(); + void addToTripBelow(); void mergeDives(); void saveSelectedDivesAs(); void exportSelectedDivesAsUDDF(); @@ -77,6 +78,7 @@ private: void updateLastUsedImageDir(const QString &s); void updateLastImageTimeOffset(int offset); int lastImageTimeOffset(); + void addToTrip(bool); }; #endif // DIVELISTVIEW_H diff --git a/qt-ui/divelogimportdialog.cpp b/qt-ui/divelogimportdialog.cpp index bff49156f..3de08b003 100644 --- a/qt-ui/divelogimportdialog.cpp +++ b/qt-ui/divelogimportdialog.cpp @@ -54,8 +54,6 @@ DiveLogImportDialog::~DiveLogImportDialog() #define VALUE_IF_CHECKED(x) (ui->x->isEnabled() ? ui->x->value() - 1 : -1) void DiveLogImportDialog::on_buttonBox_accepted() { - char *error = NULL; - if (ui->tabWidget->currentIndex() == 0) { for (int i = 0; i < fileNames.size(); ++i) { parse_csv_file(fileNames[i].toUtf8().data(), ui->CSVTime->value() - 1, @@ -65,13 +63,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() VALUE_IF_CHECKED(CSVstopdepth), ui->CSVSeparator->currentIndex(), specialCSV.contains(ui->knownImports->currentIndex()) ? CSVApps[ui->knownImports->currentIndex()].name.toUtf8().data() : "csv", - ui->CSVUnits->currentIndex(), - &error); - if (error != NULL) { - MainWindow::instance()->showError(error); - free(error); - error = NULL; - } + ui->CSVUnits->currentIndex()); } } else { for (int i = 0; i < fileNames.size(); ++i) { @@ -84,13 +76,7 @@ void DiveLogImportDialog::on_buttonBox_accepted() VALUE_IF_CHECKED(Gps), VALUE_IF_CHECKED(MaxDepth), VALUE_IF_CHECKED(MeanDepth), VALUE_IF_CHECKED(Buddy), VALUE_IF_CHECKED(Notes), VALUE_IF_CHECKED(Weight), - VALUE_IF_CHECKED(Tags), - &error); - if (error != NULL) { - MainWindow::instance()->showError(error); - free(error); - error = NULL; - } + VALUE_IF_CHECKED(Tags)); } } process_dives(true, false); diff --git a/qt-ui/diveplanner.cpp b/qt-ui/diveplanner.cpp index ad950f262..a7d5ee7c6 100644 --- a/qt-ui/diveplanner.cpp +++ b/qt-ui/diveplanner.cpp @@ -5,7 +5,6 @@ #include "mainwindow.h" #include "maintab.h" #include "tableview.h" -#include "graphicsview-common.h" #include "../dive.h" #include "../divelist.h" @@ -1067,8 +1066,9 @@ DivePlannerWidget::DivePlannerWidget(QWidget *parent, Qt::WindowFlags f) : QWidg QTableView *view = ui.cylinderTableWidget->view(); view->setColumnHidden(CylindersModel::START, true); view->setColumnHidden(CylindersModel::END, true); - // disabled as pointless outside of the disabled planner - // view->setColumnHidden(CylindersModel::DEPTH, false); +#ifdef ENABLE_PLANNER + view->setColumnHidden(CylindersModel::DEPTH, false); +#endif view->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this)); connect(ui.cylinderTableWidget, SIGNAL(addButtonClicked()), DivePlannerPointsModel::instance(), SLOT(addCylinder_clicked())); connect(ui.tableWidget, SIGNAL(addButtonClicked()), DivePlannerPointsModel::instance(), SLOT(addStop())); @@ -1546,7 +1546,6 @@ void DivePlannerPointsModel::createTemporaryPlan() } char *cache = NULL; tempDive = NULL; - const char *errorString = NULL; struct divedatapoint *dp = NULL; for (int i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = &stagingDive->cylinder[i]; @@ -1564,7 +1563,7 @@ void DivePlannerPointsModel::createTemporaryPlan() #if DEBUG_PLAN dump_plan(&diveplan); #endif - plan(&diveplan, &cache, &tempDive, isPlanner(), &errorString); + plan(&diveplan, &cache, &tempDive, isPlanner()); if (mode == ADD) { // copy the samples and events, but don't overwrite the cylinders copy_samples(tempDive, current_dive); @@ -1600,13 +1599,15 @@ void DivePlannerPointsModel::createPlan() // to not delete it later. mumble. ;p char *cache = NULL; tempDive = NULL; - const char *errorString = NULL; + + if (!diveplan.dp) + return cancelPlan(); createTemporaryPlan(); - plan(&diveplan, &cache, &tempDive, isPlanner(), &errorString); + plan(&diveplan, &cache, &tempDive, isPlanner()); copy_cylinders(stagingDive, tempDive); int mean[MAX_CYLINDERS], duration[MAX_CYLINDERS]; - per_cylinder_mean_depth(tempDive, select_dc(&tempDive->dc), mean, duration); + per_cylinder_mean_depth(tempDive, select_dc(tempDive), mean, duration); for (int i = 0; i < MAX_CYLINDERS; i++) { cylinder_t *cyl = tempDive->cylinder + i; if (cylinder_none(cyl)) diff --git a/qt-ui/globe.cpp b/qt-ui/globe.cpp index 4c6743770..961e0dbe5 100644 --- a/qt-ui/globe.cpp +++ b/qt-ui/globe.cpp @@ -1,4 +1,5 @@ #include "globe.h" +#ifndef NO_MARBLE #include "kmessagewidget.h" #include "mainwindow.h" #include "ui_mainwindow.h" @@ -295,3 +296,12 @@ void GlobeGPS::resizeEvent(QResizeEvent *event) messageWidget->setGeometry(5, 5, size - 10, 0); messageWidget->setMaximumHeight(500); } +#else + +GlobeGPS::GlobeGPS(QWidget* parent) { setText("MARBLE DISABLED AT BUILD TIME"); } +void GlobeGPS::repopulateLabels() {} +void GlobeGPS::centerOn(dive* dive) {} +bool GlobeGPS::eventFilter(QObject *obj, QEvent *ev) {} +void GlobeGPS::prepareForGetDiveCoordinates() {} +void GlobeGPS::reload() {} +#endif diff --git a/qt-ui/globe.h b/qt-ui/globe.h index 80d9613dd..f5a1e4053 100644 --- a/qt-ui/globe.h +++ b/qt-ui/globe.h @@ -1,5 +1,6 @@ #ifndef GLOBE_H #define GLOBE_H +#ifndef NO_MARBLE #include <marble/MarbleWidget.h> #include <marble/GeoDataCoordinates.h> @@ -41,4 +42,21 @@ slots: void prepareForGetDiveCoordinates(); }; +#else // NO_MARBLE +/* Dummy widget for when we don't have MarbleWidget */ +#include <QLabel> + +class GlobeGPS : public QLabel { + Q_OBJECT +public: + GlobeGPS(QWidget *parent); + void reload(); + void repopulateLabels(); + void centerOn(struct dive* dive); + bool eventFilter(QObject*, QEvent*); +public slots: + void prepareForGetDiveCoordinates(); +}; + +#endif // NO_MARBLE #endif // GLOBE_H diff --git a/qt-ui/maintab.cpp b/qt-ui/maintab.cpp index 45483f177..9ff5e9f31 100644 --- a/qt-ui/maintab.cpp +++ b/qt-ui/maintab.cpp @@ -88,8 +88,9 @@ MainTab::MainTab(QWidget *parent) : QTabWidget(parent), ui.cylinders->view()->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this)); ui.weights->view()->setItemDelegateForColumn(WeightModel::TYPE, new WSInfoDelegate(this)); - // disabled as this column is pointless outside the disabled planner - // ui.cylinders->view()->setColumnHidden(CylindersModel::DEPTH, true); +#ifdef ENABLE_PLANNER + ui.cylinders->view()->setColumnHidden(CylindersModel::DEPTH, true); +#endif completers.buddy = new QCompleter(&buddyModel, ui.buddy); completers.divemaster = new QCompleter(&diveMasterModel, ui.divemaster); completers.location = new QCompleter(&locationModel, ui.location); @@ -229,11 +230,11 @@ void MainTab::displayMessage(QString str) void MainTab::updateTextLabels(bool showUnits) { if (showUnits && prefs.text_label_with_units) { - ui.airTempLabel->setText(QApplication::translate("MainTab", "Air temp [%1]").arg(get_temp_unit())); - ui.waterTempLabel->setText(QApplication::translate("MainTab", "Water temp [%1]").arg(get_temp_unit())); + ui.airTempLabel->setText(tr("Air temp [%1]").arg(get_temp_unit())); + ui.waterTempLabel->setText(tr("Water temp [%1]").arg(get_temp_unit())); } else { - ui.airTempLabel->setText(QApplication::translate("MainTab", "Air temp", 0, QApplication::UnicodeUTF8)); - ui.waterTempLabel->setText(QApplication::translate("MainTab", "Water temp", 0, QApplication::UnicodeUTF8)); + ui.airTempLabel->setText(tr("Air temp")); + ui.waterTempLabel->setText(tr("Water temp")); } } @@ -483,7 +484,7 @@ void MainTab::updateDiveInfo(int dive) get_gas_used(d, gases); QString volumes = get_volume_string(gases[0], true); int mean[MAX_CYLINDERS], duration[MAX_CYLINDERS]; - per_cylinder_mean_depth(d, select_dc(&d->dc), mean, duration); + per_cylinder_mean_depth(d, select_dc(d), mean, duration); volume_t sac; QString SACs; if (mean[0] && duration[0]) { @@ -585,6 +586,7 @@ void MainTab::reload() void MainTab::acceptChanges() { MainWindow::instance()->dive_list()->setEnabled(true); + MainWindow::instance()->setFocus(); tabBar()->setTabIcon(0, QIcon()); // Notes tabBar()->setTabIcon(1, QIcon()); // Equipment hideMessage(); @@ -632,6 +634,7 @@ void MainTab::acceptChanges() d->cylinder[i] = multiEditEquipmentPlaceholder.cylinder[i]; } } + MainWindow::instance()->graphics()->replot(); } if (weightModel->changed) { @@ -850,27 +853,33 @@ void markChangedWidget(QWidget *w) void MainTab::on_buddy_textChanged() { - QString text = ui.buddy->toPlainText().split(",", QString::SkipEmptyParts).join(", "); + QStringList text_list = ui.buddy->toPlainText().split(",", QString::SkipEmptyParts); + for (int i = 0; i < text_list.size(); i++) + text_list[i] = text_list[i].trimmed(); + QString text = text_list.join(", "); EDIT_SELECTED_DIVES(EDIT_TEXT(mydive->buddy, text)); markChangedWidget(ui.buddy); } void MainTab::on_divemaster_textChanged() { - QString text = ui.divemaster->toPlainText().split(",", QString::SkipEmptyParts).join(", "); + QStringList text_list = ui.divemaster->toPlainText().split(",", QString::SkipEmptyParts); + for (int i = 0; i < text_list.size(); i++) + text_list[i] = text_list[i].trimmed(); + QString text = text_list.join(", "); EDIT_SELECTED_DIVES(EDIT_TEXT(mydive->divemaster, text)); markChangedWidget(ui.divemaster); } void MainTab::on_airtemp_textChanged(const QString &text) { - EDIT_SELECTED_DIVES(select_dc(&mydive->dc)->airtemp.mkelvin = parseTemperatureToMkelvin(text)); + EDIT_SELECTED_DIVES(select_dc(mydive)->airtemp.mkelvin = parseTemperatureToMkelvin(text)); markChangedWidget(ui.airtemp); } void MainTab::on_watertemp_textChanged(const QString &text) { - EDIT_SELECTED_DIVES(select_dc(&mydive->dc)->watertemp.mkelvin = parseTemperatureToMkelvin(text)); + EDIT_SELECTED_DIVES(select_dc(mydive)->watertemp.mkelvin = parseTemperatureToMkelvin(text)); markChangedWidget(ui.watertemp); } @@ -977,11 +986,15 @@ void MainTab::on_visibility_valueChanged(int value) void MainTab::editCylinderWidget(const QModelIndex &index) { - if (editMode == NONE) + if (cylindersModel->changed && editMode == NONE) { enableEdition(); - - if (index.isValid() && index.column() != CylindersModel::REMOVE) + return; + } + if (index.isValid() && index.column() != CylindersModel::REMOVE) { + if (editMode == NONE) + enableEdition(); ui.cylinders->edit(index); + } } void MainTab::editWeightWidget(const QModelIndex &index) diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp index a6a6c14cf..b63225295 100644 --- a/qt-ui/mainwindow.cpp +++ b/qt-ui/mainwindow.cpp @@ -23,6 +23,7 @@ #include "starwidget.h" #include "../dive.h" +#include "../display.h" #include "../divelist.h" #include "../pref.h" #include "../helpers.h" @@ -35,8 +36,14 @@ #include "simplewidgets.h" #include "diveplanner.h" #include "about.h" +#include "worldmap-save.h" +#ifndef NO_PRINTING #include "printdialog.h" +#endif #include "divelogimportdialog.h" +#ifndef NO_USERMANUAL +#include "usermanual.h" +#endif MainWindow *MainWindow::m_Instance = NULL; @@ -80,6 +87,16 @@ MainWindow::MainWindow() : QMainWindow(), #ifndef ENABLE_PLANNER // ui.menuLog->removeAction(ui.actionDivePlanner); #endif +#ifdef NO_MARBLE + ui.layoutWidget->hide(); + ui.menuView->removeAction(ui.actionViewGlobe); +#endif +#ifdef NO_USERMANUAL + ui.menuHelp->removeAction(ui.actionUserManual); +#endif +#ifdef NO_PRINTING + ui.menuFile->removeAction(ui.actionPrint); +#endif } MainWindow::~MainWindow() @@ -105,6 +122,7 @@ MainWindow *MainWindow::instance() // this gets called after we download dives from a divecomputer void MainWindow::refreshDisplay(bool recreateDiveList) { + showError(get_error_string()); ui.InfoWidget->reload(); TankInfoModel::instance()->update(); ui.globe->reload(); @@ -214,6 +232,7 @@ void MainWindow::on_actionClose_triggered() ui.newProfile->setEmptyState(); /* free the dives and trips */ + clear_git_id(); while (dive_table.nr) delete_single_dive(0); @@ -259,11 +278,22 @@ void MainWindow::on_actionExportUDDF_triggered() export_dives_uddf(filename.toUtf8(), false); } +void MainWindow::on_actionExportHTMLworldmap_triggered() +{ + QFileInfo fi(system_default_filename()); + QString filename = QFileDialog::getSaveFileName(this, tr("Export World Map"), fi.absolutePath(), + tr("HTML files (*.html)")); + if (!filename.isNull() && !filename.isEmpty()) + export_worldmap_HTML(filename.toUtf8().data()); +} + void MainWindow::on_actionPrint_triggered() { +#ifndef NO_PRINTING PrintDialog dlg(this); dlg.exec(); +#endif } void MainWindow::disableDcShortcuts() @@ -505,14 +535,16 @@ void MainWindow::saveSplitterSizes() void MainWindow::on_actionPreviousDC_triggered() { - dc_number--; + unsigned nrdc = number_of_computers(current_dive); + dc_number = (dc_number + nrdc - 1) % nrdc; ui.InfoWidget->updateDiveInfo(selected_dive); ui.newProfile->plotDives(QList<struct dive *>() << (current_dive)); } void MainWindow::on_actionNextDC_triggered() { - dc_number++; + unsigned nrdc = number_of_computers(current_dive); + dc_number = (dc_number + 1) % nrdc; ui.InfoWidget->updateDiveInfo(selected_dive); ui.newProfile->plotDives(QList<struct dive *>() << (current_dive)); } @@ -545,10 +577,12 @@ void MainWindow::on_actionAboutSubsurface_triggered() void MainWindow::on_actionUserManual_triggered() { +#ifndef NO_USERMANUAL if (!helpView) { helpView = new UserManual(); } helpView->show(); +#endif } QString MainWindow::filter() @@ -575,7 +609,7 @@ QString MainWindow::filter() bool MainWindow::askSaveChanges() { QString message; - QMessageBox response; + QMessageBox response(MainWindow::instance()); if (existing_filename) message = tr("Do you want to save the changes you made in the file %1?").arg(existing_filename); @@ -588,6 +622,7 @@ bool MainWindow::askSaveChanges() response.setWindowTitle(tr("Save Changes?")); // Not displayed on MacOSX as described in Qt API response.setInformativeText(tr("Changes will be lost if you don't save them.")); response.setIcon(QMessageBox::Warning); + response.setWindowModality(Qt::WindowModal); int ret = response.exec(); switch (ret) { @@ -639,8 +674,9 @@ void MainWindow::readSettings() { QSettings s; s.beginGroup("Display"); - QFont defaultFont = s.value("divelist_font", qApp->font()).value<QFont>(); - defaultFont.setPointSizeF(s.value("font_size", qApp->font().pointSizeF()).toFloat()); + QFont defaultFont = QFont(default_prefs.divelist_font); + defaultFont = s.value("divelist_font", defaultFont).value<QFont>(); + defaultFont.setPointSizeF(s.value("font_size", default_prefs.font_size).toFloat()); qApp->setFont(defaultFont); s.endGroup(); @@ -881,7 +917,7 @@ void MainWindow::recentFileTriggered(bool checked) loadFiles(QStringList() << filename); } -void MainWindow::file_save_as(void) +int MainWindow::file_save_as(void) { QString filename; const char *default_filename; @@ -892,20 +928,26 @@ void MainWindow::file_save_as(void) default_filename = prefs.default_filename; filename = QFileDialog::getSaveFileName(this, tr("Save File as"), default_filename, tr("Subsurface XML files (*.ssrf *.xml *.XML)")); - if (!filename.isNull() && !filename.isEmpty()) { + if (filename.isNull() || filename.isEmpty()) + return report_error("No filename to save into"); - if (ui.InfoWidget->isEditing()) - ui.InfoWidget->acceptChanges(); + if (ui.InfoWidget->isEditing()) + ui.InfoWidget->acceptChanges(); - save_dives(filename.toUtf8().data()); - set_filename(filename.toUtf8().data(), true); - setTitle(MWTF_FILENAME); - mark_divelist_changed(false); - addRecentFile(QStringList() << filename); + if (save_dives(filename.toUtf8().data())) { + showError(get_error_string()); + return -1; } + + showError(get_error_string()); + set_filename(filename.toUtf8().data(), true); + setTitle(MWTF_FILENAME); + mark_divelist_changed(false); + addRecentFile(QStringList() << filename); + return 0; } -void MainWindow::file_save(void) +int MainWindow::file_save(void) { const char *current_default; @@ -923,9 +965,14 @@ void MainWindow::file_save(void) if (!current_def_dir.exists()) current_def_dir.mkpath(current_def_dir.absolutePath()); } - save_dives(existing_filename); + if (save_dives(existing_filename)) { + showError(get_error_string()); + return -1; + } + showError(get_error_string()); mark_divelist_changed(false); addRecentFile(QStringList() << QString(existing_filename)); + return 0; } void MainWindow::showError(QString message) @@ -963,15 +1010,10 @@ void MainWindow::importFiles(const QStringList fileNames) return; QByteArray fileNamePtr; - char *error = NULL; + for (int i = 0; i < fileNames.size(); ++i) { fileNamePtr = QFile::encodeName(fileNames.at(i)); - parse_file(fileNamePtr.data(), &error); - if (error != NULL) { - showError(error); - free(error); - error = NULL; - } + parse_file(fileNamePtr.data()); } process_dives(true, false); refreshDisplay(); @@ -982,21 +1024,19 @@ void MainWindow::loadFiles(const QStringList fileNames) if (fileNames.isEmpty()) return; - char *error = NULL; QByteArray fileNamePtr; QStringList failedParses; for (int i = 0; i < fileNames.size(); ++i) { - fileNamePtr = QFile::encodeName(fileNames.at(i)); - parse_file(fileNamePtr.data(), &error); - set_filename(fileNamePtr.data(), true); - setTitle(MWTF_FILENAME); + int error; - if (error != NULL) { + fileNamePtr = QFile::encodeName(fileNames.at(i)); + error = parse_file(fileNamePtr.data()); + if (!error) { + set_filename(fileNamePtr.data(), true); + setTitle(MWTF_FILENAME); + } else { failedParses.append(fileNames.at(i)); - showError(error); - free(error); - error = NULL; } } diff --git a/qt-ui/mainwindow.h b/qt-ui/mainwindow.h index 8244d0a0f..a0c5be333 100644 --- a/qt-ui/mainwindow.h +++ b/qt-ui/mainwindow.h @@ -12,7 +12,6 @@ #include <QUrl> #include "ui_mainwindow.h" -#include "usermanual.h" struct DiveList; class QSortFilterProxyModel; @@ -28,6 +27,7 @@ class GlobeGPS; class MainTab; class ProfileGraphicsView; class QWebView; +class QSettings; enum MainWindowTitleFormat { MWTF_DEFAULT, @@ -90,6 +90,7 @@ slots: void on_actionSaveAs_triggered(); void on_actionClose_triggered(); void on_actionExportUDDF_triggered(); + void on_actionExportHTMLworldmap_triggered(); void on_actionPrint_triggered(); void on_actionPreferences_triggered(); void on_actionQuit_triggered(); @@ -155,7 +156,7 @@ private: Ui::MainWindow ui; QAction *actionNextDive; QAction *actionPreviousDive; - UserManual *helpView; + QMainWindow *helpView; QTreeView *yearlyStats; QAbstractItemModel *yearlyStatsModel; CurrentState state; @@ -163,8 +164,8 @@ private: static MainWindow *m_Instance; bool askSaveChanges(); void writeSettings(); - void file_save(); - void file_save_as(); + int file_save(); + int file_save_as(); void beginChangeState(CurrentState s); void saveSplitterSizes(); QString lastUsedDir(); diff --git a/qt-ui/mainwindow.ui b/qt-ui/mainwindow.ui index a933a2de9..2bf249683 100644 --- a/qt-ui/mainwindow.ui +++ b/qt-ui/mainwindow.ui @@ -551,6 +551,7 @@ <addaction name="separator"/> <addaction name="actionClose"/> <addaction name="actionExportUDDF"/> + <addaction name="actionExportHTMLworldmap"/> <addaction name="actionPrint"/> <addaction name="actionPreferences"/> <addaction name="separator"/> @@ -681,6 +682,11 @@ <string>Ctrl+U</string> </property> </action> + <action name="actionExportHTMLworldmap"> + <property name="text"> + <string>Export HTML World Map</string> + </property> + </action> <action name="actionPrint"> <property name="text"> <string>&Print</string> diff --git a/qt-ui/modeldelegates.cpp b/qt-ui/modeldelegates.cpp index 85542f0f9..7ddfb9a16 100644 --- a/qt-ui/modeldelegates.cpp +++ b/qt-ui/modeldelegates.cpp @@ -162,7 +162,10 @@ bool ComboBoxDelegate::eventFilter(QObject *object, QEvent *event) QKeyEvent *ev = static_cast<QKeyEvent *>(event); if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) { currCombo.ignoreSelection = true; - currCombo.comboEditor->showPopup(); + if (!currCombo.comboEditor->completer()->popup()->isVisible()){ + currCombo.comboEditor->showPopup(); + return true; + } } if (ev->key() == Qt::Key_Tab || ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return) { currCombo.activeText = currCombo.comboEditor->currentText(); diff --git a/qt-ui/models.cpp b/qt-ui/models.cpp index 01a3c33b3..5b011d747 100644 --- a/qt-ui/models.cpp +++ b/qt-ui/models.cpp @@ -67,7 +67,9 @@ CylindersModel::CylindersModel(QObject *parent) : current(0), rows(0) { // enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH}; setHeaderDataStrings(QStringList() << "" << tr("Type") << tr("Size") << tr("WorkPress") << tr("StartPress") << tr("EndPress") << trUtf8("O" UTF8_SUBSCRIPT_2 "%") << tr("He%") - // while the planner is disabled, we don't need this column: << tr("Switch at") +#ifdef ENABLE_PLANNER + << tr("Switch at") +#endif ); } @@ -143,9 +145,11 @@ QVariant CylindersModel::data(const QModelIndex &index, int role) const case HE: ret = percent_string(cyl->gasmix.he); break; - // case DEPTH: - // ret = get_depth_string(cyl->depth, true); - // break; +#ifdef ENABLE_PLANNER + case DEPTH: + ret = get_depth_string(cyl->depth, true); + break; +#endif } break; case Qt::DecorationRole: @@ -258,9 +262,13 @@ bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, in changed = true; } break; - // case DEPTH: - // if (CHANGED()) - // cyl->depth = string_to_depth(vString.toUtf8().data()); +#ifdef ENABLE_PLANNER + case DEPTH: + if (CHANGED()) { + cyl->depth = string_to_depth(vString.toUtf8().data()); + changed = true; + } +#endif } dataChanged(index, index); if (addDiveMode) @@ -336,7 +344,10 @@ void CylindersModel::remove(const QModelIndex &index) return; } cylinder_t *cyl = ¤t->cylinder[index.row()]; - if (DivePlannerPointsModel::instance()->tankInUse(cyl->gasmix.o2.permille, cyl->gasmix.he.permille)) { + if ((DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING && + DivePlannerPointsModel::instance()->tankInUse(cyl->gasmix.o2.permille, cyl->gasmix.he.permille)) || + (DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING && cyl->used)) + { QMessageBox::warning(MainWindow::instance(), TITLE_OR_TEXT( tr("Cylinder cannot be removed"), tr("This gas in use. Only cylinders that are not used in the dive can be removed.")), diff --git a/qt-ui/models.h b/qt-ui/models.h index 9a198f657..14969f8f4 100644 --- a/qt-ui/models.h +++ b/qt-ui/models.h @@ -102,7 +102,10 @@ public: END, O2, HE, - /* DEPTH, */ COLUMNS +#ifdef ENABLE_PLANNER + DEPTH, +#endif + COLUMNS }; explicit CylindersModel(QObject *parent = 0); diff --git a/qt-ui/preferences.cpp b/qt-ui/preferences.cpp index e2473d66b..7c74b15a6 100644 --- a/qt-ui/preferences.cpp +++ b/qt-ui/preferences.cpp @@ -88,6 +88,7 @@ void PreferencesDialog::setUiFromPrefs() } ui.displayinvalid->setChecked(prefs.display_invalid_dives); ui.display_unused_tanks->setChecked(prefs.display_unused_tanks); + ui.show_average_depth->setChecked(prefs.show_average_depth); ui.vertical_speed_minutes->setChecked(prefs.units.vertical_speed_time == units::MINUTES); ui.vertical_speed_seconds->setChecked(prefs.units.vertical_speed_time == units::SECONDS); @@ -184,6 +185,7 @@ void PreferencesDialog::syncSettings() s.setValue("gfhigh", ui.gfhigh->value()); SB("gf_low_at_maxdepth", ui.gf_low_at_maxdepth); SB("display_unused_tanks", ui.display_unused_tanks); + SB("show_average_depth", ui.show_average_depth); s.endGroup(); // Units @@ -278,6 +280,7 @@ void PreferencesDialog::loadSettings() set_gf(prefs.gflow, prefs.gfhigh, prefs.gf_low_at_maxdepth); GET_BOOL("show_sac", show_sac); GET_BOOL("display_unused_tanks", display_unused_tanks); + GET_BOOL("show_average_depth", show_average_depth); s.endGroup(); s.beginGroup("GeneralSettings"); diff --git a/qt-ui/preferences.ui b/qt-ui/preferences.ui index e0bd4c27d..06e000141 100644 --- a/qt-ui/preferences.ui +++ b/qt-ui/preferences.ui @@ -657,7 +657,18 @@ </widget> </item> </layout> - </item> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_11d"> + <item> + <widget class="QCheckBox" name="show_average_depth"> + <property name="text"> + <string>show average depth</string> + </property> + </widget> + </item> + </layout> + </item> </layout> </widget> </item> diff --git a/qt-ui/printdialog.h b/qt-ui/printdialog.h index 33cc25f03..32069a278 100644 --- a/qt-ui/printdialog.h +++ b/qt-ui/printdialog.h @@ -29,5 +29,4 @@ slots: void printClicked(); void onPaintRequested(QPrinter *); }; - #endif // PRINTDIALOG_H diff --git a/qt-ui/printlayout.cpp b/qt-ui/printlayout.cpp index 4723701b7..d146ee063 100644 --- a/qt-ui/printlayout.cpp +++ b/qt-ui/printlayout.cpp @@ -131,9 +131,11 @@ void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) painter.scale(scaleX, scaleY); // setup the profile widget - ProfileWidget2 profile; - profile.setFrameStyle(QFrame::NoFrame); -// profile->setPrintMode(true, !printOptions->color_selected); + QPointer<ProfileWidget2> profile = MainWindow::instance()->graphics(); + const int profileFrameStyle = profile->frameStyle(); + profile->setFrameStyle(QFrame::NoFrame); + profile->setPrintMode(true, !printOptions->color_selected); + QSize originalSize = profile->size(); // swap rows/col for landscape if (printer->orientation() == QPrinter::Landscape) { int swap = divesPerColumn; @@ -156,7 +158,7 @@ void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) // profilePrintTableMaxH updates after the table is created const int tableH = profilePrintTableMaxH; // resize the profile widget - profile.resize(scaledW, scaledH - tableH - padPT); + profile->resize(scaledW, scaledH - tableH - padPT); // offset table or profile on top int yOffsetProfile = 0, yOffsetTable = 0; if (printOptions->notes_up) @@ -180,8 +182,8 @@ void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) // draw a profile painter.translate((scaledW + padW) * col, (scaledH + padH) * row + yOffsetProfile); - profile.plotDives( QList<struct dive*>() << dive); - profile.render(&painter, QRect(0, 0, scaledW, scaledH - tableH - padPT)); + profile->plotDives(QList<struct dive*>() << dive); + profile->render(&painter, QRect(0, 0, scaledW, scaledH - tableH - padPT)); painter.setTransform(origTransform); // draw a table @@ -193,6 +195,13 @@ void PrintLayout::printProfileDives(int divesPerRow, int divesPerColumn) printed++; emit signalProgress((printed * 100) / total); } + // cleanup + painter.end(); + delete table; + profile->setFrameStyle(profileFrameStyle); + profile->setPrintMode(false); + profile->resize(originalSize); + profile->plotDives(QList<struct dive*>() << current_dive); } /* we create a table that has a fixed height, but can stretch to fit certain width */ diff --git a/qt-ui/profile/diveeventitem.cpp b/qt-ui/profile/diveeventitem.cpp index 1fd4ef07a..3f75ab10a 100644 --- a/qt-ui/profile/diveeventitem.cpp +++ b/qt-ui/profile/diveeventitem.cpp @@ -4,8 +4,12 @@ #include "animationfunctions.h" #include "libdivecomputer.h" #include "dive.h" +#include "profile.h" #include <QDebug> +extern struct ev_select *ev_namelist; +extern int evn_used; + DiveEventItem::DiveEventItem(QObject *parent) : DivePixmapItem(parent), vAxis(NULL), hAxis(NULL), @@ -105,6 +109,15 @@ void DiveEventItem::eventVisibilityChanged(const QString &eventName, bool visibl { } +bool DiveEventItem::shouldBeHidden() +{ + for (int i = 0; i < evn_used; i++) { + if (!strcmp(internalEvent->name, ev_namelist[i].ev_name) && ev_namelist[i].plot_ev == false) + return true; + } + return false; +} + void DiveEventItem::recalculatePos(bool instant) { if (!vAxis || !hAxis || !internalEvent || !dataModel) @@ -116,9 +129,8 @@ void DiveEventItem::recalculatePos(bool instant) hide(); return; } - if (!isVisible()) + if (!isVisible() && !shouldBeHidden()) show(); - int depth = dataModel->data(dataModel->index(result.first().row(), DivePlotDataModel::DEPTH)).toInt(); qreal x = hAxis->posAtValue(internalEvent->time.seconds); qreal y = vAxis->posAtValue(depth); @@ -126,4 +138,6 @@ void DiveEventItem::recalculatePos(bool instant) Animations::moveTo(this, x, y); else setPos(x, y); + if (isVisible() && shouldBeHidden()) + hide(); } diff --git a/qt-ui/profile/diveeventitem.h b/qt-ui/profile/diveeventitem.h index 1b138163c..f358fee6d 100644 --- a/qt-ui/profile/diveeventitem.h +++ b/qt-ui/profile/diveeventitem.h @@ -17,6 +17,7 @@ public: void setVerticalAxis(DiveCartesianAxis *axis); void setHorizontalAxis(DiveCartesianAxis *axis); void setModel(DivePlotDataModel *model); + bool shouldBeHidden(); public slots: void recalculatePos(bool instant = false); diff --git a/qt-ui/profile/diveplotdatamodel.cpp b/qt-ui/profile/diveplotdatamodel.cpp index 7412a7764..ad2859b9e 100644 --- a/qt-ui/profile/diveplotdatamodel.cpp +++ b/qt-ui/profile/diveplotdatamodel.cpp @@ -3,8 +3,6 @@ #include "display.h" #include "profile.h" #include "graphicsview-common.h" -#include "dive.h" -#include "display.h" #include "divelist.h" #include <QDebug> @@ -153,7 +151,7 @@ int DivePlotDataModel::id() const return diveId; } -int DivePlotDataModel::dcShown() const +unsigned int DivePlotDataModel::dcShown() const { return dcNr; } @@ -183,7 +181,7 @@ void DivePlotDataModel::calculateDecompression() struct dive *d = getDiveById(id()); if (!d) return; - struct divecomputer *dc = select_dc(&d->dc); + struct divecomputer *dc = select_dc(d); init_decompression(d); calculate_deco_information(d, dc, &pInfo, false); dataChanged(index(0, CEILING), index(pInfo.nr - 1, TISSUE_16)); diff --git a/qt-ui/profile/diveplotdatamodel.h b/qt-ui/profile/diveplotdatamodel.h index 272c0d1c3..35805256d 100644 --- a/qt-ui/profile/diveplotdatamodel.h +++ b/qt-ui/profile/diveplotdatamodel.h @@ -55,7 +55,7 @@ public: void setDive(struct dive *d, const plot_info &pInfo); const plot_info &data() const; int id() const; - int dcShown() const; + unsigned int dcShown() const; double pheMax(); double pn2Max(); double po2Max(); @@ -65,7 +65,7 @@ public: private: plot_info pInfo; int diveId; - int dcNr; + unsigned int dcNr; }; #endif // DIVEPLOTDATAMODEL_H diff --git a/qt-ui/profile/diveprofileitem.cpp b/qt-ui/profile/diveprofileitem.cpp index b84f2928d..2ffc7ec02 100644 --- a/qt-ui/profile/diveprofileitem.cpp +++ b/qt-ui/profile/diveprofileitem.cpp @@ -636,17 +636,29 @@ void PartialPressureGasItem::modelDataChanged(const QModelIndex &topLeft, const plot_data *entry = dataModel->data().entry; QPolygonF poly; - alertPoly.clear(); + QPolygonF alertpoly; + alertPolygons.clear(); QSettings s; s.beginGroup("TecDetails"); double threshould = s.value(threshouldKey).toDouble(); + bool inAlertFragment = false; for (int i = 0; i < dataModel->rowCount(); i++, entry++) { double value = dataModel->index(i, vDataColumn).data().toDouble(); int time = dataModel->index(i, hDataColumn).data().toInt(); QPointF point(hAxis->posAtValue(time), vAxis->posAtValue(value)); poly.push_back(point); - if (value >= threshould) - alertPoly.push_back(point); + if (value >= threshould) { + if (inAlertFragment) { + alertPolygons.back().push_back(point); + } else { + alertpoly.clear(); + alertpoly.push_back(point); + alertPolygons.append(alertpoly); + inAlertFragment = true; + } + } else { + inAlertFragment = false; + } } setPolygon(poly); /* @@ -656,10 +668,15 @@ void PartialPressureGasItem::modelDataChanged(const QModelIndex &topLeft, const void PartialPressureGasItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - painter->setPen(normalColor); + const qreal pWidth = 0.0; + painter->setPen(QPen(normalColor, pWidth)); painter->drawPolyline(polygon()); - painter->setPen(alertColor); - painter->drawPolyline(alertPoly); + + QPolygonF poly; + painter->setPen(QPen(alertColor, pWidth)); + Q_FOREACH(const QPolygonF & poly, alertPolygons) + painter->drawPolyline(poly); + } void PartialPressureGasItem::setThreshouldSettingsKey(const QString &threshouldSettingsKey) diff --git a/qt-ui/profile/diveprofileitem.h b/qt-ui/profile/diveprofileitem.h index ee7132c40..480776546 100644 --- a/qt-ui/profile/diveprofileitem.h +++ b/qt-ui/profile/diveprofileitem.h @@ -177,7 +177,7 @@ public: void setColors(const QColor &normalColor, const QColor &alertColor); private: - QPolygonF alertPoly; + QVector<QPolygonF> alertPolygons; QString threshouldKey; QString visibilityKey; QColor normalColor; diff --git a/qt-ui/profile/divetooltipitem.cpp b/qt-ui/profile/divetooltipitem.cpp index c11266a5a..0e68685b9 100644 --- a/qt-ui/profile/divetooltipitem.cpp +++ b/qt-ui/profile/divetooltipitem.cpp @@ -184,10 +184,9 @@ void ToolTipItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) void ToolTipItem::persistPos() { - QPoint currentPos = scene()->views().at(0)->mapFromScene(pos()); QSettings s; s.beginGroup("ProfileMap"); - s.setValue("tooltip_position", currentPos); + s.setValue("tooltip_position", pos()); s.endGroup(); } @@ -195,8 +194,7 @@ void ToolTipItem::readPos() { QSettings s; s.beginGroup("ProfileMap"); - QPointF value = scene()->views().at(0)->mapToScene( - s.value("tooltip_position").toPoint()); + QPointF value = s.value("tooltip_position").toPoint(); if (!scene()->sceneRect().contains(value)) { value = QPointF(0, 0); } diff --git a/qt-ui/profile/profilewidget2.cpp b/qt-ui/profile/profilewidget2.cpp index 2cdff1cff..a58244689 100644 --- a/qt-ui/profile/profilewidget2.cpp +++ b/qt-ui/profile/profilewidget2.cpp @@ -13,6 +13,7 @@ #include "planner.h" #include "device.h" #include "ruleritem.h" +#include <libdivecomputer/parser.h> #include <QSignalTransition> #include <QPropertyAnimation> #include <QMenu> @@ -21,6 +22,7 @@ #include <QSettings> #include <QScrollBar> #include <QtCore/qmath.h> +#include <QMessageBox> #ifndef QT_NO_DEBUG #include <QTableView> @@ -81,7 +83,9 @@ ProfileWidget2::ProfileWidget2(QWidget *parent) : QGraphicsView(parent), po2GasItem(new PartialPressureGasItem()), heartBeatAxis(new DiveCartesianAxis()), heartBeatItem(new DiveHeartrateItem()), - rulerItem(new RulerItem2()) + rulerItem(new RulerItem2()), + isGrayscale(false), + printMode(false) { memset(&plotInfo, 0, sizeof(plotInfo)); @@ -139,10 +143,11 @@ void ProfileWidget2::setupItemOnScene() profileYAxis->setOrientation(DiveCartesianAxis::TopToBottom); profileYAxis->setMinimum(0); profileYAxis->setTickInterval(M_OR_FT(10, 30)); - profileYAxis->setTickSize(1); + profileYAxis->setTickSize(0.5); profileYAxis->setLineSize(96); timeAxis->setLineSize(92); + timeAxis->setTickSize(-0.5); gasYAxis->setOrientation(DiveCartesianAxis::BottomToTop); gasYAxis->setTickInterval(1); @@ -173,7 +178,7 @@ void ProfileWidget2::setupItemOnScene() meanDepth->setAxis(profileYAxis); diveComputerText->setAlignment(Qt::AlignRight | Qt::AlignTop); - diveComputerText->setBrush(getColor(TIME_TEXT)); + diveComputerText->setBrush(getColor(TIME_TEXT, isGrayscale)); rulerItem->setAxis(timeAxis, profileYAxis); @@ -193,7 +198,7 @@ void ProfileWidget2::setupItemOnScene() setupItem(ITEM, timeAxis, gasYAxis, dataModel, DivePlotDataModel::VERTICAL_COLUMN, DivePlotDataModel::TIME, 0); \ ITEM->setThreshouldSettingsKey(THRESHOULD_SETTINGS); \ ITEM->setVisibilitySettingsKey(VISIBILITY_SETTINGS); \ - ITEM->setColors(getColor(COLOR), getColor(COLOR_ALERT)); \ + ITEM->setColors(getColor(COLOR, isGrayscale), getColor(COLOR_ALERT, isGrayscale)); \ ITEM->preferencesChanged(); \ ITEM->setZValue(99); @@ -335,13 +340,21 @@ void ProfileWidget2::plotDives(QList<dive *> dives) firstCall = false; } - // restore default zoom level and tooltip position + // restore default zoom level if (zoomLevel) { const qreal defScale = 1.0 / qPow(zoomFactor, (qreal)zoomLevel); scale(defScale, defScale); zoomLevel = 0; } - toolTipItem->setPos(0, 0); + + // reset some item visibility on printMode changes + toolTipItem->setVisible(!printMode); + QSettings s; + s.beginGroup("TecDetails"); + const bool rulerVisible = s.value("rulergraph", false).toBool() && !printMode; + rulerItem->setVisible(rulerVisible); + rulerItem->sourceNode()->setVisible(rulerVisible); + rulerItem->destNode()->setVisible(rulerVisible); // No need to do this again if we are already showing the same dive // computer of the same dive, so we check the unique id of the dive @@ -357,7 +370,7 @@ void ProfileWidget2::plotDives(QList<dive *> dives) // next get the dive computer structure - if there are no samples // let's create a fake profile that's somewhat reasonable for the // data that we have - struct divecomputer *currentdc = select_dc(&d->dc); + struct divecomputer *currentdc = select_dc(d); Q_ASSERT(currentdc); if (!currentdc || !currentdc->samples) { currentdc = fake_dc(currentdc); @@ -416,6 +429,10 @@ void ProfileWidget2::plotDives(QList<dive *> dives) cylinderPressureAxis->setMaximum(pInfo.maxpressure); rulerItem->setPlotInfo(pInfo); + if (prefs.show_average_depth) + meanDepth->setVisible(true); + else + meanDepth->setVisible(false); meanDepth->setMeanDepth(pInfo.meandepth); meanDepth->setLine(0, 0, timeAxis->posAtValue(d->duration.seconds), 0); meanDepth->animateMoveTo(3, profileYAxis->posAtValue(pInfo.meandepth)); @@ -438,10 +455,10 @@ void ProfileWidget2::plotDives(QList<dive *> dives) eventItems.push_back(item); event = event->next; } - // Only set visible the ones that should be visible, but how? + // Only set visible the events that should be visible Q_FOREACH(DiveEventItem * event, eventItems) { - event->setVisible(true); - // qDebug() << event->getEvent()->name << "@" << event->getEvent()->time.seconds; + event->setVisible(!event->shouldBeHidden()); + // qDebug() << event->getEvent()->name << "@" << event->getEvent()->time.seconds << "is hidden:" << event->isHidden(); } diveComputerText->setText(currentdc->model); if (MainWindow::instance()->filesFromCommandLine() && animSpeedBackup != -1){ @@ -474,6 +491,7 @@ void ProfileWidget2::settingsChanged() rulerItem->setVisible(rulerVisible); rulerItem->destNode()->setVisible(rulerVisible); rulerItem->sourceNode()->setVisible(rulerVisible); + replot(); } else { rulerItem->setVisible(false); rulerItem->destNode()->setVisible(false); @@ -581,6 +599,9 @@ void ProfileWidget2::setEmptyState() rulerItem->setVisible(false); rulerItem->destNode()->setVisible(false); rulerItem->sourceNode()->setVisible(false); + pn2GasItem->setVisible(false); + po2GasItem->setVisible(false); + pheGasItem->setVisible(false); Q_FOREACH(DiveCalculatedTissue * tissue, allTissues) { tissue->setVisible(false); } @@ -598,7 +619,7 @@ void ProfileWidget2::setProfileState() currentState = PROFILE; MainWindow::instance()->setToolButtonsEnabled(true); toolTipItem->readPos(); - setBackgroundBrush(getColor(::BACKGROUND)); + setBackgroundBrush(getColor(::BACKGROUND, isGrayscale)); background->setVisible(false); toolTipItem->setVisible(true); @@ -620,6 +641,9 @@ void ProfileWidget2::setProfileState() temperatureAxis->setLine(itemPos.temperature.expanded); cylinderPressureAxis->setLine(itemPos.cylinder.expanded); } + pn2GasItem->setVisible(s.value("pn2graph").toBool()); + po2GasItem->setVisible(s.value("po2graph").toBool()); + pheGasItem->setVisible(s.value("phegraph").toBool()); gasYAxis->setPos(itemPos.partialPressure.pos.on); gasYAxis->setLine(itemPos.partialPressure.expanded); @@ -673,11 +697,8 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) } QAction *action = m.addAction(tr("Add Bookmark"), this, SLOT(addBookmark())); action->setData(event->globalPos()); - QList<QGraphicsItem *> itemsAtPos = scene()->items(mapToScene(mapFromGlobal(event->globalPos()))); - Q_FOREACH(QGraphicsItem * i, itemsAtPos) { - DiveEventItem *item = dynamic_cast<DiveEventItem *>(i); - if (!item) - continue; + QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos())); + if (DiveEventItem *item = dynamic_cast<DiveEventItem *>(sceneItem)) { action = new QAction(&m); action->setText(tr("Remove Event")); action->setData(QVariant::fromValue<void *>(item)); // so we know what to remove. @@ -688,7 +709,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) action->setData(QVariant::fromValue<void *>(item)); connect(action, SIGNAL(triggered(bool)), this, SLOT(hideEvents())); m.addAction(action); - break; } bool some_hidden = false; for (int i = 0; i < evn_used; i++) { @@ -704,13 +724,73 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) m.exec(event->globalPos()); } +void ProfileWidget2::hideEvents() +{ + QAction *action = qobject_cast<QAction *>(sender()); + DiveEventItem *item = static_cast<DiveEventItem *>(action->data().value<void *>()); + struct event *event = item->getEvent(); + + if (QMessageBox::question(MainWindow::instance(), + TITLE_OR_TEXT(tr("Hide events"), tr("Hide all %1 events?").arg(event->name)), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { + if (event->name) { + for (int i = 0; i < evn_used; i++) { + if (!strcmp(event->name, ev_namelist[i].ev_name)) { + ev_namelist[i].plot_ev = false; + break; + } + } + } + item->hide(); + replot(); + } +} + +void ProfileWidget2::unhideEvents() +{ + for (int i = 0; i < evn_used; i++) { + ev_namelist[i].plot_ev = true; + } + replot(); +} + +void ProfileWidget2::removeEvent() +{ + QAction *action = qobject_cast<QAction *>(sender()); + DiveEventItem *item = static_cast<DiveEventItem *>(action->data().value<void *>()); + struct event *event = item->getEvent(); + + if (QMessageBox::question(MainWindow::instance(), TITLE_OR_TEXT( + tr("Remove the selected event?"), + tr("%1 @ %2:%3").arg(event->name).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), + QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) { + struct event **ep = ¤t_dc->events; + while (ep && *ep != event) + ep = &(*ep)->next; + if (ep) { + *ep = event->next; + free(event); + } + mark_divelist_changed(true); + replot(); + } +} + +void ProfileWidget2::addBookmark() +{ + QAction *action = qobject_cast<QAction *>(sender()); + QPointF scenePos = mapToScene(mapFromGlobal(action->data().toPoint())); + add_event(current_dc, timeAxis->valueAt(scenePos), SAMPLE_EVENT_BOOKMARK, 0, 0, "bookmark"); + replot(); +} + void ProfileWidget2::changeGas() { QAction *action = qobject_cast<QAction *>(sender()); QPointF scenePos = mapToScene(mapFromGlobal(action->data().toPoint())); QString gas = action->text(); // backup the things on the dataModel, since we will clear that out. - int diveComputer = dataModel->dcShown(); + unsigned int diveComputer = dataModel->dcShown(); int diveId = dataModel->id(); int o2, he; int seconds = timeAxis->valueAt(scenePos); @@ -724,3 +804,9 @@ void ProfileWidget2::changeGas() mark_divelist_changed(true); replot(); } + +void ProfileWidget2::setPrintMode(bool mode, bool grayscale) +{ + printMode = mode; + isGrayscale = mode ? grayscale : false; +} diff --git a/qt-ui/profile/profilewidget2.h b/qt-ui/profile/profilewidget2.h index b5339edef..eb7486e8d 100644 --- a/qt-ui/profile/profilewidget2.h +++ b/qt-ui/profile/profilewidget2.h @@ -67,6 +67,7 @@ public: void replot(); virtual bool eventFilter(QObject *, QEvent *); void setupItem(AbstractProfilePolygonItem *item, DiveCartesianAxis *hAxis, DiveCartesianAxis *vAxis, DivePlotDataModel *model, int vData, int hData, int zValue); + void setPrintMode(bool mode, bool grayscale = false); public slots: // Necessary to call from QAction's signals. @@ -74,6 +75,10 @@ slots: // Necessary to call from QAction's signals. void setEmptyState(); void setProfileState(); void changeGas(); + void addBookmark(); + void hideEvents(); + void unhideEvents(); + void removeEvent(); protected: virtual void resizeEvent(QResizeEvent *event); @@ -122,6 +127,8 @@ private: DiveCartesianAxis *heartBeatAxis; DiveHeartrateItem *heartBeatItem; RulerItem2 *rulerItem; + bool isGrayscale; + bool printMode; }; #endif // PROFILEWIDGET2_H diff --git a/qt-ui/profile/ruleritem.cpp b/qt-ui/profile/ruleritem.cpp index 627958e37..768d912e9 100644 --- a/qt-ui/profile/ruleritem.cpp +++ b/qt-ui/profile/ruleritem.cpp @@ -42,7 +42,7 @@ void RulerNodeItem2::recalculate() if (x() < 0) { setPos(0, y()); } else if (x() > timeAxis->posAtValue(data->sec)) { - setPos(timeAxis->posAtValue(data->sec), y()); + setPos(timeAxis->posAtValue(data->sec), depthAxis->posAtValue(data->depth)); } else { data = pInfo.entry; count = 0; @@ -77,6 +77,8 @@ RulerItem2::RulerItem2() : source(new RulerNodeItem2()), textItem->setFlag(QGraphicsItem::ItemIgnoresTransformations); textItemBack->setBrush(QColor(0xff, 0xff, 0xff, 190)); textItemBack->setPen(QColor(Qt::white)); + textItemBack->setFlag(QGraphicsItem::ItemIgnoresTransformations); + setPen(QPen(QColor(Qt::black), 0.0)); } void RulerItem2::recalculate() @@ -116,13 +118,13 @@ void RulerItem2::recalculate() } // always show the text bellow the lowest of the start and end points qreal tgtY = (startPoint.y() >= endPoint.y()) ? startPoint.y() : endPoint.y(); - textItem->setPos(tgtX - 1, tgtY + 4); + // this isn't exactly optimal, since we want to scale the 1.0, 4.0 distances as well + textItem->setPos(tgtX - 1.0, tgtY + 4.0); // setup the text background textItemBack->setVisible(startPoint.x() != endPoint.x()); - QPointF wh = mapFromScene(view->mapToScene(QPoint(textItem->boundingRect().width(), - textItem->boundingRect().height()))); - textItemBack->setRect(tgtX - 2, tgtY + 3, wh.x() + 2, wh.y() + 3); + textItemBack->setPos(textItem->x(), textItem->y()); + textItemBack->setRect(0, 0, textItem->boundingRect().width(), textItem->boundingRect().height()); } RulerNodeItem2 *RulerItem2::sourceNode() const diff --git a/qt-ui/shifttimes.ui b/qt-ui/shifttimes.ui index 55932a2d3..8aaa247ce 100644 --- a/qt-ui/shifttimes.ui +++ b/qt-ui/shifttimes.ui @@ -10,7 +10,7 @@ <x>0</x> <y>0</y> <width>343</width> - <height>177</height> + <height>224</height> </rect> </property> <property name="sizePolicy"> @@ -65,6 +65,38 @@ <number>9</number> </property> <item> + <layout class="QFormLayout" name="formLayout"> + <item row="2" column="0"> + <widget class="QLabel" name="shiftedTimeLabel"> + <property name="text"> + <string>Shifted time:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="currentTimeLabel"> + <property name="text"> + <string>Current time:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="currentTime"> + <property name="text"> + <string>0:0</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="shiftedTime"> + <property name="text"> + <string>0:0</string> + </property> + </widget> + </item> + </layout> + </item> + <item> <widget class="QTimeEdit" name="timeEdit"> <property name="date"> <date> @@ -166,5 +198,17 @@ </hint> </hints> </connection> + <connection> + <sender>timeEdit</sender> + <signal>timeChanged(const QTime)</signal> + <receiver>ShiftTimesDialog</receiver> + <slot>changeTime()</slot> + </connection> + <connection> + <sender>backwards</sender> + <signal>toggled(bool)</signal> + <receiver>ShiftTimesDialog</receiver> + <slot>changeTime()</slot> + </connection> </connections> </ui> diff --git a/qt-ui/simplewidgets.cpp b/qt-ui/simplewidgets.cpp index 2ec984b7a..be3add815 100644 --- a/qt-ui/simplewidgets.cpp +++ b/qt-ui/simplewidgets.cpp @@ -18,6 +18,7 @@ #include "../dive.h" #include "../file.h" #include "mainwindow.h" +#include "helpers.h" class MinMaxAvgWidgetPrivate { public: @@ -155,10 +156,31 @@ void ShiftTimesDialog::buttonClicked(QAbstractButton *button) } } +void ShiftTimesDialog::showEvent(QShowEvent * event) +{ + ui.timeEdit->setTime(QTime(0, 0, 0, 0)); + when = get_times();//get time of first selected dive + ui.currentTime->setText(get_dive_date_string(when)); + ui.shiftedTime->setText(get_dive_date_string(when)); +} + +void ShiftTimesDialog::changeTime() +{ + int amount; + + amount = ui.timeEdit->time().hour() * 3600 + ui.timeEdit->time().minute() * 60; + if (ui.backwards->isChecked()) + amount *= -1; + + ui.shiftedTime->setText (get_dive_date_string(amount+when)); +} + ShiftTimesDialog::ShiftTimesDialog(QWidget *parent) : QDialog(parent) { ui.setupUi(this); connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton *)), this, SLOT(buttonClicked(QAbstractButton *))); + connect(ui.timeEdit, SIGNAL(timeChanged(const QTime)), this, SLOT(changeTime())); + connect(ui.backwards, SIGNAL(toggled(bool)), this, SLOT(changeTime())); } void ShiftImageTimesDialog::buttonClicked(QAbstractButton *button) diff --git a/qt-ui/simplewidgets.h b/qt-ui/simplewidgets.h index 6e4166f35..d43d2bbcc 100644 --- a/qt-ui/simplewidgets.h +++ b/qt-ui/simplewidgets.h @@ -6,6 +6,7 @@ class QAbstractButton; #include <QWidget> #include <QDialog> +#include <stdint.h> #include "ui_renumber.h" #include "ui_shifttimes.h" @@ -52,12 +53,15 @@ class ShiftTimesDialog : public QDialog { Q_OBJECT public: static ShiftTimesDialog *instance(); + void showEvent ( QShowEvent * event ); private slots: void buttonClicked(QAbstractButton *button); + void changeTime (); private: explicit ShiftTimesDialog(QWidget *parent); + int64_t when; Ui::ShiftTimesDialog ui; }; diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 93b452475..707746573 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -103,11 +103,11 @@ static void clear_table(struct dive_table *table) table->nr = 0; } -bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, const bool selected, QString *errorMsg) +bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, const bool selected) { static const char errPrefix[] = "divelog.de-upload:"; if (!amount_selected) { - *errorMsg = tr("no dives were selected"); + report_error(tr("no dives were selected").toUtf8()); return false; } @@ -126,8 +126,7 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, if (!zip) { char buffer[1024]; zip_error_to_str(buffer, sizeof buffer, error_code, errno); - *errorMsg = tr("failed to create zip file for upload: %1") - .arg(QString::fromLocal8Bit(buffer)); + report_error(tr("failed to create zip file for upload: %s").toUtf8(), buffer); return false; } @@ -151,7 +150,7 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, continue; f = tmpfile(); if (!f) { - *errorMsg = tr("cannot create temporary file: %1").arg(qt_error_string()); + report_error(tr("cannot create temporary file: %s").toUtf8(), qt_error_string().toUtf8().data()); goto error_close_zip; } save_dive(f, dive); @@ -161,7 +160,7 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, membuf = (char *)malloc(streamsize + 1); if (!membuf || (streamsize = fread(membuf, streamsize, 1, f)) == 0) { - *errorMsg = tr("internal error: %1").arg(qt_error_string()); + report_error(tr("internal error: %s").toUtf8(), qt_error_string().toUtf8().data()); fclose(f); free((void *)membuf); goto error_close_zip; @@ -177,7 +176,7 @@ bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, xmlDoc *doc = xmlReadMemory(membuf, streamsize, "divelog", NULL, 0); if (!doc) { qWarning() << errPrefix << "could not parse back into memory the XML file we've just created!"; - *errorMsg = tr("internal error"); + report_error(tr("internal error").toUtf8()); free((void *)membuf); goto error_close_zip; } @@ -328,7 +327,7 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton *button) case QDialogButtonBox::ApplyRole: { clear_table(&gps_location_table); QByteArray url = tr("Webservice").toLocal8Bit(); - parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL, NULL); + parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL); /* now merge the data in the gps_location table into the dive_table */ if (merge_locations_into_dives()) { @@ -580,11 +579,9 @@ void DivelogsDeWebServices::downloadDives() void DivelogsDeWebServices::prepareDivesForUpload() { - QString errorText; - /* generate a random filename and create/open that file with zip_open */ QString filename = QDir::tempPath() + "/import-" + QString::number(qrand() % 99999999) + ".dld"; - if (prepare_dives_for_divelogs(filename, true, &errorText)) { + if (prepare_dives_for_divelogs(filename, true)) { QFile f(filename); if (f.open(QIODevice::ReadOnly)) { uploadDives((QIODevice *)&f); @@ -593,7 +590,7 @@ void DivelogsDeWebServices::prepareDivesForUpload() return; } } - MainWindow::instance()->showError(errorText); + MainWindow::instance()->showError(get_error_string()); } void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) @@ -861,12 +858,7 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton *button) break; } /* parse file and import dives */ - char *error = NULL; - parse_file(QFile::encodeName(zipFile.fileName()), &error); - if (error != NULL) { - MainWindow::instance()->showError(error); - free(error); - } + parse_file(QFile::encodeName(zipFile.fileName())); process_dives(true, false); MainWindow::instance()->refreshDisplay(); diff --git a/qt-ui/subsurfacewebservices.h b/qt-ui/subsurfacewebservices.h index 872cf377d..175c99109 100644 --- a/qt-ui/subsurfacewebservices.h +++ b/qt-ui/subsurfacewebservices.h @@ -89,7 +89,7 @@ private: void uploadDives(QIODevice *dldContent); explicit DivelogsDeWebServices(QWidget *parent = 0, Qt::WindowFlags f = 0); void setStatusText(int status); - bool prepare_dives_for_divelogs(const QString &filename, bool selected, QString *errorMsg); + bool prepare_dives_for_divelogs(const QString &filename, bool selected); void download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status); unsigned int download_dialog_parse_response(const QByteArray &length); diff --git a/qt-ui/tagwidget.cpp b/qt-ui/tagwidget.cpp index 6ba4a0404..fbebe3f35 100644 --- a/qt-ui/tagwidget.cpp +++ b/qt-ui/tagwidget.cpp @@ -35,7 +35,7 @@ void TagWidget::setCompleter(QCompleter *completer) m_completer = completer; m_completer->setWidget(this); connect(m_completer, SIGNAL(activated(QString)), this, SLOT(completionSelected(QString))); - connect(m_completer, SIGNAL(highlighted(QString)), this, SLOT(completionSelected(QString))); + connect(m_completer, SIGNAL(highlighted(QString)), this, SLOT(completionHighlighted(QString))); } QPair<int, int> TagWidget::getCursorTagPosition() @@ -120,8 +120,7 @@ void TagWidget::reparse() QString currentText; if (pos.first >= 0 && pos.second > 0) currentText = text().mid(pos.first, pos.second - pos.first).trimmed(); - else - currentText = ""; + if (m_completer) { m_completer->setCompletionPrefix(currentText); if (m_completer->completionCount() == 1) { @@ -138,18 +137,22 @@ void TagWidget::reparse() } } -void TagWidget::completionSelected(QString completion) +void TagWidget::completionSelected(const QString& completion) { - QPair<int, int> pos; - pos = getCursorTagPosition(); + completionHighlighted(completion); + emit textChanged(); +} + +void TagWidget::completionHighlighted(const QString& completion) +{ + QPair<int, int> pos = getCursorTagPosition(); if (pos.first >= 0 && pos.second > 0) { setText(text().remove(pos.first, pos.second - pos.first).insert(pos.first, completion)); setCursorPosition(pos.first + completion.length()); } else { - setText(completion.append(", ")); + setText(completion + QString(", ")); setCursorPosition(text().length()); } - emit(textChanged()); } void TagWidget::setCursorPosition(int position) @@ -159,7 +162,7 @@ void TagWidget::setCursorPosition(int position) blockSignals(false); } -void TagWidget::setText(QString text) +void TagWidget::setText(const QString& text) { blockSignals(true); GroupedLineEdit::setText(text); @@ -176,7 +179,19 @@ void TagWidget::clear() void TagWidget::keyPressEvent(QKeyEvent *e) { + QPair<int, int> pos; + QAbstractItemView *popup; switch (e->key()) { + case Qt::Key_Escape: + pos = getCursorTagPosition(); + if (pos.first >= 0 && pos.second > 0) { + setText(text().remove(pos.first, pos.second - pos.first)); + setCursorPosition(pos.first); + } + popup= m_completer->popup(); + if (popup) + popup->hide(); + return; case Qt::Key_Return: case Qt::Key_Enter: case Qt::Key_Tab: @@ -185,12 +200,12 @@ void TagWidget::keyPressEvent(QKeyEvent *e) * closing the QAbstractViewitem */ if (m_completer) { - QAbstractItemView *popup = m_completer->popup(); + popup = m_completer->popup(); if (popup) popup->hide(); } } - if (e->key() == Qt::Key_Tab) { // let's pretend this is a comma instead + if (e->key() == Qt::Key_Tab || e->key() == Qt::Key_Return) { // let's pretend this is a comma instead QKeyEvent fakeEvent(e->type(), Qt::Key_Comma, e->modifiers(), QString(",")); GroupedLineEdit::keyPressEvent(&fakeEvent); } else { diff --git a/qt-ui/tagwidget.h b/qt-ui/tagwidget.h index 161ec0601..62fa36f30 100644 --- a/qt-ui/tagwidget.h +++ b/qt-ui/tagwidget.h @@ -12,14 +12,15 @@ public: void setCompleter(QCompleter *completer); QPair<int, int> getCursorTagPosition(); void highlight(); - void setText(QString text); + void setText(const QString& text); void clear(); void setCursorPosition(int position); void wheelEvent(QWheelEvent *event); public slots: void reparse(); - void completionSelected(QString); + void completionSelected(const QString& text); + void completionHighlighted(const QString& text); protected: void keyPressEvent(QKeyEvent *e); diff --git a/qt-ui/usermanual.h b/qt-ui/usermanual.h index 280ca3896..7692a1143 100644 --- a/qt-ui/usermanual.h +++ b/qt-ui/usermanual.h @@ -29,5 +29,4 @@ private: Ui::UserManual *ui; void search(QString, QWebPage::FindFlags); }; - #endif // USERMANUAL_H diff --git a/save-git.c b/save-git.c index 4760fe32a..9b0d2dbba 100644 --- a/save-git.c +++ b/save-git.c @@ -15,6 +15,18 @@ #include "membuffer.h" #include "ssrf-version.h" +/* + * handle libgit2 revision 0.20 and earlier + */ +#if !LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR <= 20 && !defined(USE_LIBGIT21_API) + #define GIT_CHECKOUT_OPTIONS_INIT GIT_CHECKOUT_OPTS_INIT + #define git_checkout_options git_checkout_opts + #define git_branch_create(out,repo,branch_name,target,force,sig,msg) \ + git_branch_create(out,repo,branch_name,target,force) + #define git_reference_set_target(out,ref,target,signature,log_message) \ + git_reference_set_target(out,ref,target) +#endif + #define VA_BUF(b, fmt) do { va_list args; va_start(args, fmt); put_vformat(b, fmt, args); va_end(args); } while (0) static void cond_put_format(int cond, struct membuffer *b, const char *fmt, ...) @@ -340,16 +352,35 @@ static void create_dive_buffer(struct dive *dive, struct membuffer *b) save_dive_temperature(b, dive); } -int report_error(const char *fmt, ...) +static struct membuffer error_string_buffer = { 0 }; + +/* + * Note that the act of "getting" the error string + * buffer doesn't de-allocate the buffer, but it does + * set the buffer length to zero, so that any future + * error reports will overwrite the string rather than + * append to it. + */ +const char *get_error_string(void) { - struct membuffer b = { 0 }; - VA_BUF(&b, fmt); + const char *str; + + if (!error_string_buffer.len) + return ""; + str = mb_cstring(&error_string_buffer); + error_string_buffer.len = 0; + return str; +} - /* We should do some UI element thing describing the failure */ - put_bytes(&b, "\n", 1); - flush_buffer(&b, stderr); - free_buffer(&b); +int report_error(const char *fmt, ...) +{ + struct membuffer *buf = &error_string_buffer; + /* Previous unprinted errors? Add a newline in between */ + if (buf->len) + put_bytes(buf, "\n", 1); + VA_BUF(buf, fmt); + mb_cstring(buf); return -1; } @@ -741,15 +772,53 @@ static int create_git_tree(git_repository *repo, struct dir *root, bool select_o } /* - * libgit2 revision 0.20 and earlier do not have the signature and - * message log arguments. + * See if we can find the parent ID that the git data came from */ -#if !LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR <= 20 && !defined(USE_LIBGIT21_API) - #define git_branch_create(out,repo,branch_name,target,force,sig,msg) \ - git_branch_create(out,repo,branch_name,target,force) - #define git_reference_set_target(out,ref,target,signature,log_message) \ - git_reference_set_target(out,ref,target) -#endif +static git_object *try_to_find_parent(const char *hex_id, git_repository *repo) +{ + git_oid object_id; + git_commit *commit; + + if (!hex_id) + return NULL; + if (git_oid_fromstr(&object_id, hex_id)) + return NULL; + if (git_commit_lookup(&commit, repo, &object_id)) + return NULL; + return (git_object *)commit; +} + +static int notify_cb(git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload) +{ + report_error("File '%s' does not match in working tree", path); + return 0; /* Continue with checkout */ +} + +static git_tree *get_git_tree(git_repository *repo, git_object *parent) +{ + git_tree *tree; + if (!parent) + return NULL; + if (git_tree_lookup(&tree, repo, git_commit_tree_id((const git_commit *) parent))) + return NULL; + return tree; +} + +static int update_git_checkout(git_repository *repo, git_object *parent, git_tree *tree) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + + opts.checkout_strategy = GIT_CHECKOUT_SAFE; + opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_DIRTY; + opts.notify_cb = notify_cb; + opts.baseline = get_git_tree(repo, parent); + return git_checkout_tree(repo, (git_object *) tree, &opts); +} static int create_new_commit(git_repository *repo, const char *branch, git_oid *tree_id) { @@ -769,15 +838,18 @@ static int create_new_commit(git_repository *repo, const char *branch, git_oid * case GIT_EINVALIDSPEC: return report_error("Invalid branch name '%s'", branch); case GIT_ENOTFOUND: /* We'll happily create it */ - ref = NULL; parent = NULL; + ref = NULL; + parent = try_to_find_parent(saved_git_id, repo); break; case 0: if (git_reference_peel(&parent, ref, GIT_OBJ_COMMIT)) return report_error("Unable to look up parent in branch '%s'", branch); - /* If the parent commit has the same tree ID, do nothing */ - if (git_oid_equal(tree_id, git_commit_tree_id((const git_commit *) parent))) - return 0; + if (saved_git_id) { + const git_oid *id = git_commit_id((const git_commit *) parent); + if (git_oid_strcmp(id, saved_git_id)) + return report_error("The git branch does not match the git parent of the source"); + } /* all good */ break; @@ -790,19 +862,45 @@ static int create_new_commit(git_repository *repo, const char *branch, git_oid * if (git_signature_now(&author, "Subsurface", "subsurface@hohndel.org")) return report_error("No user name configuration in git repo"); - put_format(&commit_msg, "Created by subsurface %s\n", VERSION_STRING); - if (git_commit_create_v(&commit_id, repo, NULL, author, author, NULL, mb_cstring(&commit_msg), tree, parent != NULL, parent)) - return report_error("Git commit create failed (%s)", strerror(errno)); - - if (git_commit_lookup(&commit, repo, &commit_id)) - return report_error("Could not look up newly created commit"); + /* If the parent commit has the same tree ID, do not create a new commit */ + if (parent && git_oid_equal(tree_id, git_commit_tree_id((const git_commit *) parent))) { + /* If the parent already came from the ref, the commit is already there */ + if (ref) + return 0; + /* Else we do want to create the new branch, but with the old commit */ + commit = (git_commit *) parent; + } else { + put_format(&commit_msg, "Created by subsurface %s\n", VERSION_STRING); + if (git_commit_create_v(&commit_id, repo, NULL, author, author, NULL, mb_cstring(&commit_msg), tree, parent != NULL, parent)) + return report_error("Git commit create failed (%s)", strerror(errno)); + + if (git_commit_lookup(&commit, repo, &commit_id)) + return report_error("Could not look up newly created commit"); + } if (!ref) { if (git_branch_create(&ref, repo, branch, commit, 0, author, "Create branch")) return report_error("Failed to create branch '%s'", branch); } + /* + * If it's a checked-out branch, try to also update the working + * tree and index. If that fails (dirty working tree or whatever), + * this is not technically a save error (we did save things in + * the object database), but it can cause extreme confusion, so + * warn about it. + */ + if (git_branch_is_head(ref) && !git_repository_is_bare(repo)) { + if (update_git_checkout(repo, parent, tree)) { + const git_error *err = giterr_last(); + const char *errstr = err ? err->message : strerror(errno); + report_error("Git branch '%s' is checked out, but worktree is dirty (%s)", + branch, errstr); + } + } + if (git_reference_set_target(&ref, ref, &commit_id, author, "Subsurface save event")) return report_error("Failed to update branch '%s'", branch); + set_git_id(&commit_id); return 0; } @@ -850,8 +948,7 @@ static int do_git_save(git_repository *repo, const char *branch, bool select_onl return report_error("git tree write failed"); /* And save the tree! */ - create_new_commit(repo, branch, &id); - return 0; + return create_new_commit(repo, branch, &id); } /* @@ -876,30 +973,45 @@ struct git_repository *is_git_repository(const char *filename, const char **bran if (!flen) return NULL; + /* + * This is the "point of no return": the name matches + * the git repository name rules, and we will no longer + * return NULL. + * + * We will either return "dummy_git_repository" and the + * branch pointer will have the _whole_ filename in it, + * or we will return a real git repository with the + * branch pointer being filled in with just the branch + * name. + * + * The actual git reading/writing routines can use this + * to generate proper error messages. + */ + *branchp = filename; loc = malloc(flen+1); if (!loc) - return NULL; + return dummy_git_repository; memcpy(loc, filename, flen); loc[flen] = 0; branch = malloc(blen+1); if (!branch) { free(loc); - return NULL; + return dummy_git_repository; } memcpy(branch, filename+flen+1, blen); branch[blen] = 0; if (stat(loc, &st) < 0 || !S_ISDIR(st.st_mode)) { free(loc); - return NULL; + return dummy_git_repository; } ret = git_repository_open(&repo, loc); free(loc); if (ret < 0) { free(branch); - return NULL; + return dummy_git_repository; } *branchp = branch; return repo; @@ -907,7 +1019,11 @@ struct git_repository *is_git_repository(const char *filename, const char **bran int git_save_dives(struct git_repository *repo, const char *branch, bool select_only) { - int ret = do_git_save(repo, branch, select_only); + int ret; + + if (repo == dummy_git_repository) + return report_error("Unable to open git repository '%s'", branch); + ret = do_git_save(repo, branch, select_only); git_repository_free(repo); free((void *)branch); return ret; diff --git a/save-xml.c b/save-xml.c index a27dcfa76..61a9300bb 100644 --- a/save-xml.c +++ b/save-xml.c @@ -426,12 +426,14 @@ void save_one_dive(struct membuffer *b, struct dive *dive) put_format(b, "</dive>\n"); } -void save_dive(FILE *f, struct dive *dive) +int save_dive(FILE *f, struct dive *dive) { struct membuffer buf = { 0 }; save_one_dive(&buf, dive); flush_buffer(&buf, f); + /* Error handling? */ + return 0; } static void save_trip(struct membuffer *b, dive_trip_t *trip) @@ -493,9 +495,9 @@ static void save_one_device(void *_f, const char *model, uint32_t deviceid, #define VERSION 2 -void save_dives(const char *filename) +int save_dives(const char *filename) { - save_dives_logic(filename, false); + return save_dives_logic(filename, false); } void save_dives_buffer(struct membuffer *b, const bool select_only) @@ -593,30 +595,36 @@ static void try_to_backup(const char *filename) } } -void save_dives_logic(const char *filename, const bool select_only) +int save_dives_logic(const char *filename, const bool select_only) { struct membuffer buf = { 0 }; FILE *f; void *git; const char *branch; + int error; git = is_git_repository(filename, &branch); - if (git && !git_save_dives(git, branch, select_only)) - return; + if (git) + return git_save_dives(git, branch, select_only); try_to_backup(filename); save_dives_buffer(&buf, select_only); + error = -1; f = subsurface_fopen(filename, "w"); if (f) { flush_buffer(&buf, f); - fclose(f); + error = fclose(f); } + if (error) + report_error("Save failed (%s)", strerror(errno)); + free_buffer(&buf); + return error; } -void export_dives_uddf(const char *filename, const bool selected) +int export_dives_uddf(const char *filename, const bool selected) { FILE *f; struct membuffer buf = { 0 }; @@ -625,7 +633,7 @@ void export_dives_uddf(const char *filename, const bool selected) xmlDoc *transformed; if (!filename) - return; + return report_error("No filename for UDDF export"); /* Save XML to file and convert it into a memory buffer */ save_dives_buffer(&buf, selected); @@ -637,26 +645,27 @@ void export_dives_uddf(const char *filename, const bool selected) */ doc = xmlReadMemory(buf.buffer, buf.len, "divelog", NULL, 0); free_buffer(&buf); - if (!doc) { - fprintf(stderr, "Failed to read XML memory\n"); - return; - } + if (!doc) + return report_error("Failed to read XML memory"); /* Convert to UDDF format */ xslt = get_stylesheet("uddf-export.xslt"); + if (!xslt) + return report_error("Failed to open UDDF conversion stylesheet"); - if (!xslt) { - fprintf(stderr, "Failed to open UDDF conversion stylesheet\n"); - return; - } transformed = xsltApplyStylesheet(xslt, doc, NULL); xsltFreeStylesheet(xslt); xmlFreeDoc(doc); /* Write the transformed XML to file */ f = subsurface_fopen(filename, "w"); + if (!f) + return report_error("Failed to open %s for writing (%s)", filename, strerror(errno)); + xmlDocFormatDump(f, transformed, 1); xmlFreeDoc(transformed); fclose(f); + /* Check write errors? */ + return 0; } diff --git a/subsurface-configure.pri b/subsurface-configure.pri index dff3b6f7d..5177c9d68 100644 --- a/subsurface-configure.pri +++ b/subsurface-configure.pri @@ -138,18 +138,27 @@ isEmpty(LIBGIT2DEVEL) { # Add libiconv if needed link_pkgconfig: packagesExist(libiconv): PKGCONFIG += libiconv +# disable things when were on android +contains(QMAKE_PLATFORM, android): DEFINES += NO_MARBLE NO_USERMANUAL NO_PRINTING + # # Find libmarble # # Before Marble 4.9, the GeoDataTreeModel.h header wasn't installed # Check if it's present by trying to compile # ### FIXME: implement that -win32: CONFIG(debug, debug|release): LIBS += -lmarblewidgetd -else: LIBS += -lmarblewidget +!contains(DEFINES, NO_MARBLE) { + win32: CONFIG(debug, debug|release): LIBS += -lmarblewidgetd + else: LIBS += -lmarblewidget +} libgit21-api { DEFINES += USE_LIBGIT21_API } + +win32: console { + DEFINES += WIN32_CONSOLE_APP +} # # Platform-specific changes # diff --git a/subsurface-install.pri b/subsurface-install.pri index e4a6e0a53..2dcb98113 100644 --- a/subsurface-install.pri +++ b/subsurface-install.pri @@ -120,6 +120,9 @@ mac { QMAKE_EXTRA_TARGETS = installer nsis install.depends += dlls } +} else: android { + # Android install rules + QMAKE_BUNDLE_DATA += translation qttranslation } else { # Linux install rules # On Linux, we can count on packagers doing the right thing diff --git a/subsurface.pro b/subsurface.pro index fe7061f55..3944da095 100644 --- a/subsurface.pro +++ b/subsurface.pro @@ -4,7 +4,8 @@ QT = core gui network svg lessThan(QT_MAJOR_VERSION, 5) { QT += webkit } else { - QT += webkitwidgets + !android: QT += webkitwidgets + android: QT += androidextras } INCLUDEPATH += qt-ui $$PWD DEPENDPATH += qt-ui @@ -27,6 +28,8 @@ HEADERS = \ helpers.h \ libdivecomputer.h \ planner.h \ + worldmap-save.h \ + worldmap-options.h \ pref.h \ profile.h \ qt-gui.h \ @@ -75,6 +78,12 @@ HEADERS = \ qt-ui/profile/divetooltipitem.h \ qt-ui/profile/ruleritem.h +android: HEADERS -= \ + qt-ui/usermanual.h \ + qt-ui/printdialog.h \ + qt-ui/printlayout.h \ + qt-ui/printoptions.h + SOURCES = \ deco.c \ device.c \ @@ -90,6 +99,7 @@ SOURCES = \ parse-xml.c \ planner.c \ profile.c \ + worldmap-save.c \ qt-gui.cpp \ qthelper.cpp \ qt-ui/about.cpp \ @@ -140,10 +150,17 @@ SOURCES = \ qt-ui/profile/divetooltipitem.cpp \ qt-ui/profile/ruleritem.cpp -linux*: SOURCES += linux.c +android: SOURCES += android.cpp +else: linux*: SOURCES += linux.c mac: SOURCES += macos.c win32: SOURCES += windows.c +android: SOURCES -= \ + qt-ui/usermanual.cpp \ + qt-ui/printdialog.cpp \ + qt-ui/printlayout.cpp \ + qt-ui/printoptions.cpp + FORMS = \ qt-ui/about.ui \ qt-ui/divecomputermanagementdialog.ui \ @@ -161,6 +178,9 @@ FORMS = \ qt-ui/divelogimportdialog.ui \ qt-ui/usermanual.ui +# Nether usermanual or printing is supported on android right now +android: FORMS -= qt-ui/usermanual.ui qt-ui/printoptions.ui + RESOURCES = subsurface.qrc TRANSLATIONS = \ @@ -204,10 +224,14 @@ QTTRANSLATIONS = \ qt_sv.qm \ qt_zh_TW.qm -doc.commands += $(CHK_DIR_EXISTS) $$OUT_PWD/Documentation$$escape_expand(\\n\\t)$(MAKE) -C $$PWD/Documentation OUT=$$OUT_PWD/Documentation/ doc +doc.commands += $(CHK_DIR_EXISTS) $$OUT_PWD/Documentation || $(MKDIR) $$OUT_PWD/Documentation $$escape_expand(\\n\\t)$(MAKE) -C $$PWD/Documentation OUT=$$OUT_PWD/Documentation/ doc all.depends += doc QMAKE_EXTRA_TARGETS += doc all +marbledata.commands += $(CHK_DIR_EXISTS) $$OUT_PWD/marbledata || $(COPY_DIR) $$PWD/marbledata $$OUT_PWD +all.depends += marbledata +QMAKE_EXTRA_TARGETS += marbledata + DESKTOP_FILE = subsurface.desktop mac: ICON = packaging/macosx/Subsurface.icns else: ICON = subsurface-icon.svg diff --git a/subsurface.qrc b/subsurface.qrc index a1211a66b..ec3398a8b 100644 --- a/subsurface.qrc +++ b/subsurface.qrc @@ -40,16 +40,16 @@ <file>xslt/sensuscsv.xslt</file> <file>xslt/manualcsv2xml.xslt</file> <file>xslt/shearwater.xslt</file> - <file alias="icon_lung">icons/icon-lung.png</file> - <file alias="icon_mod">icons/icon-mod.png</file> - <file alias="icon_he">icons/icon-phegraph.png</file> - <file alias="icon_n2">icons/icon-pn2graph.png</file> - <file alias="icon_o2">icons/icon-po2graph.png</file> - <file alias="icon_ceiling_calculated">icons/icon-ceiling-calculated.png</file> + <file alias="icon_lung">icons/gas.png</file> + <file alias="icon_mod">icons/mod.png</file> + <file alias="icon_he">icons/he.png</file> + <file alias="icon_n2">icons/n2.png</file> + <file alias="icon_o2">icons/o2.png</file> + <file alias="icon_ceiling_calculated">icons/ss.png</file> <file alias="icon_ceiling_alltissues">icons/icon-ceiling-alltissues.png</file> - <file alias="icon_NDLTTS">icons/icon-NDLTTS.png</file> - <file alias="icon_ceiling_3m">icons/icon-ceiling-3m.png</file> - <file alias="icon_ceiling_dc">icons/icon-ceiling-dc.png</file> - <file alias="icon_ead">icons/icon-ead.png</file> + <file alias="icon_NDLTTS">icons/limit.png</file> + <file alias="icon_ceiling_3m">icons/3x3.png</file> + <file alias="icon_ceiling_dc">icons/pc.png</file> + <file alias="icon_ead">icons/ead.png</file> </qresource> </RCC> diff --git a/subsurfacestartup.c b/subsurfacestartup.c index 3f410aff4..06a76d04f 100644 --- a/subsurfacestartup.c +++ b/subsurfacestartup.c @@ -30,6 +30,7 @@ struct preferences default_prefs = { .display_invalid_dives = false, .show_sac = false, .display_unused_tanks = false, + .show_average_depth = true, .text_label_with_units = false }; @@ -94,7 +95,8 @@ static void print_help() printf("\n --help|-h This help text"); printf("\n --import logfile ... Logs before this option is treated as base, everything after is imported"); printf("\n --verbose|-v Verbose debug (repeat to increase verbosity)"); - printf("\n --version Prints current version\n\n"); + printf("\n --version Prints current version"); + printf("\n --win32console Create a dedicated console if needed (Windows only). Add option before everything else\n\n"); } void parse_argument(const char *arg) @@ -130,6 +132,8 @@ void parse_argument(const char *arg) print_version(); exit(0); } + if (strcmp(arg, "--win32console") == 0) + return; /* fallthrough */ case 'p': /* ignore process serial number argument when run as native macosx app */ @@ -170,6 +174,7 @@ void setup_system_prefs(void) const char *env; default_prefs.divelist_font = strdup(system_divelist_default_font); + default_prefs.font_size = system_divelist_default_font_size; default_prefs.default_filename = system_default_filename(); env = getenv("LC_MEASUREMENT"); @@ -2,6 +2,7 @@ /* implements Windows specific functions */ #include "dive.h" #include "display.h" +#define _WIN32_WINNT 0x500 #include <windows.h> #include <shlobj.h> #include <stdio.h> @@ -10,7 +11,8 @@ #include <dirent.h> #include <zip.h> -const char system_divelist_default_font[] = "Sans 8"; +const char system_divelist_default_font[] = "Sans"; +const int system_divelist_default_font_size = 8; const char *system_default_filename(void) { @@ -198,3 +200,79 @@ int subsurface_zip_close(struct zip *zip) { return zip_close(zip); } + +/* win32 console */ +static struct { + bool allocated; + UINT cp; + FILE *out, *err; +} console_desc; + +void subsurface_console_init(bool dedicated) +{ + /* if this is a console app already, do nothing */ +#ifndef WIN32_CONSOLE_APP + /* just in case of multiple calls */ + memset((void *)&console_desc, 0, sizeof(console_desc)); + /* the AttachConsole(..) call can be used to determine if the parent process + * is a terminal. if it succeeds, there is no need for a dedicated console + * window and we don't need to call the AllocConsole() function. on the other + * hand if the user has set the 'dedicated' flag to 'true' and if AttachConsole() + * has failed, we create a dedicated console window. + */ + console_desc.allocated = AttachConsole(ATTACH_PARENT_PROCESS); + if (console_desc.allocated) + dedicated = false; + if (!console_desc.allocated && dedicated) + console_desc.allocated = AllocConsole(); + if (!console_desc.allocated) + return; + + console_desc.cp = GetConsoleCP(); + SetConsoleOutputCP(CP_UTF8); /* make the ouput utf8 */ + + /* set some console modes; we don't need to reset these back. + * ENABLE_EXTENDED_FLAGS = 0x0080, ENABLE_QUICK_EDIT_MODE = 0x0040 */ + HANDLE h_in = GetStdHandle(STD_INPUT_HANDLE); + if (h_in) { + SetConsoleMode(h_in, 0x0080 | 0x0040); + CloseHandle(h_in); + } + + /* dedicated only; disable the 'x' button as it will close the main process as well */ + HWND h_cw = GetConsoleWindow(); + if (h_cw && dedicated) { + SetWindowTextA(h_cw, "Subsurface Console"); + HMENU h_menu = GetSystemMenu(h_cw, 0); + if (h_menu) { + EnableMenuItem(h_menu, SC_CLOSE, MF_BYCOMMAND | MF_DISABLED); + DrawMenuBar(h_cw); + } + SetConsoleCtrlHandler(NULL, TRUE); /* disable the CTRL handler */ + } + + /* redirect; on win32, CON is a reserved pipe target, like NUL */ + console_desc.out = freopen("CON", "w", stdout); + console_desc.err = freopen("CON", "w", stderr); + if (!dedicated) + puts(""); /* add an empty line */ +#endif +} + +void subsurface_console_exit(void) +{ +#ifndef WIN32_CONSOLE_APP + if (!console_desc.allocated) + return; + + /* close handles */ + if (console_desc.out) + fclose(console_desc.out); + if (console_desc.err) + fclose(console_desc.err); + + /* reset code page and free */ + SetConsoleOutputCP(console_desc.cp); + FreeConsole(); +#endif +} diff --git a/worldmap-options.h b/worldmap-options.h new file mode 100644 index 000000000..6e7e8728b --- /dev/null +++ b/worldmap-options.h @@ -0,0 +1,7 @@ +#ifndef WORLDMAP_OPTIONS_H +#define WORLDMAP_OPTIONS_H + +const char* map_options = "center: new google.maps.LatLng(0,0),\n\tzoom: 3,\n\tminZoom: 2,\n\tmapTypeId: google.maps.MapTypeId.SATELLITE\n\t"; +const char* css = "\n\thtml { height: 100% }\n\tbody { height: 100%; margin: 0; padding: 0 }\n\t#map-canvas { height: 100% }\n"; + +#endif// WORLDMAP-OPTIONS_H diff --git a/worldmap-save.c b/worldmap-save.c new file mode 100644 index 000000000..f9a4bf3fe --- /dev/null +++ b/worldmap-save.c @@ -0,0 +1,110 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include "dive.h" +#include "membuffer.h" +#include "worldmap-save.h" +#include "worldmap-options.h" + +char* getGoogleApi() +{ + /* google maps api auth*/ + return "https://maps.googleapis.com/maps/api/js?key=AIzaSyDzo9PWsqYDDSddVswg_13rpD9oH_dLuoQ"; +} + +void put_HTML_date(struct membuffer *b,struct dive *dive) +{ + struct tm tm; + utc_mkdate(dive->when, &tm); + put_format(b, "<p>date=%04u-%02u-%02u</p>",tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + put_format(b, "<p>time=%02u:%02u:%02u</p>",tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +void put_HTML_temp(struct membuffer *b,struct dive *dive) +{ + put_temperature(b, dive->airtemp, "<p>Air Temp: ", " C\\'</p>"); + put_temperature(b, dive->watertemp, "<p>Water Temp: ", " C\\'</p>"); +} + +void put_HTML_notes(struct membuffer *b,struct dive *dive) +{ + if (dive->notes) { + put_format(b,"<p>Notes : %s </p>",dive->notes); + } +} + +void writeMarkers(struct membuffer *b) +{ + int i; + struct dive *dive; + + for_each_dive(i, dive) { + /*export selected dives only ?*/ + + if (dive->latitude.udeg==0 && dive->longitude.udeg==0) { + continue; + } + + put_format(b,"temp = new google.maps.Marker({position: new google.maps.LatLng(%f,%f)});\n", + dive->latitude.udeg/1000000.0,dive->longitude.udeg/1000000.0); + put_string(b,"markers.push(temp);\ntempinfowindow = new google.maps.InfoWindow({content: '<div id=\"content\">'+'<div id=\"siteNotice\">'+'</div>'+'<div id=\"bodyContent\">"); + put_HTML_date(b,dive); + put_duration(b, dive->duration, "<p>duration=", " min</p>"); + put_HTML_temp(b,dive); + put_HTML_notes(b,dive); + put_string(b,"</p>'+'</div>'+'</div>'});\ninfowindows.push(tempinfowindow);\n"); + put_format(b,"google.maps.event.addListener(markers[%d], 'mouseover', function() {\ninfowindows[%d].open(map,markers[%d]);}",i,i,i); + put_format(b,");google.maps.event.addListener(markers[%d], 'mouseout', function() {\ninfowindows[%d].close();});\n",i,i); + } +} + +void insert_html_header(struct membuffer *b) +{ + put_string(b,"<!DOCTYPE html>\n<html>\n<head>\n"); + put_string(b,"<meta name=\"viewport\" content=\"initial-scale=1.0, user-scalable=no\" />\n<title>World Map</title>\n"); +} + +void insert_css(struct membuffer *b) +{ + put_format(b,"<style type=\"text/css\">%s</style>\n",css); +} + +void insert_javascript(struct membuffer *b) +{ + put_string(b,"<script type=\"text/javascript\"src=\""); + put_string(b,getGoogleApi()); + put_string(b,"&sensor=false\">\n</script>\n<script type=\"text/javascript\">\nvar map;\n"); + put_format(b,"function initialize() {\nvar mapOptions = {\n\t%s,",map_options); + put_string(b,"rotateControl: false,\n\tstreetViewControl: false,\n\tmapTypeControl: false\n};\n"); + put_string(b,"map = new google.maps.Map(document.getElementById(\"map-canvas\"),mapOptions);\nvar markers = new Array();"); + put_string(b,"\nvar infowindows = new Array();\nvar temp;\nvar tempinfowindow;\n"); + writeMarkers(b); + put_string(b,"\nfor(var i=0;i<markers.length;i++)\n\tmarkers[i].setMap(map);\n}\n"); + put_string(b,"google.maps.event.addDomListener(window, 'load', initialize);</script>\n"); +} + +void export(struct membuffer *b) +{ + insert_html_header(b); + insert_css(b); + insert_javascript(b); + put_string(b,"\t</head>\n<body>\n<div id=\"map-canvas\"/>\n</body>\n</html>"); +} + +void export_worldmap_HTML(const char* file_name) +{ + FILE *f; + + struct membuffer buf = { 0 }; + export(&buf); + + f = fopen(file_name,"w+"); + if (!f) { + printf("error"); /*report error*/ + } + + flush_buffer(&buf,f); /*check for writing errors? */ + free_buffer(&buf); + fclose(f); +} diff --git a/worldmap-save.h b/worldmap-save.h new file mode 100644 index 000000000..72363e482 --- /dev/null +++ b/worldmap-save.h @@ -0,0 +1,15 @@ +#ifndef WORLDMAP_SAVE_H +#define WORLDMAP_SAVE_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void export_worldmap_HTML(const char* x); + + +#ifdef __cplusplus +} +#endif + +#endif |