Logo Search packages:      
Sourcecode: webissues version File versions  Download package

issueview.cpp

/**************************************************************************
* This file is part of the WebIssues program
* Copyright (C) 2006 Michał Męciński
* Copyright (C) 2007 WebIssues Team
*
* 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.
**************************************************************************/

#include "issueview.h"

#include <QTabWidget>
#include <QLayout>
#include <QLabel>
#include <QGroupBox>
#include <QScrollArea>
#include <QScrollBar>
#include <QTextBrowser>
#include <QTreeView>
#include <QAction>
#include <QMenu>
#include <QMessageBox>
#include <QPushButton>
#include <QFileDialog>
#include <QApplication>
#include <QClipboard>
#include <QDesktopServices>
#include <QSettings>

#include "attributes/datetimehelper.h"
#include "models/tableitemmodel.h"
#include "models/tablemodels.h"
#include "models/tablemodelshelper.h"
#include "models/linklocator.h"
#include "models/htmlhistorywriter.h"
#include "models/issuehistoryprovider.h"
#include "models/treeviewsettings.h"
#include "models/treeviewhelper.h"
#include "models/issueviewsettings.h"
#include "data/rdb/tableiterators.h"
#include "data/datamanager.h"
#include "data/updateevent.h"
#include "data/updatebatch.h"
#include "data/rowhelpers.h"
#include "data/connectioninfo.h"
#include "data/attachmentscache.h"
#include "widgets/propertypanel.h"
#include "widgets/popeditpanel.h"
#include "widgets/popeditbox.h"
#include "dialogs/issuedialogs.h"
#include "dialogs/finditemdialog.h"
#include "dialogs/finddialog.h"
#include "dialogs/checkmessagebox.h"
#include "dialogs/attributesettingsdialog.h"
#include "xmlui/builder.h"
#include "connectionmanager.h"
#include "viewmanager.h"
#include "configdata.h"
#include "iconloader.h"

using namespace WebIssues;

00065 IssueView::IssueView( QObject* parent, QWidget* parentWidget ) : View( parent ),
    m_model( NULL ),
    m_findDialog( NULL ),
    m_gotoItemId( 0 )
{
    QAction* action;

    action = new QAction( IconLoader::icon( "file-reload" ), tr( "&Update Issue" ), this );
    action->setShortcut( QKeySequence::Refresh );
    connect( action, SIGNAL( triggered() ), this, SLOT( updateIssue() ) );
    setAction( "updateIssue", action );

    action = new QAction( IconLoader::icon( "comment" ), tr( "Add &Comment..." ), this );
    action->setShortcut( QKeySequence::New );
    connect( action, SIGNAL( triggered() ), this, SLOT( addComment() ) );
    setAction( "addComment", action );

    action = new QAction( IconLoader::icon( "file-attach" ), tr( "Add &Attachment..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( addAttachment() ) );
    setAction( "addAttachment", action );

    action = new QAction( IconLoader::icon( "edit-rename" ), tr( "&Rename Issue..." ), this );
    action->setShortcut( tr( "F2" ) );
    connect( action, SIGNAL( triggered() ), this, SLOT( editRename() ) );
    setAction( "editRename", action );

    action = new QAction( IconLoader::icon( "find" ), tr( "&Find..." ), this );
    action->setShortcut( QKeySequence::Find );
    connect( action, SIGNAL( triggered() ), this, SLOT( find() ) );
    setAction( "find", action );

    action = new QAction( IconLoader::icon( "find-next" ), tr( "Find &Next" ), this );
    action->setShortcut( QKeySequence::FindNext );
    connect( action, SIGNAL( triggered() ), this, SLOT( findNext() ) );
    setAction( "findNext", action );

    action = new QAction( IconLoader::icon( "find-previous" ), tr( "Find &Previous" ), this );
    action->setShortcut( QKeySequence::FindPrevious );
    connect( action, SIGNAL( triggered() ), this, SLOT( findPrevious() ) );
    setAction( "findPrevious", action );

    action = new QAction( IconLoader::icon( "configure-columns" ), tr( "C&onfigure Attributes..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( configureAttributes() ) );
    setAction( "configureAttributes", action );

    action = new QAction( IconLoader::icon( "find" ), tr( "Find Item..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( openLink() ) );
    setAction( "findItem", action );

    action = new QAction( IconLoader::icon( "file-open" ), tr( "Open Attachment" ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( openAttachment() ) );
    setAction( "openAttachment", action );

    action = new QAction( IconLoader::icon( "file-save-as" ), tr( "Save Attachment As..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( saveAttachment() ) );
    setAction( "saveAttachment", action );

    action = new QAction( IconLoader::icon( "mail-send" ), tr( "Send Email..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( openLink() ) );
    setAction( "sendEmail", action );

    action = new QAction( IconLoader::icon( "window-new" ), tr( "Open Link in Browser..." ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( openLink() ) );
    setAction( "openLink", action );

    action = new QAction( IconLoader::icon( "edit-copy" ), tr( "Copy Email Address" ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( copyLink() ) );
    setAction( "copyEmail", action );

    action = new QAction( IconLoader::icon( "edit-copy" ), tr( "Copy Link Address" ), this );
    connect( action, SIGNAL( triggered() ), this, SLOT( copyLink() ) );
    setAction( "copyLink", action );

    action = new QAction( IconLoader::icon( "edit-copy" ), tr( "&Copy" ), this );
    action->setShortcut( QKeySequence::Copy );
    connect( action, SIGNAL( triggered() ), this, SLOT( copy() ) );
    setAction( "editCopy", action );

    action = new QAction( tr( "Select &All" ), this );
    action->setShortcut( QKeySequence::SelectAll );
    connect( action, SIGNAL( triggered() ), this, SLOT( selectAll() ) );
    setAction( "editSelectAll", action );

    setTitle( "menuMain", tr( "&Issue" ) );
    setTitle( "menuEdit", tr( "&Edit" ) );

    loadXmlFile( ":/resources/issueview.xml" );

    m_tabWidget = new QTabWidget( parentWidget );

    connect( m_tabWidget, SIGNAL( currentChanged( int ) ), this, SLOT( tabChanged( int ) ) );

    QWidget* issueTab = new QWidget( m_tabWidget );
    QVBoxLayout* issueLayout = new QVBoxLayout( issueTab );
    QHBoxLayout* nameLayout = new QHBoxLayout();
    issueLayout->addLayout( nameLayout );
    QHBoxLayout* groupsLayout = new QHBoxLayout();
    issueLayout->addLayout( groupsLayout );

    QLabel* nameLabel = new QLabel( tr( "&Name:" ), issueTab );
    nameLayout->addWidget( nameLabel );

    m_nameEdit = new PopEditBox( issueTab );
    nameLayout->addWidget( m_nameEdit );

    nameLabel->setBuddy( m_nameEdit );

    connect( m_nameEdit, SIGNAL( linkClicked( const QString& ) ), this, SLOT( linkClicked( const QString& ) ) );
    connect( m_nameEdit, SIGNAL( linkContextMenu( const QString&, const QPoint& ) ),
        this, SLOT( linkContextMenu( const QString&, const QPoint& ) ) );
    connect( m_nameEdit, SIGNAL( buttonClicked() ), this, SLOT( editRename() ) );

    QGroupBox* propertyGroup = new QGroupBox( tr( "Properties" ), issueTab );
    QVBoxLayout* propertyLayout = new QVBoxLayout( propertyGroup );
    propertyLayout->setMargin( 0 );

    QScrollArea* propertyScroll = new QScrollArea( propertyGroup );
    propertyScroll->setFrameStyle( QFrame::NoFrame );
    propertyScroll->setWidgetResizable( true );
    m_propertyPanel = new PropertyPanel( propertyScroll );
    propertyScroll->setWidget( m_propertyPanel );
    propertyLayout->addWidget( propertyScroll );
    groupsLayout->addWidget( propertyGroup, 2 );

    QGroupBox* attributeGroup = new QGroupBox( tr( "Attributes" ), issueTab );
    QVBoxLayout* attributeLayout = new QVBoxLayout( attributeGroup );
    attributeLayout->setMargin( 0 );

    QScrollArea* attributeScroll = new QScrollArea( attributeGroup );
    attributeScroll->setWidgetResizable( true );
    attributeScroll->setFrameStyle( QFrame::NoFrame );
    m_attributePanel = new PopEditPanel( attributeScroll );
    attributeScroll->setWidget( m_attributePanel );
    attributeLayout->addWidget( attributeScroll );
    groupsLayout->addWidget( attributeGroup, 3 );

    connect( m_attributePanel, SIGNAL( linkClicked( const QString& ) ), this, SLOT( linkClicked( const QString& ) ) );
    connect( m_attributePanel, SIGNAL( linkContextMenu( const QString&, const QPoint& ) ),
        this, SLOT( linkContextMenu( const QString&, const QPoint& ) ) );
    connect( m_attributePanel, SIGNAL( buttonClicked( int ) ), this, SLOT( setValue( int ) ) );

    QWidget* historyTab = new QWidget( m_tabWidget );
    QVBoxLayout* historyLayout = new QVBoxLayout( historyTab );
    historyLayout->setMargin( 0 );

    m_browser = new QTextBrowser( historyTab );
    m_browser->setContextMenuPolicy( Qt::CustomContextMenu );

    historyLayout->addWidget( m_browser );

    connect( m_browser, SIGNAL( customContextMenuRequested( const QPoint& ) ),
        this, SLOT( historyContextMenu( const QPoint& ) ) );
    connect( m_browser, SIGNAL( anchorClicked( const QUrl& ) ), this, SLOT( anchorClicked( const QUrl& ) ) );
    connect( m_browser, SIGNAL( selectionChanged() ), this, SLOT( updateActions() ) );

    QWidget* attachmentsTab = new QWidget( m_tabWidget );
    QVBoxLayout* attachmentsLayout = new QVBoxLayout( attachmentsTab );
    attachmentsLayout->setMargin( 0 );

    m_list = new QTreeView( attachmentsTab );
    m_list->setSortingEnabled( true );
    m_list->setRootIsDecorated( false );
    m_list->setContextMenuPolicy( Qt::CustomContextMenu );

    attachmentsLayout->addWidget( m_list );

    connect( m_list, SIGNAL( customContextMenuRequested( const QPoint& ) ),
        this, SLOT( attachmentsContextMenu( const QPoint& ) ) );
    connect( m_list, SIGNAL( doubleClicked( const QModelIndex& ) ),
        this, SLOT( doubleClicked( const QModelIndex& ) ) );

    m_tabWidget->addTab( issueTab, IconLoader::icon( "issue-open" ), tr( "Issue Details" ) );
    m_tabWidget->addTab( historyTab, IconLoader::icon( "view-history" ), tr( "Issue History" ) );
    m_tabWidget->addTab( attachmentsTab, IconLoader::icon( "file-attach" ), tr( "Attachments" ) );

    setMainWidget( m_tabWidget );

    setViewerSizeHint( QSize( 700, 500 ) );
}

00245 IssueView::~IssueView()
{
    if ( isEnabled() ) {
        TreeViewSettings settings;
        settings.openAttachmentsList();

        settings.saveColumnWidths( TreeViewHelper::readColumnWidths( m_list ) );
    }

    if ( dataManager )
        dataManager->unlockIssue( id() );
}

00258 void IssueView::initialUpdate()
{
    m_model = new TableItemModel( this );
    m_model->setRootTableModel( new AttachmentsTableModel( id(), m_model ),
        dataManager->attachments()->parentIndex(), id() );

    connect( m_model, SIGNAL( layoutChanged() ), this, SLOT( updateActions() ) );

    tabChanged( 0 );
    setAccess( checkDataAccess(), true );

    dataManager->lockIssue( id() );

    if ( dataManager->issueUpdateNeeded( id() ) )
        initialUpdateIssue();
}

Access IssueView::checkDataAccess()
{
    const IssueRow* issue = dataManager->issues()->find( id() );
    if ( !issue )
        return UnknownAccess;

    m_folderId = issue->folderId();

    const FolderRow* folder = dataManager->folders()->find( issue->folderId() );
    if ( !folder )
        return NoAccess;

    if ( connectionManager->connectionInfo()->access() != AdminAccess ) {
        int userId = connectionManager->connectionInfo()->userId();
        const MemberRow* member = dataManager->members()->find( userId, folder->projectId() );
        if ( !member )
            return NoAccess;
    }

    m_typeId = folder->typeId();

    const TypeRow* type = dataManager->types()->find( folder->typeId() );
    if ( !type )
        return UnknownAccess;

    return NormalAccess;
}

00303 void IssueView::enableView()
{
    populateProperties();
    populateAttributes();

    populateComments();

    TreeViewSettings settings;
    settings.openAttachmentsList();

    m_model->setColumns( settings.loadColumns() );

    m_list->setModel( m_model );

    m_list->sortByColumn( 0, Qt::AscendingOrder );

    TreeViewHelper::applyColumnWidths( m_list, settings.loadColumnWidths() );

    connect( m_list->selectionModel(), SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ),
        this, SLOT( updateActions() ) );

    updateCaption();
    updateActions();
}

00328 void IssueView::disableView()
{
    m_propertyPanel->clear();
    m_attributePanel->clear();

    m_browser->clear();

    m_list->setModel( NULL );

    delete m_findDialog;
    m_findDialog = NULL;

    updateCaption();
}

void IssueView::gotoItem( int itemId )
{
    if ( itemId == id() )
        return;

    if ( isUpdating() )
        m_gotoItemId = itemId;

    if ( dataManager->comments()->find( itemId ) || dataManager->attachments()->find( itemId ) ) {
        m_tabWidget->setCurrentIndex( 1 );
        m_browser->scrollToAnchor( QString( "id%1" ).arg( itemId ) );
    }
}

void IssueView::tabChanged( int index )
{
    action( "configureAttributes" )->setVisible( index == 0 );
    action( "editCopy" )->setVisible( index == 1 );
    action( "editSelectAll" )->setVisible( index == 1 );
    action( "find" )->setVisible( index == 1 );
    action( "findNext" )->setVisible( index == 1 );
    action( "findPrevious" )->setVisible( index == 1 );

    if ( builder() )
        builder()->rebuildAll();

    updateActions();
}

void IssueView::updateCaption()
{
    QString name = tr( "Unknown Issue" ) ;
    if ( isEnabled() ) {
        const IssueRow* row = dataManager->issues()->find( id() );
        if ( row )
            name = row->name();
    }
    setCaption( name );
}

void IssueView::updateActions()
{
    m_selectedAttachmentId = 0;

    QModelIndex index = selectedIndex();
    if ( index.isValid() )
        m_selectedAttachmentId = m_model->data( index, TableItemModel::RowIdRole ).toInt();

    m_currentPage = m_tabWidget->currentIndex();

    m_isFindEnabled = m_findDialog && m_findDialog->isFindEnabled();

    bool hasSelection = m_browser->textCursor().hasSelection();

    bool linkNotEmpty = !m_actionLink.isEmpty();

    action( "editCopy" )->setEnabled( m_currentPage == 1 && hasSelection );
    action( "editSelectAll" )->setEnabled( m_currentPage == 1 );
    action( "find" )->setEnabled( m_currentPage == 1 );
    action( "findNext" )->setEnabled( m_currentPage == 1 && m_isFindEnabled );
    action( "findPrevious" )->setEnabled( m_currentPage == 1 && m_isFindEnabled );
    action( "openAttachment" )->setEnabled( m_currentPage == 2 && m_selectedAttachmentId != 0 || linkNotEmpty );
    action( "saveAttachment" )->setEnabled( m_currentPage == 2 && m_selectedAttachmentId != 0 || linkNotEmpty );
    action( "findItem" )->setEnabled( linkNotEmpty );
    action( "sendEmail" )->setEnabled( linkNotEmpty );
    action( "openLink" )->setEnabled( linkNotEmpty );
    action( "copyEmail" )->setEnabled( linkNotEmpty );
    action( "copyLink" )->setEnabled( linkNotEmpty );
}

void IssueView::initialUpdateIssue()
{
    UpdateBatch* batch = new UpdateBatch();
    batch->updateIssue( id() );

    executeUpdate( batch );
}

void IssueView::updateIssue()
{
    if ( isEnabled() && !isUpdating() ) {
        UpdateBatch* batch = new UpdateBatch();
        batch->updateIssue( id() );

        executeUpdate( batch );
    }
}

void IssueView::addComment()
{
    if ( isEnabled() )
        viewManager->openCommentView( id() );
}

void IssueView::addAttachment()
{
    if ( isEnabled() ) {
        QSettings* settings = configData->settings();
        QString dir = settings->value( "Paths/AddAttachment", QDir::homePath() ).toString();

        QString path = QFileDialog::getOpenFileName( mainWidget(), tr( "Add Attachment" ), dir );

        if ( !path.isEmpty() ) {
            QFileInfo fileInfo( path );

            settings->setValue( "Paths/AddAttachment", fileInfo.absoluteDir().path() );

            int size = fileInfo.size();
            int maxSize = configData->warnAttachmentSize();

            if ( maxSize > 0 && size > maxSize * 1024 * 1024 ) {
                CheckMessageBox box( mainWidget() );

                box.setIcon( QMessageBox::Warning );
                box.setWindowTitle( tr( "Warning" ) );
                box.setText( tr( "This file is bigger than %1 megabytes. The server may not support\n"
                    "such large attachments. Are you sure you want to continue?" ).arg( maxSize ) );
                box.setStandardButtons( QMessageBox::Yes | QMessageBox::No );

                if ( box.exec() == QMessageBox::No )
                    return;

                if ( box.isChecked() ) {
                    configData->setWarnAttachmentSize( 0 );
                    configData->saveSettings();
                }
            }

            AddAttachmentDialog dialog( id(), path, path, mainWidget() );
            dialog.exec();
        }
    }
}

void IssueView::editRename()
{
    if ( isEnabled() ) {
        RenameIssueDialog dialog( id(), mainWidget() );
        dialog.exec();
    }
}

void IssueView::setValue( int attributeId )
{
    if ( isEnabled() ) {
        SetValueDialog dialog( id(), attributeId, mainWidget() );
        dialog.exec();
    }
}

void IssueView::copy()
{
    if ( isEnabled() && m_currentPage == 1 )
        m_browser->copy();
}

void IssueView::selectAll()
{
    if ( isEnabled() && m_currentPage == 1 )
        m_browser->selectAll();
}

void IssueView::find()
{
    if ( isEnabled() && m_currentPage == 1 ) {
        if ( !m_findDialog ) {
            m_findDialog = new FindDialog( mainWidget() );

            connect( m_findDialog, SIGNAL( findClicked() ), this, SLOT( findClicked() ) );
            connect( m_findDialog, SIGNAL( findEnabled( bool ) ), this, SLOT( updateActions() ) );
        }

        m_findDialog->show();
        m_findDialog->activateWindow();
    }
}

void IssueView::findClicked()
{
    m_tabWidget->setCurrentIndex( 1 );

    findNext();

    viewManager->activateView( this );
    m_browser->setFocus();
}

void IssueView::findNext()
{
    if ( isEnabled() && m_currentPage == 1 && m_isFindEnabled ) {
        if ( !m_browser->find( m_findDialog->text(), m_findDialog->flags() ) ) {
            m_browser->moveCursor( QTextCursor::Start );

            if ( !m_browser->find( m_findDialog->text(), m_findDialog->flags() ) )
                showInfo( tr( "Text not found" ) );
        }
    }
}

void IssueView::findPrevious()
{
    if ( isEnabled() && m_currentPage == 1 && m_isFindEnabled ) {
        QTextCursor cursor = m_browser->textCursor();
        cursor.setPosition( cursor.selectionStart() );
        m_browser->setTextCursor( cursor );

        if ( !m_browser->find( m_findDialog->text(), m_findDialog->flags() | QTextDocument::FindBackward ) ) {
            m_browser->moveCursor( QTextCursor::End );

            if ( !m_browser->find( m_findDialog->text(), m_findDialog->flags() | QTextDocument::FindBackward ) )
                showInfo( tr( "Text not found" ) );
        }
    }
}

void IssueView::openAttachment()
{
    if ( isEnabled() && !m_actionLink.isEmpty() )
        handleAttachment( QUrl( m_actionLink ).host().toInt(), ActionOpen );
    else if ( isEnabled() && m_currentPage == 2 && m_selectedAttachmentId != 0 )
        handleAttachment( m_selectedAttachmentId, ActionOpen );
}

void IssueView::saveAttachment()
{
    if ( isEnabled() && !m_actionLink.isEmpty() )
        handleAttachment( QUrl( m_actionLink ).host().toInt(), ActionSaveAs );
    else if ( isEnabled() && m_currentPage == 2 && m_selectedAttachmentId != 0 )
        handleAttachment( m_selectedAttachmentId, ActionSaveAs );
}

void IssueView::openLink()
{
    if ( isEnabled() && !m_actionLink.isEmpty() )
        linkClicked( m_actionLink );
}

void IssueView::copyLink()
{
    if ( isEnabled() && !m_actionLink.isEmpty() ) {
        QString url = m_actionLink;

        if ( url.startsWith( "mailto:" ) )
            url = url.mid( 7 );

        QApplication::clipboard()->setText( url, QClipboard::Clipboard );
    }
}

00592 void IssueView::updateEvent( UpdateEvent* e )
{
    setAccess( checkDataAccess() );

    if ( isEnabled() ) {
        if ( e->unit() == UpdateEvent::Issue && e->id() == id() ) {
            updateCaption();
            updateProperties();
            updateAttributes();
            populateComments();
        }

        if ( e->unit() == UpdateEvent::Users ) {
            updateProperties();
            populateComments();
        }

        if ( e->unit() == UpdateEvent::Types ) {
            updateProperties();
            populateAttributes();
            populateComments();
        }

        if ( e->unit() == UpdateEvent::Projects )
            updateProperties();

        if ( e->unit() == UpdateEvent::Folder && e->id() == m_folderId ) {
            updateCaption();
            updateProperties();
            updateAttributes();
        }
    }

    if ( isEnabled() && m_gotoItemId != 0 && e->unit() == UpdateEvent::Issue && e->id() == id() ) {
        gotoItem( m_gotoItemId );
        m_gotoItemId = 0;
    }

    if ( e->unit() == UpdateEvent::Folder && e->id() == m_folderId ) {
        if ( dataManager->issueUpdateNeeded( id() ) )
            updateIssue();
    }
}

00636 void IssueView::viewEvent( ViewEvent* e )
{
    if ( isEnabled() && e->action() == ViewEvent::UpdateAttributes && e->id() == m_typeId )
        populateAttributes();
    else
        View::viewEvent( e );
}

void IssueView::populateProperties()
{
    m_propertyPanel->addProperty( "id", tr( "ID:" ) );
    m_propertyPanel->addProperty( "type", tr( "Type:" ) );
    m_propertyPanel->addSeparator();
    m_propertyPanel->addProperty( "project", tr( "Project:" ) );
    m_propertyPanel->addProperty( "folder", tr( "Folder:" ) );
    m_propertyPanel->addSeparator();
    m_propertyPanel->addProperty( "createdDate", tr( "Created Date:" ) );
    m_propertyPanel->addProperty( "createdBy", tr( "Created By:" ) );
    m_propertyPanel->addSeparator();
    m_propertyPanel->addProperty( "modifiedDate", tr( "Modified Date:" ) );
    m_propertyPanel->addProperty( "modifiedBy", tr( "Modified By:" ) );

    updateProperties();
}

void IssueView::updateProperties()
{
    const IssueRow* issue = dataManager->issues()->find( id() );

    m_nameEdit->setText( issue ? LinkLocator::convertToHtml( issue->name(), 0 ) : QString() );

    m_propertyPanel->clearValues();

    m_propertyPanel->setValue( "id", TableModelsHelper::formatId( id() ) );

    if ( issue ) {
        const TypeRow* type = dataManager->types()->find( m_typeId );
        if ( type )
            m_propertyPanel->setValue( "type", type->name() );

        const FolderRow* folder = dataManager->folders()->find( m_folderId );
        if ( folder ) {
            m_propertyPanel->setValue( "folder", folder->name() );
            const ProjectRow* project = dataManager->projects()->find( folder->projectId() );
            if ( project )
                m_propertyPanel->setValue( "project", project->name() );
        }

        m_propertyPanel->setValue( "createdDate", DateTimeHelper::formatDateTime( issue->createdDate() ) );
        m_propertyPanel->setValue( "createdBy", TableModelsHelper::userName( issue->createdUser() ) );

        m_propertyPanel->setValue( "modifiedDate", DateTimeHelper::formatDateTime( issue->modifiedDate() ) );
        m_propertyPanel->setValue( "modifiedBy", TableModelsHelper::userName( issue->modifiedUser() ) );
    }
}

void IssueView::configureAttributes()
{
    if ( isEnabled() && m_currentPage == 0 ) {
        AttributeSettingsDialog dialog( mainWidget() );

        const TypeRow* type = dataManager->types()->find( m_typeId );
        QString name = type ? type->name() : QString();

        dialog.setPrompt( tr( "Default order of attributes for issues of type <b>%1</b>:" ).arg( name ) );

        IssueViewSettings settings;
        settings.openIssueView( m_typeId );

        dialog.setAttributes( settings.loadAttributes() );

        connect( &dialog, SIGNAL( settingsApplied() ), this, SLOT( applyAttributes() ) );

        dialog.exec();
    }
}

void IssueView::applyAttributes()
{
    if ( isEnabled() ) {
        IssueViewSettings settings;
        settings.openIssueView( m_typeId );

        AttributeSettingsDialog* dialog = (AttributeSettingsDialog*)sender();
        settings.saveAttributes( dialog->attributes() );

        viewManager->postViewEvent( metaObject()->className(), ViewEvent::UpdateAttributes, m_typeId );
    }
}

void IssueView::populateAttributes()
{
    m_attributePanel->clear();

    IssueViewSettings settings;
    settings.openIssueView( m_typeId );

    QList<int> attributes = settings.loadAttributes();

    for ( int i = 0; i < attributes.count(); i++ ) {
        int attributeId = attributes.at( i );
        m_attributePanel->addAttribute( attributeId, tr( "%1:" ).arg( TableModelsHelper::attributeName( attributeId ) ) );
    }

    updateAttributes();
}

void IssueView::updateAttributes()
{
    m_attributePanel->clearValues();

    RDB::ForeignConstIterator<ValueRow> it( dataManager->values()->otherIndex(), id() );
    while ( it.next() ) {
        const ValueRow* value = it.get();
        m_attributePanel->setValue( value->attributeId(), LinkLocator::convertToHtml( value->value(), 0 ) );
    }
}

void IssueView::populateComments()
{
    IssueHistoryProvider provider;
    provider.extractIssueHistory( id() );

    HTMLHistoryWriter writer( m_browser );
    provider.write( &writer );
}

void IssueView::linkContextMenu( const QString& link, const QPoint& pos )
{
    m_actionLink = link;
    updateActions();

    QUrl url( link );

    QString menuName;
    if ( url.scheme() == "id" )
        menuName = "contextLinkItem";
    else if ( url.scheme() == "attachment" )
        menuName = "contextLinkAttachment";
    else if ( url.scheme() == "mailto" )
        menuName = "contextLinkEmail";
    else
        menuName = "contextLink";

    QMenu* menu = builder()->contextMenu( menuName );
    if ( menu )
        menu->exec( pos );

    m_actionLink.clear();
    updateActions();
}

void IssueView::historyContextMenu( const QPoint& pos )
{
    QString link = m_browser->anchorAt( pos );
    if ( !link.isEmpty() ) {
        linkContextMenu( link, m_browser->mapToGlobal( pos ) );
        return;
    }

    QString menuName;
    if ( m_browser->textCursor().hasSelection() )
        menuName = "contextSelection";
    else
        menuName = "contextHistory";

    QMenu* menu = builder()->contextMenu( menuName );
    if ( menu )
        menu->exec( m_browser->mapToGlobal( pos ) );
}

void IssueView::attachmentsContextMenu( const QPoint& pos )
{
    QModelIndex index = m_list->indexAt( pos );

    QString menuName;
    if ( index.isValid() )
        menuName = "contextAttachment";
    else
        menuName = "contextNull";

    QMenu* menu = builder()->contextMenu( menuName );
    if ( menu )
        menu->exec( m_list->viewport()->mapToGlobal( pos ) );
}

void IssueView::doubleClicked( const QModelIndex& index )
{
    if ( index.isValid() ) {
        int attachmenId = m_model->data( index, TableItemModel::RowIdRole ).toInt();
        handleAttachment( attachmenId );
    }
}

void IssueView::linkClicked( const QString& link )
{
    anchorClicked( QUrl( link ) );
}

void IssueView::anchorClicked( const QUrl& url )
{
    int pos = m_browser->verticalScrollBar()->sliderPosition();
    m_browser->setSource( QUrl() );
    m_browser->verticalScrollBar()->setSliderPosition( pos );

    if ( url.scheme() == "id" ) {
        int itemId = url.host().toInt();
        findItem( itemId );
    } else if ( url.scheme() == "attachment" ) {
        int attachmentId = url.host().toInt();
        handleAttachment( attachmentId );
    } else {
        QDesktopServices::openUrl( url );
    }
}

void IssueView::findItem( int itemId )
{
    int issueId = dataManager->findItem( itemId );

    if ( issueId == 0 ) {
        FindItemDialog dialog( mainWidget() );
        dialog.findItem( itemId );
        if ( dialog.exec() == QDialog::Accepted )
            viewManager->openIssueView( dialog.issueId(), itemId );
    } else if ( issueId == id() ) {
        gotoItem( itemId );
    } else {
        viewManager->openIssueView( issueId, itemId );
    }
}

void IssueView::handleAttachment( int attachmentId )
{
    handleAttachment( attachmentId, configData->defaultAttachmentAction() );
}

void IssueView::handleAttachment( int attachmentId, AttachmentAction action )
{
    const AttachmentRow* attachment = dataManager->attachments()->find( attachmentId );
    QString name = attachment ? attachment->name() : QString();
    int size = attachment ? attachment->size() : 0;

    if ( action == ActionAsk ) {
        CheckMessageBox box( mainWidget() );

        box.setIcon( QMessageBox::Question );
        box.setWindowTitle( tr( "Attachment" ) );
        box.setText( tr( "Do you want to save or open attachment <b>%1</b>?" ).arg( name ) );
        box.setCheckBoxText( tr( "Do this automatically for all attachments" ) );
        box.setStandardButtons( QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel );

        box.button( QMessageBox::Yes )->setText( tr( "&Save As..." ) );
        box.button( QMessageBox::Yes )->setIcon( IconLoader::icon( "file-save-as" ) );
        box.button( QMessageBox::No )->setText( tr( "&Open" ) );
        box.button( QMessageBox::No )->setIcon( IconLoader::icon( "file-open" ) );
        box.button( QMessageBox::Cancel )->setMinimumHeight( box.button( QMessageBox::Yes )->sizeHint().height() );

        int result = box.exec();

        if ( result == QMessageBox::Yes )
            action = ActionSaveAs;
        else if ( result == QMessageBox::No )
            action = ActionOpen;
        else
            return;

        if ( box.isChecked() ) {
            configData->setDefaultAttachmentAction( action );
            configData->saveSettings();
        }
    }

    QString path;

    if ( action == ActionSaveAs ) {
        QSettings* settings = configData->settings();
        QString dir = settings->value( "Paths/SaveAttachment", QDir::homePath() ).toString();

        QFileInfo fileInfo( QDir( dir ), name );

        path = QFileDialog::getSaveFileName( mainWidget(), tr( "Save Attachment" ), fileInfo.absoluteFilePath() );
        if ( path.isEmpty() )
            return;

        fileInfo.setFile( path );
        settings->setValue( "Paths/SaveAttachment", fileInfo.absoluteDir().path() );
    }

    QString cachePath = connectionManager->attachmentsCache()->findAttachment( attachmentId );

    if ( cachePath.isEmpty() ) {
        cachePath = connectionManager->attachmentsCache()->allocAttachment( attachmentId, name, size );

        GetAttachmentDialog dialog( attachmentId, cachePath, path, mainWidget() );
        dialog.download();

        if ( dialog.exec() != QDialog::Accepted ) {
            QFile::remove( cachePath );
            return;
        }

        connectionManager->attachmentsCache()->commitAttachment();
    }

    if ( action == ActionOpen ) {
        QDesktopServices::openUrl( QUrl::fromLocalFile( cachePath ) );
    } else {
        if ( QFile::exists( path ) ) {
            if ( !QFile::remove( path ) ) {
                QMessageBox::warning( mainWidget(), tr( "Error" ), tr( "The file cannot be overwritten." ) );
                return;
            }
        }

        if ( !QFile::copy( cachePath, path ) )
            QMessageBox::warning( mainWidget(), tr( "Error" ), tr( "The file cannot be saved." ) );
    }
}

QModelIndex IssueView::selectedIndex()
{
    if ( !m_list->selectionModel() )
        return QModelIndex();

    QModelIndexList selection = m_list->selectionModel()->selectedRows();
    if ( selection.isEmpty() )
        return QModelIndex();

    return selection.at( 0 );
}

#include "issueview.moc"

Generated by  Doxygen 1.6.0   Back to index