/*************************************************************************** sq_treeview.cpp - description ------------------- begin : Mon Mar 15 2004 copyright : (C) 2004 by Baryshev Dmitry email : ksquirrel.iv@gmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include "ksquirrel.h" #include "sq_iconloader.h" #include "sq_widgetstack.h" #include "sq_filethumbview.h" #include "sq_config.h" #include "sq_treeview.h" #include "sq_treeviewitem.h" #include "sq_threaddirlister.h" #include "sq_navigatordropmenu.h" #include "sq_treeviewmenu.h" #include "sq_hloptions.h" #include "sq_directorybasket.h" SQ_TreeView * SQ_TreeView::m_instance = 0; SQ_FileTreeViewBranch::SQ_FileTreeViewBranch(KFileTreeView *parent, const KURL &url, const TQString &name, const TQPixmap &pix) : KFileTreeBranch(parent, url, name, pix) {} SQ_FileTreeViewBranch::~SQ_FileTreeViewBranch() {} KFileTreeViewItem* SQ_FileTreeViewBranch::createTreeViewItem(KFileTreeViewItem *parent, KFileItem *fileItem) { KFileTreeViewItem *tvi = 0; if(parent && fileItem) tvi = new SQ_TreeViewItem(parent, fileItem, this); return tvi; } /******************************************************************************************************/ SQ_TreeView::SQ_TreeView(TQWidget *parent, const char *name) : KFileTreeView(parent, name) { m_instance = this; itemToBeOpened = 0; m_animTimer = new TQTimer(this); scanTimer = new TQTimer(this); m_ignoreClick = false; connect(m_animTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotAnimation())); connect(scanTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotDelayedScan())); dw = 0; m_recurs = No; lister = new SQ_ThreadDirLister(TQT_TQOBJECT(this)); setupRecursion(); setFrameShape(TQFrame::NoFrame); addColumn("Name"); addColumn("Check"); header()->hide(); header()->moveSection(1, 0); setColumnWidthMode(1, TQListView::Manual); setColumnWidth(1, 18); root = new SQ_FileTreeViewBranch(this, TQDir::rootDirPath(), TQString(), SQ_IconLoader::instance()->loadIcon("folder_red", KIcon::Desktop, KIcon::SizeSmall)); // some hacks to create our SQ_TreeViewItem as root item SQ_TreeViewItem *ritem = new SQ_TreeViewItem(this, new KFileItem(TQDir::rootDirPath(), "inode/directory", S_IFDIR), root); ritem->setText(0, i18n("root")); ritem->setExpandable(true); // oops :) delete root->root(); // set new root root->setRoot(ritem); addBranch(root); // don't show files setDirOnlyMode(root, true); // show '+' setRootIsDecorated(true); // connect signals // Space and Return will open item connect(this, TQT_SIGNAL(spacePressed(TQListViewItem*)), this, TQT_SIGNAL(executed(TQListViewItem*))); connect(this, TQT_SIGNAL(returnPressed(TQListViewItem*)), this, TQT_SIGNAL(executed(TQListViewItem*))); connect(this, TQT_SIGNAL(currentChanged(TQListViewItem *)), this, TQT_SLOT(slotCurrentChanged(TQListViewItem*))); connect(this, TQT_SIGNAL(executed(TQListViewItem*)), this, TQT_SLOT(slotItemExecuted(TQListViewItem*))); connect(this, TQT_SIGNAL(newURL(const KURL&)), this, TQT_SLOT(slotNewURL(const KURL&))); connect(root, TQT_SIGNAL(populateFinished(KFileTreeViewItem *)), this, TQT_SLOT(slotOpened(KFileTreeViewItem *))); connect(root, TQT_SIGNAL(deleteItem(KFileItem *)), this, TQT_SLOT(slotDeleteItemMy(KFileItem *))); setCurrentItem(root->root()); root->setChildRecurse(false); SQ_Config::instance()->setGroup("Fileview"); int sync_type = SQ_Config::instance()->readNumEntry("sync type", 0); // load url, if needed if(sync_type != 1) emitNewURL(SQ_WidgetStack::instance()->url()); setAcceptDrops(true); connect(this, TQT_SIGNAL(dropped(TQDropEvent *, TQListViewItem *, TQListViewItem *)), this, TQT_SLOT(slotDropped(TQDropEvent *, TQListViewItem *, TQListViewItem *))); connect(this, TQT_SIGNAL(contextMenu(KListView *, TQListViewItem *, const TQPoint &)), this, TQT_SLOT(slotContextMenu(KListView *, TQListViewItem *, const TQPoint &))); menu = new SQ_TreeViewMenu(this); if(SQ_HLOptions::instance()->have_directorybasket) { menu->insertSeparator(); menu->insertItem(i18n("Add to Folder Basket"), this, TQT_SLOT(slotAddToFolderBasket())); } } SQ_TreeView::~SQ_TreeView() { lister->terminate(); lister->wait(); delete lister; } void SQ_TreeView::slotCurrentChanged(TQListViewItem *item) { SQ_TreeViewItem *tvi = static_cast(item); if(tvi) cURL = tvi->url(); } void SQ_TreeView::setRecursion(int b) { // avoid duplicate calls if(m_recurs == b) return; TQListViewItemIterator it(this); SQ_TreeViewItem *tvi; // ignore root item ++it; // turn recursion on if(m_recurs == No && b) { dw = new KDirWatch(TQT_TQOBJECT(this)); connect(dw, TQT_SIGNAL(dirty(const TQString &)), this, TQT_SLOT(slotDirty(const TQString &))); dw->blockSignals(true); lister->lock(); while(it.current()) { tvi = static_cast(it.current()); if(tvi) { tvi->setParams((m_recurs == Files || m_recurs == FilesDirs), (m_recurs == Dirs || m_recurs == FilesDirs)); lister->appendURL(tvi->url()); dw->addDir(tvi->path()); } ++it; } lister->unlock(); dw->blockSignals(false); m_recurs = b; if(!lister->running()) scanTimer->start(1, true); } // turn recursion off else { delete dw; dw = 0; m_recurs = b; while(it.current()) { tvi = static_cast(it.current()); // reset item names if(tvi) { tvi->setParams((m_recurs == Files || m_recurs == FilesDirs), (m_recurs == Dirs || m_recurs == FilesDirs)); tvi->setCount(tvi->files(), tvi->dirs()); } ++it; } } } void SQ_TreeView::slotClearChecked() { TQListViewItemIterator it(this); SQ_TreeViewItem *tvi; while(it.current()) { tvi = static_cast(it.current()); if(tvi && tvi->checked()) tvi->setChecked(false); ++it; } } /* * Item executed. Let's pass its url to SQ_WidgetStack (if needed). */ void SQ_TreeView::slotItemExecuted(TQListViewItem *item) { if(!item) return; item->setOpen(true); SQ_Config::instance()->setGroup("Fileview"); int sync_type = SQ_Config::instance()->readNumEntry("sync type", 0); // current sychronization type doesn't require // passing url to SQ_WidgetStack - return if(sync_type == 1) return; KFileTreeViewItem *cur = static_cast(item); KURL Curl = cur->url(); // pass url to SQ_WidgetStack SQ_WidgetStack::instance()->setURLForCurrent(Curl, false); } void SQ_TreeView::emitNewURL(const KURL &url) { // already selected? if(!url.isLocalFile() || url.equals(currentURL(), true) || url.equals(cURL, true)) return; cURL = url; // tree is invisible. // save url for future use if(!isVisible()) { pendingURL = url; pendingURL.adjustPath(1); return; } else pendingURL = KURL(); emit newURL(url); } /* * Set given item visible, current, and populate it. */ void SQ_TreeView::populateItem(KFileTreeViewItem *item) { if(item->url().equals(cURL, true)) { setSelected(item, true); setCurrentItem(item); // 1/5 of height give above items // 4/5 of height give below items setContentsPos(contentsX(), itemPos(item)-visibleHeight()/5); } itemToBeOpened = item; item->setOpen(true); } /* * Load url. */ void SQ_TreeView::slotNewURL(const KURL &url) { KURL k(url); k.adjustPath(1); KURL last = k; TQString s; // divide url to paths. // for example, "/home/krasu/1/2" will be divided to // // "/home/krasu/1/2" // "/home/krasu/1" // "/home/krasu" // "/home" // "/" // while(true) { s = k.path(); // remove "/" s.remove(0, 1); paths.prepend(s); k = k.upURL(); if(k.equals(last, true)) break; last = k; } while(doSearch()) {} } /* * New item is opened. Try to continue loading url. */ void SQ_TreeView::slotOpened(KFileTreeViewItem *item) { if(!item) return; // continue searhing... if(item == itemToBeOpened) doSearch(); } /* * Search first available url in variable 'paths'. Open found item. * If item was found return true. */ bool SQ_TreeView::doSearch() { // all items are opened if(paths.empty()) { itemToBeOpened = 0; return false; } TQStringList::iterator it = paths.begin(); KFileTreeViewItem *found = findItem(root, *it); // item not found. It means that // // * we loaded all subpaths - nothing to do // * item we needed is not loaded yet. populateItem() _now_ is trying to open // new subpath, and we will continue searching in slotOpened(). // if(!found) return false; // ok, item is found // // remove first entry from 'paths' // and try to open item paths.erase(it); populateItem(found); // done, but subpaths are pending... return true; } /* * On show event load saved url, if any. See emitNewURL(). */ void SQ_TreeView::showEvent(TQShowEvent *) { // if pending url is valid if(!pendingURL.isEmpty()) { // set pending url to current url KURL url = pendingURL; // reset pending url pendingURL = KURL(); // finally, load url emit newURL(url); } } void SQ_TreeView::slotNewTreeViewItems(KFileTreeBranch *, const KFileTreeViewItemList &list) { if(m_recurs == No) return; // uuuuuuuggggggghhhhhhh :) KFileTreeViewItemListIterator it(list); KFileTreeViewItem *item; lister->lock(); while((item = it.current())) { lister->appendURL(item->url()); dw->addDir(item->path()); ++it; } lister->unlock(); if(!lister->running()) scanTimer->start(1, true); } void SQ_TreeView::slotDelayedScan() { if(!lister->running()) lister->start(); } void SQ_TreeView::customEvent(TQCustomEvent *e) { if(!e || e->type() != SQ_ItemsEventId) return; SQ_ItemsEvent *ie = static_cast(e); SQ_TreeViewItem *tvi = static_cast(root->findTVIByURL(ie->url())); if(tvi) { tvi->setParams((m_recurs == Files || m_recurs == FilesDirs), (m_recurs == Dirs || m_recurs == FilesDirs)); tvi->setCount(ie->files(), ie->dirs()); } } void SQ_TreeView::slotAnimation() { KFileTreeViewItem *it = m_mapFolders.first(); for(;it;it = m_mapFolders.next()) it->setPixmap(0, SmallIcon("clock")); } void SQ_TreeView::startAnimation(KFileTreeViewItem *item, const char *, uint) { if(!item) return; m_mapFolders.append(item); if(!m_animTimer->isActive()) m_animTimer->start(50, true); } void SQ_TreeView::stopAnimation(KFileTreeViewItem *item) { if(!item) return; int f = m_mapFolders.find(item); if(f != -1) { item->setPixmap(0, itemIcon(item)); m_mapFolders.remove(item); } m_animTimer->stop(); } void SQ_TreeView::viewportResizeEvent(TQResizeEvent *) { setColumnWidth(0, viewport()->width() - columnWidth(1)); triggerUpdate(); } void SQ_TreeView::clearSelection() { if(!m_ignoreClick) KFileTreeView::clearSelection(); } void SQ_TreeView::setSelected(TQListViewItem *item, bool selected) { if(!m_ignoreClick) KFileTreeView::setSelected(item, selected); } void SQ_TreeView::setCurrentItem(TQListViewItem *item) { if(!m_ignoreClick) KFileTreeView::setCurrentItem(item); } void SQ_TreeView::setOpen(TQListViewItem *item, bool open) { if(!m_ignoreClick) KFileTreeView::setOpen(item, open); } void SQ_TreeView::contentsMousePressEvent(TQMouseEvent *e) { TQListViewItem *item; TQPoint point = e->pos(); point.setY(point.y() - contentsY()); if(header()->sectionAt(point.x()) && (item = itemAt(point))) { SQ_TreeViewItem *m = static_cast(item); if(m) { int state = e->state(); if(!state) toggle(m, true); else { TQListViewItemIterator it(this); // toggle parent item if(state == TQt::ShiftButton) toggle(m, true); else if(state == TQt::ControlButton) toggle(m, false, true); else if(state == TQt::AltButton) toggle(m, false, false); SQ_TreeViewItem *tvi = static_cast(m->firstChild()); // toggle all child items while(tvi) { if(state == TQt::ShiftButton) toggle(tvi, false, m->checked()); else if(state == TQt::ControlButton) toggle(tvi, false, true); else if(state == TQt::AltButton) toggle(tvi, false, false); tvi = static_cast(tvi->nextSibling()); } } } m_ignoreClick = true; } KFileTreeView::contentsMousePressEvent(e); m_ignoreClick = false; } void SQ_TreeView::contentsMouseDoubleClickEvent(TQMouseEvent *e) { if(header()->sectionAt(e->x())) m_ignoreClick = true; KFileTreeView::contentsMouseDoubleClickEvent(e); m_ignoreClick = false; } void SQ_TreeView::setupRecursion() { SQ_Config::instance()->setGroup("Sidebar"); setRecursion(SQ_Config::instance()->readNumEntry("recursion_type", No)); } void SQ_TreeView::toggle(SQ_TreeViewItem *item, bool togg, bool set) { if(togg) item->setChecked(!item->checked()); else item->setChecked(set); if(item->checked()) emit urlAdded(item->url()); else emit urlRemoved(item->url()); } void SQ_TreeView::slotDropped(TQDropEvent *e, TQListViewItem *parent, TQListViewItem *item) { if(!item) item = parent; KFileTreeViewItem *cur = static_cast(item); if(!cur) return; KURL::List list; KURLDrag::decode(e, list); SQ_NavigatorDropMenu::instance()->setupFiles(list, cur->url()); SQ_NavigatorDropMenu::instance()->exec(TQCursor::pos(), true); } void SQ_TreeView::slotContextMenu(KListView *, TQListViewItem *item, const TQPoint &point) { if(item) { KFileTreeViewItem *kfi = static_cast(item); menu->updateDirActions(kfi->isDir()); menu->setURL(kfi->url()); menu->exec(point); } } void SQ_TreeView::slotDirty(const TQString &path) { KURL url; url.setPath(path); lister->lock(); // we don't need update urls that are not yet updated // by threaded lister if(!lister->hasURL(url)) lister->appendURL(url); else if(lister->isCurrent(url)) { lister->terminate(); lister->wait(); lister->closeDir(); } lister->unlock(); if(!lister->running()) scanTimer->start(1000, true); } void SQ_TreeView::slotDeleteItemMy(KFileItem *fi) { if(m_recurs != No && fi) dw->removeDir(fi->url().path()); } void SQ_TreeView::slotAddToFolderBasket() { KFileItem fi(KFileItem::Unknown, KFileItem::Unknown, menu->url()); KFileItemList list; list.append(&fi); SQ_DirectoryBasket::instance()->add(list); } #include "sq_treeview.moc"