1 /***************************************************************************
2 * Copyright (C) 2003 by S�astien Laot *
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. *
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. *
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 ***************************************************************************/
23 #include <Q3TextStream>
31 #include <kcolordrag.h>
32 #include <k3urldrag.h>
33 #include <q3stylesheet.h>
35 #include <kmimetype.h>
36 #include <kmessagebox.h>
38 #include <kdesktopfile.h>
39 #include <kapplication.h>
40 #include <kaboutdata.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>
50 #include <kstandarddirs.h>
51 #include <kurifilter.h>
57 #include "notefactory.h"
59 #include "linklabel.h"
63 #include "variouswidgets.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();
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();
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();
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);
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);
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();
119 Note
* NoteFactory::createNoteColor(const QColor
&color
, Basket
*parent
)
121 Note
*note
= new Note(parent
);
122 new ColorContent(note
, color
);
126 /** Return a string list containing {url1, title1, url2, title2, url3, title3...}
128 QStringList
NoteFactory::textToURLList(const QString
&text
)
134 QStringList texts
= QStringList::split('\n', text
);
137 QStringList::iterator it
;
138 for (it
= texts
.begin(); it
!= texts
.end(); ++it
) {
139 // Strip white spaces:
140 (*it
) = (*it
).trimmed();
142 // Don't care of empty entries:
146 // Compute lower case equivalent:
147 QString ltext
= (*it
).toLower();
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
);
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)
192 list
.append(""); // We don't have any title
194 return QStringList(); // FAILED: treat the text as a text, and not as a URL 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()!
212 Note
*lastInserted
= 0;
213 QStringList::iterator it
;
214 for (it
= uriList
.begin(); it
!= uriList
.end(); ++it
) {
217 QString title
= (*it
);
219 note
= createNoteLinkOrLauncher(KUrl(url
), parent
);
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):
225 // std::cout << "Drop URL: " << (*it).prettyUrl() << std::endl;
229 lastInserted
->setNext(note
);
230 note
->setPrev(lastInserted
);
236 return firstNote
; // It don't return ALL inserted notes !
239 //QString newText = text.trimmed(); // The text for a new note, without useless spaces
240 /* Else, it's a text or an HTML note, so, create it */
241 if (Qt::mightBeRichText(/*newT*/text
))
242 return createNoteHtml(/*newT*/text
, parent
);
244 return createNoteText(/*newT*/text
, parent
);
247 Note
* NoteFactory::createNoteLauncher(const KUrl
&url
, Basket
*parent
)
250 return createNoteLauncher("", "", "", parent
);
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())
261 return loadFile(fileName
, parent
);
264 QString
NoteFactory::createNoteLauncherFile(const QString
&command
, const QString
&name
, const QString
&icon
, Basket
*parent
)
266 QString content
= QString(
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
);
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
);
302 return createNoteLink(url
, parent
);
305 #include <q3strlist.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
;
316 Note
* NoteFactory::dropNote(QMimeSource
*source
, Basket
*parent
, bool fromDrop
, QDropEvent::Action action
, Note */
*noteSource*/
)
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"));
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 */
354 if ( Q3ImageDrag::decode(source
, pixmap
) )
355 return createNoteImage(pixmap
, parent
);
357 // K3ColorDrag::decode() is buggy and can trheat strings like "#include <foo.h>" as a black color
358 #include <QTextDocument>
359 // The correct "ideal" code:
361 if ( K3ColorDrag::decode(source, color) ) {
362 createNoteColor(color, parent);
365 // And then the hack (if provide color MIME type or a text that contains color), using createNote Color RegExp:
367 QRegExp
exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
368 if (source
->provides("application/x-color") || (Q3TextDrag::decode(source
, hack
) && (exp
.search(hack
) != -1)) ) {
370 if (K3ColorDrag::decode(source
, color
))
371 return createNoteColor(color
, parent
);
372 // if ( (note = createNoteColor(color, parent)) )
374 // // Theorically it should be returned. If not, continue by dropping other things
378 if ( K3URLDrag::decode(source
, urls
) ) {
379 // If it's a Paste, we should know if files should be copied (copy&paste) or moved (cut&paste):
380 if (!fromDrop
&& Tools::isAFileCut(source
))
381 action
= QDropEvent::Move
;
382 return dropURLs(urls
, parent
, action
, fromDrop
);
385 // FIXME: use dropURLs() also from Mozilla?
388 * Mozilla's stuff sometimes uses utf-16-le - little-endian UTF-16.
390 * This has the property that for the ASCII subset case (And indeed, the
391 * ISO-8859-1 subset, I think), if you treat it as a C-style string,
392 * it'll come out to one character long in most cases, since it looks
395 * "<\0H\0T\0M\0L\0>\0"
397 * A strlen() call on that will give you 1, which simply isn't correct.
398 * That might, I suppose, be the answer, or something close.
400 * Also, Mozilla's drag/drop code predates the use of MIME types in XDnD
401 * - hence it'll throw about STRING and UTF8_STRING quite happily, hence
402 * the odd named types.
404 * Thanks to Dave Cridland for having said me that.
406 if (source
->provides("text/x-moz-url")) { // FOR MOZILLA
407 // Get the array and create a QChar array of 1/2 of the size
408 QByteArray mozilla
= source
->encodedData("text/x-moz-url");
409 Q3MemArray
<QChar
> chars( mozilla
.count() / 2 );
410 // A small debug work to know the value of each bytes
411 if (Global::debugWindow
)
412 for (uint i
= 0; i
< mozilla
.count(); i
++)
413 *Global::debugWindow
<< QString("'") + QChar(mozilla
[i
]) + "' " + QString::number(int(mozilla
[i
]));
414 // text/x-moz-url give the URL followed by the link title and separed by OxOA (10 decimal: new line?)
417 // For each little endian mozilla chars, copy it to the array of QChars
418 for (uint i
= 0; i
< mozilla
.count(); i
+= 2) {
419 chars
[i
/2] = QChar(mozilla
[i
], mozilla
[i
+1]);
420 if (mozilla
[i
] == 0x0A) {
422 name
= &(chars
[i
/2+1]);
425 // Create a QString that take the address of the first QChar and a length
426 if (name
== 0L) { // We haven't found name (FIXME: Is it possible ?)
427 QString
normalHtml(&(chars
[0]), chars
.size());
428 return createNoteLink(normalHtml
, parent
);
430 QString
normalHtml( &(chars
[0]), size
);
431 QString
normalTitle( name
, chars
.size()-size
-1);
432 return createNoteLink(normalHtml
, normalTitle
, parent
);
436 if (source
->provides("text/html")) {
438 Q3CString
subtype("html");
439 // If the text/html comes from Mozilla or GNOME it can be UTF-16 encoded: we need ExtendedTextDrag to check that
440 ExtendedTextDrag::decode(source
, html
, subtype
);
441 return createNoteHtml(html
, parent
);
445 // If the text/plain comes from GEdit or GNOME it can be empty: we need ExtendedTextDrag to check other MIME types
446 if ( ExtendedTextDrag::decode(source
, text
) )
447 return createNoteFromText(text
, parent
);
449 /* Unsucceful drop */
450 note
= createNoteUnknown(source
, parent
);
451 QString message
= i18n("<p>%1 doesn't support the data you've dropped.<br>"
452 "It however created a generic note, allowing you to drag or copy it to an application that understand it.</p>"
453 "<p>If you want the support of these data, please contact developer or visit the "
454 "<a href=\"http://basket.kde.org/dropdb.php\">BasKet Drop Database</a>.</p>").arg(kapp
->aboutData()->programName());
455 KMessageBox::information(parent
, message
, i18n("Unsupported MIME Type(s)"),
456 "unsupportedDropInfo", KMessageBox::AllowLink
);
460 Note
* NoteFactory::createNoteUnknown(QMimeSource
*source
, Basket
*parent
/*, const QString &annotations*/)
462 // Save the MimeSource in a file: create and open the file:
463 QString fileName
= createFileForNewNote(parent
, "unknown");
464 QFile
file(parent
->fullPath() + fileName
);
465 if ( ! file
.open(QIODevice::WriteOnly
) )
467 QDataStream
stream(&file
);
470 for (int i
= 0; source
->format(i
); ++i
)
471 if ( *(source
->format(i
)) )
472 stream
<< QString(source
->format(i
)); // Output the '\0'-terminated format name string
474 // Echo end of MIME types list delimiter:
477 // Echo the length (in bytes) and then the data, and then same for next MIME type:
478 for (int i
= 0; source
->format(i
); ++i
)
479 if ( *(source
->format(i
)) ) {
480 QByteArray data
= source
->encodedData(source
->format(i
));
481 stream
<< (quint32
)data
.count();
482 stream
.writeRawBytes(data
.data(), data
.count());
486 Note
*note
= new Note(parent
);
487 new UnknownContent(note
, fileName
);
491 Note
* NoteFactory::dropURLs(KUrl::List urls
, Basket
*parent
, QDropEvent::Action action
, bool fromDrop
)
493 int shouldAsk
= 0; // shouldAsk==0: don't ask ; shouldAsk==1: ask for "file" ; shouldAsk>=2: ask for "files"
494 bool shiftPressed
= Keyboard::shiftPressed();
495 bool ctrlPressed
= Keyboard::controlPressed();
496 bool modified
= fromDrop
&& (shiftPressed
|| ctrlPressed
);
498 if (modified
) // Then no menu + modified action
499 ; // action is already set: no work to do
500 else if (fromDrop
) { // Compute if user should be asked or not
501 for ( KUrl::List::iterator it
= urls
.begin(); it
!= urls
.end(); ++it
)
502 if ((*it
).protocol() != "mailto") { // Do not ask when dropping mail address :-)
504 if (shouldAsk
== 1/*2*/) // Sufficient
509 menu
.insertItem( SmallIconSet("goto"), i18n("&Move Here\tShift"), 0 );
510 menu
.insertItem( SmallIconSet("editcopy"), i18n("&Copy Here\tCtrl"), 1 );
511 menu
.insertItem( SmallIconSet("www"), i18n("&Link Here\tCtrl+Shift"), 2 );
512 menu
.insertSeparator();
513 menu
.insertItem( SmallIconSet("cancel"), i18n("C&ancel\tEscape"), 3 );
514 int id
= menu
.exec(QCursor::pos());
516 case 0: action
= QDropEvent::Move
; break;
517 case 1: action
= QDropEvent::Copy
; break;
518 case 2: action
= QDropEvent::Link
; break;
523 } else { // fromPaste
527 /* Policy of drops of URL:
528 * Email: [Modifier keys: Useless]
529 + - Link mail address
530 * Remote URL: [Modifier keys: {Copy,Link}]
531 + - Download as Image, Animation and Launcher
533 * Local URL: [Modifier keys: {Copy,Move,Link}]
534 * - Copy as Image, Animation and Launcher [Modifier keys: {Copy,Move,Link}]
535 * - Link folder [Modifier keys: Useless]
536 * - Make Launcher of executable [Modifier keys: {Copy_exec,Move_exec,Link_Launcher}]
537 * - Ask for file (if use want to copy and it is a sound: make Sound)
538 * Policy of pastes of URL: [NO modifier keys]
540 * - But copy when ask should be done
541 * - Unless cut-selection is true: move files instead
542 * Policy of file created in the basket dir: [NO modifier keys]
543 * - View as Image, Animation, Sound, Launcher
548 Note
*lastInserted
= 0;
549 for (KUrl::List::iterator it
= urls
.begin(); it
!= urls
.end(); ++it
) {
550 if ( ((*it
).protocol() == "mailto") ||
551 (action
== QDropEvent::Link
) )
552 note
= createNoteLinkOrLauncher(*it
, parent
);
553 else if (!(*it
).isLocalFile()) {
554 if ( action
!= QDropEvent::Link
&& (maybeImageOrAnimation(*it
)/* || maybeSound(*it)*/) )
555 note
= copyFileAndLoad(*it
, parent
);
557 note
= createNoteLinkOrLauncher(*it
, parent
);
559 if (action
== QDropEvent::Copy
)
560 note
= copyFileAndLoad(*it
, parent
);
561 else if (action
== QDropEvent::Move
)
562 note
= moveFileAndLoad(*it
, parent
);
564 note
= createNoteLinkOrLauncher(*it
, parent
);
567 // If we got a new note, insert it in a linked list (we will return the first note of that list):
569 DEBUG_WIN
<< "Drop URL: " + (*it
).prettyUrl();
573 lastInserted
->setNext(note
);
574 note
->setPrev(lastInserted
);
582 void NoteFactory::consumeContent(QDataStream
&stream
, NoteType::Id type
)
584 if (type
== NoteType::Link
) {
587 quint64 autoTitle64
, autoIcon64
;
588 stream
>> url
>> title
>> icon
>> autoTitle64
>> autoIcon64
;
589 } else if (type
== NoteType::Color
) {
595 Note
* NoteFactory::decodeContent(QDataStream
&stream
, NoteType::Id type
, Basket
*parent
)
597 /* if (type == NoteType::Text) {
600 return NoteFactory::createNoteText(text, parent);
601 } else if (type == NoteType::Html) {
604 return NoteFactory::createNoteHtml(html, parent);
605 } else if (type == NoteType::Image) {
608 return NoteFactory::createNoteImage(pixmap, parent);
610 if (type
== NoteType::Link
) {
613 quint64 autoTitle64
, autoIcon64
;
614 bool autoTitle
, autoIcon
;
615 stream
>> url
>> title
>> icon
>> autoTitle64
>> autoIcon64
;
616 autoTitle
= (bool)autoTitle64
;
617 autoIcon
= (bool)autoIcon64
;
618 Note
*note
= NoteFactory::createNoteLink(url
, parent
);
619 ((LinkContent
*)(note
->content()))->setLink(url
, title
, icon
, autoTitle
, autoIcon
);
621 } else if (type
== NoteType::Color
) {
624 return NoteFactory::createNoteColor(color
, parent
);
626 return 0; // NoteFactory::loadFile() is sufficient
629 // mayBeLauncher: url.url().endsWith(".desktop");
631 bool NoteFactory::maybeText(const KUrl
&url
)
633 QString path
= url
.url().toLower();
634 return path
.endsWith(".txt");
637 bool NoteFactory::maybeHtml(const KUrl
&url
)
639 QString path
= url
.url().toLower();
640 return path
.endsWith(".html") || path
.endsWith(".htm");
643 bool NoteFactory::maybeImageOrAnimation(const KUrl
&url
)
645 /* Examples on my machine:
646 QImageDrag can understands
647 {"image/png", "image/bmp", "image/jpeg", "image/pgm", "image/ppm", "image/xbm", "image/xpm"}
648 QImageIO::inputFormats() returns
649 {"BMP", "GIF", "JPEG", "MNG", "PBM", "PGM", "PNG", "PPM", "XBM", "XPM"}
650 QImageDecoder::inputFormats():
651 {"GIF", "MNG", "PNG"} */
652 Q3StrList list
= QImageIO::inputFormats();
653 list
.prepend("jpg"); // Since QImageDrag return only "JPEG" and extensions can be "JPG"; preprend for heuristic optim.
655 QString path
= url
.url().toLower();
656 for (s
= list
.first(); s
; s
= list
.next())
657 if (path
.endsWith(QString(".") + QString(s
).toLower()))
659 // TODO: Search real MIME type for local files?
663 bool NoteFactory::maybeAnimation(const KUrl
&url
)
665 QString path
= url
.url().toLower();
666 return path
.endsWith(".mng") || path
.endsWith(".gif");
669 bool NoteFactory::maybeSound(const KUrl
&url
)
671 QString path
= url
.url().toLower();
672 return path
.endsWith(".mp3") || path
.endsWith(".ogg");
675 bool NoteFactory::maybeLauncher(const KUrl
&url
)
677 QString path
= url
.url().toLower();
678 return path
.endsWith(".desktop");
683 Note
* NoteFactory::copyFileAndLoad(const KUrl
&url
, Basket
*parent
)
685 QString fileName
= fileNameForNewNote(parent
, url
.fileName());
686 QString fullPath
= parent
->fullPathForFileName(fileName
);
688 if (Global::debugWindow
)
689 *Global::debugWindow
<< "copyFileAndLoad: " + url
.prettyUrl() + " to " + fullPath
;
691 // QString annotations = i18n("Original file: %1").arg(url.prettyUrl());
692 // parent->dontCareOfCreation(fullPath);
695 // KIO::CopyJob *copyJob = KIO::copy(url, KUrl(fullPath));
696 // parent->connect( copyJob, SIGNAL(copyingDone(KIO::Job *, const KUrl &, const KUrl &, bool, bool)),
697 // parent, SLOT(slotCopyingDone(KIO::Job *, const KUrl &, const KUrl &, bool, bool)) );
699 KIO::FileCopyJob
*copyJob
= new KIO::FileCopyJob(
700 url
, KUrl(fullPath
), 0666, /*move=*/false,
701 /*overwrite=*/true, /*resume=*/true, /*showProgress=*/true );
702 parent
->connect( copyJob
, SIGNAL(result(KIO::Job
*)),
703 parent
, SLOT(slotCopyingDone2(KIO::Job
*)) );
706 NoteType::Id type
= typeForURL(url
, parent
); // Use the type of the original file because the target doesn't exist yet
707 return loadFile(fileName
, type
, parent
);
710 Note
* NoteFactory::moveFileAndLoad(const KUrl
&url
, Basket
*parent
)
712 // Globally the same as copyFileAndLoad() but move instead of copy (KIO::move())
713 QString fileName
= fileNameForNewNote(parent
, url
.fileName());
714 QString fullPath
= parent
->fullPathForFileName(fileName
);
716 if (Global::debugWindow
)
717 *Global::debugWindow
<< "moveFileAndLoad: " + url
.prettyUrl() + " to " + fullPath
;
719 // QString annotations = i18n("Original file: %1").arg(url.prettyUrl());
720 // parent->dontCareOfCreation(fullPath);
723 // KIO::CopyJob *copyJob = KIO::move(url, KUrl(fullPath));
724 // parent->connect( copyJob, SIGNAL(copyingDone(KIO::Job *, const KUrl &, const KUrl &, bool, bool)),
725 // parent, SLOT(slotCopyingDone(KIO::Job *, const KUrl &, const KUrl &, bool, bool)) );
727 KIO::FileCopyJob
*copyJob
= new KIO::FileCopyJob(
728 url
, KUrl(fullPath
), 0666, /*move=*/true,
729 /*overwrite=*/true, /*resume=*/true, /*showProgress=*/true );
730 parent
->connect( copyJob
, SIGNAL(result(KIO::Job
*)),
731 parent
, SLOT(slotCopyingDone2(KIO::Job
*)) );
734 NoteType::Id type
= typeForURL(url
, parent
); // Use the type of the original file because the target doesn't exist yet
735 return loadFile(fileName
, type
, parent
);
738 Note
* NoteFactory::loadFile(const QString
&fileName
, Basket
*parent
)
740 // The file MUST exists
741 QFileInfo
file( KUrl(parent
->fullPathForFileName(fileName
)).path() );
742 if ( ! file
.exists() )
745 NoteType::Id type
= typeForURL(parent
->fullPathForFileName(fileName
), parent
);
746 Note
*note
= loadFile(fileName
, type
, parent
);
750 Note
* NoteFactory::loadFile(const QString
&fileName
, NoteType::Id type
, Basket
*parent
)
752 Note
*note
= new Note(parent
);
754 case NoteType::Text
: new TextContent( note
, fileName
); break;
755 case NoteType::Html
: new HtmlContent( note
, fileName
); break;
756 case NoteType::Image
: new ImageContent( note
, fileName
); break;
757 case NoteType::Animation
: new AnimationContent( note
, fileName
); break;
758 case NoteType::Sound
: new SoundContent( note
, fileName
); break;
759 case NoteType::File
: new FileContent( note
, fileName
); break;
760 case NoteType::Launcher
: new LauncherContent( note
, fileName
); break;
761 case NoteType::Unknown
: new UnknownContent( note
, fileName
); break;
765 case NoteType::Color
:
772 NoteType::Id
NoteFactory::typeForURL(const KUrl
&url
, Basket */
*parent*/
)
774 /* KMimeType::Ptr kMimeType = KMimeType::findByUrl(url);
775 if (Global::debugWindow)
776 *Global::debugWindow << "typeForURL: " + kMimeType->parentMimeType();//property("MimeType").toString();*/
777 bool viewText
= Settings::viewTextFileContent();
778 bool viewHTML
= Settings::viewHtmlFileContent();
779 bool viewImage
= Settings::viewImageFileContent();
780 bool viewSound
= Settings::viewSoundFileContent();
782 KFileMetaInfo
metaInfo(url
);
783 if (Global::debugWindow
&& metaInfo
.isEmpty())
784 *Global::debugWindow
<< "typeForURL: metaInfo is empty for " + url
.prettyUrl();
785 if (metaInfo
.isEmpty()) { // metaInfo is empty for GIF files on my machine !
786 if (viewText
&& maybeText(url
)) return NoteType::Text
;
787 else if (viewHTML
&& (maybeHtml(url
))) return NoteType::Html
;
788 else if (viewImage
&& maybeAnimation(url
)) return NoteType::Animation
; // See Note::movieStatus(int)
789 else if (viewImage
&& maybeImageOrAnimation(url
)) return NoteType::Image
; // for more explanations
790 else if (viewSound
&& maybeSound(url
)) return NoteType::Sound
;
791 else if (maybeLauncher(url
)) return NoteType::Launcher
;
792 else return NoteType::File
;
794 QString mimeType
= metaInfo
.mimeType();
796 if (Global::debugWindow
)
797 *Global::debugWindow
<< "typeForURL: " + url
.prettyUrl() + " ; MIME type = " + mimeType
;
799 if (mimeType
== "application/x-desktop") return NoteType::Launcher
;
800 else if (viewText
&& mimeType
.startsWith("text/plain")) return NoteType::Text
;
801 else if (viewHTML
&& mimeType
.startsWith("text/html")) return NoteType::Html
;
802 else if (viewImage
&& mimeType
== "movie/x-mng") return NoteType::Animation
;
803 else if (viewImage
&& mimeType
== "image/gif") return NoteType::Animation
;
804 else if (viewImage
&& mimeType
.startsWith("image/")) return NoteType::Image
;
805 else if (viewSound
&& mimeType
.startsWith("audio/")) return NoteType::Sound
;
806 else return NoteType::File
;
809 QString
NoteFactory::fileNameForNewNote(Basket
*parent
, const QString
&wantedName
)
811 return Tools::fileNameForNewFile(wantedName
, parent
->fullPath());
814 // Create a file to store a new note in Basket parent and with extension extension.
815 // If wantedName is provided, the function will first try to use this file name, or derive it if it's impossible
816 // (extension willn't be used for that case)
817 QString
NoteFactory::createFileForNewNote(Basket
*parent
, const QString
&extension
, const QString
&wantedName
)
824 if (wantedName
.isEmpty()) { // TODO: fileNameForNewNote(parent, "note1."+extension);
826 for (/*int nb = 1*/; ; ++nb
) { // TODO: FIXME: If overflow ???
827 fileName
= "note" + QString::number(nb
)/*.rightJustified(5, '0')*/ + "." + extension
;
828 fullName
= parent
->fullPath() + fileName
;
829 dir
= QDir(fullName
);
830 if ( ! dir
.exists(fullName
) )
834 fileName
= fileNameForNewNote(parent
, wantedName
);
835 fullName
= parent
->fullPath() + fileName
;
839 // parent->dontCareOfCreation(fullName);
840 QFile
file(fullName
);
841 file
.open(QIODevice::WriteOnly
);
847 KUrl
NoteFactory::filteredURL(const KUrl
&url
)
849 // KURIFilter::filteredURI() is slow if the URL contains only letters, digits and '-' or '+'.
850 // So, we don't use that function is that case:
852 for (uint i
= 0; i
< url
.url().length(); ++i
) {
853 QChar c
= url
.url()[i
];
854 if (!c
.isLetterOrNumber() && c
!= '-' && c
!= '+') {
862 return KURIFilter::self()->filteredURI(url
);
865 QString
NoteFactory::titleForURL(const KUrl
&url
)
867 QString title
= url
.prettyUrl();
868 QString home
= "file:" + QDir::homePath() + "/";
870 if (title
.startsWith("mailto:"))
871 return title
.remove(0, 7);
873 if (title
.startsWith(home
))
874 title
= "~/" + title
.remove(0, home
.length());
876 if (title
.startsWith("file://"))
877 title
= title
.remove(0, 7); // 7 == QString("file://").length() - 1
878 else if (title
.startsWith("file:"))
879 title
= title
.remove(0, 5); // 5 == QString("file:").length() - 1
880 else if (title
.startsWith("http://www."))
881 title
= title
.remove(0, 11); // 11 == QString("http://www.").length() - 1
882 else if (title
.startsWith("http://"))
883 title
= title
.remove(0, 7); // 7 == QString("http://").length() - 1
885 if ( ! url
.isLocalFile() ) {
886 if (title
.endsWith("/index.html") && title
.length() > 11)
887 title
.truncate(title
.length() - 11); // 11 == QString("/index.html").length()
888 else if (title
.endsWith("/index.htm") && title
.length() > 10)
889 title
.truncate(title
.length() - 10); // 10 == QString("/index.htm").length()
890 else if (title
.endsWith("/index.xhtml") && title
.length() > 12)
891 title
.truncate(title
.length() - 12); // 12 == QString("/index.xhtml").length()
892 else if (title
.endsWith("/index.php") && title
.length() > 10)
893 title
.truncate(title
.length() - 10); // 10 == QString("/index.php").length()
894 else if (title
.endsWith("/index.asp") && title
.length() > 10)
895 title
.truncate(title
.length() - 10); // 10 == QString("/index.asp").length()
896 else if (title
.endsWith("/index.php3") && title
.length() > 11)
897 title
.truncate(title
.length() - 11); // 11 == QString("/index.php3").length()
898 else if (title
.endsWith("/index.php4") && title
.length() > 11)
899 title
.truncate(title
.length() - 11); // 11 == QString("/index.php4").length()
900 else if (title
.endsWith("/index.php5") && title
.length() > 11)
901 title
.truncate(title
.length() - 11); // 11 == QString("/index.php5").length()
904 if (title
.length() > 2 && title
.endsWith("/")) // length > 2 because "/" and "~/" shouldn't be transformed to "" and "~"
905 title
.truncate(title
.length() - 1); // eg. transform "www.kde.org/" to "www.kde.org"
910 QString
NoteFactory::iconForURL(const KUrl
&url
)
912 QString icon
= KMimeType::iconNameForUrl(url
.url());
913 if ( url
.protocol() == "mailto" )
918 // TODO: Can I add "autoTitle" and "autoIcon" entries to .desktop files? or just store them in basket, as now...
920 /* Try our better to find an icon suited to the command line
921 * eg. "/usr/bin/kwrite-3.2 ~/myfile.txt /home/other/file.xml"
922 * will give the "kwrite" icon!
924 QString
NoteFactory::iconForCommand(const QString
&command
)
928 // 1. Use first word as icon (typically the program without argument)
929 icon
= QStringList::split(' ', command
).first();
930 // 2. If the command is a full path, take only the program file name
931 icon
= icon
.mid(icon
.findRev('/') + 1); // strip path if given [But it doesn't care of such
932 // "myprogram /my/path/argument" -> return "argument". Would
933 // must first strip first word and then strip path... Useful ??
934 // 3. Use characters before any '-' (e.g. use "gimp" icon if run command is "gimp-1.3")
935 if ( ! isIconExist(icon
) )
936 icon
= QStringList::split('-', icon
).first();
937 // 4. If the icon still not findable, use a generic icon
938 if ( ! isIconExist(icon
) )
944 bool NoteFactory::isIconExist(const QString
&icon
)
946 return ! kapp
->iconLoader()->loadIcon(icon
, KIconLoader::NoGroup
, 16, KIconLoader::DefaultState
, 0L, true).isNull();
949 Note
* NoteFactory::createEmptyNote(NoteType::Id type
, Basket
*parent
)
954 return NoteFactory::createNoteText("", parent
, /*reallyPlainText=*/true);
956 return NoteFactory::createNoteHtml("", parent
);
957 case NoteType::Image
:
958 pixmap
= new QPixmap( QSize(Settings::defImageX(), Settings::defImageY()) );
960 pixmap
->setMask(pixmap
->createHeuristicMask());
961 return NoteFactory::createNoteImage(*pixmap
, parent
);
963 return NoteFactory::createNoteLink(KUrl(), parent
);
964 case NoteType::Launcher
:
965 return NoteFactory::createNoteLauncher(KUrl(), parent
);
966 case NoteType::Color
:
967 return NoteFactory::createNoteColor(Qt::black
, parent
);
969 case NoteType::Animation
:
970 case NoteType::Sound
:
972 case NoteType::Unknown
:
973 return 0; // Not possible!
977 Note
* NoteFactory::importKMenuLauncher(Basket
*parent
)
979 KOpenWithDlg
dialog(parent
);
980 dialog
.setSaveNewApplications(true); // To create temp file, needed by createNoteLauncher()
982 if (dialog
.service()) {
983 // * KStandardDirs::locateLocal() return a local file even if it is a system wide one (local one doesn't exists)
984 // * desktopEntryPath() returns the full path for system wide ressources, but relative path if in home
985 QString serviceUrl
= dialog
.service()->desktopEntryPath();
986 if ( ! serviceUrl
.startsWith("/") )
987 serviceUrl
= dialog
.service()->KStandardDirs::locateLocal(); //locateLocal("xdgdata-apps", serviceUrl);
988 return createNoteLauncher(serviceUrl
, parent
);
993 Note
* NoteFactory::importIcon(Basket
*parent
)
995 QString iconName
= KIconDialog::getIcon( KIconLoader::Desktop
, KIcon::Application
, false, Settings::defIconSize() );
996 if ( ! iconName
.isEmpty() ) {
997 IconSizeDialog
dialog(i18n("Import Icon as Image"), i18n("Choose the size of the icon to import as an image:"), iconName
, Settings::defIconSize(), 0);
999 if (dialog
.iconSize() > 0) {
1000 Settings::setDefIconSize(dialog
.iconSize());
1001 Settings::saveConfig();
1002 return createNoteImage( DesktopIcon(iconName
, dialog
.iconSize()), parent
); // TODO: wantedName = iconName !
1008 Note
* NoteFactory::importFileContent(Basket
*parent
)
1010 KUrl url
= KFileDialog::getOpenUrl( QString::null
, QString::null
, parent
, i18n("Load File Content into a Note") );
1011 if ( ! url
.isEmpty() )
1012 return copyFileAndLoad(url
, parent
);