glogg-1.1.4/ 0000755 0001750 0001750 00000000000 13076706620 011613 5 ustar nicko nicko glogg-1.1.4/runtests.sh 0000755 0001750 0001750 00000000462 13076461374 014047 0 ustar nicko nicko #!/bin/sh
./tests/logcrawler_tests $*
result=$?
ICON_OK=gtk-apply
ICON_ERROR=gtk-cancel
if [ `which notify-send` ]; then
if [ "$result" != "0" ]; then
notify-send -i $ICON_ERROR "glogg" "Tests failed!"
else
notify-send -i $ICON_OK "glogg" "Tests passed!"
fi
fi
exit $result
glogg-1.1.4/glogg.qrc 0000644 0001750 0001750 00000001637 13076461374 013434 0 ustar nicko nicko
images/hicolor/16x16/glogg.png
images/hicolor/24x24/glogg.png
images/hicolor/32x32/glogg.png
images/hicolor/48x48/glogg.png
images/open14.png
images/open14@2x.png
images/reload14.png
images/reload14@2x.png
images/olddata_icon.png
images/olddata_icon@2x.png
images/newdata_icon.png
images/newdata_icon@2x.png
images/newfiltered_icon.png
images/newfiltered_icon@2x.png
images/stop14.png
images/plus.png
images/minus.png
images/down.png
images/up.png
images/arrowdown.png
images/arrowup.png
images/darkclosebutton.png
glogg-1.1.4/src/ 0000755 0001750 0001750 00000000000 13076461374 012406 5 ustar nicko nicko glogg-1.1.4/src/inotifywatchtowerdriver.h 0000644 0001750 0001750 00000010467 13076461374 017574 0 ustar nicko nicko /*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef INOTIFYWATCHTOWERDRIVER_H
#define INOTIFYWATCHTOWERDRIVER_H
#include
#include
#include
template
class ObservedFile;
template
class ObservedFileList;
class INotifyWatchTowerDriver {
public:
class FileId {
public:
friend class INotifyWatchTowerDriver;
FileId() { wd_ = -1; }
bool operator==( const FileId& other ) const
{ return wd_ == other.wd_; }
private:
FileId( int wd ) { wd_ = wd; }
int wd_;
};
class DirId {
public:
friend class INotifyWatchTowerDriver;
DirId() { wd_ = -1; }
bool operator==( const DirId& other ) const
{ return wd_ == other.wd_; }
bool valid() const
{ return (wd_ != -1); }
private:
DirId( int wd ) { wd_ = wd; }
int wd_;
};
class SymlinkId {
public:
friend class INotifyWatchTowerDriver;
SymlinkId() { wd_ = -1; }
bool operator==( const SymlinkId& other ) const
{ return wd_ == other.wd_; }
private:
SymlinkId( int wd ) { wd_ = wd; }
int wd_;
};
// Dummy class for inotify
class FileChangeToken {
public:
FileChangeToken() {}
FileChangeToken( const std::string& ) {}
void readFromFile( const std::string& ) {}
bool operator!=( const FileChangeToken& )
{ return true; }
};
#ifdef HAS_TEMPLATE_ALIASES
using INotifyObservedFile = ObservedFile;
using INotifyObservedFileList = ObservedFileList;
#else
typedef ObservedFile INotifyObservedFile;
typedef ObservedFileList INotifyObservedFileList;
#endif
// Default constructor
INotifyWatchTowerDriver();
~INotifyWatchTowerDriver();
// No copy/assign/move please
INotifyWatchTowerDriver( const INotifyWatchTowerDriver& ) = delete;
INotifyWatchTowerDriver& operator=( const INotifyWatchTowerDriver& ) = delete;
INotifyWatchTowerDriver( const INotifyWatchTowerDriver&& ) = delete;
INotifyWatchTowerDriver& operator=( const INotifyWatchTowerDriver&& ) = delete;
FileId addFile( const std::string& file_name );
SymlinkId addSymlink( const std::string& file_name );
DirId addDir( const std::string& file_name );
void removeFile( const FileId& file_id );
void removeSymlink( const SymlinkId& symlink_id );
void removeDir( const DirId& dir_id );
// Wait for an event for the OS, treat it and
// return a list of files to notify about.
// This must be called with the lock on the list held,
// the function will unlock it temporary whilst blocking.
// Also returns a list of file that need readding
// (because of renames/symlink...)
std::vector waitAndProcessEvents(
INotifyObservedFileList* list,
std::unique_lock* list_mutex,
std::vector* files_needing_readding,
int timeout_ms );
// Interrupt waitAndProcessEvents if it is blocking.
void interruptWait();
private:
// Only written at initialisation so no protection needed.
const int inotify_fd_;
// Breaking pipe
int breaking_pipe_read_fd_;
int breaking_pipe_write_fd_;
// Private member functions
size_t processINotifyEvent( const struct inotify_event* event,
INotifyObservedFileList* list,
std::vector* files_to_notify,
std::vector* files_needing_readding );
};
#endif
glogg-1.1.4/src/qtfilewatcher.cpp 0000644 0001750 0001750 00000011353 13076461374 015757 0 ustar nicko nicko /*
* Copyright (C) 2010 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include "log.h"
#include "qtfilewatcher.h"
#include
#include
QtFileWatcher::QtFileWatcher() : FileWatcher(), qtFileWatcher_( this )
{
monitoringState_ = None;
connect( &qtFileWatcher_, SIGNAL( fileChanged( const QString& ) ),
this, SLOT( fileChangedOnDisk( const QString& ) ) );
connect( &qtFileWatcher_, SIGNAL( directoryChanged( const QString& ) ),
this, SLOT( directoryChangedOnDisk( const QString& ) ) );
}
QtFileWatcher::~QtFileWatcher()
{
disconnect( &qtFileWatcher_ );
}
void QtFileWatcher::addFile( const QString& fileName )
{
LOG(logDEBUG) << "QtFileWatcher::addFile " << fileName.toStdString();
QFileInfo fileInfo = QFileInfo( fileName );
if ( fileMonitored_.isEmpty() ) {
fileMonitored_ = fileName;
// Initialise the Qt file watcher
qtFileWatcher_.addPath( fileInfo.path() );
if ( fileInfo.exists() ) {
LOG(logDEBUG) << "QtFileWatcher::addFile: file exists.";
qtFileWatcher_.addPath( fileName );
monitoringState_ = FileExists;
}
else {
LOG(logDEBUG) << "QtFileWatcher::addFile: file doesn't exist.";
monitoringState_ = FileRemoved;
}
}
else {
LOG(logWARNING) << "QtFileWatcher::addFile " << fileName.toStdString()
<< "- Already watching a file (" << fileMonitored_.toStdString()
<< ")!";
}
}
void QtFileWatcher::removeFile( const QString& fileName )
{
LOG(logDEBUG) << "QtFileWatcher::removeFile " << fileName.toStdString();
QFileInfo fileInfo = QFileInfo( fileName );
if ( fileName == fileMonitored_ ) {
if ( monitoringState_ == FileExists )
qtFileWatcher_.removePath( fileName );
qtFileWatcher_.removePath( fileInfo.path() );
fileMonitored_.clear();
monitoringState_ = None;
}
else {
LOG(logWARNING) << "QtFileWatcher::removeFile - The file is not watched!";
}
// For debug purpose:
foreach (QString str, qtFileWatcher_.files()) {
LOG(logERROR) << "File still watched: " << str.toStdString();
}
foreach (QString str, qtFileWatcher_.directories()) {
LOG(logERROR) << "Directories still watched: " << str.toStdString();
}
}
//
// Slots
//
void QtFileWatcher::fileChangedOnDisk( const QString& filename )
{
LOG(logDEBUG) << "QtFileWatcher::fileChangedOnDisk " << filename.toStdString();
if ( ( monitoringState_ == FileExists ) && ( filename == fileMonitored_ ) )
{
emit fileChanged( filename );
// If the file has been removed...
if ( !QFileInfo( filename ).exists() )
monitoringState_ = FileRemoved;
}
else
LOG(logWARNING) << "QtFileWatcher::fileChangedOnDisk - call from Qt but no file monitored";
}
void QtFileWatcher::directoryChangedOnDisk( const QString& filename )
{
LOG(logDEBUG) << "QtFileWatcher::directoryChangedOnDisk " << filename.toStdString();
if ( monitoringState_ == FileRemoved ) {
if ( QFileInfo( fileMonitored_ ).exists() ) {
LOG(logDEBUG) << "QtFileWatcher::directoryChangedOnDisk - our file reappeared!";
// The file has been recreated, we have to watch it again.
monitoringState_ = FileExists;
// Restore the Qt file watcher (automatically cancelled
// when the file is deleted)
qtFileWatcher_.addPath( fileMonitored_ );
emit fileChanged( fileMonitored_ );
}
else {
LOG(logWARNING) << "QtFileWatcher::directoryChangedOnDisk - not the file we are watching";
}
}
else if ( monitoringState_ == FileExists )
{
if ( ! QFileInfo( fileMonitored_ ).exists() ) {
LOG(logDEBUG) << "QtFileWatcher::directoryChangedOnDisk - our file disappeared!";
monitoringState_ = FileRemoved;
emit fileChanged( filename );
}
else {
LOG(logWARNING) << "QtFileWatcher::directoryChangedOnDisk - not the file we are watching";
}
}
}
glogg-1.1.4/src/menuactiontooltipbehavior.cpp 0000644 0001750 0001750 00000007331 13076461374 020413 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include
#include
#include
#include
#include "menuactiontooltipbehavior.h"
// It would be nice to only need action, and have action be the parent,
// however implementation needs the parent menu (see showToolTip), and
// since neither action nor parent is guaranteed to die before the other,
// for memory-management purposes the parent will have to be specified
// explicity (probably, the window owning the action and the menu).
MenuActionToolTipBehavior::MenuActionToolTipBehavior(QAction *action,
QMenu *parentMenu,
QObject *parent = 0)
: QObject(parent),
action(action),
parentMenu(parentMenu),
toolTipDelayMs(1000),
timerId(0),
hoverPoint()
{
connect(action, SIGNAL(hovered()), this, SLOT(onActionHovered()));
}
int MenuActionToolTipBehavior::toolTipDelay()
{
return toolTipDelayMs;
}
void MenuActionToolTipBehavior::setToolTipDelay(int delayMs)
{
toolTipDelayMs = delayMs;
}
void MenuActionToolTipBehavior::timerEvent(QTimerEvent *event)
{
// Not ours, don't touch
if (event->timerId() != timerId) {
QObject::timerEvent(event);
return;
}
killTimer(timerId); // interested in a single shot
timerId = 0;
// Has the mouse waited unmoved in one location for 'delay' ms?
const QPoint &mousePos = QCursor::pos();
if (hoverPoint == mousePos)
showToolTip(hoverPoint);
}
void MenuActionToolTipBehavior::onActionHovered()
{
const QPoint &mousePos = QCursor::pos();
// Hover is fired on keyboard focus over action in menu, ignore it
const QPoint &relativeMousePos = parentMenu->mapFromGlobal(mousePos);
if (!parentMenu->actionGeometry(action).contains(relativeMousePos)) {
if (timerId != 0) { // once timer expires its check will fail anyway
killTimer(timerId);
timerId = 0;
}
QToolTip::hideText(); // there might be one currently shown
return;
}
// Record location
hoverPoint = mousePos;
// Restart timer
if (timerId != 0)
killTimer(timerId);
timerId = startTimer(toolTipDelayMs);
}
void MenuActionToolTipBehavior::showToolTip(const QPoint &position)
{
const QString &toolTip = action->toolTip();
// Show tooltip until mouse moves at all
// NOTE: using action->parentWidget() which is the MainWindow,
// does not work (tooltip is not cleared when upon leaving the
// region). This is the only reason we need parentMenu here. Just
// a wild guess: maybe it isn't cleared because it would be
// cleared on a mouse move over the designated widget, but mouse
// move doesn't happen over MainWindow, since the mouse is over
// the menu even when out of the activeRegion.
QPoint relativePos = parentMenu->mapFromGlobal(position);
QRect activeRegion(relativePos.x(), relativePos.y(), 1, 1);
QToolTip::showText(position, toolTip, parentMenu, activeRegion);
}
glogg-1.1.4/src/filewatcher.cpp 0000644 0001750 0001750 00000000000 13076461374 015375 0 ustar nicko nicko glogg-1.1.4/src/loadingstatus.h 0000644 0001750 0001750 00000002141 13076461374 015436 0 ustar nicko nicko /*
* Copyright (C) 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef LOADINGSTATUS_H
#define LOADINGSTATUS_H
#include
// Loading status of a file
enum class LoadingStatus {
Successful,
Interrupted,
NoMemory
};
// Data status (whether new, not seen, data is available)
enum class DataStatus {
OLD_DATA,
NEW_DATA,
NEW_FILTERED_DATA
};
Q_DECLARE_METATYPE( DataStatus )
Q_DECLARE_METATYPE( LoadingStatus )
#endif
glogg-1.1.4/src/externalcom.h 0000644 0001750 0001750 00000003350 13076461374 015101 0 ustar nicko nicko /*
* Copyright (C) 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef EXTERNALCOM_H
#define EXTERNALCOM_H
#include
class CantCreateExternalErr {};
/*
* Virtual class representing another instance of glogg.
* Sending messages to an object of this class will forward
* them to the instance using the underlying IPC.
*/
class ExternalInstance
{
public:
ExternalInstance() {}
virtual ~ExternalInstance() {}
virtual void loadFile( const QString& file_name ) const = 0;
virtual uint32_t getVersion() const = 0;
};
/*
* Class receiving messages from another instance of glogg.
* Messages are forwarded to the application by signals.
*/
class ExternalCommunicator : public QObject
{
Q_OBJECT
public:
ExternalCommunicator() : QObject() {}
virtual ExternalInstance* otherInstance() const = 0;
/* Instruct the communicator to start listening for
* remote initiated operations */
virtual void startListening() = 0;
signals:
void loadFile( const QString& file_name );
public slots:
virtual qint32 version() const = 0;
};
#endif
glogg-1.1.4/src/infoline.cpp 0000644 0001750 0001750 00000004163 13076461374 014721 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include "log.h"
#include "infoline.h"
#include
// This file implements InfoLine. It is responsible for decorating the
// widget and managing the completion gauge.
InfoLine::InfoLine() :
QLabel(), origPalette_( palette() ),
backgroundColor_( origPalette_.color( QPalette::Button ) ),
darkBackgroundColor_( origPalette_.color( QPalette::Dark ) )
{
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
}
void InfoLine::displayGauge( int completion )
{
int changeoverX = width() * completion / 100;
// Create a gradient for the progress bar
QLinearGradient linearGrad( changeoverX - 1, 0, changeoverX + 1, 0 );
linearGrad.setColorAt( 0, darkBackgroundColor_ );
linearGrad.setColorAt( 1, backgroundColor_ );
// Apply the gradient to the current palette (background)
QPalette newPalette = origPalette_;
newPalette.setBrush( backgroundRole(), QBrush( linearGrad ) );
setPalette( newPalette );
}
void InfoLine::hideGauge()
{
setPalette( origPalette_ );
}
// Custom painter: draw the background then call QLabel's painter
void InfoLine::paintEvent( QPaintEvent* paintEvent )
{
// Fill the widget background
{
QPainter painter( this );
painter.fillRect( 0, 0, this->width(), this->height(),
palette().brush( backgroundRole() ) );
}
// Call the parent's painter
QLabel::paintEvent( paintEvent );
}
glogg-1.1.4/src/socketexternalcom.h 0000644 0001750 0001750 00000001456 13076461374 016317 0 ustar nicko nicko #ifndef SOCKETEXTERNALCOM_H
#define SOCKETEXTERNALCOM_H
#include "externalcom.h"
#include
#include
class SocketExternalInstance : public ExternalInstance
{
public:
SocketExternalInstance();
void loadFile( const QString& file_name ) const override;
uint32_t getVersion() const override;
private:
QSharedMemory* memory_;
};
class SocketExternalCommunicator : public ExternalCommunicator
{
Q_OBJECT
public:
SocketExternalCommunicator();
~SocketExternalCommunicator();
ExternalInstance* otherInstance() const override;
void startListening() override;
public slots:
qint32 version() const override;
private slots:
void onConnection();
private:
QSharedMemory* memory_;
QLocalServer* server_;
};
#endif // SOCKETEXTERNALCOM_H
glogg-1.1.4/src/optionsdialog.h 0000644 0001750 0001750 00000004023 13076461374 015431 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010, 2013 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef OPTIONSDIALOG_H
#define OPTIONSDIALOG_H
#include
#include "configuration.h"
#include "ui_optionsdialog.h"
// Implements the main option dialog box
class OptionsDialog : public QDialog, public Ui::OptionsDialog
{
Q_OBJECT
public:
OptionsDialog(QWidget* parent = 0);
signals:
// Is emitted when new settings must be used
void optionsChanged();
private slots:
// Clears and updates the font size box with the sizes allowed
// by the passed font family.
void updateFontSize(const QString& fontFamily);
// Update the content of the global Config() using parameters
// from the dialog box.
void updateConfigFromDialog();
// Called when a ok/cancel/apply button is clicked.
void onButtonBoxClicked( QAbstractButton* button );
// Called when the 'incremental' button is toggled.
void onIncrementalChanged();
// Called when the 'polling' checkbox is toggled.
void onPollingChanged();
private:
void setupTabs();
void setupFontList();
void setupRegexp();
void setupIncremental();
void setupPolling();
int getRegexpIndex( SearchRegexpType syntax ) const;
SearchRegexpType getRegexpTypeFromIndex( int index ) const;
void updateDialogFromConfig();
QValidator* polling_interval_validator_;
};
#endif
glogg-1.1.4/src/signalmux.cpp 0000644 0001750 0001750 00000011555 13076461374 015130 0 ustar nicko nicko /*
* Copyright (C) 2013 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include "signalmux.h"
#include
#include
#include "log.h"
SignalMux::SignalMux() :
connectionList_()
{
currentDocument_ = nullptr;
}
void SignalMux::connect( QObject *sender,
const char *signal, const char *slot )
{
Connection new_connection = {
sender,
nullptr,
signal,
slot };
connectionList_.push_back( new_connection );
connect( new_connection );
}
void SignalMux::disconnect( QObject *sender,
const char *signal, const char *slot )
{
// Find any signal that match our description
auto connection = std::find_if(
connectionList_.begin(), connectionList_.end(),
[sender, signal, slot]( const Connection& c ) -> bool
{ return ((QObject*)c.source == sender) && (c.sink == nullptr) &&
(qstrcmp( c.signal, signal) == 0) && (qstrcmp( c.slot, slot) == 0); } );
if ( connection != connectionList_.end() )
{
disconnect( *connection );
connectionList_.erase( connection );
}
else
{
LOG( logERROR ) << "SignalMux disconnecting a non-existing signal";
}
}
// Upstream signals
void SignalMux::connect( const char* signal,
QObject* receiver, const char* slot )
{
Connection new_connection = {
nullptr,
receiver,
signal,
slot };
connectionList_.push_back( new_connection );
connect( new_connection );
}
void SignalMux::disconnect( const char *signal,
QObject* receiver, const char *slot )
{
// Find any signal that match our description
auto connection = std::find_if(
connectionList_.begin(), connectionList_.end(),
[receiver, signal, slot]( const Connection& c ) -> bool
{ return ((QObject*)c.sink == receiver) && (c.source == nullptr) &&
(qstrcmp( c.signal, signal) == 0) && (qstrcmp( c.slot, slot) == 0); } );
if ( connection != connectionList_.end() )
{
disconnect( *connection );
connectionList_.erase( connection );
}
else
{
LOG( logERROR ) << "SignalMux disconnecting a non-existing signal";
}
}
void SignalMux::setCurrentDocument( QObject* current_document )
{
// First disconnect everything to/from the old document
for ( auto c: connectionList_ )
disconnect( c );
currentDocument_ = current_document;
// And now connect to/from the new document
for ( auto c: connectionList_ )
connect( c );
// And ask the doc to emit all state signals
MuxableDocumentInterface* doc =
dynamic_cast( current_document );
if ( doc )
doc->sendAllStateSignals();
}
/*
* Private functions
*/
void SignalMux::connect( const Connection& connection )
{
if ( currentDocument_ )
{
LOG( logDEBUG ) << "SignalMux::connect";
if ( connection.source && ( ! connection.sink ) )
{
// Downstream signal
QObject::connect( connection.source, connection.signal,
currentDocument_, connection.slot );
}
else if ( ( ! connection.source ) && connection.sink )
{
// Upstream signal
QObject::connect( currentDocument_, connection.signal,
connection.sink, connection.slot );
}
else
{
LOG( logERROR ) << "SignalMux has an unexpected signal";
}
}
}
void SignalMux::disconnect( const Connection& connection )
{
if ( currentDocument_ )
{
LOG( logDEBUG ) << "SignalMux::disconnect";
if ( connection.source && ( ! connection.sink ) )
{
// Downstream signal
QObject::disconnect( connection.source, connection.signal,
currentDocument_, connection.slot );
}
else if ( ( ! connection.source ) && connection.sink )
{
// Upstream signal
QObject::disconnect( currentDocument_, connection.signal,
connection.sink, connection.slot );
}
else
{
LOG( logERROR ) << "SignalMux has an unexpected signal";
}
}
}
glogg-1.1.4/src/quickfindpattern.cpp 0000644 0001750 0001750 00000006515 13076461374 016474 0 ustar nicko nicko /*
* Copyright (C) 2010 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
// This file implements QuickFindPattern.
// This class implements part of the Quick Find mechanism, it only stores the
// current search pattern, once it has been confirmed (return pressed),
// it can be asked to return the matches in a specific string.
#include "quickfindpattern.h"
#include "persistentinfo.h"
#include "configuration.h"
QuickFindPattern::QuickFindPattern() : QObject(), regexp_()
{
active_ = false;
}
void QuickFindPattern::changeSearchPattern( const QString& pattern )
{
// Determine the type of regexp depending on the config
QRegExp::PatternSyntax syntax;
switch ( Persistent( "settings" )->quickfindRegexpType() ) {
case Wildcard:
syntax = QRegExp::Wildcard;
break;
case FixedString:
syntax = QRegExp::FixedString;
break;
default:
syntax = QRegExp::RegExp2;
break;
}
regexp_.setPattern( pattern );
regexp_.setPatternSyntax( syntax );
if ( regexp_.isValid() && ( ! regexp_.isEmpty() ) )
active_ = true;
else
active_ = false;
emit patternUpdated();
}
void QuickFindPattern::changeSearchPattern( const QString& pattern, bool ignoreCase )
{
regexp_.setCaseSensitivity(
ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive );
changeSearchPattern( pattern );
}
bool QuickFindPattern::matchLine( const QString& line,
QList& matches ) const
{
matches.clear();
if ( active_ ) {
int pos = 0;
while ( ( pos = regexp_.indexIn( line, pos ) ) != -1 ) {
int length = regexp_.matchedLength();
matches << QuickFindMatch( pos, length );
pos += length;
}
}
return ( matches.count() > 0 );
}
bool QuickFindPattern::isLineMatching( const QString& line, int column ) const
{
int pos = 0;
if ( ! active_ )
return false;
if ( ( pos = regexp_.indexIn( line, column ) ) != -1 ) {
lastMatchStart_ = pos;
lastMatchEnd_ = pos + regexp_.matchedLength() - 1;
return true;
}
else
return false;
}
bool QuickFindPattern::isLineMatchingBackward(
const QString& line, int column ) const
{
int pos = 0;
if ( ! active_ )
return false;
if ( ( pos = regexp_.lastIndexIn( line, column ) ) != -1 ) {
lastMatchStart_ = pos;
lastMatchEnd_ = pos + regexp_.matchedLength() - 1;
return true;
}
else
return false;
}
void QuickFindPattern::getLastMatch( int* start_col, int* end_col ) const
{
*start_col = lastMatchStart_;
*end_col = lastMatchEnd_;
}
glogg-1.1.4/src/menuactiontooltipbehavior.h 0000644 0001750 0001750 00000003251 13076461374 020055 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef MANUACTIONTOOLTIPBEHAVIOR_H
#define MANUACTIONTOOLTIPBEHAVIOR_H
#include
#include
class QAction;
class QMenu;
class QTimerEvent;
// Provides a behavior to show an action's tooltip after mouse is unmoved for
// a specified number of 'ms'. E.g. used for tooltips with full-path for recent
// files in the file menu. Not thread-safe.
class MenuActionToolTipBehavior : public QObject
{
Q_OBJECT;
public:
MenuActionToolTipBehavior(QAction *action, QMenu *parentMenu,
QObject *parent);
// Time in ms that mouse needs to stay unmoved for tooltip to be shown
int toolTipDelay(); /* ms */
void setToolTipDelay(int ms);
private:
void timerEvent(QTimerEvent *event);
void showToolTip(const QPoint &position);
private slots:
void onActionHovered();
private:
QAction *action;
QMenu *parentMenu;
int toolTipDelayMs;
int timerId;
QPoint hoverPoint;
};
#endif
glogg-1.1.4/src/perfcounter.h 0000644 0001750 0001750 00000003540 13076461374 015115 0 ustar nicko nicko /*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include
// This class is a counter that remembers the number of events occuring within one second
// it can be used for performance measurement (e.g. FPS)
class PerfCounter {
public:
PerfCounter() {}
// Count a new event, returns true if it has been counted.
// If the function returns false, it indicates the current second is elapsed
// and the user should read and reset the counter before re-adding the event.
bool addEvent() {
using namespace std::chrono;
if ( counter_ == 0 )
first_event_date_ = steady_clock::now();
if ( duration_cast(
steady_clock::now() - first_event_date_ ).count() < 1000000 ) {
++counter_;
return true;
}
else {
return false;
}
}
// Read and reset the counter. Returns the number of events that occured in the
// previous second.
uint32_t readAndReset() {
uint32_t value = counter_;
counter_ = 0;
return value;
}
private:
uint32_t counter_ = 0;
std::chrono::steady_clock::time_point first_event_date_;
};
glogg-1.1.4/src/dbusexternalcom.h 0000644 0001750 0001750 00000004103 13076461374 015754 0 ustar nicko nicko /*
* Copyright (C) 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef DBUSEXTERNALCOM_H
#define DBUSEXTERNALCOM_H
#include "externalcom.h"
#include
#include
#include
// An implementation of ExternalInstance using D-Bus via Qt
class DBusExternalInstance : public ExternalInstance {
public:
DBusExternalInstance();
~DBusExternalInstance() {}
virtual void loadFile( const QString& file_name ) const;
virtual uint32_t getVersion() const;
private:
std::shared_ptr dbusInterface_;
};
class DBusInterfaceExternalCommunicator : public QObject
{
Q_OBJECT
public:
DBusInterfaceExternalCommunicator() : QObject() {}
~DBusInterfaceExternalCommunicator() {}
public slots:
void loadFile( const QString& file_name );
qint32 version() const;
signals:
void signalLoadFile( const QString& file_name );
};
// An implementation of ExternalCommunicator using D-Bus via Qt
class DBusExternalCommunicator : public ExternalCommunicator
{
Q_OBJECT
public:
// Constructor: initialise the D-Bus connection,
// can throw if D-Bus is not available
DBusExternalCommunicator();
~DBusExternalCommunicator() {}
virtual void startListening();
virtual ExternalInstance* otherInstance() const;
public slots:
qint32 version() const;
private:
std::shared_ptr dbus_iface_object_;
};
#endif
glogg-1.1.4/src/winwatchtowerdriver.h 0000644 0001750 0001750 00000014624 13076461374 016707 0 ustar nicko nicko /*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef WINWATCHTOWERDRIVER_H
#define WINWATCHTOWERDRIVER_H
#include
#include
#include
#include
#include
#include
#define WIN32_LEAN_AND_MEAN
#include
// Utility class
// Encapsulate a directory notification returned by Windows'
// ReadDirectoryChangesW.
class WinNotificationInfo {
public:
enum class Action { UNDEF,
ADDED,
REMOVED,
MODIFIED,
RENAMED_OLD_NAME,
RENAMED_NEW_NAME };
WinNotificationInfo() { action_ = Action::UNDEF; }
WinNotificationInfo( Action action, std::wstring file_name )
{ action_ = action; file_name_ = file_name; }
Action action() const { return action_; }
std::wstring fileName() const { return file_name_; }
private:
Action action_;
std::wstring file_name_;
};
class WinNotificationInfoList {
public:
WinNotificationInfoList( const char* buffer, size_t buffer_size );
// Iterator
class iterator : std::iterator {
public:
iterator( WinNotificationInfoList* list, const char* position )
{ list_ = list; position_ = position; }
const WinNotificationInfo& operator*() const
{ return list_->current_notification_; }
const WinNotificationInfo* operator->() const
{ return &( list_->current_notification_ ); }
const WinNotificationInfo& operator++() {
position_ = list_->advanceToNext();
return list_->current_notification_;
}
WinNotificationInfo operator++( int ) {
WinNotificationInfo tmp { list_->current_notification_ };
operator++();
return tmp;
}
bool operator!=( const iterator& other ) const {
return ( list_ != other.list_ ) || ( position_ != other.position_ );
}
private:
WinNotificationInfoList* list_;
const char* position_;
};
iterator begin() { return iterator( this, pointer_ ); }
iterator end() { return iterator( this, nullptr ); }
private:
const char* advanceToNext();
// Current notification (in the byte stream)
const char* pointer_;
// Next notification (in the byte stream)
const char* next_;
WinNotificationInfo current_notification_;
const char* updateCurrentNotification( const char* new_position );
};
template
class ObservedFile;
template
class ObservedFileList;
class WinWatchTowerDriver {
public:
struct WinWatchedDirRecord {
WinWatchedDirRecord( const std::string& file_name )
: path_( file_name ) { }
static const int READ_DIR_CHANGE_BUFFER_SIZE = 4096;
std::string path_;
void* handle_ = nullptr;
static const unsigned long buffer_length_ = READ_DIR_CHANGE_BUFFER_SIZE;
char buffer_[buffer_length_];
};
class FileId { };
class SymlinkId { };
class DirId {
public:
friend class WinWatchTowerDriver;
DirId() {}
bool operator==( const DirId& other ) const
{ return dir_record_ == other.dir_record_; }
bool valid() const
{ return ( dir_record_ != nullptr ); }
private:
std::shared_ptr dir_record_;
};
// On Windows, the token is the "last write" time
class FileChangeToken {
public:
FileChangeToken() {}
FileChangeToken( const std::string& file_name )
{ readFromFile( file_name ); }
void readFromFile( const std::string& file_name );
bool operator==( const FileChangeToken& other )
{ return ( low_date_time_ == other.low_date_time_ ) &&
( high_date_time_ == other.high_date_time_ ) &&
( low_file_size_ == other.low_file_size_ ) &&
( high_file_size_ == other.high_file_size_); }
bool operator!=( const FileChangeToken& other )
{ return ! operator==( other ); }
private:
DWORD low_date_time_ = 0;
DWORD high_date_time_ = 0;
DWORD low_file_size_ = 0;
DWORD high_file_size_ = 0;
};
// Default constructor
WinWatchTowerDriver();
~WinWatchTowerDriver();
FileId addFile( const std::string& file_name );
SymlinkId addSymlink( const std::string& file_name );
DirId addDir( const std::string& file_name );
void removeFile( const FileId& file_id );
void removeSymlink( const SymlinkId& symlink_id );
void removeDir( const DirId& dir_id );
std::vector*> waitAndProcessEvents(
ObservedFileList* list,
std::unique_lock* lock,
std::vector*>* files_needing_readding,
int timout_ms );
// Interrupt waitAndProcessEvents
void interruptWait();
private:
// An action which will be picked up by the worker thread.
class Action {
public:
Action( std::function function ) : function_ { function } {}
~Action() {}
void operator()() { function_(); }
private:
std::function function_;
};
// Action
std::mutex action_mutex_;
std::condition_variable action_done_cv_;
std::unique_ptr scheduled_action_ = nullptr;
// Win32 notification variables
HANDLE hCompPort_;
OVERLAPPED overlapped_;
unsigned long buffer_length_;
// List of directory records
// Accessed exclusively in the worker thread context
std::vector> dir_records_ { };
// Private member functions
void serialisedAddDir(
const std::string& file_name,
DirId& dir_id );
};
#endif
glogg-1.1.4/src/recentfiles.h 0000644 0001750 0001750 00000003013 13076461374 015057 0 ustar nicko nicko /*
* Copyright (C) 2011 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef RECENTFILES_H
#define RECENTFILES_H
#include
#include
#include "persistable.h"
// Manage the list of recently opened files
class RecentFiles : public Persistable
{
public:
// Creates an empty set of recent files
RecentFiles();
// Adds the passed filename to the list of recently used searches
void addRecent( const QString& text );
// Returns a list of recent files (latest loaded first)
QStringList recentFiles() const;
// Reads/writes the current config in the QSettings object passed
virtual void saveToStorage( QSettings& settings ) const;
virtual void retrieveFromStorage( QSettings& settings );
private:
static const int RECENTFILES_VERSION;
static const int MAX_NUMBER_OF_FILES;
QStringList recentFiles_;
};
#endif
glogg-1.1.4/src/versionchecker.h 0000644 0001750 0001750 00000004546 13076461374 015602 0 ustar nicko nicko /*
* Copyright (C) 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef VERSIONCHECKER_H
#define VERSIONCHECKER_H
#include
#include
#include
#include "persistable.h"
// This class holds the configuration options and persistent
// data for the version checker
class VersionCheckerConfig : public Persistable
{
public:
VersionCheckerConfig();
// Accessors
bool versionCheckingEnabled() const
{ return enabled_; }
void setVersionCheckingEnabled( bool enabled )
{ enabled_ = enabled; }
std::time_t nextDeadline() const
{ return next_deadline_; }
void setNextDeadline( std::time_t deadline )
{ next_deadline_ = deadline; }
// Reads/writes the current config in the QSettings object passed
virtual void saveToStorage( QSettings& settings ) const;
virtual void retrieveFromStorage( QSettings& settings );
private:
bool enabled_;
std::time_t next_deadline_;
};
// This class compares the current version number with the latest
// stored on a central server
class VersionChecker : public QObject
{
Q_OBJECT
public:
VersionChecker();
~VersionChecker();
// Starts an asynchronous check for a newer version if it is needed.
// A newVersionFound signal is sent if one is found.
// In case of error or if no new version is found, no signal is emitted.
void startCheck();
signals:
// New version "version" is available
void newVersionFound( const QString& version );
private slots:
// Called when download is finished
void downloadFinished( QNetworkReply* );
private:
static const char* VERSION_URL;
static const uint64_t CHECK_INTERVAL_S;
QNetworkAccessManager manager_;
};
#endif
glogg-1.1.4/src/persistentinfo.h 0000644 0001750 0001750 00000005304 13076461374 015635 0 ustar nicko nicko /*
* Copyright (C) 2011 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef PERSISTENTINFO_H
#define PERSISTENTINFO_H
#include
#include
#include
class Persistable;
// Singleton class managing the saving of persistent data to permanent storage
// Clients must implement Persistable and register with this object, they can
// then be saved/loaded.
class PersistentInfo {
public:
// Initialise the storage backend for the Persistable, migrating the settings
// if needed. Must be called before any other function.
void migrateAndInit();
// Register a Persistable
void registerPersistable( std::shared_ptr object,
const QString& name );
// Get a Persistable (or NULL if it doesn't exist)
std::shared_ptr getPersistable( const QString& name );
// Save a persistable to its permanent storage
void save( const QString& name );
// Retrieve a persistable from permanent storage
void retrieve( const QString& name );
private:
// Can't be constructed or copied (singleton)
PersistentInfo();
PersistentInfo( const PersistentInfo& );
~PersistentInfo();
// Has migrateAndInit() been called?
bool initialised_;
// List of persistables
QHash> objectList_;
// Qt setting object
QSettings* settings_;
// allow this function to create one instance
friend PersistentInfo& GetPersistentInfo();
};
PersistentInfo& GetPersistentInfo();
// Global function used to get a reference to an object
// from the PersistentInfo store
template
std::shared_ptr Persistent( const char* name )
{
std::shared_ptr p =
GetPersistentInfo().getPersistable( QString( name ) );
return std::dynamic_pointer_cast(p);
}
template
std::shared_ptr PersistentCopy( const char* name )
{
std::shared_ptr p =
GetPersistentInfo().getPersistable( QString( name ) );
return std::make_shared( *( std::dynamic_pointer_cast(p) ) );
}
#endif
glogg-1.1.4/src/main.cpp 0000644 0001750 0001750 00000022005 13076461374 014035 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010, 2011, 2013, 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include
#include
#include
#include
namespace po = boost::program_options;
#include
#include
using namespace std;
#ifdef _WIN32
#include "unistd.h"
#endif
#include "persistentinfo.h"
#include "sessioninfo.h"
#include "configuration.h"
#include "filterset.h"
#include "recentfiles.h"
#include "session.h"
#include "mainwindow.h"
#include "savedsearches.h"
#include "loadingstatus.h"
#include "externalcom.h"
#ifdef GLOGG_SUPPORTS_DBUS
#include "dbusexternalcom.h"
#elif GLOGG_SUPPORTS_SOCKETIPC
#include "socketexternalcom.h"
#endif
#include "log.h"
static void print_version();
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
vector filenames;
// Configuration
bool new_session = false;
bool load_session = false;
bool multi_instance = false;
#ifdef _WIN32
bool log_to_file = false;
#endif
TLogLevel logLevel = logWARNING;
try {
po::options_description desc("Usage: glogg [options] [files]");
desc.add_options()
("help,h", "print out program usage (this message)")
("version,v", "print glogg's version information")
("multi,m", "allow multiple instance of glogg to run simultaneously (use together with -s)")
("load-session,s", "load the previous session (default when no file is passed)")
("new-session,n", "do not load the previous session (default when a file is passed)")
#ifdef _WIN32
("log,l", "save the log to a file (Windows only)")
#endif
("debug,d", "output more debug (include multiple times for more verbosity e.g. -dddd)")
;
po::options_description desc_hidden("Hidden options");
// For -dd, -ddd...
for ( string s = "dd"; s.length() <= 10; s.append("d") )
desc_hidden.add_options()(s.c_str(), "debug");
desc_hidden.add_options()
("input-file", po::value>(), "input file")
;
po::options_description all_options("all options");
all_options.add(desc).add(desc_hidden);
po::positional_options_description positional;
positional.add("input-file", -1);
int command_line_style = (((po::command_line_style::unix_style ^
po::command_line_style::allow_guessing) |
po::command_line_style::allow_long_disguise) ^
po::command_line_style::allow_sticky);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
options(all_options).
positional(positional).
style(command_line_style).run(),
vm);
po::notify(vm);
if ( vm.count("help") ) {
desc.print(cout);
return 0;
}
if ( vm.count("version") ) {
print_version();
return 0;
}
if ( vm.count( "debug" ) )
logLevel = logINFO;
if ( vm.count( "multi" ) )
multi_instance = true;
if ( vm.count( "new-session" ) )
new_session = true;
if ( vm.count( "load-session" ) )
load_session = true;
#ifdef _WIN32
if ( vm.count( "log" ) )
log_to_file = true;
#endif
for ( string s = "dd"; s.length() <= 10; s.append("d") )
if ( vm.count( s ) )
logLevel = (TLogLevel) (logWARNING + s.length());
if ( vm.count("input-file") ) {
filenames = vm["input-file"].as>();
}
}
catch(exception& e) {
cerr << "Option processing error: " << e.what() << endl;
return 1;
}
catch(...) {
cerr << "Exception of unknown type!\n";
}
#ifdef _WIN32
if ( log_to_file )
{
char file_name[255];
snprintf( file_name, sizeof file_name, "glogg_%d.log", getpid() );
FILE* file = fopen(file_name, "w");
Output2FILE::Stream() = file;
}
#endif
FILELog::setReportingLevel( logLevel );
for ( auto& filename: filenames ) {
if ( ! filename.empty() ) {
// Convert to absolute path
QFileInfo file( QString::fromLocal8Bit( filename.c_str() ) );
filename = file.absoluteFilePath().toStdString();
LOG( logDEBUG ) << "Filename: " << filename;
}
}
// External communicator
shared_ptr externalCommunicator = nullptr;
shared_ptr externalInstance = nullptr;
try {
#ifdef GLOGG_SUPPORTS_DBUS
externalCommunicator = make_shared();
externalInstance = shared_ptr(
externalCommunicator->otherInstance() );
#elif GLOGG_SUPPORTS_SOCKETIPC
externalCommunicator = make_shared();
auto ptr = externalCommunicator->otherInstance();
externalInstance = shared_ptr( ptr );
#endif
}
catch(CantCreateExternalErr& e) {
LOG(logWARNING) << "Cannot initialise external communication.";
}
LOG(logDEBUG) << "externalInstance = " << externalInstance;
if ( ( ! multi_instance ) && externalInstance ) {
uint32_t version = externalInstance->getVersion();
LOG(logINFO) << "Found another glogg (version = "
<< std::setbase(16) << version << ")";
for ( const auto& filename: filenames ) {
externalInstance->loadFile( QString::fromStdString( filename ) );
}
return 0;
}
else {
// FIXME: there is a race condition here. One glogg could start
// between the declaration of externalInstance and here,
// is it a problem?
if ( externalCommunicator )
externalCommunicator->startListening();
}
// Register types for Qt
qRegisterMetaType("LoadingStatus");
// Register the configuration items
GetPersistentInfo().migrateAndInit();
GetPersistentInfo().registerPersistable(
std::make_shared(), QString( "session" ) );
GetPersistentInfo().registerPersistable(
std::make_shared(), QString( "settings" ) );
GetPersistentInfo().registerPersistable(
std::make_shared(), QString( "filterSet" ) );
GetPersistentInfo().registerPersistable(
std::make_shared(), QString( "savedSearches" ) );
GetPersistentInfo().registerPersistable(
std::make_shared(), QString( "recentFiles" ) );
#ifdef GLOGG_SUPPORTS_VERSION_CHECKING
GetPersistentInfo().registerPersistable(
std::make_shared(), QString( "versionChecker" ) );
#endif
#ifdef _WIN32
// Allow the app to raise it's own windows (in case an external
// glogg send us a file to open)
AllowSetForegroundWindow(ASFW_ANY);
#endif
// We support high-dpi (aka Retina) displays
app.setAttribute( Qt::AA_UseHighDpiPixmaps );
// No icon in menus
app.setAttribute( Qt::AA_DontShowIconsInMenus );
// FIXME: should be replaced by a two staged init of MainWindow
GetPersistentInfo().retrieve( QString( "settings" ) );
std::unique_ptr session( new Session() );
MainWindow mw( std::move( session ), externalCommunicator );
// Geometry
mw.reloadGeometry();
// Load the existing session if needed
std::shared_ptr config =
Persistent( "settings" );
if ( load_session || ( filenames.empty() && !new_session && config->loadLastSession() ) )
mw.reloadSession();
LOG(logDEBUG) << "MainWindow created.";
mw.show();
for ( const auto& filename: filenames ) {
mw.loadInitialFile( QString::fromStdString( filename ) );
}
mw.startBackgroundTasks();
return app.exec();
}
static void print_version()
{
cout << "glogg " GLOGG_VERSION "\n";
#ifdef GLOGG_COMMIT
cout << "Built " GLOGG_DATE " from " GLOGG_COMMIT "\n";
#endif
cout << "Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicolas Bonnefon and other contributors\n";
cout << "This is free software. You may redistribute copies of it under the terms of\n";
cout << "the GNU General Public License .\n";
cout << "There is NO WARRANTY, to the extent permitted by law.\n";
}
glogg-1.1.4/src/filtersdialog.ui 0000644 0001750 0001750 00000013645 13076461374 015606 0 ustar nicko nicko
FiltersDialog
0
0
520
288
Filters
-
QLayout::SetNoConstraint
-
-
-
-
6
-
New Filter
:/images/plus.png:/images/plus.png
-
Delete Filter
:/images/minus.png:/images/minus.png
-
Qt::Horizontal
40
20
-
Move Filter Up
:/images/up.png:/images/up.png
-
Move Filter Down
:/images/down.png:/images/down.png
-
-
-
Matching Pattern:
-
false
-
Fore Color:
-
false
-
false
-
Back Color:
-
false
Ignore case
false
-
Qt::Horizontal
QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok
glogg-1.1.4/src/quickfindmux.h 0000644 0001750 0001750 00000010776 13076461374 015301 0 ustar nicko nicko /*
* Copyright (C) 2013, 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef QUICKFINDMUX_H
#define QUICKFINDMUX_H
#include
#include
#include
#include
#include "quickfindpattern.h"
// Interface representing a widget searchable in both direction.
class SearchableWidgetInterface {
public:
virtual void searchForward() = 0;
virtual void searchBackward() = 0;
virtual void incrementallySearchForward() = 0;
virtual void incrementallySearchBackward() = 0;
virtual void incrementalSearchStop() = 0;
virtual void incrementalSearchAbort() = 0;
};
// Interface representing the selector. It will be called and asked
// who the search have to be forwarded to.
class QuickFindMuxSelectorInterface {
public:
// Return the searchable widget to use.
SearchableWidgetInterface* getActiveSearchable() const
{ return doGetActiveSearchable(); }
// Return the list of all possible searchables, this
// is done on registration in order to establish
// listeners on all searchables.
std::vector getAllSearchables() const
{ return doGetAllSearchables(); }
protected:
virtual SearchableWidgetInterface* doGetActiveSearchable() const = 0;
virtual std::vector doGetAllSearchables() const = 0;
};
class QFNotification;
// Represents a multiplexer (unique application wise) dispatching the
// Quick Find search from the UI to the relevant view.
// It is also its responsability to determine if an incremental search
// must be performed and to react accordingly.
class QuickFindMux : public QObject
{
Q_OBJECT
public:
enum QFDirection {
Forward,
Backward,
};
// Construct the multiplexer, taking a reference to the pattern
QuickFindMux( std::shared_ptr pattern );
// Register a new selector, which will be called and asked
// who the search have to be forwarded to.
// The selector is called immediately when registering to get the list of
// searchables.
// The previous selector and its associated views are automatically
// deregistered.
// A null selector is accepted, in this case QFM functionalities are
// disabled until a valid selector is registered.
void registerSelector( const QuickFindMuxSelectorInterface* selector );
// Set the direction that will be used by the search when searching
// forward.
void setDirection( QFDirection direction );
signals:
void patternChanged( const QString& );
void notify( const QFNotification& );
void clearNotification();
public slots:
// Signal the current pattern must be altered (will start an incremental
// search if the options are configured in such a way).
void setNewPattern( const QString& new_pattern, bool ignore_case );
// Signal the current pattern must be altered and is confirmed
// (will stop an incremental search if needed)
void confirmPattern( const QString& new_pattern, bool ignore_case );
// Signal the user cancelled the search
// (used for incremental only)
void cancelSearch();
// Starts a search in the specified direction
void searchNext();
void searchPrevious();
// Idem but ignore the direction and always search in the
// specified direction
void searchForward();
void searchBackward();
private slots:
void changeQuickFind( const QString& new_pattern,
QuickFindMux::QFDirection new_direction );
void notifyPatternChanged();
private:
const QuickFindMuxSelectorInterface* selector_;
// The (application wide) quick find pattern
std::shared_ptr pattern_;
QFDirection currentDirection_;
std::vector registeredSearchables_;
SearchableWidgetInterface* getSearchableWidget() const;
void registerSearchable( QObject* searchable );
void unregisterAllSearchables();
};
#endif
glogg-1.1.4/src/tabbedcrawlerwidget.h 0000644 0001750 0001750 00000003400 13076461374 016561 0 ustar nicko nicko /*
* Copyright (C) 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef TABBEDCRAWLERWIDGET_H
#define TABBEDCRAWLERWIDGET_H
#include
#include
#include "loadingstatus.h"
// This class represents glogg's main widget, a tabbed
// group of CrawlerWidgets.
// This is a very slightly customised QTabWidget, with
// a particular style.
class TabbedCrawlerWidget : public QTabWidget
{
Q_OBJECT
public:
TabbedCrawlerWidget();
virtual ~TabbedCrawlerWidget() {}
// "Overridden" addTab/removeTab that automatically
// show/hide the tab bar
// The tab is created with the 'old data' icon.
int addTab( QWidget* page, const QString& label );
void removeTab( int index );
// Set the data status (icon) for the tab number 'index'
void setTabDataStatus( int index, DataStatus status );
protected:
void keyPressEvent( QKeyEvent* event );
void mouseReleaseEvent( QMouseEvent *event);
private:
const QIcon olddata_icon_;
const QIcon newdata_icon_;
const QIcon newfiltered_icon_;
QTabBar myTabBar_;
};
#endif
glogg-1.1.4/src/filewatcher.h 0000644 0001750 0001750 00000003116 13076461374 015055 0 ustar nicko nicko /*
* Copyright (C) 2010, 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef FILEWATCHER_H
#define FILEWATCHER_H
#include
// This abstract class defines a way to watch a group of (potentially
// absent) files for update.
class FileWatcher : public QObject {
Q_OBJECT
public:
// Create an empty object
FileWatcher() {}
// Destroy the object
virtual ~FileWatcher() {}
// Adds the file to the list of file to watch
// (do nothing if a file is already monitored)
virtual void addFile( const QString& fileName ) = 0;
// Removes the file to the list of file to watch
// (do nothing if said file is not monitored)
virtual void removeFile( const QString& fileName ) = 0;
// Set the polling interval (0 means disabled)
virtual void setPollingInterval( uint32_t ) {}
signals:
// Sent when the file on disk has changed in any way.
void fileChanged( const QString& );
};
#endif
glogg-1.1.4/src/watchtower.h 0000644 0001750 0001750 00000025236 13076461374 014756 0 ustar nicko nicko /*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef WATCHTOWER_H
#define WATCHTOWER_H
#include "config.h"
#include
#include
#include
#include
#include "watchtowerlist.h"
// Allow the client to register for notification on an arbitrary number
// of files. It will be notified to any change (creation/deletion/modification)
// on those files.
// It is passed a platform specific driver.
// FIXME: Where to put that so it is not dependant
// Registration object to implement RAII
#ifdef HAS_TEMPLATE_ALIASES
using Registration = std::shared_ptr;
#else
typedef std::shared_ptr Registration;
#endif
template
class WatchTower {
public:
// Create an empty watchtower
WatchTower();
// Destroy the object
~WatchTower();
// Set the polling interval (in ms)
// 0 disables polling and is the default
void setPollingInterval( int interval_ms );
// Add a file to the notification list. notification will be called when
// the file is modified, moved or deleted.
// Lifetime of the notification is tied to the Registration object returned.
// Note the notification function is called with a mutex held and in a
// third party thread, beware of races!
Registration addFile( const std::string& file_name,
std::function notification );
// Number of watched directories (for tests)
unsigned int numberWatchedDirectories() const;
private:
// The driver (parametrised)
Driver driver_;
// List of files/dirs observed
ObservedFileList observed_file_list_;
// Protects the observed_file_list_
std::mutex observers_mutex_;
// Polling interval (0 disables polling)
uint32_t polling_interval_ms_ = 0;
// Thread
std::atomic_bool running_;
std::thread thread_;
// Exist as long as the onject exists, to ensure observers won't try to
// call us if we are dead.
std::shared_ptr heartBeat_;
// Private member functions
std::tuple
addFileToDriver( const std::string& );
static void removeNotification( WatchTower* watch_tower,
std::shared_ptr notification );
void run();
};
// Class template implementation
#include
#include
#include
#include
#include "log.h"
namespace {
bool isSymLink( const std::string& file_name );
std::string directory_path( const std::string& path );
};
template
WatchTower::WatchTower()
: driver_(), thread_(),
heartBeat_(std::shared_ptr((void*) 0xDEADC0DE, [] (void*) {}))
{
running_ = true;
thread_ = std::thread( &WatchTower::run, this );
}
template
WatchTower::~WatchTower()
{
running_ = false;
driver_.interruptWait();
thread_.join();
}
template
void WatchTower::setPollingInterval( int interval_ms )
{
if ( polling_interval_ms_ != interval_ms ) {
polling_interval_ms_ = interval_ms;
// Break out of the wait
if ( polling_interval_ms_ > 0 )
driver_.interruptWait();
}
}
template
Registration WatchTower::addFile(
const std::string& file_name,
std::function notification )
{
// LOG(logDEBUG) << "WatchTower::addFile " << file_name;
std::weak_ptr weakHeartBeat(heartBeat_);
std::lock_guard lock( observers_mutex_ );
auto existing_observed_file =
observed_file_list_.searchByName( file_name );
std::shared_ptr> ptr( new std::function(std::move( notification )) );
if ( ! existing_observed_file )
{
typename Driver::FileId file_id;
typename Driver::SymlinkId symlink_id;
std::tie( file_id, symlink_id ) = addFileToDriver( file_name );
auto new_file = observed_file_list_.addNewObservedFile(
ObservedFile( file_name, ptr, file_id, symlink_id ) );
auto dir = observed_file_list_.watchedDirectoryForFile( file_name );
if ( ! dir ) {
LOG(logDEBUG) << "WatchTower::addFile dir for " << file_name
<< " not watched, adding...";
dir = observed_file_list_.addWatchedDirectoryForFile( file_name,
[this, weakHeartBeat] (ObservedDir* dir) {
if ( auto heart_beat = weakHeartBeat.lock() ) {
driver_.removeDir( dir->dir_id_ );
} } );
dir->dir_id_ = driver_.addDir( dir->path );
if ( ! dir->dir_id_.valid() ) {
LOG(logWARNING) << "WatchTower::addFile driver failed to add dir";
dir = nullptr;
}
}
else {
LOG(logDEBUG) << "WatchTower::addFile Found exisiting watch for dir " << file_name;
}
// Associate the dir to the file
if ( dir )
new_file->dir_ = dir;
}
else
{
existing_observed_file->addCallback( ptr );
}
// Returns a shared pointer that removes its own entry
// from the list of watched stuff when it goes out of scope!
// Uses a custom deleter to do the work.
return std::shared_ptr( 0x0, [this, ptr, weakHeartBeat] (void*) {
if ( auto heart_beat = weakHeartBeat.lock() )
WatchTower::removeNotification( this, ptr );
} );
}
template
unsigned int WatchTower::numberWatchedDirectories() const
{
return observed_file_list_.numberWatchedDirectories();
}
//
// Private functions
//
// Add the passed file name to the driver, returning the file and symlink id
template
std::tuple
WatchTower::addFileToDriver( const std::string& file_name )
{
typename Driver::SymlinkId symlink_id;
auto file_id = driver_.addFile( file_name );
if ( isSymLink( file_name ) )
{
// We want to follow the name (as opposed to the inode)
// so we watch the symlink as well.
symlink_id = driver_.addSymlink( file_name );
}
return std::make_tuple( file_id, symlink_id );
}
// Called by the dtor for a registration object
template
void WatchTower::removeNotification(
WatchTower* watch_tower, std::shared_ptr notification )
{
LOG(logDEBUG) << "WatchTower::removeNotification";
std::lock_guard lock( watch_tower->observers_mutex_ );
auto file =
watch_tower->observed_file_list_.removeCallback( notification );
if ( file )
{
watch_tower->driver_.removeFile( file->file_id_ );
watch_tower->driver_.removeSymlink( file->symlink_id_ );
}
}
// Run in its own thread
template
void WatchTower::run()
{
while ( running_ ) {
std::unique_lock lock( observers_mutex_ );
std::vector*> files_needing_readding;
auto files = driver_.waitAndProcessEvents(
&observed_file_list_, &lock, &files_needing_readding, polling_interval_ms_ );
LOG(logDEBUG) << "WatchTower::run: waitAndProcessEvents returned "
<< files.size() << " files, " << files_needing_readding.size()
<< " needing re-adding";
for ( auto file: files_needing_readding ) {
// A file 'needing readding' has the same name,
// but probably a different inode, so it needs
// to be readded for some drivers that rely on the
// inode (e.g. inotify)
driver_.removeFile( file->file_id_ );
driver_.removeSymlink( file->symlink_id_ );
std::tie( file->file_id_, file->symlink_id_ ) =
addFileToDriver( file->file_name_ );
}
for ( auto file: files ) {
for ( auto observer: file->callbacks ) {
LOG(logDEBUG) << "WatchTower::run: notifying the client!";
// Here we have to cast our generic pointer back to
// the function pointer in order to perform the call
const std::shared_ptr> fptr =
std::static_pointer_cast>( observer );
// The observer is called with the mutex held,
// Let's hope it doesn't do anything too funky.
(*fptr)();
file->markAsChanged();
}
}
if ( polling_interval_ms_ > 0 ) {
// Also call files that have not been called for a while
for ( auto file: observed_file_list_ ) {
uint32_t ms_since_last_check =
std::chrono::duration_cast(
std::chrono::steady_clock::now() - file->timeForLastCheck() ).count();
if ( ( ms_since_last_check > polling_interval_ms_ ) && file->hasChanged() ) {
LOG(logDEBUG) << "WatchTower::run: " << file->file_name_;
for ( auto observer: file->callbacks ) {
LOG(logDEBUG) << "WatchTower::run: notifying the client because of a timeout!";
// Here we have to cast our generic pointer back to
// the function pointer in order to perform the call
const std::shared_ptr> fptr =
std::static_pointer_cast>( observer );
// The observer is called with the mutex held,
// Let's hope it doesn't do anything too funky.
(*fptr)();
}
file->markAsChanged();
}
}
}
}
}
namespace {
bool isSymLink( const std::string& file_name )
{
#ifdef HAVE_SYMLINK
struct stat buf;
lstat( file_name.c_str(), &buf );
return ( S_ISLNK(buf.st_mode) );
#else
return false;
#endif
}
};
#endif
glogg-1.1.4/src/quickfind.cpp 0000644 0001750 0001750 00000026227 13076461374 015100 0 ustar nicko nicko /*
* Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
// This file implements QuickFind.
// This class implements the Quick Find mechanism using references
// to the logData, the QFP and the selection passed.
// Search is started just after the selection and the selection is updated
// if a match is found.
#include
#include "log.h"
#include "quickfindpattern.h"
#include "selection.h"
#include "data/abstractlogdata.h"
#include "quickfind.h"
void SearchingNotifier::reset()
{
dotToDisplay_ = 0;
startTime_ = QTime::currentTime();
}
void SearchingNotifier::sendNotification( qint64 current_line, qint64 nb_lines )
{
LOG( logDEBUG ) << "Emitting Searching....";
qint64 progress;
if ( current_line < 0 )
progress = ( nb_lines + current_line ) * 100 / nb_lines;
else
progress = current_line * 100 / nb_lines;
emit notify( QFNotificationProgress( progress ) );
QApplication::processEvents( QEventLoop::ExcludeUserInputEvents );
startTime_ = QTime::currentTime().addMSecs( -800 );
}
void QuickFind::LastMatchPosition::set( int line, int column )
{
if ( ( line_ == -1 ) ||
( ( line <= line_ ) && ( column < column_ ) ) )
{
line_ = line;
column_ = column;
}
}
void QuickFind::LastMatchPosition::set( const FilePosition &position )
{
set( position.line(), position.column() );
}
bool QuickFind::LastMatchPosition::isLater( int line, int column ) const
{
if ( line_ == -1 )
return false;
else if ( ( line == line_ ) && ( column >= column_ ) )
return true;
else if ( line > line_ )
return true;
else
return false;
}
bool QuickFind::LastMatchPosition::isLater( const FilePosition &position ) const
{
return isLater( position.line(), position.column() );
}
bool QuickFind::LastMatchPosition::isSooner( int line, int column ) const
{
if ( line_ == -1 )
return false;
else if ( ( line == line_ ) && ( column <= column_ ) )
return true;
else if ( line < line_ )
return true;
else
return false;
}
bool QuickFind::LastMatchPosition::isSooner( const FilePosition &position ) const
{
return isSooner( position.line(), position.column() );
}
QuickFind::QuickFind( const AbstractLogData* const logData,
Selection* selection,
const QuickFindPattern* const quickFindPattern ) :
logData_( logData ), selection_( selection ),
quickFindPattern_( quickFindPattern ),
lastMatch_(), firstMatch_(), searchingNotifier_(),
incrementalSearchStatus_()
{
connect( &searchingNotifier_, SIGNAL( notify( const QFNotification& ) ),
this, SIGNAL( notify( const QFNotification& ) ) );
}
void QuickFind::incrementalSearchStop()
{
if ( incrementalSearchStatus_.isOngoing() ) {
if ( selection_->isEmpty() ) {
// Nothing found?
// We reset the selection to what it was
*selection_ = incrementalSearchStatus_.initialSelection();
}
incrementalSearchStatus_ = IncrementalSearchStatus();
}
}
void QuickFind::incrementalSearchAbort()
{
if ( incrementalSearchStatus_.isOngoing() ) {
// We reset the selection to what it was
*selection_ = incrementalSearchStatus_.initialSelection();
incrementalSearchStatus_ = IncrementalSearchStatus();
}
}
qint64 QuickFind::incrementallySearchForward()
{
LOG( logDEBUG ) << "QuickFind::incrementallySearchForward";
// Position where we start the search from
FilePosition start_position = selection_->getNextPosition();
if ( incrementalSearchStatus_.direction() == Forward ) {
// An incremental search is active, we restart the search
// from the initial point
LOG( logDEBUG ) << "Restart search from initial point";
start_position = incrementalSearchStatus_.position();
}
else {
// It's a new search so we search from the selection
incrementalSearchStatus_ = IncrementalSearchStatus(
Forward,
start_position,
*selection_ );
}
qint64 line_found = doSearchForward( start_position );
if ( line_found >= 0 ) {
// We have found a result...
// ... the caller will jump to this line.
return line_found;
}
else {
// No result...
// ... we want the client to show the initial line.
selection_->clear();
return incrementalSearchStatus_.position().line();
}
}
qint64 QuickFind::incrementallySearchBackward()
{
LOG( logDEBUG ) << "QuickFind::incrementallySearchBackward";
// Position where we start the search from
FilePosition start_position = selection_->getPreviousPosition();
if ( incrementalSearchStatus_.direction() == Backward ) {
// An incremental search is active, we restart the search
// from the initial point
LOG( logDEBUG ) << "Restart search from initial point";
start_position = incrementalSearchStatus_.position();
}
else {
// It's a new search so we search from the selection
incrementalSearchStatus_ = IncrementalSearchStatus(
Backward,
start_position,
*selection_ );
}
qint64 line_found = doSearchBackward( start_position );
if ( line_found >= 0 ) {
// We have found a result...
// ... the caller will jump to this line.
return line_found;
}
else {
// No result...
// ... we want the client to show the initial line.
selection_->clear();
return incrementalSearchStatus_.position().line();
}
}
qint64 QuickFind::searchForward()
{
incrementalSearchStatus_ = IncrementalSearchStatus();
// Position where we start the search from
FilePosition start_position = selection_->getNextPosition();
return doSearchForward( start_position );
}
qint64 QuickFind::searchBackward()
{
incrementalSearchStatus_ = IncrementalSearchStatus();
// Position where we start the search from
FilePosition start_position = selection_->getPreviousPosition();
return doSearchBackward( start_position );
}
// Internal implementation of forward search,
// returns the line where the pattern is found or -1 if not found.
// Parameters are the position the search shall start
qint64 QuickFind::doSearchForward( const FilePosition &start_position )
{
bool found = false;
int found_start_col;
int found_end_col;
if ( ! quickFindPattern_->isActive() )
return -1;
// Optimisation: if we are already after the last match,
// we don't do any search at all.
if ( lastMatch_.isLater( start_position ) ) {
// Send a notification
emit notify( QFNotificationReachedEndOfFile() );
return -1;
}
qint64 line = start_position.line();
LOG( logDEBUG ) << "Start searching at line " << line;
// We look at the rest of the first line
if ( quickFindPattern_->isLineMatching(
logData_->getExpandedLineString( line ),
start_position.column() ) ) {
quickFindPattern_->getLastMatch( &found_start_col, &found_end_col );
found = true;
}
else {
searchingNotifier_.reset();
// And then the rest of the file
qint64 nb_lines = logData_->getNbLine();
line++;
while ( line < nb_lines ) {
if ( quickFindPattern_->isLineMatching(
logData_->getExpandedLineString( line ) ) ) {
quickFindPattern_->getLastMatch(
&found_start_col, &found_end_col );
found = true;
break;
}
line++;
// See if we need to notify of the ongoing search
searchingNotifier_.ping( line, nb_lines );
}
}
if ( found ) {
selection_->selectPortion(
line, found_start_col, found_end_col );
// Clear any notification
emit clearNotification();
return line;
}
else {
// Update the position of the last match
FilePosition last_match_position = selection_->getPreviousPosition();
lastMatch_.set( last_match_position );
// Send a notification
emit notify( QFNotificationReachedEndOfFile() );
return -1;
}
}
// Internal implementation of backward search,
// returns the line where the pattern is found or -1 if not found.
// Parameters are the position the search shall start
qint64 QuickFind::doSearchBackward( const FilePosition &start_position )
{
bool found = false;
int start_col;
int end_col;
if ( ! quickFindPattern_->isActive() )
return -1;
// Optimisation: if we are already before the first match,
// we don't do any search at all.
if ( firstMatch_.isSooner( start_position ) ) {
// Send a notification
emit notify( QFNotificationReachedBegininningOfFile() );
return -1;
}
qint64 line = start_position.line();
LOG( logDEBUG ) << "Start searching at line " << line;
// We look at the beginning of the first line
if ( ( start_position.column() > 0 )
&& ( quickFindPattern_->isLineMatchingBackward(
logData_->getExpandedLineString( line ),
start_position.column() ) )
) {
quickFindPattern_->getLastMatch( &start_col, &end_col );
found = true;
}
else {
searchingNotifier_.reset();
// And then the rest of the file
qint64 nb_lines = logData_->getNbLine();
line--;
while ( line >= 0 ) {
if ( quickFindPattern_->isLineMatchingBackward(
logData_->getExpandedLineString( line ) ) ) {
quickFindPattern_->getLastMatch( &start_col, &end_col );
found = true;
break;
}
line--;
// See if we need to notify of the ongoing search
searchingNotifier_.ping( -line, nb_lines );
}
}
if ( found )
{
selection_->selectPortion( line, start_col, end_col );
// Clear any notification
emit clearNotification();
return line;
}
else {
// Update the position of the first match
FilePosition first_match_position = selection_->getNextPosition();
firstMatch_.set( first_match_position );
// Send a notification
LOG( logDEBUG ) << "QF: Send BOF notification.";
emit notify( QFNotificationReachedBegininningOfFile() );
return -1;
}
}
void QuickFind::resetLimits()
{
lastMatch_.reset();
firstMatch_.reset();
}
glogg-1.1.4/src/viewtools.h 0000644 0001750 0001750 00000004246 13076461374 014620 0 ustar nicko nicko /*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef VIEWTOOLS_H
#define VIEWTOOLS_H
#include
#include
// This class is a controller for an elastic hook manipulated with
// the mouse wheel or touchpad.
// It is used for the "follow" line at the end of the file.
class ElasticHook : public QObject {
Q_OBJECT
public:
ElasticHook( int hook_threshold ) : hook_threshold_( hook_threshold ) {}
// Instruct the elastic to move by the passed pixels
// (a positive value increase the elastic tension)
void move( int value );
// Hold the elastic and prevent automatic decrease.
void hold() { held_ = true; }
// Release the elastic.
void release() { held_ = false; }
// Programmatically force the hook hooked or not.
void hook( bool hooked )
{ hooked_ = hooked; }
// Return the "length" of the elastic hook.
int length() const { return position_; }
bool isHooked() const { return hooked_; }
protected:
void timerEvent( QTimerEvent *event );
signals:
// Sent when the length has changed
void lengthChanged();
// Sent when the hooked status has changed
void hooked( bool is_hooked );
private:
void decreasePosition();
static constexpr int TIMER_PERIOD_MS = 10;
static constexpr int DECREASE_RATE = 4;
const int hook_threshold_;
bool hooked_ = false;
bool held_ = false;
int position_ = 0;
int timer_id_ = 0;
std::chrono::time_point last_update_;
};
#endif
glogg-1.1.4/src/dbuscontrol.h 0000644 0001750 0001750 00000000231 13076461374 015111 0 ustar nicko nicko #include
#include
class DBusControl : public QObject {
Q_OBJECT
public slots:
QString version(void) { return "1.0.0"; }
};
glogg-1.1.4/src/configuration.cpp 0000644 0001750 0001750 00000012067 13076461374 015767 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010, 2013, 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include
#include "log.h"
#include "configuration.h"
Configuration::Configuration()
{
// Should have some sensible default values.
mainFont_ = QFont("monaco", 10);
mainFont_.setStyleHint( QFont::Courier, QFont::PreferOutline );
mainRegexpType_ = ExtendedRegexp;
quickfindRegexpType_ = FixedString;
quickfindIncremental_ = true;
#ifdef GLOGG_SUPPORTS_POLLING
pollingEnabled_ = true;
#else
pollingEnabled_ = false;
#endif
pollIntervalMs_ = 2000;
loadLastSession_ = true;
overviewVisible_ = true;
lineNumbersVisibleInMain_ = false;
lineNumbersVisibleInFiltered_ = true;
QFontInfo fi(mainFont_);
LOG(logDEBUG) << "Default font is " << fi.family().toStdString();
searchAutoRefresh_ = false;
searchIgnoreCase_ = false;
}
// Accessor functions
QFont Configuration::mainFont() const
{
return mainFont_;
}
void Configuration::setMainFont( QFont newFont )
{
LOG(logDEBUG) << "Configuration::setMainFont";
mainFont_ = newFont;
}
void Configuration::retrieveFromStorage( QSettings& settings )
{
LOG(logDEBUG) << "Configuration::retrieveFromStorage";
// Fonts
QString family = settings.value( "mainFont.family" ).toString();
int size = settings.value( "mainFont.size" ).toInt();
// If no config read, keep the default
if ( !family.isNull() )
mainFont_ = QFont( family, size );
// Regexp types
mainRegexpType_ = static_cast(
settings.value( "regexpType.main", mainRegexpType_ ).toInt() );
quickfindRegexpType_ = static_cast(
settings.value( "regexpType.quickfind", quickfindRegexpType_ ).toInt() );
if ( settings.contains( "quickfind.incremental" ) )
quickfindIncremental_ = settings.value( "quickfind.incremental" ).toBool();
// "Advanced" settings
if ( settings.contains( "polling.enabled" ) )
pollingEnabled_ = settings.value( "polling.enabled" ).toBool();
if ( settings.contains( "polling.intervalMs" ) )
pollIntervalMs_ = settings.value( "polling.intervalMs" ).toInt();
if ( settings.contains( "session.loadLast" ) )
loadLastSession_ = settings.value( "session.loadLast" ).toBool();
// View settings
if ( settings.contains( "view.overviewVisible" ) )
overviewVisible_ = settings.value( "view.overviewVisible" ).toBool();
if ( settings.contains( "view.lineNumbersVisibleInMain" ) )
lineNumbersVisibleInMain_ =
settings.value( "view.lineNumbersVisibleInMain" ).toBool();
if ( settings.contains( "view.lineNumbersVisibleInFiltered" ) )
lineNumbersVisibleInFiltered_ =
settings.value( "view.lineNumbersVisibleInFiltered" ).toBool();
// Some sanity check (mainly for people upgrading)
if ( quickfindIncremental_ )
quickfindRegexpType_ = FixedString;
// Default crawler settings
if ( settings.contains( "defaultView.searchAutoRefresh" ) )
searchAutoRefresh_ = settings.value( "defaultView.searchAutoRefresh" ).toBool();
if ( settings.contains( "defaultView.searchIgnoreCase" ) )
searchIgnoreCase_ = settings.value( "defaultView.searchIgnoreCase" ).toBool();
}
void Configuration::saveToStorage( QSettings& settings ) const
{
LOG(logDEBUG) << "Configuration::saveToStorage";
QFontInfo fi(mainFont_);
settings.setValue( "mainFont.family", fi.family() );
settings.setValue( "mainFont.size", fi.pointSize() );
settings.setValue( "regexpType.main", static_cast( mainRegexpType_ ) );
settings.setValue( "regexpType.quickfind", static_cast( quickfindRegexpType_ ) );
settings.setValue( "quickfind.incremental", quickfindIncremental_ );
settings.setValue( "polling.enabled", pollingEnabled_ );
settings.setValue( "polling.intervalMs", pollIntervalMs_ );
settings.setValue( "session.loadLast", loadLastSession_);
settings.setValue( "view.overviewVisible", overviewVisible_ );
settings.setValue( "view.lineNumbersVisibleInMain", lineNumbersVisibleInMain_ );
settings.setValue( "view.lineNumbersVisibleInFiltered", lineNumbersVisibleInFiltered_ );
settings.setValue( "defaultView.searchAutoRefresh", searchAutoRefresh_ );
settings.setValue( "defaultView.searchIgnoreCase", searchIgnoreCase_ );
}
glogg-1.1.4/src/quickfindpattern.h 0000644 0001750 0001750 00000005577 13076461374 016150 0 ustar nicko nicko /*
* Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef QUICKFINDPATTERN_H
#define QUICKFINDPATTERN_H
#include
#include
#include
#include
// Represents a match result for QuickFind
class QuickFindMatch
{
public:
// Construct a match (must be initialised)
QuickFindMatch( int start_column, int length )
{ startColumn_ = start_column; length_ = length; }
// Accessor functions
int startColumn() const { return startColumn_; }
int length() const { return length_; }
private:
int startColumn_;
int length_;
};
// Represents a search pattern for QuickFind (without its results)
class QuickFindPattern : public QObject
{
Q_OBJECT
public:
// Construct an empty search
QuickFindPattern();
// Set the search to a new pattern, using the current
// case status
void changeSearchPattern( const QString& pattern );
// Set the search to a new pattern, as well as the case status
void changeSearchPattern( const QString& pattern, bool ignoreCase );
// Returns whether the search is active (i.e. valid and non empty regexp)
bool isActive() const { return active_; }
// Return the text of the regex
QString getPattern() const { return regexp_.pattern(); }
// Returns whether the passed line match the quick find search.
// If so, it populate the passed list with the list of matches
// within this particular line.
bool matchLine( const QString& line,
QList& matches ) const;
// Returns whether there is a match in the passed line, starting at
// the passed column.
// Results are stored internally.
bool isLineMatching( const QString& line, int column = 0 ) const;
// Same as isLineMatching but search backward
bool isLineMatchingBackward( const QString& line, int column = -1 ) const;
// Must be called when isLineMatching returns 'true', returns
// the position of the first match found.
void getLastMatch( int* start_col, int* end_col ) const;
signals:
// Sent when the pattern is changed
void patternUpdated();
private:
bool active_;
QRegExp regexp_;
mutable int lastMatchStart_;
mutable int lastMatchEnd_;
};
#endif
glogg-1.1.4/src/encodingspeculator.cpp 0000644 0001750 0001750 00000006630 13076461374 017007 0 ustar nicko nicko /*
* Copyright (C) 2016 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include "encodingspeculator.h"
#include
void EncodingSpeculator::inject_byte( uint8_t byte )
{
if ( ! ( byte & 0x80 ) ) {
// 7-bit character, all fine
}
else {
switch ( state_ ) {
case State::ASCIIOnly:
case State::ValidUTF8:
if ( ( byte & 0xE0 ) == 0xC0 ) {
state_ = State::UTF8LeadingByteSeen;
code_point_ = ( byte & 0x1F ) << 6;
continuation_left_ = 1;
min_value_ = 0x80;
// std::cout << "Lead: cp= " << std::hex << code_point_ << std::endl;
}
else if ( ( byte & 0xF0 ) == 0xE0 ) {
state_ = State::UTF8LeadingByteSeen;
code_point_ = ( byte & 0x0F ) << 12;
continuation_left_ = 2;
min_value_ = 0x800;
// std::cout << "Lead 3: cp= " << std::hex << code_point_ << std::endl;
}
else if ( ( byte & 0xF8 ) == 0xF0 ) {
state_ = State::UTF8LeadingByteSeen;
code_point_ = ( byte & 0x07 ) << 18;
continuation_left_ = 3;
min_value_ = 0x800;
// std::cout << "Lead 4: cp= " << std::hex << code_point_ << std::endl;
}
else {
state_ = State::Unknown8Bit;
}
break;
case State::UTF8LeadingByteSeen:
if ( ( byte & 0xC0 ) == 0x80 ) {
--continuation_left_;
code_point_ |= ( byte & 0x3F ) << (continuation_left_ * 6);
// std::cout << "Cont: cp= " << std::hex << code_point_ << std::endl;
if ( continuation_left_ == 0 ) {
if ( code_point_ >= min_value_ )
state_ = State::ValidUTF8;
else
state_ = State::Unknown8Bit;
}
}
else {
state_ = State::Unknown8Bit;
}
break;
}
// state_ = State::Unknown8Bit;
}
}
EncodingSpeculator::Encoding EncodingSpeculator::guess() const
{
Encoding guess;
switch ( state_ ) {
case State::ASCIIOnly:
guess = Encoding::ASCII7;
break;
case State::Unknown8Bit:
case State::UTF8LeadingByteSeen:
guess = Encoding::ASCII8;
break;
case State::ValidUTF8:
guess = Encoding::UTF8;
break;
default:
guess = Encoding::ASCII8;
}
return guess;
}
glogg-1.1.4/src/abstractlogview.cpp 0000644 0001750 0001750 00000156660 13076461374 016330 0 ustar nicko nicko /*
* Copyright (C) 2009, 2010, 2011, 2012, 2013, 2015 Nicolas Bonnefon
* and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
// This file implements the AbstractLogView base class.
// Most of the actual drawing and event management common to the two views
// is implemented in this class. The class only calls protected virtual
// functions when view specific behaviour is desired, using the template
// pattern.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "log.h"
#include "persistentinfo.h"
#include "filterset.h"
#include "logmainview.h"
#include "quickfind.h"
#include "quickfindpattern.h"
#include "overview.h"
#include "configuration.h"
namespace {
int mapPullToFollowLength( int length );
};
namespace {
int countDigits( quint64 n )
{
if (n == 0)
return 1;
// We must force the compiler to not store intermediate results
// in registers because this causes incorrect result on some
// systems under optimizations level >0. For the skeptical:
//
// #include
// #include
// int main(int argc, char **argv) {
// (void)argc;
// long long int n = atoll(argv[1]);
// return floor( log( n ) / log( 10 ) + 1 );
// }
//
// This is on Thinkpad T60 (Genuine Intel(R) CPU T2300).
// $ g++ --version
// g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
// $ g++ -O0 -Wall -W -o math math.cpp -lm; ./math 10; echo $?
// 2
// $ g++ -O1 -Wall -W -o math math.cpp -lm; ./math 10; echo $?
// 1
//
// A fix is to (1) explicitly place intermediate results in
// variables *and* (2) [A] mark them as 'volatile', or [B] pass
// -ffloat-store to g++ (note that approach [A] is more portable).
volatile qreal ln_n = qLn( n );
volatile qreal ln_10 = qLn( 10 );
volatile qreal lg_n = ln_n / ln_10;
volatile qreal lg_n_1 = lg_n + 1;
volatile qreal fl_lg_n_1 = qFloor( lg_n_1 );
return fl_lg_n_1;
}
} // anon namespace
LineChunk::LineChunk( int first_col, int last_col, ChunkType type )
{
// LOG(logDEBUG) << "new LineChunk: " << first_col << " " << last_col;
start_ = first_col;
end_ = last_col;
type_ = type;
}
QList LineChunk::select( int sel_start, int sel_end ) const
{
QList list;
if ( ( sel_start < start_ ) && ( sel_end < start_ ) ) {
// Selection BEFORE this chunk: no change
list << LineChunk( *this );
}
else if ( sel_start > end_ ) {
// Selection AFTER this chunk: no change
list << LineChunk( *this );
}
else /* if ( ( sel_start >= start_ ) && ( sel_end <= end_ ) ) */
{
// We only want to consider what's inside THIS chunk
sel_start = qMax( sel_start, start_ );
sel_end = qMin( sel_end, end_ );
if ( sel_start > start_ )
list << LineChunk( start_, sel_start - 1, type_ );
list << LineChunk( sel_start, sel_end, Selected );
if ( sel_end < end_ )
list << LineChunk( sel_end + 1, end_, type_ );
}
return list;
}
inline void LineDrawer::addChunk( int first_col, int last_col,
QColor fore, QColor back )
{
if ( first_col < 0 )
first_col = 0;
int length = last_col - first_col + 1;
if ( length > 0 ) {
list << Chunk ( first_col, length, fore, back );
}
}
inline void LineDrawer::addChunk( const LineChunk& chunk,
QColor fore, QColor back )
{
int first_col = chunk.start();
int last_col = chunk.end();
addChunk( first_col, last_col, fore, back );
}
inline void LineDrawer::draw( QPainter& painter,
int initialXPos, int initialYPos,
int line_width, const QString& line,
int leftExtraBackgroundPx )
{
QFontMetrics fm = painter.fontMetrics();
const int fontHeight = fm.height();
const int fontAscent = fm.ascent();
// For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the
// following give the right result, not sure why:
const int fontWidth = fm.width( QChar('a') );
int xPos = initialXPos;
int yPos = initialYPos;
foreach ( Chunk chunk, list ) {
// Draw each chunk
// LOG(logDEBUG) << "Chunk: " << chunk.start() << " " << chunk.length();
QString cutline = line.mid( chunk.start(), chunk.length() );
const int chunk_width = cutline.length() * fontWidth;
if ( xPos == initialXPos ) {
// First chunk, we extend the left background a bit,
// it looks prettier.
painter.fillRect( xPos - leftExtraBackgroundPx, yPos,
chunk_width + leftExtraBackgroundPx,
fontHeight, chunk.backColor() );
}
else {
// other chunks...
painter.fillRect( xPos, yPos, chunk_width,
fontHeight, chunk.backColor() );
}
painter.setPen( chunk.foreColor() );
painter.drawText( xPos, yPos + fontAscent, cutline );
xPos += chunk_width;
}
// Draw the empty block at the end of the line
int blank_width = line_width - xPos;
if ( blank_width > 0 )
painter.fillRect( xPos, yPos, blank_width, fontHeight, backColor_ );
}
const int DigitsBuffer::timeout_ = 2000;
DigitsBuffer::DigitsBuffer() : QObject()
{
}
void DigitsBuffer::reset()
{
LOG(logDEBUG) << "DigitsBuffer::reset()";
timer_.stop();
digits_.clear();
}
void DigitsBuffer::add( char character )
{
LOG(logDEBUG) << "DigitsBuffer::add()";
digits_.append( QChar( character ) );
timer_.start( timeout_ , this );
}
int DigitsBuffer::content()
{
int result = digits_.toInt();
reset();
return result;
}
void DigitsBuffer::timerEvent( QTimerEvent* event )
{
if ( event->timerId() == timer_.timerId() ) {
reset();
}
else {
QObject::timerEvent( event );
}
}
AbstractLogView::AbstractLogView(const AbstractLogData* newLogData,
const QuickFindPattern* const quickFindPattern, QWidget* parent) :
QAbstractScrollArea( parent ),
followElasticHook_( HOOK_THRESHOLD ),
lineNumbersVisible_( false ),
selectionStartPos_(),
selectionCurrentEndPos_(),
autoScrollTimer_(),
selection_(),
quickFindPattern_( quickFindPattern ),
quickFind_( newLogData, &selection_, quickFindPattern )
{
logData = newLogData;
followMode_ = false;
selectionStarted_ = false;
markingClickInitiated_ = false;
firstLine = 0;
lastLineAligned = false;
firstCol = 0;
overview_ = NULL;
overviewWidget_ = NULL;
// Display
leftMarginPx_ = 0;
// Fonts (sensible default for overview widget)
charWidth_ = 1;
charHeight_ = 10;
// Create the viewport QWidget
setViewport( 0 );
// Hovering
setMouseTracking( true );
lastHoveredLine_ = -1;
// Init the popup menu
createMenu();
// Signals
connect( quickFindPattern_, SIGNAL( patternUpdated() ),
this, SLOT ( handlePatternUpdated() ) );
connect( &quickFind_, SIGNAL( notify( const QFNotification& ) ),
this, SIGNAL( notifyQuickFind( const QFNotification& ) ) );
connect( &quickFind_, SIGNAL( clearNotification() ),
this, SIGNAL( clearQuickFindNotification() ) );
connect( &followElasticHook_, SIGNAL( lengthChanged() ),
this, SLOT( repaint() ) );
connect( &followElasticHook_, SIGNAL( hooked( bool ) ),
this, SIGNAL( followModeChanged( bool ) ) );
}
AbstractLogView::~AbstractLogView()
{
}
//
// Received events
//
void AbstractLogView::changeEvent( QEvent* changeEvent )
{
QAbstractScrollArea::changeEvent( changeEvent );
// Stop the timer if the widget becomes inactive
if ( changeEvent->type() == QEvent::ActivationChange ) {
if ( ! isActiveWindow() )
autoScrollTimer_.stop();
}
viewport()->update();
}
void AbstractLogView::mousePressEvent( QMouseEvent* mouseEvent )
{
static std::shared_ptr config =
Persistent( "settings" );
if ( mouseEvent->button() == Qt::LeftButton )
{
int line = convertCoordToLine( mouseEvent->y() );
if ( mouseEvent->modifiers() & Qt::ShiftModifier )
{
selection_.selectRangeFromPrevious( line );
emit updateLineNumber( line );
update();
}
else
{
if ( mouseEvent->x() < bulletZoneWidthPx_ ) {
// Mark a line if it is clicked in the left margin
// (only if click and release in the same area)
markingClickInitiated_ = true;
markingClickLine_ = line;
}
else {
// Select the line, and start a selection
if ( line < logData->getNbLine() ) {
selection_.selectLine( line );
emit updateLineNumber( line );
emit newSelection( line );
}
// Remember the click in case we're starting a selection
selectionStarted_ = true;
selectionStartPos_ = convertCoordToFilePos( mouseEvent->pos() );
selectionCurrentEndPos_ = selectionStartPos_;
}
}
// Invalidate our cache
textAreaCache_.invalid_ = true;
}
else if ( mouseEvent->button() == Qt::RightButton )
{
// Prepare the popup depending on selection type
if ( selection_.isSingleLine() ) {
copyAction_->setText( "&Copy this line" );
}
else {
copyAction_->setText( "&Copy" );
copyAction_->setStatusTip( tr("Copy the selection") );
}
if ( selection_.isPortion() ) {
findNextAction_->setEnabled( true );
findPreviousAction_->setEnabled( true );
addToSearchAction_->setEnabled( true );
}
else {
findNextAction_->setEnabled( false );
findPreviousAction_->setEnabled( false );
addToSearchAction_->setEnabled( false );
}
// "Add to search" only makes sense in regexp mode
if ( config->mainRegexpType() != ExtendedRegexp )
addToSearchAction_->setEnabled( false );
// Display the popup (blocking)
popupMenu_->exec( QCursor::pos() );
}
emit activity();
}
void AbstractLogView::mouseMoveEvent( QMouseEvent* mouseEvent )
{
// Selection implementation
if ( selectionStarted_ )
{
// Invalidate our cache
textAreaCache_.invalid_ = true;
QPoint thisEndPos = convertCoordToFilePos( mouseEvent->pos() );
if ( thisEndPos != selectionCurrentEndPos_ )
{
// Are we on a different line?
if ( selectionStartPos_.y() != thisEndPos.y() )
{
if ( thisEndPos.y() != selectionCurrentEndPos_.y() )
{
// This is a 'range' selection
selection_.selectRange( selectionStartPos_.y(),
thisEndPos.y() );
emit updateLineNumber( thisEndPos.y() );
update();
}
}
// So we are on the same line. Are we moving horizontaly?
else if ( thisEndPos.x() != selectionCurrentEndPos_.x() )
{
// This is a 'portion' selection
selection_.selectPortion( thisEndPos.y(),
selectionStartPos_.x(), thisEndPos.x() );
update();
}
// On the same line, and moving vertically then
else
{
// This is a 'line' selection
selection_.selectLine( thisEndPos.y() );
emit updateLineNumber( thisEndPos.y() );
update();
}
selectionCurrentEndPos_ = thisEndPos;
// Do we need to scroll while extending the selection?
QRect visible = viewport()->rect();
if ( visible.contains( mouseEvent->pos() ) )
autoScrollTimer_.stop();
else if ( ! autoScrollTimer_.isActive() )
autoScrollTimer_.start( 100, this );
}
}
else {
considerMouseHovering( mouseEvent->x(), mouseEvent->y() );
}
}
void AbstractLogView::mouseReleaseEvent( QMouseEvent* mouseEvent )
{
if ( markingClickInitiated_ ) {
markingClickInitiated_ = false;
int line = convertCoordToLine( mouseEvent->y() );
if ( line == markingClickLine_ ) {
// Invalidate our cache
textAreaCache_.invalid_ = true;
emit markLine( line );
}
}
else {
selectionStarted_ = false;
if ( autoScrollTimer_.isActive() )
autoScrollTimer_.stop();
updateGlobalSelection();
}
}
void AbstractLogView::mouseDoubleClickEvent( QMouseEvent* mouseEvent )
{
if ( mouseEvent->button() == Qt::LeftButton )
{
// Invalidate our cache
textAreaCache_.invalid_ = true;
const QPoint pos = convertCoordToFilePos( mouseEvent->pos() );
selectWordAtPosition( pos );
}
emit activity();
}
void AbstractLogView::timerEvent( QTimerEvent* timerEvent )
{
if ( timerEvent->timerId() == autoScrollTimer_.timerId() ) {
QRect visible = viewport()->rect();
const QPoint globalPos = QCursor::pos();
const QPoint pos = viewport()->mapFromGlobal( globalPos );
QMouseEvent ev( QEvent::MouseMove, pos, globalPos, Qt::LeftButton,
Qt::LeftButton, Qt::NoModifier );
mouseMoveEvent( &ev );
int deltaX = qMax( pos.x() - visible.left(),
visible.right() - pos.x() ) - visible.width();
int deltaY = qMax( pos.y() - visible.top(),
visible.bottom() - pos.y() ) - visible.height();
int delta = qMax( deltaX, deltaY );
if ( delta >= 0 ) {
if ( delta < 7 )
delta = 7;
int timeout = 4900 / ( delta * delta );
autoScrollTimer_.start( timeout, this );
if ( deltaX > 0 )
horizontalScrollBar()->triggerAction(
pos.x() 0 )
verticalScrollBar()->triggerAction(
pos.y() modifiers() & Qt::ControlModifier) == Qt::ControlModifier;
bool shiftModifier = (keyEvent->modifiers() & Qt::ShiftModifier) == Qt::ShiftModifier;
bool noModifier = keyEvent->modifiers() == Qt::NoModifier;
if ( keyEvent->key() == Qt::Key_Left && noModifier )
horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepSub);
else if ( keyEvent->key() == Qt::Key_Right && noModifier )
horizontalScrollBar()->triggerAction(QScrollBar::SliderPageStepAdd);
else if ( keyEvent->key() == Qt::Key_Home && !controlModifier)
jumpToStartOfLine();
else if ( keyEvent->key() == Qt::Key_End && !controlModifier)
jumpToRightOfScreen();
else if ( (keyEvent->key() == Qt::Key_PageDown && controlModifier)
|| (keyEvent->key() == Qt::Key_End && controlModifier) )
{
disableFollow(); // duplicate of 'G' action.
selection_.selectLine( logData->getNbLine() - 1 );
emit updateLineNumber( logData->getNbLine() - 1 );
jumpToBottom();
}
else if ( (keyEvent->key() == Qt::Key_PageUp && controlModifier)
|| (keyEvent->key() == Qt::Key_Home && controlModifier) )
{
disableFollow(); // like 'g' but 0 input first line action.
selectAndDisplayLine( 0 );
emit updateLineNumber( 0 );
}
else if ( keyEvent->key() == Qt::Key_F3 && !shiftModifier )
searchNext(); // duplicate of 'n' action.
else if ( keyEvent->key() == Qt::Key_F3 && shiftModifier )
searchPrevious(); // duplicate of 'N' action.
else {
const char character = (keyEvent->text())[0].toLatin1();
if ( keyEvent->modifiers() == Qt::NoModifier &&
( character >= '0' ) && ( character <= '9' ) ) {
// Adds the digit to the timed buffer
digitsBuffer_.add( character );
}
else {
switch ( (keyEvent->text())[0].toLatin1() ) {
case 'j':
{
int delta = qMax( 1, digitsBuffer_.content() );
disableFollow();
//verticalScrollBar()->triggerAction(
//QScrollBar::SliderSingleStepAdd);
moveSelection( delta );
break;
}
case 'k':
{
int delta = qMin( -1, - digitsBuffer_.content() );
disableFollow();
//verticalScrollBar()->triggerAction(
//QScrollBar::SliderSingleStepSub);
moveSelection( delta );
break;
}
case 'h':
horizontalScrollBar()->triggerAction(
QScrollBar::SliderSingleStepSub);
break;
case 'l':
horizontalScrollBar()->triggerAction(
QScrollBar::SliderSingleStepAdd);
break;
case '0':
jumpToStartOfLine();
break;
case '$':
jumpToEndOfLine();
break;
case 'g':
{
int newLine = qMax( 0, digitsBuffer_.content() - 1 );
if ( newLine >= logData->getNbLine() )
newLine = logData->getNbLine() - 1;
disableFollow();
selectAndDisplayLine( newLine );
emit updateLineNumber( newLine );
break;
}
case 'G':
disableFollow();
selection_.selectLine( logData->getNbLine() - 1 );
emit updateLineNumber( logData->getNbLine() - 1 );
jumpToBottom();
break;
case 'n':
emit searchNext();
break;
case 'N':
emit searchPrevious();
break;
case '*':
// Use the selected 'word' and search forward
findNextSelected();
break;
case '#':
// Use the selected 'word' and search backward
findPreviousSelected();
break;
default:
keyEvent->ignore();
}
}
}
if ( keyEvent->isAccepted() ) {
emit activity();
}
else {
// Only pass bare keys to the superclass this is so that
// shortcuts such as Ctrl+Alt+Arrow are handled by the parent.
LOG(logDEBUG) << std::hex << keyEvent->modifiers();
if ( keyEvent->modifiers() == Qt::NoModifier ||
keyEvent->modifiers() == Qt::KeypadModifier ) {
QAbstractScrollArea::keyPressEvent( keyEvent );
}
}
}
void AbstractLogView::wheelEvent( QWheelEvent* wheelEvent )
{
emit activity();
// LOG(logDEBUG) << "wheelEvent";
// This is to handle the case where follow mode is on, but the user
// has moved using the scroll bar. We take them back to the bottom.
if ( followMode_ )
jumpToBottom();
int y_delta = 0;
if ( verticalScrollBar()->value() == verticalScrollBar()->maximum() ) {
// First see if we need to block the elastic (on Mac)
if ( wheelEvent->phase() == Qt::ScrollBegin )
followElasticHook_.hold();
else if ( wheelEvent->phase() == Qt::ScrollEnd )
followElasticHook_.release();
auto pixel_delta = wheelEvent->pixelDelta();
if ( pixel_delta.isNull() ) {
y_delta = wheelEvent->angleDelta().y() / 0.7;
}
else {
y_delta = pixel_delta.y();
}
// LOG(logDEBUG) << "Elastic " << y_delta;
followElasticHook_.move( - y_delta );
}
// LOG(logDEBUG) << "Length = " << followElasticHook_.length();
if ( followElasticHook_.length() == 0 && !followElasticHook_.isHooked() ) {
QAbstractScrollArea::wheelEvent( wheelEvent );
}
}
void AbstractLogView::resizeEvent( QResizeEvent* )
{
if ( logData == NULL )
return;
LOG(logDEBUG) << "resizeEvent received";
updateDisplaySize();
}
bool AbstractLogView::event( QEvent* e )
{
LOG(logDEBUG4) << "Event! Type: " << e->type();
// Make sure we ignore the gesture events as
// they seem to be accepted by default.
if ( e->type() == QEvent::Gesture ) {
auto gesture_event = dynamic_cast( e );
if ( gesture_event ) {
foreach( QGesture* gesture, gesture_event->gestures() ) {
LOG(logDEBUG4) << "Gesture: " << gesture->gestureType();
gesture_event->ignore( gesture );
}
// Ensure the event is sent up to parents who might care
return false;
}
}
return QAbstractScrollArea::event( e );
}
void AbstractLogView::scrollContentsBy( int dx, int dy )
{
LOG(logDEBUG) << "scrollContentsBy received " << dy
<< "position " << verticalScrollBar()->value();
int32_t last_top_line = ( logData->getNbLine() - getNbVisibleLines() );
if ( ( last_top_line > 0 ) && verticalScrollBar()->value() > last_top_line ) {
// The user is going further than the last line, we need to lock the last line at the bottom
LOG(logDEBUG) << "scrollContentsBy beyond!";
firstLine = last_top_line;
lastLineAligned = true;
}
else {
firstLine = verticalScrollBar()->value();
lastLineAligned = false;
}
firstCol = (firstCol - dx) > 0 ? firstCol - dx : 0;
LineNumber last_line = firstLine + getNbVisibleLines();
// Update the overview if we have one
if ( overview_ != NULL )
overview_->updateCurrentPosition( firstLine, last_line );
// Are we hovering over a new line?
const QPoint mouse_pos = mapFromGlobal( QCursor::pos() );
considerMouseHovering( mouse_pos.x(), mouse_pos.y() );
// Redraw
update();
}
void AbstractLogView::paintEvent( QPaintEvent* paintEvent )
{
const QRect invalidRect = paintEvent->rect();
if ( (invalidRect.isEmpty()) || (logData == NULL) )
return;
LOG(logDEBUG4) << "paintEvent received, firstLine=" << firstLine
<< " lastLineAligned=" << lastLineAligned
<< " rect: " << invalidRect.topLeft().x() <<
", " << invalidRect.topLeft().y() <<
", " << invalidRect.bottomRight().x() <<
", " << invalidRect.bottomRight().y();
#ifdef GLOGG_PERF_MEASURE_FPS
static uint32_t maxline = logData->getNbLine();
if ( ! perfCounter_.addEvent() && logData->getNbLine() > maxline ) {
LOG(logWARNING) << "Redraw per second: " << perfCounter_.readAndReset()
<< " lines: " << logData->getNbLine();
perfCounter_.addEvent();
maxline = logData->getNbLine();
}
#endif
auto start = std::chrono::system_clock::now();
// Can we use our cache?
int32_t delta_y = textAreaCache_.first_line_ - firstLine;
if ( textAreaCache_.invalid_ || ( textAreaCache_.first_column_ != firstCol ) ) {
// Force a full redraw
delta_y = INT32_MAX;
}
if ( delta_y != 0 ) {
// Full or partial redraw
drawTextArea( &textAreaCache_.pixmap_, delta_y );
textAreaCache_.invalid_ = false;
textAreaCache_.first_line_ = firstLine;
textAreaCache_.first_column_ = firstCol;
LOG(logDEBUG) << "End of writing " <<
std::chrono::duration_cast
( std::chrono::system_clock::now() - start ).count();
}
else {
// Use the cache as is: nothing to do!
}
// Height including the potentially invisible last line
const int whole_height = getNbVisibleLines() * charHeight_;
// Height in pixels of the "pull to follow" bottom bar.
int pullToFollowHeight = mapPullToFollowLength( followElasticHook_.length() )
+ ( followElasticHook_.isHooked() ?
( whole_height - viewport()->height() ) + PULL_TO_FOLLOW_HOOKED_HEIGHT : 0 );
if ( pullToFollowHeight
&& ( pullToFollowCache_.nb_columns_ != getNbVisibleCols() ) ) {
LOG(logDEBUG) << "Drawing pull to follow bar";
pullToFollowCache_.pixmap_ = drawPullToFollowBar(
viewport()->width(), viewport()->devicePixelRatio() );
pullToFollowCache_.nb_columns_ = getNbVisibleCols();
}
QPainter devicePainter( viewport() );
int drawingTopPosition = - pullToFollowHeight;
int drawingPullToFollowTopPosition = drawingTopPosition + whole_height;
// This is to cover the special case where there is less than a screenful
// worth of data, we want to see the document from the top, rather than
// pushing the first couple of lines above the viewport.
if ( followElasticHook_.isHooked() && ( logData->getNbLine() < getNbVisibleLines() ) ) {
drawingTopOffset_ = 0;
drawingTopPosition += ( whole_height - viewport()->height() ) + PULL_TO_FOLLOW_HOOKED_HEIGHT;
drawingPullToFollowTopPosition = drawingTopPosition + viewport()->height() - PULL_TO_FOLLOW_HOOKED_HEIGHT;
}
// This is the case where the user is on the 'extra' slot at the end
// and is aligned on the last line (but no elastic shown)
else if ( lastLineAligned && !followElasticHook_.isHooked() ) {
drawingTopOffset_ = - ( whole_height - viewport()->height() );
drawingTopPosition += drawingTopOffset_;
drawingPullToFollowTopPosition = drawingTopPosition + whole_height;
}
else {
drawingTopOffset_ = - pullToFollowHeight;
}
devicePainter.drawPixmap( 0, drawingTopPosition, textAreaCache_.pixmap_ );
// Draw the "pull to follow" zone if needed
if ( pullToFollowHeight ) {
devicePainter.drawPixmap( 0,
drawingPullToFollowTopPosition,
pullToFollowCache_.pixmap_ );
}
LOG(logDEBUG) << "End of repaint " <<
std::chrono::duration_cast
( std::chrono::system_clock::now() - start ).count();
}
// These two functions are virtual and this implementation is clearly
// only valid for a non-filtered display.
// We count on the 'filtered' derived classes to override them.
qint64 AbstractLogView::displayLineNumber( int lineNumber ) const
{
return lineNumber + 1; // show a 1-based index
}
qint64 AbstractLogView::maxDisplayLineNumber() const
{
return logData->getNbLine();
}
void AbstractLogView::setOverview( Overview* overview,
OverviewWidget* overview_widget )
{
overview_ = overview;
overviewWidget_ = overview_widget;
if ( overviewWidget_ ) {
connect( overviewWidget_, SIGNAL( lineClicked ( int ) ),
this, SIGNAL( followDisabled() ) );
connect( overviewWidget_, SIGNAL( lineClicked ( int ) ),
this, SLOT( jumpToLine( int ) ) );
}
refreshOverview();
}
void AbstractLogView::searchUsingFunction(
qint64 (QuickFind::*search_function)() )
{
disableFollow();
int line = (quickFind_.*search_function)();
if ( line >= 0 ) {
LOG(logDEBUG) << "search " << line;
displayLine( line );
emit updateLineNumber( line );
}
}
void AbstractLogView::searchForward()
{
searchUsingFunction( &QuickFind::searchForward );
}
void AbstractLogView::searchBackward()
{
searchUsingFunction( &QuickFind::searchBackward );
}
void AbstractLogView::incrementallySearchForward()
{
searchUsingFunction( &QuickFind::incrementallySearchForward );
}
void AbstractLogView::incrementallySearchBackward()
{
searchUsingFunction( &QuickFind::incrementallySearchBackward );
}
void AbstractLogView::incrementalSearchAbort()
{
quickFind_.incrementalSearchAbort();
emit changeQuickFind(
"",
QuickFindMux::Forward );
}
void AbstractLogView::incrementalSearchStop()
{
quickFind_.incrementalSearchStop();
}
void AbstractLogView::followSet( bool checked )
{
followMode_ = checked;
followElasticHook_.hook( checked );
update();
if ( checked )
jumpToBottom();
}
void AbstractLogView::refreshOverview()
{
assert( overviewWidget_ );
// Create space for the Overview if needed
if ( ( getOverview() != NULL ) && getOverview()->isVisible() ) {
setViewportMargins( 0, 0, OVERVIEW_WIDTH, 0 );
overviewWidget_->show();
}
else {
setViewportMargins( 0, 0, 0, 0 );
overviewWidget_->hide();
}
}
// Reset the QuickFind when the pattern is changed.
void AbstractLogView::handlePatternUpdated()
{
LOG(logDEBUG) << "AbstractLogView::handlePatternUpdated()";
quickFind_.resetLimits();
update();
}
// OR the current with the current search expression
void AbstractLogView::addToSearch()
{
if ( selection_.isPortion() ) {
LOG(logDEBUG) << "AbstractLogView::addToSearch()";
emit addToSearch( selection_.getSelectedText( logData ) );
}
else {
LOG(logERROR) << "AbstractLogView::addToSearch called for a wrong type of selection";
}
}
// Find next occurence of the selected text (*)
void AbstractLogView::findNextSelected()
{
// Use the selected 'word' and search forward
if ( selection_.isPortion() ) {
emit changeQuickFind(
selection_.getSelectedText( logData ),
QuickFindMux::Forward );
emit searchNext();
}
}
// Find next previous of the selected text (#)
void AbstractLogView::findPreviousSelected()
{
if ( selection_.isPortion() ) {
emit changeQuickFind(
selection_.getSelectedText( logData ),
QuickFindMux::Backward );
emit searchNext();
}
}
// Copy the selection to the clipboard
void AbstractLogView::copy()
{
static QClipboard* clipboard = QApplication::clipboard();
clipboard->setText( selection_.getSelectedText( logData ) );
}
//
// Public functions
//
void AbstractLogView::updateData()
{
LOG(logDEBUG) << "AbstractLogView::updateData";
// Check the top Line is within range
if ( firstLine >= logData->getNbLine() ) {
firstLine = 0;
firstCol = 0;
verticalScrollBar()->setValue( 0 );
horizontalScrollBar()->setValue( 0 );
}
// Crop selection if it become out of range
selection_.crop( logData->getNbLine() - 1 );
// Adapt the scroll bars to the new content
updateScrollBars();
// Calculate the index of the last line shown
LineNumber last_line = std::min( static_cast( logData->getNbLine() ),
static_cast( firstLine + getNbVisibleLines() ) );
// Reset the QuickFind in case we have new stuff to search into
quickFind_.resetLimits();
if ( followMode_ )
jumpToBottom();
// Update the overview if we have one
if ( overview_ != NULL )
overview_->updateCurrentPosition( firstLine, last_line );
// Invalidate our cache
textAreaCache_.invalid_ = true;
// Repaint!
update();
}
void AbstractLogView::updateDisplaySize()
{
// Font is assumed to be mono-space (is restricted by options dialog)
QFontMetrics fm = fontMetrics();
charHeight_ = fm.height();
// For some reason on Qt 4.8.2 for Win, maxWidth() is wrong but the
// following give the right result, not sure why:
charWidth_ = fm.width( QChar('a') );
// Update the scroll bars
updateScrollBars();
verticalScrollBar()->setPageStep( getNbVisibleLines() );
if ( followMode_ )
jumpToBottom();
LOG(logDEBUG) << "viewport.width()=" << viewport()->width();
LOG(logDEBUG) << "viewport.height()=" << viewport()->height();
LOG(logDEBUG) << "width()=" << width();
LOG(logDEBUG) << "height()=" << height();
if ( overviewWidget_ )
overviewWidget_->setGeometry( viewport()->width() + 2, 1,
OVERVIEW_WIDTH - 1, viewport()->height() );
// Our text area cache is now invalid
textAreaCache_.invalid_ = true;
textAreaCache_.pixmap_ = QPixmap {
viewport()->width() * viewport()->devicePixelRatio(),
static_cast( getNbVisibleLines() ) * charHeight_ * viewport()->devicePixelRatio() };
textAreaCache_.pixmap_.setDevicePixelRatio( viewport()->devicePixelRatio() );
}
int AbstractLogView::getTopLine() const
{
return firstLine;
}
QString AbstractLogView::getSelection() const
{
return selection_.getSelectedText( logData );
}
void AbstractLogView::selectAll()
{
selection_.selectRange( 0, logData->getNbLine() - 1 );
textAreaCache_.invalid_ = true;
update();
}
void AbstractLogView::selectAndDisplayLine( int line )
{
disableFollow();
selection_.selectLine( line );
displayLine( line );
emit updateLineNumber( line );
}
// The difference between this function and displayLine() is quite
// subtle: this one always jump, even if the line passed is visible.
void AbstractLogView::jumpToLine( int line )
{
// Put the selected line in the middle if possible
int newTopLine = line - ( getNbVisibleLines() / 2 );
if ( newTopLine < 0 )
newTopLine = 0;
// This will also trigger a scrollContents event
verticalScrollBar()->setValue( newTopLine );
}
void AbstractLogView::setLineNumbersVisible( bool lineNumbersVisible )
{
lineNumbersVisible_ = lineNumbersVisible;
}
void AbstractLogView::forceRefresh()
{
// Invalidate our cache
textAreaCache_.invalid_ = true;
}
//
// Private functions
//
// Returns the number of lines visible in the viewport
LineNumber AbstractLogView::getNbVisibleLines() const
{
return static_cast( viewport()->height() / charHeight_ + 1 );
}
// Returns the number of columns visible in the viewport
int AbstractLogView::getNbVisibleCols() const
{
return ( viewport()->width() - leftMarginPx_ ) / charWidth_ + 1;
}
// Converts the mouse x, y coordinates to the line number in the file
int AbstractLogView::convertCoordToLine(int yPos) const
{
int line = firstLine + ( yPos - drawingTopOffset_ ) / charHeight_;
return line;
}
// Converts the mouse x, y coordinates to the char coordinates (in the file)
// This function ensure the pos exists in the file.
QPoint AbstractLogView::convertCoordToFilePos( const QPoint& pos ) const
{
int line = convertCoordToLine( pos.y() );
if ( line >= logData->getNbLine() )
line = logData->getNbLine() - 1;
if ( line < 0 )
line = 0;
// Determine column in screen space and convert it to file space
int column = firstCol + ( pos.x() - leftMarginPx_ ) / charWidth_;
QString this_line = logData->getExpandedLineString( line );
const int length = this_line.length();
if ( column >= length )
column = length - 1;
if ( column < 0 )
column = 0;
LOG(logDEBUG4) << "AbstractLogView::convertCoordToFilePos col="
<< column << " line=" << line;
QPoint point( column, line );
return point;
}
// Makes the widget adjust itself to display the passed line.
// Doing so, it will throw itself a scrollContents event.
void AbstractLogView::displayLine( LineNumber line )
{
// If the line is already the screen
if ( ( line >= firstLine ) &&
( line < ( firstLine + getNbVisibleLines() ) ) ) {
// Invalidate our cache
textAreaCache_.invalid_ = true;
// ... don't scroll and just repaint
update();
} else {
jumpToLine( line );
}
}
// Move the selection up and down by the passed number of lines
void AbstractLogView::moveSelection( int delta )
{
LOG(logDEBUG) << "AbstractLogView::moveSelection delta=" << delta;
QList selection = selection_.getLines();
int new_line;
// If nothing is selected, do as if line -1 was.
if ( selection.isEmpty() )
selection.append( -1 );
if ( delta < 0 )
new_line = selection.first() + delta;
else
new_line = selection.last() + delta;
if ( new_line < 0 )
new_line = 0;
else if ( new_line >= logData->getNbLine() )
new_line = logData->getNbLine() - 1;
// Select and display the new line
selection_.selectLine( new_line );
displayLine( new_line );
emit updateLineNumber( new_line );
emit newSelection( new_line );
}
// Make the start of the lines visible
void AbstractLogView::jumpToStartOfLine()
{
horizontalScrollBar()->setValue( 0 );
}
// Make the end of the lines in the selection visible
void AbstractLogView::jumpToEndOfLine()
{
QList selection = selection_.getLines();
// Search the longest line in the selection
int max_length = 0;
foreach ( int line, selection ) {
int length = logData->getLineLength( line );
if ( length > max_length )
max_length = length;
}
horizontalScrollBar()->setValue( max_length - getNbVisibleCols() );
}
// Make the end of the lines on the screen visible
void AbstractLogView::jumpToRightOfScreen()
{
QList selection = selection_.getLines();
// Search the longest line on screen
int max_length = 0;
for ( auto i = firstLine; i <= ( firstLine + getNbVisibleLines() ); i++ ) {
int length = logData->getLineLength( i );
if ( length > max_length )
max_length = length;
}
horizontalScrollBar()->setValue( max_length - getNbVisibleCols() );
}
// Jump to the first line
void AbstractLogView::jumpToTop()
{
// This will also trigger a scrollContents event
verticalScrollBar()->setValue( 0 );
update(); // in case the screen hasn't moved
}
// Jump to the last line
void AbstractLogView::jumpToBottom()
{
const int new_top_line =
qMax( logData->getNbLine() - getNbVisibleLines() + 1, 0LL );
// This will also trigger a scrollContents event
verticalScrollBar()->setValue( new_top_line );
update(); // in case the screen hasn't moved
}
// Returns whether the character passed is a 'word' character
inline bool AbstractLogView::isCharWord( char c )
{
if ( ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||
( ( c >= 'a' ) && ( c <= 'z' ) ) ||
( ( c >= '0' ) && ( c <= '9' ) ) ||
( ( c == '_' ) ) )
return true;
else
return false;
}
// Select the word under the given position
void AbstractLogView::selectWordAtPosition( const QPoint& pos )
{
const int x = pos.x();
const QString line = logData->getExpandedLineString( pos.y() );
if ( isCharWord( line[x].toLatin1() ) ) {
// Search backward for the first character in the word
int currentPos = x;
for ( ; currentPos > 0; currentPos-- )
if ( ! isCharWord( line[currentPos].toLatin1() ) )
break;
// Exclude the first char of the line if needed
if ( ! isCharWord( line[currentPos].toLatin1() ) )
currentPos++;
int start = currentPos;
// Now search for the end
currentPos = x;
for ( ; currentPos < line.length() - 1; currentPos++ )
if ( ! isCharWord( line[currentPos].toLatin1() ) )
break;
// Exclude the last char of the line if needed
if ( ! isCharWord( line[currentPos].toLatin1() ) )
currentPos--;
int end = currentPos;
selection_.selectPortion( pos.y(), start, end );
updateGlobalSelection();
update();
}
}
// Update the system global (middle click) selection (X11 only)
void AbstractLogView::updateGlobalSelection()
{
static QClipboard* const clipboard = QApplication::clipboard();
// Updating it only for "non-trivial" (range or portion) selections
if ( ! selection_.isSingleLine() )
clipboard->setText( selection_.getSelectedText( logData ),
QClipboard::Selection );
}
// Create the pop-up menu
void AbstractLogView::createMenu()
{
copyAction_ = new QAction( tr("&Copy"), this );
// No text as this action title depends on the type of selection
connect( copyAction_, SIGNAL(triggered()), this, SLOT(copy()) );
// For '#' and '*', shortcuts doesn't seem to work but
// at least it displays them in the menu, we manually handle those keys
// as keys event anyway (in keyPressEvent).
findNextAction_ = new QAction(tr("Find &next"), this);
findNextAction_->setShortcut( Qt::Key_Asterisk );
findNextAction_->setStatusTip( tr("Find the next occurence") );
connect( findNextAction_, SIGNAL(triggered()),
this, SLOT( findNextSelected() ) );
findPreviousAction_ = new QAction( tr("Find &previous"), this );
findPreviousAction_->setShortcut( tr("#") );
findPreviousAction_->setStatusTip( tr("Find the previous occurence") );
connect( findPreviousAction_, SIGNAL(triggered()),
this, SLOT( findPreviousSelected() ) );
addToSearchAction_ = new QAction( tr("&Add to search"), this );
addToSearchAction_->setStatusTip(
tr("Add the selection to the current search") );
connect( addToSearchAction_, SIGNAL( triggered() ),
this, SLOT( addToSearch() ) );
popupMenu_ = new QMenu( this );
popupMenu_->addAction( copyAction_ );
popupMenu_->addSeparator();
popupMenu_->addAction( findNextAction_ );
popupMenu_->addAction( findPreviousAction_ );
popupMenu_->addAction( addToSearchAction_ );
}
void AbstractLogView::considerMouseHovering( int x_pos, int y_pos )
{
int line = convertCoordToLine( y_pos );
if ( ( x_pos < leftMarginPx_ )
&& ( line >= 0 )
&& ( line < logData->getNbLine() ) ) {
// Mouse moved in the margin, send event up
// (possibly to highlight the overview)
if ( line != lastHoveredLine_ ) {
LOG(logDEBUG) << "Mouse moved in margin line: " << line;
emit mouseHoveredOverLine( line );
lastHoveredLine_ = line;
}
}
else {
if ( lastHoveredLine_ != -1 ) {
emit mouseLeftHoveringZone();
lastHoveredLine_ = -1;
}
}
}
void AbstractLogView::updateScrollBars()
{
verticalScrollBar()->setRange( 0, std::max( 0LL,
logData->getNbLine() - getNbVisibleLines() + 1 ) );
const int hScrollMaxValue = std::max( 0,
logData->getMaxLength() - getNbVisibleCols() + 1 );
horizontalScrollBar()->setRange( 0, hScrollMaxValue );
}
void AbstractLogView::drawTextArea( QPaintDevice* paint_device, int32_t delta_y )
{
// LOG( logDEBUG ) << "devicePixelRatio: " << viewport()->devicePixelRatio();
// LOG( logDEBUG ) << "viewport size: " << viewport()->size().width();
// LOG( logDEBUG ) << "pixmap size: " << textPixmap.width();
// Repaint the viewport
QPainter painter( paint_device );
// LOG( logDEBUG ) << "font: " << viewport()->font().family().toStdString();
// LOG( logDEBUG ) << "font painter: " << painter.font().family().toStdString();
painter.setFont( this->font() );
const int fontHeight = charHeight_;
const int fontAscent = painter.fontMetrics().ascent();
const int nbCols = getNbVisibleCols();
const int paintDeviceHeight = paint_device->height() / viewport()->devicePixelRatio();
const int paintDeviceWidth = paint_device->width() / viewport()->devicePixelRatio();
const QPalette& palette = viewport()->palette();
std::shared_ptr filterSet =
Persistent( "filterSet" );
QColor foreColor, backColor;
static const QBrush normalBulletBrush = QBrush( Qt::white );
static const QBrush matchBulletBrush = QBrush( Qt::red );
static const QBrush markBrush = QBrush( "dodgerblue" );
static const int SEPARATOR_WIDTH = 1;
static const qreal BULLET_AREA_WIDTH = 11;
static const int CONTENT_MARGIN_WIDTH = 1;
static const int LINE_NUMBER_PADDING = 3;
// First check the lines to be drawn are within range (might not be the case if
// the file has just changed)
const int64_t lines_in_file = logData->getNbLine();
if ( firstLine > lines_in_file )
firstLine = lines_in_file ? lines_in_file - 1 : 0;
const int64_t nbLines = std::min(
static_cast( getNbVisibleLines() ), lines_in_file - firstLine );
const int bottomOfTextPx = nbLines * fontHeight;
LOG(logDEBUG) << "drawing lines from " << firstLine << " (" << nbLines << " lines)";
LOG(logDEBUG) << "bottomOfTextPx: " << bottomOfTextPx;
LOG(logDEBUG) << "Height: " << paintDeviceHeight;
// Lines to write
const QStringList lines = logData->getExpandedLines( firstLine, nbLines );
// First draw the bullet left margin
painter.setPen(palette.color(QPalette::Text));
painter.fillRect( 0, 0,
BULLET_AREA_WIDTH, paintDeviceHeight,
Qt::darkGray );
// Column at which the content should start (pixels)
qreal contentStartPosX = BULLET_AREA_WIDTH + SEPARATOR_WIDTH;
// This is also the bullet zone width, used for marking clicks
bulletZoneWidthPx_ = contentStartPosX;
// Update the length of line numbers
const int nbDigitsInLineNumber = countDigits( maxDisplayLineNumber() );
// Draw the line numbers area
int lineNumberAreaStartX = 0;
if ( lineNumbersVisible_ ) {
int lineNumberWidth = charWidth_ * nbDigitsInLineNumber;
int lineNumberAreaWidth =
2 * LINE_NUMBER_PADDING + lineNumberWidth;
lineNumberAreaStartX = contentStartPosX;
painter.setPen(palette.color(QPalette::Text));
/* Not sure if it looks good...
painter.drawLine( contentStartPosX + lineNumberAreaWidth,
0,
contentStartPosX + lineNumberAreaWidth,
viewport()->height() );
*/
painter.fillRect( contentStartPosX - SEPARATOR_WIDTH, 0,
lineNumberAreaWidth + SEPARATOR_WIDTH, paintDeviceHeight,
Qt::lightGray );
// Update for drawing the actual text
contentStartPosX += lineNumberAreaWidth;
}
else {
painter.fillRect( contentStartPosX - SEPARATOR_WIDTH, 0,
SEPARATOR_WIDTH + 1, paintDeviceHeight,
Qt::lightGray );
// contentStartPosX += SEPARATOR_WIDTH;
}
painter.drawLine( BULLET_AREA_WIDTH, 0,
BULLET_AREA_WIDTH, paintDeviceHeight - 1 );
// This is the total width of the 'margin' (including line number if any)
// used for mouse calculation etc...
leftMarginPx_ = contentStartPosX + SEPARATOR_WIDTH;
// Then draw each line
for (int i = 0; i < nbLines; i++) {
const LineNumber line_index = i + firstLine;
// Position in pixel of the base line of the line to print
const int yPos = i * fontHeight;
const int xPos = contentStartPosX + CONTENT_MARGIN_WIDTH;
// string to print, cut to fit the length and position of the view
const QString line = lines[i];
const QString cutLine = line.mid( firstCol, nbCols );
if ( selection_.isLineSelected( line_index ) ) {
// Reverse the selected line
foreColor = palette.color( QPalette::HighlightedText );
backColor = palette.color( QPalette::Highlight );
painter.setPen(palette.color(QPalette::Text));
}
else if ( filterSet->matchLine( logData->getLineString( line_index ),
&foreColor, &backColor ) ) {
// Apply a filter to the line
}
else {
// Use the default colors
foreColor = palette.color( QPalette::Text );
backColor = palette.color( QPalette::Base );
}
// Is there something selected in the line?
int sel_start, sel_end;
bool isSelection =
selection_.getPortionForLine( line_index, &sel_start, &sel_end );
// Has the line got elements to be highlighted
QList qfMatchList;
bool isMatch =
quickFindPattern_->matchLine( line, qfMatchList );
if ( isSelection || isMatch ) {
// We use the LineDrawer and its chunks because the
// line has to be somehow highlighted
LineDrawer lineDrawer( backColor );
// First we create a list of chunks with the highlights
QList chunkList;
int column = 0; // Current column in line space
foreach( const QuickFindMatch match, qfMatchList ) {
int start = match.startColumn() - firstCol;
int end = start + match.length();
// Ignore matches that are *completely* outside view area
if ( ( start < 0 && end < 0 ) || start >= nbCols )
continue;
if ( start > column )
chunkList << LineChunk( column, start - 1, LineChunk::Normal );
column = qMin( start + match.length() - 1, nbCols );
chunkList << LineChunk( qMax( start, 0 ), column,
LineChunk::Highlighted );
column++;
}
if ( column <= cutLine.length() - 1 )
chunkList << LineChunk( column, cutLine.length() - 1, LineChunk::Normal );
// Then we add the selection if needed
QList newChunkList;
if ( isSelection ) {
sel_start -= firstCol; // coord in line space
sel_end -= firstCol;
foreach ( const LineChunk chunk, chunkList ) {
newChunkList << chunk.select( sel_start, sel_end );
}
}
else
newChunkList = chunkList;
foreach ( const LineChunk chunk, newChunkList ) {
// Select the colours
QColor fore;
QColor back;
switch ( chunk.type() ) {
case LineChunk::Normal:
fore = foreColor;
back = backColor;
break;
case LineChunk::Highlighted:
fore = QColor( "black" );
back = QColor( "yellow" );
// fore = highlightForeColor;
// back = highlightBackColor;
break;
case LineChunk::Selected:
fore = palette.color( QPalette::HighlightedText ),
back = palette.color( QPalette::Highlight );
break;
}
lineDrawer.addChunk ( chunk, fore, back );
}
lineDrawer.draw( painter, xPos, yPos,
viewport()->width(), cutLine,
CONTENT_MARGIN_WIDTH );
}
else {
// Nothing to be highlighted, we print the whole line!
painter.fillRect( xPos - CONTENT_MARGIN_WIDTH, yPos,
viewport()->width(), fontHeight, backColor );
// (the rectangle is extended on the left to cover the small
// margin, it looks better (LineDrawer does the same) )
painter.setPen( foreColor );
painter.drawText( xPos, yPos + fontAscent, cutLine );
}
// Then draw the bullet
painter.setPen( palette.color( QPalette::Text ) );
const qreal circleSize = 3;
const qreal arrowHeight = 4;
const qreal middleXLine = BULLET_AREA_WIDTH / 2;
const qreal middleYLine = yPos + (fontHeight / 2);
const LineType line_type = lineType( line_index );
if ( line_type == Marked ) {
// A pretty arrow if the line is marked
const QPointF points[7] = {
QPointF(1, middleYLine - 2),
QPointF(middleXLine, middleYLine - 2),
QPointF(middleXLine, middleYLine - arrowHeight),
QPointF(BULLET_AREA_WIDTH - 1, middleYLine),
QPointF(middleXLine, middleYLine + arrowHeight),
QPointF(middleXLine, middleYLine + 2),
QPointF(1, middleYLine + 2 ),
};
painter.setBrush( markBrush );
painter.drawPolygon( points, 7 );
}
else {
// For pretty circles
painter.setRenderHint( QPainter::Antialiasing );
if ( lineType( line_index ) == Match )
painter.setBrush( matchBulletBrush );
else
painter.setBrush( normalBulletBrush );
painter.drawEllipse( middleXLine - circleSize,
middleYLine - circleSize,
circleSize * 2, circleSize * 2 );
}
// Draw the line number
if ( lineNumbersVisible_ ) {
static const QString lineNumberFormat( "%1" );
const QString& lineNumberStr =
lineNumberFormat.arg( displayLineNumber( line_index ),
nbDigitsInLineNumber );
painter.setPen( palette.color( QPalette::Text ) );
painter.drawText( lineNumberAreaStartX + LINE_NUMBER_PADDING,
yPos + fontAscent, lineNumberStr );
}
} // For each line
if ( bottomOfTextPx < paintDeviceHeight ) {
// The lines don't cover the whole device
painter.fillRect( contentStartPosX, bottomOfTextPx,
paintDeviceWidth - contentStartPosX,
paintDeviceHeight, palette.color( QPalette::Window ) );
}
}
// Draw the "pull to follow" bar and return a pixmap.
// The width is passed in "logic" pixels.
QPixmap AbstractLogView::drawPullToFollowBar( int width, float pixel_ratio )
{
static constexpr int barWidth = 40;
QPixmap pixmap ( static_cast( width ) * pixel_ratio, barWidth * 6.0 );
pixmap.setDevicePixelRatio( pixel_ratio );
pixmap.fill( this->palette().color( this->backgroundRole() ) );
const int nbBars = width / (barWidth * 2) + 1;
QPainter painter( &pixmap );
painter.setPen( QPen( QColor( 0, 0, 0, 0 ) ) );
painter.setBrush( QBrush( QColor( "lightyellow" ) ) );
for ( int i = 0; i < nbBars; ++i ) {
QPoint points[4] = {
{ (i*2+1)*barWidth, 0 },
{ 0, (i*2+1)*barWidth },
{ 0, (i+1)*2*barWidth },
{ (i+1)*2*barWidth, 0 }
};
painter.drawConvexPolygon( points, 4 );
}
return pixmap;
}
void AbstractLogView::disableFollow()
{
emit followModeChanged( false );
followElasticHook_.hook( false );
}
namespace {
// Convert the length of the pull to follow bar to pixels
int mapPullToFollowLength( int length )
{
return length / 14;
}
};
glogg-1.1.4/src/winfilewatcher.h 0000644 0001750 0001750 00000003375 13076461374 015602 0 ustar nicko nicko /*
* Copyright (C) 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef WINFILEWATCHER_H
#define WINFILEWATCHER_H
#include "filewatcher.h"
#include
// #include "inotifywatchtower.h"
// An implementation of FileWatcher, as an adapter to INotifyWatchTower.
// This is Linux only, and require a recent version of the kernel.
// Please note that due to the implementation of the constructor
// this class is not thread safe and shall always be used from the main UI thread.
class WinFileWatcher : public FileWatcher {
Q_OBJECT
public:
// Create the empty object
WinFileWatcher();
// Destroy the object
~WinFileWatcher();
void addFile( const QString& fileName ) override;
void removeFile( const QString& fileName ) override;
signals:
void fileChanged( const QString& );
private:
// The following variables are protected by watched_files_mutex_
QString watched_file_name_;
// Reference to the (unique) watchtower.
// static std::shared_ptr watch_tower_;
// std::shared_ptr notification_;
};
#endif
glogg-1.1.4/src/recentfiles.cpp 0000644 0001750 0001750 00000005626 13076461374 015426 0 ustar nicko nicko /*
* Copyright (C) 2011 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
// This file implements class RecentFiles
#include
#include
#include "log.h"
#include "recentfiles.h"
const int RecentFiles::RECENTFILES_VERSION = 1;
const int RecentFiles::MAX_NUMBER_OF_FILES = 10;
RecentFiles::RecentFiles() : recentFiles_()
{
}
void RecentFiles::addRecent( const QString& text )
{
// First prune non existent files
QMutableStringListIterator i(recentFiles_);
while ( i.hasNext() ) {
if ( !QFile::exists(i.next()) )
i.remove();
}
// Remove any copy of the about to be added filename
recentFiles_.removeAll( text );
// Add at the front
recentFiles_.push_front( text );
// Trim the list if it's too long
while ( recentFiles_.size() > MAX_NUMBER_OF_FILES )
recentFiles_.pop_back();
}
QStringList RecentFiles::recentFiles() const
{
return recentFiles_;
}
//
// Persistable virtual functions implementation
//
void RecentFiles::saveToStorage( QSettings& settings ) const
{
LOG(logDEBUG) << "RecentFiles::saveToStorage";
settings.beginGroup( "RecentFiles" );
settings.setValue( "version", RECENTFILES_VERSION );
settings.beginWriteArray( "filesHistory" );
for (int i = 0; i < recentFiles_.size(); ++i) {
settings.setArrayIndex( i );
settings.setValue( "name", recentFiles_.at( i ) );
}
settings.endArray();
settings.endGroup();
}
void RecentFiles::retrieveFromStorage( QSettings& settings )
{
LOG(logDEBUG) << "RecentFiles::retrieveFromStorage";
recentFiles_.clear();
if ( settings.contains( "RecentFiles/version" ) ) {
// Unserialise the "new style" stored history
settings.beginGroup( "RecentFiles" );
if ( settings.value( "version" ) == RECENTFILES_VERSION ) {
int size = settings.beginReadArray( "filesHistory" );
for (int i = 0; i < size; ++i) {
settings.setArrayIndex(i);
QString search = settings.value( "name" ).toString();
recentFiles_.append( search );
}
settings.endArray();
}
else {
LOG(logERROR) << "Unknown version of FilterSet, ignoring it...";
}
settings.endGroup();
}
}
glogg-1.1.4/src/sessioninfo.h 0000644 0001750 0001750 00000004162 13076461374 015121 0 ustar nicko nicko /*
* Copyright (C) 2011, 2014 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef SESSIONINFO_H
#define SESSIONINFO_H
#include
#include
#include
#include
#include "persistable.h"
// Simple component class containing information related to the session
// to be persisted and reloaded upon start
class SessionInfo : public Persistable {
public:
SessionInfo() : openFiles_() { }
// Geometry of the main window
// (this is an opaque string which is interpreted by the
// MainWindow implementation)
QByteArray geometry() const
{ return geometry_; }
void setGeometry( const QByteArray& geometry )
{ geometry_ = geometry; }
struct OpenFile
{
std::string fileName;
uint64_t topLine;
// The view context contains parameter specific to the view's
// implementation (such as geometry...)
std::string viewContext;
};
// List of the loaded files
std::vector openFiles() const
{ return openFiles_; }
void setOpenFiles( const std::vector& loaded_files )
{ openFiles_ = loaded_files; }
// Reads/writes the current config in the QSettings object passed
virtual void saveToStorage( QSettings& settings ) const;
virtual void retrieveFromStorage( QSettings& settings );
private:
static const int OPENFILES_VERSION;
QByteArray geometry_;
QByteArray crawlerState_;
std::vector openFiles_;
};
#endif
glogg-1.1.4/src/selection.h 0000644 0001750 0001750 00000010164 13076461374 014546 0 ustar nicko nicko /*
* Copyright (C) 2010, 2013 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#ifndef SELECTION_H
#define SELECTION_H
#include
#include
#include "utils.h"
class AbstractLogData;
class Portion
{
public:
Portion() { line_ = -1; }
Portion( int line, int start_column, int end_column )
{ line_ = line; startColumn_ = start_column; endColumn_ = end_column; }
int line() const { return line_; }
int startColumn() const { return startColumn_; }
int endColumn() const { return endColumn_; }
bool isValid() const { return ( line_ != -1 ); }
private:
int line_;
int startColumn_;
int endColumn_;
};
// Represents a selection in an AbstractLogView
class Selection
{
public:
// Construct an empty selection
Selection();
// Clear the selection
void clear() { selectedPartial_.line = -1; selectedLine_ = -1; };
// Select one line
void selectLine( int line )
{ selectedPartial_.line = -1; selectedRange_.startLine = -1;
selectedLine_ = line; };
// Select a portion of line (both start and end included)
void selectPortion( int line, int start_column, int end_column );
void selectPortion( Portion selection )
{ selectPortion( selection.line(), selection.startColumn(),
selection.endColumn() ); }
// Select a range of lines (both start and end included)
void selectRange( int start_line, int end_line );
// Select a range from the previously selected line or beginning
// of range (shift+click behaviour)
void selectRangeFromPrevious( int line );
// Crop selection so that in fit in the range ending with the line passed.
void crop( int last_line );
// Returns whether the selection is empty
bool isEmpty() const
{ return ( selectedPartial_.line == -1 ) && ( selectedLine_ == -1 ); }
// Returns whether the selection is a single line
bool isSingleLine() const { return ( selectedLine_ != -1 ); }
// Returns whether the selection is a portion of line
bool isPortion() const { return ( selectedPartial_.line != -1 ); }
// Returns whether a portion is selected or not on the passed line.
// If so, returns the portion position.
bool getPortionForLine( int line,
int* start_column, int* end_column ) const;
// Get a list of selected line(s), in order.
QList getLines() const;
// Returns wether the line passed is selected (entirely).
bool isLineSelected( int line ) const;
// Returns the line selected or -1 if not a single line selection
qint64 selectedLine() const;
// Returns the text selected from the passed AbstractLogData
QString getSelectedText( const AbstractLogData* logData ) const;
// Return the position immediately after the current selection
// (used for searches).
// This is the next character or the start of the next line.
FilePosition getNextPosition() const;
// Idem from the position immediately before selection.
FilePosition getPreviousPosition() const;
private:
// Line number currently selected, or -1 if none selected
int selectedLine_;
struct SelectedPartial {
int line;
int startColumn;
int endColumn;
};
struct SelectedRange {
// The limits of the range, sorted
int startLine;
int endLine;
// The line selected first, used for shift+click
int firstLine;
};
struct SelectedPartial selectedPartial_;
struct SelectedRange selectedRange_;
};
#endif
glogg-1.1.4/src/winwatchtowerdriver.cpp 0000644 0001750 0001750 00000031224 13076461374 017235 0 ustar nicko nicko /*
* Copyright (C) 2015 Nicolas Bonnefon and other contributors
*
* This file is part of glogg.
*
* glogg 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 3 of the License, or
* (at your option) any later version.
*
* glogg 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 glogg. If not, see .
*/
#include "winwatchtowerdriver.h"
#define WIN32_LEAN_AND_MEAN
#include
#include
#include