korganizer

koviewmanager.cpp
00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2001,2003 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of TQt, and distribute the resulting executable,
00023     without including the source code for TQt in the source distribution.
00024 */
00025 
00026 #include <tqwidgetstack.h>
00027 #include <tqtabwidget.h>
00028 
00029 #include <libkcal/calendarresources.h>
00030 #include <kactioncollection.h>
00031 #include <kconfig.h>
00032 #include <kglobal.h>
00033 
00034 #include "actionmanager.h"
00035 #include "calendarview.h"
00036 #include "datenavigator.h"
00037 #include "kotodoview.h"
00038 #include "koagendaview.h"
00039 #include "komonthview.h"
00040 #include "kolistview.h"
00041 #include "kowhatsnextview.h"
00042 #include "kojournalview.h"
00043 #include "kotimelineview.h"
00044 #include "koprefs.h"
00045 #include "koglobals.h"
00046 #include "navigatorbar.h"
00047 #include "multiagendaview.h"
00048 #include <korganizer/mainwindow.h>
00049 
00050 #include "koviewmanager.h"
00051 #include "koviewmanager.moc"
00052 
00053 KOViewManager::KOViewManager( CalendarView *mainView ) :
00054   TQObject(), mMainView( mainView )
00055 {
00056   mCurrentView = 0;
00057 
00058   mLastEventView = 0;
00059 
00060   mWhatsNextView = 0;
00061   mTodoView = 0;
00062   mAgendaView = 0;
00063   mAgendaSideBySideView = 0;
00064   mMonthView = 0;
00065   mListView = 0;
00066   mJournalView = 0;
00067   mTimelineView = 0;
00068   mAgendaViewTabs = 0;
00069   mAgendaViewTabIndex = 0;
00070   mAgendaMode = AGENDA_NONE;
00071 }
00072 
00073 KOViewManager::~KOViewManager()
00074 {
00075 }
00076 
00077 
00078 KOrg::BaseView *KOViewManager::currentView()
00079 {
00080   return mCurrentView;
00081 }
00082 
00083 void KOViewManager::readSettings(KConfig *config)
00084 {
00085   config->setGroup("General");
00086   TQString view = config->readEntry("Current View");
00087 
00088   if (view == "WhatsNext") showWhatsNextView();
00089   else if (view == "Month") showMonthView();
00090   else if (view == "List") showListView();
00091   else if (view == "Journal") showJournalView();
00092   else if (view == "Todo") showTodoView();
00093   else if (view == "Timeline") showTimelineView();
00094   else {
00095     mAgendaMode = AgendaMode( config->readNumEntry( "Agenda Mode", AGENDA_OTHER ) );
00096 
00097     switch ( mAgendaMode ) {
00098       case AGENDA_WORK_WEEK:
00099         showWorkWeekView();
00100         break;
00101       case AGENDA_WEEK:
00102         showWeekView();
00103         break;
00104       case AGENDA_NEXTX:
00105         showNextXView();
00106         break;
00107       case AGENDA_DAY:
00108         showDayView();
00109         break;
00110       case AGENDA_NONE:
00111         // Someone has been playing with the config file.
00112       default:
00113         mAgendaMode = AGENDA_OTHER;
00114         showAgendaView();
00115     }
00116   }
00117 }
00118 
00119 void KOViewManager::writeSettings(KConfig *config)
00120 {
00121   config->setGroup("General");
00122 
00123   TQString view;
00124   if (mCurrentView == mWhatsNextView) view = "WhatsNext";
00125   else if (mCurrentView == mMonthView) view = "Month";
00126   else if (mCurrentView == mListView) view = "List";
00127   else if (mCurrentView == mJournalView) view = "Journal";
00128   else if (mCurrentView == mTodoView) view = "Todo";
00129   else if (mCurrentView == mTimelineView) view = "Timeline";
00130   else {
00131     view = "Agenda";
00132     config->writeEntry( "Agenda Mode", mAgendaMode );
00133   }
00134 
00135   config->writeEntry("Current View",view);
00136 
00137   if (mAgendaView) {
00138     mAgendaView->writeSettings(config);
00139   }
00140   if (mListView) {
00141     mListView->writeSettings(config);
00142   }
00143   if (mTodoView) {
00144     mTodoView->saveLayout(config,"Todo View");
00145   }
00146 }
00147 
00148 void KOViewManager::showView(KOrg::BaseView *view)
00149 {
00150   if( view == mCurrentView ) return;
00151 
00152   mCurrentView = view;
00153 
00154   if ( mCurrentView && mCurrentView->isEventView() ) {
00155     mLastEventView = mCurrentView;
00156   }
00157 
00158   if ( mAgendaView ) mAgendaView->deleteSelectedDateTime();
00159 
00160   raiseCurrentView();
00161 
00162   mMainView->processIncidenceSelection( 0, TQDate() );
00163 
00164   mMainView->updateView();
00165 
00166   mMainView->adaptNavigationUnits();
00167 }
00168 
00169 void KOViewManager::goMenu( bool enable )
00170 {
00171   KOrg::MainWindow *w = ActionManager::findInstance( KURL() );
00172   if ( w ) {
00173     KActionCollection *ac = w->getActionCollection();
00174     if ( ac ) {
00175       KAction *action;
00176       action = ac->action( "go_today" );
00177       if ( action ) {
00178         action->setEnabled( enable );
00179       }
00180       action = ac->action( "go_previous" );
00181       if ( action ) {
00182         action->setEnabled( enable );
00183       }
00184       action = ac->action( "go_next" );
00185       if ( action ) {
00186         action->setEnabled( enable );
00187       }
00188     }
00189   }
00190 }
00191 
00192 void KOViewManager::raiseCurrentView()
00193 {
00194   if ((mMonthView && KOPrefs::instance()->mFullViewMonth && mCurrentView == mMonthView) ||
00195       (mTodoView && KOPrefs::instance()->mFullViewTodo && mCurrentView == mTodoView)) {
00196     mMainView->showLeftFrame( false );
00197     if ( mCurrentView == mTodoView ) {
00198       mMainView->navigatorBar()->hide();
00199     } else {
00200       mMainView->navigatorBar()->show();
00201     }
00202   } else {
00203     mMainView->showLeftFrame( true );
00204     mMainView->navigatorBar()->hide();
00205   }
00206   mMainView->viewStack()->raiseWidget( widgetForView( mCurrentView  ) );
00207 }
00208 
00209 void KOViewManager::updateView()
00210 {
00211   if ( mCurrentView ) mCurrentView->updateView();
00212 }
00213 
00214 void KOViewManager::updateView(const TQDate &start, const TQDate &end)
00215 {
00216 //  kdDebug(5850) << "KOViewManager::updateView()" << endl;
00217 
00218   if (mCurrentView) mCurrentView->showDates(start, end);
00219 
00220   if (mTodoView) mTodoView->updateView();
00221 }
00222 
00223 void KOViewManager::connectView(KOrg::BaseView *view)
00224 {
00225   if (!view) return;
00226 
00227   // selecting an incidence
00228   connect( view, TQT_SIGNAL( incidenceSelected( Incidence *,const TQDate & ) ),
00229            mMainView, TQT_SLOT( processMainViewSelection( Incidence *,const TQDate & ) ) );
00230 
00231   // showing/editing/deleting an incidence. The calendar view takes care of the action.
00232   connect( view, TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)),
00233            mMainView, TQT_SLOT(showIncidence(Incidence *,const TQDate &)) );
00234   connect( view, TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)),
00235            mMainView, TQT_SLOT(editIncidence(Incidence *,const TQDate &)) );
00236   connect( view, TQT_SIGNAL(deleteIncidenceSignal(Incidence *)),
00237            mMainView, TQT_SLOT(deleteIncidence(Incidence *)) );
00238   connect( view, TQT_SIGNAL(copyIncidenceSignal(Incidence *)),
00239            mMainView, TQT_SLOT(copyIncidence(Incidence *)) );
00240   connect( view, TQT_SIGNAL(cutIncidenceSignal(Incidence *)),
00241            mMainView, TQT_SLOT(cutIncidence(Incidence *)) );
00242   connect( view, TQT_SIGNAL(pasteIncidenceSignal()),
00243            mMainView, TQT_SLOT(pasteIncidence()));
00244   connect( view, TQT_SIGNAL(toggleAlarmSignal(Incidence *)),
00245            mMainView, TQT_SLOT(toggleAlarm(Incidence *)) );
00246   connect( view,TQT_SIGNAL(dissociateOccurrenceSignal(Incidence *,const TQDate &)),
00247            mMainView, TQT_SLOT(dissociateOccurrence(Incidence *,const TQDate &)) );
00248   connect( view,TQT_SIGNAL(dissociateFutureOccurrenceSignal(Incidence *,const TQDate &)),
00249            mMainView, TQT_SLOT(dissociateFutureOccurrence(Incidence *,const TQDate &)) );
00250 
00251   // signals to create new incidences
00252   connect( view, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)),
00253            mMainView, TQT_SLOT(newEvent(ResourceCalendar *,const TQString &)) );
00254   connect( view, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &,const TQDate &)),
00255            mMainView, TQT_SLOT(newEvent(ResourceCalendar *,const TQString &,const TQDate &)) );
00256   connect( view, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &,const TQDateTime &)),
00257            mMainView, TQT_SLOT(newEvent(ResourceCalendar *,const TQString &,const TQDateTime &)) );
00258   connect( view, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &,const TQDateTime &,const TQDateTime &)),
00259            mMainView, TQT_SLOT(newEvent(ResourceCalendar *,const TQString &,const TQDateTime &,const TQDateTime &)) );
00260 
00261   connect( view, TQT_SIGNAL(newTodoSignal(ResourceCalendar *,const TQString &,const TQDate &)),
00262            mMainView, TQT_SLOT(newTodo(ResourceCalendar *,const TQString &,const TQDate &)) );
00263   connect( view, TQT_SIGNAL(newSubTodoSignal(Todo *)),
00264            mMainView, TQT_SLOT(newSubTodo(Todo *)) );
00265 
00266   connect( view, TQT_SIGNAL(newJournalSignal(ResourceCalendar *,const TQString &,const TQDate &)),
00267            mMainView, TQT_SLOT(newJournal(ResourceCalendar *,const TQString &,const TQDate &)) );
00268 
00269   // reload settings
00270   connect(mMainView, TQT_SIGNAL(configChanged()), view, TQT_SLOT(updateConfig()));
00271 
00272   // Notifications about added, changed and deleted incidences
00273   connect( mMainView, TQT_SIGNAL( dayPassed( const TQDate & ) ),
00274            view, TQT_SLOT( dayPassed( const TQDate & ) ) );
00275   connect( view, TQT_SIGNAL( startMultiModify( const TQString & ) ),
00276            mMainView, TQT_SLOT( startMultiModify( const TQString & ) ) );
00277   connect( view, TQT_SIGNAL( endMultiModify() ),
00278            mMainView, TQT_SLOT( endMultiModify() ) );
00279 
00280   connect( mMainView, TQT_SIGNAL( newIncidenceChanger( IncidenceChangerBase* ) ),
00281            view, TQT_SLOT( setIncidenceChanger( IncidenceChangerBase * ) ) );
00282   view->setIncidenceChanger( mMainView->incidenceChanger() );
00283 }
00284 
00285 void KOViewManager::connectTodoView( KOTodoView* todoView )
00286 {
00287   if (!todoView) return;
00288 
00289   // SIGNALS/SLOTS FOR TODO VIEW
00290   connect( todoView, TQT_SIGNAL( purgeCompletedSignal() ),
00291            mMainView, TQT_SLOT( purgeCompleted() ) );
00292   connect( todoView, TQT_SIGNAL( unSubTodoSignal() ),
00293            mMainView, TQT_SLOT( todo_unsub() ) );
00294   connect( todoView, TQT_SIGNAL( unAllSubTodoSignal() ),
00295            mMainView, TQT_SLOT( makeSubTodosIndependent() ) );
00296 }
00297 
00298 void KOViewManager::zoomInHorizontally()
00299 {
00300   if( mAgendaView == mCurrentView ) mAgendaView->zoomInHorizontally();
00301 }
00302 void KOViewManager::zoomOutHorizontally()
00303 {
00304   if( mAgendaView== mCurrentView ) mAgendaView->zoomOutHorizontally();
00305 }
00306 void KOViewManager::zoomInVertically()
00307 {
00308   if( mAgendaView== mCurrentView ) mAgendaView->zoomInVertically();
00309 }
00310 void KOViewManager::zoomOutVertically()
00311 {
00312   if( mAgendaView== mCurrentView ) mAgendaView->zoomOutVertically();
00313 }
00314 
00315 void KOViewManager::addView(KOrg::BaseView *view)
00316 {
00317   connectView( view );
00318   mMainView->viewStack()->addWidget( view );
00319 }
00320 
00321 void KOViewManager::showWhatsNextView()
00322 {
00323   if (!mWhatsNextView) {
00324     mWhatsNextView = new KOWhatsNextView(mMainView->calendar(),mMainView->viewStack(),
00325                                          "KOViewManager::WhatsNextView");
00326     addView(mWhatsNextView);
00327   }
00328   goMenu( true );
00329   showView(mWhatsNextView);
00330 }
00331 
00332 void KOViewManager::showListView()
00333 {
00334   if (!mListView) {
00335     mListView = new KOListView(mMainView->calendar(), mMainView->viewStack(), "KOViewManager::ListView");
00336     addView(mListView);
00337   }
00338   goMenu( true );
00339   showView(mListView);
00340 }
00341 
00342 void KOViewManager::showAgendaView()
00343 {
00344   // If the user opens a local file, through menu->open ( for example ), then
00345   // it doesn't make sense to use multiagenda.
00346   CalendarResources *calres = dynamic_cast<CalendarResources*>( mMainView->calendar() );
00347   bool isLocalFile = !calres;
00348 
00349   int mode = KOPrefs::instance()->agendaViewCalendarDisplay();
00350 
00351   const bool showBoth = ( mode == KOPrefs::AllCalendarViews && !isLocalFile );
00352 
00353   const bool showMerged = showBoth || mode == KOPrefs::CalendarsMerged || isLocalFile;
00354 
00355   const bool showSideBySide = !isLocalFile && ( showBoth || mode == KOPrefs::CalendarsSideBySide );
00356 
00357   TQWidget *parent = mMainView->viewStack();
00358   if ( !mAgendaViewTabs && showBoth ) {
00359     mAgendaViewTabs = new TQTabWidget( mMainView->viewStack() );
00360     connect( mAgendaViewTabs, TQT_SIGNAL( currentChanged( TQWidget* ) ),
00361              this, TQT_SLOT( currentAgendaViewTabChanged( TQWidget* ) ) );
00362     parent = mAgendaViewTabs;
00363 
00364     KConfig *config = KOGlobals::self()->config();
00365     config->setGroup( "Views" );
00366     mAgendaViewTabIndex = config->readNumEntry( "Agenda View Tab Index", 0 );
00367   }
00368 
00369   if ( !mAgendaView && showMerged ) {
00370     mAgendaView = new KOAgendaView( mMainView->calendar(),
00371                                     mMainView,
00372                                     parent,
00373                                     "KOViewManager::AgendaView" );
00374 
00375     addView(mAgendaView);
00376 
00377     connect(mAgendaView, TQT_SIGNAL( toggleExpand() ),
00378             mMainView, TQT_SLOT( toggleExpand() ) );
00379     connect(mMainView, TQT_SIGNAL( calendarViewExpanded( bool ) ),
00380             mAgendaView, TQT_SLOT( setExpandedButton( bool ) ) );
00381 
00382     connect( mAgendaView,TQT_SIGNAL( zoomViewHorizontally(const TQDate &, int )),
00383              mMainView->dateNavigator(),TQT_SLOT( selectDates( const TQDate &, int ) ) );
00384     mAgendaView->readSettings();
00385   }
00386 
00387   if ( !mAgendaSideBySideView && showSideBySide ) {
00388     mAgendaSideBySideView =
00389       new MultiAgendaView( mMainView->calendar(), mMainView, parent,
00390                         "KOViewManager::AgendaSideBySideView" );
00391 
00392     addView(mAgendaSideBySideView);
00393 
00394 /*    connect(mAgendaSideBySideView, TQT_SIGNAL( toggleExpand() ),
00395             mMainView, TQT_SLOT( toggleExpand() ) );
00396     connect(mMainView, TQT_SIGNAL( calendarViewExpanded( bool ) ),
00397             mAgendaSideBySideView, TQT_SLOT( setExpandedButton( bool ) ) );
00398 
00399     connect( mAgendaSideBySideView,TQT_SIGNAL( zoomViewHorizontally(const TQDate &, int )),
00400              mMainView->dateNavigator(),TQT_SLOT( selectDates( const TQDate &, int ) ) );*/
00401   }
00402 
00403   if ( showBoth && mAgendaViewTabs ) {
00404     if ( mAgendaView && mAgendaViewTabs->indexOf( mAgendaView ) < 0 )
00405       mAgendaViewTabs->addTab( mAgendaView, i18n("Merged calendar") );
00406     if ( mAgendaSideBySideView && mAgendaViewTabs->indexOf( mAgendaSideBySideView ) < 0 )
00407       mAgendaViewTabs->addTab( mAgendaSideBySideView, i18n("Calendars Side by Side") );
00408     mAgendaViewTabs->setCurrentPage( mAgendaViewTabIndex );
00409   } else {
00410     if ( mAgendaView && mMainView->viewStack()->id( mAgendaView ) < 0 )
00411       mMainView->viewStack()->addWidget( mAgendaView );
00412     if ( mAgendaSideBySideView && mMainView->viewStack()->id( mAgendaSideBySideView ) < 0 )
00413       mMainView->viewStack()->addWidget( mAgendaSideBySideView );
00414   }
00415 
00416   goMenu( true );
00417   if ( mAgendaViewTabs && showBoth )
00418     showView( static_cast<KOrg::BaseView*>( mAgendaViewTabs->currentPage() ) );
00419   else if ( mAgendaView && showMerged )
00420     showView( mAgendaView );
00421   else if ( mAgendaSideBySideView && showSideBySide )
00422     showView( mAgendaSideBySideView );
00423 }
00424 
00425 void KOViewManager::showDayView()
00426 {
00427   mAgendaMode = AGENDA_DAY;
00428   showAgendaView();
00429   mMainView->dateNavigator()->selectDates( 1 );
00430 }
00431 
00432 void KOViewManager::showWorkWeekView()
00433 {
00434   mAgendaMode = AGENDA_WORK_WEEK;
00435   showAgendaView();
00436   mMainView->dateNavigator()->selectWorkWeek();
00437 }
00438 
00439 void KOViewManager::showWeekView()
00440 {
00441   mAgendaMode = AGENDA_WEEK;
00442   showAgendaView();
00443   mMainView->dateNavigator()->selectWeek();
00444 }
00445 
00446 void KOViewManager::showNextXView()
00447 {
00448   mAgendaMode = AGENDA_NEXTX;
00449   showAgendaView();
00450   mMainView->dateNavigator()->selectDates( TQDate::currentDate(),
00451                                            KOPrefs::instance()->mNextXDays );
00452 }
00453 
00454 void KOViewManager::showMonthView()
00455 {
00456   if (!mMonthView) {
00457     mMonthView = new KOMonthView(mMainView->calendar(), mMainView->viewStack(), "KOViewManager::MonthView");
00458     addView(mMonthView);
00459   }
00460 
00461   goMenu( true );
00462   showView(mMonthView);
00463 }
00464 
00465 void KOViewManager::showTodoView()
00466 {
00467   if ( !mTodoView ) {
00468     mTodoView = new KOTodoView( mMainView->calendar(), mMainView->viewStack(),
00469                                 "KOViewManager::TodoView" );
00470     mTodoView->setCalendar( mMainView->calendar() );
00471     addView( mTodoView );
00472     connectTodoView( mTodoView );
00473 
00474     KConfig *config = KOGlobals::self()->config();
00475     mTodoView->restoreLayout( config, "Todo View" );
00476   }
00477 
00478   goMenu( false );
00479   showView( mTodoView );
00480 }
00481 
00482 void KOViewManager::showJournalView()
00483 {
00484   if (!mJournalView) {
00485     mJournalView = new KOJournalView(mMainView->calendar(),mMainView->viewStack(),
00486                                      "KOViewManager::JournalView");
00487     addView(mJournalView);
00488   }
00489 
00490   goMenu( true );
00491   showView(mJournalView);
00492 }
00493 
00494 
00495 void KOViewManager::showTimelineView()
00496 {
00497   if (!mTimelineView) {
00498     mTimelineView = new KOTimelineView(mMainView->calendar(),mMainView->viewStack(),
00499                                      "KOViewManager::TimelineView");
00500     addView(mTimelineView);
00501   }
00502   goMenu( true );
00503   showView(mTimelineView);
00504 }
00505 
00506 void KOViewManager::showEventView()
00507 {
00508   if ( mLastEventView ) {
00509     goMenu( true );
00510     showView( mLastEventView );
00511   } else {
00512     showWeekView();
00513   }
00514 }
00515 
00516 Incidence *KOViewManager::currentSelection()
00517 {
00518   if ( !mCurrentView ) return 0;
00519   Incidence::List incidenceList = mCurrentView->selectedIncidences();
00520   if ( incidenceList.isEmpty() ) return 0;
00521 
00522   return incidenceList.first();
00523 }
00524 
00525 TQDate KOViewManager::currentSelectionDate()
00526 {
00527   TQDate qd;
00528   if (mCurrentView) {
00529     DateList qvl = mCurrentView->selectedIncidenceDates();
00530     if (!qvl.isEmpty()) qd = qvl.first();
00531   }
00532   return qd;
00533 }
00534 
00535 void KOViewManager::setDocumentId( const TQString &id )
00536 {
00537   if (mTodoView) mTodoView->setDocumentId( id );
00538 }
00539 
00540 
00541 TQWidget* KOViewManager::widgetForView( KOrg::BaseView* view ) const
00542 {
00543   const bool showBoth = KOPrefs::instance()->agendaViewCalendarDisplay() == KOPrefs::AllCalendarViews;
00544   if ( (view == mAgendaView || view == mAgendaSideBySideView) && mAgendaViewTabs && showBoth ) {
00545     return mAgendaViewTabs;
00546   }
00547   return view;
00548 }
00549 
00550 
00551 void KOViewManager::currentAgendaViewTabChanged( TQWidget* widget )
00552 {
00553   KConfig *config = KOGlobals::self()->config();
00554   config->setGroup( "Views" );
00555   config->writeEntry( "Agenda View Tab Index", mAgendaViewTabs->currentPageIndex() );
00556 
00557   goMenu( true );
00558   showView( static_cast<KOrg::BaseView*>( widget ) );
00559 }
00560 
00561 void KOViewManager::resourcesChanged()
00562 {
00563   if ( mAgendaView )
00564     mAgendaView->resourcesChanged();
00565   if ( mAgendaSideBySideView )
00566     mAgendaSideBySideView->resourcesChanged();
00567 }
00568 
00569 void KOViewManager::updateMultiCalendarDisplay()
00570 {
00571   if ( agendaIsSelected() ) {
00572     showAgendaView();
00573   } else {
00574     updateView();
00575   }
00576 }
00577 
00578 bool KOViewManager::agendaIsSelected() const
00579 {
00580   return mCurrentView == mAgendaView            ||
00581          mCurrentView == mAgendaSideBySideView  ||
00582         ( mAgendaViewTabs && mCurrentView == mAgendaViewTabs->currentPage() );
00583 }