bool AMActionHistoryModel3::logCompletedAction(const AMAction3 *completedAction, AMDatabase *database, int parentLogId){
	if(completedAction && completedAction->inFinalState()) {

		if(database == AMDatabase::database("scanActions") && AMActionRunner3::scanActionRunner()->cachedLogCount() > 200){
			database->commitTransaction();
			AMActionRunner3::scanActionRunner()->resetCachedLogCount();
		}
		if(database == AMDatabase::database("scanActions")){
			if(!database->transactionInProgress())
				database->startTransaction();
			AMActionRunner3::scanActionRunner()->incrementCachedLogCount();
		}

		AMActionLog3 actionLog(completedAction);
		actionLog.setParentId(parentLogId);
		if(actionLog.storeToDb(database)){
			AMActionLogItem3* item = new AMActionLogItem3(actionLog);
			emit modelAboutToBeRefreshed();
			/// \todo Ordering... This may end up at the wrong spot until a full refresh is done.  Most of the time, any actions added will be the most recent ones, however that is not guaranteed.
			appendItem(item);
			visibleActionsCount_++;
			emit modelRefreshed();

			return true;
		}
		return false;
	}
	else {
		return false;
	}
}
bool AMActionHistoryModel3::logUncompletedAction(const AMAction3 *uncompletedAction, AMDatabase *database, int parentLogId){
	if(uncompletedAction && !uncompletedAction->inFinalState()){

		if(database == AMDatabase::database("scanActions") && AMActionRunner3::scanActionRunner()->cachedLogCount() > 200){
			database->commitTransaction();
			AMActionRunner3::scanActionRunner()->resetCachedLogCount();
		}
		if(database == AMDatabase::database("scanActions")){
			if(!database->transactionInProgress())
				database->startTransaction();
			AMActionRunner3::scanActionRunner()->incrementCachedLogCount();
		}

		/*
		AMActionLog3 actionLog(uncompletedAction);
		actionLog.setParentId(parentLogId);
		bool success = actionLog.storeToDb(database);
		*/
		AMActionLog3 *actionLog = new AMActionLog3(uncompletedAction);
		infosToLogsForUncompletedActions_.insert(uncompletedAction->info(), actionLog);
		actionLog->setParentId(parentLogId);
		bool success = actionLog->storeToDb(database);
		const AMListAction3 *listAction = qobject_cast<const AMListAction3*>(uncompletedAction);
		if(success && listAction){
			AMListAction3 *modifyListAction = const_cast<AMListAction3*>(listAction);
			modifyListAction->setLogActionId(actionLog->id());
		}

		AMActionLogItem3* item = new AMActionLogItem3(*actionLog);
		emit modelAboutToBeRefreshed();
		/// \todo Ordering... This may end up at the wrong spot until a full refresh is done.  Most of the time, any actions added will be the most recent ones, however that is not guaranteed.
		appendItem(item);
		visibleActionsCount_++;
		emit modelRefreshed();

		return success;
	}
	return false;
}
void AMActionHistoryModel3::refreshFromDb()
{
	emit modelAboutToBeRefreshed();

	// clear the model:
	clear();

	if(!db_)
		return;

	// Get all of the ids of the AMActionLogs to display. Need to order in descending (most recent first) so we get the right limit.
	QList<int> ids;
	QList<int> parentIds;
	QSqlQuery q;
	QSqlQuery q2;	// used to count the total number in the visible range, if we didn't have a limit.
	QSqlQuery q3;	// used to find the "good" places for show more actions operations

	q = db_->select(actionLogTableName_,
			"id,parentId",
			"1 ORDER BY startDateTime DESC LIMIT ?");
	q.bindValue(0, maximumActionsLimit_);
	q2 = db_->select(actionLogTableName_,
			 "COUNT(1)");
	q3 = db_->select(actionLogTableName_, "id", "parentId=-1");

	// run the query and get the ids:
	if(!q.exec())
		AMErrorMon::alert(this, AMACTIONHISTORYMODEL_FAILED_TO_EXECUTE_DB_QUERY, "Could not execute the query to refresh the action history. Please report this problem to the Acquaman developers." % q.lastError().text());
	while(q.next()){
		ids << q.value(0).toInt();
		parentIds << q.value(1).toInt();
	}
	q.finish();

	// get the total count:
	if(q2.exec() && q2.first()) {
		visibleActionsCount_ = q2.value(0).toInt();
	}
	else {
		AMErrorMon::alert(this, AMACTIONHISTORYMODEL_FAILED_TO_EXECUTE_DB_QUERY_NUMBER_OF_ITEMS, "Could not execute the query to determine the number of items in the action history. Please report this problem to the Acquaman developers." % q.lastError().text());
		visibleActionsCount_ = -1;	// you should never see this
	}
	q2.finish();

	topLevelIds_.clear();
	if(!q3.exec())
		AMErrorMon::alert(this, AMACTIONHISTORYMODEL_FAILED_TO_EXECUTE_DB_QUERY_ALL_TOP_LEVELS, "Could not execute the query to determine the top level items in the action history. Please report this problem to the Acquaman developers." % q.lastError().text());
	while(q3.next()){
		topLevelIds_ << q3.value(0).toInt();
	}
	q3.finish();

	bool prunedLists = false;
	while(!prunedLists && (ids.count() > 0) ){
		if(parentIds.last() != -1){
			parentIds.removeLast();
			ids.removeLast();
		}
		else
			prunedLists = true;
	}

	if(!ids.isEmpty()) {
		// switch order
		QMap<int, int> parentIdsAndIdsAscending;
		for(int i=ids.count()-1; i>=0; i--)
			parentIdsAndIdsAscending.insertMulti(parentIds.at(i), ids.at(i));

		if(!recurseActionsLogLevelCreate(-1, parentIdsAndIdsAscending))
			AMErrorMon::alert(this, AMACTIONHISTORYMODEL_REFRESHFROMDB_FAILED_TO_CREATE_TREE, "The action history failed to generate its internal tree. Please report this problem to the Acquaman developers ");
	}

	emit modelRefreshed();
}
LYGithubProductBacklogCentralWidget::LYGithubProductBacklogCentralWidget(const QString &username, const QString &repository, QWidget *parent) :
	QWidget(parent)
{
	productBacklog_ = new LYGithubProductBacklog();

	networkBusyView_ = 0;

	statusLogView_ = new LYGithubProductBacklogStatusLogView();
	statusLogView_->hide();

	treeView_ = new QTreeView();
	treeView_->setModel(productBacklog_->model());
	treeView_->setSelectionBehavior(QAbstractItemView::SelectItems);
	treeView_->setSelectionMode(QAbstractItemView::SingleSelection);
	treeView_->setAttribute(Qt::WA_MacShowFocusRect, false);
	treeView_->setDragEnabled(true);
	treeView_->viewport()->setAcceptDrops(true);
	treeView_->setDropIndicatorShown(true);
	treeView_->setDragDropMode(QTreeView::InternalMove);
	treeView_->setContextMenuPolicy(Qt::CustomContextMenu);

	connect(treeView_, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint)));

	uploadChangesButton_ = new QPushButton("Upload Changes");
	uploadChangesButton_->setEnabled(false);

	closeIssueButton_ = new QPushButton("Close Issue");
	closeIssueButton_->setEnabled(false);

	addIssueButton_ = new QPushButton("Add Issue");

	QHBoxLayout *hl = new QHBoxLayout();
	hl->addWidget(addIssueButton_);
	hl->addWidget(closeIssueButton_);
	hl->addStretch(10);
	hl->addWidget(uploadChangesButton_);

	QVBoxLayout *vl = new QVBoxLayout();
	vl->addLayout(hl);
	vl->addWidget(treeView_);

	setLayout(vl);

	connect(productBacklog_, SIGNAL(activeChanges(bool)), this, SLOT(onActiveChangesChanged(bool)));
	connect(uploadChangesButton_, SIGNAL(clicked()), this, SLOT(onUploadChangesButtonClicked()));
	connect(productBacklog_, SIGNAL(authenticated(bool)), this, SLOT(onAuthenticated(bool)));
	connect(productBacklog_, SIGNAL(uploaded(bool)), this, SLOT(onUploaded(bool)));

	connect(productBacklog_->productBacklogModel(), SIGNAL(modelAboutToBeRefreshed()), this, SLOT(onModelAboutToBeRefreshed()));
	connect(productBacklog_->productBacklogModel(), SIGNAL(modelRefreshed()), this, SLOT(onModelRefreshed()));

	connect(productBacklog_, SIGNAL(sanityCheckReturned(LYProductBacklogModel::ProductBacklogSanityChecks)), this, SLOT(onSanityCheckReturned(LYProductBacklogModel::ProductBacklogSanityChecks)));
	connect(productBacklog_, SIGNAL(networkRequestBusy(bool, QString)), this, SLOT(onNetworkRequestBusy(bool, QString)));

	connect(treeView_, SIGNAL(clicked(QModelIndex)), this, SLOT(onTreeViewIndexClicked(QModelIndex)));
	connect(addIssueButton_, SIGNAL(clicked()), this, SLOT(onAddIssueButtonClicked()));
	connect(closeIssueButton_, SIGNAL(clicked()), this, SLOT(onCloseIssueButtonClicked()));

	authenticationView_ = new LYGithubProductBacklogAuthenticationView(username, repository);
	connect(authenticationView_, SIGNAL(submitAuthenticationInformation(QString,QString,QString)), this, SLOT(onSubmitAuthenticationInformationAvailable(QString,QString,QString)));
	authenticationView_->show();
	authenticationView_->raise();
}