/* * Copyright (c) 2020 Mark Liversedge (liversedge@gmail.com) * * 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 2 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, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "NavigationModel.h" #include "RideCache.h" // a little too intertwined with these two, // probably needs refactoring out at some point. #include "LTMSidebar.h" #include "NewSideBar.h" #include "MainWindow.h" NavigationModel::NavigationModel(AthleteTab *tab) : tab(tab), block(false), viewinit(false), drinit(false), iteminit(false) { connect(tab, SIGNAL(viewChanged(int)), this, SLOT(viewChanged(int))); connect(tab, SIGNAL(rideItemSelected(RideItem*)), this, SLOT(rideChanged(RideItem*))); connect(static_cast(tab->view(0))->sidebar, SIGNAL(dateRangeChanged(DateRange)), this, SLOT(dateChanged(DateRange))); connect(tab->context->mainWindow, SIGNAL(backClicked()), this, SLOT(back())); connect(tab->context->mainWindow, SIGNAL(forwardClicked()), this, SLOT(forward())); stackpointer=-1; } NavigationModel::~NavigationModel() { } void NavigationModel::addToStack(NavigationEvent &event) { if (stackpointer == -1) { // empty stack.clear(); stack << event; stackpointer++; } else if (stackpointer == (stack.count()-1)) { // end, just append stack << event; stackpointer++; } else if (stackpointer < (stack.count()-1)) { // middle, truncate stack.remove(stackpointer+1, stack.count() - (stackpointer+1)); stack << event; stackpointer++; } } void NavigationModel::rideChanged(RideItem *x) { if (block) return; // initial values if (iteminit == false) { iteminit = true; item = x; return; } if (item != x) { // add to recent if (!recent.contains(x)) recent << x; // add to the stack -- truncate if needed NavigationEvent add(NavigationEvent::RIDE, x); add.before.setValue(item); addToStack(add); item = x; } } void NavigationModel::dateChanged(DateRange x) { if (block) return; // initial values if (drinit == false) { drinit = true; dr = x; return; } if (dr != x) { // add to the stack NavigationEvent add(NavigationEvent::DATERANGE, x); add.before.setValue(dr); addToStack(add); dr = x; } } void NavigationModel::viewChanged(int x) { if (block) return; // initial values if (viewinit == false) { viewinit = true; view = x; return; } if (view != x) { // add to the stack NavigationEvent add(NavigationEvent::VIEW, x); add.before.setValue(view); addToStack(add); view = x; } } void NavigationModel::action(bool redo, NavigationEvent event) { block = true; // don't observe events during redo/undo // NOTE: as well as undo/redo the state variables are // also updated- so we DELIBERATELY use 'view' // 'dr' amd 'item' when extracting from the // before and after QVariants to update state // at the same time as executing the undo/redo. switch(event.type) { case NavigationEvent::VIEW: { view = redo ? event.after.toInt() : event.before.toInt(); switch (view) { case 0: tab->context->mainWindow->selectTrends(); break; case 1: tab->context->mainWindow->selectAnalysis(); break; case 2: tab->context->mainWindow->selectDiary(); break; case 3: tab->context->mainWindow->selectTrain(); break; } } break; case NavigationEvent::RIDE: { RideItem *pitem = redo ? event.after.value() : event.before.value(); // don't select deleted rides (!!) if (!tab->context->athlete->rideCache->deletelist.contains(pitem)) { item = pitem; tab->setNoSwitch(true); // can't be doing that when we are undo/redo ride selection tab->context->athlete->selectRideFile(item->fileName); tab->setNoSwitch(false); } } break; case NavigationEvent::DATERANGE: { dr = redo ? event.after.value() : event.before.value(); static_cast(tab->view(0))->sidebar->selectDateRange(dr); } break; } block = false; } void NavigationModel::back() { // are we the current tab? if (tab->context->mainWindow->athleteTab() == tab) { if (stackpointer >= 0) { // we need to check for rideselects+view change // so we reverse them both at the same time // this is because when a ride is selected on // any view other than analysis it triggers a // view change, so we need to do them together if (stackpointer > 0 && stack[stackpointer].type == NavigationEvent::VIEW && // switch view stack[stackpointer].after.toInt() == 1 && // switch to analysis stack[stackpointer-1].type == NavigationEvent::RIDE) { // ride selected before action(false, stack[stackpointer]); stackpointer--; } // undo action(false, stack[stackpointer]); stackpointer--; } } } void NavigationModel::forward() { // are we the current tab? if (tab->context->mainWindow->athleteTab() == tab) { if ((stackpointer+1) < stack.count() && stack.count()) { // check for ride select followed by view change to analysis if (stackpointer+2 < stack.count() && stack[stackpointer+1].type == NavigationEvent::RIDE && // ride selected before stack[stackpointer+2].type == NavigationEvent::VIEW && // switch view stack[stackpointer+2].after.toInt() == 1) { // switch to analysis stackpointer++; action(false, stack[stackpointer]); } // redo stackpointer++; action(true, stack[stackpointer]); } } }