Ran am2cmake.
[basket4.git] / src / notefactory.cpp
blob6ced3166400ed02ab74727421514a0ca10777b4a
1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
3 * slaout@linux62.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include <qstring.h>
22 //Added by qt3to4:
23 #include <Q3TextStream>
24 #include <Q3CString>
25 #include <QDropEvent>
26 #include <Q3MemArray>
27 #include <kurl.h>
28 #include <qpixmap.h>
29 #include <qcolor.h>
30 #include <qregexp.h>
31 #include <kcolordrag.h>
32 #include <kurldrag.h>
33 #include <q3stylesheet.h>
34 #include <qdir.h>
35 #include <kmimetype.h>
36 #include <kmessagebox.h>
37 #include <klocale.h>
38 #include <kdesktopfile.h>
39 #include <kapplication.h>
40 #include <kaboutdata.h>
41 #include <qfile.h>
42 #include <kfilemetainfo.h>
43 #include <kio/jobclasses.h>
44 #include <qtextcodec.h>
45 #include <kopenwith.h>
46 #include <kfiledialog.h>
47 #include <kiconloader.h>
48 #include <qfileinfo.h>
49 #include <kpopupmenu.h>
50 #include <kstandarddirs.h>
51 #include <kurifilter.h>
53 #include <iostream>
55 #include "basket.h"
56 #include "note.h"
57 #include "notefactory.h"
58 #include "notedrag.h"
59 #include "linklabel.h"
60 #include "global.h"
61 #include "settings.h"
62 #include "keyboard.h"
63 #include "variouswidgets.h"
64 #include "tools.h"
65 #include "kicondialog.h"
67 #include "debugwindow.h"
69 /** Create notes from scratch (just a content) */
71 Note* NoteFactory::createNoteText(const QString &text, Basket *parent, bool reallyPlainText/* = false*/)
73 Note *note = new Note(parent);
74 if (reallyPlainText) {
75 TextContent *content = new TextContent(note, createFileForNewNote(parent, "txt"));
76 content->setText(text);
77 content->saveToFile();
78 } else {
79 HtmlContent *content = new HtmlContent(note, createFileForNewNote(parent, "html"));
80 QString html = "<html><head><meta name=\"qrichtext\" content=\"1\" /></head><body>" + Tools::textToHTMLWithoutP(text) + "</body></html>";
81 content->setHtml(html);
82 content->saveToFile();
84 return note;
87 Note* NoteFactory::createNoteHtml(const QString &html, Basket *parent)
89 Note *note = new Note(parent);
90 HtmlContent *content = new HtmlContent(note, createFileForNewNote(parent, "html"));
91 content->setHtml(html);
92 content->saveToFile();
93 return note;
96 Note* NoteFactory::createNoteLink(const KURL &url, Basket *parent)
98 Note *note = new Note(parent);
99 new LinkContent(note, url, titleForURL(url), iconForURL(url), /*autoTitle=*/true, /*autoIcon=*/true);
100 return note;
103 Note* NoteFactory::createNoteLink(const KURL &url, const QString &title, Basket *parent)
105 Note *note = new Note(parent);
106 new LinkContent(note, url, title, iconForURL(url), /*autoTitle=*/false, /*autoIcon=*/true);
107 return note;
110 Note* NoteFactory::createNoteImage(const QPixmap &image, Basket *parent)
112 Note *note = new Note(parent);
113 ImageContent *content = new ImageContent(note, createFileForNewNote(parent, "png"));
114 content->setPixmap(image);
115 content->saveToFile();
116 return note;
119 Note* NoteFactory::createNoteColor(const QColor &color, Basket *parent)
121 Note *note = new Note(parent);
122 new ColorContent(note, color);
123 return note;
126 /** Return a string list containing {url1, title1, url2, title2, url3, title3...}
128 QStringList NoteFactory::textToURLList(const QString &text)
130 // List to return:
131 QStringList list;
133 // Split lines:
134 QStringList texts = QStringList::split('\n', text);
136 // For each lines:
137 QStringList::iterator it;
138 for (it = texts.begin(); it != texts.end(); ++it) {
139 // Strip white spaces:
140 (*it) = (*it).stripWhiteSpace();
142 // Don't care of empty entries:
143 if ((*it).isEmpty())
144 continue;
146 // Compute lower case equivalent:
147 QString ltext = (*it).lower();
149 /* Search for mail address ("*@*.*" ; "*" can contain '_', '-', or '.') and add protocol to it */
150 QString mailExpString = "[\\w-\\.]+@[\\w-\\.]+\\.[\\w]+";
151 QRegExp mailExp("^"+mailExpString+"$");
152 if (mailExp.search(ltext) != -1) {
153 ltext.insert(0, "mailto:");
154 (*it).insert(0, "mailto:");
157 // TODO: Recognize "<link>" (link between '<' and '>')
158 // TODO: Replace " at " by "@" and " dot " by "." to look for e-mail addresses
160 /* Search for mail address like "Name <address@provider.net>" */
161 QRegExp namedMailExp("^([\\w\\s]+)\\s<("+mailExpString+")>$");
162 //namedMailExp.setCaseSensitive(true); // For the name to be keeped with uppercases // DOESN'T WORK !
163 if (namedMailExp.search(ltext) != -1) {
164 QString name = namedMailExp.cap(1);
165 QString address = "mailto:" + namedMailExp.cap(2);
166 // Threat it NOW, as it's an exception (it have a title):
167 list.append(address);
168 list.append(name);
169 continue;
172 /* Search for an url and create an URL note */
173 if ( ltext.startsWith("/") && ltext[1] != '/' && ltext[1] != '*' || // Take files but not C/C++/... comments !
174 ltext.startsWith("file:") ||
175 ltext.startsWith("http://") ||
176 ltext.startsWith("https://") ||
177 ltext.startsWith("www.") ||
178 ltext.startsWith("ftp.") ||
179 ltext.startsWith("ftp://") ||
180 ltext.startsWith("mailto:") ) {
182 // First, correct the text to use the good format for the url
183 if (ltext.startsWith( "/"))
184 (*it).insert(0, "file:");
185 if (ltext.startsWith("www."))
186 (*it).insert(0, "http://");
187 if (ltext.startsWith("ftp."))
188 (*it).insert(0, "ftp://");
190 // And create the Url note (or launcher if URL point a .desktop file)
191 list.append(*it);
192 list.append(""); // We don't have any title
193 } else
194 return QStringList(); // FAILED: treat the text as a text, and not as a URL list!
196 return list;
199 Note* NoteFactory::createNoteFromText(const QString &text, Basket *parent)
201 /* Search for a color (#RGB , #RRGGBB , #RRRGGGBBB , #RRRRGGGGBBBB) and create a color note */
202 QRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
203 if ( exp.search(text) != -1 )
204 return createNoteColor(QColor(text), parent);
206 /* Try to convert the text as a URL or a list of URLs */
207 QStringList uriList = textToURLList(text);
208 if ( ! uriList.isEmpty() ) {
209 // TODO: This code is almost duplicated from fropURLs()!
210 Note *note;
211 Note *firstNote = 0;
212 Note *lastInserted = 0;
213 QStringList::iterator it;
214 for (it = uriList.begin(); it != uriList.end(); ++it) {
215 QString url = (*it);
216 ++it;
217 QString title = (*it);
218 if (title.isEmpty())
219 note = createNoteLinkOrLauncher(KURL(url), parent);
220 else
221 note = createNoteLink(KURL(url), title, parent);
223 // If we got a new note, insert it in a linked list (we will return the first note of that list):
224 if (note) {
225 // std::cout << "Drop URL: " << (*it).prettyURL() << std::endl;
226 if (!firstNote)
227 firstNote = note;
228 else {
229 lastInserted->setNext(note);
230 note->setPrev(lastInserted);
232 lastInserted = note;
236 return firstNote; // It don't return ALL inserted notes !
239 //QString newText = text.stripWhiteSpace(); // The text for a new note, without useless spaces
240 /* Else, it's a text or an HTML note, so, create it */
241 if (Q3StyleSheet::mightBeRichText(/*newT*/text))
242 return createNoteHtml(/*newT*/text, parent);
243 else
244 return createNoteText(/*newT*/text, parent);
247 Note* NoteFactory::createNoteLauncher(const KURL &url, Basket *parent)
249 if (url.isEmpty())
250 return createNoteLauncher("", "", "", parent);
251 else
252 return copyFileAndLoad(url, parent);
255 Note* NoteFactory::createNoteLauncher(const QString &command, const QString &name, const QString &icon, Basket *parent)
257 QString fileName = createNoteLauncherFile(command, name, icon, parent);
258 if (fileName.isEmpty())
259 return 0L;
260 else
261 return loadFile(fileName, parent);
264 QString NoteFactory::createNoteLauncherFile(const QString &command, const QString &name, const QString &icon, Basket *parent)
266 QString content = QString(
267 "[Desktop Entry]\n"
268 "Exec=%1\n"
269 "Name=%2\n"
270 "Icon=%3\n"
271 "Encoding=UTF-8\n"
272 "Type=Application\n").arg(command, name, icon.isEmpty() ? QString("exec") : icon);
273 QString fileName = fileNameForNewNote(parent, "launcher.desktop");
274 QString fullPath = parent->fullPathForFileName(fileName);
275 // parent->dontCareOfCreation(fullPath);
276 QFile file(fullPath);
277 if ( file.open(QIODevice::WriteOnly) ) {
278 Q3TextStream stream(&file);
279 stream.setEncoding(Q3TextStream::UnicodeUTF8);
280 stream << content;
281 file.close();
282 return fileName;
283 } else
284 return QString();
287 Note* NoteFactory::createNoteLinkOrLauncher(const KURL &url, Basket *parent)
289 // IMPORTANT: we create the service ONLY if the extension is ".desktop".
290 // Otherwise, KService take a long time to analyse all the file
291 // and output such things to stdout:
292 // "Invalid entry (missing '=') at /my/file.ogg:11984"
293 // "Invalid entry (missing ']') at /my/file.ogg:11984"...
294 KService::Ptr service;
295 if (url.fileName().endsWith(".desktop"))
296 service = new KService(url.path());
298 // If link point to a .desktop file then add a launcher, otherwise it's a link
299 if (service && service->isValid())
300 return createNoteLauncher(url, parent);
301 else
302 return createNoteLink(url, parent);
305 #include <q3strlist.h>
306 #include <qimage.h>
308 bool NoteFactory::movingNotesInTheSameBasket(QMimeSource *source, Basket *parent, QDropEvent::Action action)
310 if (NoteDrag::canDecode(source))
311 return action == QDropEvent::Move && NoteDrag::basketOf(source) == parent;
312 else
313 return false;
316 Note* NoteFactory::dropNote(QMimeSource *source, Basket *parent, bool fromDrop, QDropEvent::Action action, Note */*noteSource*/)
318 Note *note = 0L;
320 /* No data */
321 if (source->format(0) == 0L) {
322 // TODO: add a parameter to say if it's from a clipboard paste, a selection paste, or a drop
323 // To be able to say "The clipboard/selection/drop is empty".
324 // KMessageBox::error(parent, i18n("There is no data to insert."), i18n("No Data"));
325 return 0;
328 /* Debug */
329 if (Global::debugWindow) {
330 *Global::debugWindow << "<b>Drop :</b>";
331 for (int i = 0; source->format(i); ++i)
332 if ( *(source->format(i)) )
333 *Global::debugWindow << "\t[" + QString::number(i) + "] " + QString(source->format(i));
334 switch (action) { // The source want that we:
335 case QDropEvent::Copy: *Global::debugWindow << ">> Drop action: Copy"; break;
336 case QDropEvent::Move: *Global::debugWindow << ">> Drop action: Move"; break;
337 case QDropEvent::Link: *Global::debugWindow << ">> Drop action: Link"; break;
338 case QDropEvent::Private: *Global::debugWindow << ">> Drop action: Private"; break; // What is it? (Copy?)
339 case QDropEvent::UserAction: *Global::debugWindow << ">> Drop action: UserAction"; break; // Not currently
340 default: *Global::debugWindow << ">> Drop action: Unknown"; // supported by Qt!
344 /* Copy or move a Note */
345 if (NoteDrag::canDecode(source)) {
346 bool moveFiles = fromDrop && action == QDropEvent::Move;
347 bool moveNotes = moveFiles;
348 return NoteDrag::decode(source, parent, moveFiles, moveNotes); // Filename will be kept
351 /* Else : Drop object to note */
353 QPixmap pixmap;
354 if ( Q3ImageDrag::decode(source, pixmap) )
355 return createNoteImage(pixmap, parent);
357 // KColorDrag::decode() is buggy and can trheat strings like "#include <foo.h>" as a black color
358 // The correct "ideal" code:
359 /*QColor color;
360 if ( KColorDrag::decode(source, color) ) {
361 createNoteColor(color, parent);
362 return;
364 // And then the hack (if provide color MIME type or a text that contains color), using createNote Color RegExp:
365 QString hack;
366 QRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
367 if (source->provides("application/x-color") || (Q3TextDrag::decode(source, hack) && (exp.search(hack) != -1)) ) {
368 QColor color;
369 if (KColorDrag::decode(source, color))
370 return createNoteColor(color, parent);
371 // if ( (note = createNoteColor(color, parent)) )
372 // return note;
373 // // Theorically it should be returned. If not, continue by dropping other things
376 KURL::List urls;
377 if ( KURLDrag::decode(source, urls) ) {
378 // If it's a Paste, we should know if files should be copied (copy&paste) or moved (cut&paste):
379 if (!fromDrop && Tools::isAFileCut(source))
380 action = QDropEvent::Move;
381 return dropURLs(urls, parent, action, fromDrop);
384 // FIXME: use dropURLs() also from Mozilla?
387 * Mozilla's stuff sometimes uses utf-16-le - little-endian UTF-16.
389 * This has the property that for the ASCII subset case (And indeed, the
390 * ISO-8859-1 subset, I think), if you treat it as a C-style string,
391 * it'll come out to one character long in most cases, since it looks
392 * like:
394 * "<\0H\0T\0M\0L\0>\0"
396 * A strlen() call on that will give you 1, which simply isn't correct.
397 * That might, I suppose, be the answer, or something close.
399 * Also, Mozilla's drag/drop code predates the use of MIME types in XDnD
400 * - hence it'll throw about STRING and UTF8_STRING quite happily, hence
401 * the odd named types.
403 * Thanks to Dave Cridland for having said me that.
405 if (source->provides("text/x-moz-url")) { // FOR MOZILLA
406 // Get the array and create a QChar array of 1/2 of the size
407 QByteArray mozilla = source->encodedData("text/x-moz-url");
408 Q3MemArray<QChar> chars( mozilla.count() / 2 );
409 // A small debug work to know the value of each bytes
410 if (Global::debugWindow)
411 for (uint i = 0; i < mozilla.count(); i++)
412 *Global::debugWindow << QString("'") + QChar(mozilla[i]) + "' " + QString::number(int(mozilla[i]));
413 // text/x-moz-url give the URL followed by the link title and separed by OxOA (10 decimal: new line?)
414 uint size = 0;
415 QChar *name = 0L;
416 // For each little endian mozilla chars, copy it to the array of QChars
417 for (uint i = 0; i < mozilla.count(); i += 2) {
418 chars[i/2] = QChar(mozilla[i], mozilla[i+1]);
419 if (mozilla[i] == 0x0A) {
420 size = i/2;
421 name = &(chars[i/2+1]);
424 // Create a QString that take the address of the first QChar and a length
425 if (name == 0L) { // We haven't found name (FIXME: Is it possible ?)
426 QString normalHtml(&(chars[0]), chars.size());
427 return createNoteLink(normalHtml, parent);
428 } else {
429 QString normalHtml( &(chars[0]), size );
430 QString normalTitle( name, chars.size()-size-1);
431 return createNoteLink(normalHtml, normalTitle, parent);
435 if (source->provides("text/html")) {
436 QString html;
437 Q3CString subtype("html");
438 // If the text/html comes from Mozilla or GNOME it can be UTF-16 encoded: we need ExtendedTextDrag to check that
439 ExtendedTextDrag::decode(source, html, subtype);
440 return createNoteHtml(html, parent);
443 QString text;
444 // If the text/plain comes from GEdit or GNOME it can be empty: we need ExtendedTextDrag to check other MIME types
445 if ( ExtendedTextDrag::decode(source, text) )
446 return createNoteFromText(text, parent);
448 /* Unsucceful drop */
449 note = createNoteUnknown(source, parent);
450 QString message = i18n("<p>%1 doesn't support the data you've dropped.<br>"
451 "It however created a generic note, allowing you to drag or copy it to an application that understand it.</p>"
452 "<p>If you want the support of these data, please contact developer or visit the "
453 "<a href=\"http://basket.kde.org/dropdb.php\">BasKet Drop Database</a>.</p>").arg(kapp->aboutData()->programName());
454 KMessageBox::information(parent, message, i18n("Unsupported MIME Type(s)"),
455 "unsupportedDropInfo", KMessageBox::AllowLink);
456 return note;
459 Note* NoteFactory::createNoteUnknown(QMimeSource *source, Basket *parent/*, const QString &annotations*/)
461 // Save the MimeSource in a file: create and open the file:
462 QString fileName = createFileForNewNote(parent, "unknown");
463 QFile file(parent->fullPath() + fileName);
464 if ( ! file.open(QIODevice::WriteOnly) )
465 return 0L;
466 QDataStream stream(&file);
468 // Echo MIME types:
469 for (int i = 0; source->format(i); ++i)
470 if ( *(source->format(i)) )
471 stream << QString(source->format(i)); // Output the '\0'-terminated format name string
473 // Echo end of MIME types list delimiter:
474 stream << "";
476 // Echo the length (in bytes) and then the data, and then same for next MIME type:
477 for (int i = 0; source->format(i); ++i)
478 if ( *(source->format(i)) ) {
479 QByteArray data = source->encodedData(source->format(i));
480 stream << (Q_UINT32)data.count();
481 stream.writeRawBytes(data.data(), data.count());
483 file.close();
485 Note *note = new Note(parent);
486 new UnknownContent(note, fileName);
487 return note;
490 Note* NoteFactory::dropURLs(KURL::List urls, Basket *parent, QDropEvent::Action action, bool fromDrop)
492 int shouldAsk = 0; // shouldAsk==0: don't ask ; shouldAsk==1: ask for "file" ; shouldAsk>=2: ask for "files"
493 bool shiftPressed = Keyboard::shiftPressed();
494 bool ctrlPressed = Keyboard::controlPressed();
495 bool modified = fromDrop && (shiftPressed || ctrlPressed);
497 if (modified) // Then no menu + modified action
498 ; // action is already set: no work to do
499 else if (fromDrop) { // Compute if user should be asked or not
500 for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it )
501 if ((*it).protocol() != "mailto") { // Do not ask when dropping mail address :-)
502 shouldAsk++;
503 if (shouldAsk == 1/*2*/) // Sufficient
504 break;
506 if (shouldAsk) {
507 KPopupMenu menu(parent);
508 menu.insertItem( SmallIconSet("goto"), i18n("&Move Here\tShift"), 0 );
509 menu.insertItem( SmallIconSet("editcopy"), i18n("&Copy Here\tCtrl"), 1 );
510 menu.insertItem( SmallIconSet("www"), i18n("&Link Here\tCtrl+Shift"), 2 );
511 menu.insertSeparator();
512 menu.insertItem( SmallIconSet("cancel"), i18n("C&ancel\tEscape"), 3 );
513 int id = menu.exec(QCursor::pos());
514 switch (id) {
515 case 0: action = QDropEvent::Move; break;
516 case 1: action = QDropEvent::Copy; break;
517 case 2: action = QDropEvent::Link; break;
518 default: return 0;
520 modified = true;
522 } else { // fromPaste
526 /* Policy of drops of URL:
527 * Email: [Modifier keys: Useless]
528 + - Link mail address
529 * Remote URL: [Modifier keys: {Copy,Link}]
530 + - Download as Image, Animation and Launcher
531 + - Link other URLs
532 * Local URL: [Modifier keys: {Copy,Move,Link}]
533 * - Copy as Image, Animation and Launcher [Modifier keys: {Copy,Move,Link}]
534 * - Link folder [Modifier keys: Useless]
535 * - Make Launcher of executable [Modifier keys: {Copy_exec,Move_exec,Link_Launcher}]
536 * - Ask for file (if use want to copy and it is a sound: make Sound)
537 * Policy of pastes of URL: [NO modifier keys]
538 * - Same as drops
539 * - But copy when ask should be done
540 * - Unless cut-selection is true: move files instead
541 * Policy of file created in the basket dir: [NO modifier keys]
542 * - View as Image, Animation, Sound, Launcher
543 * - View as File
545 Note *note;
546 Note *firstNote = 0;
547 Note *lastInserted = 0;
548 for (KURL::List::iterator it = urls.begin(); it != urls.end(); ++it) {
549 if ( ((*it).protocol() == "mailto") ||
550 (action == QDropEvent::Link) )
551 note = createNoteLinkOrLauncher(*it, parent);
552 else if (!(*it).isLocalFile()) {
553 if ( action != QDropEvent::Link && (maybeImageOrAnimation(*it)/* || maybeSound(*it)*/) )
554 note = copyFileAndLoad(*it, parent);
555 else
556 note = createNoteLinkOrLauncher(*it, parent);
557 } else {
558 if (action == QDropEvent::Copy)
559 note = copyFileAndLoad(*it, parent);
560 else if (action == QDropEvent::Move)
561 note = moveFileAndLoad(*it, parent);
562 else
563 note = createNoteLinkOrLauncher(*it, parent);
566 // If we got a new note, insert it in a linked list (we will return the first note of that list):
567 if (note) {
568 DEBUG_WIN << "Drop URL: " + (*it).prettyURL();
569 if (!firstNote)
570 firstNote = note;
571 else {
572 lastInserted->setNext(note);
573 note->setPrev(lastInserted);
575 lastInserted = note;
578 return firstNote;
581 void NoteFactory::consumeContent(QDataStream &stream, NoteType::Id type)
583 if (type == NoteType::Link) {
584 KURL url;
585 QString title, icon;
586 Q_UINT64 autoTitle64, autoIcon64;
587 stream >> url >> title >> icon >> autoTitle64 >> autoIcon64;
588 } else if (type == NoteType::Color) {
589 QColor color;
590 stream >> color;
594 Note* NoteFactory::decodeContent(QDataStream &stream, NoteType::Id type, Basket *parent)
596 /* if (type == NoteType::Text) {
597 QString text;
598 stream >> text;
599 return NoteFactory::createNoteText(text, parent);
600 } else if (type == NoteType::Html) {
601 QString html;
602 stream >> html;
603 return NoteFactory::createNoteHtml(html, parent);
604 } else if (type == NoteType::Image) {
605 QPixmap pixmap;
606 stream >> pixmap;
607 return NoteFactory::createNoteImage(pixmap, parent);
608 } else */
609 if (type == NoteType::Link) {
610 KURL url;
611 QString title, icon;
612 Q_UINT64 autoTitle64, autoIcon64;
613 bool autoTitle, autoIcon;
614 stream >> url >> title >> icon >> autoTitle64 >> autoIcon64;
615 autoTitle = (bool)autoTitle64;
616 autoIcon = (bool)autoIcon64;
617 Note *note = NoteFactory::createNoteLink(url, parent);
618 ((LinkContent*)(note->content()))->setLink(url, title, icon, autoTitle, autoIcon);
619 return note;
620 } else if (type == NoteType::Color) {
621 QColor color;
622 stream >> color;
623 return NoteFactory::createNoteColor(color, parent);
624 } else
625 return 0; // NoteFactory::loadFile() is sufficient
628 // mayBeLauncher: url.url().endsWith(".desktop");
630 bool NoteFactory::maybeText(const KURL &url)
632 QString path = url.url().lower();
633 return path.endsWith(".txt");
636 bool NoteFactory::maybeHtml(const KURL &url)
638 QString path = url.url().lower();
639 return path.endsWith(".html") || path.endsWith(".htm");
642 bool NoteFactory::maybeImageOrAnimation(const KURL &url)
644 /* Examples on my machine:
645 QImageDrag can understands
646 {"image/png", "image/bmp", "image/jpeg", "image/pgm", "image/ppm", "image/xbm", "image/xpm"}
647 QImageIO::inputFormats() returns
648 {"BMP", "GIF", "JPEG", "MNG", "PBM", "PGM", "PNG", "PPM", "XBM", "XPM"}
649 QImageDecoder::inputFormats():
650 {"GIF", "MNG", "PNG"} */
651 Q3StrList list = QImageIO::inputFormats();
652 list.prepend("jpg"); // Since QImageDrag return only "JPEG" and extensions can be "JPG"; preprend for heuristic optim.
653 char *s;
654 QString path = url.url().lower();
655 for (s = list.first(); s; s = list.next())
656 if (path.endsWith(QString(".") + QString(s).lower()))
657 return true;
658 // TODO: Search real MIME type for local files?
659 return false;
662 bool NoteFactory::maybeAnimation(const KURL &url)
664 QString path = url.url().lower();
665 return path.endsWith(".mng") || path.endsWith(".gif");
668 bool NoteFactory::maybeSound(const KURL &url)
670 QString path = url.url().lower();
671 return path.endsWith(".mp3") || path.endsWith(".ogg");
674 bool NoteFactory::maybeLauncher(const KURL &url)
676 QString path = url.url().lower();
677 return path.endsWith(".desktop");
680 ////////////// NEW:
682 Note* NoteFactory::copyFileAndLoad(const KURL &url, Basket *parent)
684 QString fileName = fileNameForNewNote(parent, url.fileName());
685 QString fullPath = parent->fullPathForFileName(fileName);
687 if (Global::debugWindow)
688 *Global::debugWindow << "copyFileAndLoad: " + url.prettyURL() + " to " + fullPath;
690 // QString annotations = i18n("Original file: %1").arg(url.prettyURL());
691 // parent->dontCareOfCreation(fullPath);
694 // KIO::CopyJob *copyJob = KIO::copy(url, KURL(fullPath));
695 // parent->connect( copyJob, SIGNAL(copyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)),
696 // parent, SLOT(slotCopyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)) );
698 KIO::FileCopyJob *copyJob = new KIO::FileCopyJob(
699 url, KURL(fullPath), 0666, /*move=*/false,
700 /*overwrite=*/true, /*resume=*/true, /*showProgress=*/true );
701 parent->connect( copyJob, SIGNAL(result(KIO::Job *)),
702 parent, SLOT(slotCopyingDone2(KIO::Job *)) );
705 NoteType::Id type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
706 return loadFile(fileName, type, parent);
709 Note* NoteFactory::moveFileAndLoad(const KURL &url, Basket *parent)
711 // Globally the same as copyFileAndLoad() but move instead of copy (KIO::move())
712 QString fileName = fileNameForNewNote(parent, url.fileName());
713 QString fullPath = parent->fullPathForFileName(fileName);
715 if (Global::debugWindow)
716 *Global::debugWindow << "moveFileAndLoad: " + url.prettyURL() + " to " + fullPath;
718 // QString annotations = i18n("Original file: %1").arg(url.prettyURL());
719 // parent->dontCareOfCreation(fullPath);
722 // KIO::CopyJob *copyJob = KIO::move(url, KURL(fullPath));
723 // parent->connect( copyJob, SIGNAL(copyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)),
724 // parent, SLOT(slotCopyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)) );
726 KIO::FileCopyJob *copyJob = new KIO::FileCopyJob(
727 url, KURL(fullPath), 0666, /*move=*/true,
728 /*overwrite=*/true, /*resume=*/true, /*showProgress=*/true );
729 parent->connect( copyJob, SIGNAL(result(KIO::Job *)),
730 parent, SLOT(slotCopyingDone2(KIO::Job *)) );
733 NoteType::Id type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
734 return loadFile(fileName, type, parent);
737 Note* NoteFactory::loadFile(const QString &fileName, Basket *parent)
739 // The file MUST exists
740 QFileInfo file( KURL(parent->fullPathForFileName(fileName)).path() );
741 if ( ! file.exists() )
742 return 0L;
744 NoteType::Id type = typeForURL(parent->fullPathForFileName(fileName), parent);
745 Note *note = loadFile(fileName, type, parent);
746 return note;
749 Note* NoteFactory::loadFile(const QString &fileName, NoteType::Id type, Basket *parent)
751 Note *note = new Note(parent);
752 switch (type) {
753 case NoteType::Text: new TextContent( note, fileName ); break;
754 case NoteType::Html: new HtmlContent( note, fileName ); break;
755 case NoteType::Image: new ImageContent( note, fileName ); break;
756 case NoteType::Animation: new AnimationContent( note, fileName ); break;
757 case NoteType::Sound: new SoundContent( note, fileName ); break;
758 case NoteType::File: new FileContent( note, fileName ); break;
759 case NoteType::Launcher: new LauncherContent( note, fileName ); break;
760 case NoteType::Unknown: new UnknownContent( note, fileName ); break;
762 default:
763 case NoteType::Link:
764 case NoteType::Color:
765 return 0;
768 return note;
771 NoteType::Id NoteFactory::typeForURL(const KURL &url, Basket */*parent*/)
773 /* KMimeType::Ptr kMimeType = KMimeType::findByURL(url);
774 if (Global::debugWindow)
775 *Global::debugWindow << "typeForURL: " + kMimeType->parentMimeType();//property("MimeType").toString();*/
776 bool viewText = Settings::viewTextFileContent();
777 bool viewHTML = Settings::viewHtmlFileContent();
778 bool viewImage = Settings::viewImageFileContent();
779 bool viewSound = Settings::viewSoundFileContent();
781 KFileMetaInfo metaInfo(url);
782 if (Global::debugWindow && metaInfo.isEmpty())
783 *Global::debugWindow << "typeForURL: metaInfo is empty for " + url.prettyURL();
784 if (metaInfo.isEmpty()) { // metaInfo is empty for GIF files on my machine !
785 if (viewText && maybeText(url)) return NoteType::Text;
786 else if (viewHTML && (maybeHtml(url))) return NoteType::Html;
787 else if (viewImage && maybeAnimation(url)) return NoteType::Animation; // See Note::movieStatus(int)
788 else if (viewImage && maybeImageOrAnimation(url)) return NoteType::Image; // for more explanations
789 else if (viewSound && maybeSound(url)) return NoteType::Sound;
790 else if (maybeLauncher(url)) return NoteType::Launcher;
791 else return NoteType::File;
793 QString mimeType = metaInfo.mimeType();
795 if (Global::debugWindow)
796 *Global::debugWindow << "typeForURL: " + url.prettyURL() + " ; MIME type = " + mimeType;
798 if (mimeType == "application/x-desktop") return NoteType::Launcher;
799 else if (viewText && mimeType.startsWith("text/plain")) return NoteType::Text;
800 else if (viewHTML && mimeType.startsWith("text/html")) return NoteType::Html;
801 else if (viewImage && mimeType == "movie/x-mng") return NoteType::Animation;
802 else if (viewImage && mimeType == "image/gif") return NoteType::Animation;
803 else if (viewImage && mimeType.startsWith("image/")) return NoteType::Image;
804 else if (viewSound && mimeType.startsWith("audio/")) return NoteType::Sound;
805 else return NoteType::File;
808 QString NoteFactory::fileNameForNewNote(Basket *parent, const QString &wantedName)
810 return Tools::fileNameForNewFile(wantedName, parent->fullPath());
813 // Create a file to store a new note in Basket parent and with extension extension.
814 // If wantedName is provided, the function will first try to use this file name, or derive it if it's impossible
815 // (extension willn't be used for that case)
816 QString NoteFactory::createFileForNewNote(Basket *parent, const QString &extension, const QString &wantedName)
818 static int nb = 1;
820 QString fileName;
821 QString fullName;
823 if (wantedName.isEmpty()) { // TODO: fileNameForNewNote(parent, "note1."+extension);
824 QDir dir;
825 for (/*int nb = 1*/; ; ++nb) { // TODO: FIXME: If overflow ???
826 fileName = "note" + QString::number(nb)/*.rightJustify(5, '0')*/ + "." + extension;
827 fullName = parent->fullPath() + fileName;
828 dir = QDir(fullName);
829 if ( ! dir.exists(fullName) )
830 break;
832 } else {
833 fileName = fileNameForNewNote(parent, wantedName);
834 fullName = parent->fullPath() + fileName;
837 // Create the file
838 // parent->dontCareOfCreation(fullName);
839 QFile file(fullName);
840 file.open(QIODevice::WriteOnly);
841 file.close();
843 return fileName;
846 KURL NoteFactory::filteredURL(const KURL &url)
848 // KURIFilter::filteredURI() is slow if the URL contains only letters, digits and '-' or '+'.
849 // So, we don't use that function is that case:
850 bool isSlow = true;
851 for (uint i = 0; i < url.url().length(); ++i) {
852 QChar c = url.url()[i];
853 if (!c.isLetterOrNumber() && c != '-' && c != '+') {
854 isSlow = false;
855 break;
858 if (isSlow)
859 return url;
860 else
861 return KURIFilter::self()->filteredURI(url);
864 QString NoteFactory::titleForURL(const KURL &url)
866 QString title = url.prettyURL();
867 QString home = "file:" + QDir::homeDirPath() + "/";
869 if (title.startsWith("mailto:"))
870 return title.remove(0, 7);
872 if (title.startsWith(home))
873 title = "~/" + title.remove(0, home.length());
875 if (title.startsWith("file://"))
876 title = title.remove(0, 7); // 7 == QString("file://").length() - 1
877 else if (title.startsWith("file:"))
878 title = title.remove(0, 5); // 5 == QString("file:").length() - 1
879 else if (title.startsWith("http://www."))
880 title = title.remove(0, 11); // 11 == QString("http://www.").length() - 1
881 else if (title.startsWith("http://"))
882 title = title.remove(0, 7); // 7 == QString("http://").length() - 1
884 if ( ! url.isLocalFile() ) {
885 if (title.endsWith("/index.html") && title.length() > 11)
886 title.truncate(title.length() - 11); // 11 == QString("/index.html").length()
887 else if (title.endsWith("/index.htm") && title.length() > 10)
888 title.truncate(title.length() - 10); // 10 == QString("/index.htm").length()
889 else if (title.endsWith("/index.xhtml") && title.length() > 12)
890 title.truncate(title.length() - 12); // 12 == QString("/index.xhtml").length()
891 else if (title.endsWith("/index.php") && title.length() > 10)
892 title.truncate(title.length() - 10); // 10 == QString("/index.php").length()
893 else if (title.endsWith("/index.asp") && title.length() > 10)
894 title.truncate(title.length() - 10); // 10 == QString("/index.asp").length()
895 else if (title.endsWith("/index.php3") && title.length() > 11)
896 title.truncate(title.length() - 11); // 11 == QString("/index.php3").length()
897 else if (title.endsWith("/index.php4") && title.length() > 11)
898 title.truncate(title.length() - 11); // 11 == QString("/index.php4").length()
899 else if (title.endsWith("/index.php5") && title.length() > 11)
900 title.truncate(title.length() - 11); // 11 == QString("/index.php5").length()
903 if (title.length() > 2 && title.endsWith("/")) // length > 2 because "/" and "~/" shouldn't be transformed to "" and "~"
904 title.truncate(title.length() - 1); // eg. transform "www.kde.org/" to "www.kde.org"
906 return title;
909 QString NoteFactory::iconForURL(const KURL &url)
911 QString icon = KMimeType::iconForURL(url.url());
912 if ( url.protocol() == "mailto" )
913 icon = "message";
914 return icon;
917 // TODO: Can I add "autoTitle" and "autoIcon" entries to .desktop files? or just store them in basket, as now...
919 /* Try our better to find an icon suited to the command line
920 * eg. "/usr/bin/kwrite-3.2 ~/myfile.txt /home/other/file.xml"
921 * will give the "kwrite" icon!
923 QString NoteFactory::iconForCommand(const QString &command)
925 QString icon;
927 // 1. Use first word as icon (typically the program without argument)
928 icon = QStringList::split(' ', command).first();
929 // 2. If the command is a full path, take only the program file name
930 icon = icon.mid(icon.findRev('/') + 1); // strip path if given [But it doesn't care of such
931 // "myprogram /my/path/argument" -> return "argument". Would
932 // must first strip first word and then strip path... Useful ??
933 // 3. Use characters before any '-' (e.g. use "gimp" icon if run command is "gimp-1.3")
934 if ( ! isIconExist(icon) )
935 icon = QStringList::split('-', icon).first();
936 // 4. If the icon still not findable, use a generic icon
937 if ( ! isIconExist(icon) )
938 icon = "exec";
940 return icon;
943 bool NoteFactory::isIconExist(const QString &icon)
945 return ! kapp->iconLoader()->loadIcon(icon, KIcon::NoGroup, 16, KIcon::DefaultState, 0L, true).isNull();
948 Note* NoteFactory::createEmptyNote(NoteType::Id type, Basket *parent)
950 QPixmap *pixmap;
951 switch (type) {
952 case NoteType::Text:
953 return NoteFactory::createNoteText("", parent, /*reallyPlainText=*/true);
954 case NoteType::Html:
955 return NoteFactory::createNoteHtml("", parent);
956 case NoteType::Image:
957 pixmap = new QPixmap( QSize(Settings::defImageX(), Settings::defImageY()) );
958 pixmap->fill();
959 pixmap->setMask(pixmap->createHeuristicMask());
960 return NoteFactory::createNoteImage(*pixmap, parent);
961 case NoteType::Link:
962 return NoteFactory::createNoteLink(KURL(), parent);
963 case NoteType::Launcher:
964 return NoteFactory::createNoteLauncher(KURL(), parent);
965 case NoteType::Color:
966 return NoteFactory::createNoteColor(Qt::black, parent);
967 default:
968 case NoteType::Animation:
969 case NoteType::Sound:
970 case NoteType::File:
971 case NoteType::Unknown:
972 return 0; // Not possible!
976 Note* NoteFactory::importKMenuLauncher(Basket *parent)
978 KOpenWithDlg dialog(parent);
979 dialog.setSaveNewApplications(true); // To create temp file, needed by createNoteLauncher()
980 dialog.exec();
981 if (dialog.service()) {
982 // * locateLocal() return a local file even if it is a system wide one (local one doesn't exists)
983 // * desktopEntryPath() returns the full path for system wide ressources, but relative path if in home
984 QString serviceUrl = dialog.service()->desktopEntryPath();
985 if ( ! serviceUrl.startsWith("/") )
986 serviceUrl = dialog.service()->locateLocal(); //locateLocal("xdgdata-apps", serviceUrl);
987 return createNoteLauncher(serviceUrl, parent);
989 return 0;
992 Note* NoteFactory::importIcon(Basket *parent)
994 QString iconName = KIconDialog::getIcon( KIcon::Desktop, KIcon::Application, false, Settings::defIconSize() );
995 if ( ! iconName.isEmpty() ) {
996 IconSizeDialog dialog(i18n("Import Icon as Image"), i18n("Choose the size of the icon to import as an image:"), iconName, Settings::defIconSize(), 0);
997 dialog.exec();
998 if (dialog.iconSize() > 0) {
999 Settings::setDefIconSize(dialog.iconSize());
1000 Settings::saveConfig();
1001 return createNoteImage( DesktopIcon(iconName, dialog.iconSize()), parent ); // TODO: wantedName = iconName !
1004 return 0;
1007 Note* NoteFactory::importFileContent(Basket *parent)
1009 KURL url = KFileDialog::getOpenURL( QString::null, QString::null, parent, i18n("Load File Content into a Note") );
1010 if ( ! url.isEmpty() )
1011 return copyFileAndLoad(url, parent);
1012 return 0;