1
/***************************************************************************
2
* Copyright (C) 2003 by S�bastien Lao�t *
3
* sebastien.laout@tuxfamily.org *
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
***************************************************************************/
26
#include <kcolordrag.h>
28
#include <qstylesheet.h>
30
#include <kmimetype.h>
31
#include <kmessagebox.h>
33
#include <kdesktopfile.h>
34
#include <kapplication.h>
36
#include <kfilemetainfo.h>
37
#include <kio/jobclasses.h>
38
#include <qtextcodec.h>
39
#include <kopenwith.h>
40
#include <kfiledialog.h>
41
#include <kicondialog.h>
42
#include <kiconloader.h>
43
#include <qfileinfo.h>
44
#include <kpopupmenu.h>
45
#include <kstandarddirs.h>
46
#include <kurifilter.h>
50
#include "itemfactory.h"
52
#include "linklabel.h"
57
#include "debugwindow.h"
59
/** Create items from scratch (just a content) */
61
Item* ItemFactory::createItemText(const QString &text, Basket *parent, const QString &annotations)
63
Item *item = new Item(createFileForNewItem(parent, "txt"), Settings::defTextFont(), Settings::defTextColor(),
64
annotations, false, parent);
66
parent->insertItem(item);
72
Item* ItemFactory::createItemHtml(const QString &html, Basket *parent, const QString &annotations)
74
Item *item = new Item(createFileForNewItem(parent, "html"), //Settings::defHtmlShowSource(),
75
annotations, false, parent);
77
parent->insertItem(item);
83
Item* ItemFactory::createItemLink(const KURL &url, Basket *parent, const QString &annotations)
85
Item *item = new Item(url, titleForURL(url), iconForURL(url), true, true,
86
annotations, false, parent);
87
parent->insertItem(item);
93
// FIXME: , title = QString(), ???
95
// The same as ItemFactory::createItemLink but with a defined title :
96
Item* ItemFactory::createItemLink(const KURL &url, const QString &title, Basket *parent, const QString &annotations)
98
Item *item = new Item(url, title, iconForURL(url), /*autoTitle=*/false, true,
99
annotations, false, parent);
100
parent->insertItem(item);
106
Item* ItemFactory::createItemImage(const QPixmap &image, Basket *parent, const QString &annotations)
108
Item *item = new Item(createFileForNewItem(parent, "png"), Item::Image, // Take care of image format
109
annotations, false, parent);
110
item->setPixmap(image);
111
parent->insertItem(item);
117
Item* ItemFactory::createItemColor(const QColor &color, Basket *parent, const QString &annotations)
119
Item *item = new Item(color,
120
annotations, false, parent);
121
parent->insertItem(item);
127
/** Return a string list containing {url1, title1, url2, title2, url3, title3...}
129
QStringList ItemFactory::textToURLList(const QString &text)
135
QStringList texts = QStringList::split('\n', text);
138
QStringList::iterator it;
139
for (it = texts.begin(); it != texts.end(); ++it) {
140
// Strip white spaces:
141
(*it) = (*it).stripWhiteSpace();
143
// Don't care of empty entries:
147
// Compute lower case equivalent:
148
QString ltext = (*it).lower();
150
/* Search for mail address ("*@*.*" ; "*" can contain '_', '-', or '.') and add protocol to it */
151
QString mailExpString = "[\\w-\\.]+@[\\w-\\.]+\\.[\\w]+";
152
QRegExp mailExp("^"+mailExpString+"$");
153
if (mailExp.search(ltext) != -1) {
154
ltext.insert(0, "mailto:");
155
(*it).insert(0, "mailto:");
158
// TODO: Recognize "<link>" (link between '<' and '>')
159
// TODO: Replace " at " by "@" and " dot " by "." to look for e-mail addresses
161
/* Search for mail address like "Name <address@provider.net>" */
162
QRegExp namedMailExp("^([\\w\\s]+)\\s<("+mailExpString+")>$");
163
//namedMailExp.setCaseSensitive(true); // For the name to be keeped with uppercases // DOESN'T WORK !
164
if (namedMailExp.search(ltext) != -1) {
165
QString name = namedMailExp.cap(1);
166
QString address = "mailto:" + namedMailExp.cap(2);
167
// Threat it NOW, as it's an exception (it have a title):
168
list.append(address);
173
/* Search for an url and create an URL item */
174
if ( ltext.startsWith("/") && ltext[1] != '/' && ltext[1] != '*' || // Take files but not C/C++/... comments !
175
ltext.startsWith("file:") ||
176
ltext.startsWith("http://") ||
177
ltext.startsWith("www.") ||
178
ltext.startsWith("ftp") ||
179
ltext.startsWith("mailto:") ) {
181
// First, correct the text to use the good format for the url
182
if (ltext.startsWith( "/"))
183
(*it).insert(0, "file:");
184
if (ltext.startsWith("www."))
185
(*it).insert(0, "http://");
186
if (ltext.startsWith("ftp."))
187
(*it).insert(0, "ftp://");
189
// And create the Url item (or launcher if URL point a .desktop file)
191
list.append(""); // We don't have any title
193
return QStringList(); // FAILED: treat the text as a text, and not as a URL list!
198
Item* ItemFactory::createItem(const QString &text, Basket *parent, const QString &annotations)
200
/* Search for a color (#RGB , #RRGGBB , #RRRGGGBBB , #RRRRGGGGBBBB) and create a color item */
201
QRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
202
if ( exp.search(text) != -1 )
203
return createItemColor(QColor(text), parent, annotations);
205
/* Try to convert the text as a URL or a list of URLs */
206
QStringList uriList = textToURLList(text);
207
if ( ! uriList.isEmpty() ) {
209
//bool insertAfter = parent->m_insertAfter; // To past all URLS at the same place (and not one in the right place
210
// and the others at begin or end)
211
QStringList::iterator it;
212
for (it = uriList.begin(); it != uriList.end(); ++it) {
215
QString title = (*it);
217
lastItem = createItemLinkOrLauncher(KURL(url), parent);
219
lastItem = createItemLink(KURL(url), title, parent);
220
// After last item was inserted, the insert position is reseted : re-set it
221
parent->m_insertAtItem = lastItem;
222
parent->m_insertAfter = true;
224
return lastItem; // It don't return ALL inserted items !
227
QString newText = text.stripWhiteSpace(); // The text for a new item, without useless spaces
228
/* Else, it's a text or an HTML item, so, create it */
229
if (QStyleSheet::mightBeRichText(newText)) // ISITUSEFULL: Verify all lines ?
230
return createItemHtml(newText, parent, annotations);
232
return createItemText(newText, parent, annotations);
235
Item* ItemFactory::createItemLauncher(const KURL &url, Basket *parent)
238
return createItemLauncher("", "", "", parent);
240
return copyFileAndLoad(url, parent);
243
Item* ItemFactory::createItemLauncher(const QString &command, const QString &name, const QString &icon, Basket *parent)
245
QString fileName = createItemLauncherFile(command, name, icon, parent);
246
if (fileName.isEmpty())
249
return loadFile(fileName, parent);
252
QString ItemFactory::createItemLauncherFile(const QString &command, const QString &name, const QString &icon, Basket *parent)
254
QString content = QString(
260
"Type=Application\n").arg(command, name, icon.isEmpty() ? "exec" : icon);
261
QString fileName = fileNameForNewItem(parent, "launcher.desktop");
262
QString fullPath = parent->fullPathForFileName(fileName);
263
parent->dontCareOfCreation(fullPath);
264
QFile file(fullPath);
265
if ( file.open(IO_WriteOnly) ) {
266
QTextStream stream(&file);
267
stream.setEncoding(QTextStream::UnicodeUTF8);
275
Item* ItemFactory::createItemLinkOrLauncher(const KURL &url, Basket *parent)
277
// IMPORTANT: we create the service ONLY if the extension is ".desktop".
278
// Otherwise, KService take a long time to analyse all the file
279
// and output such things to stdout:
280
// "Invalid entry (missing '=') at /my/file.ogg:11984"
281
// "Invalid entry (missing ']') at /my/file.ogg:11984"...
282
KService::Ptr service;
283
if (url.fileName().endsWith(".desktop"))
284
service = new KService(Item::urlWithoutProtocol(url));
286
// If link point to a .desktop file then add a launcher, otherwise it's a link
287
if (service && service->isValid())
288
return createItemLauncher(url, parent);
290
return createItemLink(url, parent);
293
#include <qstrlist.h>
296
void ItemFactory::dropItem(QMimeSource *source, Basket *parent, bool fromDrop, QDropEvent::Action action, Item *itemSource)
301
if (source->format(0) == 0L) {
302
// TODO: add a parameter to say if it's from a clipboard paste, a selection paste, or a drop
303
// To be able to say "The clipboard/selection/drop is empty".
304
// KMessageBox::error(parent, i18n("There is no data to insert."), i18n("No Data"));
309
if (Global::debugWindow) {
310
*Global::debugWindow << "<b>Drop :</b>";
311
for(int i = 0; source->format(i); ++i)
312
if ( *(source->format(i)) )
313
*Global::debugWindow << "\t[" + QString::number(i) + "] " + QString(source->format(i));
314
switch (action) { // The source want that we:
315
case QDropEvent::Copy: *Global::debugWindow << ">> Drop action: Copy"; break;
316
case QDropEvent::Move: *Global::debugWindow << ">> Drop action: Move"; break;
317
case QDropEvent::Link: *Global::debugWindow << ">> Drop action: Link"; break;
318
case QDropEvent::Private: *Global::debugWindow << ">> Drop action: Private"; break; // What is it? (Copy?)
319
case QDropEvent::UserAction: *Global::debugWindow << ">> Drop action: UserAction"; break; // Not currently
320
default: *Global::debugWindow << ">> Drop action: Unknown"; // supported by QT!
324
/* Copy or move an Item */
325
if (ItemDrag::canDecode(source)) {
326
if (Global::debugWindow)
327
*Global::debugWindow << ">> Drop an item";
329
if ( action == QDropEvent::Move && itemSource != 0 && // We *move* an *item* : just reinsert it to well place
330
! itemSource->parentBasket()->isLocked() ) { // Unless parent source is locked : then it's a copy
331
// TODO: Copy the file ??? Not for the moment !
332
if (itemSource->parentBasket() == parent) { // If drop in the same basket : keep the file and just move the item
333
if (Global::debugWindow)
334
*Global::debugWindow << ">> DROP : Just move the item to its new position";
335
parent->changeItemPlace(itemSource); //Just remove and reinsert
337
} else { // Drop to another basket
338
// Another way to do the thing : move the file (eventually rename it) : will be deleted
339
// for the original basket (so, old item deleted) and insert the new item.
340
if (Global::debugWindow)
341
*Global::debugWindow << ">> DROP : Move to another basket";
342
item = ItemDrag::decode(source, parent, /*move=*/true); // Filename will be kept
343
parent->insertItem(item); // (eventualy with a number added)
345
//if (!item->useFile()) // If useFile(), ItemDrag::decode() will move the file!
346
// FIXME: text/html/image items aren't managed the same way other useFile() items are :-/
347
if (item->type() != Item::Animation && item->type() != Item::Sound &&
348
item->type() != Item::File && item->type() != Item::Launcher &&
349
item->type() != Item::Unknow)
350
((Item*)itemSource)->parentBasket()->delItem(itemSource, false); // Delete the item, & file whereas it's a mirror
353
} // Else, at this point it's a copy (link an item not supported : act as a copy !)
355
// Do not try to copy the file, because if it is a cut and paste, item file could be deleted and then re-created
356
item = ItemDrag::decode(source, parent, /*move=*/false);
357
parent->insertItem(item);
362
/* Else : Drop object to item */
365
if ( QImageDrag::decode(source, pixmap) ) {
366
createItemImage(pixmap, parent);
370
// KColorDrag::decode() is buggy and can trheat strings like "#include <foo.h>" as a black color
371
// The correct "ideal" code:
373
if ( KColorDrag::decode(source, color) ) {
374
createItemColor(color, parent);
377
// And then the hack (if provide color MIME type or a text that contains color), using createItem Color RegExp:
379
QRegExp exp("^#(?:[a-fA-F\\d]{3}){1,4}$");
380
if (source->provides("application/x-color") ||
381
(QTextDrag::decode(source, hack) && (exp.search(hack) != -1)) ) {
383
if ( KColorDrag::decode(source, color) )
384
if (createItemColor(color, parent))
386
// Theorically it should be returned. If not, continue by dropping other things
390
if ( KURLDrag::decode(source, urls) ) {
391
if (!fromDrop) // If it's a Paste, we should know if files should be copied (copy&paste) or moved (cut&paste)
392
if (source->provides("application/x-kde-cutselection")) {
393
QByteArray array = source->encodedData("application/x-kde-cutselection");
394
if ( !array.isEmpty() && QCString(array.data(), array.size() + 1).at(0) == '1' )
395
action = QDropEvent::Move;
397
dropURLs(urls, parent, action, fromDrop);
401
// FIXME: use dropURLs() also from Mozilla?
404
* Mozilla's stuff sometimes uses utf-16-le - little-endian UTF-16.
406
* This has the property that for the ASCII subset case (And indeed, the
407
* ISO-8859-1 subset, I think), if you treat it as a C-style string,
408
* it'll come out to one character long in most cases, since it looks
411
* "<\0H\0T\0M\0L\0>\0"
413
* A strlen() call on that will give you 1, which simply isn't correct.
414
* That might, I suppose, be the answer, or something close.
416
* Also, Mozilla's drag/drop code predates the use of MIME types in XDnD
417
* - hence it'll throw about STRING and UTF8_STRING quite happily, hence
418
* the odd named types.
420
* Thanks to Dave Cridland for having said me that.
422
if (source->provides("text/x-moz-url")) { // FOR MOZILLA
423
// Get the array and create a QChar array of 1/2 of the size
424
QByteArray mozilla = source->encodedData("text/x-moz-url");
425
QMemArray<QChar> chars( mozilla.count() / 2 );
426
// A small debug work to know the value of each bytes
427
if (Global::debugWindow)
428
for (uint i = 0; i < mozilla.count(); i++)
429
*Global::debugWindow << QString("'") + QChar(mozilla[i]) + "' " + QString::number(int(mozilla[i]));
430
// text/x-moz-url give the URL followed by the link title and separed by OxOA (10 decimal: new line?)
433
// For each little endian mozilla chars, copy it to the array of QChars
434
for (uint i = 0; i < mozilla.count(); i += 2) {
435
chars[i/2] = QChar(mozilla[i], mozilla[i+1]);
436
if (mozilla[i] == 0x0A) {
438
name = &(chars[i/2+1]);
441
// Create a QString that take the address of the first QChar and a length
442
if (name == 0L) { // We haven't found name (FIXME: Is it possible ?)
443
QString normalHtml(&(chars[0]), chars.size());
444
createItemLink(normalHtml, parent);
446
QString normalHtml( &(chars[0]), size );
447
QString normalTitle( name, chars.size()-size-1);
448
createItemLink(normalHtml, normalTitle, parent);
453
if (source->provides("text/html")) {
455
QCString subtype("html");
456
// If the text/html comes from Mozilla or GNOME it can be UTF-16 encoded: we need ExtendedTextDrag to check that
457
ExtendedTextDrag::decode(source, html, subtype);
458
createItemHtml(html, parent);
463
// If the text/plain comes from GEdit or GNOME it can be empty: we need ExtendedTextDrag to check other MIME types
464
if ( ExtendedTextDrag::decode(source, text) ) {
465
createItem(text, parent);
469
/* Unsucceful drop */
470
createItemUnknow(source, parent, "");
471
QString message = i18n("<p>BasKet doesn't support the data you've dropped.<br>"
472
"It however created a generic item, allowing you to drag or copy "
473
"them to an application that understand them.</p>"
474
"If you want BasKet to support them, please contact developer or visit the "
475
"<a href=\"http://basket.kde.org/dropdb.php\">BasKet Drop Database</a>.</p>");
476
KMessageBox::information(parent, message, i18n("Unsupported MIME Type(s)"),
477
"unsupportedDropInfo", KMessageBox::AllowLink);
481
Item* ItemFactory::createItemUnknow(QMimeSource *source, Basket *parent, const QString &annotations)
483
// Save the MimeSource in a file: create and open the file:
484
QString fileName = createFileForNewItem(parent, "unknow");
485
QFile file(parent->fullPath() + fileName);
486
if ( ! file.open(IO_WriteOnly) )
488
QDataStream stream(&file);
491
for (int i = 0; source->format(i); ++i)
492
if ( *(source->format(i)) )
493
stream << QString(source->format(i)); // Output the '\0'-terminated format name string
495
// Echo end of MIME types list delimiter:
498
// Echo the length (in bytes) and then the data, and then same for next MIME type:
499
for (int i = 0; source->format(i); ++i)
500
if ( *(source->format(i)) ) {
501
QByteArray data = source->encodedData(source->format(i));
502
stream << (Q_UINT32)data.count();
503
stream.writeRawBytes(data.data(), data.count());
507
Item *item = new Item(fileName, Item::Unknow, annotations, false, parent);
508
item->loadContent(); // FIXME: Why not loadContent() in the contructor ???????????? I think I should
509
parent->insertItem(item);
515
void ItemFactory::dropURLs(KURL::List urls, Basket *parent, QDropEvent::Action action, bool fromDrop)
519
// Drop ONLY LINKS for this type of basket!
520
// This prevent cutting a file in Konqueror to be immediatly pasted into BasKet!!
521
if (parent->isAClipboard()) {
522
for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it ) {
523
item = createItemLinkOrLauncher(*it, parent);
524
// After last item was inserted, the insert position is reseted : re-set it
525
parent->m_insertAtItem = item;
526
parent->m_insertAfter = true;
531
// TODO: Basket::insertItems(QPtrList<Item> items) or Basket::insertItemAndDon'tReset(Item *item)
532
//bool insertAfter = parent->m_insertAfter; // To past all URLS at the same place (and not one in the right place
533
// and the others at begin or end)
534
// BEGIN Insertion work
535
int shouldAsk = 0; // shouldAsk==0: don't ask ; shouldAsk==1: ask for "file" ; shouldAsk>=2: ask for "files"
536
bool shiftPressed = Keyboard::shiftPressed();
537
bool ctrlPressed = Keyboard::controlPressed();
538
bool modified = fromDrop && (shiftPressed || ctrlPressed);
540
if (modified) // Then no menu + modified action
541
; // action is already set: no work to do
542
else if (fromDrop) { // Compute if user should be asked or not
543
for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it )
544
if ((*it).protocol() != "mailto") { // Do not ask when dropping mail address :-)
546
if (shouldAsk == 1/*2*/) // Sufficient
550
KPopupMenu menu(parent);
551
menu.insertItem( SmallIconSet("goto"), i18n("&Move Here"), 0 );
552
menu.insertItem( SmallIconSet("editcopy"), i18n("&Copy Here"), 1 );
553
menu.insertItem( SmallIconSet("html"), i18n("&Link Here"), 2 );
554
menu.insertSeparator();
555
menu.insertItem( SmallIconSet("cancel"), i18n("C&ancel"), 3 );
556
int id = menu.exec(QCursor::pos());
558
case 0: action = QDropEvent::Move; break;
559
case 1: action = QDropEvent::Copy; break;
560
case 2: action = QDropEvent::Link; break;
565
} else { // fromPaste
569
/* Policy of drops of URL:
570
* Email: [Modifier keys: Useless]
571
+ - Link mail address
572
* Remote URL: [Modifier keys: {Copy,Link}]
573
+ - Download as Image, Animation and Launcher
575
* Local URL: [Modifier keys: {Copy,Move,Link}]
576
* - Copy as Image, Animation and Launcher (or mirror?) [Modifier keys: {Copy,Move,Link}]
577
* - Link directory [Modifier keys: Useless]
578
* - Make Launcher of executable [Modifier keys: {Copy_exec,Move_exec,Link_Launcher}]
579
* - Ask for file (if use want to copy and it is a sound: make Sound)
580
* Policy of pastes of URL: [NO modifier keys]
582
* - But copy when ask should be done
583
* - Unless cut-selection is true: move files instead
584
* Policy of file created in the basket dir: [NO modifier keys]
585
* - View as Image, Animation, Sound, Launcher
588
for ( KURL::List::iterator it = urls.begin(); it != urls.end(); ++it ) {
589
if ( ((*it).protocol() == "mailto") ||
590
(action == QDropEvent::Link) )
591
item = createItemLinkOrLauncher(*it, parent);
592
else if (!(*it).isLocalFile()) {
593
if ( action != QDropEvent::Link && (maybeImageOrAnimation(*it)/* || maybeSound(*it)*/) )
594
item = copyFileAndLoad(*it, parent);
596
item = createItemLinkOrLauncher(*it, parent);
598
if (action == QDropEvent::Copy)
599
item = copyFileAndLoad(*it, parent);
600
else if (action == QDropEvent::Move)
601
item = moveFileAndLoad(*it, parent);
603
item = createItemLinkOrLauncher(*it, parent);
606
// END Insertion work
607
// After last item was inserted, the insert position is reseted : re-set it
608
parent->m_insertAtItem = item;
609
parent->m_insertAfter = true;
613
// mayBeLauncher: url.url().endsWith(".desktop");
615
bool ItemFactory::maybeText(const KURL &url)
617
QString path = url.url().lower();
618
return path.endsWith(".txt");
621
bool ItemFactory::maybeHtml(const KURL &url)
623
QString path = url.url().lower();
624
return path.endsWith(".html") || path.endsWith(".htm");
627
bool ItemFactory::maybeImageOrAnimation(const KURL &url)
629
/* Examples on my machine:
630
QImageDrag can understands
631
{"image/png", "image/bmp", "image/jpeg", "image/pgm", "image/ppm", "image/xbm", "image/xpm"}
632
QImageIO::inputFormats() returns
633
{"BMP", "GIF", "JPEG", "MNG", "PBM", "PGM", "PNG", "PPM", "XBM", "XPM"}
634
QImageDecoder::inputFormats():
635
{"GIF", "MNG", "PNG"} */
636
QStrList list = QImageIO::inputFormats();
637
list.prepend("jpg"); // Since QImageDrag return only "JPEG" and extensions can be "JPG"; preprend for heuristic optim.
639
QString path = url.url().lower();
640
for (s = list.first(); s; s = list.next())
641
if (path.endsWith(QString(".") + QString(s).lower()))
643
// TODO: Search real MIME type for local files?
647
bool ItemFactory::maybeAnimation(const KURL &url)
649
QString path = url.url().lower();
650
return path.endsWith(".mng") || path.endsWith(".gif");
653
bool ItemFactory::maybeSound(const KURL &url)
655
QString path = url.url().lower();
656
return path.endsWith(".mp3") || path.endsWith(".ogg");
659
bool ItemFactory::maybeLauncher(const KURL &url)
661
QString path = url.url().lower();
662
return path.endsWith(".desktop");
667
Item* ItemFactory::copyFileAndLoad(const KURL &url, Basket *parent)
669
QString fileName = fileNameForNewItem(parent, url.fileName());
670
QString fullPath = parent->fullPathForFileName(fileName);
672
if (Global::debugWindow)
673
*Global::debugWindow << "copyFileAndLoad: " + url.prettyURL() + " to " + fullPath;
675
QString annotations = i18n("Original file: %1").arg(url.prettyURL());
676
parent->dontCareOfCreation(fullPath);
677
KIO::CopyJob *copyJob = KIO::copy(url, KURL(fullPath));
678
parent->connect( copyJob, SIGNAL(copyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)),
679
parent, SLOT(slotCopyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)) );
681
Item::Type type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
682
Item *item = new Item(fileName, type, annotations, false, parent);
683
parent->insertItem(item);
689
Item* ItemFactory::moveFileAndLoad(const KURL &url, Basket *parent)
691
// Globally the same as copyFileAndLoad() but move instead of copy (KIO::move())
692
QString fileName = fileNameForNewItem(parent, url.fileName());
693
QString fullPath = parent->fullPathForFileName(fileName);
695
if (Global::debugWindow)
696
*Global::debugWindow << "moveFileAndLoad: " + url.prettyURL() + " to " + fullPath;
698
QString annotations = i18n("Original file: %1").arg(url.prettyURL());
699
parent->dontCareOfCreation(fullPath);
700
KIO::CopyJob *copyJob = KIO::move(url, KURL(fullPath));
701
parent->connect( copyJob, SIGNAL(copyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)),
702
parent, SLOT(slotCopyingDone(KIO::Job *, const KURL &, const KURL &, bool, bool)) );
704
Item::Type type = typeForURL(url, parent); // Use the type of the original file because the target doesn't exist yet
705
Item *item = new Item(fileName, type, annotations, false, parent);
706
parent->insertItem(item);
712
Item* ItemFactory::loadFile(const QString &fileName, Basket *parent, const QString &annotations)
716
// The file MUST exists
717
QFileInfo file( Item::urlWithoutProtocol(parent->fullPathForFileName(fileName)) );
718
if ( ! file.exists() )
721
Item::Type type = typeForURL(parent->fullPathForFileName(fileName), parent);
722
item = new Item(fileName, type, annotations, false, parent);
724
parent->insertItem(item);
730
Item::Type ItemFactory::typeForURL(const KURL &url, Basket *parent)
732
/* KMimeType::Ptr kMimeType = KMimeType::findByURL(url);
733
if (Global::debugWindow)
734
*Global::debugWindow << "typeForURL: " + kMimeType->parentMimeType();//property("MimeType").toString();*/
735
bool viewText = parent->viewFileContent(Basket::FileText);
736
bool viewHTML = parent->viewFileContent(Basket::FileHTML);
737
bool viewImage = parent->viewFileContent(Basket::FileImage);
738
bool viewSound = parent->viewFileContent(Basket::FileSound);
740
KFileMetaInfo metaInfo(url);
741
if (Global::debugWindow && metaInfo.isEmpty())
742
*Global::debugWindow << "typeForURL: metaInfo is empty for " + url.prettyURL();
743
if (metaInfo.isEmpty()) { // metaInfo is empty for GIF files on my machine !
744
if (viewText && maybeText(url)) return Item::Text;
745
else if (viewHTML && (maybeHtml(url))) return Item::Html;
746
else if (viewImage && maybeAnimation(url)) return Item::Animation; // See Item::movieStatus(int)
747
else if (viewImage && maybeImageOrAnimation(url)) return Item::Image; // for more explanations
748
else if (viewSound && maybeSound(url)) return Item::Sound;
749
else if (maybeLauncher(url)) return Item::Launcher;
750
else return Item::File;
752
QString mimeType = metaInfo.mimeType();
754
if (Global::debugWindow)
755
*Global::debugWindow << "typeForURL: " + url.prettyURL() + " ; MIME type = " + mimeType;
757
if (mimeType == "application/x-desktop") return Item::Launcher;
758
else if (viewText && mimeType.startsWith("text/plain")) return Item::Text;
759
else if (viewHTML && mimeType.startsWith("text/html")) return Item::Html;
760
else if (viewImage && mimeType == "movie/x-mng") return Item::Animation;
761
else if (viewImage && mimeType == "image/gif") return Item::Animation;
762
else if (viewImage && mimeType.startsWith("image/")) return Item::Image;
763
else if (viewSound && mimeType.startsWith("audio/")) return Item::Sound;
764
else return Item::File;
767
QString ItemFactory::fileNameForNewFile(const QString &wantedName, const QString &destFolder)
769
QString fileName = wantedName;
770
QString fullName = destFolder + fileName;
771
QString extension = "";
775
// First check if the file do not exists yet (simplier and more often case)
776
dir = QDir(fullName);
777
if ( ! dir.exists(fullName) )
780
// Find the file extension, if it exists : Split fileName in fileName and extension
781
// Example : fileName == "item5-3.txt" => fileName = "item5-3" and extension = ".txt"
782
int extIndex = fileName.findRev('.');
783
if (extIndex != -1 && extIndex != int(fileName.length()-1)) { // Extension found and fileName do not ends with '.' !
784
extension = fileName.mid(extIndex);
785
fileName.truncate(extIndex);
786
} // else fileName = fileName and extension = ""
788
// Find the file number, if it exists : Split fileName in fileName and number
789
// Example : fileName == "item5-3" => fileName = "item5" and number = 3
790
int extNumber = fileName.findRev('-');
791
if (extNumber != -1 && extNumber != int(fileName.length()-1)) { // Number found and fileName do not ends with '-' !
793
int theNumber = fileName.mid(extNumber + 1).toInt(&isANumber);
796
fileName.truncate(extNumber);
798
} // else fileName = fileName and number = 2 (because if the file already exists, the genereated name is at last the 2nd)
801
for (/*int number = 2*/; ; ++number) { // TODO: FIXME: If overflow ???
802
finalName = fileName + "-" + QString::number(number) + extension;
803
fullName = destFolder + finalName;
804
dir = QDir(fullName);
805
if ( ! dir.exists(fullName) )
812
QString ItemFactory::fileNameForNewItem(Basket *parent, const QString &wantedName)
814
return fileNameForNewFile(wantedName, parent->fullPath());
817
// Create a file to store a new item in Basket parent and with extension extension.
818
// If wantedName is provided, the function will first try to use this file name, or derive it if it's impossible
819
// (extension willn't be used for that case)
820
QString ItemFactory::createFileForNewItem(Basket *parent, const QString &extension, const QString &wantedName)
827
if (wantedName.isEmpty()) {
829
for (/*int nb = 1*/; ; ++nb) { // TODO: FIXME: If overflow ???
830
fileName = "item" + QString::number(nb)/*.rightJustify(5, '0')*/ + "." + extension;
831
fullName = parent->fullPath() + fileName;
832
dir = QDir(fullName);
833
if ( ! dir.exists(fullName) )
837
fileName = fileNameForNewItem(parent, wantedName);
838
fullName = parent->fullPath() + fileName;
842
parent->dontCareOfCreation(fullName);
843
QFile file(fullName);
844
file.open(IO_WriteOnly);
850
QFont ItemFactory::fontForFontType(int type)
855
case 1: font.setFamily("SansSerif"); break;
856
case 2: font.setFamily("Serif"); break;
857
case 3: font.setFamily("Courier"); break;
858
default: font.setFamily("System"); // case O or another illegal type...
864
QString ItemFactory::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, 5); // 5 == QString("file:").length() - 1
877
else if (title.startsWith("http://"))
878
title = title.remove(0, 7); // 7 == QString("http://").length() - 1
880
if ( ! url.isLocalFile() ) {
881
if (title.endsWith("/index.html") && title.length() > 11)
882
title.truncate(title.length() - 11); // 11 == QString("/index.html").length()
883
else if (title.endsWith("/index.htm") && title.length() > 10)
884
title.truncate(title.length() - 10); // 10 == QString("/index.htm").length()
885
else if (title.endsWith("/index.xhtml") && title.length() > 12)
886
title.truncate(title.length() - 12); // 12 == QString("/index.xhtml").length()
887
else if (title.endsWith("/index.php") && title.length() > 10)
888
title.truncate(title.length() - 10); // 10 == QString("/index.php").length()
889
else if (title.endsWith("/index.php3") && title.length() > 11)
890
title.truncate(title.length() - 11); // 11 == QString("/index.php3").length()
891
else if (title.endsWith("/index.php4") && title.length() > 11)
892
title.truncate(title.length() - 11); // 11 == QString("/index.php4").length()
893
else if (title.endsWith("/index.php5") && title.length() > 11)
894
title.truncate(title.length() - 11); // 11 == QString("/index.php5").length()
897
if (title.length() > 2 && title.endsWith("/")) // length > 2 because "/" and "~/" shouldn't be transformed to "" and "~"
898
title.truncate(title.length() - 1); // eg. transform "www.kde.org/" to "www.kde.org"
903
QString ItemFactory::iconForURL(const KURL &url)
905
QString icon = KMimeType::iconForURL(url.url());
906
if ( url.protocol() == "mailto" )
911
// TODO: Can I add "autoTitle" and "autoIcon" entries to .desktop files? or just store them in basket, as now...
913
/* Try our better to find an icon suited to the command line
914
* eg. "/usr/bin/kwrite-3.2 ~/myfile.txt /home/other/file.xml"
915
* will give the "kwrite" icon!
917
QString ItemFactory::iconForCommand(const QString &command)
921
// 1. Use first word as icon (typically the program without argument)
922
icon = QStringList::split(' ', command).first();
923
// 2. If the command is a full path, take only the program file name
924
icon = icon.mid(icon.findRev('/') + 1); // strip path if given [But it doesn't care of such
925
// "myprogram /my/path/argument" -> return "argument". Would
926
// must first strip first word and then strip path... Useful ??
927
// 3. Use characters before any '-' (e.g. use "gimp" icon if run command is "gimp-1.3")
928
if ( ! isIconExist(icon) )
929
icon = QStringList::split('-', icon).first();
930
// 4. If the icon still not findable, use a generic icon
931
if ( ! isIconExist(icon) )
937
bool ItemFactory::isIconExist(const QString &icon)
939
return ! kapp->iconLoader()->loadIcon(icon, KIcon::NoGroup, 16, KIcon::DefaultState, 0L, true).isNull();
942
void ItemFactory::insertEmpty(int type, Basket *parent)
949
item = ItemFactory::createItemText("", parent);
952
item = ItemFactory::createItemHtml("", parent);
955
pixmap = new QPixmap( QSize(Settings::defImageX(), Settings::defImageY()) );
957
pixmap->setMask(pixmap->createHeuristicMask());
958
item = ItemFactory::createItemImage(*pixmap, parent);
961
item = ItemFactory::createItemLink(KURL(), parent);
964
item = ItemFactory::createItemLauncher(KURL(), parent);
967
item = ItemFactory::createItemColor(Qt::black, parent);
971
parent->ensureVisibleItem(item);
972
parent->unselectAllBut(item);
973
parent->setFocusedItem(item);
978
void ItemFactory::insertWizard(int type, Basket *parent)
980
KOpenWithDlg *dlg = 0L;
986
case 1: // importKmenuLauncher
987
dlg = new KOpenWithDlg(parent);
988
dlg->setSaveNewApplications(true); // To create temp file, needed by createItemLauncher()
990
if (dlg->service()) {
991
// * locateLocal() return a local file even if it is a system wide one (local one doesn't exists)
992
// * desktopEntryPath() returns the full path for system wide ressources, but relative path if in home
993
QString serviceUrl = dlg->service()->desktopEntryPath();
994
if ( ! serviceUrl.startsWith("/") )
995
serviceUrl = dlg->service()->locateLocal(); //locateLocal("xdgdata-apps", serviceUrl);
996
item = createItemLauncher(serviceUrl, parent);
999
case 2: // importIcon
1000
iconName = KIconDialog::getIcon( KIcon::Desktop, KIcon::Application, false, Settings::defIconSize() );
1001
if ( ! iconName.isEmpty() )
1002
item = createItemImage( DesktopIcon(iconName, Settings::defIconSize()),
1003
parent, i18n("Icon name: %1").arg(iconName) );
1004
// TODO: wantedName = iconName !!!!!!!!!!!!!!
1006
case 3: //loadFromFile
1007
url = KFileDialog::getOpenURL( QString::null, QString::null, parent,
1008
i18n("Load a File Content into an Item") );
1009
if ( ! url.isEmpty() )
1010
item = copyFileAndLoad(url, parent);
1012
case 4: // mirrorFile
1013
url = KFileDialog::getOpenURL( QString::null, QString::null, parent,
1014
i18n("Mirror a File Content into an Item") );
1015
if ( ! url.isEmpty() )
1016
item = loadFile(Item::urlWithoutProtocol(url), parent);
1020
parent->ensureVisibleItem(item);
1021
parent->unselectAllBut(item);
1022
parent->setFocusedItem(item);