Commit Graph
100 Commits
Author SHA1 Message Date
Maarten ter Huurne 3694deecf6 Be resilient against zero or multiple "Categories" OPK metadata keys
For zero keys, the OPK file itself would be parsed as a settings file.
For multiple keys, all first categories would be appended to the path,
leading to a much too deep directory.
2014-08-17 20:37:15 +02:00
Maarten ter Huurne 7992d83e10 Pass C++ string to LinkApp constructor
All callers already had C++ strings and LinkApp wants a C++ string,
but because of the argument type the strings had to be converted.
2014-08-17 18:52:18 +02:00
Maarten ter Huurne 79ef176831 Disable delete context menu option for undeletable link files
Before this change, it was possible to delete for example a link that
is stored on a read-only file system and it would disappear after
deletion but reappear as soon as the menu restarts.
2014-08-17 18:43:02 +02:00
Maarten ter Huurne c3df9c4517 Removed ".empty" files for non-empty directories
Git doesn't track directories, just files, so dummy files are necessary
to preserve empty directories. These directories were not empty though.
Removing the dummy files avoids GMenu2X scanning them on startup.
2014-08-17 17:12:33 +02:00
Maarten ter Huurne fe0c7c2e8d When going up a level in the selector, put cursor on old dir
This adds a sense of continuity to the navigation.
2014-08-17 11:15:21 +02:00
Maarten ter Huurne bb30dedd09 When selector goes up from the root directory, stay in the root
Previously the selector would exit, but I don't see the logic in that.
2014-08-17 10:25:28 +02:00
Maarten ter Huurne ba09d2599d Avoid out-of-range lookup when FileLister::browse is passed empty string
When the path is the empty string, it is handled as the current working
directory, which is consistent with how relative paths are handled.
2014-08-17 10:22:09 +02:00
Maarten ter Huurne 20725fcdda Do not list ".." as a subdirectory of the root directory
In Linux "/.." exists and loops back onto the root. But for the user
it is confusing to see a ".." entry listed that does nothing when
selected, so hide it.
2014-08-17 10:20:22 +02:00
Maarten ter Huurne 0b0e278459 Deal gracefully with the selector being started on a non-existent dir
If directory browsing is enabled, fall back to the closest existing
parent directory. If directory browsing is disabled, show empty list.
2014-08-17 10:05:21 +02:00
Maarten ter Huurne 9e22b0328f Made new "no items" text in Selector translatable 2014-08-17 08:41:09 +02:00
Maarten ter Huurne e2382e11fd Made SettingsDialog::exec() return whether a setting was changed
Previously, exec() always returned true and a separate edited() method
would indicate whether any settings were changed.
2014-08-16 17:11:18 +02:00
Maarten ter Huurne faf2495727 Use unique_ptr to make ownership of MenuSettings explicit 2014-08-16 17:01:04 +02:00
Maarten ter Huurne 8a1bb5694f Renamed SettingsDialog::voices to settings
I have no idea where that original name came from, but it didn't make
any sense to me.
2014-08-16 16:43:26 +02:00
Maarten ter Huurne dc39fcc01f Make Selector robust against an empty file list
This can happen on a directory with 0 file matches when directories are
not being shown (otherwise there is either ".." or a subdir).

Since there is nothing to be selected, the Accept button is disabled
and no cursor is shown.
2014-08-16 15:50:16 +02:00
Maarten ter Huurne 3cce71284a Redistribute any leftover vertical space in Selector 2014-08-16 15:42:33 +02:00
Maarten ter Huurne ffb736d80f Log surface load failures at DEBUG level instead of ERROR
Since we no longer check file existance before attempting a load,
load failures are often not errors. For example a missing preview
image is harmless.
2014-08-16 15:42:33 +02:00
Maarten ter Huurne d16cb902b3 Improved Selector's handling of the folder icon
Renamed fontHeight to lineHeight, since it is computed using both the
font's line spacing and the height of the folder icon. For the latter,
use the actual icon height plus two pixels spacing instead of the
hardcoded value of 20 (16 height + 4 pixels spacing).

Fixed recently introduced bug where "top" is added to the folder icon
y-coordinate twice. Also center the icon vertically when a large font
is used.

Don't crash if the folder icon is missing. This could only happen on
broken installations though. Also don't search for the icon twice:
SurfaceCollection::skinRes already calls addSkinRes internally when
there is no cached surface for the key.
2014-08-16 15:41:06 +02:00
Maarten ter Huurne 88a4d1d911 Minor cleanups in Selector
Removed unused data member selRow.
Keep reference to LinkApp instead of pointer.
Call LinkApp::getSelectorBrowser once and store result.
Renamed fontheight to fontHeight.
Use iY consistently: store the item's y-coordinate in it.
2014-08-16 07:31:33 +02:00
Maarten ter Huurne fe0db484ec Removed "delegate.h"
The abstraction it provided was so thin that I think it is simpler to
just have the code use std::bind directly.
2014-08-16 07:07:41 +02:00
Maarten ter Huurne 7e308879c1 Pass action to IconButton constructor
This removes the need for a separate setAction method.

The default action is the empty action, which does nothing. However,
a touch event on a button with the empty action is no longer considered
handled.

Menu::btnContextMenu was changed from a unique_ptr to a plain data
member.
2014-08-16 06:44:13 +02:00
Maarten ter Huurne 428a316bc1 Removed GMenu2X pointer from ButtonBox
It was only used to fetch resY, so I replaced that by taking the
y-coordinate as an argument. That is also more consistent with the
x-coordinate which was already an argument.

The x-coordinate was changed to a signed int, since that is the norm
for paint coordinates. It can be useful for drawing widgets that are
partially off screen, for example during a (dis)appear animation.

In InputDialog, the ButtonBox field was changed from a pointer to
a plain data member. There was no need to dynamically allocate it.
2014-08-16 05:52:25 +02:00
Maarten ter Huurne e5a432cb6b In C++11, it is no longer necessary to avoid ">>" in nested templates 2014-08-16 05:42:39 +02:00
Maarten ter Huurne 004c41e2ef Simplify bottom bar button drawing in Selector
Put the drawButton calls left-to-right.
Avoid duplicate code.

The order was changed: "Select" is now always first, to be consistent
with other dialogs.
2014-08-15 18:53:08 +02:00
Maarten ter Huurne 3df841c150 Handle labelless icon positioning in GMenu2X::drawButton(Right)
Instead of correcting the returned coordinate with "- 10" externally,
omit the white space inside the methods.

Note that Font::getTextWidth, which was used until recently, considers
an empty string to have width 1, so 3 + getTextWidth("") + 6 == 10.

There is a difference in how buttons that have neither a label nor an
icon are positioned in the new code, but that is a situation that
should not occur in practice. Plus I'd argue that the new behavior is
actually better in that case.
2014-08-15 18:49:58 +02:00
Maarten ter Huurne 45304052cd Changed icon lookup in GMenu2X::drawButton(Right)
Look up the icon with the "skin:" prefix to be consistent with how
other lookups of the same icons are performed. Also don't perform
repeated lookups.
2014-08-15 17:59:55 +02:00
Maarten ter Huurne 8e885bc2c1 Removed GMenu2X::drawButton variant
This was only called form ButtonBox, so I moved the code there.

I still think a paint method shouldn't be repositioning widgets, but
that's something for a later cleanup.
2014-08-15 17:27:09 +02:00
Maarten ter Huurne faf0287cf5 Simplify code to reload section icons when skin changes 2014-08-15 16:54:30 +02:00
Maarten ter Huurne c1d57b62de In GMenu2X::drawButton, don't build a rectangle if we only need width 2014-08-15 16:35:24 +02:00
Maarten ter Huurne b202f1949a Minor optimizations in Menu
Use emplace_back when a new string is put into a vector.
Removed unused variables.
When searching section names, compare C++ string to C string, so we
don't need to construct a C++ string for the second string just for
the search.
2014-08-15 14:27:05 +02:00
Maarten ter Huurne 739a9463be Don't use string::compare if there are better alternatives
When comparing full strings, operator== will do nicely.
When comparing the first char, "s[0] == c" is more efficient if we know
the string cannot be empty.
2014-08-15 14:19:17 +02:00
Maarten ter Huurne 6e8491abee Don't use (void) for empty argument list
This is necessary in C, but unusual in C++.
2014-08-15 14:01:11 +02:00
Maarten ter Huurne d66ca947c4 Store Menu's Monitors in unique_ptrs
This results in a small code reduction and some ease of mind.
2014-08-15 13:56:20 +02:00
Maarten ter Huurne 7134eb3778 Store "bgmain" in the GMenu2X object instead of in SurfaceCollection
Since this surface is created by initBG instead of loaded from skin
search paths, it didn't really fit in SurfaceCollection. After removing
it, one of SurfaceCollection's methods could be removed as well.
2014-08-15 04:03:00 +02:00
Maarten ter Huurne ab7a4c1cf7 Have Font::write return the width of the drawn text
This avoids having to do separate getTextWidth calls in several places.

More getTextWidth calls could be saved by splitting the rendering of
the font to an off-screen buffer from the final composition onto the
destination surface: the routines that draw text inside a box have to
compute the width before they can draw the box and currently the box
has to be drawn before the text.
2014-08-15 03:32:32 +02:00
Maarten ter Huurne 6895ac0b54 Removed static version of SurfaceCollection::getSkinFilePath
There were no external callers anymore.
2014-08-15 01:46:35 +02:00
Maarten ter Huurne c8ea974f51 Fixed compile error on platforms where ENABLE_CPUFREQ is defined 2014-08-15 01:41:20 +02:00
Maarten ter Huurne 02384c8e72 Removed skin lookup feature from OffscreenSurface::loadImage
Instead, make the caller perform the lookup. This simplifies the
interface of loadImage and it removes the dependency from
OffscreenSurface on SurfaceCollection.
2014-08-15 01:34:35 +02:00
Maarten ter Huurne 2c47515321 Explicitly enable screen when PowerSaver exits
In theory the timer could expire between the button press that starts
the application launch and the moment the PowerSaver destructor runs.
And I think this does in fact happen occasionally thanks to the CPU
hogging SD controller driver on the GCW Zero, leading to an application
launching with a blanked screen.
2014-08-14 09:51:51 +02:00
Maarten ter Huurne c1689e41fb Dismantled Singleton pattern of PowerSaver
The instance-on-demand didn't really work, since we needed explicit
control over this object's destruction to ensure the timer is stopped
when launching an application. And trying to combine getInstance() with
explicit external delete was just ugly.
2014-08-14 09:47:05 +02:00
Maarten ter Huurne 8d662c95f6 Various cleanups in PowerSaver
Use nullptr instead of NULL, evaluate pointer as bool where possible.
Declare the timer callback as a friend function so the methods it uses
can stay private.
Initialize screenState to false; was uninitialized which means that
an initially blanked screen might not be unblanked.
Force screen enable in constructor instead of when timeout is set.
Add removeScreenTimer method.
Include C++-ified versions of the C headers.
2014-08-14 09:28:41 +02:00
Maarten ter Huurne 6a2a0969f3 Set close-on-exec flag on inotify file descriptor 2014-08-14 07:30:33 +02:00
Maarten ter Huurne 47d349f78b Simplified file extension filtering code
Don't create any temporary strings, just use the d_name field and
strcasecmp.
2014-08-14 06:43:37 +02:00
Maarten ter Huurne 404b2a1029 Use proper feature test macro for d_type support 2014-08-14 06:10:55 +02:00
Maarten ter Huurne 56beec0895 Convert file name to C++ string as late as possible
This avoids copying the string if the file is rejected because its
category (directory or regular file) is disabled.
2014-08-14 05:30:31 +02:00
Maarten ter Huurne b6f6bb387c Skip stat() call when the d_type field can tell us the file type
This is a BSD extension also present in glibc and uClibc. Not all libc
implementations and not all file systems support it, so we keep the
stat-based code as a fallback.
2014-08-14 05:25:26 +02:00
Maarten ter Huurne a505d3e494 Removed FileLister::insertFile
This was a nasty hack to insert English into the list of translations.
Instead of modifying the scan results, make a copy and modify the copy.
2014-08-14 05:08:36 +02:00
Maarten ter Huurne 85b072ca43 Removed FileLister::at
In the STL, 'at' will perform range checking while operator[] will not.
Since GMenu2X doesn't use exceptions, range checking is not possible,
so 'at' and operator[] were identical.
2014-08-14 05:02:24 +02:00
Maarten ter Huurne ba47744a52 Put trivial FileLister method implementations in header
This cuts a few hundred bytes off the stripped binary size.
2014-08-14 04:59:07 +02:00
Maarten ter Huurne eb3def19ea Replaced generic exclude mechanism by updir filter in FileLister
The exclude mechanism was only used to filter out ".." for the skins
lister, so this lighter filter does all we need.
2014-08-14 04:45:26 +02:00
Maarten ter Huurne 60aaf5954a Show a file selector in OPK links only if Exec requires a file
Previously, if a user installed a new version of an OPK with the same
name as one that had a link configuration file, GMenu2X could request
a file for an application whose new version required no files to
launch.

In practice, this transition would occur only once per OPK application,
when its developer decided to use a custom browser after having used
GMenu2X's file browser. This bug would then show GMenu2X's file browser
to request a file that would not even be passed to the application.

Thanks to Nebuleon for analyzing the problem and the above description.
2014-08-14 02:08:51 +02:00
Maarten ter Huurne 35724170f8 Make FileLister::browse not log a non-existing directory as an error
To avoid unnecessary existence checks, we consider browsing a
non-existing directory to be a normal situation.
2014-08-13 22:59:13 +02:00
Maarten ter Huurne 13f574bfb2 Avoid moving around entries unless new ones were found
When scanning multiple directories in succession, it's possible that
in some of them no matches are found. Avoid moving all pre-existing
entries back and forth in that case.

Also use move instead of copy for transferring the pre-existing
entries from the vector to the set.
2014-08-13 07:35:29 +02:00
Maarten ter Huurne 90a5f82b92 In FileLister::browse, declare variables when they are needed 2014-08-13 07:14:30 +02:00
Maarten ter Huurne 2c76ddae50 Removed special case handling for !(showDirectories || showFiles)
The special case is not necessary for correctness. It would help
performance in theory, but it seems very unlikely that any code would
request a directory scan when it interested in neither the subdirs nor
the files in that directory.
2014-08-13 07:08:04 +02:00
Maarten ter Huurne 46c64ea984 Move instead of copy strings at the end of FileLister::browse
This takes a little trickery, which I wrapped and documented.
2014-08-13 07:06:07 +02:00
Maarten ter Huurne a741653a13 Don't pre-strip file name extensions in Selector
This operation is not so slow that it really needs caching. Also
reducing the delay before showing a directory will have more impact
on the user experience than a slightly faster paint.
2014-08-12 09:17:38 +02:00
Maarten ter Huurne 1e50dff746 Moved code to remove a file name extension into utilities module 2014-08-12 09:07:37 +02:00
Maarten ter Huurne aa5d8faeb6 Removed SurfaceCollection::defaultAlpha
The Selector was the only user and that code was removed.
2014-08-12 08:50:52 +02:00
Maarten ter Huurne 1229ef32e5 Removed screenshot caching from Selector
The way the caching was implemented was rather problematic. Also I'm
not convinced caching is useful at all. So I removed it. If at some
point we decide that caching is a good idea, it would be best to
re-implement it from scratch, so nothing of value is lost by removing
the existing caching code.

One problem was that the prepare method would check the existence of
a screenshot file for every file in the directory, which adds to the
noticable delay before showing a directory which holds many files.

Another problem is there was no upper limit to the number of
screenshots that would be cached. On directories with many screenshots
the memory use could rise to several hundred megabytes, which can be
problematic on some of the systems we want to support.

Also there was a rather nasty hack present to ensure screenshots were
loaded without an alpha channel.

My main doubt about the usefulness of screenshot caching is that
caching will only speed up the showing of an image from the second
time onwards. In typical use, the average screenshot is displayed at
most once. If the image load time is long enough to annoy the user,
any real solution would have to work also for the first showing.
For example, background (asynchronous) loading could be implemented.
2014-08-12 08:50:04 +02:00
Maarten ter Huurne 3305cf289b Minor cleanups in Selector::exec
Declare variables where they are needed instead of way in advance.
2014-08-12 08:38:05 +02:00
Maarten ter Huurne b15175b05b Require valid section index passed to Menu::addActionLink
All calls to this method are in GMenu2X::initMenu() and they will only
pass valid indices, so the range check was redundant. Also the return
value was never used.

Added an assert to spot any invalid indices from future code.
2014-08-12 06:42:58 +02:00
Maarten ter Huurne cc38c8ec00 Fixed misleading indentation in Menu::addActionLink
A reader might have concluded that the icon was always set.
2014-08-12 06:30:56 +02:00
Maarten ter Huurne 6492ab2fa2 Apply a minimum size for the scroll bar thumb
On long lists the computed size can become less than 1 pixel.
2014-08-12 00:01:40 +02:00
Maarten ter Huurne 60183e539d Removed most uses of naked Surface pointers
However, SurfaceCollection remains a very important user.
2014-08-10 14:21:14 +02:00
Maarten ter Huurne 4bd1c799bd Created separate subclasses for output and off-screen surfaces
There are a few exclusive operations for each type. Also we no longer
need the freeWhenDone flag since the class now determines whether the
surface should be freed or not.
2014-08-10 13:25:54 +02:00
Maarten ter Huurne b833f59bb5 Show at least one row per page
In theory a font could be so large that no full row would fit on the
allotted space; in that case showing a partial row is better than
nothing and will avoid bugs where -1 wraps around on unsigned exprs.
2014-08-10 10:22:13 +02:00
Maarten ter Huurne 0535f8273b Rewrote the manual paging code
Nebuleon spotted a bug in TextManualDialog where the unsigned value
'pages[page].text.size() - rowsPerPage' could wrap around at 0 for
short chapters. I decided to change a bit more than just fixing the
bug though.
2014-08-10 10:09:42 +02:00
Maarten ter Huurne 799ebb9a29 Let SurfaceCollection::add make a copy of an existing surface
This allows the SurfaceCollection to claim ownership of the surface,
while ownership was undefined before.
2014-08-10 05:42:41 +02:00
Maarten ter Huurne aff5f53f7d Don't pass around naked Surface pointers when drawing
Use references instead.
2014-08-10 04:26:59 +02:00
Maarten ter Huurne 20c5ec4eb6 Put Surface argument first in Dialog paint methods
This is the convention that most classes stick to. The likely reason
why Dialog didn't stick to the convention was to be able to provide
a default value for this argument, but that feature wasn't very useful
since every caller already had access to the default surface.
2014-08-10 04:02:10 +02:00
Maarten ter Huurne 5a281cf661 Implement the standard set of copy and move operations for Surface 2014-08-10 03:18:32 +02:00
Maarten ter Huurne 614f74a63d Removed halfW/H caching in Surface
This is a very cheap computation, no need to cache it.
2014-08-10 02:55:52 +02:00
Maarten ter Huurne 7c4fa5d77a Launch application after destructing menu
This allows all destructors to do their job before we pass control to
a new process.
2014-08-10 02:09:34 +02:00
Maarten ter Huurne 303ecf298a Moved console bind+activate code into Launcher
This way, we can launch console applications from the Explorer.
2014-08-08 02:04:05 +02:00
Maarten ter Huurne dc23718f7f Enable "Display Console" flag for all platforms
Applications with this flag will not have their stdout/stderr
redirected to a log file when logging is enabled. That is a useful
feature also on platforms where we don't micromanage the console.
2014-08-08 01:35:51 +02:00
Maarten ter Huurne 3e34124e68 Added Launcher class
Currently the launched application is exec-ed from deep within the menu
code. This means a lot of destructors are never run and as a result
for example file descriptors from the menu remain open in the launched
process.

This is a first step to move the launch invocation from a deep call
stack to the top level.
2014-08-07 16:48:30 +02:00
Maarten ter Huurne e32964bb50 Made Font ownership explicit using unique_ptr 2014-07-31 23:20:31 +02:00
Maarten ter Huurne 902145b698 Removed Surface::write method
All it did was redirect to Font::write, so it's better to call that
method directly.
2014-07-31 23:16:51 +02:00
Maarten ter Huurne 3a57912dca Removed ChangeLog again
Setting the Automake strictness to 'foreign' disables enforcement
of GNU project rules. Thanks to Paul Wise for the tip.
2014-07-25 06:34:34 +02:00
Maarten ter Huurne 98413bac90 Make a pro-forma ChangeLog file
Automake considers an absent ChangeLog to be fatal.
2014-07-24 20:36:44 +02:00
Maarten ter Huurne 5af64ef29f Update translation credit for Norwegian 2014-07-24 14:15:40 +02:00
Maarten ter Huurne 8722b355ac Use a single "about.txt" for all platforms
The content of this file is not actually platform specific, so having
it in multiple locations is just an extra maintenance burden.
2014-07-24 14:13:55 +02:00
Maarten ter Huurne 48dcfc95c5 Removed ChangeLog
It is not maintained. Please read the Git log instead.
2014-07-24 14:09:35 +02:00
Maarten ter Huurne e44db20f31 Changed word wrapping to avoid trailing empty line
If a text file ends in a newline, the previous line splitting code
would append an empty line at the end, which looked odd. So now we
explicitly check that a newline is only inserted if more characters
follow.
2014-07-24 12:34:19 +02:00
Maarten ter Huurne 485bab3b48 Made text file reading more efficient and robust
Instead of reading the file line by line and then concatenating those
lines, just load the entire thing in one go. And pay more attention to
error conditions.
2014-07-24 10:41:20 +02:00
Maarten ter Huurne d4344c7960 Revert "Removed support for multi-line text drawing"
This reverts commit 0908aa7bb7.

It turns out there are multi-line text messages in use: Nebuleon found
one in the confirmation message when deleting a section.
2014-07-23 02:22:27 +02:00
Maarten ter Huurne c311a7f16d Add imgv (image viewer) icon to 320x240 Scanlines* skins 2014-07-19 22:36:51 +02:00
Maarten ter Huurne 4744b5ec32 Add icons that were missing in 320x240 Default skin 2014-07-19 22:36:46 +02:00
Maarten ter Huurne 79dcc8a146 Implemented stream output for RGBAColor
In commit 950518f3 I changed the component type of RGBAColor from
16-bit to 8-bit integers. Unfortunately, in C++ 8-bit integers are
identical to characters, so this broke the writing of colors to
output streams.
2014-07-19 12:06:09 +02:00
Maarten ter Huurne 9b93eabcc5 Clean up trim() utility function 2014-07-19 02:32:30 +02:00
Maarten ter Huurne 5e31df9e0f Use horizontal ruler in About texts
The special case of a line containing four dashes is rendered as a
pretty horizontal ruler, but the About texts contained five...
2014-07-19 01:33:28 +02:00
Maarten ter Huurne 9d0a49524f Optimized PNG encoding
This reduces the installed size.
2014-07-19 00:43:06 +02:00
Maarten ter Huurne de2fdfca87 Remove SDL_gfx as a dependency
The Surface class was changed to not use routines from SDL_gfx anymore.
2014-07-19 00:43:06 +02:00
Maarten ter Huurne 08ffbc76de Use our own alpha blended rectangle outline instead of SDL_gfx
I implemented it as four 1-pixel-wide filled rectangles. While this
is not the fastest way to do it, I doubt this will have a significant
impact on overall performance.

Note that the proper way to clip a rectangle outline is to clip the
outline's four lines individually, not clip the rectangle and then
draw a smaller rectangle outline. This means that an optimized drawing
routine would have to be aware of whether clipping occurs, complicating
the code.
2014-07-19 00:43:06 +02:00
Maarten ter Huurne 0dd027fcf4 Use our own alpha blended rectangle fill instead of SDL_gfx
I want to remove the dependency on SDL_gfx, since only two functions
from that library are actually used. Also this new blend implementation
is more optimized, especially the 32bpp case, which is the one most
platforms are using.
2014-07-19 00:43:06 +02:00
Maarten ter Huurne 9df565b73c Made rect+color the preferred argument style for (filled) rectangle drawing
This is the opposite of the old situation, when the structs were
unraveled.

The definitions for the alternative styles were moved to the header,
so the compiler has more opportunities for optimizing the conversions.
2014-07-19 00:43:06 +02:00
Maarten ter Huurne dd27cb2e07 Add constructors for RGBAColor 2014-07-19 00:43:06 +02:00
Maarten ter Huurne 5f454a8569 Made strtorgba into a factory method of RGBAColor
The method is RGBAColor::fromString.
2014-07-19 00:43:06 +02:00
Maarten ter Huurne 950518f3a7 Made RGBAColor components 8 bits wide
The values are all in the range 0..255, so storing more than 8 bits
is wasteful.
2014-07-19 00:43:05 +02:00
Maarten ter Huurne 2a4b519282 Removed return code from surface drawing methods
Not a single caller actually checked the return codes.
2014-07-19 00:43:05 +02:00