Jämför commits
	
		
			9 Incheckningar
		
	
	
		
			10d61acbd0
			...
			31f0568a87
		
	
	| Upphovsman | SHA1 | Datum | |
|---|---|---|---|
| 31f0568a87 | |||
| 238f9add49 | |||
| 7c63ee9178 | |||
| 1edfcc8f23 | |||
| 2df273dee3 | |||
| 5a47f5949f | |||
| e6a0c0daee | |||
| 11b070ed42 | |||
| 47874b3706 | 
| @@ -1,16 +1,50 @@ | ||||
| #include <QtConcurrent> | ||||
| #include "ipcpreviewworker.h" | ||||
| #include "previewgeneratormapfunctor.h" | ||||
| IPCPreviewWorker::IPCPreviewWorker() | ||||
| IPCPreviewWorker::IPCPreviewWorker(QLocalSocket *peer) | ||||
| { | ||||
| 	this->peer = peer; | ||||
| 	this->connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::resultReadyAt, this, | ||||
| 				  [this](int index) { emit previewGenerated(previewWorkerWatcher.resultAt(index)); }); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::finished, this, [this] { emit finished(); }); | ||||
| 				  [this](int index) | ||||
| 				  { | ||||
| 					  if(this->peer != nullptr) | ||||
| 					  { | ||||
| 						  QDataStream stream{this->peer}; | ||||
| 						  stream << previewWorkerWatcher.resultAt(index); | ||||
| 						  this->peer->flush(); | ||||
| 					  } | ||||
| 				  }); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::finished, this, &IPCPreviewWorker::shutdownSocket); | ||||
| 	connect(this->peer, &QLocalSocket::disconnected, this, &IPCPreviewWorker::shutdownSocket); | ||||
| } | ||||
|  | ||||
| void IPCPreviewWorker::start(RenderConfig config, const QVector<RenderTarget> &targets, QLocalSocket *peer) | ||||
| void IPCPreviewWorker::shutdownSocket() | ||||
| { | ||||
| 	if(cleaned) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	cleaned = true; | ||||
| 	if(this->peer != nullptr) | ||||
| 	{ | ||||
| 		if(this->peer->state() == QLocalSocket::ConnectedState) | ||||
| 		{ | ||||
| 			this->peer->flush(); | ||||
| 			this->peer->waitForBytesWritten(); | ||||
| 			this->peer->disconnectFromServer(); | ||||
| 			if(this->peer->state() != QLocalSocket::UnconnectedState) | ||||
| 			{ | ||||
| 				this->peer->waitForDisconnected(); | ||||
| 			} | ||||
| 		} | ||||
| 		delete this->peer; | ||||
| 		this->peer = nullptr; | ||||
| 	} | ||||
| 	emit finished(); | ||||
| } | ||||
|  | ||||
| void IPCPreviewWorker::start(RenderConfig config, const QVector<RenderTarget> &targets) | ||||
| { | ||||
| 	stop(); | ||||
| 	auto mapFunctor = PreviewGeneratorMapFunctor(); | ||||
| 	mapFunctor.setRenderConfig(config); | ||||
|  | ||||
|   | ||||
| @@ -11,13 +11,21 @@ class IPCPreviewWorker : public QObject | ||||
| 	Q_OBJECT | ||||
|   private: | ||||
| 	QFutureWatcher<QByteArray> previewWorkerWatcher; | ||||
| 	QLocalSocket *peer; | ||||
| 	bool cleaned = false; | ||||
|  | ||||
|   public: | ||||
| 	IPCPreviewWorker(); | ||||
| 	void start(RenderConfig config, const QVector<RenderTarget> &targets, QLocalSocket *peer); | ||||
| 	IPCPreviewWorker(QLocalSocket *peer); | ||||
| 	void start(RenderConfig config, const QVector<RenderTarget> &targets); | ||||
| 	void stop(); | ||||
| 	~IPCPreviewWorker() | ||||
| 	{ | ||||
| 		delete this->peer; | ||||
| 	} | ||||
|   private slots: | ||||
| 	void shutdownSocket(); | ||||
|  | ||||
|   signals: | ||||
| 	void previewGenerated(QByteArray); | ||||
| 	void finished(); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -18,8 +18,6 @@ IpcServer::IpcServer() | ||||
| 	/* Only 1, we are doing work for the GUI, not a service for general availability */ | ||||
| 	this->spawningServer.setMaxPendingConnections(1); | ||||
| 	connect(&this->spawningServer, &QLocalServer::newConnection, this, &IpcServer::spawnerNewConnection); | ||||
| 	connect(&this->previewWorker, &IPCPreviewWorker::previewGenerated, this, &IpcServer::handlePreviewGenerated); | ||||
| 	connect(&this->previewWorker, &IPCPreviewWorker::finished, this, [this] { this->currentSocket->flush(); }); | ||||
| } | ||||
|  | ||||
| bool IpcServer::startSpawner(QString socketPath) | ||||
| @@ -31,8 +29,6 @@ bool IpcServer::startSpawner(QString socketPath) | ||||
| void IpcServer::spawnerNewConnection() | ||||
| { | ||||
| 	QLocalSocket *socket = this->spawningServer.nextPendingConnection(); | ||||
| 	connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); | ||||
| 	this->currentSocket = socket; | ||||
| 	if(socket != nullptr) | ||||
| 	{ | ||||
| 		if(!socket->waitForReadyRead()) | ||||
| @@ -53,21 +49,22 @@ void IpcServer::spawnerNewConnection() | ||||
| 				stream.startTransaction(); | ||||
| 				stream >> renderConfig >> targets; | ||||
| 			} while(!stream.commitTransaction() && socket->state() == QLocalSocket::ConnectedState); | ||||
|  | ||||
| 			stream << targets.count(); | ||||
| 			socket->flush(); | ||||
| 			previewWorker.start(renderConfig, targets, socket); | ||||
| 			if(socket->state() == QLocalSocket::ConnectedState) | ||||
| 			{ | ||||
| 				stream << targets.count(); | ||||
| 				socket->flush(); | ||||
| 				IPCPreviewWorker *previewWorker = new IPCPreviewWorker(socket); | ||||
| 				connect(previewWorker, &IPCPreviewWorker::finished, this, [previewWorker] { delete previewWorker; }); | ||||
| 				previewWorker->start(renderConfig, targets); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				delete socket; | ||||
| 			} | ||||
| 		} | ||||
| 		if(command == StopGeneratePreviews) | ||||
| 		{ | ||||
| 			previewWorker.stop(); | ||||
| 			/* TODO: implement */ | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IpcServer::handlePreviewGenerated(QByteArray ba) | ||||
| { | ||||
| 	QDataStream stream{this->currentSocket}; | ||||
| 	stream << ba; | ||||
| 	this->currentSocket->flush(); | ||||
| } | ||||
|   | ||||
| @@ -10,13 +10,10 @@ class IpcServer : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
|   private: | ||||
| 	IPCPreviewWorker previewWorker; | ||||
| 	QLocalServer spawningServer; | ||||
| 	QLocalSocket *currentSocket = nullptr; | ||||
| 	SaveFileResult addFile(QString file); | ||||
|   private slots: | ||||
| 	void spawnerNewConnection(); | ||||
| 	void handlePreviewGenerated(QByteArray ba); | ||||
|  | ||||
|   public: | ||||
| 	IpcServer(); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include <QFileDialog> | ||||
| #include <QScreen> | ||||
| #include <QProgressDialog> | ||||
| #include <QDesktopWidget> | ||||
| #include "mainwindow.h" | ||||
| #include "ui_mainwindow.h" | ||||
| #include "clicklabel.h" | ||||
| @@ -647,6 +648,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned | ||||
| 		headerLabel->setText(QString("Path: ") + preview->getDocumentPath()); | ||||
|  | ||||
| 		ClickLabel *label = dynamic_cast<ClickLabel *>(preview->createPreviewWidget()); | ||||
| 		label->setMaximumWidth(QApplication::desktop()->availableGeometry().width() - 200); | ||||
|  | ||||
| 		QVBoxLayout *previewLayout = new QVBoxLayout(); | ||||
|  | ||||
| @@ -678,6 +680,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned | ||||
| 		previewLayout->setMargin(0); | ||||
| 		previewLayout->insertStretch(0, 1); | ||||
| 		previewLayout->insertStretch(-1, 1); | ||||
| 		previewLayout->setAlignment(Qt::AlignCenter); | ||||
| 		QWidget *previewWidget = new QWidget(); | ||||
|  | ||||
| 		previewWidget->setLayout(previewLayout); | ||||
| @@ -806,6 +809,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 	ui->comboPreviewFiles->clear(); | ||||
| 	ui->comboPreviewFiles->addItem("All previews"); | ||||
| 	ui->comboPreviewFiles->setVisible(true); | ||||
| 	ui->lblTotalPreviewPagesCount->setText(""); | ||||
|  | ||||
| 	bool hasDeleted = false; | ||||
| 	QHash<QString, bool> seenMap; | ||||
| @@ -816,7 +820,6 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
|  | ||||
| 		if(!seenMap.contains(absPath)) | ||||
| 		{ | ||||
| 			seenMap[absPath] = true; | ||||
| 			QString fileName = pathInfo.fileName(); | ||||
| 			QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeResultsList); | ||||
|  | ||||
| @@ -830,17 +833,18 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 		bool exists = pathInfo.exists(); | ||||
| 		if(exists) | ||||
| 		{ | ||||
| 			if(!result.wasContentSearch) | ||||
| 			if(result.wasContentSearch) | ||||
| 			{ | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 			if(!pathInfo.suffix().contains("htm")) // hack until we can preview them properly... | ||||
| 			{ | ||||
| 				if(PreviewGenerator::get(pathInfo) != nullptr) | ||||
| 				if(!pathInfo.suffix().contains("htm")) // hack until we can preview them properly... | ||||
| 				{ | ||||
| 					this->previewableSearchResults.append(result); | ||||
| 					ui->comboPreviewFiles->addItem(result.fileData.absPath); | ||||
| 					if(PreviewGenerator::get(pathInfo) != nullptr) | ||||
| 					{ | ||||
| 						this->previewableSearchResults.append(result); | ||||
| 						if(!seenMap.contains(result.fileData.absPath)) | ||||
| 						{ | ||||
| 							ui->comboPreviewFiles->addItem(result.fileData.absPath); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @@ -848,6 +852,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 		{ | ||||
| 			hasDeleted = true; | ||||
| 		} | ||||
| 		seenMap[absPath] = true; | ||||
| 	} | ||||
|  | ||||
| 	ui->treeResultsList->resizeColumnToContents(0); | ||||
| @@ -863,6 +868,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 	} | ||||
|  | ||||
| 	QString statusText = "Results: " + QString::number(results.size()) + " files"; | ||||
| 	statusText += ", previewable: " + QString::number(this->previewableSearchResults.count()); | ||||
| 	if(hasDeleted) | ||||
| 	{ | ||||
| 		statusText += " WARNING: Some files are inaccessible. No preview available for those. Index may be out of sync"; | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include <QMutexLocker> | ||||
| #include <QPainter> | ||||
| #include <QRegularExpression> | ||||
| #include "previewgeneratorpdf.h" | ||||
|  | ||||
| static QMutex cacheMutex; | ||||
| @@ -17,6 +18,7 @@ Poppler::Document *PreviewGeneratorPdf::document(QString path) | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	result->setRenderHint(Poppler::Document::TextAntialiasing); | ||||
|  | ||||
| 	QMutexLocker locker(&cacheMutex); | ||||
| 	documentcache.insert(path, result); | ||||
| 	locker.unlock(); | ||||
| @@ -45,7 +47,12 @@ QSharedPointer<PreviewResult> PreviewGeneratorPdf::generate(RenderConfig config, | ||||
| 	QImage img = pdfPage->renderToImage(config.scaleX, config.scaleY); | ||||
| 	for(QString &word : config.wordsToHighlight) | ||||
| 	{ | ||||
| 		QList<QRectF> rects = pdfPage->search(word, Poppler::Page::SearchFlag::IgnoreCase); | ||||
| 		QList<QRectF> rects = | ||||
| 			pdfPage->search(word, Poppler::Page::SearchFlag::IgnoreCase | Poppler::Page::SearchFlag::WholeWords); | ||||
| 		if(rects.empty()) | ||||
| 		{ | ||||
| 			rects = pdfPage->search(word, Poppler::Page::SearchFlag::IgnoreCase); | ||||
| 		} | ||||
| 		for(QRectF &rect : rects) | ||||
| 		{ | ||||
| 			QPainter painter(&img); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include <QTextStream> | ||||
| #include <QRegularExpression> | ||||
|  | ||||
| #include "previewgeneratorplaintext.h" | ||||
| #include "previewresultplaintext.h" | ||||
| @@ -57,6 +58,7 @@ QString PreviewGeneratorPlainText::generatePreviewText(QString content, RenderCo | ||||
| 		++i; | ||||
| 	} | ||||
|  | ||||
| 	resulText = resulText.toHtmlEscaped(); | ||||
| 	QString header = "<b>" + fileName + "</b> "; | ||||
| 	for(QString &word : config.wordsToHighlight) | ||||
| 	{ | ||||
| @@ -74,10 +76,19 @@ QString PreviewGeneratorPlainText::generatePreviewText(QString content, RenderCo | ||||
| 	return header + resulText.replace("\n", "<br>").mid(0, 1000); | ||||
| } | ||||
|  | ||||
| struct Snippet | ||||
| { | ||||
| 	/* Contains each line number and line of the snippet*/ | ||||
| 	QString snippetText; | ||||
|  | ||||
| 	/* How many times a word occurs in the snippetText */ | ||||
| 	QHash<QString, int> wordCountMap; | ||||
| }; | ||||
|  | ||||
| QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, RenderConfig config, QString fileName) | ||||
| { | ||||
| 	QString resultText; | ||||
| 	const unsigned int contextLinesCount = 2; | ||||
| 	QVector<Snippet> snippets; | ||||
| 	const int contextLinesCount = 2; | ||||
| 	LimitQueue<QString> queue(contextLinesCount); | ||||
| 	QString currentLine; | ||||
| 	currentLine.reserve(512); | ||||
| @@ -85,38 +96,73 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, | ||||
| 	/* How many lines to read after a line with a match (like grep -A ) */ | ||||
| 	int justReadLinesCount = -1; | ||||
|  | ||||
| 	auto appendLine = [&resultText](int lineNumber, QString &line) | ||||
| 	{ resultText.append(QString("<b>%1</b>%2<br>").arg(lineNumber).arg(line)); }; | ||||
| 	struct Snippet currentSnippet; | ||||
|  | ||||
| 	QHash<QString, int> countmap; | ||||
| 	QString header = "<b>" + fileName + "</b> "; | ||||
|  | ||||
| 	unsigned int snippetsCount = 0; | ||||
| 	unsigned int lineCount = 0; | ||||
| 	while(in.readLineInto(¤tLine) && snippetsCount < MAX_SNIPPETS) | ||||
| 	auto appendLine = [¤tSnippet, &config](int lineNumber, QString &line) | ||||
| 	{ | ||||
| 		int foundWordsCount = 0; | ||||
| 		for(QString &word : config.wordsToHighlight) | ||||
| 		{ | ||||
| 			QRegularExpression searchRegex("\\b" + word + "\\b"); | ||||
| 			bool containsRegex = line.contains(searchRegex); | ||||
| 			bool contains = false; | ||||
| 			if(!containsRegex) | ||||
| 			{ | ||||
| 				contains = line.contains(word, Qt::CaseInsensitive); | ||||
| 			} | ||||
| 			if(containsRegex || contains) | ||||
| 			{ | ||||
| 				currentSnippet.wordCountMap[word] = currentSnippet.wordCountMap.value(word, 0) + 1; | ||||
| 				QString replacementString = "<span style=\"background-color: yellow;\">" + word + "</span>"; | ||||
| 				if(containsRegex) | ||||
| 				{ | ||||
| 					line.replace(searchRegex, replacementString); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					line.replace(word, replacementString, Qt::CaseInsensitive); | ||||
| 				} | ||||
| 				++foundWordsCount; | ||||
| 			} | ||||
| 		} | ||||
| 		currentSnippet.snippetText.append(QString("<b>%1</b>%2<br>").arg(lineNumber).arg(line)); | ||||
| 		return foundWordsCount; | ||||
| 	}; | ||||
|  | ||||
| 	unsigned int lineCount = 0; | ||||
| 	while(in.readLineInto(¤tLine)) | ||||
| 	{ | ||||
| 		currentLine = currentLine.toHtmlEscaped(); | ||||
| 		++lineCount; | ||||
| 		bool matched = false; | ||||
| 		if(justReadLinesCount > 0) | ||||
| 		{ | ||||
| 			appendLine(lineCount, currentLine); | ||||
| 			--justReadLinesCount; | ||||
|  | ||||
| 			int result = appendLine(lineCount, currentLine); | ||||
| 			if(justReadLinesCount == 1 && result > 0) | ||||
| 			{ | ||||
| 				justReadLinesCount = contextLinesCount; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				--justReadLinesCount; | ||||
| 			} | ||||
|  | ||||
| 			continue; | ||||
| 		} | ||||
| 		if(justReadLinesCount == 0) | ||||
| 		{ | ||||
| 			resultText += "---<br>"; | ||||
| 			currentSnippet.snippetText += "---<br>"; | ||||
| 			justReadLinesCount = -1; | ||||
| 			++snippetsCount; | ||||
| 			snippets.append(currentSnippet); | ||||
| 			currentSnippet = {}; | ||||
| 		} | ||||
| 		for(QString &word : config.wordsToHighlight) | ||||
| 		{ | ||||
| 			if(currentLine.contains(word, Qt::CaseInsensitive)) | ||||
| 			{ | ||||
| 				countmap[word] = countmap.value(word, 0) + 1; | ||||
| 				matched = true; | ||||
| 				currentLine.replace(word, "<span style=\"background-color: yellow;\">" + word + "</span>", | ||||
| 									Qt::CaseInsensitive); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if(matched) | ||||
| @@ -125,7 +171,6 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, | ||||
| 			{ | ||||
| 				int queuedLineCount = lineCount - queue.size(); | ||||
| 				QString queuedLine = queue.dequeue(); | ||||
|  | ||||
| 				appendLine(queuedLineCount, queuedLine); | ||||
| 			} | ||||
| 			appendLine(lineCount, currentLine); | ||||
| @@ -137,13 +182,77 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(!currentSnippet.snippetText.isEmpty()) | ||||
| 	{ | ||||
| 		currentSnippet.snippetText += "---<br>"; | ||||
| 		snippets.append(currentSnippet); | ||||
| 	} | ||||
|  | ||||
| 	std::sort(snippets.begin(), snippets.end(), | ||||
| 			  [](Snippet &a, Snippet &b) | ||||
| 			  { | ||||
| 				  int differentWordsA = 0; | ||||
| 				  int totalWordsA = 0; | ||||
| 				  int differentWordsB = 0; | ||||
| 				  int totalWordsB = 0; | ||||
| 				  for(int count : a.wordCountMap.values()) | ||||
| 				  { | ||||
| 					  if(count > 0) | ||||
| 					  { | ||||
| 						  ++differentWordsA; | ||||
| 					  } | ||||
| 					  totalWordsA += count; | ||||
| 				  } | ||||
| 				  for(int count : b.wordCountMap.values()) | ||||
| 				  { | ||||
| 					  if(count > 0) | ||||
| 					  { | ||||
| 						  ++differentWordsB; | ||||
| 					  } | ||||
| 					  totalWordsB += count; | ||||
| 				  } | ||||
|  | ||||
| 				  if(differentWordsA > differentWordsB) | ||||
| 				  { | ||||
| 					  return true; | ||||
| 				  } | ||||
| 				  if(differentWordsA == differentWordsB) | ||||
| 				  { | ||||
| 					  return totalWordsA > totalWordsB; | ||||
| 				  } | ||||
| 				  return false; | ||||
| 			  }); | ||||
|  | ||||
| 	QString resultText = ""; | ||||
|  | ||||
| 	unsigned int snippetsCount = 0; | ||||
|  | ||||
| 	QString header = "<b>" + fileName + "</b> "; | ||||
|  | ||||
| 	QHash<QString, int> totalWordCountMap; | ||||
| 	bool isTruncated = false; | ||||
| 	for(Snippet &snippet : snippets) | ||||
| 	{ | ||||
| 		if(snippetsCount++ < MAX_SNIPPETS) | ||||
| 		{ | ||||
| 			resultText += snippet.snippetText; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			isTruncated = true; | ||||
| 		} | ||||
| 		for(auto it = snippet.wordCountMap.keyValueBegin(); it != snippet.wordCountMap.keyValueEnd(); it++) | ||||
| 		{ | ||||
| 			totalWordCountMap[it->first] = totalWordCountMap.value(it->first, 0) + it->second; | ||||
| 		} | ||||
| 	} | ||||
| 	if(isTruncated) | ||||
| 	{ | ||||
| 		header += "(truncated) "; | ||||
| 	} | ||||
| 	for(QString &word : config.wordsToHighlight) | ||||
| 	{ | ||||
| 		header += word + ": " + QString::number(countmap[word]) + " "; | ||||
| 	} | ||||
| 	if(snippetsCount == MAX_SNIPPETS) | ||||
| 	{ | ||||
| 		header += "(truncated)"; | ||||
| 		header += word + ": " + QString::number(totalWordCountMap[word]) + " "; | ||||
| 	} | ||||
| 	header += "<hr>"; | ||||
|  | ||||
|   | ||||
| @@ -6,11 +6,11 @@ template <class T> class LimitQueue | ||||
| { | ||||
|   protected: | ||||
| 	QQueue<T> queue; | ||||
| 	unsigned int limit = 0; | ||||
| 	int limit = 0; | ||||
|  | ||||
|   public: | ||||
| 	LimitQueue(); | ||||
| 	LimitQueue(unsigned int limit) | ||||
| 	LimitQueue(int limit) | ||||
| 	{ | ||||
| 		this->limit = limit; | ||||
| 	} | ||||
| @@ -34,7 +34,7 @@ template <class T> class LimitQueue | ||||
| 		return queue.dequeue(); | ||||
| 	} | ||||
|  | ||||
| 	void setLimit(unsigned int limit) | ||||
| 	void setLimit(int limit) | ||||
| 	{ | ||||
| 		this->limit = limit; | ||||
| 	} | ||||
|   | ||||
		Referens i nytt ärende
	
	Block a user