2017-10-28 18:28:14 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 Chris Pavlina <pavlina.chris@gmail.com>
|
|
|
|
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
|
2019-06-05 19:15:57 +00:00
|
|
|
* Copyright (C) 2014-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
2017-10-28 18:28:14 +00:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation, either version 3 of the License, or (at your
|
|
|
|
* option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
#ifndef LIB_TREE_MODEL_ADAPTER_H
|
|
|
|
#define LIB_TREE_MODEL_ADAPTER_H
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
#include <lib_id.h>
|
2018-07-27 20:47:51 +00:00
|
|
|
#include <lib_tree_model.h>
|
2017-10-28 18:28:14 +00:00
|
|
|
#include <wx/hashmap.h>
|
|
|
|
#include <wx/dataview.h>
|
2019-02-10 00:57:53 +00:00
|
|
|
#include <wx/headerctrl.h>
|
2017-10-28 18:28:14 +00:00
|
|
|
#include <vector>
|
|
|
|
#include <functional>
|
2020-02-07 17:06:24 +00:00
|
|
|
#include <set>
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Adapter class in the component selector Model-View-Adapter (mediated MVC)
|
|
|
|
* architecture. The other pieces are in:
|
|
|
|
*
|
|
|
|
* - Model: CMP_TREE_NODE and descendants in eeschema/cmp_tree_model.h
|
|
|
|
* - View:
|
|
|
|
* - DIALOG_CHOOSE_COMPONENT in eeschema/dialogs/dialog_choose_component.h
|
|
|
|
* - wxDataViewCtrl
|
|
|
|
*
|
|
|
|
* This adapter presents the interface specified by wxDataViewModel to the
|
|
|
|
* wxDataViewCtrl:
|
|
|
|
*
|
|
|
|
* +---+ +------------------+
|
|
|
|
* +---+ Generates | A | | VIEW |
|
|
|
|
* | M | from libs | D | wxDataViewModel |------------------|
|
|
|
|
* | O | <---------- | A | <------------------> | wxDataViewCtrl |
|
|
|
|
* | D | | P | |------------------|
|
|
|
|
* | E | <---------> | T | <------------------- | wxTextCtrl |
|
|
|
|
* | L | UpdateScore | E | UpdateSearchString() |------------------|
|
|
|
|
* +---+ | R | | |
|
|
|
|
* +---+ +------------------+
|
|
|
|
*
|
|
|
|
* Because this adapter is a wxDataViewModel, it is reference-counted by
|
|
|
|
* wxObject. To ensure this interface is used correctly, the constructor
|
2018-07-27 20:47:51 +00:00
|
|
|
* is private; LIB_TREE_MODEL_ADAPTER should be created by the static
|
|
|
|
* factory method LIB_TREE_MODEL_ADAPTER::Create().
|
2017-10-28 18:28:14 +00:00
|
|
|
*
|
|
|
|
* Quick summary of methods used to drive this class:
|
|
|
|
*
|
|
|
|
* - `SetFilter()` - set whether the view is restricted to power parts
|
|
|
|
* - `ShowUnits()` - set whether units are displayed
|
|
|
|
* - `SetPreselectNode()` - set a node to highlight when not searching
|
|
|
|
* - `AddLibrary()` - populate the model with all aliases in a library
|
|
|
|
* - `AddAliasList()` - populate the model with a specific list of aliases
|
|
|
|
*
|
|
|
|
* Quick summary of methods used by the View:
|
|
|
|
*
|
|
|
|
* - `UpdateSearchString()` - pass in the user's search text
|
|
|
|
* - `AttachTo()` - pass in the wxDataViewCtrl
|
|
|
|
* - `GetAliasFor()` - get the LIB_ALIAS* for a selected item
|
|
|
|
* - `GetUnitFor()` - get the unit for a selected item
|
|
|
|
* - `GetComponentsCount()` - count the aliases loaded
|
|
|
|
*
|
|
|
|
* Methods implemented as part of wxDataViewModel:
|
|
|
|
*
|
|
|
|
* - `HasContainerColumns()` - whether a parent item has more than one column
|
|
|
|
* - `IsContainer()` - whether an item is a parent
|
|
|
|
* - `GetParent()` - return the parent of an item, or invalid if root
|
|
|
|
* - `GetChildren()` - get the children of an item
|
|
|
|
* - `GetColumnCount()` - get the number of columns in the view
|
|
|
|
* - `GetColumnType()` - get the data type shown in each column
|
|
|
|
* - `GetValue()` - get the data shown in a cell
|
|
|
|
* - `SetValue()` - edit the data in a cell (does nothing)
|
|
|
|
* - `GetAttr()` - get any per-item formatting
|
|
|
|
* - `Compare()` - compare two rows, for sorting
|
|
|
|
* - `HasDefaultCompare()` - whether sorted by default
|
|
|
|
*/
|
2019-09-18 23:00:48 +00:00
|
|
|
|
2020-01-13 01:44:19 +00:00
|
|
|
class APP_SETTINGS_BASE;
|
2019-06-05 19:15:57 +00:00
|
|
|
class TOOL_INTERACTIVE;
|
2020-03-24 01:01:23 +00:00
|
|
|
class EDA_BASE_FRAME;
|
|
|
|
|
2019-06-05 19:15:57 +00:00
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
class LIB_TREE_MODEL_ADAPTER: public wxDataViewModel
|
2017-10-28 18:28:14 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reference-counting container for a pointer to CMP_TREE_MODEL_ADAPTER_BASE.
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
typedef wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> PTR;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Destructor. Do NOT delete this class manually; it is reference-counted
|
|
|
|
* by wxObject.
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
~LIB_TREE_MODEL_ADAPTER();
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This enum allows a selective filtering of components to list
|
|
|
|
*/
|
|
|
|
enum CMP_FILTER_TYPE
|
|
|
|
{
|
|
|
|
CMP_FILTER_NONE, ///< no filtering
|
|
|
|
CMP_FILTER_POWER, ///< list components flagged PWR
|
|
|
|
};
|
|
|
|
|
2019-09-18 23:00:48 +00:00
|
|
|
/**
|
|
|
|
* This enum defines the order of the columns in the tree view
|
|
|
|
*/
|
|
|
|
enum TREE_COLS
|
|
|
|
{
|
|
|
|
PART_COL = 0, ///< Part name column
|
|
|
|
DESC_COL, ///< Part description column
|
|
|
|
NUM_COLS ///< The number of tree columns
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the column widths to the config file. This requires the tree view to still be
|
|
|
|
* valid.
|
|
|
|
*/
|
|
|
|
void SaveColWidths();
|
2020-02-07 17:06:24 +00:00
|
|
|
void SavePinnedItems();
|
2019-09-18 23:00:48 +00:00
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
/**
|
|
|
|
* Set the component filter type. Must be set before adding libraries
|
|
|
|
*
|
|
|
|
* @param aFilter if CMP_FILTER_POWER, only power parts are loaded
|
|
|
|
*/
|
|
|
|
void SetFilter( CMP_FILTER_TYPE aFilter );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the active filter.
|
|
|
|
*/
|
|
|
|
CMP_FILTER_TYPE GetFilter() const { return m_filter; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether or not to show units. May be set at any time; updates at the next
|
|
|
|
* UpdateSearchString()
|
|
|
|
*
|
|
|
|
* @param aShow if true, units are displayed
|
|
|
|
*/
|
|
|
|
void ShowUnits( bool aShow );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the component name to be selected if there are no search results.
|
|
|
|
* May be set at any time; updates at the next UpdateSearchString().
|
|
|
|
*
|
|
|
|
* @param aLibId symbol #LIB_ID to be selected
|
|
|
|
* @param aUnit unit to be selected, if > 0 (0 selects the alias itself)
|
|
|
|
*/
|
|
|
|
void SetPreselectNode( LIB_ID const& aLibId, int aUnit );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the given list of components by alias. To be called in the setup
|
|
|
|
* phase.
|
|
|
|
*
|
2018-07-27 20:47:51 +00:00
|
|
|
* @param aNodeName the parent node the components will appear under
|
|
|
|
* @param aDesc the description field of the parent node
|
2018-07-30 13:18:37 +00:00
|
|
|
* @param aItemList list of components
|
2017-10-28 18:28:14 +00:00
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
void DoAddLibrary( wxString const& aNodeName, wxString const& aDesc,
|
2018-08-05 11:56:02 +00:00
|
|
|
std::vector<LIB_TREE_ITEM*> const& aItemList, bool presorted );
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sort the tree and assign ranks after adding libraries.
|
|
|
|
*/
|
|
|
|
void AssignIntrinsicRanks() { m_tree.AssignIntrinsicRanks(); }
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the search string provided by the user.
|
|
|
|
*
|
|
|
|
* @param aSearch full, unprocessed search text
|
|
|
|
*/
|
|
|
|
void UpdateSearchString( wxString const& aSearch );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attach to a wxDataViewCtrl and initialize it. This will set up columns
|
|
|
|
* and associate the model via the adapter.
|
|
|
|
*
|
|
|
|
* @param aDataViewCtrl the view component in the dialog
|
|
|
|
*/
|
|
|
|
void AttachTo( wxDataViewCtrl* aDataViewCtrl );
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the alias for the given item.
|
|
|
|
*
|
|
|
|
* @param aSelection item from the wxDataViewCtrl
|
|
|
|
* (see wxDataViewCtrl::GetSelection())
|
|
|
|
*
|
|
|
|
* @return alias, or nullptr if none is selected
|
|
|
|
*/
|
|
|
|
LIB_ID GetAliasFor( const wxDataViewItem& aSelection ) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the unit for the given item.
|
|
|
|
*
|
|
|
|
* @param aSelection item from the wxDataViewCtrl
|
|
|
|
* (see wxDataViewCtrl::GetSelection())
|
|
|
|
*
|
|
|
|
* @return Unit, or zero if the alias itself is selected. Return valid is
|
|
|
|
* invalid if GetAliasFor() returns nullptr.
|
|
|
|
*/
|
|
|
|
int GetUnitFor( const wxDataViewItem& aSelection ) const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return node type for the given item.
|
|
|
|
*
|
|
|
|
* @param aSelection item from the wxDataViewCtrl
|
|
|
|
* (see wxDataViewCtrl::GetSelection())
|
|
|
|
*
|
|
|
|
* @return Type of the selected node, might be INVALID.
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE::TYPE GetTypeFor( const wxDataViewItem& aSelection ) const;
|
|
|
|
|
2020-02-07 17:06:24 +00:00
|
|
|
LIB_TREE_NODE* GetTreeNodeFor( const wxDataViewItem& aSelection ) const;
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
virtual wxString GenerateInfo( LIB_ID const& aLibId, int aUnit ) { return wxEmptyString; };
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the number of components loaded in the tree.
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
int GetItemCount() const;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2017-11-22 11:06:17 +00:00
|
|
|
/**
|
|
|
|
* Return the number of libraries loaded in the tree.
|
|
|
|
*/
|
|
|
|
virtual int GetLibrariesCount() const
|
|
|
|
{
|
2020-02-07 17:06:24 +00:00
|
|
|
return m_tree.m_Children.size();
|
2017-11-22 11:06:17 +00:00
|
|
|
}
|
|
|
|
|
2017-11-14 11:03:19 +00:00
|
|
|
/**
|
|
|
|
* Returns tree item corresponding to part.
|
|
|
|
*
|
|
|
|
* @param aLibId specifies the part and library name to be searched for.
|
|
|
|
* @return Tree data item representing the part. Might be invalid if nothings was found.
|
|
|
|
*/
|
|
|
|
wxDataViewItem FindItem( const LIB_ID& aLibId );
|
|
|
|
|
2017-11-21 13:54:30 +00:00
|
|
|
/**
|
|
|
|
* Populate a list of all the children of an item
|
|
|
|
*
|
|
|
|
* @return number of children
|
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
unsigned int GetChildren( wxDataViewItem const& aItem,
|
|
|
|
wxDataViewItemArray& aChildren ) const override;
|
2017-11-21 13:54:30 +00:00
|
|
|
|
2018-04-28 09:43:41 +00:00
|
|
|
// Freezing/Thawing. Used when updating the table model so that we don't try and fetch
|
|
|
|
// values during updating. Primarily a problem on OSX which doesn't pay attention to the
|
|
|
|
// wxDataViewCtrl's freeze count when updating the keyWindow.
|
|
|
|
void Freeze() { m_freeze++; }
|
|
|
|
void Thaw() { m_freeze--; }
|
|
|
|
bool IsFrozen() const { return m_freeze; }
|
|
|
|
|
2020-03-31 01:09:01 +00:00
|
|
|
void RefreshTree();
|
2020-03-08 15:14:22 +00:00
|
|
|
|
2019-06-05 19:15:57 +00:00
|
|
|
// Allows subclasses to nominate a context menu handler.
|
|
|
|
virtual TOOL_INTERACTIVE* GetContextMenuTool() { return nullptr; }
|
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
protected:
|
2018-07-27 20:47:51 +00:00
|
|
|
static wxDataViewItem ToItem( LIB_TREE_NODE const* aNode );
|
2020-02-07 17:06:24 +00:00
|
|
|
static LIB_TREE_NODE* ToNode( wxDataViewItem aItem );
|
2018-07-27 20:47:51 +00:00
|
|
|
static unsigned int IntoArray( LIB_TREE_NODE const& aNode, wxDataViewItemArray& aChildren );
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE_ROOT m_tree;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2020-05-31 21:42:04 +00:00
|
|
|
/**
|
|
|
|
* Creates the adapter
|
|
|
|
* @param aParent is the parent frame
|
|
|
|
* @param aPinnedKey is the key to load the pinned libraries list from the project file
|
|
|
|
*/
|
|
|
|
LIB_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, wxString aPinnedKey );
|
2020-02-14 14:22:21 +00:00
|
|
|
|
|
|
|
LIB_TREE_NODE_LIB& DoAddLibraryNode( wxString const& aNodeName, wxString const& aDesc );
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check whether a container has columns too
|
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
bool HasContainerColumns( wxDataViewItem const& aItem ) const override;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Check whether an item can have children.
|
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
bool IsContainer( wxDataViewItem const& aItem ) const override;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the parent of an item.
|
|
|
|
*
|
|
|
|
* @return parent of aItem, or an invalid wxDataViewItem if parent is root
|
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
wxDataViewItem GetParent( wxDataViewItem const& aItem ) const override;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2019-09-18 23:00:48 +00:00
|
|
|
unsigned int GetColumnCount() const override { return NUM_COLS; }
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
2019-06-05 19:15:57 +00:00
|
|
|
* Return the type of data stored in the column as indicated by wxVariant::GetType()
|
2017-10-28 18:28:14 +00:00
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
wxString GetColumnType( unsigned int aCol ) const override { return "string"; }
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the value of an item.
|
|
|
|
*
|
|
|
|
* @param aVariant wxVariant to receive the data
|
|
|
|
* @param aItem item whose data will be placed into aVariant
|
|
|
|
* @param aCol column number of the data
|
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
void GetValue( wxVariant& aVariant,
|
|
|
|
wxDataViewItem const& aItem,
|
|
|
|
unsigned int aCol ) const override;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the value of an item. Does nothing - this model doesn't support
|
|
|
|
* editing.
|
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
bool SetValue( wxVariant const& aVariant,
|
|
|
|
wxDataViewItem const& aItem,
|
|
|
|
unsigned int aCol ) override { return false; }
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get any formatting for an item.
|
|
|
|
*
|
|
|
|
* @param aItem item to get formatting for
|
|
|
|
* @param aCol column number of interest
|
|
|
|
* @param aAttr receiver for attributes
|
|
|
|
* @return true iff the item has non-default attributes
|
|
|
|
*/
|
2019-06-05 19:15:57 +00:00
|
|
|
bool GetAttr( wxDataViewItem const& aItem,
|
|
|
|
unsigned int aCol,
|
|
|
|
wxDataViewItemAttr& aAttr ) const override;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2020-02-16 09:21:36 +00:00
|
|
|
/**
|
|
|
|
* @return a unicode string to mark a node name like
|
|
|
|
* a pinned library name
|
|
|
|
* This is not an ascii7 char, but a unicode char
|
|
|
|
*/
|
|
|
|
const wxString GetPinningSymbol() const
|
|
|
|
{
|
|
|
|
return wxString::FromUTF8( "☆ " );
|
|
|
|
}
|
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
private:
|
2020-02-14 14:22:21 +00:00
|
|
|
EDA_BASE_FRAME* m_parent;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2020-02-14 14:22:21 +00:00
|
|
|
CMP_FILTER_TYPE m_filter;
|
|
|
|
bool m_show_units;
|
|
|
|
LIB_ID m_preselect_lib_id;
|
|
|
|
int m_preselect_unit;
|
|
|
|
int m_freeze;
|
2017-10-28 18:28:14 +00:00
|
|
|
|
2020-02-14 14:22:21 +00:00
|
|
|
wxDataViewColumn* m_col_part;
|
|
|
|
wxDataViewColumn* m_col_desc;
|
|
|
|
wxDataViewCtrl* m_widget;
|
2019-09-18 23:00:48 +00:00
|
|
|
|
2020-02-14 14:22:21 +00:00
|
|
|
int m_colWidths[NUM_COLS];
|
|
|
|
wxArrayString m_pinnedLibs;
|
2020-05-31 21:42:04 +00:00
|
|
|
wxString m_pinnedKey;
|
2020-02-07 17:06:24 +00:00
|
|
|
|
2017-10-28 18:28:14 +00:00
|
|
|
/**
|
2020-02-14 14:22:21 +00:00
|
|
|
* Find any results worth highlighting and expand them, according to given criteria
|
2018-06-10 22:20:06 +00:00
|
|
|
* The highest-scoring node is written to aHighScore
|
2017-10-28 18:28:14 +00:00
|
|
|
*/
|
2020-02-14 14:22:21 +00:00
|
|
|
void FindAndExpand( LIB_TREE_NODE& aNode, std::function<bool( LIB_TREE_NODE const* )> aFunc,
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE** aHighScore );
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
2018-06-10 22:20:06 +00:00
|
|
|
* Find and expand successful search results. Return the best match (if any).
|
2017-10-28 18:28:14 +00:00
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* ShowResults();
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
2018-06-10 22:20:06 +00:00
|
|
|
* Find and expand preselected node. Return the best match (if any).
|
2017-10-28 18:28:14 +00:00
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* ShowPreselect();
|
2017-10-28 18:28:14 +00:00
|
|
|
|
|
|
|
/**
|
2018-06-10 22:20:06 +00:00
|
|
|
* Find and expand a library if there is only one. Return the best match (if any).
|
2017-10-28 18:28:14 +00:00
|
|
|
*/
|
2018-07-27 20:47:51 +00:00
|
|
|
LIB_TREE_NODE* ShowSingleLibrary();
|
2017-10-28 18:28:14 +00:00
|
|
|
};
|
|
|
|
|
2018-07-27 20:47:51 +00:00
|
|
|
#endif // LIB_TREE_MODEL_ADAPTER_H
|
2017-10-28 18:28:14 +00:00
|
|
|
|