(sender());
if (action)
loadFile(action->data().toString());
}
// Close current tab
void MainWindow::closeTab()
{
int currentIndex = mainTabWidget_.currentIndex();
if ( currentIndex >= 0 )
{
closeTab(currentIndex);
}
}
// Close all tabs
void MainWindow::closeAll()
{
while ( mainTabWidget_.count() )
{
closeTab(0);
}
}
// Select all the text in the currently selected view
void MainWindow::selectAll()
{
CrawlerWidget* current = currentCrawlerWidget();
if ( current )
current->selectAll();
}
// Copy the currently selected line into the clipboard
void MainWindow::copy()
{
static QClipboard* clipboard = QApplication::clipboard();
CrawlerWidget* current = currentCrawlerWidget();
if ( current ) {
clipboard->setText( current->getSelectedText() );
// Put it in the global selection as well (X11 only)
clipboard->setText( current->getSelectedText(),
QClipboard::Selection );
}
}
// Display the QuickFind bar
void MainWindow::find()
{
displayQuickFindBar( QuickFindMux::Forward );
}
// Opens the 'Filters' dialog box
void MainWindow::filters()
{
FiltersDialog dialog(this);
signalMux_.connect(&dialog, SIGNAL( optionsChanged() ), SLOT( applyConfiguration() ));
dialog.exec();
signalMux_.disconnect(&dialog, SIGNAL( optionsChanged() ), SLOT( applyConfiguration() ));
}
// Opens the 'Options' modal dialog box
void MainWindow::options()
{
OptionsDialog dialog(this);
signalMux_.connect(&dialog, SIGNAL( optionsChanged() ), SLOT( applyConfiguration() ));
dialog.exec();
signalMux_.disconnect(&dialog, SIGNAL( optionsChanged() ), SLOT( applyConfiguration() ));
}
// Opens the 'About' dialog box.
void MainWindow::about()
{
QMessageBox::about(this, tr("About glogg"),
tr("glogg " GLOGG_VERSION "
"
"A fast, advanced log explorer."
#ifdef GLOGG_COMMIT
"
Built " GLOGG_DATE " from " GLOGG_COMMIT
#endif
"
http://glogg.bonnefon.org/
"
"Copyright © 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicolas Bonnefon and other contributors"
"
You may modify and redistribute the program under the terms of the GPL (version 3 or later)." ) );
}
// Opens the 'About Qt' dialog box.
void MainWindow::aboutQt()
{
}
void MainWindow::encodingChanged( QAction* action )
{
int i = 0;
for ( i = 0; i < CrawlerWidget::ENCODING_MAX; ++i )
if ( action == encodingAction[i] )
break;
LOG(logDEBUG) << "encodingChanged, encoding " << i;
currentCrawlerWidget()->setEncoding( static_cast( i ) );
updateInfoLine();
}
void MainWindow::toggleOverviewVisibility( bool isVisible )
{
std::shared_ptr config =
Persistent( "settings" );
config->setOverviewVisible( isVisible );
emit optionsChanged();
}
void MainWindow::toggleMainLineNumbersVisibility( bool isVisible )
{
std::shared_ptr config =
Persistent( "settings" );
config->setMainLineNumbersVisible( isVisible );
emit optionsChanged();
}
void MainWindow::toggleFilteredLineNumbersVisibility( bool isVisible )
{
std::shared_ptr config =
Persistent( "settings" );
config->setFilteredLineNumbersVisible( isVisible );
emit optionsChanged();
}
void MainWindow::changeFollowMode( bool follow )
{
followAction->setChecked( follow );
}
void MainWindow::lineNumberHandler( int line )
{
// The line number received is the internal (starts at 0)
lineNbField->setText( tr( "Line %1" ).arg( line + 1 ) );
}
void MainWindow::updateLoadingProgress( int progress )
{
LOG(logDEBUG) << "Loading progress: " << progress;
QString current_file =
session_->getFilename( currentCrawlerWidget() ).c_str();
// We ignore 0% and 100% to avoid a flash when the file (or update)
// is very short.
if ( progress > 0 && progress < 100 ) {
infoLine->setText( current_file +
tr( " - Indexing lines... (%1 %)" ).arg( progress ) );
infoLine->displayGauge( progress );
stopAction->setEnabled( true );
reloadAction->setEnabled( false );
}
}
void MainWindow::handleLoadingFinished( LoadingStatus status )
{
LOG(logDEBUG) << "handleLoadingFinished success=" <<
( status == LoadingStatus::Successful );
// No file is loading
loadingFileName.clear();
if ( status == LoadingStatus::Successful )
{
updateInfoLine();
infoLine->hideGauge();
stopAction->setEnabled( false );
reloadAction->setEnabled( true );
// Now everything is ready, we can finally show the file!
currentCrawlerWidget()->show();
}
else
{
if ( status == LoadingStatus::NoMemory )
{
QMessageBox alertBox;
alertBox.setText( "Not enough memory." );
alertBox.setInformativeText( "The system does not have enough \
memory to hold the index for this file. The file will now be closed." );
alertBox.setIcon( QMessageBox::Critical );
alertBox.exec();
}
closeTab( mainTabWidget_.currentIndex() );
}
// mainTabWidget_.setEnabled( true );
}
void MainWindow::handleSearchRefreshChanged( int state )
{
auto config = Persistent( "settings" );
config->setSearchAutoRefreshDefault( state == Qt::Checked );
}
void MainWindow::handleIgnoreCaseChanged( int state )
{
auto config = Persistent( "settings" );
config->setSearchIgnoreCaseDefault( state == Qt::Checked );
}
void MainWindow::closeTab( int index )
{
auto widget = dynamic_cast(
mainTabWidget_.widget( index ) );
assert( widget );
widget->stopLoading();
mainTabWidget_.removeTab( index );
session_->close( widget );
delete widget;
}
void MainWindow::currentTabChanged( int index )
{
LOG(logDEBUG) << "currentTabChanged";
if ( index >= 0 )
{
CrawlerWidget* crawler_widget = dynamic_cast(
mainTabWidget_.widget( index ) );
signalMux_.setCurrentDocument( crawler_widget );
quickFindMux_.registerSelector( crawler_widget );
// New tab is set up with fonts etc...
emit optionsChanged();
// Update the menu bar
updateMenuBarFromDocument( crawler_widget );
// Update the title bar
updateTitleBar( QString(
session_->getFilename( crawler_widget ).c_str() ) );
}
else
{
// No tab left
signalMux_.setCurrentDocument( nullptr );
quickFindMux_.registerSelector( nullptr );
infoLine->hideGauge();
infoLine->clear();
updateTitleBar( QString() );
}
}
void MainWindow::changeQFPattern( const QString& newPattern )
{
quickFindWidget_.changeDisplayedPattern( newPattern );
}
void MainWindow::loadFileNonInteractive( const QString& file_name )
{
LOG(logDEBUG) << "loadFileNonInteractive( "
<< file_name.toStdString() << " )";
loadFile( file_name );
// Try to get the window to the front
// This is a bit of a hack but has been tested on:
// Qt 5.3 / Gnome / Linux
// Qt 4.8 / Win7
#ifdef _WIN32
// Hack copied from http://qt-project.org/forums/viewthread/6164
::SetWindowPos((HWND) effectiveWinId(), HWND_TOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
::SetWindowPos((HWND) effectiveWinId(), HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#else
Qt::WindowFlags window_flags = windowFlags();
window_flags |= Qt::WindowStaysOnTopHint;
setWindowFlags( window_flags );
#endif
activateWindow();
raise();
#ifndef _WIN32
window_flags = windowFlags();
window_flags &= ~Qt::WindowStaysOnTopHint;
setWindowFlags( window_flags );
#endif
showNormal();
}
void MainWindow::newVersionNotification( const QString& new_version )
{
LOG(logDEBUG) << "newVersionNotification( " <<
new_version.toStdString() << " )";
QMessageBox msgBox;
msgBox.setText( QString( "A new version of glogg (%1) is available for download "
"http://glogg.bonnefon.org/download.html"
).arg( new_version ) );
msgBox.exec();
}
//
// Events
//
// Closes the application
void MainWindow::closeEvent( QCloseEvent *event )
{
writeSettings();
event->accept();
}
// Accepts the drag event if it looks like a filename
void MainWindow::dragEnterEvent( QDragEnterEvent* event )
{
if ( event->mimeData()->hasFormat( "text/uri-list" ) )
event->acceptProposedAction();
}
// Tries and loads the file if the URL dropped is local
void MainWindow::dropEvent( QDropEvent* event )
{
QList urls = event->mimeData()->urls();
if ( urls.isEmpty() )
return;
QString fileName = urls.first().toLocalFile();
if ( fileName.isEmpty() )
return;
loadFile( fileName );
}
void MainWindow::keyPressEvent( QKeyEvent* keyEvent )
{
LOG(logDEBUG4) << "keyPressEvent received";
switch ( (keyEvent->text())[0].toLatin1() ) {
case '/':
displayQuickFindBar( QuickFindMux::Forward );
break;
case '?':
displayQuickFindBar( QuickFindMux::Backward );
break;
default:
keyEvent->ignore();
}
if ( !keyEvent->isAccepted() )
QMainWindow::keyPressEvent( keyEvent );
}
//
// Private functions
//
// Create a CrawlerWidget for the passed file, start its loading
// and update the title bar.
// The loading is done asynchronously.
bool MainWindow::loadFile( const QString& fileName )
{
LOG(logDEBUG) << "loadFile ( " << fileName.toStdString() << " )";
// First check if the file is already open...
CrawlerWidget* existing_crawler = dynamic_cast(
session_->getViewIfOpen( fileName.toStdString() ) );
if ( existing_crawler ) {
// ... and switch to it.
mainTabWidget_.setCurrentWidget( existing_crawler );
return true;
}
// Load the file
loadingFileName = fileName;
try {
CrawlerWidget* crawler_widget = dynamic_cast(
session_->open( fileName.toStdString(),
[]() { return new CrawlerWidget(); } ) );
assert( crawler_widget );
// We won't show the widget until the file is fully loaded
crawler_widget->hide();
// We disable the tab widget to avoid having someone switch
// tab during loading. (maybe FIXME)
//mainTabWidget_.setEnabled( false );
int index = mainTabWidget_.addTab(
crawler_widget, strippedName( fileName ) );
// Setting the new tab, the user will see a blank page for the duration
// of the loading, with no way to switch to another tab
mainTabWidget_.setCurrentIndex( index );
// Update the recent files list
// (reload the list first in case another glogg changed it)
GetPersistentInfo().retrieve( "recentFiles" );
recentFiles_->addRecent( fileName );
GetPersistentInfo().save( "recentFiles" );
updateRecentFileActions();
}
catch ( FileUnreadableErr ) {
LOG(logDEBUG) << "Can't open file " << fileName.toStdString();
return false;
}
LOG(logDEBUG) << "Success loading file " << fileName.toStdString();
return true;
}
// Strips the passed filename from its directory part.
QString MainWindow::strippedName( const QString& fullFileName ) const
{
return QFileInfo( fullFileName ).fileName();
}
// Return the currently active CrawlerWidget, or NULL if none
CrawlerWidget* MainWindow::currentCrawlerWidget() const
{
auto current = dynamic_cast(
mainTabWidget_.currentWidget() );
return current;
}
// Update the title bar.
void MainWindow::updateTitleBar( const QString& file_name )
{
QString shownName = tr( "Untitled" );
if ( !file_name.isEmpty() )
shownName = strippedName( file_name );
setWindowTitle(
tr("%1 - %2").arg(shownName).arg(tr("glogg"))
#ifdef GLOGG_COMMIT
+ " (dev build " GLOGG_VERSION ")"
#endif
);
}
// Updates the actions for the recent files.
// Must be called after having added a new name to the list.
void MainWindow::updateRecentFileActions()
{
QStringList recent_files = recentFiles_->recentFiles();
for ( int j = 0; j < MaxRecentFiles; ++j ) {
if ( j < recent_files.count() ) {
QString text = tr("&%1 %2").arg(j + 1).arg(strippedName(recent_files[j]));
recentFileActions[j]->setText( text );
recentFileActions[j]->setToolTip( recent_files[j] );
recentFileActions[j]->setData( recent_files[j] );
recentFileActions[j]->setVisible( true );
}
else {
recentFileActions[j]->setVisible( false );
}
}
// separatorAction->setVisible(!recentFiles.isEmpty());
}
// Update our menu bar to match the settings of the crawler
// (used when the tab is changed)
void MainWindow::updateMenuBarFromDocument( const CrawlerWidget* crawler )
{
auto encoding = crawler->encodingSetting();
encodingAction[static_cast( encoding )]->setChecked( true );
}
// Update the top info line from the session
void MainWindow::updateInfoLine()
{
QLocale defaultLocale;
// Following should always work as we will only receive enter
// this slot if there is a crawler connected.
QString current_file =
session_->getFilename( currentCrawlerWidget() ).c_str();
uint64_t fileSize;
uint32_t fileNbLine;
QDateTime lastModified;
session_->getFileInfo( currentCrawlerWidget(),
&fileSize, &fileNbLine, &lastModified );
if ( lastModified.isValid() ) {
const QString date =
defaultLocale.toString( lastModified, QLocale::NarrowFormat );
infoLine->setText( tr( "%1 (%2 - %3 lines - modified on %4 - %5)" )
.arg(current_file).arg(readableSize(fileSize))
.arg(fileNbLine).arg( date )
.arg(currentCrawlerWidget()->encodingText()) );
}
else {
infoLine->setText( tr( "%1 (%2 - %3 lines - %4)" )
.arg(current_file).arg(readableSize(fileSize))
.arg(fileNbLine)
.arg(currentCrawlerWidget()->encodingText()) );
}
}
// Write settings to permanent storage
void MainWindow::writeSettings()
{
// Save the session
// Generate the ordered list of widgets and their topLine
std::vector<
std::tuple>
> widget_list;
for ( int i = 0; i < mainTabWidget_.count(); ++i )
{
auto view = dynamic_cast( mainTabWidget_.widget( i ) );
widget_list.push_back( std::make_tuple(
view,
0UL,
view->context() ) );
}
session_->save( widget_list, saveGeometry() );
// User settings
GetPersistentInfo().save( QString( "settings" ) );
}
// Read settings from permanent storage
void MainWindow::readSettings()
{
// Get and restore the session
// GetPersistentInfo().retrieve( QString( "session" ) );
// SessionInfo session = Persistent( "session" );
/*
* FIXME: should be in the session
crawlerWidget->restoreState( session.crawlerState() );
*/
// History of recent files
GetPersistentInfo().retrieve( QString( "recentFiles" ) );
updateRecentFileActions();
// GetPersistentInfo().retrieve( QString( "settings" ) );
GetPersistentInfo().retrieve( QString( "filterSet" ) );
}
void MainWindow::displayQuickFindBar( QuickFindMux::QFDirection direction )
{
LOG(logDEBUG) << "MainWindow::displayQuickFindBar";
// Warn crawlers so they can save the position of the focus in order
// to do incremental search in the right view.
emit enteringQuickFind();
quickFindMux_.setDirection( direction );
quickFindWidget_.userActivate();
}
// Returns the size in human readable format
static QString readableSize( qint64 size )
{
static const QString sizeStrs[] = {
QObject::tr("B"), QObject::tr("KiB"), QObject::tr("MiB"),
QObject::tr("GiB"), QObject::tr("TiB") };
QLocale defaultLocale;
unsigned int i;
double humanSize = size;
for ( i=0; i+1 < (sizeof(sizeStrs)/sizeof(QString)) && (humanSize/1024.0) >= 1024.0; i++ )
humanSize /= 1024.0;
if ( humanSize >= 1024.0 ) {
humanSize /= 1024.0;
i++;
}
QString output;
if ( i == 0 )
// No decimal part if we display straight bytes.
output = defaultLocale.toString( (int) humanSize );
else
output = defaultLocale.toString( humanSize, 'f', 1 );
output += QString(" ") + sizeStrs[i];
return output;
}