emailuis/nmhswidget/src/nmhswidget.cpp
branchRCL_3
changeset 63 d189ee25cf9d
equal deleted inserted replaced
61:dcf0eedfc1a3 63:d189ee25cf9d
       
     1 /*
       
     2  * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     3  * All rights reserved.
       
     4  * This component and the accompanying materials are made available
       
     5  * under the terms of "Eclipse Public License v1.0"
       
     6  * which accompanies this distribution, and is available
       
     7  * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8  *
       
     9  * Initial Contributors:
       
    10  * Nokia Corporation - initial contribution.
       
    11  *
       
    12  * Contributors:
       
    13  *
       
    14  * Description: 
       
    15  *
       
    16  */
       
    17 #include <QtGui>
       
    18 #include <QGraphicsLinearLayout>
       
    19 #include <hbcolorscheme.h>
       
    20 #include <hbdocumentloader.h>
       
    21 #include <QTranslator>
       
    22 #include <hbframedrawer.h>
       
    23 #include <hbframeitem.h>
       
    24 #include <hblabel.h>
       
    25 #include "nmcommon.h"
       
    26 #include "nmhswidget.h"
       
    27 #include "nmhswidgetemailengine.h"
       
    28 #include "nmmessageenvelope.h"
       
    29 #include "nmhswidgettitlerow.h"
       
    30 #include "nmhswidgetemailrow.h"
       
    31 #include "nmhswidgetconsts.h"
       
    32 #include "nmhswidgetdatetimeobserver.h"
       
    33 #include "emailtrace.h"
       
    34 
       
    35 NmHsWidget::NmHsWidget(QGraphicsItem *parent, Qt::WindowFlags flags)
       
    36     : HbWidget(parent, flags), 
       
    37       mMainContainer(0),
       
    38       mEmptySpaceContainer(0),
       
    39       mWidgetContainer(0),
       
    40       mTitleRow(0),
       
    41       mContentContainer(0),
       
    42       mNoMailsLabel(0),
       
    43       mContentLayout(0),              
       
    44       mBackgroundFrameDrawer(0),
       
    45       mTranslator(0),
       
    46       mEngine(0),      
       
    47       mAccountId(0),
       
    48       mAccountIconName(),
       
    49       mDateObserver(0),
       
    50       mIsExpanded(false)
       
    51 {
       
    52     NM_FUNCTION;
       
    53 }
       
    54 
       
    55 /*!
       
    56  Destructor
       
    57  */
       
    58 NmHsWidget::~NmHsWidget()
       
    59 {
       
    60     NM_FUNCTION;
       
    61 
       
    62     delete mTranslator;
       
    63     mTranslator = NULL;
       
    64 
       
    65     delete mEngine;
       
    66     mEngine = NULL;
       
    67 
       
    68     delete mBackgroundFrameDrawer;
       
    69     mBackgroundFrameDrawer = NULL;
       
    70 
       
    71     delete mDateObserver;
       
    72     mDateObserver = NULL;
       
    73 }
       
    74 
       
    75 /*!
       
    76  \fn QPainterPath NmHsWidget::shape()
       
    77 
       
    78  Called by home screen fw to check widget boundaries, needed to draw
       
    79  outside widget boundingRect.
       
    80  /return QPainterPath path describing actual boundaries of widget 
       
    81   including child items
       
    82  */
       
    83 QPainterPath NmHsWidget::shape() const
       
    84 {
       
    85     NM_FUNCTION;
       
    86     
       
    87     QPainterPath path;
       
    88     path.setFillRule(Qt::WindingFill);
       
    89     if (mWidgetContainer){
       
    90         //add mWidgetContainer using geometry to get
       
    91         //correct point for top-left-corner
       
    92         QRectF widgetRect = mWidgetContainer->geometry();
       
    93         path.addRect(widgetRect); 
       
    94         
       
    95         //then fetch shape from title row 
       
    96         QPainterPath titlepath;
       
    97         titlepath.addPath(mTitleRow->shape());
       
    98         //translate it's location to be inside mWidgetContainer
       
    99         titlepath.translate(widgetRect.topLeft());
       
   100         //and finally add it to path
       
   101         path.addPath(titlepath);    
       
   102     }
       
   103     //simplified path, i.e. only outlines
       
   104     return path.simplified();
       
   105 }
       
   106 
       
   107 /*!
       
   108  \fn void NmHsWidget::onShow()
       
   109 
       
   110  called by home screen fw when widget gets visible
       
   111  */
       
   112 void NmHsWidget::onShow()
       
   113 {
       
   114     NM_FUNCTION;
       
   115     if (mEngine) {
       
   116         mEngine->activate();
       
   117     }
       
   118 }
       
   119 
       
   120 /*!
       
   121  \fn void NmHsWidget::onHide()
       
   122 
       
   123  called by home screen fw when widget gets hidden
       
   124  */
       
   125 void NmHsWidget::onHide()
       
   126 {
       
   127     NM_FUNCTION;
       
   128     if (mEngine) {
       
   129         mEngine->suspend();
       
   130     }
       
   131 }
       
   132 
       
   133 /*!
       
   134  \fn bool NmHsWidget::loadDocML(HbDocumentLoader &loader)
       
   135  
       
   136  loads layout data and child items from docml file. Must be called after constructor.
       
   137  /return true if loading succeeded, otherwise false. False indicates that object is unusable
       
   138  */
       
   139 bool NmHsWidget::loadDocML(HbDocumentLoader &loader)
       
   140 {
       
   141     NM_FUNCTION;
       
   142     
       
   143     bool ok(false);
       
   144     loader.load(KNmHsWidgetDocML, &ok);
       
   145     
       
   146     if(ok) {
       
   147         mMainContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetMainContainer));  
       
   148         mWidgetContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetContainer));
       
   149         mContentContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetContentContainer));
       
   150         mEmptySpaceContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetEmptySpaceContainer));
       
   151         mNoMailsLabel = static_cast<HbLabel*> (loader.findWidget(KNmHsWidgetNoMailsLabel));
       
   152         if (!mMainContainer || !mWidgetContainer || !mContentContainer 
       
   153                 || !mEmptySpaceContainer || !mNoMailsLabel ) {
       
   154             //something failed in documentloader, no point to continue
       
   155             NM_ERROR(1,"NmHsWidget::loadDocML fail @ containers or label");
       
   156             ok = false;
       
   157         }
       
   158     }
       
   159     return ok;
       
   160 }
       
   161 
       
   162 /*!
       
   163  Initializes Localization.
       
   164  /post mTranslator constructed & localization file loaded
       
   165  */
       
   166 void NmHsWidget::setupLocalization()
       
   167 {
       
   168     NM_FUNCTION;
       
   169 
       
   170     //Use correct localisation
       
   171     mTranslator = new QTranslator();
       
   172     QString lang = QLocale::system().name();
       
   173     mTranslator->load(KNmHsWidgetLocFileName + lang, KNmHsWidgetLocLocation);
       
   174     QCoreApplication::installTranslator(mTranslator);
       
   175 }
       
   176 
       
   177 /*!
       
   178  Initializes UI. Everything that is not done in docml files should be here.
       
   179  return true if ok, in error false.
       
   180  */
       
   181 void NmHsWidget::setupUi()
       
   182 {
       
   183     NM_FUNCTION;
       
   184 
       
   185     //main level layout needed to control docml objects
       
   186     QGraphicsLinearLayout *widgetLayout = new QGraphicsLinearLayout(Qt::Vertical);
       
   187     widgetLayout->setContentsMargins(KNmHsWidgetContentsMargin, KNmHsWidgetContentsMargin,
       
   188             KNmHsWidgetContentsMargin, KNmHsWidgetContentsMargin);
       
   189     widgetLayout->setSpacing(KNmHsWidgetContentsMargin);
       
   190     widgetLayout->addItem(mMainContainer);
       
   191     this->setLayout(widgetLayout);
       
   192 
       
   193     //fetch pointer to content container layout
       
   194     //to be able to add/remove email rows and no mails label
       
   195     mContentLayout = (QGraphicsLinearLayout*) mContentContainer->layout();
       
   196     
       
   197     //set noMailsLabel properties not supported by doc loader 
       
   198     QColor newFontColor;
       
   199     newFontColor = HbColorScheme::color("qtc_hs_list_item_content_normal");
       
   200     mNoMailsLabel->setTextColor(newFontColor);
       
   201     mNoMailsLabel->setVisible(true);   
       
   202     
       
   203     mContentLayout->removeItem(mNoMailsLabel);
       
   204     
       
   205     //widget background
       
   206     mBackgroundFrameDrawer = new HbFrameDrawer(KNmHsWidgetBackgroundImage,
       
   207         HbFrameDrawer::NinePieces);
       
   208     HbFrameItem* backgroundLayoutItem = new HbFrameItem(mBackgroundFrameDrawer);
       
   209     //set to NULL to indicate that ownership transferred
       
   210     mBackgroundFrameDrawer = NULL;
       
   211     mWidgetContainer->setBackgroundItem(backgroundLayoutItem);
       
   212 }
       
   213 
       
   214 /*!
       
   215  Initializes the widget.
       
   216  
       
   217  called by home screen fw when widget is added to home screen
       
   218  */
       
   219 void NmHsWidget::onInitialize()
       
   220 {
       
   221     NM_FUNCTION;
       
   222     
       
   223     QT_TRY {
       
   224 	    // Use document loader to load the contents
       
   225 	    HbDocumentLoader loader;
       
   226 		//setup localization before docml loading
       
   227 	    setupLocalization();
       
   228 		
       
   229 	    //load containers and mNoMailsLabel
       
   230         if (!loadDocML(loader)) {
       
   231             NM_ERROR(1,"NmHsWidget::onInitialize Fail @ loader");
       
   232             emit error(); //failure, no point to continue
       
   233             return;
       
   234         }
       
   235 
       
   236         //construct title row
       
   237         mTitleRow = new NmHsWidgetTitleRow(this);
       
   238         if (!mTitleRow->setupUI(loader)) {
       
   239             //title row creation failed
       
   240             NM_ERROR(1,"NmHsWidget::onInitialize fail @ titlerow");
       
   241             emit error(); //failure, no point to continue
       
   242             return;            
       
   243         }
       
   244 				
       
   245         setupUi();
       
   246 
       
   247         //Engine construction is 2 phased. 
       
   248         mEngine = new NmHsWidgetEmailEngine(mAccountId);
       
   249         //Client must connect to exception signals before calling the initialize function
       
   250         //because we don't want to miss any signals.
       
   251         connect(mEngine, SIGNAL( exceptionOccured(const int&) ), this,
       
   252             SLOT( onEngineException(const int&) ));
       
   253         if (!mEngine->initialize()) {
       
   254             //engine construction failed. Give up.
       
   255             NM_ERROR(1,"NmHsWidget::onInitialize fail @ engine");
       
   256             emit error();
       
   257             return;
       
   258         }
       
   259 
       
   260         mTitleRow->updateAccountName(mEngine->accountName());
       
   261 
       
   262         //create observer for date/time change events
       
   263         mDateObserver = new NmHsWidgetDateTimeObserver();
       
   264 
       
   265         //Crete MailRows and associated connections
       
   266         createMailRowsList();
       
   267 
       
   268         updateMailData();
       
   269         mTitleRow->updateUnreadCount(mEngine->unreadCount());
       
   270         mTitleRow->setAccountIcon(mAccountIconName);
       
   271         mTitleRow->setExpandCollapseIcon(mIsExpanded);
       
   272 
       
   273         //Get signals about changes in mail data
       
   274         connect(mEngine, SIGNAL( mailDataChanged() ), this, SLOT( updateMailData() ));
       
   275 
       
   276         //Get Signals about changes in unread count
       
   277         connect(mEngine, SIGNAL( unreadCountChanged(const int&) )
       
   278                 ,mTitleRow, SLOT( updateUnreadCount(const int&) ) );
       
   279         
       
   280         //Get signals about account name changes
       
   281         connect(mEngine, SIGNAL( accountNameChanged(const QString&) )
       
   282                 ,mTitleRow, SLOT( updateAccountName(const QString&) ) );
       
   283 
       
   284 	    //Get signals about user actions
       
   285 	    connect(mTitleRow, SIGNAL( mailboxLaunchTriggered() )
       
   286 	            ,mEngine, SLOT( launchMailAppInboxView() ) );
       
   287 	    connect(mTitleRow, SIGNAL( expandCollapseButtonPressed() )
       
   288 	            ,this, SLOT( handleExpandCollapseEvent() ) );
       
   289 	    
       
   290 	    setMinimumSize(mTitleRow->minimumWidth(), 
       
   291 	            mEmptySpaceContainer->minimumHeight() + mTitleRow->minimumHeight());
       
   292     }
       
   293     QT_CATCH(...) {
       
   294         NM_ERROR(1,"NmHsWidget::onInitialize fail @ catch");
       
   295         emit error();
       
   296     }
       
   297 }
       
   298 
       
   299 /*!
       
   300  updateMailData slot
       
   301  */
       
   302 void NmHsWidget::updateMailData()
       
   303 {
       
   304     NM_FUNCTION;
       
   305 
       
   306     QList<NmMessageEnvelope> envelopes;
       
   307     int count = 0;
       
   308     if (mIsExpanded) {
       
   309         count = mEngine->getEnvelopes(envelopes, KMaxNumberOfMailsShown);
       
   310     }
       
   311 
       
   312     updateLayout(count);
       
   313     //count is safe for envelopes and mMailRows
       
   314     for (int i = 0; i < count; i++) {
       
   315         mMailRows.at(i)->updateMailData(envelopes.at(i));
       
   316     }
       
   317 }
       
   318 
       
   319 /*!
       
   320  Sets monitored account id from given string
       
   321  Needed for home screen framework which supports only QString type properties
       
   322  */
       
   323 void NmHsWidget::setAccountId(const QString &text)
       
   324 {
       
   325     NM_FUNCTION;
       
   326 
       
   327     bool ok;
       
   328     quint64 id = text.toULongLong(&ok);
       
   329     if (!ok) {
       
   330         NM_ERROR(1, "NmHsWidget::setAccountId: invalid account ID data, signal finished()!!!");
       
   331         //No valid account id so give up
       
   332         emit finished();
       
   333         return;
       
   334     }
       
   335 
       
   336     mAccountId.setId(id);
       
   337 }
       
   338 
       
   339 /*!
       
   340  Returns monitored account id as a string
       
   341  Needed for home screen framework which supports only QString type properties
       
   342  */
       
   343 QString NmHsWidget::accountId() const
       
   344 {
       
   345     NM_FUNCTION;
       
   346     return QString::number(mAccountId.id());
       
   347 }
       
   348 
       
   349 /*!
       
   350  Sets monitored account icon name from given string
       
   351  */
       
   352 void NmHsWidget::setAccountIconName(const QString &text)
       
   353 {
       
   354     NM_FUNCTION;
       
   355     mAccountIconName = text;
       
   356 }
       
   357 
       
   358 /*!
       
   359  Returns monitored account icon name
       
   360  */
       
   361 QString NmHsWidget::accountIconName() const
       
   362 {
       
   363     NM_FUNCTION;
       
   364     return mAccountIconName;
       
   365 }
       
   366 
       
   367 /*!
       
   368  Slot to handle expand/collapse trigger event
       
   369  */
       
   370 void NmHsWidget::handleExpandCollapseEvent()
       
   371 {
       
   372     NM_FUNCTION;
       
   373     toggleExpansionState();
       
   374 }
       
   375 
       
   376 /*!
       
   377  Sets widget expand/collapse state
       
   378  /post widget expansion state is changed
       
   379  */
       
   380 void NmHsWidget::toggleExpansionState()
       
   381 {
       
   382     NM_FUNCTION;
       
   383 
       
   384     mIsExpanded = !mIsExpanded;
       
   385 
       
   386     //save new state to home screen
       
   387     QStringList propertiesList;
       
   388     propertiesList.append("widgetState");
       
   389     emit setPreferences(propertiesList);
       
   390 
       
   391     //handle state change drawing
       
   392     updateMailData();
       
   393 
       
   394     mTitleRow->setExpandCollapseIcon(mIsExpanded);
       
   395 }
       
   396 
       
   397 /*!
       
   398  Sets expand/collapse state from given string (needed by homescreen)
       
   399  */
       
   400 void NmHsWidget::setWidgetStateProperty(QString value)
       
   401 {
       
   402     NM_FUNCTION;
       
   403     if (value == KNmHsWidgetStateCollapsed) {
       
   404         mIsExpanded = false;
       
   405     }
       
   406     else {
       
   407         mIsExpanded = true;
       
   408     }
       
   409 }
       
   410 
       
   411 /*!
       
   412  Returns widget expand/collapse state as string (needed by homescreen) 
       
   413  */
       
   414 QString NmHsWidget::widgetStateProperty()
       
   415 {
       
   416     NM_FUNCTION;
       
   417     if (mIsExpanded) {
       
   418         return KNmHsWidgetStateExpanded;
       
   419     }
       
   420     else {
       
   421         return KNmHsWidgetStateCollapsed;
       
   422     }
       
   423 }
       
   424 
       
   425 /*!
       
   426  Updates mMailRows list to include KMaxNumberOfMailsShown mail row widgets
       
   427  /post mMailRows contains KMaxNumberOfMailsShown mailRows 
       
   428  */
       
   429 void NmHsWidget::createMailRowsList()
       
   430 {
       
   431     NM_FUNCTION;
       
   432 
       
   433     //make sure that there are as many email rows as needed
       
   434     while (mMailRows.count() < KMaxNumberOfMailsShown) {
       
   435         NmHsWidgetEmailRow *row = new NmHsWidgetEmailRow(this);
       
   436         if (!row->setupUI()) {
       
   437             NM_ERROR(1, "NmHsWidget::createMailRowsList row->setUpUI() fails");
       
   438             //if setupUI fails no point to proceed
       
   439             emit error();
       
   440             return;
       
   441         }
       
   442         connect(row, SIGNAL(mailViewerLaunchTriggered(const NmId&)), mEngine,
       
   443             SLOT(launchMailAppMailViewer(const NmId&)));
       
   444         connect(mDateObserver, SIGNAL(dateTimeChanged()), row, SLOT(updateDateTime()));
       
   445         mMailRows.append(row);
       
   446     }
       
   447 
       
   448 }
       
   449 
       
   450 /*!
       
   451  Updates the Layout to contain the right items
       
   452  /param mailCount defines how many emails is to be shown
       
   453  /post If widget is collapsed, the layout contains only titleRow widget.
       
   454  If widget is expanded and mailCount is 0 layout will contain
       
   455  titlerow & noMailsLabel. 
       
   456  If widget is expanded and mailCount is greter
       
   457  than zero, layout will contain titlerow and KMaxNumberOfMailsShown times
       
   458  emailrow(s)
       
   459  */
       
   460 void NmHsWidget::updateLayout(const int mailCount)
       
   461 {
       
   462     NM_FUNCTION;
       
   463 
       
   464     if (mIsExpanded) {
       
   465         //set container height to content height 
       
   466         qreal contentHeight = KMaxNumberOfMailsShown
       
   467                 * mMailRows.first()->maximumHeight();
       
   468         mContentContainer->setMaximumHeight(contentHeight);
       
   469         mContentContainer->setVisible(true);
       
   470         if (mailCount == 0) {
       
   471             addNoMailsLabelToLayout();
       
   472             removeEmailRowsFromLayout();
       
   473         }
       
   474         else {
       
   475             removeNoMailsLabelFromLayout();
       
   476             addEmailRowsToLayout();
       
   477         }
       
   478     }
       
   479     else {
       
   480         removeNoMailsLabelFromLayout();
       
   481         removeEmailRowsFromLayout();
       
   482         mContentContainer->setVisible(false);
       
   483         mContentContainer->setMaximumHeight(0);        
       
   484     }
       
   485 
       
   486     //resize the widget to new layout size
       
   487     qreal totalHeight = mEmptySpaceContainer->preferredHeight() + mTitleRow->containerHeight() + mContentContainer->maximumHeight();
       
   488     //set maximum sizes, otherwise widget will stay huge also when collapsed
       
   489     setMaximumHeight(totalHeight);
       
   490     mMainContainer->setMaximumHeight(totalHeight);
       
   491     mWidgetContainer->setMaximumHeight(totalHeight - mEmptySpaceContainer->preferredHeight());
       
   492     //resize here or widget cannot draw mail rows when expanding
       
   493     resize(mTitleRow->maximumWidth(), totalHeight);
       
   494     mMainContainer->resize(mTitleRow->maximumWidth(), totalHeight);
       
   495     mWidgetContainer->resize(mTitleRow->maximumWidth(), totalHeight - mEmptySpaceContainer->preferredHeight());
       
   496 
       
   497     updateMailRowsVisibility(mailCount);
       
   498 }
       
   499 
       
   500 /*!
       
   501  Updates mNoMailsLabel visibility based on widget state
       
   502  /param mailCount defines how many mail rows is needed
       
   503  /post if mail count is 0 and mIsExpanded equals true, then
       
   504  the mNoMailLabel is added to the mContentLayout. 
       
   505  */
       
   506 void NmHsWidget::addNoMailsLabelToLayout()
       
   507 {
       
   508     NM_FUNCTION;
       
   509 
       
   510     if (mNoMailsLabel->isVisible() || mMailRows.isEmpty()) {
       
   511         return;
       
   512     }
       
   513     //Add mNoMailsLabel to layout if not yet there and show it
       
   514     mContentLayout->addItem(mNoMailsLabel);
       
   515     //resize the widget to new layout size
       
   516     mNoMailsLabel->show();
       
   517 }
       
   518 
       
   519 /*!
       
   520  removeNoMailsLabelFromLayout removes mNoMailsLabel from the layout
       
   521  /post mNoMailsLabel is not in mContentLayout
       
   522  */
       
   523 void NmHsWidget::removeNoMailsLabelFromLayout()
       
   524 {
       
   525     NM_FUNCTION;
       
   526     //remove mNoMailsLabel from Layout and hide it
       
   527     mContentLayout->removeItem(mNoMailsLabel);
       
   528     mNoMailsLabel->hide();
       
   529 }
       
   530 
       
   531 /*!
       
   532  addEmailRowsToLayout adds every emailrow to the layout
       
   533  /post all elements in mMailRows are added to mContentLayout
       
   534  */
       
   535 void NmHsWidget::addEmailRowsToLayout()
       
   536 {
       
   537     NM_FUNCTION;
       
   538     foreach(NmHsWidgetEmailRow *row, mMailRows)
       
   539         {
       
   540             mContentLayout->addItem(row);
       
   541         }
       
   542 }
       
   543 
       
   544 /*!
       
   545  removeEmailRowsFromLayout removes every emailrow from the layout
       
   546  /post none of the elements in mMailRows are in mContentLayout
       
   547  */
       
   548 void NmHsWidget::removeEmailRowsFromLayout()
       
   549 {
       
   550     NM_FUNCTION;
       
   551     foreach(NmHsWidgetEmailRow *row, mMailRows)
       
   552         {
       
   553             mContentLayout->removeItem(row);
       
   554         }
       
   555 }
       
   556 
       
   557 /*!
       
   558  Updates mail row visibilities in static widget
       
   559  /param visibleCount defines how many items do have mail data
       
   560  /post all row items having mail data are visible, other rows are hidden
       
   561  */
       
   562 void NmHsWidget::updateMailRowsVisibility(const int visibleCount)
       
   563 {
       
   564     NM_FUNCTION;
       
   565 
       
   566     // set visible as many rows as requested by visibleCount param
       
   567     bool isVisible;
       
   568 
       
   569     for (int i = 0; i < mMailRows.count(); i++) {
       
   570         isVisible = false;
       
   571         if ((mIsExpanded) && (i < visibleCount)) {
       
   572             isVisible = true;
       
   573         }
       
   574         mMailRows.at(i)->setVisible(isVisible);
       
   575     }
       
   576 }
       
   577 
       
   578 /*!
       
   579  onEngineException (NmHsWidgetEmailEngineExceptionCode exc)
       
   580  signals widget to be finalized
       
   581  /param exc exception type
       
   582  */
       
   583 void NmHsWidget::onEngineException(const int& exc)
       
   584 {
       
   585     NM_FUNCTION;
       
   586     switch (exc) {
       
   587         case (NmEngineExcAccountDeleted):
       
   588             emit finished(); //succesful ending
       
   589             break;
       
   590         case (NmEngineExcFailure):
       
   591             emit error(); //failure
       
   592             break;
       
   593         default:
       
   594             break;
       
   595     }
       
   596 }