/***************************************************************************
 *   Copyright (C) 2003 by S�astien Laot                                 *
 *   slaout@linux62.org                                                    *
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.             *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <tqstring.h>
#include <kurl.h>
#include <tqpixmap.h>
#include <tqcolor.h>
#include <tqregexp.h>
#include <kcolordrag.h>
#include <kurldrag.h>
#include <tqstylesheet.h>
#include <tqdir.h>
#include <kmimetype.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <kdesktopfile.h>
#include <tdeapplication.h>
#include <tdeaboutdata.h>
#include <tqfile.h>
#include <tdefilemetainfo.h>
#include <tdeio/jobclasses.h>
#include <tqtextcodec.h>
#include <kopenwith.h>
#include <tdefiledialog.h>
#include <kicondialog.h>
#include <kiconloader.h>
#include <tqfileinfo.h>
#include <tdepopupmenu.h>
#include <kstandarddirs.h>
#include <kurifilter.h>

#include <iostream>

#include "basket.h"
#include "note.h"
#include "notefactory.h"
#include "notedrag.h"
#include "linklabel.h"
#include "global.h"
#include "settings.h"
#include "keyboard.h"
#include "variouswidgets.h"
#include "tools.h"

#include "debugwindow.h"

/** Create notes from scratch (just a content) */

Note* NoteFactory::createNoteText(const TQString &text, Basket *parent, bool reallyPlainText/* = false*/)
{
	Note *note = new Note(parent);
	if (reallyPlainText) {
		TextContent *content = new TextContent(note, createFileForNewNote(parent, "txt"));
		content->setText(text);
		content->saveToFile();
	} else {
		HtmlContent *content = new HtmlContent(note, createFileForNewNote(parent, "html"));
		TQString html = "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>" + Tools::textToHTMLWithoutP(text) + "</body></html>";
		content->setHtml(html);
		content->saveToFile();
	}
	return note;
}

Note* NoteFactory::createNoteHtml(const TQString &html, Basket *parent)
{
	Note *note = new Note(parent);
	HtmlContent *content = new HtmlContent(note, createFileForNewNote(parent, "html"));
	content->setHtml(html);
	content->saveToFile();
	return note;
}

Note* NoteFactory::createNoteLink(const KURL &url, Basket *parent)
{
	Note *note = new Note(parent);
	new LinkContent(note, url, titleForURL(url), iconForURL(url), /*autoTitle=*/true, /*autoIcon=*/true);
	return note;
}

Note* NoteFactory::createNoteLink(const KURL &url, const TQString &title, Basket *parent)
{
	Note *note = new Note(parent);
	new LinkContent(note, url, title, iconForURL(url), /*autoTitle=*/false, /*autoIcon=*/true);
	return note;
}

Note* NoteFactory::createNoteImage(const TQPixmap &image, Basket *parent)
{
	Note *note = new Note(parent);
	ImageContent *content = new ImageContent(note, createFileForNewNote(parent, "png"));
	content->setPixmap(image);
	content->saveToFile();
	return note;
}

Note* NoteFactory::createNoteColor(const TQColor &color, Basket *parent)
{
	Note *note = new Note(parent);
	new ColorContent(note, color);
	return note;
}

/** Return a string list containing {url1, title1, url2, title2, url3, title3...}
 */
TQStringList NoteFactory::textToURLList(const TQString &text)
{
	// List to return:
	TQStringList list;

	// Split lines:
	TQStringList texts = TQStringList::split('\n', text);

	// For each lines:
	TQStringList::iterator it;
	for (it = texts.begin(); it != texts.end(); ++it) {
		// Strip white spaces:
		(*it) = (*it).stripWhiteSpace();

		// Don't care of empty entries:
		if ((*it).isEmpty())
			continue;

		// Compute lower case equivalent:
		TQString ltext = (*it).lower();

		/* Search for mail address ("*@*.*" ; "*" can contain '_', '-', or '.') and add protocol to it */
		TQString mailExpString = "[\\w-\\.]+@[\\w-\\.]+\\.[\\w]+";
		TQRegExp mailExp("^"+mailExpString+"$");
		if (mailExp.search(ltext) != -1) {
			ltext.insert(0, "mailto:");
			(*it).insert(0, "mailto:");
		}

		// TODO: Recognize "<link>" (link between '<' and '>')
		// TODO: Replace " at " by "@" and " dot " by "." to look for e-mail addresses

		/* Search for mail address like "Name <address@provider.net>" */
		TQRegExp namedMailExp("^([\\w\\s]+)\\s<("+mailExpString+")>$");
		//namedMailExp.setCaseSensitive(true); // For the name to be keeped with uppercases // DOESN'T WORK !
		if (namedMailExp.search(ltext) != -1) {
			TQString name    = namedMailExp.cap(1);
			TQString address = "mailto:" + namedMailExp.cap(2);
			// Threat it NOW, as it's an exception (it have a title):
			list.append(address);
			list.append(name);
			continue;
		}

		/* Search for an url and create an URL note */
		if ( ltext.startsWith("/") && ltext[1] != '/' && ltext[1] != '*' || // Take files but not C/C++/... comments !
				   ltext.startsWith("file:")    ||
				   ltext.startsWith("http://")  ||
				   ltext.startsWith("https://") ||
				   ltext.startsWith("www.")     ||
				   ltext.startsWith("ftp.")     ||
				   ltext.startsWith("ftp://")   ||
				   ltext.startsWith("mailto:")    ) {

			// First, correct the text to use the good format for the url
			if (ltext.startsWith( "/"))
				(*it).insert(0, "file:");
			if (ltext.startsWith("www."))
				(*it).insert(0, "http://");
			if (ltext.startsWith("ftp."))
				(*it).insert(0, "ftp://");

			// And create the Url note (or launcher if URL point a .desktop file)
			list.append(*it);
			list.append(""); // We don't have any title
				   } else
					   return TQStringList(); // FAILED: treat the text as a text, and not as a URL list!
	}
	return list;
}

Note* NoteFactory::createNoteFromText(const TQString &text, Basket *parent)
{
	/* Search for a color (#RGB , #RRGGBB , #RRRGGGBBB , #RRRRGGGGBBBB) and create a color note */
	TQRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
	if ( exp.search(text) != -1 )
		return createNoteColor(TQColor(text), parent);

	/* Try to convert the text as a URL or a list of URLs */
	TQStringList uriList = textToURLList(text);
	if ( ! uriList.isEmpty() ) {
		// TODO: This code is almost duplicated from fropURLs()!
		Note *note;
		Note *firstNote = 0;
		Note *lastInserted = 0;
		TQStringList::iterator it;
		for (it = uriList.begin(); it != uriList.end(); ++it) {
			TQString url = (*it);
			++it;
			TQString title = (*it);
			if (title.isEmpty())
				note = createNoteLinkOrLauncher(KURL(url), parent);
			else
				note = createNoteLink(KURL(url), title, parent);

			// If we got a new note, insert it in a linked list (we will return the first note of that list):
			if (note) {
//				std::cout << "Drop URL: " << (*it).prettyURL() << std::endl;
				if (!firstNote)
					firstNote = note;
				else {
					lastInserted->setNext(note);
					note->setPrev(lastInserted);
				}
				lastInserted = note;
			}

		}
		return firstNote; // It don't return ALL inserted notes !
	}

	//TQString newText = text.stripWhiteSpace(); // The text for a new note, without useless spaces
	/* Else, it's a text or an HTML note, so, create it */
	if (TQStyleSheet::mightBeRichText(/*newT*/text))
		return createNoteHtml(/*newT*/text, parent);
	else
		return createNoteText(/*newT*/text, parent);
}

Note* NoteFactory::createNoteLauncher(const KURL &url, Basket *parent)
{
	if (url.isEmpty())
		return createNoteLauncher("", "", "", parent);
	else
		return copyFileAndLoad(url, parent);
}

Note* NoteFactory::createNoteLauncher(const TQString &command, const TQString &name, const TQString &icon, Basket *parent)
{
	TQString fileName = createNoteLauncherFile(command, name, icon, parent);
	if (fileName.isEmpty())
		return 0L;
	else
		return loadFile(fileName, parent);
}

TQString NoteFactory::createNoteLauncherFile(const TQString &command, const TQString &name, const TQString &icon, Basket *parent)
{
	TQString content = TQString(
			"[Desktop Entry]\n"
			"Exec=%1\n"
			"Name=%2\n"
			"Icon=%3\n"
			"Encoding=UTF-8\n"
			"Type=Application\n").arg(command, name, icon.isEmpty() ? TQString("application-x-executable") : icon);
	TQString fileName = fileNameForNewNote(parent, "launcher.desktop");
	TQString fullPath = parent->fullPathForFileName(fileName);
//	parent->dontCareOfCreation(fullPath);
	TQFile file(fullPath);
	if ( file.open(IO_WriteOnly) ) {
		TQTextStream stream(&file);
		stream.setEncoding(TQTextStream::UnicodeUTF8);
		stream << content;
		file.close();
		return fileName;
	} else
		return TQString();
}

Note* NoteFactory::createNoteLinkOrLauncher(const KURL &url, Basket *parent)
{
	// IMPORTANT: we create the service ONLY if the extension is ".desktop".
	//            Otherwise, KService take a long time to analyse all the file
	//            and output such things to stdout:
	//            "Invalid entry (missing '=') at /my/file.ogg:11984"
	//            "Invalid entry (missing ']') at /my/file.ogg:11984"...
	KService::Ptr service;
	if (url.fileName().endsWith(".desktop"))
		service = new KService(url.path());

	// If link point to a .desktop file then add a launcher, otherwise it's a link
	if (service && service->isValid())
		return createNoteLauncher(url, parent);
	else
		return createNoteLink(url, parent);
}

#include <tqstrlist.h>
#include <tqimage.h>

bool NoteFactory::movingNotesInTheSameBasket(TQMimeSource *source, Basket *parent, TQDropEvent::Action action)
{
	if (NoteDrag::canDecode(source))
		return action == TQDropEvent::Move && NoteDrag::basketOf(source) == parent;
	else
		return false;
}

Note* NoteFactory::dropNote(TQMimeSource *source, Basket *parent, bool fromDrop, TQDropEvent::Action action, Note */*noteSource*/)
{
	Note *note = 0L;

	/* No data */
	if (source->format(0) == 0L) {
		// TODO: add a parameter to say if it's from a clipboard paste, a selection paste, or a drop
		//       To be able to say "The clipboard/selection/drop is empty".
//		KMessageBox::error(parent, i18n("There is no data to insert."), i18n("No Data"));
		return 0;
	}

	/* Debug */
	if (Global::debugWindow) {
		*Global::debugWindow << "<b>Drop :</b>";
		for (int i = 0; source->format(i); ++i)
			if ( *(source->format(i)) )
				*Global::debugWindow << "\t[" + TQString::number(i) + "] " + TQString(source->format(i));
		switch (action) { // The source want that we:
			case TQDropEvent::Copy:       *Global::debugWindow << ">> Drop action: Copy";       break;
			case TQDropEvent::Move:       *Global::debugWindow << ">> Drop action: Move";       break;
			case TQDropEvent::Link:       *Global::debugWindow << ">> Drop action: Link";       break;
			case TQDropEvent::Private:    *Global::debugWindow << ">> Drop action: Private";    break; // What is it? (Copy?)
			case TQDropEvent::UserAction: *Global::debugWindow << ">> Drop action: UserAction"; break; // Not currently
			default:                     *Global::debugWindow << ">> Drop action: Unknown";           //  supported by TQt!
		}
	}

	/* Copy or move a Note */
	if (NoteDrag::canDecode(source)) {
		bool moveFiles = fromDrop && action == TQDropEvent::Move;
		bool moveNotes = moveFiles;
		return NoteDrag::decode(source, parent, moveFiles, moveNotes); // Filename will be kept
	}

	/* Else : Drop object to note */

	TQPixmap pixmap;
	if ( TQImageDrag::decode(source, pixmap) )
		return createNoteImage(pixmap, parent);

	// KColorDrag::decode() is buggy and can trheat strings like "#include <foo.h>" as a black color
	// The correct "ideal" code:
	/*TQColor color;
	if ( KColorDrag::decode(source, color) ) {
	createNoteColor(color, parent);
	return;
}*/
	// And then the hack (if provide color MIME type or a text that contains color), using createNote Color RegExp:
	TQString hack;
	TQRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
	if (source->provides("application/x-color") || (TQTextDrag::decode(source, hack) && (exp.search(hack) != -1)) ) {
		TQColor color;
		if (KColorDrag::decode(source, color))
			return createNoteColor(color, parent);
//			if ( (note = createNoteColor(color, parent)) )
//				return note;
//			// Theorically it should be returned. If not, continue by dropping other things
	}

	KURL::List urls;
	if ( KURLDrag::decode(source, urls) ) {
		// If it's a Paste, we should know if files should be copied (copy&paste) or moved (cut&paste):
		if (!fromDrop && Tools::isAFileCut(source))
			action = TQDropEvent::Move;
		return dropURLs(urls, parent, action, fromDrop);
	}

	// FIXME: use dropURLs() also from Mozilla?

	/*
	* Mozilla's stuff sometimes uses utf-16-le - little-endian UTF-16.
	*
	* This has the property that for the ASCII subset case (And indeed, the
	* ISO-8859-1 subset, I think), if you treat it as a C-style string,
	* it'll come out to one character long in most cases, since it looks
	 * like:
	*
	* "<\0H\0T\0M\0L\0>\0"
	*
	* A strlen() call on that will give you 1, which simply isn't correct.
	* That might, I suppose, be the answer, or something close.
	*
	* Also, Mozilla's drag/drop code predates the use of MIME types in XDnD
	* - hence it'll throw about STRING and UTF8_STRING quite happily, hence
	* the odd named types.
	*
	* Thanks to Dave Cridland for having said me that.
	*/
	if (source->provides("text/x-moz-url")) { // FOR MOZILLA
		// Get the array and create a TQChar array of 1/2 of the size
		TQByteArray mozilla = source->encodedData("text/x-moz-url");
		TQMemArray<TQChar> chars( mozilla.count() / 2 );
		// A small debug work to know the value of each bytes
		if (Global::debugWindow)
			for (uint i = 0; i < mozilla.count(); i++)
				*Global::debugWindow << TQString("'") + TQChar(mozilla[i]) + "' " + TQString::number(int(mozilla[i]));
		// text/x-moz-url give the URL followed by the link title and separed by OxOA (10 decimal: new line?)
		uint size   = 0;
		TQChar *name = 0L;
		// For each little endian mozilla chars, copy it to the array of TQChars
		for (uint i = 0; i < mozilla.count(); i += 2) {
			chars[i/2] = TQChar(mozilla[i], mozilla[i+1]);
			if (mozilla[i] == 0x0A) {
				size = i/2;
				name = &(chars[i/2+1]);
			}
		}
		// Create a TQString that take the address of the first TQChar and a length
		if (name == 0L) { // We haven't found name (FIXME: Is it possible ?)
			TQString normalHtml(&(chars[0]), chars.size());
			return createNoteLink(normalHtml, parent);
		} else {
			TQString normalHtml(  &(chars[0]), size               );
			TQString normalTitle( name,        chars.size()-size-1);
			return createNoteLink(normalHtml, normalTitle, parent);
		}
	}

	if (source->provides("text/html")) {
		TQString html;
		TQCString subtype("html");
		// If the text/html comes from Mozilla or GNOME it can be UTF-16 encoded: we need ExtendedTextDrag to check that
		ExtendedTextDrag::decode(source, html, subtype);
		return createNoteHtml(html, parent);
	}

	TQString text;
	// If the text/plain comes from GEdit or GNOME it can be empty: we need ExtendedTextDrag to check other MIME types
	if ( ExtendedTextDrag::decode(source, text) )
		return createNoteFromText(text, parent);

	/* Unsucceful drop */
	note = createNoteUnknown(source, parent);
	TQString message = i18n("<p>%1 doesn't support the data you've dropped.<br>"
			"It however created a generic note, allowing you to drag or copy it to an application that understand it.</p>").arg(kapp->aboutData()->programName());
	KMessageBox::information(parent, message, i18n("Unsupported MIME Type(s)"),
							 "unsupportedDropInfo", KMessageBox::AllowLink);
	return note;
}

Note* NoteFactory::createNoteUnknown(TQMimeSource *source, Basket *parent/*, const TQString &annotations*/)
{
	// Save the MimeSource in a file: create and open the file:
	TQString fileName = createFileForNewNote(parent, "unknown");
	TQFile file(parent->fullPath() + fileName);
	if ( ! file.open(IO_WriteOnly) )
		return 0L;
	TQDataStream stream(&file);

	// Echo MIME types:
	for (int i = 0; source->format(i); ++i)
		if ( *(source->format(i)) )
			stream << TQString(source->format(i)); // Output the '\0'-terminated format name string

	// Echo end of MIME types list delimiter:
	stream << "";

	// Echo the length (in bytes) and then the data, and then same for next MIME type:
	for (int i = 0; source->format(i); ++i)
		if ( *(source->format(i)) ) {
		TQByteArray data = source->encodedData(source->format(i));
		stream << (TQ_UINT32)data.count();
		stream.writeRawBytes(data.data(), data.count());
		}
		file.close();

		Note *note = new Note(parent);
		new UnknownContent(note, fileName);
		return note;
}

Note* NoteFactory::dropURLs(KURL::List urls, Basket *parent, TQDropEvent::Action action, bool fromDrop)
{
	int  shouldAsk    = 0; // shouldAsk==0: don't ask ; shouldAsk==1: ask for "file" ; shouldAsk>=2: ask for "files"
	bool shiftPressed = Keyboard::shiftPressed();
	bool ctrlPressed  = Keyboard::controlPressed();
	bool modified     = fromDrop && (shiftPressed || ctrlPressed);

	if (modified) // Then no menu + modified action
		; // action is already set: no work to do
	else if (fromDrop) { // Compute if user should be asked or not
		for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it )
			if ((*it).protocol() != "mailto") { // Do not ask when dropping mail address :-)
			shouldAsk++;
			if (shouldAsk == 1/*2*/) // Sufficient
				break;
			}
			if (shouldAsk) {
				TDEPopupMenu menu(parent);
				menu.insertItem( SmallIconSet("goto"),     i18n("&Move Here\tShift"),      0 );
				menu.insertItem( SmallIconSet("edit-copy"), i18n("&Copy Here\tCtrl"),       1 );
				menu.insertItem( SmallIconSet("www"),      i18n("&Link Here\tCtrl+Shift"), 2 );
				menu.insertSeparator();
				menu.insertItem( SmallIconSet("cancel"),   i18n("C&ancel\tEscape"),        3 );
				int id = menu.exec(TQCursor::pos());
				switch (id) {
					case 0: action = TQDropEvent::Move; break;
					case 1: action = TQDropEvent::Copy; break;
					case 2: action = TQDropEvent::Link; break;
					default: return 0;
				}
				modified = true;
			}
	} else { // fromPaste
		;
	}

	/* Policy of drops of URL:
	*   Email: [Modifier keys: Useless]
	+    - Link mail address
	*   Remote URL: [Modifier keys: {Copy,Link}]
	+    - Download as Image, Animation and Launcher
	+    - Link other URLs
	*   Local URL: [Modifier keys: {Copy,Move,Link}]
	*    - Copy as Image, Animation and Launcher [Modifier keys: {Copy,Move,Link}]
	*    - Link folder [Modifier keys: Useless]
	*    - Make Launcher of executable [Modifier keys: {Copy_exec,Move_exec,Link_Launcher}]
	*    - Ask for file (if use want to copy and it is a sound: make Sound)
	* Policy of pastes of URL: [NO modifier keys]
	*   - Same as drops
	*   - But copy when ask should be done
	*   - Unless cut-selection is true: move files instead
	* Policy of file created in the basket dir: [NO modifier keys]
	*   - View as Image, Animation, Sound, Launcher
	*   - View as File
	*/
	Note *note;
	Note *firstNote = 0;
	Note *lastInserted = 0;
	for (KURL::List::iterator it = urls.begin(); it != urls.end(); ++it) {
		if ( ((*it).protocol() == "mailto") ||
					  (action == TQDropEvent::Link)    )
			note = createNoteLinkOrLauncher(*it, parent);
		else if (!(*it).isLocalFile()) {
			if ( action != TQDropEvent::Link && (maybeImageOrAnimation(*it)/* || maybeSound(*it)*/) )
				note = copyFileAndLoad(*it, parent);
			else
				note = createNoteLinkOrLauncher(*it, parent);
		} else {
			if (action == TQDropEvent::Copy)
				note = copyFileAndLoad(*it, parent);
			else if (action == TQDropEvent::Move)
				note = moveFileAndLoad(*it, parent);
			else
				note = createNoteLinkOrLauncher(*it, parent);
		}

		// If we got a new note, insert it in a linked list (we will return the first note of that list):
		if (note) {
			DEBUG_WIN << "Drop URL: " + (*it).prettyURL();
			if (!firstNote)
				firstNote = note;
			else {
				lastInserted->setNext(note);
				note->setPrev(lastInserted);
			}
			lastInserted = note;
		}
	}
	return firstNote;
}

void NoteFactory::consumeContent(TQDataStream &stream, NoteType::Id type)
{
	if (type == NoteType::Link) {
		KURL url;
		TQString title, icon;
		TQ_UINT64 autoTitle64, autoIcon64;
		stream >> url >> title >> icon >> autoTitle64 >> autoIcon64;
	} else if (type == NoteType::Color) {
		TQColor color;
		stream >> color;
	}
}

Note* NoteFactory::decodeContent(TQDataStream &stream, NoteType::Id type, Basket *parent)
{
/*	if (type == NoteType::Text) {
	TQString text;
	stream >> text;
	return NoteFactory::createNoteText(text, parent);
} else if (type == NoteType::Html) {
	TQString html;
	stream >> html;
	return NoteFactory::createNoteHtml(html, parent);
} else if (type == NoteType::Image) {
	TQPixmap pixmap;
	stream >> pixmap;
	return NoteFactory::createNoteImage(pixmap, parent);
} else */
	if (type == NoteType::Link) {
		KURL url;
		TQString title, icon;
		TQ_UINT64 autoTitle64, autoIcon64;
		bool autoTitle, autoIcon;
		stream >> url >> title >> icon >> autoTitle64 >> autoIcon64;
		autoTitle = (bool)autoTitle64;
		autoIcon  = (bool)autoIcon64;
		Note *note = NoteFactory::createNoteLink(url, parent);
		((LinkContent*)(note->content()))->setLink(url, title, icon, autoTitle, autoIcon);
		return note;
	} else if (type == NoteType::Color) {
		TQColor color;
		stream >> color;
		return NoteFactory::createNoteColor(color, parent);
	} else
		return 0; // NoteFactory::loadFile() is sufficient
}

// mayBeLauncher: url.url().endsWith(".desktop");

bool NoteFactory::maybeText(const KURL &url)
{
	TQString path = url.url().lower();
	return path.endsWith(".txt");
}

bool NoteFactory::maybeHtml(const KURL &url)
{
	TQString path = url.url().lower();
	return path.endsWith(".html") || path.endsWith(".htm");
}

bool NoteFactory::maybeImageOrAnimation(const KURL &url)
{
	/* Examples on my machine:
	TQImageDrag can understands
	{"image/png", "image/bmp", "image/jpeg", "image/pgm", "image/ppm", "image/xbm", "image/xpm"}
	TQImageIO::inputFormats() returns
	{"BMP", "GIF", "JPEG", "MNG", "PBM", "PGM", "PNG", "PPM", "XBM", "XPM"}
		TQImageDecoder::inputFormats():
	{"GIF", "MNG", "PNG"} */
	TQStrList list = TQImageIO::inputFormats();
	list.prepend("jpg"); // Since TQImageDrag return only "JPEG" and extensions can be "JPG"; preprend for heuristic optim.
	char *s;
	TQString path = url.url().lower();
	for (s = list.first(); s; s = list.next())
		if (path.endsWith(TQString(".") + TQString(s).lower()))
			return true;
	// TODO: Search real MIME type for local files?
	return false;
}

bool NoteFactory::maybeAnimation(const KURL &url)
{
	TQString path = url.url().lower();
	return path.endsWith(".mng") || path.endsWith(".gif");
}

bool NoteFactory::maybeSound(const KURL &url)
{
	TQString path = url.url().lower();
	return path.endsWith(".mp3") || path.endsWith(".ogg");
}

bool NoteFactory::maybeLauncher(const KURL &url)
{
	TQString path = url.url().lower();
	return path.endsWith(".desktop");
}

////////////// NEW:

Note* NoteFactory::copyFileAndLoad(const KURL &url, Basket *parent)
{
	TQString fileName = fileNameForNewNote(parent, url.fileName());
	TQString fullPath = parent->fullPathForFileName(fileName);

	if (Global::debugWindow)
		*Global::debugWindow << "copyFileAndLoad: " + url.prettyURL() + " to " + fullPath;

//	TQString annotations = i18n("Original file: %1").arg(url.prettyURL());
//	parent->dontCareOfCreation(fullPath);


//	TDEIO::CopyJob *copyJob = TDEIO::copy(url, KURL(fullPath));
//	parent->connect( copyJob,  TQ_SIGNAL(copyingDone(TDEIO::Job *, const KURL &, const KURL &, bool, bool)),
//					 parent, TQ_SLOT(slotCopyingDone(TDEIO::Job *, const KURL &, const KURL &, bool, bool)) );

	TDEIO::FileCopyJob *copyJob = new TDEIO::FileCopyJob(
			url, KURL(fullPath), 0666, /*move=*/false,
			/*overwrite=*/true, /*resume=*/true, /*showProgress=*/true );
	parent->connect( copyJob,  TQ_SIGNAL(result(TDEIO::Job *)),
					 parent, TQ_SLOT(slotCopyingDone2(TDEIO::Job *)) );


	NoteType::Id type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
	return loadFile(fileName, type, parent);
}

Note* NoteFactory::moveFileAndLoad(const KURL &url, Basket *parent)
{
	// Globally the same as copyFileAndLoad() but move instead of copy (TDEIO::move())
	TQString fileName = fileNameForNewNote(parent, url.fileName());
	TQString fullPath = parent->fullPathForFileName(fileName);

	if (Global::debugWindow)
		*Global::debugWindow << "moveFileAndLoad: " + url.prettyURL() + " to " + fullPath;

//	TQString annotations = i18n("Original file: %1").arg(url.prettyURL());
//	parent->dontCareOfCreation(fullPath);


//	TDEIO::CopyJob *copyJob = TDEIO::move(url, KURL(fullPath));
//	parent->connect( copyJob,  TQ_SIGNAL(copyingDone(TDEIO::Job *, const KURL &, const KURL &, bool, bool)),
//					 parent, TQ_SLOT(slotCopyingDone(TDEIO::Job *, const KURL &, const KURL &, bool, bool)) );

	TDEIO::FileCopyJob *copyJob = new TDEIO::FileCopyJob(
			url, KURL(fullPath), 0666, /*move=*/true,
			/*overwrite=*/true, /*resume=*/true, /*showProgress=*/true );
	parent->connect( copyJob,  TQ_SIGNAL(result(TDEIO::Job *)),
					 parent, TQ_SLOT(slotCopyingDone2(TDEIO::Job *)) );


	NoteType::Id type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
	return loadFile(fileName, type, parent);
}

Note* NoteFactory::loadFile(const TQString &fileName, Basket *parent)
{
	// The file MUST exists
	TQFileInfo file( KURL(parent->fullPathForFileName(fileName)).path() );
	if ( ! file.exists() )
		return 0L;

	NoteType::Id type = typeForURL(parent->fullPathForFileName(fileName), parent);
	Note *note = loadFile(fileName, type, parent);
	return note;
}

Note* NoteFactory::loadFile(const TQString &fileName, NoteType::Id type, Basket *parent)
{
	Note *note = new Note(parent);
	switch (type) {
		case NoteType::Text:      new TextContent(      note, fileName ); break;
		case NoteType::Html:      new HtmlContent(      note, fileName ); break;
		case NoteType::Image:     new ImageContent(     note, fileName ); break;
		case NoteType::Animation: new AnimationContent( note, fileName ); break;
		case NoteType::Sound:     new SoundContent(     note, fileName ); break;
		case NoteType::File:      new FileContent(      note, fileName ); break;
		case NoteType::Launcher:  new LauncherContent(  note, fileName ); break;
		case NoteType::Unknown:   new UnknownContent(   note, fileName ); break;

		default:
		case NoteType::Link:
		case NoteType::Color:
			return 0;
	}

	return note;
}

NoteType::Id NoteFactory::typeForURL(const KURL &url, Basket */*parent*/)
{
/*	KMimeType::Ptr kMimeType = KMimeType::findByURL(url);
	if (Global::debugWindow)
		*Global::debugWindow << "typeForURL: " + kMimeType->parentMimeType();//property("MimeType").toString();*/
	bool viewText  = Settings::viewTextFileContent();
	bool viewHTML  = Settings::viewHtmlFileContent();
	bool viewImage = Settings::viewImageFileContent();
	bool viewSound = Settings::viewSoundFileContent();

	KFileMetaInfo metaInfo(url);
	if (Global::debugWindow && metaInfo.isEmpty())
		*Global::debugWindow << "typeForURL: metaInfo is empty for " + url.prettyURL();
	if (metaInfo.isEmpty()) { // metaInfo is empty for GIF files on my machine !
		if      (viewText  && maybeText(url))             return NoteType::Text;
		else if (viewHTML  && (maybeHtml(url)))           return NoteType::Html;
		else if (viewImage && maybeAnimation(url))        return NoteType::Animation; // See Note::movieStatus(int)
		else if (viewImage && maybeImageOrAnimation(url)) return NoteType::Image;     //  for more explanations
		else if (viewSound && maybeSound(url))            return NoteType::Sound;
		else if (maybeLauncher(url))                      return NoteType::Launcher;
		else                                              return NoteType::File;
	}
	TQString mimeType = metaInfo.mimeType();

	if (Global::debugWindow)
		*Global::debugWindow << "typeForURL: " + url.prettyURL() + " ; MIME type = " + mimeType;

	if      (mimeType == "application/x-desktop")            return NoteType::Launcher;
	else if (viewText  && mimeType.startsWith("text/plain")) return NoteType::Text;
	else if (viewHTML  && mimeType.startsWith("text/html"))  return NoteType::Html;
	else if (viewImage && mimeType == "movie/x-mng")         return NoteType::Animation;
	else if (viewImage && mimeType == "image/gif")           return NoteType::Animation;
	else if (viewImage && mimeType.startsWith("image/"))     return NoteType::Image;
	else if (viewSound && mimeType.startsWith("audio/"))     return NoteType::Sound;
	else                                                     return NoteType::File;
}

TQString NoteFactory::fileNameForNewNote(Basket *parent, const TQString &wantedName)
{
	return Tools::fileNameForNewFile(wantedName, parent->fullPath());
}

// Create a file to store a new note in Basket parent and with extension extension.
// If wantedName is provided, the function will first try to use this file name, or derive it if it's impossible
//  (extension willn't be used for that case)
TQString NoteFactory::createFileForNewNote(Basket *parent, const TQString &extension, const TQString &wantedName)
{
	static int nb = 1;

	TQString fileName;
	TQString fullName;

	if (wantedName.isEmpty()) { // TODO: fileNameForNewNote(parent, "note1."+extension);
		TQDir dir;
		for (/*int nb = 1*/; ; ++nb) { // TODO: FIXME: If overflow ???
			fileName = "note" + TQString::number(nb)/*.rightJustify(5, '0')*/ + "." + extension;
			fullName = parent->fullPath() + fileName;
			dir = TQDir(fullName);
			if ( ! dir.exists(fullName) )
				break;
		}
	} else {
		fileName = fileNameForNewNote(parent, wantedName);
		fullName = parent->fullPath() + fileName;
	}

	// Create the file
//	parent->dontCareOfCreation(fullName);
	TQFile file(fullName);
	file.open(IO_WriteOnly);
	file.close();

	return fileName;
}

KURL NoteFactory::filteredURL(const KURL &url)
{
	// KURIFilter::filteredURI() is slow if the URL contains only letters, digits and '-' or '+'.
	// So, we don't use that function is that case:
	bool isSlow = true;
	for (uint i = 0; i < url.url().length(); ++i) {
		TQChar c = url.url()[i];
		if (!c.isLetterOrNumber() && c != '-' && c != '+') {
			isSlow = false;
			break;
		}
	}
	if (isSlow)
		return url;
	else
		return KURIFilter::self()->filteredURI(url);
}

TQString NoteFactory::titleForURL(const KURL &url)
{
	TQString title = url.prettyURL();
	TQString home  = "file:" + TQDir::homeDirPath() + "/";

	if (title.startsWith("mailto:"))
		return title.remove(0, 7);

	if (title.startsWith(home))
		title = "~/" + title.remove(0, home.length());

	if (title.startsWith("file://"))
		title = title.remove(0, 7); // 7 == TQString("file://").length() - 1
	else if (title.startsWith("file:"))
		title = title.remove(0, 5); // 5 == TQString("file:").length() - 1
	else if (title.startsWith("http://www."))
		title = title.remove(0, 11); // 11 == TQString("http://www.").length() - 1
	else if (title.startsWith("http://"))
		title = title.remove(0, 7); // 7 == TQString("http://").length() - 1

	if ( ! url.isLocalFile() ) {
		if (title.endsWith("/index.html") && title.length() > 11)
			title.truncate(title.length() - 11); // 11 == TQString("/index.html").length()
		else if (title.endsWith("/index.htm") && title.length() > 10)
			title.truncate(title.length() - 10); // 10 == TQString("/index.htm").length()
		else if (title.endsWith("/index.xhtml") && title.length() > 12)
			title.truncate(title.length() - 12); // 12 == TQString("/index.xhtml").length()
		else if (title.endsWith("/index.php") && title.length() > 10)
			title.truncate(title.length() - 10); // 10 == TQString("/index.php").length()
		else if (title.endsWith("/index.asp") && title.length() > 10)
			title.truncate(title.length() - 10); // 10 == TQString("/index.asp").length()
		else if (title.endsWith("/index.php3") && title.length() > 11)
			title.truncate(title.length() - 11); // 11 == TQString("/index.php3").length()
		else if (title.endsWith("/index.php4") && title.length() > 11)
			title.truncate(title.length() - 11); // 11 == TQString("/index.php4").length()
		else if (title.endsWith("/index.php5") && title.length() > 11)
			title.truncate(title.length() - 11); // 11 == TQString("/index.php5").length()
	}

	if (title.length() > 2 && title.endsWith("/")) // length > 2 because "/" and "~/" shouldn't be transformed to "" and "~"
		title.truncate(title.length() - 1); // eg. transform "www.kde.org/" to "www.kde.org"

	return title;
}

TQString NoteFactory::iconForURL(const KURL &url)
{
	TQString icon = KMimeType::iconForURL(url.url());
	if ( url.protocol() == "mailto" )
		icon = "message";
	return icon;
}

// TODO: Can I add "autoTitle" and "autoIcon" entries to .desktop files? or just store them in basket, as now...

/* Try our better to find an icon suited to the command line
 * eg. "/usr/bin/kwrite-3.2 ~/myfile.txt /home/other/file.xml"
 * will give the "kwrite" icon!
 */
TQString NoteFactory::iconForCommand(const TQString &command)
{
	TQString icon;

	// 1. Use first word as icon (typically the program without argument)
	icon = TQStringList::split(' ', command).first();
	// 2. If the command is a full path, take only the program file name
	icon = icon.mid(icon.findRev('/') + 1); // strip path if given [But it doesn't care of such
	                                                   // "myprogram /my/path/argument" -> return "argument". Would
	                                                   // must first strip first word and then strip path... Useful ??
	// 3. Use characters before any '-' (e.g. use "gimp" icon if run command is "gimp-1.3")
	if ( ! isIconExist(icon) )
		icon = TQStringList::split('-', icon).first();
	// 4. If the icon still not findable, use a generic icon
	if ( ! isIconExist(icon) )
		icon = "application-x-executable";

	return icon;
}

bool NoteFactory::isIconExist(const TQString &icon)
{
	return ! kapp->iconLoader()->loadIcon(icon, TDEIcon::NoGroup, 16, TDEIcon::DefaultState, 0L, true).isNull();
}

Note* NoteFactory::createEmptyNote(NoteType::Id type, Basket *parent)
{
	TQPixmap *pixmap;
	switch (type) {
		case NoteType::Text:
			return NoteFactory::createNoteText("", parent, /*reallyPlainText=*/true);
		case NoteType::Html:
			return NoteFactory::createNoteHtml("", parent);
		case NoteType::Image:
			pixmap = new TQPixmap( TQSize(Settings::defImageX(), Settings::defImageY()) );
			pixmap->fill();
			pixmap->setMask(pixmap->createHeuristicMask());
			return NoteFactory::createNoteImage(*pixmap, parent);
		case NoteType::Link:
			return NoteFactory::createNoteLink(KURL(), parent);
		case NoteType::Launcher:
			return NoteFactory::createNoteLauncher(KURL(), parent);
		case NoteType::Color:
			return NoteFactory::createNoteColor(TQt::black, parent);
		default:
		case NoteType::Animation:
		case NoteType::Sound:
		case NoteType::File:
		case NoteType::Unknown:
			return 0; // Not possible!
	}
}

Note* NoteFactory::importKMenuLauncher(Basket *parent)
{
	KOpenWithDlg dialog(parent);
	dialog.setSaveNewApplications(true); // To create temp file, needed by createNoteLauncher()
	dialog.exec();
	if (dialog.service()) {
		// * locateLocal() return a local file even if it is a system wide one (local one doesn't exists)
		// * desktopEntryPath() returns the full path for system wide ressources, but relative path if in home
		TQString serviceUrl = dialog.service()->desktopEntryPath();
		if ( ! serviceUrl.startsWith("/") )
			serviceUrl = dialog.service()->locateLocal(); //locateLocal("xdgdata-apps", serviceUrl);
		return createNoteLauncher(serviceUrl, parent);
	}
	return 0;
}

Note* NoteFactory::importIcon(Basket *parent)
{
	TQString iconName = TDEIconDialog::getIcon( TDEIcon::Desktop, TDEIcon::Application, false, Settings::defIconSize() );
	if ( ! iconName.isEmpty() ) {
		IconSizeDialog dialog(i18n("Import Icon as Image"), i18n("Choose the size of the icon to import as an image:"), iconName, Settings::defIconSize(), 0);
		dialog.exec();
		if (dialog.iconSize() > 0) {
			Settings::setDefIconSize(dialog.iconSize());
			Settings::saveConfig();
			return createNoteImage( DesktopIcon(iconName, dialog.iconSize()), parent ); // TODO: wantedName = iconName !
		}
	}
	return 0;
}

Note* NoteFactory::importFileContent(Basket *parent)
{
	KURL url = KFileDialog::getOpenURL( TQString(), TQString(), parent, i18n("Load File Content into a Note") );
	if ( ! url.isEmpty() )
		return copyFileAndLoad(url, parent);
	return 0;
}
