Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kmenuedit / treeview.cpp
blob168b0314d8ba3e637d6a27a2fc1c3b400c67e830
1 /*
2 * Copyright (C) 2000 Matthias Elter <elter@kde.org>
3 * Copyright (C) 2001-2002 Raffaele Sandrini <sandrini@kde.org>
4 * Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include <unistd.h>
24 #include <QCursor>
25 #include <QDataStream>
26 #include <QDir>
27 #include <Qt3Support/Q3ColorDrag>
28 #include <QEvent>
29 #include <QFileInfo>
30 #include <Qt3Support/Q3Header>
31 #include <QPainter>
32 #include <QRegExp>
33 //Added by qt3to4:
34 #include <QPixmap>
35 #include <Q3PtrList>
36 #include <QFrame>
37 #include <QDropEvent>
38 #include <QMenu>
40 #include <kaction.h>
41 #include <kactioncollection.h>
42 #include <kapplication.h>
43 #include <kbuildsycocaprogressdialog.h>
44 #include <kdebug.h>
45 #include <kdesktopfile.h>
46 #include <kglobal.h>
47 #include <kiconloader.h>
48 #include <kinputdialog.h>
49 #include <klocale.h>
50 #include <kmessagebox.h>
51 #include <kservice.h>
52 #include <kservicegroup.h>
53 #include <kconfig.h>
54 #include <kconfiggroup.h>
55 #include <kstandarddirs.h>
56 #include <k3multipledrag.h>
57 #include <k3urldrag.h>
59 #include "treeview.h"
60 #include "treeview.moc"
61 #include "khotkeys.h"
62 #include "menufile.h"
63 #include "menuinfo.h"
65 #define MOVE_FOLDER 'M'
66 #define COPY_FOLDER 'C'
67 #define MOVE_FILE 'm'
68 #define COPY_FILE 'c'
69 #define COPY_SEPARATOR 'S'
71 TreeItem::TreeItem(Q3ListViewItem *parent, Q3ListViewItem *after, const QString& menuId, bool __init)
72 :Q3ListViewItem(parent, after), _hidden(false), _init(__init), _layoutDirty(false), _menuId(menuId),
73 m_folderInfo(0), m_entryInfo(0) {}
75 TreeItem::TreeItem(Q3ListView *parent, Q3ListViewItem *after, const QString& menuId, bool __init)
76 : Q3ListViewItem(parent, after), _hidden(false), _init(__init), _layoutDirty(false), _menuId(menuId),
77 m_folderInfo(0), m_entryInfo(0) {}
79 void TreeItem::setName(const QString &name)
81 _name = name;
82 update();
85 void TreeItem::setHidden(bool b)
87 if (_hidden == b) return;
88 _hidden = b;
89 update();
92 void TreeItem::update()
94 QString s = _name;
95 if (_hidden)
96 s += i18n(" [Hidden]");
97 setText(0, s);
100 void TreeItem::setOpen(bool o)
102 if (o)
103 load();
105 Q3ListViewItem::setOpen(o);
108 void TreeItem::load()
110 if (m_folderInfo && !_init)
112 _init = true;
113 TreeView *tv = static_cast<TreeView *>(listView());
114 tv->fillBranch(m_folderInfo, this);
118 void TreeItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align )
120 Q3ListViewItem::paintCell(p, cg, column, width, align);
122 if (!m_folderInfo && !m_entryInfo)
124 // Draw Separator
125 int h = (height() / 2) -1;
126 if (isSelected())
127 p->setPen( cg.color( QPalette::HighlightedText ) );
128 else
129 p->setPen( cg.color( QPalette::Text ) );
130 p->drawLine(0, h,
131 width, h);
135 void TreeItem::setup()
137 Q3ListViewItem::setup();
138 if (!m_folderInfo && !m_entryInfo)
139 setHeight(8);
142 static QPixmap appIcon(const QString &iconName)
144 QPixmap normal = KIconLoader::global()->loadIcon(iconName, KIconLoader::Small, 0, KIconLoader::DefaultState, QStringList(), 0L, true);
145 // make sure they are not larger than 20x20
146 if (normal.width() > 20 || normal.height() > 20)
148 QImage tmp = normal.toImage();
149 tmp = tmp.scaled(20, 20, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
150 normal = QPixmap::fromImage(tmp);
152 return normal;
156 TreeView::TreeView( bool controlCenter, KActionCollection *ac, QWidget *parent, const char *name )
157 : K3ListView(parent), m_ac(ac), m_rmb(0), m_clipboard(0),
158 m_clipboardFolderInfo(0), m_clipboardEntryInfo(0),
159 m_controlCenter(controlCenter), m_layoutDirty(false)
161 setObjectName(name);
162 setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
163 setAllColumnsShowFocus(true);
164 setRootIsDecorated(true);
165 setSorting(-1);
166 setAcceptDrops(true);
167 setDropVisualizer(true);
168 setDragEnabled(true);
169 setMinimumWidth(240);
171 addColumn("");
172 header()->hide();
174 connect(this, SIGNAL(dropped(QDropEvent*, Q3ListViewItem*, Q3ListViewItem*)),
175 SLOT(slotDropped(QDropEvent*, Q3ListViewItem*, Q3ListViewItem*)));
177 connect(this, SIGNAL(clicked( Q3ListViewItem* )),
178 SLOT(itemSelected( Q3ListViewItem* )));
180 connect(this,SIGNAL(selectionChanged ( Q3ListViewItem * )),
181 SLOT(itemSelected( Q3ListViewItem* )));
183 connect(this, SIGNAL(rightButtonPressed(Q3ListViewItem*, const QPoint&, int)),
184 SLOT(slotRMBPressed(Q3ListViewItem*, const QPoint&)));
186 // connect actions
187 connect(m_ac->action("newitem"), SIGNAL(activated()), SLOT(newitem()));
188 connect(m_ac->action("newsubmenu"), SIGNAL(activated()), SLOT(newsubmenu()));
189 if (m_ac->action("newsep"))
190 connect(m_ac->action("newsep"), SIGNAL(activated()), SLOT(newsep()));
192 m_menuFile = new MenuFile( KStandardDirs::locateLocal("xdgconf-menu", "applications-kmenuedit.menu"));
193 m_rootFolder = new MenuFolderInfo;
194 m_separator = new MenuSeparatorInfo;
195 m_drag = 0;
197 // Read menu format configuration information
198 KSharedConfig::Ptr pConfig = KSharedConfig::openConfig("kickerrc");
199 KConfigGroup cg(pConfig, "menus");
200 m_detailedMenuEntries = cg.readEntry("DetailedMenuEntries", true);
201 if (m_detailedMenuEntries)
203 m_detailedEntriesNamesFirst = cg.readEntry("DetailedEntriesNamesFirst", false);
207 TreeView::~TreeView() {
208 cleanupClipboard();
209 delete m_rootFolder;
210 delete m_separator;
213 void TreeView::setViewMode(bool showHidden)
215 delete m_rmb;
217 // setup rmb menu
218 m_rmb = new QMenu(this);
219 QAction *action;
221 action = m_ac->action("edit_cut");
222 if(action) {
223 m_rmb->addAction( action );
224 action->setEnabled(false);
225 connect(action, SIGNAL(activated()), SLOT(cut()));
228 action = m_ac->action("edit_copy");
229 if(action) {
230 m_rmb->addAction( action );
231 action->setEnabled(false);
232 connect(action, SIGNAL(activated()), SLOT(copy()));
235 action = m_ac->action("edit_paste");
236 if(action) {
237 m_rmb->addAction( action );
238 action->setEnabled(false);
239 connect(action, SIGNAL(activated()), SLOT(paste()));
242 m_rmb->addSeparator();
244 action = m_ac->action("delete");
245 if(action) {
246 m_rmb->addAction( action );
247 action->setEnabled(false);
248 connect(action, SIGNAL(activated()), SLOT(del()));
251 m_rmb->addSeparator();
253 if(m_ac->action("newitem"))
254 m_rmb->addAction( m_ac->action("newitem") );
255 if(m_ac->action("newsubmenu"))
256 m_rmb->addAction( m_ac->action("newsubmenu") );
257 if(m_ac->action("newsep"))
258 m_rmb->addAction( m_ac->action("newsep") );
260 m_showHidden = showHidden;
261 readMenuFolderInfo();
262 fill();
265 void TreeView::readMenuFolderInfo(MenuFolderInfo *folderInfo, KServiceGroup::Ptr folder, const QString &prefix)
267 if (!folderInfo)
269 folderInfo = m_rootFolder;
270 if (m_controlCenter)
271 folder = KServiceGroup::baseGroup("settings");
272 else
273 folder = KServiceGroup::root();
276 if (!folder || !folder->isValid())
277 return;
279 folderInfo->caption = folder->caption();
280 folderInfo->comment = folder->comment();
282 // Item names may contain ampersands. To avoid them being converted
283 // to accelerators, replace them with two ampersands.
284 folderInfo->hidden = folder->noDisplay();
285 folderInfo->directoryFile = folder->directoryEntryPath();
286 folderInfo->icon = folder->icon();
287 QString id = folder->relPath();
288 int i = id.lastIndexOf('/', -2);
289 id = id.mid(i+1);
290 folderInfo->id = id;
291 folderInfo->fullId = prefix + id;
293 foreach(const KSycocaEntry::Ptr &e, folder->entries(true, !m_showHidden, true, m_detailedMenuEntries && !m_detailedEntriesNamesFirst))
295 if (e->isType(KST_KServiceGroup))
297 KServiceGroup::Ptr g(KServiceGroup::Ptr::staticCast(e));
298 MenuFolderInfo *subFolderInfo = new MenuFolderInfo();
299 readMenuFolderInfo(subFolderInfo, g, folderInfo->fullId);
300 folderInfo->add(subFolderInfo, true);
302 else if (e->isType(KST_KService))
304 folderInfo->add(new MenuEntryInfo(KService::Ptr::staticCast(e)), true);
306 else if (e->isType(KST_KServiceSeparator))
308 folderInfo->add(m_separator, true);
313 void TreeView::fill()
315 QApplication::setOverrideCursor(Qt::WaitCursor);
316 clear();
317 fillBranch(m_rootFolder, 0);
318 QApplication::restoreOverrideCursor();
321 QString TreeView::findName(KDesktopFile *df, bool deleted)
323 QString name = df->readName();
324 if (deleted)
326 if (name == "empty")
327 name.clear();
328 if (name.isEmpty())
330 QString file = df->fileName();
331 QString res = df->resource();
333 bool isLocal = true;
334 QStringList files = KGlobal::dirs()->findAllResources(res.toLatin1(), file);
335 for(QStringList::ConstIterator it = files.begin();
336 it != files.end();
337 ++it)
339 if (isLocal)
341 isLocal = false;
342 continue;
345 KDesktopFile df2(*it);
346 name = df2.readName();
348 if (!name.isEmpty() && (name != "empty"))
349 return name;
353 return name;
356 TreeItem *TreeView::createTreeItem(TreeItem *parent, Q3ListViewItem *after, MenuFolderInfo *folderInfo, bool _init)
358 TreeItem *item;
359 if (parent == 0)
360 item = new TreeItem(this, after, QString(), _init);
361 else
362 item = new TreeItem(parent, after, QString(), _init);
364 item->setMenuFolderInfo(folderInfo);
365 item->setName(folderInfo->caption);
366 item->setPixmap(0, appIcon(folderInfo->icon));
367 item->setDirectoryPath(folderInfo->fullId);
368 item->setHidden(folderInfo->hidden);
369 item->setExpandable(true);
370 return item;
373 TreeItem *TreeView::createTreeItem(TreeItem *parent, Q3ListViewItem *after, MenuEntryInfo *entryInfo, bool _init)
375 bool hidden = entryInfo->hidden;
377 TreeItem* item;
378 if (parent == 0)
379 item = new TreeItem(this, after, entryInfo->menuId(), _init);
380 else
381 item = new TreeItem(parent, after, entryInfo->menuId(),_init);
383 QString name;
385 if (m_detailedMenuEntries && entryInfo->description.length() != 0)
387 if (m_detailedEntriesNamesFirst)
389 name = entryInfo->caption + " (" + entryInfo->description + ')';
391 else
393 name = entryInfo->description + " (" + entryInfo->caption + ')';
396 else
398 name = entryInfo->caption;
400 item->setMenuEntryInfo(entryInfo);
401 item->setName(name);
402 item->setPixmap(0, appIcon(entryInfo->icon));
404 item->setHidden(hidden);
405 return item;
408 TreeItem *TreeView::createTreeItem(TreeItem *parent, Q3ListViewItem *after, MenuSeparatorInfo *, bool _init)
410 TreeItem* item;
411 if (parent == 0)
412 item = new TreeItem(this, after, QString(), _init);
413 else
414 item = new TreeItem(parent, after, QString(),_init);
416 return item;
419 void TreeView::fillBranch(MenuFolderInfo *folderInfo, TreeItem *parent)
421 QString relPath = parent ? parent->directory() : QString();
422 Q3PtrListIterator<MenuInfo> it( folderInfo->initialLayout );
423 TreeItem *after = 0;
424 for (MenuInfo *info; (info = it.current()); ++it)
426 MenuEntryInfo *entry = dynamic_cast<MenuEntryInfo*>(info);
427 if (entry)
429 after = createTreeItem(parent, after, entry);
430 continue;
433 MenuFolderInfo *subFolder = dynamic_cast<MenuFolderInfo*>(info);
434 if (subFolder)
436 after = createTreeItem(parent, after, subFolder);
437 continue;
439 MenuSeparatorInfo *separator = dynamic_cast<MenuSeparatorInfo*>(info);
440 if (separator)
442 after = createTreeItem(parent, after, separator);
443 continue;
448 void TreeView::closeAllItems(Q3ListViewItem *item)
450 if (!item) return;
451 while(item)
453 item->setOpen(false);
454 closeAllItems(item->firstChild());
455 item = item->nextSibling();
459 void TreeView::selectMenu(const QString &menu)
461 closeAllItems(firstChild());
463 if (menu.length() <= 1)
465 setCurrentItem(firstChild());
466 clearSelection();
467 return; // Root menu
470 QString restMenu = menu.mid(1);
471 if (!restMenu.endsWith('/'))
472 restMenu += '/';
474 TreeItem *item = 0;
477 int i = restMenu.indexOf("/");
478 QString subMenu = restMenu.left(i+1);
479 restMenu = restMenu.mid(i+1);
481 item = (TreeItem*)(item ? item->firstChild() : firstChild());
482 while(item)
484 MenuFolderInfo *folderInfo = item->folderInfo();
485 if (folderInfo && (folderInfo->id == subMenu))
487 item->setOpen(true);
488 break;
490 item = (TreeItem*) item->nextSibling();
493 while( item && !restMenu.isEmpty());
495 if (item)
497 setCurrentItem(item);
498 ensureItemVisible(item);
502 void TreeView::selectMenuEntry(const QString &menuEntry)
504 TreeItem *item = (TreeItem *) selectedItem();
505 if (!item)
507 item = (TreeItem *) currentItem();
508 while (item && item->isDirectory())
509 item = (TreeItem*) item->nextSibling();
511 else
512 item = (TreeItem *) item->firstChild();
514 while(item)
516 MenuEntryInfo *entry = item->entryInfo();
517 if (entry && (entry->menuId() == menuEntry))
519 setCurrentItem(item);
520 ensureItemVisible(item);
521 return;
523 item = (TreeItem*) item->nextSibling();
527 void TreeView::itemSelected(Q3ListViewItem *item)
529 TreeItem *_item = (TreeItem*)item;
530 bool selected = false;
531 bool dselected = false;
532 if (_item) {
533 selected = true;
534 dselected = _item->isHidden();
537 m_ac->action("edit_cut")->setEnabled(selected);
538 m_ac->action("edit_copy")->setEnabled(selected);
540 if (m_ac->action("delete"))
541 m_ac->action("delete")->setEnabled(selected && !dselected);
543 if(!item)
545 emit disableAction();
546 return;
549 if (_item->isDirectory())
550 emit entrySelected(_item->folderInfo());
551 else
552 emit entrySelected(_item->entryInfo());
555 void TreeView::currentChanged(MenuFolderInfo *folderInfo)
557 TreeItem *item = (TreeItem*)selectedItem();
558 if (item == 0) return;
559 if (folderInfo == 0) return;
561 item->setName(folderInfo->caption);
562 item->setPixmap(0, appIcon(folderInfo->icon));
565 void TreeView::currentChanged(MenuEntryInfo *entryInfo)
567 TreeItem *item = (TreeItem*)selectedItem();
568 if (item == 0) return;
569 if (entryInfo == 0) return;
571 QString name;
573 if (m_detailedMenuEntries && entryInfo->description.length() != 0)
575 if (m_detailedEntriesNamesFirst)
577 name = entryInfo->caption + " (" + entryInfo->description + ')';
579 else
581 name = entryInfo->description + " (" + entryInfo->caption + ')';
584 else
586 name = entryInfo->caption;
588 item->setName(name);
589 item->setPixmap(0, appIcon(entryInfo->icon));
592 QStringList TreeView::fileList(const QString& rPath)
594 QString relativePath = rPath;
596 // truncate "/.directory"
597 int pos = relativePath.lastIndexOf("/.directory");
598 if (pos > 0) relativePath.truncate(pos);
600 QStringList filelist;
602 // loop through all resource dirs and build a file list
603 QStringList resdirlist = KGlobal::dirs()->resourceDirs("apps");
604 for (QStringList::ConstIterator it = resdirlist.begin(); it != resdirlist.end(); ++it)
606 QDir dir((*it) + '/' + relativePath);
607 if(!dir.exists()) continue;
609 dir.setFilter(QDir::Files);
610 dir.setNameFilters(QStringList() << "*.desktop;*.kdelnk");
612 // build a list of files
613 QStringList files = dir.entryList();
614 for (QStringList::ConstIterator it = files.begin(); it != files.end(); ++it) {
615 // does not work?!
616 //if (filelist.contains(*it)) continue;
618 if (relativePath.isEmpty()) {
619 filelist.removeAll(*it); // hack
620 filelist.append(*it);
622 else {
623 filelist.removeAll(relativePath + '/' + *it); //hack
624 filelist.append(relativePath + '/' + *it);
628 return filelist;
631 QStringList TreeView::dirList(const QString& rPath)
633 QString relativePath = rPath;
635 // truncate "/.directory"
636 int pos = relativePath.lastIndexOf("/.directory");
637 if (pos > 0) relativePath.truncate(pos);
639 QStringList dirlist;
641 // loop through all resource dirs and build a subdir list
642 QStringList resdirlist = KGlobal::dirs()->resourceDirs("apps");
643 for (QStringList::ConstIterator it = resdirlist.begin(); it != resdirlist.end(); ++it)
645 QDir dir((*it) + '/' + relativePath);
646 if(!dir.exists()) continue;
647 dir.setFilter(QDir::Dirs);
649 // build a list of subdirs
650 QStringList subdirs = dir.entryList();
651 for (QStringList::ConstIterator it = subdirs.begin(); it != subdirs.end(); ++it) {
652 if ((*it) == "." || (*it) == "..") continue;
653 // does not work?!
654 // if (dirlist.contains(*it)) continue;
656 if (relativePath.isEmpty()) {
657 dirlist.removeAll(*it); //hack
658 dirlist.append(*it);
660 else {
661 dirlist.removeAll(relativePath + '/' + *it); //hack
662 dirlist.append(relativePath + '/' + *it);
666 return dirlist;
669 bool TreeView::acceptDrag(QDropEvent* e) const
671 if (e->provides("application/x-kmenuedit-internal") &&
672 (e->source() == const_cast<TreeView *>(this)))
673 return true;
674 KUrl::List urls;
675 if (K3URLDrag::decode(e, urls) && (urls.count() == 1) &&
676 urls[0].isLocalFile() && urls[0].path().endsWith(".desktop"))
677 return true;
678 return false;
682 static QString createDesktopFile(const QString &file, QString *menuId, QStringList *excludeList)
684 QString base = file.mid(file.lastIndexOf('/')+1);
685 base = base.left(base.lastIndexOf('.'));
687 QRegExp r("(.*)(?=-\\d+)");
688 base = (r.indexIn(base) > -1) ? r.cap(1) : base;
690 QString result = KService::newServicePath(true, base, menuId, excludeList);
691 excludeList->append(*menuId);
692 // Todo for Undo-support: Undo menuId allocation:
694 return result;
697 static KDesktopFile *copyDesktopFile(MenuEntryInfo *entryInfo, QString *menuId, QStringList *excludeList)
699 QString result = createDesktopFile(entryInfo->file(), menuId, excludeList);
700 KDesktopFile *df = entryInfo->desktopFile()->copyTo(result);
701 df->desktopGroup().deleteEntry("Categories"); // Don't set any categories!
703 return df;
706 static QString createDirectoryFile(const QString &file, QStringList *excludeList)
708 QString base = file.mid(file.lastIndexOf('/')+1);
709 base = base.left(base.lastIndexOf('.'));
711 QString result;
712 int i = 1;
713 while(true)
715 if (i == 1)
716 result = base + ".directory";
717 else
718 result = base + QString("-%1.directory").arg(i);
720 if (!excludeList->contains(result))
722 if (KStandardDirs::locate("xdgdata-dirs", result).isEmpty())
723 break;
725 i++;
727 excludeList->append(result);
728 result = KStandardDirs::locateLocal("xdgdata-dirs", result);
729 return result;
733 void TreeView::slotDropped (QDropEvent * e, Q3ListViewItem *parent, Q3ListViewItem*after)
735 if(!e) return;
737 // get destination folder
738 TreeItem *parentItem = static_cast<TreeItem*>(parent);
739 QString folder = parentItem ? parentItem->directory() : QString();
740 MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
742 if (e->source() != this)
744 // External drop
745 KUrl::List urls;
746 if (!K3URLDrag::decode(e, urls) || (urls.count() != 1) || !urls[0].isLocalFile())
747 return;
748 QString path = urls[0].path();
749 if (!path.endsWith(".desktop"))
750 return;
752 QString menuId;
753 QString result = createDesktopFile(path, &menuId, &m_newMenuIds);
754 KDesktopFile orig_df(path);
755 KDesktopFile *df = orig_df.copyTo(result);
756 df->desktopGroup().deleteEntry("Categories"); // Don't set any categories!
758 KService::Ptr s(new KService(df));
759 s->setMenuId(menuId);
761 MenuEntryInfo *entryInfo = new MenuEntryInfo(s, df);
763 QString oldCaption = entryInfo->caption;
764 QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption, oldCaption);
765 entryInfo->setCaption(newCaption);
767 // Add file to menu
768 // m_menuFile->addEntry(folder, menuId);
769 m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
771 // create the TreeItem
772 if(parentItem)
773 parentItem->setOpen(true);
775 // update fileInfo data
776 parentFolderInfo->add(entryInfo);
778 TreeItem *newItem = createTreeItem(parentItem, after, entryInfo, true);
780 setSelected ( newItem, true);
781 itemSelected( newItem);
783 m_drag = 0;
784 setLayoutDirty(parentItem);
785 return;
788 // is there content in the clipboard?
789 if (!m_drag) return;
791 if (m_dragItem == after) return; // Nothing to do
793 int command = m_drag;
794 if (command == MOVE_FOLDER)
796 MenuFolderInfo *folderInfo = m_dragInfo;
797 if (e->action() == QDropEvent::Copy)
799 // Ugh.. this is hard :)
800 // * Create new .directory file
801 // Add
803 else
805 TreeItem *tmpItem = static_cast<TreeItem*>(parentItem);
806 while ( tmpItem )
808 if ( tmpItem == m_dragItem )
810 m_drag = 0;
811 return;
813 tmpItem = static_cast<TreeItem*>(tmpItem->parent() );
816 // Remove MenuFolderInfo
817 TreeItem *oldParentItem = static_cast<TreeItem*>(m_dragItem->parent());
818 MenuFolderInfo *oldParentFolderInfo = oldParentItem ? oldParentItem->folderInfo() : m_rootFolder;
819 oldParentFolderInfo->take(folderInfo);
821 // Move menu
822 QString oldFolder = folderInfo->fullId;
823 QString folderName = folderInfo->id;
824 QString newFolder = m_menuFile->uniqueMenuName(folder, folderName, parentFolderInfo->existingMenuIds());
825 folderInfo->id = newFolder;
827 // Add file to menu
828 //m_menuFile->moveMenu(oldFolder, folder + newFolder);
829 m_menuFile->pushAction(MenuFile::MOVE_MENU, oldFolder, folder + newFolder);
831 // Make sure caption is unique
832 QString newCaption = parentFolderInfo->uniqueMenuCaption(folderInfo->caption);
833 if (newCaption != folderInfo->caption)
835 folderInfo->setCaption(newCaption);
838 // create the TreeItem
839 if(parentItem)
840 parentItem->setOpen(true);
842 // update fileInfo data
843 folderInfo->updateFullId(parentFolderInfo->fullId);
844 folderInfo->setInUse(true);
845 parentFolderInfo->add(folderInfo);
847 if ((parentItem != oldParentItem) || !after)
849 if (oldParentItem)
850 oldParentItem->takeItem(m_dragItem);
851 else
852 takeItem(m_dragItem);
853 if (parentItem)
854 parentItem->insertItem(m_dragItem);
855 else
856 insertItem(m_dragItem);
858 m_dragItem->moveItem(after);
859 m_dragItem->setName(folderInfo->caption);
860 m_dragItem->setDirectoryPath(folderInfo->fullId);
861 setSelected(m_dragItem, true);
862 itemSelected(m_dragItem);
865 else if (command == MOVE_FILE)
867 MenuEntryInfo *entryInfo = m_dragItem->entryInfo();
868 QString menuId = entryInfo->menuId();
870 if (e->action() == QDropEvent::Copy)
873 // Need to copy file and then add it
874 KDesktopFile *df = copyDesktopFile(entryInfo, &menuId, &m_newMenuIds); // Duplicate
875 //UNDO-ACTION: NEW_MENU_ID (menuId)
877 KService::Ptr s(new KService(df));
878 s->setMenuId(menuId);
880 entryInfo = new MenuEntryInfo(s, df);
882 QString oldCaption = entryInfo->caption;
883 QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption, oldCaption);
884 entryInfo->setCaption(newCaption);
886 else
888 del(m_dragItem, false);
889 QString oldCaption = entryInfo->caption;
890 QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption);
891 entryInfo->setCaption(newCaption);
892 entryInfo->setInUse(true);
894 // Add file to menu
895 // m_menuFile->addEntry(folder, menuId);
896 m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
898 // create the TreeItem
899 if(parentItem)
900 parentItem->setOpen(true);
902 // update fileInfo data
903 parentFolderInfo->add(entryInfo);
905 TreeItem *newItem = createTreeItem(parentItem, after, entryInfo, true);
907 setSelected ( newItem, true);
908 itemSelected( newItem);
910 else if (command == COPY_SEPARATOR)
912 if (e->action() != QDropEvent::Copy)
913 del(m_dragItem, false);
915 TreeItem *newItem = createTreeItem(parentItem, after, m_separator, true);
917 setSelected ( newItem, true);
918 itemSelected( newItem);
920 else
922 // Error
924 m_drag = 0;
925 setLayoutDirty(parentItem);
929 void TreeView::startDrag()
931 Q3DragObject *drag = dragObject();
933 if (!drag)
934 return;
936 drag->dragMove();
939 Q3DragObject *TreeView::dragObject()
941 m_dragPath.clear();
942 TreeItem *item = (TreeItem*)selectedItem();
943 if(item == 0) return 0;
945 K3MultipleDrag *drag = new K3MultipleDrag( this );
947 if (item->isDirectory())
949 m_drag = MOVE_FOLDER;
950 m_dragInfo = item->folderInfo();
951 m_dragItem = item;
953 else if (item->isEntry())
955 m_drag = MOVE_FILE;
956 m_dragInfo = 0;
957 m_dragItem = item;
958 QString menuId = item->menuId();
959 m_dragPath = item->entryInfo()->service->entryPath();
960 if (!m_dragPath.isEmpty())
961 m_dragPath = KStandardDirs::locate("apps", m_dragPath);
962 if (!m_dragPath.isEmpty())
964 KUrl url;
965 url.setPath(m_dragPath);
966 drag->addDragObject( new K3URLDrag(url, 0));
969 else
971 m_drag = COPY_SEPARATOR;
972 m_dragInfo = 0;
973 m_dragItem = item;
976 drag->addDragObject( new Q3StoredDrag("application/x-kmenuedit-internal", 0));
977 if ( item->pixmap(0) )
978 drag->setPixmap(*item->pixmap(0));
979 return drag;
982 void TreeView::slotRMBPressed(Q3ListViewItem*, const QPoint& p)
984 TreeItem *item = (TreeItem*)selectedItem();
985 if(item == 0) return;
987 if(m_rmb) m_rmb->exec(p);
990 void TreeView::newsubmenu()
992 TreeItem *parentItem = 0;
993 TreeItem *item = (TreeItem*)selectedItem();
995 bool ok;
996 QString caption = KInputDialog::getText( i18n( "New Submenu" ),
997 i18n( "Submenu name:" ), QString(), &ok, this );
999 if (!ok) return;
1001 QString file = caption;
1002 file.replace('/', '-');
1004 file = createDirectoryFile(file, &m_newDirectoryList); // Create
1006 // get destination folder
1007 QString folder;
1009 if(!item)
1011 parentItem = 0;
1012 folder.clear();
1014 else if(item->isDirectory())
1016 parentItem = item;
1017 item = 0;
1018 folder = parentItem->directory();
1020 else
1022 parentItem = static_cast<TreeItem*>(item->parent());
1023 folder = parentItem ? parentItem->directory() : QString();
1026 MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
1027 MenuFolderInfo *folderInfo = new MenuFolderInfo();
1028 folderInfo->caption = parentFolderInfo->uniqueMenuCaption(caption);
1029 folderInfo->id = m_menuFile->uniqueMenuName(folder, caption, parentFolderInfo->existingMenuIds());
1030 folderInfo->directoryFile = file;
1031 folderInfo->icon = "package";
1032 folderInfo->hidden = false;
1033 folderInfo->setDirty();
1035 KDesktopFile *df = new KDesktopFile(file);
1036 KConfigGroup desktopGroup = df->desktopGroup();
1037 desktopGroup.writeEntry("Name", folderInfo->caption);
1038 desktopGroup.writeEntry("Icon", folderInfo->icon);
1039 df->sync();
1040 delete df;
1041 // Add file to menu
1042 // m_menuFile->addMenu(folder + folderInfo->id, file);
1043 m_menuFile->pushAction(MenuFile::ADD_MENU, folder + folderInfo->id, file);
1045 folderInfo->fullId = parentFolderInfo->fullId + folderInfo->id;
1047 // create the TreeItem
1048 if(parentItem)
1049 parentItem->setOpen(true);
1051 // update fileInfo data
1052 parentFolderInfo->add(folderInfo);
1054 TreeItem *newItem = createTreeItem(parentItem, item, folderInfo, true);
1056 setSelected ( newItem, true);
1057 itemSelected( newItem);
1059 setLayoutDirty(parentItem);
1062 void TreeView::newitem()
1064 TreeItem *parentItem = 0;
1065 TreeItem *item = (TreeItem*)selectedItem();
1067 bool ok;
1068 QString caption = KInputDialog::getText( i18n( "New Item" ),
1069 i18n( "Item name:" ), QString(), &ok, this );
1071 if (!ok) return;
1073 QString menuId;
1074 QString file = caption;
1075 file.replace('/', '-');
1077 file = createDesktopFile(file, &menuId, &m_newMenuIds); // Create
1079 KDesktopFile *df = new KDesktopFile(file);
1080 KConfigGroup desktopGroup = df->desktopGroup();
1081 desktopGroup.writeEntry("Name", caption);
1082 desktopGroup.writeEntry("Type", "Application");
1084 // get destination folder
1085 QString folder;
1087 if(!item)
1089 parentItem = 0;
1090 folder.clear();
1092 else if(item->isDirectory())
1094 parentItem = item;
1095 item = 0;
1096 folder = parentItem->directory();
1098 else
1100 parentItem = static_cast<TreeItem*>(item->parent());
1101 folder = parentItem ? parentItem->directory() : QString();
1104 MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
1106 // Add file to menu
1107 // m_menuFile->addEntry(folder, menuId);
1108 m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
1110 KService::Ptr s(new KService(df));
1111 s->setMenuId(menuId);
1113 MenuEntryInfo *entryInfo = new MenuEntryInfo(s, df);
1115 // create the TreeItem
1116 if(parentItem)
1117 parentItem->setOpen(true);
1119 // update fileInfo data
1120 parentFolderInfo->add(entryInfo);
1122 TreeItem *newItem = createTreeItem(parentItem, item, entryInfo, true);
1124 setSelected ( newItem, true);
1125 itemSelected( newItem);
1127 setLayoutDirty(parentItem);
1130 void TreeView::newsep()
1132 TreeItem *parentItem = 0;
1133 TreeItem *item = (TreeItem*)selectedItem();
1135 if(!item)
1137 parentItem = 0;
1139 else if(item->isDirectory())
1141 parentItem = item;
1142 item = 0;
1144 else
1146 parentItem = static_cast<TreeItem*>(item->parent());
1149 // create the TreeItem
1150 if(parentItem)
1151 parentItem->setOpen(true);
1153 TreeItem *newItem = createTreeItem(parentItem, item, m_separator, true);
1155 setSelected ( newItem, true);
1156 itemSelected( newItem);
1158 setLayoutDirty(parentItem);
1161 void TreeView::cut()
1163 copy( true );
1165 m_ac->action("edit_cut")->setEnabled(false);
1166 m_ac->action("edit_copy")->setEnabled(false);
1167 m_ac->action("delete")->setEnabled(false);
1169 // Select new current item
1170 setSelected( currentItem(), true );
1171 // Switch the UI to show that item
1172 itemSelected( selectedItem() );
1175 void TreeView::copy()
1177 copy( false );
1180 void TreeView::copy( bool cutting )
1182 TreeItem *item = (TreeItem*)selectedItem();
1184 // nil selected? -> nil to copy
1185 if (item == 0) return;
1187 if (cutting)
1188 setLayoutDirty((TreeItem*)item->parent());
1190 // clean up old stuff
1191 cleanupClipboard();
1193 // is item a folder or a file?
1194 if(item->isDirectory())
1196 QString folder = item->directory();
1197 if (cutting)
1199 // Place in clipboard
1200 m_clipboard = MOVE_FOLDER;
1201 m_clipboardFolderInfo = item->folderInfo();
1203 del(item, false);
1205 else
1207 // Place in clipboard
1208 m_clipboard = COPY_FOLDER;
1209 m_clipboardFolderInfo = item->folderInfo();
1212 else if (item->isEntry())
1214 if (cutting)
1216 // Place in clipboard
1217 m_clipboard = MOVE_FILE;
1218 m_clipboardEntryInfo = item->entryInfo();
1220 del(item, false);
1222 else
1224 // Place in clipboard
1225 m_clipboard = COPY_FILE;
1226 m_clipboardEntryInfo = item->entryInfo();
1229 else
1231 // Place in clipboard
1232 m_clipboard = COPY_SEPARATOR;
1233 if (cutting)
1234 del(item, false);
1237 m_ac->action("edit_paste")->setEnabled(true);
1241 void TreeView::paste()
1243 TreeItem *parentItem = 0;
1244 TreeItem *item = (TreeItem*)selectedItem();
1246 // nil selected? -> nil to paste to
1247 if (item == 0) return;
1249 // is there content in the clipboard?
1250 if (!m_clipboard) return;
1252 // get destination folder
1253 QString folder;
1255 if(item->isDirectory())
1257 parentItem = item;
1258 item = 0;
1259 folder = parentItem->directory();
1261 else
1263 parentItem = static_cast<TreeItem*>(item->parent());
1264 folder = parentItem ? parentItem->directory() : QString();
1267 MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
1268 int command = m_clipboard;
1269 if ((command == COPY_FOLDER) || (command == MOVE_FOLDER))
1271 MenuFolderInfo *folderInfo = m_clipboardFolderInfo;
1272 if (command == COPY_FOLDER)
1274 // Ugh.. this is hard :)
1275 // * Create new .directory file
1276 // Add
1278 else if (command == MOVE_FOLDER)
1280 // Move menu
1281 QString oldFolder = folderInfo->fullId;
1282 QString folderName = folderInfo->id;
1283 QString newFolder = m_menuFile->uniqueMenuName(folder, folderName, parentFolderInfo->existingMenuIds());
1284 folderInfo->id = newFolder;
1286 // Add file to menu
1287 // m_menuFile->moveMenu(oldFolder, folder + newFolder);
1288 m_menuFile->pushAction(MenuFile::MOVE_MENU, oldFolder, folder + newFolder);
1290 // Make sure caption is unique
1291 QString newCaption = parentFolderInfo->uniqueMenuCaption(folderInfo->caption);
1292 if (newCaption != folderInfo->caption)
1294 folderInfo->setCaption(newCaption);
1296 // create the TreeItem
1297 if(parentItem)
1298 parentItem->setOpen(true);
1300 // update fileInfo data
1301 folderInfo->fullId = parentFolderInfo->fullId + folderInfo->id;
1302 folderInfo->setInUse(true);
1303 parentFolderInfo->add(folderInfo);
1305 TreeItem *newItem = createTreeItem(parentItem, item, folderInfo);
1307 setSelected ( newItem, true);
1308 itemSelected( newItem);
1311 m_clipboard = COPY_FOLDER; // Next one copies.
1313 else if ((command == COPY_FILE) || (command == MOVE_FILE))
1315 MenuEntryInfo *entryInfo = m_clipboardEntryInfo;
1316 QString menuId;
1318 if (command == COPY_FILE)
1320 // Need to copy file and then add it
1321 KDesktopFile *df = copyDesktopFile(entryInfo, &menuId, &m_newMenuIds); // Duplicate
1323 KService::Ptr s(new KService(df));
1324 s->setMenuId(menuId);
1325 entryInfo = new MenuEntryInfo(s, df);
1327 QString oldCaption = entryInfo->caption;
1328 QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption, oldCaption);
1329 entryInfo->setCaption(newCaption);
1331 else if (command == MOVE_FILE)
1333 menuId = entryInfo->menuId();
1334 m_clipboard = COPY_FILE; // Next one copies.
1336 QString oldCaption = entryInfo->caption;
1337 QString newCaption = parentFolderInfo->uniqueItemCaption(oldCaption);
1338 entryInfo->setCaption(newCaption);
1339 entryInfo->setInUse(true);
1341 // Add file to menu
1342 // m_menuFile->addEntry(folder, menuId);
1343 m_menuFile->pushAction(MenuFile::ADD_ENTRY, folder, menuId);
1345 // create the TreeItem
1346 if(parentItem)
1347 parentItem->setOpen(true);
1349 // update fileInfo data
1350 parentFolderInfo->add(entryInfo);
1352 TreeItem *newItem = createTreeItem(parentItem, item, entryInfo, true);
1354 setSelected ( newItem, true);
1355 itemSelected( newItem);
1357 else
1359 // create separator
1360 if(parentItem)
1361 parentItem->setOpen(true);
1363 TreeItem *newItem = createTreeItem(parentItem, item, m_separator, true);
1365 setSelected ( newItem, true);
1366 itemSelected( newItem);
1368 setLayoutDirty(parentItem);
1371 void TreeView::del()
1373 TreeItem *item = (TreeItem*)selectedItem();
1375 // nil selected? -> nil to delete
1376 if (item == 0) return;
1378 del(item, true);
1380 m_ac->action("edit_cut")->setEnabled(false);
1381 m_ac->action("edit_copy")->setEnabled(false);
1382 m_ac->action("delete")->setEnabled(false);
1383 // Select new current item
1384 setSelected( currentItem(), true );
1385 // Switch the UI to show that item
1386 itemSelected( selectedItem() );
1389 void TreeView::del(TreeItem *item, bool deleteInfo)
1391 TreeItem *parentItem = static_cast<TreeItem*>(item->parent());
1392 // is file a .directory or a .desktop file
1393 if(item->isDirectory())
1395 MenuFolderInfo *folderInfo = item->folderInfo();
1397 // Remove MenuFolderInfo
1398 MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
1399 parentFolderInfo->take(folderInfo);
1400 folderInfo->setInUse(false);
1402 if (m_clipboard == COPY_FOLDER && (m_clipboardFolderInfo == folderInfo))
1404 // Copy + Del == Cut
1405 m_clipboard = MOVE_FOLDER; // Clipboard now owns folderInfo
1408 else
1410 if (folderInfo->takeRecursive(m_clipboardFolderInfo))
1411 m_clipboard = MOVE_FOLDER; // Clipboard now owns m_clipboardFolderInfo
1413 if (deleteInfo)
1414 delete folderInfo; // Delete folderInfo
1417 // Remove from menu
1418 // m_menuFile->removeMenu(item->directory());
1419 m_menuFile->pushAction(MenuFile::REMOVE_MENU, item->directory(), QString());
1421 // Remove tree item
1422 delete item;
1424 else if (item->isEntry())
1426 MenuEntryInfo *entryInfo = item->entryInfo();
1427 QString menuId = entryInfo->menuId();
1429 // Remove MenuFolderInfo
1430 MenuFolderInfo *parentFolderInfo = parentItem ? parentItem->folderInfo() : m_rootFolder;
1431 parentFolderInfo->take(entryInfo);
1432 entryInfo->setInUse(false);
1434 if (m_clipboard == COPY_FILE && (m_clipboardEntryInfo == entryInfo))
1436 // Copy + Del == Cut
1437 m_clipboard = MOVE_FILE; // Clipboard now owns entryInfo
1439 else
1441 if (deleteInfo)
1442 delete entryInfo; // Delete entryInfo
1445 // Remove from menu
1446 QString folder = parentItem ? parentItem->directory() : QString();
1447 // m_menuFile->removeEntry(folder, menuId);
1448 m_menuFile->pushAction(MenuFile::REMOVE_ENTRY, folder, menuId);
1450 // Remove tree item
1451 delete item;
1453 else
1455 // Remove separator
1456 delete item;
1458 setLayoutDirty(parentItem);
1461 void TreeView::cleanupClipboard() {
1462 if (m_clipboard == MOVE_FOLDER)
1463 delete m_clipboardFolderInfo;
1464 m_clipboardFolderInfo = 0;
1466 if (m_clipboard == MOVE_FILE)
1467 delete m_clipboardEntryInfo;
1468 m_clipboardEntryInfo = 0;
1470 m_clipboard = 0;
1473 static QStringList extractLayout(TreeItem *item)
1475 bool firstFolder = true;
1476 bool firstEntry = true;
1477 QStringList layout;
1478 for(;item; item = static_cast<TreeItem*>(item->nextSibling()))
1480 if (item->isDirectory())
1482 if (firstFolder)
1484 firstFolder = false;
1485 layout << ":M"; // Add new folders here...
1487 layout << (item->folderInfo()->id);
1489 else if (item->isEntry())
1491 if (firstEntry)
1493 firstEntry = false;
1494 layout << ":F"; // Add new entries here...
1496 layout << (item->entryInfo()->menuId());
1498 else
1500 layout << ":S";
1503 return layout;
1506 QStringList TreeItem::layout()
1508 QStringList layout = extractLayout(static_cast<TreeItem*>(firstChild()));
1509 _layoutDirty = false;
1510 return layout;
1513 void TreeView::saveLayout()
1515 if (m_layoutDirty)
1517 QStringList layout = extractLayout(static_cast<TreeItem*>(firstChild()));
1518 m_menuFile->setLayout(m_rootFolder->fullId, layout);
1519 m_layoutDirty = false;
1522 Q3PtrList<Q3ListViewItem> lst;
1523 Q3ListViewItemIterator it( this );
1524 while ( it.current() ) {
1525 TreeItem *item = static_cast<TreeItem*>(it.current());
1526 if ( item->isLayoutDirty() )
1528 m_menuFile->setLayout(item->folderInfo()->fullId, item->layout());
1530 ++it;
1534 bool TreeView::save()
1536 saveLayout();
1537 m_rootFolder->save(m_menuFile);
1539 bool success = m_menuFile->performAllActions();
1541 m_newMenuIds.clear();
1542 m_newDirectoryList.clear();
1544 if (success)
1546 KBuildSycocaProgressDialog::rebuildKSycoca(this);
1548 else
1550 KMessageBox::sorry(this, "<qt>"+i18n("Menu changes could not be saved because of the following problem:")+"<br><br>"+
1551 m_menuFile->error()+"</qt>");
1553 return success;
1556 void TreeView::setLayoutDirty(TreeItem *parentItem)
1558 if (parentItem)
1559 parentItem->setLayoutDirty();
1560 else
1561 m_layoutDirty = true;
1564 bool TreeView::isLayoutDirty()
1566 Q3PtrList<Q3ListViewItem> lst;
1567 Q3ListViewItemIterator it( this );
1568 while ( it.current() ) {
1569 if ( static_cast<TreeItem*>(it.current())->isLayoutDirty() )
1570 return true;
1571 ++it;
1573 return false;
1576 bool TreeView::dirty()
1578 return m_layoutDirty || m_rootFolder->hasDirt() || m_menuFile->dirty() || isLayoutDirty();
1581 void TreeView::findServiceShortcut(const KShortcut&cut, KService::Ptr &service)
1583 service = m_rootFolder->findServiceShortcut(cut);