diff options
Diffstat (limited to 'kicker/kicker/ui/browser_mnu.cpp')
-rw-r--r-- | kicker/kicker/ui/browser_mnu.cpp | 552 |
1 files changed, 552 insertions, 0 deletions
diff --git a/kicker/kicker/ui/browser_mnu.cpp b/kicker/kicker/ui/browser_mnu.cpp new file mode 100644 index 000000000..9561881eb --- /dev/null +++ b/kicker/kicker/ui/browser_mnu.cpp @@ -0,0 +1,552 @@ +/***************************************************************** + +Copyright (c) 2001 Matthias Elter <elter@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + +#include <qdir.h> +#include <qpixmap.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kdesktopfile.h> +#include <kdirwatch.h> +#include <kfileitem.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <kio/global.h> +#include <klocale.h> +#include <kmimetype.h> +#include <konq_operations.h> +#include <kprocess.h> +#include <krun.h> +#include <ksimpleconfig.h> +#include <kstringhandler.h> +#include <kurldrag.h> + +#include "kickerSettings.h" + +#include "browser_mnu.h" +#include "browser_mnu.moc" + +#define CICON(a) (*_icons)[a] + +QMap<QString, QPixmap> *PanelBrowserMenu::_icons = 0; + +PanelBrowserMenu::PanelBrowserMenu(QString path, QWidget *parent, const char *name, int startid) + : KPanelMenu(path, parent, name) + , _mimecheckTimer(0) + , _startid(startid) + , _dirty(false) + , _filesOnly(false) +{ + _lastpress = QPoint(-1, -1); + setAcceptDrops(true); // Should depend on permissions of path. + + // we are not interested for dirty events on files inside the + // directory (see slotClearIfNeeded) + connect( &_dirWatch, SIGNAL(dirty(const QString&)), + this, SLOT(slotClearIfNeeded(const QString&)) ); + connect( &_dirWatch, SIGNAL(created(const QString&)), + this, SLOT(slotClear()) ); + connect( &_dirWatch, SIGNAL(deleted(const QString&)), + this, SLOT(slotClear()) ); + + kdDebug() << "PanelBrowserMenu Constructor " << path << endl; +} + +PanelBrowserMenu::~PanelBrowserMenu() +{ + kdDebug() << "PanelBrowserMenu Destructor " << path() << endl; +} + +void PanelBrowserMenu::slotClearIfNeeded(const QString& p) +{ + if (p == path()) + slotClear(); +} + +void PanelBrowserMenu::initialize() +{ + _lastpress = QPoint(-1, -1); + + // don't change menu if already visible + if (isVisible()) + return; + + if (_dirty) { + // directory content changed while menu was visible + slotClear(); + setInitialized(false); + _dirty = false; + } + + if (initialized()) return; + setInitialized(true); + + // start watching if not already done + if (!_dirWatch.contains(path())) + _dirWatch.addDir( path() ); + + // setup icon map + initIconMap(); + + // clear maps + _filemap.clear(); + _mimemap.clear(); + + int filter = QDir::Dirs | QDir::Files; + if (KickerSettings::showHiddenFiles()) + { + filter |= QDir::Hidden; + } + + QDir dir(path(), QString::null, QDir::DirsFirst | QDir::Name | QDir::IgnoreCase, filter); + + // does the directory exist? + if (!dir.exists()) { + insertItem(i18n("Failed to Read Folder")); + return; + } + + // get entry list + const QFileInfoList *list = dir.entryInfoList(); + + // no list -> read error + if (!list) { + insertItem(i18n("Failed to Read Folder")); + return; + } + + KURL url; + url.setPath(path()); + if (!kapp->authorizeURLAction("list", KURL(), url)) + { + insertItem(i18n("Not Authorized to Read Folder")); + return; + } + + // insert file manager and terminal entries + // only the first part menu got them + if(_startid == 0 && !_filesOnly) { + insertTitle(path()); + insertItem(CICON("kfm"), i18n("Open in File Manager"), this, SLOT(slotOpenFileManager())); + if (kapp->authorize("shell_access")) + insertItem(CICON("terminal"), i18n("Open in Terminal"), this, SLOT(slotOpenTerminal())); + } + + + bool first_entry = true; + bool dirfile_separator = false; + unsigned int item_count = 0; + int run_id = _startid; + + // get list iterator + QFileInfoListIterator it(*list); + + // jump to startid + it += _startid; + + // iterate over entry list + for (; it.current(); ++it) + { + // bump id + run_id++; + + QFileInfo *fi = it.current(); + // handle directories + if (fi->isDir()) + { + QString name = fi->fileName(); + + // ignore . and .. entries + if (name == "." || name == "..") continue; + + QPixmap icon; + QString path = fi->absFilePath(); + + // parse .directory if it does exist + if (QFile::exists(path + "/.directory")) { + + KSimpleConfig c(path + "/.directory", true); + c.setDesktopGroup(); + QString iconPath = c.readEntry("Icon"); + + if ( iconPath.startsWith("./") ) + iconPath = path + '/' + iconPath.mid(2); + + icon = KGlobal::iconLoader()->loadIcon(iconPath, + KIcon::Small, KIcon::SizeSmall, + KIcon::DefaultState, 0, true); + if(icon.isNull()) + icon = CICON("folder"); + name = c.readEntry("Name", name); + } + + // use cached folder icon for directories without special icon + if (icon.isNull()) + icon = CICON("folder"); + + // insert separator if we are the first menu entry + if(first_entry) { + if (_startid == 0 && !_filesOnly) + insertSeparator(); + first_entry = false; + } + + // append menu entry + PanelBrowserMenu *submenu = new PanelBrowserMenu(path, this); + submenu->_filesOnly = _filesOnly; + append(icon, name, submenu); + + // bump item count + item_count++; + + dirfile_separator = true; + } + // handle files + else if(fi->isFile()) + { + QString name = fi->fileName(); + QString title = KIO::decodeFileName(name); + + QPixmap icon; + QString path = fi->absFilePath(); + + bool mimecheck = false; + + // .desktop files + if(KDesktopFile::isDesktopFile(path)) + { + KSimpleConfig c(path, true); + c.setDesktopGroup(); + title = c.readEntry("Name", title); + + QString s = c.readEntry("Icon"); + if(!_icons->contains(s)) { + icon = KGlobal::iconLoader()->loadIcon(s, KIcon::Small, KIcon::SizeSmall, + KIcon::DefaultState, 0, true); + + if(icon.isNull()) { + QString type = c.readEntry("Type", "Application"); + if (type == "Directory") + icon = CICON("folder"); + else if (type == "Mimetype") + icon = CICON("txt"); + else if (type == "FSDevice") + icon = CICON("chardevice"); + else + icon = CICON("exec"); + } + else + _icons->insert(s, icon); + } + else + icon = CICON(s); + } + else { + // set unknown icon + icon = CICON("unknown"); + + // mark for delayed mimetime check + mimecheck = true; + } + + // insert separator if we are the first menu entry + if(first_entry) { + if(_startid == 0 && !_filesOnly) + insertSeparator(); + first_entry = false; + } + + // insert separator if we we first file after at least one directory + if (dirfile_separator) { + insertSeparator(); + dirfile_separator = false; + } + + // append file entry + append(icon, title, name, mimecheck); + + // bump item count + item_count++; + } + + if (item_count == KickerSettings::maxEntries2()) + { + // Only insert a "More" item if there are actually more items. + ++it; + if( it.current() ) { + insertSeparator(); + append(CICON("kdisknav"), i18n("More"), new PanelBrowserMenu(path(), this, 0, run_id)); + } + break; + } + } + +#if 0 + // WABA: tear off handles don't work together with dynamically updated + // menus. We can't update the menu while torn off, and we don't know + // when it is torn off. + if(KGlobalSettings::insertTearOffHandle() && item_count > 0) + insertTearOffHandle(); +#endif + + adjustSize(); + + QString dirname = path(); + + int maxlen = contentsRect().width() - 40; + if(item_count == 0) + maxlen = fontMetrics().width(dirname); + + if (fontMetrics().width(dirname) > maxlen) { + while ((!dirname.isEmpty()) && (fontMetrics().width(dirname) > (maxlen - fontMetrics().width("...")))) + dirname = dirname.remove(0, 1); + dirname.prepend("..."); + } + setCaption(dirname); + + // setup and start delayed mimetype check timer + if(_mimemap.count() > 0) { + + if(!_mimecheckTimer) + _mimecheckTimer = new QTimer(this); + + connect(_mimecheckTimer, SIGNAL(timeout()), SLOT(slotMimeCheck())); + _mimecheckTimer->start(0); + } +} + +void PanelBrowserMenu::append(const QPixmap &pixmap, const QString &title, const QString &file, bool mimecheck) +{ + // avoid &'s being converted to accelerators + QString newTitle = title; + newTitle = KStringHandler::cEmSqueeze( newTitle, fontMetrics(), 20 ); + newTitle.replace("&", "&&"); + + // insert menu item + int id = insertItem(pixmap, newTitle); + + // insert into file map + _filemap.insert(id, file); + + // insert into mimetype check map + if(mimecheck) + _mimemap.insert(id, true); +} + +void PanelBrowserMenu::append(const QPixmap &pixmap, const QString &title, PanelBrowserMenu *subMenu) +{ + // avoid &'s being converted to accelerators + QString newTitle = title; + newTitle = KStringHandler::cEmSqueeze( newTitle, fontMetrics(), 20 ); + newTitle.replace("&", "&&"); + + // insert submenu + insertItem(pixmap, newTitle, subMenu); + // remember submenu for later deletion + _subMenus.append(subMenu); +} + +void PanelBrowserMenu::mousePressEvent(QMouseEvent *e) +{ + QPopupMenu::mousePressEvent(e); + _lastpress = e->pos(); +} + +void PanelBrowserMenu::mouseMoveEvent(QMouseEvent *e) +{ + QPopupMenu::mouseMoveEvent(e); + + if (!(e->state() & LeftButton)) return; + if(_lastpress == QPoint(-1, -1)) return; + + // DND delay + if((_lastpress - e->pos()).manhattanLength() < 12) return; + + // get id + int id = idAt(_lastpress); + if(!_filemap.contains(id)) return; + + // reset _lastpress + _lastpress = QPoint(-1, -1); + + // start drag + KURL url; + url.setPath(path() + "/" + _filemap[id]); + KURL::List files(url); + KURLDrag *d = new KURLDrag(files, this); + connect(d, SIGNAL(destroyed()), this, SLOT(slotDragObjectDestroyed())); + d->setPixmap(iconSet(id)->pixmap()); + d->drag(); +} + +void PanelBrowserMenu::slotDragObjectDestroyed() +{ + if (KURLDrag::target() != this) + { + close(); + } +} + +void PanelBrowserMenu::dragEnterEvent( QDragEnterEvent *ev ) +{ + if (KURLDrag::canDecode(ev)) + { + ev->accept(); + } + KPanelMenu::dragEnterEvent(ev); +} + +void PanelBrowserMenu::dragMoveEvent(QDragMoveEvent *ev) +{ + QMouseEvent mev(QEvent::MouseMove, ev->pos(), Qt::NoButton, Qt::LeftButton); + QPopupMenu::mouseMoveEvent(&mev); +} + +void PanelBrowserMenu::dropEvent( QDropEvent *ev ) +{ + KURL u( path() ); + KFileItem item( u, QString::fromLatin1( "inode/directory" ), KFileItem::Unknown ); + KonqOperations::doDrop( &item, u, ev, this ); + KPanelMenu::dropEvent(ev); + // ### TODO: Update list +} + +void PanelBrowserMenu::slotExec(int id) +{ + kapp->propagateSessionManager(); + + if(!_filemap.contains(id)) return; + + KURL url; + url.setPath(path() + "/" + _filemap[id]); + new KRun(url, 0, true); // will delete itself + _lastpress = QPoint(-1, -1); +} + +void PanelBrowserMenu::slotOpenTerminal() +{ + KConfig * config = kapp->config(); + config->setGroup("General"); + QString term = config->readPathEntry("TerminalApplication", "konsole"); + + KProcess proc; + proc << term; + if (term == "konsole") + proc << "--workdir" << path(); + else + proc.setWorkingDirectory(path()); + proc.start(KProcess::DontCare); +} + +void PanelBrowserMenu::slotOpenFileManager() +{ + new KRun(path()); +} + +void PanelBrowserMenu::slotMimeCheck() +{ + // get the first map entry + QMap<int, bool>::Iterator it = _mimemap.begin(); + + // no mime types left to check -> stop timer + if(it == _mimemap.end()) { + _mimecheckTimer->stop(); + delete _mimecheckTimer; + _mimecheckTimer = 0; + return; + } + + int id = it.key(); + QString file = _filemap[id]; + + _mimemap.remove(it); + + KURL url; + url.setPath( path() + '/' + file ); + +// KMimeType::Ptr mt = KMimeType::findByURL(url, 0, true, false); +// QString icon(mt->icon(url, true)); + QString icon = KMimeType::iconForURL( url ); +// kdDebug() << url.url() << ": " << icon << endl; + + file = KStringHandler::cEmSqueeze( file, fontMetrics(), 20 ); + file.replace("&", "&&"); + + if(!_icons->contains(icon)) { + QPixmap pm = SmallIcon(icon); + if( pm.height() > 16 ) + { + QPixmap cropped( 16, 16 ); + copyBlt( &cropped, 0, 0, &pm, 0, 0, 16, 16 ); + pm = cropped; + } + _icons->insert(icon, pm); + changeItem(id, pm, file); + } + else + changeItem(id, CICON(icon), file); +} + +void PanelBrowserMenu::slotClear() +{ + // no need to watch any further + if (_dirWatch.contains(path())) + _dirWatch.removeDir( path() ); + + // don't change menu if already visible + if (isVisible()) { + _dirty = true; + return; + } + KPanelMenu::slotClear(); + + for (QValueVector<PanelBrowserMenu*>::iterator it = _subMenus.begin(); + it != _subMenus.end(); + ++it) + { + delete *it; + } + _subMenus.clear(); // deletes submenus +} + +void PanelBrowserMenu::initIconMap() +{ + if(_icons) return; + +// kdDebug() << "PanelBrowserMenu::initIconMap" << endl; + + _icons = new QMap<QString, QPixmap>; + + _icons->insert("folder", SmallIcon("folder")); + _icons->insert("unknown", SmallIcon("mime_empty")); + _icons->insert("folder_open", SmallIcon("folder_open")); + _icons->insert("kdisknav", SmallIcon("kdisknav")); + _icons->insert("kfm", SmallIcon("kfm")); + _icons->insert("terminal", SmallIcon("terminal")); + _icons->insert("txt", SmallIcon("txt")); + _icons->insert("exec", SmallIcon("exec")); + _icons->insert("chardevice", SmallIcon("chardevice")); +} + +// vim: sw=4 et |