Bug 262571 - Ktorrent 4.0.5 GUI hangs when scrolling trough long list with selected torrents
Summary: Ktorrent 4.0.5 GUI hangs when scrolling trough long list with selected torrents
Status: RESOLVED FIXED
Alias: None
Product: ktorrent
Classification: Applications
Component: general (show other bugs)
Version: unspecified
Platform: Slackware Linux
: NOR normal
Target Milestone: ---
Assignee: Joris Guisson
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2011-01-08 19:46 UTC by thordn
Modified: 2011-01-18 18:15 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report:


Attachments
infro from four killall -11 ktorrent cases with hung gui. (33.13 KB, text/plain)
2011-01-09 17:27 UTC, thordn
Details
First attempt at a solution (3.48 KB, patch)
2011-01-11 18:35 UTC, Joris Guisson
Details
Info from KDE crashhandler, with patch #5 after killall -11 (12.73 KB, text/plain)
2011-01-16 11:46 UTC, thordn
Details
Info from KDE crashhandler, with patch #7 after killall -11 (7.53 KB, text/plain)
2011-01-16 11:47 UTC, thordn
Details

Note You need to log in before you can comment on or make changes to this bug.
Description thordn 2011-01-08 19:46:58 UTC
Version:           unspecified (using KDE 4.4.3) 
OS:                Linux

Ktorrent 4.0.5 with patches from bug 261903 compiled from sources.

Reproducible: Always

Steps to Reproduce:
Load 4000-5000 torrents. Exit ktorrent. Start ktorrent, sort list on names (maybe not needed). Go to about 10% below top of list, click on torrent to select.
go to about 10% above bottom of list, shift-click to select all torrents in between. Now use scroll-wheel on mouse to quickly scroll through the list. GUI will normally lockup/hang within a minute.

Actual Results:  
Sometimes GUI hangs fore some seconds, sometimes it seems to hang forever (have not waited more than a few hours). 
When terminating the application i sometimes get a segfault error, but otherwise no useful info.

Expected Results:  
GUI should not lock up/hang.

Probably some kind of timing problem, I normally run from another machine (kde 3.5.9) with SSH over a 100 mbit/s ethernet, then it often hangs even when you are scrolling slowly/carefully with only a few 100 selected torrents.

When I do this I often have torrents completing loading and seeding, they are small, typically from 1 k to 1 meg each.
Comment 1 Joris Guisson 2011-01-09 14:40:22 UTC
If it hangs do this:

killall -11 ktorrent

That will trigger a crash, the kde crash dialog should then appear, so I can see where it hangs.

I will do some expirements with a lot of torrents, to see if performance can be improved.
Comment 2 thordn 2011-01-09 17:27:43 UTC
Created attachment 55771 [details]
infro from four killall -11 ktorrent cases with hung gui.

Hi, I am now thinking the problem has to to more with the resorting of the list than the scrolling, only that you do not notice the problem until it wont scroll.

You can also get the hang by selecting torrents as above, then re-sort the list several times by clicking on the various sort-options. (the last two crash-infos were made that way).
Comment 3 Joris Guisson 2011-01-09 19:08:34 UTC
It would appear that getting the selected rows, is the big bottleneck.
Comment 4 Joris Guisson 2011-01-09 19:49:06 UTC
Looking more closely at the backtraces, what is happening is this:

- QM decides a torrent is stalled for too long
- QM decreases it's priority
- This triggers an update in the GUI to update tool buttons and menu items
- The update asks the current selection

My guess is that given the amount of torrents, this scenario is happening many times in a short period of time. Due to the fact that running a lot of torrents at the same time, will probably lead to a lot of stalled torrents. It might be minutes before a torrent actually gets a chance to connect to a peer.
Comment 5 Joris Guisson 2011-01-11 18:35:17 UTC
Created attachment 55877 [details]
First attempt at a solution
Comment 6 Joris Guisson 2011-01-11 18:37:17 UTC
Could you try this patch ? This will postpone the GUI update until the QM is finished. And thus will only do one GUI update.
Comment 7 Joris Guisson 2011-01-15 12:13:32 UTC
commit 09c02baef0bc9a9925367f50e86b44c6c9d2ae70
branch master
Author: Joris <joris.guisson@gmail.com>
Date:   Wed Jan 12 18:35:51 2011 +0100

    Don't update GUI actions when queue is being ordered, improves performance
    
    CCBUG: 262571

diff --git a/ChangeLog b/ChangeLog
index 510bf53..9110e1a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,7 @@ Changes in 4.2:
 
 Changes in 4.1rc1:
 - Make sure that apply button of config dialog is enabled properly when the group changes in the scanfolder settings
+- Don't update GUI actions when queue is being ordered, improves performance (262571)
 
 Changes in 4.1beta1:
 - Remove libktupnp it is now part of libktorrent
diff --git a/ktorrent/core.cpp b/ktorrent/core.cpp
index 45b5a75..4d4cb99 100644
--- a/ktorrent/core.cpp
+++ b/ktorrent/core.cpp
@@ -68,7 +68,12 @@ namespace kt
 {
 	const Uint32 CORE_UPDATE_INTERVAL = 250;
 
-	Core::Core(kt::GUI* gui) : gui(gui),keep_seeding(true),sleep_suppression_cookie(-1),exiting(false)
+	Core::Core(kt::GUI* gui) 
+		: gui(gui),
+		keep_seeding(true),
+		sleep_suppression_cookie(-1),
+		exiting(false),
+		reordering_queue(false)
 	{
 		UpdateCurrentTime();
 		qman = new QueueManager();
@@ -78,6 +83,8 @@ namespace kt
 				this, SLOT(enqueueTorrentOverMaxRatio( bt::TorrentInterface* )));
 		connect(qman, SIGNAL(lowDiskSpace(bt::TorrentInterface*, bool)),
 				this, SLOT(onLowDiskSpace(bt::TorrentInterface*, bool)));
+		connect(qman, SIGNAL(orderingQueue()), this, SLOT(beforeQueueReorder()));
+		connect(qman, SIGNAL(queueOrdered()), this, SLOT(afterQueueReorder()));
 		
 		data_dir = Settings::tempDir().toLocalFile();
 		bool dd_not_exist = !bt::Exists(data_dir);
@@ -118,7 +125,6 @@ namespace kt
 		applySettings();
 		gman->loadGroups();
 		
-		connect(qman,SIGNAL(queueOrdered()),this,SLOT(startUpdateTimer()));
 		connect(magnet,SIGNAL(metadataFound(bt::MagnetLink,QByteArray,bool)),
 				this,SLOT(onMetadataDownloaded(bt::MagnetLink,QByteArray,bool)));
 		
@@ -1062,7 +1068,9 @@ namespace kt
 				magnet->updateMagnetDownloaders();
 				// check if the priority of stalled torrents must be decreased
 				if (Settings::decreasePriorityOfStalledTorrents())
+				{
 					qman->checkStalledTorrents(bt::CurrentTime(),Settings::stallTimer());
+				}
 			}
 		}
 		catch (bt::Error & err)
@@ -1380,7 +1388,20 @@ namespace kt
 	void Core::onStatusChanged(bt::TorrentInterface* tc)
 	{
 		Q_UNUSED(tc);
+		if (!reordering_queue)
+			gui->updateActions();
+	}
+	
+	void Core::beforeQueueReorder()
+	{
+		reordering_queue = true;
+	}
+	
+	void Core::afterQueueReorder()
+	{
+		reordering_queue = false;
 		gui->updateActions();
+		startUpdateTimer();
 	}
 	
 	void Core::load(const bt::MagnetLink& mlink,const QString & group)
diff --git a/ktorrent/core.h b/ktorrent/core.h
index 4a3eb76..0deaf28 100644
--- a/ktorrent/core.h
+++ b/ktorrent/core.h
@@ -246,6 +246,8 @@ namespace kt
 		void checkForKDE3Torrents();
 		void delayedRemove(bt::TorrentInterface* tc);
 		void delayedStart();
+		void beforeQueueReorder();
+		void afterQueueReorder();
 
 	private:
 		GUI* gui;
@@ -262,6 +264,7 @@ namespace kt
 		int sleep_suppression_cookie;
 		QMap<bt::TorrentInterface*,bool> delayed_removal;
 		bool exiting;
+		bool reordering_queue;
 	};
 }
 
diff --git a/libktcore/torrent/queuemanager.cpp b/libktcore/torrent/queuemanager.cpp
index a8197bb..375dde7 100644
--- a/libktcore/torrent/queuemanager.cpp
+++ b/libktcore/torrent/queuemanager.cpp
@@ -547,6 +547,8 @@ namespace kt
 		if (ordering || !downloads.count() || exiting)
 			return;
 		
+		emit orderingQueue();
+		
 		downloads.sort(); // sort downloads, even when suspended so that the QM widget is updated
 		if (Settings::manuallyControlTorrents() || suspended_state)
 		{
diff --git a/libktcore/torrent/queuemanager.h b/libktcore/torrent/queuemanager.h
index ef99585..b8c5ee7 100644
--- a/libktcore/torrent/queuemanager.h
+++ b/libktcore/torrent/queuemanager.h
@@ -255,8 +255,11 @@ namespace kt
 		*/
 		void lowDiskSpace(bt::TorrentInterface* tc, bool stopped);
 		
+		/// Emitted before the queue is reordered
+		void orderingQueue();
+		
 		/**
-		* Emitted when the QM reorders it's queue
+		* Emitted when the QM has reordered it's queue
 		*/
 		void queueOrdered();
Comment 8 thordn 2011-01-15 12:18:07 UTC
Result of testing patch in comment #5 and #6:

Tried it on the 4.0.5 version with the timer update patch in libktorrent.
(4.0.5 because libktorrent-1.1beta wont build on my system)

When patching I got: 
patching file ktorrent/core.cpp
Hunk #3 FAILED at 125.

So tried both variants of it, but no luck, still hangs for hours. (This was on a 4x2.2 GHz machine so its a bit slower than on previous test)
Comment 9 Joris Guisson 2011-01-15 12:19:18 UTC
commit e217541dbc20b6259dabf666d526e67d501073ee
branch 4.1
Author: Joris <joris.guisson@gmail.com>
Date:   Wed Jan 12 18:35:51 2011 +0100

    Merge to 1.1: Don't update GUI actions when queue is being ordered, improves performance
    
    CCBUG: 262571

diff --git a/ChangeLog b/ChangeLog
index 444ee8b..9659fa2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
 Changes in 4.1rc1:
 - Make sure that apply button of config dialog is enabled properly when the group changes in the scanfolder settings
+- Don't update GUI actions when queue is being ordered, improves performance (262571)
 
 Changes in 4.1beta1:
 - Remove libktupnp it is now part of libktorrent
diff --git a/ktorrent/core.cpp b/ktorrent/core.cpp
index e3a4342..9eaedcc 100644
--- a/ktorrent/core.cpp
+++ b/ktorrent/core.cpp
@@ -68,7 +68,12 @@ namespace kt
 {
 	const Uint32 CORE_UPDATE_INTERVAL = 250;
 
-	Core::Core(kt::GUI* gui) : gui(gui),keep_seeding(true),sleep_suppression_cookie(-1),exiting(false)
+	Core::Core(kt::GUI* gui) 
+		: gui(gui),
+		keep_seeding(true),
+		sleep_suppression_cookie(-1),
+		exiting(false),
+		reordering_queue(false)
 	{
 		UpdateCurrentTime();
 		qman = new QueueManager();
@@ -78,6 +83,8 @@ namespace kt
 				this, SLOT(enqueueTorrentOverMaxRatio( bt::TorrentInterface* )));
 		connect(qman, SIGNAL(lowDiskSpace(bt::TorrentInterface*, bool)),
 				this, SLOT(onLowDiskSpace(bt::TorrentInterface*, bool)));
+		connect(qman, SIGNAL(orderingQueue()), this, SLOT(beforeQueueReorder()));
+		connect(qman, SIGNAL(queueOrdered()), this, SLOT(afterQueueReorder()));
 		
 		data_dir = Settings::tempDir().toLocalFile();
 		bool dd_not_exist = !bt::Exists(data_dir);
@@ -118,7 +125,6 @@ namespace kt
 		applySettings();
 		gman->loadGroups();
 		
-		connect(qman,SIGNAL(queueOrdered()),this,SLOT(startUpdateTimer()));
 		connect(magnet,SIGNAL(metadataFound(bt::MagnetLink,QByteArray,bool)),
 				this,SLOT(onMetadataDownloaded(bt::MagnetLink,QByteArray,bool)));
 		
@@ -1062,7 +1068,9 @@ namespace kt
 				magnet->updateMagnetDownloaders();
 				// check if the priority of stalled torrents must be decreased
 				if (Settings::decreasePriorityOfStalledTorrents())
+				{
 					qman->checkStalledTorrents(bt::CurrentTime(),Settings::stallTimer());
+				}
 			}
 		}
 		catch (bt::Error & err)
@@ -1380,7 +1388,20 @@ namespace kt
 	void Core::onStatusChanged(bt::TorrentInterface* tc)
 	{
 		Q_UNUSED(tc);
+		if (!reordering_queue)
+			gui->updateActions();
+	}
+	
+	void Core::beforeQueueReorder()
+	{
+		reordering_queue = true;
+	}
+	
+	void Core::afterQueueReorder()
+	{
+		reordering_queue = false;
 		gui->updateActions();
+		startUpdateTimer();
 	}
 	
 	void Core::load(const bt::MagnetLink& mlink,const QString & group)
diff --git a/ktorrent/core.h b/ktorrent/core.h
index 4a3eb76..0deaf28 100644
--- a/ktorrent/core.h
+++ b/ktorrent/core.h
@@ -246,6 +246,8 @@ namespace kt
 		void checkForKDE3Torrents();
 		void delayedRemove(bt::TorrentInterface* tc);
 		void delayedStart();
+		void beforeQueueReorder();
+		void afterQueueReorder();
 
 	private:
 		GUI* gui;
@@ -262,6 +264,7 @@ namespace kt
 		int sleep_suppression_cookie;
 		QMap<bt::TorrentInterface*,bool> delayed_removal;
 		bool exiting;
+		bool reordering_queue;
 	};
 }
 
diff --git a/libktcore/torrent/queuemanager.cpp b/libktcore/torrent/queuemanager.cpp
index a8197bb..375dde7 100644
--- a/libktcore/torrent/queuemanager.cpp
+++ b/libktcore/torrent/queuemanager.cpp
@@ -547,6 +547,8 @@ namespace kt
 		if (ordering || !downloads.count() || exiting)
 			return;
 		
+		emit orderingQueue();
+		
 		downloads.sort(); // sort downloads, even when suspended so that the QM widget is updated
 		if (Settings::manuallyControlTorrents() || suspended_state)
 		{
diff --git a/libktcore/torrent/queuemanager.h b/libktcore/torrent/queuemanager.h
index ef99585..b8c5ee7 100644
--- a/libktcore/torrent/queuemanager.h
+++ b/libktcore/torrent/queuemanager.h
@@ -255,8 +255,11 @@ namespace kt
 		*/
 		void lowDiskSpace(bt::TorrentInterface* tc, bool stopped);
 		
+		/// Emitted before the queue is reordered
+		void orderingQueue();
+		
 		/**
-		* Emitted when the QM reorders it's queue
+		* Emitted when the QM has reordered it's queue
 		*/
 		void queueOrdered();
Comment 10 thordn 2011-01-16 11:46:23 UTC
Created attachment 56078 [details]
Info from KDE crashhandler, with patch #5 after killall -11
Comment 11 thordn 2011-01-16 11:47:33 UTC
Created attachment 56079 [details]
Info from KDE crashhandler, with patch #7 after killall -11
Comment 12 Joris Guisson 2011-01-18 18:13:23 UTC
commit b60a28c9d0a017d840010c347d633f61d48ba121
branch master
Author: Joris <joris.guisson@gmail.com>
Date:   Tue Jan 18 18:08:18 2011 +0100

    Improve performance of ktorrent in situations where there are many torrents:
    - Log viewer no longer chokes on many log messages
    - Load torrents earlier to prevent unnecessary gui updates
    - Make GUI updates more efficient
    - Don't sort when appending torrents to QM, seeing that orderQueue does that anyway
    - Do not load all discovered torrents in one go in scanfolder plugin
    
    BUG: 262571

diff --git a/ChangeLog b/ChangeLog
index a035c48..9d27535 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,8 +3,8 @@ Changes in 4.2:
 
 Changes in 4.1rc1:
 - Make sure that apply button of config dialog is enabled properly when the group changes in the scanfolder settings
-- Don't update GUI actions when queue is being ordered, improves performance (262571)
 - Add kio-magnet
+- Improve performance of ktorrent in situations where there are many torrents (262571)
 
 Changes in 4.1beta1:
 - Remove libktupnp it is now part of libktorrent
diff --git a/ktorrent/gui.cpp b/ktorrent/gui.cpp
index 14d842f..8fd67bf 100644
--- a/ktorrent/gui.cpp
+++ b/ktorrent/gui.cpp
@@ -88,6 +88,8 @@ namespace kt
 		part_manager = new KParts::PartManager(this);
 		connect(part_manager,SIGNAL(activePartChanged(KParts::Part*)),this,SLOT(activePartChanged(KParts::Part*)));
 		core = new Core(this);
+		core->loadTorrents();
+		
 		tray_icon = new TrayIcon(core,this);
 		
 		central = new CentralWidget(this);
@@ -104,7 +106,7 @@ namespace kt
 		setStatusBar(status_bar);
 
 		//Marker mark("core->loadTorrents()");
-		core->loadTorrents();
+		//core->loadTorrents();
 	
 		//mark.update();
 		connect(&timer,SIGNAL(timeout()),this,SLOT(update()));
diff --git a/ktorrent/view/view.cpp b/ktorrent/view/view.cpp
index 74b88c9..fc5e2a0 100644
--- a/ktorrent/view/view.cpp
+++ b/ktorrent/view/view.cpp
@@ -145,27 +145,34 @@ namespace kt
 				QAbstractItemView::update(idx);
 		}
 	}
-
-	bool View::needToUpdateCaption()
+	
+	struct RunningCounter
 	{
-		Uint32 torrents = 0;
-		Uint32 running = 0;
-		QList<bt::TorrentInterface*> all;
-		model->allTorrents(all);
-		foreach (bt::TorrentInterface* ti,all)
+	public:
+		Uint32 torrents;
+		Uint32 running;
+		
+		RunningCounter() : torrents(0),running(0)
+		{}
+		
+		bool operator()(bt::TorrentInterface* tc)
 		{
-			if (!group || (group && group->isMember(ti)))
-			{
-				torrents++;
-				if (ti->getStats().running)
-					running++;
-			}
+			torrents++;
+			if (tc->getStats().running)
+				running++;
+			return true;
 		}
+	};
+
+	bool View::needToUpdateCaption()
+	{
+		RunningCounter rc;
+		model->visit(rc);
 
-		if (num_running != running || num_torrents != torrents)
+		if (num_running != rc.running || num_torrents != rc.torrents)
 		{
-			num_running = running;
-			num_torrents = torrents;
+			num_running = rc.running;
+			num_torrents = rc.torrents;
 			return true;
 		}
 		
diff --git a/ktorrent/view/viewmanager.cpp b/ktorrent/view/viewmanager.cpp
index 249a571..5dadd85 100644
--- a/ktorrent/view/viewmanager.cpp
+++ b/ktorrent/view/viewmanager.cpp
@@ -411,6 +411,28 @@ namespace kt
 			return kt::JobTracker::createJobWidget(job);
 	}
 
+	struct StartAndStopAllVisitor
+	{
+		QAction* start_all;
+		QAction* stop_all;
+		
+		StartAndStopAllVisitor(QAction* start_all,QAction* stop_all) : start_all(start_all),stop_all(stop_all)
+		{}
+		
+		bool operator()(bt::TorrentInterface* tc)
+		{
+			if (tc->getJobQueue()->runningJobs())
+				return true;
+			
+			const TorrentStats & s = tc->getStats();
+			if (s.running || (tc->isAllowedToStart() && !tc->overMaxRatio() && !tc->overMaxSeedTime()))
+				stop_all->setEnabled(true);
+			else
+				start_all->setEnabled(true);
+			
+			return !stop_all->isEnabled() || !start_all->isEnabled();
+		}
+	};
 	
 	void ViewManager::updateActions()
 	{
@@ -517,24 +539,10 @@ namespace kt
 		
 		if (qm_enabled)
 		{
-			QList<bt::TorrentInterface*> all;
-			current->viewModel()->allTorrents(all);
 			start_all->setEnabled(false);
 			stop_all->setEnabled(false);
-			foreach (bt::TorrentInterface* tc,all)
-			{
-				if (tc->getJobQueue()->runningJobs())
-					continue;
-				
-				const TorrentStats & s = tc->getStats();
-				if (s.running || (tc->isAllowedToStart() && !tc->overMaxRatio() && !tc->overMaxSeedTime()))
-					stop_all->setEnabled(true);
-				else
-					start_all->setEnabled(true);
-				
-				if (stop_all->isEnabled() && start_all->isEnabled())
-					break;
-			}
+			StartAndStopAllVisitor v(start_all,stop_all);
+			current->viewModel()->visit(v);
 		}
 		else
 		{
diff --git a/ktorrent/view/viewmodel.h b/ktorrent/view/viewmodel.h
index dcfc036..71b305c 100644
--- a/ktorrent/view/viewmodel.h
+++ b/ktorrent/view/viewmodel.h
@@ -119,6 +119,20 @@ namespace kt
 		 */
 		void allTorrents(QList<bt::TorrentInterface*> & tlist) const;
 		
+		/**
+		 * Visit all visible torrents in the model, and apply an action to them
+		 */
+		template<class Action>
+		void visit(Action & a)
+		{
+			foreach (Item* item,torrents)
+			{
+				if (item->member(group))
+					if (!a(item->tc))
+						break;
+			}
+		}
+		
 		/// Get the list of indexes which need to be updated
 		const QModelIndexList & updateList() const {return update_list;}
 		
diff --git a/libktcore/torrent/queuemanager.cpp b/libktcore/torrent/queuemanager.cpp
index 375dde7..813e711 100644
--- a/libktcore/torrent/queuemanager.cpp
+++ b/libktcore/torrent/queuemanager.cpp
@@ -67,7 +67,6 @@ namespace kt
 	void QueueManager::append(bt::TorrentInterface* tc)
 	{
 		downloads.append(tc);
-		downloads.sort();
 		connect(tc, SIGNAL(diskSpaceLow(bt::TorrentInterface*, bool)), this, SLOT(onLowDiskSpace(bt::TorrentInterface*, bool)));	
 		connect(tc, SIGNAL(torrentStopped(bt::TorrentInterface*)), this, SLOT(torrentStopped(bt::TorrentInterface*)));
 		connect(tc,SIGNAL(updateQueue()),this,SLOT(orderQueue()));
diff --git a/plugins/logviewer/logviewer.cpp b/plugins/logviewer/logviewer.cpp
index 6b6e0c3..f8d08bf 100644
--- a/plugins/logviewer/logviewer.cpp
+++ b/plugins/logviewer/logviewer.cpp
@@ -31,22 +31,8 @@
 
 namespace kt
 {
-	const int LOG_EVENT_TYPE = 65432;
-	
-	class LogEvent : public QEvent
-	{
-		QString str;
-	public:
-		LogEvent(const QString & str) : QEvent((QEvent::Type)LOG_EVENT_TYPE),str(str)
-		{}
-		
-		virtual ~LogEvent()
-		{}
-		
-		const QString & msg() const {return str;}
-	};
 
-	LogViewer::LogViewer(LogFlags* flags,QWidget *parent) : Activity(i18n("Log"),"utilities-log-viewer",100,parent),use_rich_text(true),flags(flags),suspended(false),menu(0)
+	LogViewer::LogViewer(LogFlags* flags,QWidget *parent) : Activity(i18n("Log"),"utilities-log-viewer",100,parent),use_rich_text(true),flags(flags),suspended(false),menu(0),max_block_count(200)
 	{
 		setToolTip(i18n("View the logging output generated by KTorrent")); 
 		QVBoxLayout* layout = new QVBoxLayout(this);
@@ -54,7 +40,7 @@ namespace kt
 		layout->setMargin(0);
 		layout->setSpacing(0);
 		layout->addWidget(output);
-		output->document()->setMaximumBlockCount(200);
+		output->document()->setMaximumBlockCount(max_block_count);
 		output->setContextMenuPolicy(Qt::CustomContextMenu);
 		connect(output,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(showMenu(QPoint)));
 		
@@ -76,34 +62,34 @@ namespace kt
 			to add strings to it, this will ensure that strings will only be added in the main application
 			thread.
 		*/
-		if(arg==0x00 || flags->checkFlags(arg))
+		if (arg==0x00 || flags->checkFlags(arg))
 		{
-			if(use_rich_text)
+			QMutexLocker lock(&mutex);
+			if (use_rich_text)
 			{
-				QString tmp = line;
-				LogEvent* le = new LogEvent(flags->getFormattedMessage(arg, tmp));
-				QApplication::postEvent(this,le);
+				pending.append(flags->getFormattedMessage(arg, line));
 			}
 			else
 			{
-				LogEvent* le = new LogEvent(line);
-				QApplication::postEvent(this,le);
+				pending.append(line);
 			}
+			
+			while (pending.size() > max_block_count)
+				pending.pop_front();
 		}
 	}
 	
-	void LogViewer::customEvent(QEvent* ev)
+	void LogViewer::processPending()
 	{
-		if (ev->type() == LOG_EVENT_TYPE)
+		QMutexLocker lock(&mutex);
+		foreach (const QString & line,pending)
 		{
-			LogEvent* le = (LogEvent*)ev;
-			if (!suspended)
-			{
-				QTextCharFormat fm = output->currentCharFormat();
-				output->append(le->msg());
-				output->setCurrentCharFormat(fm);
-			}
+			QTextCharFormat fm = output->currentCharFormat();
+			output->append(line);
+			output->setCurrentCharFormat(fm);
 		}
+		
+		pending.clear();
 	}
 	
 	void LogViewer::setRichText(bool val)
@@ -113,6 +99,7 @@ namespace kt
 	
 	void LogViewer::setMaxBlockCount(int max)
 	{
+		max_block_count = max;
 		output->document()->setMaximumBlockCount(max);
 	}
 
diff --git a/plugins/logviewer/logviewer.h b/plugins/logviewer/logviewer.h
index c1efd87..377426f 100644
--- a/plugins/logviewer/logviewer.h
+++ b/plugins/logviewer/logviewer.h
@@ -21,10 +21,12 @@
 #define KTLOGVIEWER_H
 
 #include <QTextBrowser>
+#include <QMutex>
 #include <interfaces/activity.h>
 #include <interfaces/logmonitorinterface.h>
 #include "logflags.h"
 
+
 namespace kt
 {
 	/**
@@ -38,10 +40,10 @@ namespace kt
 		virtual ~LogViewer();
 
 		virtual void message(const QString& line, unsigned int arg);
-		virtual void customEvent(QEvent* ev);
 		
 		void setRichText(bool val);
 		void setMaxBlockCount(int max);
+		void processPending();
 		
 	public slots:
 		void showMenu(const QPoint & pos);
@@ -54,6 +56,10 @@ namespace kt
 		bool suspended;
 		QMenu* menu;
 		QAction* suspend_action;
+		int max_block_count;
+		
+		QMutex mutex;
+		QStringList pending;
 	};
 
 }
diff --git a/plugins/logviewer/logviewerplugin.cpp b/plugins/logviewer/logviewerplugin.cpp
index b2d35dd..5c3e35c 100644
--- a/plugins/logviewer/logviewerplugin.cpp
+++ b/plugins/logviewer/logviewerplugin.cpp
@@ -144,7 +144,11 @@ namespace kt
 		}
 	}
 
-
+	void LogViewerPlugin::guiUpdate()
+	{
+		if (lv)
+			lv->processPending();
+	}
 
 	bool LogViewerPlugin::versionCheck(const QString & version) const
 	{
diff --git a/plugins/logviewer/logviewerplugin.h b/plugins/logviewer/logviewerplugin.h
index 4599de2..cd84d5a 100644
--- a/plugins/logviewer/logviewerplugin.h
+++ b/plugins/logviewer/logviewerplugin.h
@@ -51,6 +51,7 @@ namespace kt
 		virtual void load();
 		virtual void unload();
 		virtual bool versionCheck(const QString& version) const;
+		virtual void guiUpdate();
 		
 	private slots:
 		void applySettings();
diff --git a/plugins/scanfolder/scanfolder.cpp b/plugins/scanfolder/scanfolder.cpp
index 141b29d..b2f7f49 100644
--- a/plugins/scanfolder/scanfolder.cpp
+++ b/plugins/scanfolder/scanfolder.cpp
@@ -139,23 +139,44 @@ namespace kt
 					m_incomplePollingTimer.start(10000);
 				}
 			}
-			else
+			else if (!m_to_load.contains(source) && !m_pendingURLs.contains(source))
 			{
 				bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : found " << source << endl;
-				//Add pending entry...
-				m_pendingURLs.push_back(source);
-				
-				QString group;
-				if (ScanFolderPluginSettings::addToGroup())
-					group = ScanFolderPluginSettings::group();
-				//Load torrent
-				if (ScanFolderPluginSettings::openSilently())
-					m_core->loadSilently(source,group);
-				else
-					m_core->load(source,group);
+				m_to_load.append(source);
 			}
 		}
+		
+		loadDelayed();
+	}
+	
+	void ScanFolder::loadDelayed()
+	{
+		// Don't load to many in a row
+		int loaded = 0;
+		KUrl::List::iterator i = m_to_load.begin();
+		while (i != m_to_load.end() && loaded < 10)
+		{
+			KUrl source = *i;
+			//Add pending entry...
+			m_pendingURLs.push_back(source);
+			QString group;
+			if (ScanFolderPluginSettings::addToGroup())
+				group = ScanFolderPluginSettings::group();
+		
+			//Load torrent
+			if (ScanFolderPluginSettings::openSilently())
+				m_core->loadSilently(source,group);
+			else
+				m_core->load(source,group);
+				
+			i = m_to_load.erase(i);
+			loaded++;
+		}
+		
+		if (!m_to_load.isEmpty())
+			QTimer::singleShot(2000, this, SLOT(loadDelayed()));
 	}
+
 	
 	void ScanFolder::onLoadingFinished(const KUrl & url, bool success, bool canceled)
 	{
@@ -195,7 +216,7 @@ namespace kt
 					QFile::remove(dirname + "." + name);
 
 				// NetAccess considered harmfull !!!
-				KIO::file_move(url, destination);
+				KIO::file_move(url, destination, -1, KIO::HideProgressInfo);
 				break;
 			case defaultAction:
 				QFile f(dirname + "." + name);
diff --git a/plugins/scanfolder/scanfolder.h b/plugins/scanfolder/scanfolder.h
index b811c7e..c161954 100644
--- a/plugins/scanfolder/scanfolder.h
+++ b/plugins/scanfolder/scanfolder.h
@@ -55,48 +55,50 @@ namespace kt
 	*/
 	class ScanFolder : public QObject
 	{
-			Q_OBJECT
-		public:
-			
-			/**
-			 * Default constructor.
-			 * @param core Pointer to core interface
-			 * @param dir Full directory path
-			 * @param action Action to perform on loaded torrents.
-			 */
-			ScanFolder(CoreInterface* core, const QString& dir, LoadedTorrentAction action = defaultAction);
-			~ScanFolder();
+		Q_OBJECT
+	public:
+		
+		/**
+			* Default constructor.
+			* @param core Pointer to core interface
+			* @param dir Full directory path
+			* @param action Action to perform on loaded torrents.
+			*/
+		ScanFolder(CoreInterface* core, const QString& dir, LoadedTorrentAction action = defaultAction);
+		~ScanFolder();
 
 
-			///Accessor method for m_loadedAction.
-			void setLoadedAction(const LoadedTorrentAction& theValue);
-			///Accessor method for m_loadedAction.
-			LoadedTorrentAction loadedAction() const { return m_loadedAction; }
+		///Accessor method for m_loadedAction.
+		void setLoadedAction(const LoadedTorrentAction& theValue);
+		///Accessor method for m_loadedAction.
+		LoadedTorrentAction loadedAction() const { return m_loadedAction; }
 
-			///Returns true if this object is valid, that is - weather directory is valid and this object does its work.
-			bool isValid() const { return m_valid; }
-			
-			///Sets directory path
-			void setFolderUrl(QString& url);
+		///Returns true if this object is valid, that is - weather directory is valid and this object does its work.
+		bool isValid() const { return m_valid; }
+		
+		///Sets directory path
+		void setFolderUrl(QString& url);
 
-		public slots:
-			void onNewItems(const KFileItemList &items);
-			void onLoadingFinished(const KUrl & url,bool success,bool canceled);
-			void onIncompletePollingTimeout();
-			
-		private:
-			/// Check if the URL is a complete file
-			bool incomplete(const KUrl & src);
+	public slots:
+		void onNewItems(const KFileItemList &items);
+		void onLoadingFinished(const KUrl & url,bool success,bool canceled);
+		void onIncompletePollingTimeout();
+		void loadDelayed();
+		
+	private:
+		/// Check if the URL is a complete file
+		bool incomplete(const KUrl & src);
 
-		private:
-			QString m_root_dir;
-			CoreInterface* m_core;
-			bool m_valid;
-			KDirLister* m_dir;
-			LoadedTorrentAction m_loadedAction;
-			QList<KUrl> m_pendingURLs;
-			QList<KUrl> m_incompleteURLs;
-			QTimer m_incomplePollingTimer;
+	private:
+		QString m_root_dir;
+		CoreInterface* m_core;
+		bool m_valid;
+		KDirLister* m_dir;
+		LoadedTorrentAction m_loadedAction;
+		KUrl::List m_to_load;
+		KUrl::List m_pendingURLs;
+		KUrl::List m_incompleteURLs;
+		QTimer m_incomplePollingTimer;
 	};
 }
 #endif
Comment 13 Joris Guisson 2011-01-18 18:15:00 UTC
commit a3399fa89ee372aaa55c217b45ebdb827f55f42b
branch 4.1
Author: Joris <joris.guisson@gmail.com>
Date:   Tue Jan 18 18:09:48 2011 +0100

    Improve performance of ktorrent in situations where there are many torrents:
    - Log viewer no longer chokes on many log messages
    - Load torrents earlier to prevent unnecessary gui updates
    - Make GUI updates more efficient
    - Don't sort when appending torrents to QM, seeing that orderQueue does that anyway
    - Do not load all discovered torrents in one go in scanfolder plugin
    
    CCBUG: 262571
    
    Conflicts:
    
    	ChangeLog

diff --git a/ChangeLog b/ChangeLog
index 444ee8b..1455529 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
 Changes in 4.1rc1:
 - Make sure that apply button of config dialog is enabled properly when the group changes in the scanfolder settings
+- Add kio-magnet
+- Improve performance of ktorrent in situations where there are many torrents (262571)
 
 Changes in 4.1beta1:
 - Remove libktupnp it is now part of libktorrent
diff --git a/ktorrent/gui.cpp b/ktorrent/gui.cpp
index 14d842f..8fd67bf 100644
--- a/ktorrent/gui.cpp
+++ b/ktorrent/gui.cpp
@@ -88,6 +88,8 @@ namespace kt
 		part_manager = new KParts::PartManager(this);
 		connect(part_manager,SIGNAL(activePartChanged(KParts::Part*)),this,SLOT(activePartChanged(KParts::Part*)));
 		core = new Core(this);
+		core->loadTorrents();
+		
 		tray_icon = new TrayIcon(core,this);
 		
 		central = new CentralWidget(this);
@@ -104,7 +106,7 @@ namespace kt
 		setStatusBar(status_bar);
 
 		//Marker mark("core->loadTorrents()");
-		core->loadTorrents();
+		//core->loadTorrents();
 	
 		//mark.update();
 		connect(&timer,SIGNAL(timeout()),this,SLOT(update()));
diff --git a/ktorrent/view/view.cpp b/ktorrent/view/view.cpp
index 74b88c9..fc5e2a0 100644
--- a/ktorrent/view/view.cpp
+++ b/ktorrent/view/view.cpp
@@ -145,27 +145,34 @@ namespace kt
 				QAbstractItemView::update(idx);
 		}
 	}
-
-	bool View::needToUpdateCaption()
+	
+	struct RunningCounter
 	{
-		Uint32 torrents = 0;
-		Uint32 running = 0;
-		QList<bt::TorrentInterface*> all;
-		model->allTorrents(all);
-		foreach (bt::TorrentInterface* ti,all)
+	public:
+		Uint32 torrents;
+		Uint32 running;
+		
+		RunningCounter() : torrents(0),running(0)
+		{}
+		
+		bool operator()(bt::TorrentInterface* tc)
 		{
-			if (!group || (group && group->isMember(ti)))
-			{
-				torrents++;
-				if (ti->getStats().running)
-					running++;
-			}
+			torrents++;
+			if (tc->getStats().running)
+				running++;
+			return true;
 		}
+	};
+
+	bool View::needToUpdateCaption()
+	{
+		RunningCounter rc;
+		model->visit(rc);
 
-		if (num_running != running || num_torrents != torrents)
+		if (num_running != rc.running || num_torrents != rc.torrents)
 		{
-			num_running = running;
-			num_torrents = torrents;
+			num_running = rc.running;
+			num_torrents = rc.torrents;
 			return true;
 		}
 		
diff --git a/ktorrent/view/viewmanager.cpp b/ktorrent/view/viewmanager.cpp
index 249a571..5dadd85 100644
--- a/ktorrent/view/viewmanager.cpp
+++ b/ktorrent/view/viewmanager.cpp
@@ -411,6 +411,28 @@ namespace kt
 			return kt::JobTracker::createJobWidget(job);
 	}
 
+	struct StartAndStopAllVisitor
+	{
+		QAction* start_all;
+		QAction* stop_all;
+		
+		StartAndStopAllVisitor(QAction* start_all,QAction* stop_all) : start_all(start_all),stop_all(stop_all)
+		{}
+		
+		bool operator()(bt::TorrentInterface* tc)
+		{
+			if (tc->getJobQueue()->runningJobs())
+				return true;
+			
+			const TorrentStats & s = tc->getStats();
+			if (s.running || (tc->isAllowedToStart() && !tc->overMaxRatio() && !tc->overMaxSeedTime()))
+				stop_all->setEnabled(true);
+			else
+				start_all->setEnabled(true);
+			
+			return !stop_all->isEnabled() || !start_all->isEnabled();
+		}
+	};
 	
 	void ViewManager::updateActions()
 	{
@@ -517,24 +539,10 @@ namespace kt
 		
 		if (qm_enabled)
 		{
-			QList<bt::TorrentInterface*> all;
-			current->viewModel()->allTorrents(all);
 			start_all->setEnabled(false);
 			stop_all->setEnabled(false);
-			foreach (bt::TorrentInterface* tc,all)
-			{
-				if (tc->getJobQueue()->runningJobs())
-					continue;
-				
-				const TorrentStats & s = tc->getStats();
-				if (s.running || (tc->isAllowedToStart() && !tc->overMaxRatio() && !tc->overMaxSeedTime()))
-					stop_all->setEnabled(true);
-				else
-					start_all->setEnabled(true);
-				
-				if (stop_all->isEnabled() && start_all->isEnabled())
-					break;
-			}
+			StartAndStopAllVisitor v(start_all,stop_all);
+			current->viewModel()->visit(v);
 		}
 		else
 		{
diff --git a/ktorrent/view/viewmodel.h b/ktorrent/view/viewmodel.h
index dcfc036..71b305c 100644
--- a/ktorrent/view/viewmodel.h
+++ b/ktorrent/view/viewmodel.h
@@ -119,6 +119,20 @@ namespace kt
 		 */
 		void allTorrents(QList<bt::TorrentInterface*> & tlist) const;
 		
+		/**
+		 * Visit all visible torrents in the model, and apply an action to them
+		 */
+		template<class Action>
+		void visit(Action & a)
+		{
+			foreach (Item* item,torrents)
+			{
+				if (item->member(group))
+					if (!a(item->tc))
+						break;
+			}
+		}
+		
 		/// Get the list of indexes which need to be updated
 		const QModelIndexList & updateList() const {return update_list;}
 		
diff --git a/libktcore/torrent/queuemanager.cpp b/libktcore/torrent/queuemanager.cpp
index a8197bb..bb0a629 100644
--- a/libktcore/torrent/queuemanager.cpp
+++ b/libktcore/torrent/queuemanager.cpp
@@ -67,7 +67,6 @@ namespace kt
 	void QueueManager::append(bt::TorrentInterface* tc)
 	{
 		downloads.append(tc);
-		downloads.sort();
 		connect(tc, SIGNAL(diskSpaceLow(bt::TorrentInterface*, bool)), this, SLOT(onLowDiskSpace(bt::TorrentInterface*, bool)));	
 		connect(tc, SIGNAL(torrentStopped(bt::TorrentInterface*)), this, SLOT(torrentStopped(bt::TorrentInterface*)));
 		connect(tc,SIGNAL(updateQueue()),this,SLOT(orderQueue()));
diff --git a/plugins/logviewer/logviewer.cpp b/plugins/logviewer/logviewer.cpp
index 6b6e0c3..f8d08bf 100644
--- a/plugins/logviewer/logviewer.cpp
+++ b/plugins/logviewer/logviewer.cpp
@@ -31,22 +31,8 @@
 
 namespace kt
 {
-	const int LOG_EVENT_TYPE = 65432;
-	
-	class LogEvent : public QEvent
-	{
-		QString str;
-	public:
-		LogEvent(const QString & str) : QEvent((QEvent::Type)LOG_EVENT_TYPE),str(str)
-		{}
-		
-		virtual ~LogEvent()
-		{}
-		
-		const QString & msg() const {return str;}
-	};
 
-	LogViewer::LogViewer(LogFlags* flags,QWidget *parent) : Activity(i18n("Log"),"utilities-log-viewer",100,parent),use_rich_text(true),flags(flags),suspended(false),menu(0)
+	LogViewer::LogViewer(LogFlags* flags,QWidget *parent) : Activity(i18n("Log"),"utilities-log-viewer",100,parent),use_rich_text(true),flags(flags),suspended(false),menu(0),max_block_count(200)
 	{
 		setToolTip(i18n("View the logging output generated by KTorrent")); 
 		QVBoxLayout* layout = new QVBoxLayout(this);
@@ -54,7 +40,7 @@ namespace kt
 		layout->setMargin(0);
 		layout->setSpacing(0);
 		layout->addWidget(output);
-		output->document()->setMaximumBlockCount(200);
+		output->document()->setMaximumBlockCount(max_block_count);
 		output->setContextMenuPolicy(Qt::CustomContextMenu);
 		connect(output,SIGNAL(customContextMenuRequested(QPoint)),this,SLOT(showMenu(QPoint)));
 		
@@ -76,34 +62,34 @@ namespace kt
 			to add strings to it, this will ensure that strings will only be added in the main application
 			thread.
 		*/
-		if(arg==0x00 || flags->checkFlags(arg))
+		if (arg==0x00 || flags->checkFlags(arg))
 		{
-			if(use_rich_text)
+			QMutexLocker lock(&mutex);
+			if (use_rich_text)
 			{
-				QString tmp = line;
-				LogEvent* le = new LogEvent(flags->getFormattedMessage(arg, tmp));
-				QApplication::postEvent(this,le);
+				pending.append(flags->getFormattedMessage(arg, line));
 			}
 			else
 			{
-				LogEvent* le = new LogEvent(line);
-				QApplication::postEvent(this,le);
+				pending.append(line);
 			}
+			
+			while (pending.size() > max_block_count)
+				pending.pop_front();
 		}
 	}
 	
-	void LogViewer::customEvent(QEvent* ev)
+	void LogViewer::processPending()
 	{
-		if (ev->type() == LOG_EVENT_TYPE)
+		QMutexLocker lock(&mutex);
+		foreach (const QString & line,pending)
 		{
-			LogEvent* le = (LogEvent*)ev;
-			if (!suspended)
-			{
-				QTextCharFormat fm = output->currentCharFormat();
-				output->append(le->msg());
-				output->setCurrentCharFormat(fm);
-			}
+			QTextCharFormat fm = output->currentCharFormat();
+			output->append(line);
+			output->setCurrentCharFormat(fm);
 		}
+		
+		pending.clear();
 	}
 	
 	void LogViewer::setRichText(bool val)
@@ -113,6 +99,7 @@ namespace kt
 	
 	void LogViewer::setMaxBlockCount(int max)
 	{
+		max_block_count = max;
 		output->document()->setMaximumBlockCount(max);
 	}
 
diff --git a/plugins/logviewer/logviewer.h b/plugins/logviewer/logviewer.h
index c1efd87..377426f 100644
--- a/plugins/logviewer/logviewer.h
+++ b/plugins/logviewer/logviewer.h
@@ -21,10 +21,12 @@
 #define KTLOGVIEWER_H
 
 #include <QTextBrowser>
+#include <QMutex>
 #include <interfaces/activity.h>
 #include <interfaces/logmonitorinterface.h>
 #include "logflags.h"
 
+
 namespace kt
 {
 	/**
@@ -38,10 +40,10 @@ namespace kt
 		virtual ~LogViewer();
 
 		virtual void message(const QString& line, unsigned int arg);
-		virtual void customEvent(QEvent* ev);
 		
 		void setRichText(bool val);
 		void setMaxBlockCount(int max);
+		void processPending();
 		
 	public slots:
 		void showMenu(const QPoint & pos);
@@ -54,6 +56,10 @@ namespace kt
 		bool suspended;
 		QMenu* menu;
 		QAction* suspend_action;
+		int max_block_count;
+		
+		QMutex mutex;
+		QStringList pending;
 	};
 
 }
diff --git a/plugins/logviewer/logviewerplugin.cpp b/plugins/logviewer/logviewerplugin.cpp
index b2d35dd..5c3e35c 100644
--- a/plugins/logviewer/logviewerplugin.cpp
+++ b/plugins/logviewer/logviewerplugin.cpp
@@ -144,7 +144,11 @@ namespace kt
 		}
 	}
 
-
+	void LogViewerPlugin::guiUpdate()
+	{
+		if (lv)
+			lv->processPending();
+	}
 
 	bool LogViewerPlugin::versionCheck(const QString & version) const
 	{
diff --git a/plugins/logviewer/logviewerplugin.h b/plugins/logviewer/logviewerplugin.h
index 4599de2..cd84d5a 100644
--- a/plugins/logviewer/logviewerplugin.h
+++ b/plugins/logviewer/logviewerplugin.h
@@ -51,6 +51,7 @@ namespace kt
 		virtual void load();
 		virtual void unload();
 		virtual bool versionCheck(const QString& version) const;
+		virtual void guiUpdate();
 		
 	private slots:
 		void applySettings();
diff --git a/plugins/scanfolder/scanfolder.cpp b/plugins/scanfolder/scanfolder.cpp
index 141b29d..b2f7f49 100644
--- a/plugins/scanfolder/scanfolder.cpp
+++ b/plugins/scanfolder/scanfolder.cpp
@@ -139,23 +139,44 @@ namespace kt
 					m_incomplePollingTimer.start(10000);
 				}
 			}
-			else
+			else if (!m_to_load.contains(source) && !m_pendingURLs.contains(source))
 			{
 				bt::Out(SYS_SNF|LOG_NOTICE) << "ScanFolder : found " << source << endl;
-				//Add pending entry...
-				m_pendingURLs.push_back(source);
-				
-				QString group;
-				if (ScanFolderPluginSettings::addToGroup())
-					group = ScanFolderPluginSettings::group();
-				//Load torrent
-				if (ScanFolderPluginSettings::openSilently())
-					m_core->loadSilently(source,group);
-				else
-					m_core->load(source,group);
+				m_to_load.append(source);
 			}
 		}
+		
+		loadDelayed();
+	}
+	
+	void ScanFolder::loadDelayed()
+	{
+		// Don't load to many in a row
+		int loaded = 0;
+		KUrl::List::iterator i = m_to_load.begin();
+		while (i != m_to_load.end() && loaded < 10)
+		{
+			KUrl source = *i;
+			//Add pending entry...
+			m_pendingURLs.push_back(source);
+			QString group;
+			if (ScanFolderPluginSettings::addToGroup())
+				group = ScanFolderPluginSettings::group();
+		
+			//Load torrent
+			if (ScanFolderPluginSettings::openSilently())
+				m_core->loadSilently(source,group);
+			else
+				m_core->load(source,group);
+				
+			i = m_to_load.erase(i);
+			loaded++;
+		}
+		
+		if (!m_to_load.isEmpty())
+			QTimer::singleShot(2000, this, SLOT(loadDelayed()));
 	}
+
 	
 	void ScanFolder::onLoadingFinished(const KUrl & url, bool success, bool canceled)
 	{
@@ -195,7 +216,7 @@ namespace kt
 					QFile::remove(dirname + "." + name);
 
 				// NetAccess considered harmfull !!!
-				KIO::file_move(url, destination);
+				KIO::file_move(url, destination, -1, KIO::HideProgressInfo);
 				break;
 			case defaultAction:
 				QFile f(dirname + "." + name);
diff --git a/plugins/scanfolder/scanfolder.h b/plugins/scanfolder/scanfolder.h
index b811c7e..c161954 100644
--- a/plugins/scanfolder/scanfolder.h
+++ b/plugins/scanfolder/scanfolder.h
@@ -55,48 +55,50 @@ namespace kt
 	*/
 	class ScanFolder : public QObject
 	{
-			Q_OBJECT
-		public:
-			
-			/**
-			 * Default constructor.
-			 * @param core Pointer to core interface
-			 * @param dir Full directory path
-			 * @param action Action to perform on loaded torrents.
-			 */
-			ScanFolder(CoreInterface* core, const QString& dir, LoadedTorrentAction action = defaultAction);
-			~ScanFolder();
+		Q_OBJECT
+	public:
+		
+		/**
+			* Default constructor.
+			* @param core Pointer to core interface
+			* @param dir Full directory path
+			* @param action Action to perform on loaded torrents.
+			*/
+		ScanFolder(CoreInterface* core, const QString& dir, LoadedTorrentAction action = defaultAction);
+		~ScanFolder();
 
 
-			///Accessor method for m_loadedAction.
-			void setLoadedAction(const LoadedTorrentAction& theValue);
-			///Accessor method for m_loadedAction.
-			LoadedTorrentAction loadedAction() const { return m_loadedAction; }
+		///Accessor method for m_loadedAction.
+		void setLoadedAction(const LoadedTorrentAction& theValue);
+		///Accessor method for m_loadedAction.
+		LoadedTorrentAction loadedAction() const { return m_loadedAction; }
 
-			///Returns true if this object is valid, that is - weather directory is valid and this object does its work.
-			bool isValid() const { return m_valid; }
-			
-			///Sets directory path
-			void setFolderUrl(QString& url);
+		///Returns true if this object is valid, that is - weather directory is valid and this object does its work.
+		bool isValid() const { return m_valid; }
+		
+		///Sets directory path
+		void setFolderUrl(QString& url);
 
-		public slots:
-			void onNewItems(const KFileItemList &items);
-			void onLoadingFinished(const KUrl & url,bool success,bool canceled);
-			void onIncompletePollingTimeout();
-			
-		private:
-			/// Check if the URL is a complete file
-			bool incomplete(const KUrl & src);
+	public slots:
+		void onNewItems(const KFileItemList &items);
+		void onLoadingFinished(const KUrl & url,bool success,bool canceled);
+		void onIncompletePollingTimeout();
+		void loadDelayed();
+		
+	private:
+		/// Check if the URL is a complete file
+		bool incomplete(const KUrl & src);
 
-		private:
-			QString m_root_dir;
-			CoreInterface* m_core;
-			bool m_valid;
-			KDirLister* m_dir;
-			LoadedTorrentAction m_loadedAction;
-			QList<KUrl> m_pendingURLs;
-			QList<KUrl> m_incompleteURLs;
-			QTimer m_incomplePollingTimer;
+	private:
+		QString m_root_dir;
+		CoreInterface* m_core;
+		bool m_valid;
+		KDirLister* m_dir;
+		LoadedTorrentAction m_loadedAction;
+		KUrl::List m_to_load;
+		KUrl::List m_pendingURLs;
+		KUrl::List m_incompleteURLs;
+		QTimer m_incomplePollingTimer;
 	};
 }
 #endif