/* * * $Id: k3bfiletreecombobox.cpp 619556 2007-01-03 17:38:12Z trueg $ * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org> * * This file is part of the K3b project. * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org> * * 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. * See the file "COPYING" for the exact licensing terms. */ #include "k3bfiletreecombobox.h" #include "k3bfiletreeview.h" #include <k3bdevicemanager.h> #include <k3bdevice.h> #include <k3bcore.h> #include <k3bglobals.h> #include <tqrect.h> #include <tqapplication.h> #include <tqstyle.h> #include <tqlistbox.h> #include <tqheader.h> #include <tqevent.h> #include <tqpainter.h> #include <tqpalette.h> #include <tqdrawutil.h> #include <tqdir.h> #include <kdebug.h> #include <kiconloader.h> #include <kurlcompletion.h> #include <kuser.h> class K3bFileTreeComboBox::Private { public: Private() { poppedUp = false; ignoreNextMouseClick = false; } bool poppedUp; bool ignoreNextMouseClick; // used when the view was hidden with the arrow button KURLCompletion* urlCompletion; }; K3bFileTreeComboBox::K3bFileTreeComboBox( TQWidget* parent, const char* name ) : KComboBox( true, parent, name ) { d = new Private; d->urlCompletion = new KURLCompletion(); setCompletionObject( d->urlCompletion ); m_fileTreeView = new K3bFileTreeView( this ); m_fileTreeView->hide(); m_fileTreeView->reparent( this, WType_Popup, TQPoint(0,0), false ); m_fileTreeView->header()->hide(); m_fileTreeView->installEventFilter(this); m_fileTreeView->addDefaultBranches(); m_fileTreeView->addCdDeviceBranches( k3bcore->deviceManager() ); // HACK! Why the hell is TQComboBox that closed??? listBox()->insertItem( "HACK" ); connect( m_fileTreeView, TQT_SIGNAL(deviceExecuted(K3bDevice::Device*)), this, TQT_SLOT(slotDeviceExecuted(K3bDevice::Device*)) ); connect( m_fileTreeView, TQT_SIGNAL(urlExecuted(const KURL&)), this, TQT_SLOT(slotUrlExecuted(const KURL&)) ); connect( lineEdit(), TQT_SIGNAL(returnPressed()), this, TQT_SLOT(slotGoUrl()) ); // TODO: subclass KURLCompletition to support the dev:/ stuff and block any non-local urls } K3bFileTreeComboBox::~K3bFileTreeComboBox() { delete d->urlCompletion; delete d; } void K3bFileTreeComboBox::slotDeviceExecuted( K3bDevice::Device* dev ) { setDevice( dev ); popdown(); emit deviceExecuted( dev ); } void K3bFileTreeComboBox::slotUrlExecuted( const KURL& url ) { setUrl( url ); emit urlExecuted( url ); } void K3bFileTreeComboBox::setUrl( const KURL& url ) { setEditText( SmallIcon("folder"), K3b::convertToLocalUrl(url).path() ); popdown(); } void K3bFileTreeComboBox::setDevice( K3bDevice::Device* dev ) { setEditText( SmallIcon("cdrom_unmount"), dev->vendor() + " " + dev->description() + " (" + dev->blockDeviceName() + ")" ); } void K3bFileTreeComboBox::setEditText( const TQPixmap& pix, const TQString& t ) { // TQComboBox::changeItem() doesn't honor the pixmap when // using an editable combobox, so we just remove and insert setUpdatesEnabled( false ); lineEdit()->setUpdatesEnabled( false ); removeItem(0); insertItem( pix, t, 0 ); lineEdit()->setText( t ); setUpdatesEnabled( true ); lineEdit()->setUpdatesEnabled( true ); update(); } void K3bFileTreeComboBox::setCurrentItem( int ) { // the current item is always 0 due to the ugly but quite smart HACK ;) } void K3bFileTreeComboBox::setCurrentText( const TQString& ) { } void K3bFileTreeComboBox::popup() { // code mainly from qcombobox.cpp m_fileTreeView->triggerUpdate(); int w = TQMAX( m_fileTreeView->sizeHint().width(), width() ); int h = m_fileTreeView->sizeHint().height(); TQRect screen = TQApplication::desktop()->availableGeometry( this ); int sx = screen.x(); // screen pos int sy = screen.y(); int sw = screen.width(); // screen width int sh = screen.height(); // screen height TQPoint pos = mapToGlobal( TQPoint(0,height()) ); // ## Similar code is in TQPopupMenu int x = pos.x(); int y = pos.y(); // the complete widget must be visible if ( x + w > sx + sw ) x = sx+sw - w; if ( x < sx ) x = sx; if (y + h > sy+sh && y - h - height() >= 0 ) y = y - h - height(); TQRect rect = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, TQStyle::SC_ComboBoxListBoxPopup, TQStyleOption( x, y, w, h ) ); // work around older styles that don't implement the combobox // listbox popup subcontrol if ( rect.isNull() ) rect.setRect( x, y, w, h ); m_fileTreeView->setGeometry( rect ); m_fileTreeView->raise(); // TODO: somehow set the current item m_fileTreeView->show(); d->poppedUp = true; d->ignoreNextMouseClick = false; } void K3bFileTreeComboBox::popdown() { m_fileTreeView->hide(); d->poppedUp = false; repaint(); // repaint the arrow } void K3bFileTreeComboBox::slotGoUrl() { TQString p = currentText(); // check for a media url or a device string if( K3bDevice::Device* dev = K3b::urlToDevice( p ) ) { emit deviceExecuted( dev ); return; } // check for our own internal format else if( p.contains("/dev/") ) { int pos1 = p.findRev('('); int pos2 = p.findRev(')'); TQString devStr = p.mid( pos1+1, pos2-pos1-1 ); if( K3bDevice::Device* dev = k3bcore->deviceManager()->findDevice( devStr ) ) { emit deviceExecuted( dev ); return; } } // no device -> select url // // Properly replace home dirs. // a single ~ will be replaced with the current user's home dir // while for example "~ftp" will be replaced by the home dir of user // ftp // // TODO: move this to k3bglobals // to expand another user's home dir we need a tilde followed by a user name static TQRegExp someUsersHomeDir( "\\~([^/]+)" ); int pos = 0; while( ( pos = someUsersHomeDir.search( p, pos ) ) != -1 ) { KUser user( someUsersHomeDir.cap(1) ); if( user.isValid() ) p.replace( pos, someUsersHomeDir.cap(1).length() + 1, user.homeDir() ); else ++pos; // skip this ~ } // now replace the unmatched tildes with our home dir p.replace( "~", K3b::prepareDir( TQDir::homeDirPath() ) ); lineEdit()->setText( p ); KURL url; url.setPath( p ); emit urlExecuted( url ); } bool K3bFileTreeComboBox::eventFilter( TQObject* o, TQEvent* e ) { if( dynamic_cast<K3bFileTreeView*>(o) == m_fileTreeView ) { if( e->type() == TQEvent::DragLeave ) { // the user dragged a dir from the filetree // now popdown(); return true; } else if( e->type() == TQEvent::KeyPress ) { TQKeyEvent *k = (TQKeyEvent *)e; if( k->key() == TQt::Key_Escape ) { popdown(); return true; } } else if( e->type() == TQEvent::MouseButtonPress ) { TQMouseEvent* me = (TQMouseEvent*)e; if ( !TQT_TQRECT_OBJECT(m_fileTreeView->rect()).contains( me->pos() ) ) { TQRect arrowRect = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, TQStyle::SC_ComboBoxArrow); arrowRect = TQStyle::visualRect(arrowRect, this); // Correction for motif style, where arrow is smaller // and thus has a rect that doesn't fit the button. arrowRect.setHeight( TQMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); if ( arrowRect.contains( mapFromGlobal(me->globalPos()) ) ) { d->ignoreNextMouseClick = true; // in the case we hit the arrow button } popdown(); } return false; // we need this in the listview } return false; } else return KComboBox::eventFilter(o, e); } void K3bFileTreeComboBox::mousePressEvent( TQMouseEvent* e ) { // mainly from qcombobox.cpp if ( e->button() != Qt::LeftButton ) return; if ( d->ignoreNextMouseClick ) { d->ignoreNextMouseClick = FALSE; return; } TQRect arrowRect = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, TQStyle::SC_ComboBoxArrow); arrowRect = TQStyle::visualRect(arrowRect, this); // Correction for motif style, where arrow is smaller // and thus has a rect that doesn't fit the button. arrowRect.setHeight( TQMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); if ( arrowRect.contains( e->pos() ) ) { popup(); repaint( FALSE ); } } void K3bFileTreeComboBox::keyPressEvent( TQKeyEvent* e ) { if( e->key() == TQt::Key_Escape ) { popdown(); } KComboBox::keyPressEvent(e); } void K3bFileTreeComboBox::paintEvent( TQPaintEvent* ) { // a lot of code from qcombobox.cpp // we only need this since there is no way to change the status of the arrow-button TQPainter p( this ); const TQColorGroup & g = colorGroup(); p.setPen(g.text()); TQStyle::SFlags flags = TQStyle::Style_Default; if (isEnabled()) flags |= TQStyle::Style_Enabled; if (hasFocus()) flags |= TQStyle::Style_HasFocus; if ( width() < 5 || height() < 5 ) { qDrawShadePanel( &p, rect(), g, FALSE, 2, &g.brush( TQColorGroup::Button ) ); return; } // bool reverse = TQApplication::reverseLayout(); style().drawComplexControl( TQStyle::CC_ComboBox, &p, this, rect(), g, flags, TQStyle::SC_All, (d->poppedUp ? TQStyle::SC_ComboBoxArrow : TQStyle::SC_None )); TQRect re = style().querySubControlMetrics( TQStyle::CC_ComboBox, this, TQStyle::SC_ComboBoxEditField ); re = TQStyle::visualRect(re, this); p.setClipRect( re ); // TQListBoxItem * item = listBox()->item( 0 ); // if ( item ) { // // we calculate the TQListBoxTexts height (ignoring strut) // int itemh = d->listBox()->fontMetrics().lineSpacing() + 2; // p.translate( re.x(), re.y() + (re.height() - itemh)/2 ); // item->paint( &p ); // } // } else if ( d->listBox() && d->listBox()->item( 0 ) ) { p.setClipping( FALSE ); TQListBoxItem * item = listBox()->item( 0 ); const TQPixmap *pix = item->pixmap(); if ( pix ) { p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), colorGroup().brush( TQColorGroup::Base ) ); p.drawPixmap( re.x() + 2, re.y() + ( re.height() - pix->height() ) / 2, *pix ); } // } p.setClipping( FALSE ); } #include "k3bfiletreecombobox.moc"