Commit graph

106 commits

Author SHA1 Message Date
Berthold Stoeger
c5c8bfec65 undo: switch to dive after replan / profile edit
It is confusing when undoing a command and nothing happens
in the UI. Therefore, switch to the corresponding dive when
undoing/redoing a replan or profile edit.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-03-12 18:32:22 +01:00
Berthold Stoeger
ea0c030770 undo: split replanDive and editProfile commands
These two actions were using the same command with a flag
controlling the name of the command, which is shown in
the undo menu.

However, the replanDive does much more (such as changing
the notes) and in the future we may want to be more
fine-grained with respect to profile editing. Therefore,
split these commands into two separate ones.

Moreover, make the editProfile command more flexible.
Pass an enum describing the action instead and also
a counter indicating how many points have been
moved or removed.

Finally, don't consume the input dive in the editProfile
command, because we will want to keep the original dive
while editing the profile.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-03-12 18:32:22 +01:00
Berthold Stoeger
9e0712d5dc core: replace dive master by dive guide
In general, replace "dive master" by "dive guide".

However, do not change written dive logs for now. On reading,
accept both versions.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-02-15 09:35:43 -08:00
Berthold Stoeger
889827aadb desktop: don't update notes field when executing command
User report: when switching focus between windows, the
cursor position gets lost. This is due to a note-edit
command being fired, which then overwrites the notes tab.

To prevent this, don't update the notes field when placing
a command. Moreover, generally don't update the dive
selection when placing a command as that also rewrites all
the values.

Should this be extended to other fields?

Fixes #3365

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-02-15 09:34:23 -08:00
Berthold Stoeger
7aacde3169 profile: remove ProfileScene::dataModel
All data access is now directly via the plot_info structure
owned by the ProfileScene itself.

Also removes DivePercentageItem::hColumn, which was an
artifact from the DivePlotDataModel.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-12-17 11:54:23 -08:00
Berthold Stoeger
5d1f12438b cleanup: remove 'const constexpr' pairs
'constexpr' implies 'const' since it is a stronger guarantee,
so let's remove the redundant 'const'.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-12-17 10:36:13 -08:00
Berthold Stoeger
1af67512a1 cylinders: add cylinder before hidden cylinders
When adding a cylinder, it was added at the end of the list.
This would make hidden cylinders visible as the new rule is
to only hide unused cylinders at the end of the list.

Therefore, add the cylinder after the last used cylinder,
i.e. before the first hidden cylinder.

This means that the position where the cylinder is added has
to be hidden in the undo command.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-12-13 11:54:24 -08:00
Berthold Stoeger
cbe6d89767 profile: fix editing depth / duration
The undo commands for depth and duration editing cleared
the samples of the dive computer. They relied on the profile
automatically creating a fake profile. However, at some point
that code got removed. Therefore, do it explicitly in the undo
command.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-12-04 09:40:08 -08:00
Berthold Stoeger
64faa23448 undo: renumber cylinders when deleting a cylinder
Removal of a cylinder requires a renumbering of
cylinders in the core data structures (samples, etc.).

The renumbering was performed in the undo-action of
cylinder removal, but not during actual cylinder removal.
What a mess!

Add the missing call.

Attention: this makes the deletion of sensor-readings
on cylinder-deletion non-undoable!

Undo will have to be fixed in upcoming commits.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-09-03 13:35:28 -07:00
mikeller
2d734c529b desktop: Add the capability to copy / paste dive number and date / time.
This is adding the capability to select 'Dive number' and 'Date / Time'
in the 'Copy dive components' dialog, and then copy them into the
clipboard.
When using 'Paste dive components, these values will then be pasted into
the selected dive(s).
This is intended to help with workflows that import dive information
from two different sources, like general information from another
logging program, and CCR ppO2 sensor readings from a unit log, and then
stitch them together into one cohesive entry with all data per dive.
Copied data is also output into formatted text when pasting the
clipboard outside of the application:

```
Dive number: 401
Date / time: Sun 2 May 2021 12:00 AM
```

No translations have been added as of now - I could not find any
information on how strings are translated for this project.

Signed-off-by: Michael Keller <github@ike.ch>
2021-05-19 15:15:34 -07:00
Berthold Stoeger
5692f0d68a undo: add missing invalidate_dive_cache() calls
The AddWeight, RemoveWeight, EditWeight and ReplanDive
commands were missing invalidate_dive_cache() calls.
Add them to ensure that the dives are written to git
logs on save.

Fixes #3150

Reported-by: Peter Zaal <peter.zaal@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2021-01-11 08:42:17 -08:00
Linus Torvalds
1808804bab Make sure to sanitize the gasmix after editing it
Otherwise we end up showing the gasmix in a different form after editing
for the usual air percentages, because we haven't normalized the gasmix
values and store them back in the dive in the non-normalized format.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2021-01-10 13:44:43 -08:00
Berthold Stoeger
50b11024d6 core: keep tank infos in a dynamic table
The list of known tank types were kept in a fixed size table.
Instead, use a dynamic table with our horrendous table macros.
This is more flexible and sensible.

While doing this, clean up the TankInfoModel, which was leaking
memory.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-12-13 11:49:59 -08:00
Dirk Hohndel
8e3f7f9565 desktop: fix translation implementation
We need to always call the tr() function of the base class. This will have
consequences for our translations.

See: #3068

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-11-08 14:39:26 -08:00
Dirk Hohndel
f193c2ef08 cleanup: fix deprecated QVector constructor
Annoyingly, the replacement has only been available since Qt 5.14.
To make the code less messy, implement our own stdToQt conversion helper.

Suggested-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-10-26 19:27:03 -07:00
Berthold Stoeger
fe3021b88a cleanup: consistently use get_cylinder() accessor
get_cylinder(d, i) is more readable than d->cylinders.cylinders[i].
Moreover, it does bound checking and is more flexible with respect to
changing the core data structures. Most places already used this accessor,
but some still accessed the cylinders directly.

This patch unifies the accesses by consistently switching to get_cylinder().
The affected code is in C++ and accesses the cylinder as reference or
object, whereas the get_cylinder() function is C and returns a pointer.
This results in funky looking "*get_cylinder(d, i)" expressions.
Arguably still better than the original.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-08-21 08:48:22 -07:00
Berthold Stoeger
beef8096f7 undo: update cylinder related info when editing cylinder data
We have to call update_cylinder_related_info() when adding /
editing / removing cylinders. This could be done in a common
base class of the commands. For simplicity, let's call
the function in the respective undo()/redo() functions.

Partially fixes #2814.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-06 11:44:22 -07:00
Berthold Stoeger
b949bad026 core: always keep an empty cylinder at the end of the cylinder array
This will be temporarilly used by the planner to mark consumption of
air at the surface. Do this by creating a new function add_cylinder,
which replaces add_to_cylinder_table() and takes care of always adding
a dummy cylinder at the end of the table. Make the original
add_to_cylinder_table() local, so that it cannot be accessed anymore.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-01 12:36:28 +02:00
Dirk Hohndel
6d187b5f4a
Merge pull request #2643 from bstoeger/cylinder4
First steps of cylinder-editing undo
2020-04-11 11:03:05 -07:00
Berthold Stoeger
4489389a01 undo: refine pasting of cylinders
When pasting cylinders, the destination dive got a verbatim copy
of the cylinders of the source dive. This is not what users want:
for example, the start and stop pressures from the dive computer
as well as the gas mix may be overwritten.

There seems to be no perfect solution, since some times users may
want to paste the gas-mix.

Therefore, let's choose a heuristic approach for now (in the future
we might implement a UI-flag):

When copying over existing cylinders, only copy type and maximum
pressure.

When adding new cylinders (i.e. from-dive has more cylinders than
to-dive), copy everything, but reset start- and top-pressure to 0,
which represents unkown.

Moroever, in the latter case, set "manually added" to true, since
obviously this wasn't added by a dive computer.

Fixes #2676

Reported-by: Miika Turkia <miika.turkia@gmail.com>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-10 09:43:32 -07:00
Berthold Stoeger
e2f77f9238 undo: call invalidate_dive_cache() when editing cylinders
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:35 +02:00
Berthold Stoeger
2eeb5f4fc2 undo: more fine-grained editing of cylinder
Don't overwrite the full cylinder when editing a single field.
Implement three "modes": editing of type, pressure and gasmix.

Don't consider individual fields, because some of them are
related. E.g. you can change the gasmix by setting the MOD.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:35 +02:00
Berthold Stoeger
4e8a838f74 undo: store all cylinders in EditCylinderBase
We stored only one cylinder in EditCylinderBase, which is the
base class of RemoveCylinder and EditCylinder. This turns out
to be too crude: when removing the "same" cylinder from
multiple dives, there are some "hidden variables" such as
bestmix_o2 or manually_added, which might actually be different.
We don't want to overwrite those on undo of delete.
Moreover, the cylinder edit is way too crude at the moment,
as it overwrites the whole cylinder even when the user edited
only a single field. To enable a more refined edit, we have
to store each changed cylinder.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:35 +02:00
Berthold Stoeger
79d117b5bc undo: be more flexible about which cylinders to edit
Currently we use the same_cylinder() function to determine
which cylinders should be edited in a multi-dive edit. Make
this more flexible by introducing a flag-set, such that
the undo-command can select which cylinders are considered
as equal:
	- same type
	- same pressure
	- same gas mix
	- same size

Currently both undo commands use same type, pressure and
gas so that the behavior stays unchanged.

The future goal is to split the cylinder-edit undo command
into different commands so that when, for example, editing
the type only the type is considered by not the gas mix.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:35 +02:00
Berthold Stoeger
c4bf1ce891 undo: remove only "non-protected" cylinders
The undo-code must take care not to remove used cylinders.
To do so, extend the constructor of EditCylinderBase,
which collects the cylinders and dives to edit, by the
"nonProtectedOnly" boolean argument. If true, only those
cylinders for wich "is_cylinder_prot" returns false
will be added.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:35 +02:00
Berthold Stoeger
5b7a316593 undo: reorder cylinders on remove-cylinder undo/redo
The cylinders in the events must be reordered if we remove
a cylinder. To avoid duplication of code, move the reordering
function into qthelper.cpp, though it might not be ideal
there.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:35 +02:00
Berthold Stoeger
aa7b0cadb2 undo: add cylinder undo commands by copy & paste
Do a simple copy & paste followed by a simple search & replace
to generate cylinder undo commands from weight undo commands.
Obviously, this is still missing the necessary code to keep
the dive-data consistent after cylinder editing.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-07 00:13:34 +02:00
Berthold Stoeger
69d437bc86 undo/mobile: keep track of dive sites
When editing the dive site of a dive, the dive-table of the
corresponding dive site was not properly updated by the undo
commands. Try to get this right.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-04-04 11:49:23 -07:00
Berthold Stoeger
91f7689787 undo: autogenerate string get() and data() functions using a template
Do this in analogy to other types. However, here we have to convert
from / to QString. We could do this in an even more general way
by using two templat parameters: one for the Qt type, one for the
core type and define conversion functions. However, let's not do
this for now.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-31 21:53:19 +02:00
Berthold Stoeger
ea813938a8 undo: autogenerate trivial set() and data() functions by a template
Some dive-editing undo commands have trivial setter and getter
functions: they simply read and write a struct dive member.
Autogenerate these in a template to which we pass a pointer
to member as template argument.

For the invalid-flag we have to turn the edit command from int
to bool, since that is how the flag is store in the dive struct.

Sadly, quite a number of the setters do funky things and we
cannot autogenerate them.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-31 21:53:19 +02:00
Berthold Stoeger
ad540ce5e8 undo: generate fieldId() virtual functions by templates
Most edit commands derive from a common base class EditBase,
which declares a fieldId() virtual function that has to be
defined by the child classes. This is tedious. For some reason
the C++ makers refuse to allow "virtual member constants".

To make the code somewhat less verbose, create these functions
by a template. Of course, we could introduce the template
parameter directly in the EditBase class. However, that would
mean that the code in this base class is generated for every
single undo command. I'm not sure we want that.

This should als make it somewhat less tedious to create new
edit commands by copy & paste.

We could do the same for the fieldName. However, that is more
complicated for two reasons:

1) For historic reasons(?) C++ doesn't allow for string literals
   as template parameters. Therefore, arrays-of-string would have
   to be defined, which is not very nice.
2) We would have to make sure that these strings are recognized
   by Qt's translation machinery and use the QT_TRANSLATE_NOOP
   macro, which makes the whole thing even less attractive.
Maybe later.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-31 21:53:19 +02:00
Berthold Stoeger
10a4225013 mobile: when editing dives update the fulltext filter
In the mobile version of the dive-editing command, the fulltext
filter was not updated. Thus, when editing a dive while the filter
was active, the dive would disappear.

Unregister the old and register the new version.

Reported-by: Chirana Gheorghita Eugeniu Theodor <office@adaptcom.ro>
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-28 10:55:04 -07:00
Berthold Stoeger
76cab71b7c mobile: fix filter flag when editing dives
When editing dives, the undo-command sends a filte changed signal.
The models catch the signal and check whether the filter status
changed.

The mobile-version of the dive-edit command simply exchanged the
dives. This could lead to inconsistencies when the filter flag
was overwritten. Therefore, make sure that the filter flag
is not overwritten by the dive-exchange.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-28 10:55:04 -07:00
Dirk Hohndel
a109a28367 translations: qualify tr function to get correct context
The way inheritance is implemented for the undo commands confuses the Qt
translation tooling - with the result that the context assumed by the
tools used to extract the strings doesn't match the context calculated
at runtime - so all the translations for the strings in undo commands
fail (including creating proper numerus forms).

This change forces a consistant context tag, at the price of creating a
significant delta for the source strings (the strings themselves stay
the same, but the context for a lot of them changes). I am hoping that
Transifex is smart enough to automagically add the correct translations
for these, but I guess I won't know until I try.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-03-22 12:02:48 -07:00
Berthold Stoeger
4e47cdfa2c Undo: implement invalidate-dive command
Connect command to context menu.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-20 15:20:44 -07:00
Berthold Stoeger
9722f04e40 undo: remove EditNumber command
Number editing works via the RenumberDives command.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-17 16:54:38 -07:00
Berthold Stoeger
219420bd6e filter: update fulltext index when editing dive
In edit commands, the fulltext might have changed and therefore
we have to update the fulltext index.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-17 16:50:38 -07:00
Robert C. Helling
285fa8acbc Grammar: replaces 'indexes' by 'indices'
Grammar-nazi ran

git grep -l 'indexes' | xargs sed -i '' -e 's/indexes/indices/g'

to prevent future wincing when reading the source code.

Unfortunatly, Qt itself is infected as in
QModelIndexList QItemSelection::indexes() const

Signed-off-by: Robert C. Helling <helling@atdotde.de>
2020-03-11 08:26:30 -07:00
Dirk Hohndel
0212b1b9f7 undo infrastructure: improve undo command texts
For many of the commands it is fairly easy to add information that makes
it easier to figure out what actually happened. That's especially true
for commands operating on dives. Trip and dive site edits haven't been
given these more elaborate undo texts (yet).

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-03-10 09:25:57 -07:00
Berthold Stoeger
57b96490b2 mobile/undo: create EditDive command
Command that just swaps two dives. This is rather complex,
as for example a dive site might be created.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-10 09:25:57 -07:00
Dirk Hohndel
7d9e907681 debug: try to capture changes that don't invalidate git cache
At least in those cases where we are sending a divesChanged signal we can
easily check if the cache was properly invalidated. Of course this won't help
in cases where we don't notify the dive list about changes, either.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-03-07 18:47:34 -08:00
Berthold Stoeger
2a97934db4 Cleanup: Move stringToList to core/qthelper.cpp
The same code was used in desktop and undo commands. Let's unify.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-03-06 10:00:13 +01:00
Dirk Hohndel
24e02d878d code cleanup: remove unused member variable
It was initialized, but never referenced.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-01-10 02:37:03 +09:00
Dirk Hohndel
57f5d4b784 code cleanup: replace deprecated fromStdVector() method
Replacement was only introduced in Qt 5.14

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-01-10 02:37:03 +09:00
Dirk Hohndel
c3614040d9 code cleanup: ensure all fields are initialized
In a total abundance of caution, make sure we don't exit the constructor
leaving fields uninitialized.

Fixes CID 351437

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-01-10 02:37:03 +09:00
willemferguson
d2cf58e07e core: read and write the user-specified salinity
Both XML and git storage are added.

Signed-off-by: willemferguson <willemferguson@zoology.up.ac.za>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2020-01-06 07:00:34 +09:00
Berthold Stoeger
e453525afb Translation: explicitly show no dive-count if only one dive edited
We relied upon the translators to remove the parenthesis in cases
like "Edited notes (%n dives)" for n = 1 dives. Dirk doesn't want
that. Therefore, do it in the C++-code.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-05 10:14:25 -08:00
Berthold Stoeger
72c6b83866 Undo: make weight editing undoable
Implement the EditWeight undo command. Since there is common code
(storage of the old weight), this creates a common base class for
RemoveWeight and EditWeight. The model calls directly into the undo
command, which is somewhat unfortunate as it feels like a layering
violation. It's the easy thing to do for now.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-05 10:14:25 -08:00
Berthold Stoeger
ab99ca85f1 Cleanup: use free_weightsystem function instead of explicit free
Instead of freeing internal data of the weightsystem structure,
call the free_weightsystem function (which has to be made extern
at first). This makes things more future-proof, should the
weightsystem struct ever be extended.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-05 10:14:25 -08:00
Berthold Stoeger
dc67876c79 Cleanup: introduce empty_weightsystem constant
To make things more future-proof, introduce an empty_weightsystem
constant. Replace explicit aggragate initialization of empty
weightsystems by this constant.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-05 10:14:25 -08:00
Berthold Stoeger
b3f530bfb9 Undo: make weight-deletion an undoable action
This one is a bit more complicated than weight adding, because the
multiple-dive case is not well defined. If multiple dives are selected,
this implementation will search for weights that are identical to the
weight deleted in the currently shown dive. The position of the weight
in the list is ignored.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-05 10:14:25 -08:00
Berthold Stoeger
147a36647c Undo: make adding of weights an undoable action
Introduce an AddWeight undo command. This is modelled after the
numerous dive-edit undo commands. The redo and undo actions are
connected to the WeightModel via two new signals, weightAdded
and weightRemoved.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-05 10:14:25 -08:00
Berthold Stoeger
7787bfbf9e Selection: move commands/command_private.* to core/selection.*
The file command_private.cpp had functions concerning selections
only. To make these functions accessible from outside the undo
machinery, turn it into a part of the core-library. Currently,
only C++ functions are exported. We might think about also
exporting a C interface.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-04 13:00:23 +01:00
Berthold Stoeger
a6fa6cdb41 Undo: make profile-editing undoable
Recently, undo of dive-replanning was introduced. Therefore,
it appears logical to do the same thing for editing of the
profile of manually added dives.

For now, use the same undo-command, just change the displayed
text from "replan dive" to "edit profile". Move the fixup dive
call into the undo-command.

Eventually, every action on the profile should be made undoable.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-12-03 20:55:58 -08:00
willemferguson
6d7f26f4bf Desktop: add additional star widgets to Information tab
Connect the UI to the underlying dive structure. Enable proper initialisation
and management of star widgets while Information tab is active. Enable undo for
the addtional star widgets.

Signed-off-by: willemferguson <willemferguson@zoology.up.ac.za>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2019-12-03 20:37:57 -08:00
Berthold Stoeger
5e29245e68 Refactoring: move undo commands to top level
In the future we might want to use undo-commands for mobile as
well (even if not implementing undo).

Therefore, move the undo-command source from desktop-widgets
to their own commands top-level folder.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2019-11-14 21:02:07 +01:00
Renamed from desktop-widgets/command_edit.cpp (Browse further)