Files
Sourcetrail/src/lib_gui/qt/element/code/QtCodeFileList.cpp
T

625 lines
14 KiB
C++

#include "QtCodeFileList.h"
#include <QScrollBar>
#include <QTimer>
#include <QVBoxLayout>
#include "FilePath.h"
#include "ResourcePaths.h"
#include "utility.h"
#include "utilityApp.h"
#include "ColorScheme.h"
#include "QtCodeFile.h"
#include "QtCodeFileTitleBar.h"
#include "QtCodeNavigator.h"
#include "QtCodeSnippet.h"
#include "SourceLocationFile.h"
#include "utilityQt.h"
void QtCodeFileListScrollArea::keyPressEvent(QKeyEvent* event)
{
switch (event->key())
{
case Qt::Key_Up:
case Qt::Key_Down:
case Qt::Key_Left:
case Qt::Key_Right:
QWidget::keyPressEvent(event);
return;
}
QScrollArea::keyPressEvent(event);
}
QtCodeFileList::QtCodeFileList(QtCodeNavigator* navigator)
: QFrame()
, m_navigator(navigator)
, m_mirroredTitleBar(nullptr)
, m_mirroredSnippetScrollBar(nullptr)
{
m_scrollArea = new QtCodeFileListScrollArea();
m_scrollArea->setObjectName(QStringLiteral("code_container"));
m_scrollArea->setWidgetResizable(true);
m_filesArea = new QFrame();
m_filesArea->setObjectName(QStringLiteral("code_file_list"));
QVBoxLayout* innerLayout = new QVBoxLayout();
innerLayout->setSpacing(0);
innerLayout->setContentsMargins(0, 0, 0, 0);
innerLayout->setAlignment(Qt::AlignTop);
m_filesArea->setLayout(innerLayout);
m_scrollArea->setWidget(m_filesArea);
m_firstSnippetTitleBar = new QtCodeFileTitleBar(m_scrollArea, true);
m_firstSnippetTitleBar->hide();
m_lastSnippetScrollBar = new QScrollBar(Qt::Horizontal, m_scrollArea);
if (utility::getOsType() == OS_MAC)
{
// set style on scrollbar because it always has bright background by default
m_lastSnippetScrollBar->setStyleSheet(
utility::getStyleSheet(ResourcePaths::getGuiDirectoryPath().concatenate(L"main/scrollbar.css"))
.c_str());
m_styleSize = m_lastSnippetScrollBar->styleSheet().size();
}
else
{
m_lastSnippetScrollBar->setObjectName(QStringLiteral("last_scroll_bar"));
}
m_lastSnippetScrollBar->hide();
connect(
m_lastSnippetScrollBar, &QScrollBar::valueChanged, this, &QtCodeFileList::scrollLastSnippet);
QVBoxLayout* layout = new QVBoxLayout();
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_scrollArea);
setLayout(layout);
m_scrollSpeedChangeListener.setScrollBar(m_scrollArea->verticalScrollBar());
connect(
m_scrollArea->verticalScrollBar(),
&QScrollBar::valueChanged,
this,
&QtCodeFileList::updateSnippetTitleAndScrollBar);
connect(
m_scrollArea->verticalScrollBar(),
&QScrollBar::valueChanged,
m_navigator,
&QtCodeNavigator::scrolled);
}
void QtCodeFileList::clear()
{
for (QtCodeFile* file: m_files)
{
file->hide();
file->deleteLater();
}
m_files.clear();
m_scrollArea->verticalScrollBar()->setValue(0);
clearSnippetTitleAndScrollBar();
}
void QtCodeFileList::clearSnippetTitleAndScrollBar()
{
updateFirstSnippetTitleBar(nullptr);
updateLastSnippetScrollBar(nullptr);
}
QtCodeFile* QtCodeFileList::getFile(const FilePath& filePath)
{
QtCodeFile* file = nullptr;
for (QtCodeFile* filePtr: m_files)
{
if (filePtr->getFilePath() == filePath)
{
file = filePtr;
break;
}
}
if (!file)
{
file = new QtCodeFile(filePath, m_navigator, !m_files.size());
m_files.push_back(file);
m_filesArea->layout()->addWidget(file);
file->hide();
}
return file;
}
void QtCodeFileList::addFile(const CodeFileParams& params)
{
QtCodeFile* file = getFile(params.locationFile->getFilePath());
file->setWholeFile(params.locationFile->isWhole(), static_cast<int>(params.referenceCount));
file->setModificationTime(params.modificationTime);
file->setIsComplete(params.locationFile->isComplete());
file->setIsIndexed(params.locationFile->isIndexed());
if (params.isMinimized)
{
file->setMinimized();
}
else
{
bool same = true;
const std::vector<QtCodeSnippet*> snippets = file->getSnippets();
if (params.snippetParams.size() != snippets.size())
{
same = false;
}
else
{
for (size_t i = 0; i < snippets.size(); i++)
{
if (params.snippetParams[i].startLineNumber != snippets[i]->getStartLineNumber() ||
params.snippetParams[i].endLineNumber != snippets[i]->getEndLineNumber())
{
same = false;
break;
}
}
}
if (!same)
{
Id focusedLocationId = 0;
const CodeFocusHandler::Focus& currentFocus = m_navigator->getCurrentFocus();
if (currentFocus.area &&
file->getFilePath() == currentFocus.area->getSourceLocationFile()->getFilePath())
{
focusedLocationId = currentFocus.locationId;
}
file->clearSnippets();
for (const CodeSnippetParams& snippetParams: params.snippetParams)
{
file->addCodeSnippet(snippetParams);
}
if (focusedLocationId)
{
file->setFocus(focusedLocationId);
}
}
file->setSnippets();
}
}
QScrollArea* QtCodeFileList::getScrollArea()
{
return m_scrollArea;
}
void QtCodeFileList::updateSourceLocations(const CodeSnippetParams& params)
{
QtCodeFile* file = getFile(params.locationFile->getFilePath());
if (file)
{
file->updateSourceLocations(params);
}
}
void QtCodeFileList::updateFiles()
{
for (QtCodeFile* file: m_files)
{
file->setProperty("last", file == m_files.back());
file->updateContent();
file->updateTitleBar();
file->show();
}
// Perform delayed so all widgets are already visible
QTimer::singleShot(100, this, &QtCodeFileList::updateSnippetTitleAndScrollBarSlot);
}
void QtCodeFileList::scrollTo(
const FilePath& filePath,
size_t lineNumber,
Id locationId,
Id scopeLocationId,
bool animated,
CodeScrollParams::Target target,
bool focusTarget)
{
QtCodeFile* file = getFile(filePath);
if (!file)
{
return;
}
QtCodeSnippet* snippet = nullptr;
Id targetLocationId = scopeLocationId ? scopeLocationId : locationId;
if (targetLocationId)
{
snippet = file->getSnippetForLocationId(targetLocationId);
}
else if (lineNumber)
{
snippet = file->getSnippetForLine(static_cast<unsigned int>(lineNumber));
}
else
{
snippet = file->getSnippetForLine(1);
focusTarget = false;
}
if (!snippet || !snippet->isVisible())
{
ensureWidgetVisibleAnimated(m_filesArea, file->getTitleBar(), QRect(), animated, target);
return;
}
size_t endLineNumber = 0;
if (!lineNumber)
{
if (targetLocationId)
{
std::pair<size_t, size_t> lineNumbers = snippet->getLineNumbersForLocationId(
targetLocationId);
lineNumber = lineNumbers.first;
if (lineNumbers.first != lineNumbers.second)
{
endLineNumber = lineNumbers.second;
}
}
else
{
lineNumber = 1;
focusTarget = false;
}
}
QRectF lineRect = snippet->getLineRectForLineNumber(lineNumber);
if (endLineNumber)
{
lineRect |= snippet->getLineRectForLineNumber(endLineNumber);
}
ensureWidgetVisibleAnimated(m_filesArea, snippet, lineRect, animated, target);
snippet->ensureLocationIdVisible(targetLocationId, animated);
if (focusTarget)
{
m_navigator->setFocusedLocationId(
snippet->getArea(),
lineNumber,
snippet->getArea()->getColumnNumberForLocationId(locationId),
locationId,
{},
false,
false);
}
}
void QtCodeFileList::onWindowFocus()
{
for (QtCodeFile* file: m_files)
{
file->updateTitleBar();
}
if (m_firstSnippetTitleBar->isVisible())
{
m_firstSnippetTitleBar->getTitleButton()->updateTexts();
}
}
void QtCodeFileList::findScreenMatches(
const std::wstring& query, std::vector<std::pair<QtCodeArea*, Id>>* screenMatches)
{
for (QtCodeFile* file: m_files)
{
file->findScreenMatches(query, screenMatches);
}
}
void QtCodeFileList::setFocus(Id locationId)
{
for (QtCodeFile* file: m_files)
{
if (file->setFocus(locationId))
{
return;
}
}
}
void QtCodeFileList::setFocusOnTop()
{
if (m_files.size())
{
m_files[0]->focusTop();
}
}
void QtCodeFileList::moveFocus(const CodeFocusHandler::Focus& focus, CodeFocusHandler::Direction direction)
{
for (size_t i = 0; i < m_files.size(); i++)
{
QtCodeFile* file = m_files[i];
if (file->moveFocus(focus, direction))
{
return;
}
else if (file->hasFocus(focus))
{
if (direction == CodeFocusHandler::Direction::UP && i > 0)
{
m_files[i - 1]->focusBottom();
}
else if (direction == CodeFocusHandler::Direction::DOWN && i < m_files.size() - 1)
{
m_files[i + 1]->focusTop();
}
return;
}
}
}
void QtCodeFileList::copySelection()
{
for (QtCodeFile* file: m_files)
{
file->copySelection();
}
}
void QtCodeFileList::maximizeFirstFile()
{
if (m_files.size())
{
m_files[0]->clickedMaximizeButton();
}
}
std::pair<QtCodeFile*, Id> QtCodeFileList::getFirstFileWithActiveLocationId() const
{
for (QtCodeFile* file: m_files)
{
std::pair<QtCodeSnippet*, Id> snippet = file->getFirstSnippetWithActiveLocationId(0);
if (snippet.first != nullptr)
{
return std::make_pair(file, snippet.second);
}
}
return std::make_pair(nullptr, 0);
}
std::pair<QtCodeSnippet*, Id> QtCodeFileList::getFirstSnippetAndActiveLocationId() const
{
std::pair<QtCodeSnippet*, Id> result(nullptr, 0);
if (m_files.size())
{
std::vector<QtCodeSnippet*> snippets = m_files[0]->getVisibleSnippets();
if (snippets.size())
{
result.first = snippets[0];
result.second = snippets[0]->getFirstActiveLocationId(0);
}
}
return result;
}
void QtCodeFileList::resizeEvent(QResizeEvent* event)
{
clearSnippetTitleAndScrollBar();
updateSnippetTitleAndScrollBar();
}
void QtCodeFileList::updateSnippetTitleAndScrollBarSlot()
{
updateSnippetTitleAndScrollBar(0);
if (m_firstSnippetTitleBar && m_firstSnippetFile)
{
m_firstSnippetTitleBar->setIsFocused(
m_navigator->getCurrentFocus().file == m_firstSnippetFile);
}
}
void QtCodeFileList::updateSnippetTitleAndScrollBar(int value)
{
QtCodeFile* firstFile = nullptr;
QScrollBar* lastSnippetScrollBar = nullptr;
int fileTitleBarOffset = 0;
const QRect visibleRect(-m_filesArea->pos(), m_scrollArea->viewport()->size());
for (QtCodeFile* file: m_files)
{
QRect fileRect = getFocusRectForWidget(file, m_filesArea);
if (!firstFile && visibleRect.top() > fileRect.top() &&
visibleRect.top() < fileRect.bottom() + 1 && file->getVisibleSnippets().size())
{
firstFile = file;
fileTitleBarOffset = std::min(
0, fileRect.bottom() + 1 - (visibleRect.top() + m_firstSnippetTitleBar->height()));
}
if (visibleRect.bottom() > fileRect.top() && fileRect.bottom() > visibleRect.bottom())
{
for (QtCodeSnippet* snippet: file->getVisibleSnippets())
{
QScrollBar* scrollbar = snippet->getArea()->horizontalScrollBar();
if (!scrollbar || scrollbar->minimum() == scrollbar->maximum())
{
continue;
}
QRect snippetRect = getFocusRectForWidget(snippet, m_filesArea);
if (visibleRect.bottom() > snippetRect.top() + scrollbar->height() &&
snippetRect.bottom() - scrollbar->height() > visibleRect.bottom())
{
lastSnippetScrollBar = scrollbar;
break;
}
}
}
if (lastSnippetScrollBar)
{
break;
}
}
updateFirstSnippetTitleBar(firstFile, fileTitleBarOffset);
updateLastSnippetScrollBar(lastSnippetScrollBar);
}
void QtCodeFileList::scrollLastSnippet(int value)
{
if (m_mirroredSnippetScrollBar && m_mirroredSnippetScrollBar->value() != value)
{
m_mirroredSnippetScrollBar->setValue(value);
}
}
void QtCodeFileList::scrollLastSnippetScrollBar(int value)
{
if (m_lastSnippetScrollBar->value() != value)
{
m_lastSnippetScrollBar->setValue(value);
}
}
void QtCodeFileList::updateFirstSnippetTitleBar(QtCodeFile* file, int fileTitleBarOffset)
{
const QtCodeFileTitleBar* mirroredTitleBar = file ? file->getTitleBar() : nullptr;
if (m_mirroredTitleBar != mirroredTitleBar)
{
m_mirroredTitleBar = mirroredTitleBar;
if (m_mirroredTitleBar && file)
{
m_firstSnippetTitleBar->updateFromOther(mirroredTitleBar);
connect(
m_firstSnippetTitleBar,
&QtCodeFileTitleBar::minimize,
file,
&QtCodeFile::clickedMinimizeButton);
connect(
m_firstSnippetTitleBar,
&QtCodeFileTitleBar::snippet,
file,
&QtCodeFile::clickedSnippetButton);
connect(
m_firstSnippetTitleBar,
&QtCodeFileTitleBar::maximize,
file,
&QtCodeFile::clickedMaximizeButton);
connect(m_firstSnippetTitleBar, &QtHoverButton::hoveredIn, [this, file]() {
m_navigator->setFocusedFile(file);
m_navigator->setFocus();
});
m_firstSnippetTitleBar->setGeometry(
file->pos().x() + mirroredTitleBar->pos().x(),
0,
mirroredTitleBar->width(),
mirroredTitleBar->height());
m_firstSnippetTitleBar->show();
m_firstSnippetFile = file;
}
else
{
// Forces the title button icon to get reloaded next time, which fixes a color change
// issue when changing color scheme
m_firstSnippetTitleBar->getTitleButton()->setFilePath(FilePath());
m_firstSnippetTitleBar->hide();
m_firstSnippetFile = nullptr;
}
}
if (m_firstSnippetTitleBar->isVisible())
{
QRect rect = m_firstSnippetTitleBar->geometry();
int height = rect.height();
if (fileTitleBarOffset != rect.y())
{
rect.setY(fileTitleBarOffset);
rect.setHeight(height);
m_firstSnippetTitleBar->setGeometry(rect);
m_firstSnippetTitleBar->update();
}
}
}
void QtCodeFileList::updateLastSnippetScrollBar(QScrollBar* mirroredScrollBar)
{
if (m_mirroredSnippetScrollBar != mirroredScrollBar)
{
if (m_mirroredSnippetScrollBar)
{
m_mirroredSnippetScrollBar->disconnect(this);
}
m_mirroredSnippetScrollBar = mirroredScrollBar;
if (mirroredScrollBar)
{
connect(
mirroredScrollBar,
&QScrollBar::valueChanged,
this,
&QtCodeFileList::scrollLastSnippetScrollBar);
m_lastSnippetScrollBar->setMinimum(mirroredScrollBar->minimum());
m_lastSnippetScrollBar->setMaximum(mirroredScrollBar->maximum());
m_lastSnippetScrollBar->setValue(mirroredScrollBar->value());
m_lastSnippetScrollBar->setPageStep(mirroredScrollBar->pageStep());
m_lastSnippetScrollBar->setSingleStep(mirroredScrollBar->singleStep());
m_lastSnippetScrollBar->setGeometry(
mirroredScrollBar->mapTo(m_scrollArea, mirroredScrollBar->pos()).x(),
m_scrollArea->viewport()->size().height() - mirroredScrollBar->height(),
mirroredScrollBar->width(),
mirroredScrollBar->height());
m_lastSnippetScrollBar->show();
if (utility::getOsType() == OS_MAC)
{
// set style on scrollbar because it always has bright background by default
QString style = m_lastSnippetScrollBar->styleSheet();
style.resize(m_styleSize);
m_lastSnippetScrollBar->setStyleSheet(
style +
("\nQScrollBar:horizontal { background: " +
ColorScheme::getInstance()->getColor("code/snippet/background") + "; }")
.c_str());
}
}
else
{
m_lastSnippetScrollBar->hide();
}
}
}