diff --git a/debugger/variable/variablecollection.h b/debugger/variable/variablecollection.h index ca7546c7e8..deacba0bda 100644 --- a/debugger/variable/variablecollection.h +++ b/debugger/variable/variablecollection.h @@ -1,228 +1,227 @@ /* * KDevelop Debugger Support * * Copyright 2007 Hamish Rodda * Copyright 2008 Vladimir Prus * Copyright 2009 Niko Sams * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDEV_VARIABLECOLLECTION_H #define KDEV_VARIABLECOLLECTION_H #include #include -#include #include #include #include "../util/treemodel.h" #include "../util/treeitem.h" #include "../../interfaces/idocument.h" #include "../debuggerexport.h" #include "../interfaces/idebugsession.h" #include "../../interfaces/idebugcontroller.h" namespace GDBDebugger { class GdbTest; } namespace KDevelop { class VariableToolTip; class KDEVPLATFORMDEBUGGER_EXPORT Variable : public TreeItem { Q_OBJECT friend class GDBDebugger::GdbTest; public: protected: Variable(TreeModel* model, TreeItem* parent, const QString& expression, const QString& display = ""); public: enum format_t { Natural, Binary, Octal, Decimal, Hexadecimal }; static format_t str2format(const QString& str); static QString format2str(format_t format); QString expression() const; bool inScope() const; void setInScope(bool v); void setValue(const QString &v); QString value() const; void setTopLevel(bool v); using TreeItem::setHasMore; using TreeItem::setHasMoreInitial; using TreeItem::appendChild; using TreeItem::deleteChildren; using TreeItem::isExpanded; using TreeItem::parent; using TreeItem::model; ~Variable(); /* Connects this variable to debugger, fetching the current value and otherwise preparing this variable to be displayed everywhere. The attempt may fail, for example if the expression is invalid. Calls slot 'callbackMethod' in 'callback' to notify of the result. The slot should be taking 'bool ok' parameter. */ virtual void attachMaybe(QObject *callback = 0, const char *callbackMethod = 0) = 0; virtual bool canSetFormat() const { return false; } void setFormat(format_t format); format_t format() const { return m_format; } virtual void formatChanged(); // get/set 'changed' state, if the variable changed it will be highlighted bool isChanged() const { return changed_; } void setChanged(bool c); void resetChanged(); public slots: void die(); protected: bool topLevel() const { return topLevel_; } private: // TreeItem overrides QVariant data(int column, int role) const; private: QString expression_; bool inScope_; bool topLevel_; bool changed_; format_t m_format; }; class KDEVPLATFORMDEBUGGER_EXPORT TooltipRoot : public TreeItem { public: TooltipRoot(TreeModel* model) : TreeItem(model) {} void init(Variable *var) { appendChild(var); } void fetchMoreChildren() {} }; class KDEVPLATFORMDEBUGGER_EXPORT Watches : public TreeItem { friend class GDBDebugger::GdbTest; public: Watches(TreeModel* model, TreeItem* parent); Variable* add(const QString& expression); void reinstall(); Variable *addFinishResult(const QString& convenienceVarible); void removeFinishResult(); void resetChanged(); using TreeItem::childCount; friend class VariableCollection; friend class IVariableController; private: QVariant data(int column, int role) const; void fetchMoreChildren() {} Variable* finishResult_; }; class KDEVPLATFORMDEBUGGER_EXPORT Locals : public TreeItem { public: Locals(TreeModel* model, TreeItem* parent, const QString &name); QList updateLocals(QStringList locals); void resetChanged(); using TreeItem::deleteChildren; using TreeItem::setHasMore; friend class VariableCollection; friend class IVariableController; private: void fetchMoreChildren() {} }; class KDEVPLATFORMDEBUGGER_EXPORT VariablesRoot : public TreeItem { public: VariablesRoot(TreeModel* model); Watches *watches() const { return watches_; } Locals *locals(const QString &name = "Locals"); QHash allLocals() const; void fetchMoreChildren() {} void resetChanged(); private: Watches *watches_; QHash locals_; }; class KDEVPLATFORMDEBUGGER_EXPORT VariableCollection : public TreeModel { Q_OBJECT public: VariableCollection(IDebugController* parent); virtual ~VariableCollection(); VariablesRoot* root() const { return universe_; } Watches* watches() const { return universe_->watches(); } Locals* locals(const QString &name = "Locals") const { return universe_->locals(name); } QHash allLocals() const { return universe_->allLocals(); } public Q_SLOTS: void variableWidgetShown(); void variableWidgetHidden(); private Q_SLOTS: void updateAutoUpdate(KDevelop::IDebugSession* session = 0); private Q_SLOTS: void textDocumentCreated( KDevelop::IDocument*); void viewCreated(KTextEditor::Document*, KTextEditor::View*); void textHintRequested(const KTextEditor::Cursor&, QString&); private: VariablesRoot* universe_; - QPointer activeTooltip_; + QWeakPointer activeTooltip_; bool m_widgetVisible; }; } #endif diff --git a/language/duchain/builders/abstractcontextbuilder.h b/language/duchain/builders/abstractcontextbuilder.h index 56b8484ccd..12857be67c 100644 --- a/language/duchain/builders/abstractcontextbuilder.h +++ b/language/duchain/builders/abstractcontextbuilder.h @@ -1,672 +1,662 @@ /*************************************************************************** * This file is part of KDevelop * * Copyright 2008 Andreas Pakulat * * Copyright 2006 Roberto Raggi * * Copyright 2006-2008 Hamish Rodda * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef ABSTRACTABSTRACTCONTEXTBUILDER_H #define ABSTRACTABSTRACTCONTEXTBUILDER_H #include #include #include #include "../topducontext.h" #include "../duchainpointer.h" #include "../duchainlock.h" #include "../duchain.h" #include "../ducontext.h" #include "../identifier.h" #include "../indexedstring.h" #include "../parsingenvironment.h" namespace KDevelop { /** * \short Abstract definition-use chain context builder class * * The AbstractContextBuilder is a convenience class template for creating customized * definition-use chain context builders from an AST. It simplifies: * - use of your editor integrator * - creating or modifying an existing DUContext tree * - following a DUContext tree for second and subsequent passes, if required * - opening and closing DUContext instances * - tracking which DUContext instances are still present when recompiling, and removing DUContexts which no longer exist in the source code. * * \author Hamish Rodda \ */ template class AbstractContextBuilder { public: /// Constructor. AbstractContextBuilder() : m_compilingContexts( false ) , m_recompiling( false ) , m_lastContext( 0 ) { } /// Destructor. Deletes the editor integrator, if one was created specifically for this builder only. virtual ~AbstractContextBuilder() { } /** * Entry point for building a definition-use chain with this builder. * * This function determines whether we are updating a chain, or creating a new one. If we are * creating a new chain, a new TopDUContext is created and registered with DUChain. * * \param url Url of the document being parsed. * \param node AST node to start building from. * \param updateContext TopDUContext to update if a duchain was previously created for this url, otherwise pass a null pointer. * * \returns the newly created or updated TopDUContext pointer. */ virtual ReferencedTopDUContext build( const IndexedString& url, T* node, ReferencedTopDUContext updateContext = ReferencedTopDUContext() ) { m_compilingContexts = true; m_url = url; ReferencedTopDUContext top; { DUChainWriteLocker lock( DUChain::lock() ); top = updateContext.data(); if( top ) { m_recompiling = true; Q_ASSERT(top->type() == DUContext::Global); Q_ASSERT(DUChain::self()->chainForIndex(top->ownIndex()) == top); } else { top = newTopContext( RangeInRevision( CursorInRevision( 0, 0 ), CursorInRevision( INT_MAX, INT_MAX ) ) ); DUChain::self()->addDocumentChain( top ); top->setType( DUContext::Global ); } setEncountered( top ); setContextOnNode( node, top ); } supportBuild( node, top ); m_compilingContexts = false; return top; } protected: /** * Support another builder by tracking the current context. * @param context the context to use. Must be set when the given node has no context. When it has one attached, this parameter is not needed. */ virtual void supportBuild( T* node, DUContext* context = 0 ) { if (!context) context = contextFromNode(node); Q_ASSERT(context); openContext( context ); startVisiting(node); closeContext(); Q_ASSERT(m_contextStack.isEmpty()); } /** * Entry point to your visitor. Reimplement and call the appropriate visit function. * * \param node AST node to visit. */ virtual void startVisiting( T* node ) = 0; /** * Associate a \a context with a given AST \a node. Once called on a \a node, the * contextFromNode() function should return this \a context when called. * * \param node AST node to associate * \param context DUContext to associate */ virtual void setContextOnNode( T* node, DUContext* context ) = 0; /** * Retrieve an associated DUContext from the given \a node. Used on second and * subsequent passes of the context builder (for supporting other builds) * * \param node AST node which was previously associated * \returns the DUContext which was previously associated */ virtual DUContext* contextFromNode( T* node ) = 0; /** * Retrieves a text range from the given nodes. * * As editor integrators have to be extended to determine ranges from AST nodes, * this function must be reimplemented to allow generic retrieving of rangs from nodes. * * \param fromNode the AST node to start from (on the start boundary) * \param toNode the AST node to end at (on the end boundary) * * \returns the text range encompassing the given AST node(s) */ virtual RangeInRevision editorFindRange( T* fromNode, T* toNode ) = 0; /** * Retrieve a text range for the given nodes. This is a special function required * by c++ support as a different range may need to be retrieved depending on * whether macros are involved. It is not usually required to implement this * function separately to editorFindRange() for other languages. * * \param fromNode the AST node to start from (on the start boundary) * \param toNode the AST node to end at (on the end boundary) * * \returns the text range encompassing the given AST node(s) */ virtual RangeInRevision editorFindRangeForContext( T* fromNode, T* toNode ) { return editorFindRange(fromNode, toNode); } /** * Determine the QualifiedIdentifier which corresponds to the given ast \a node. * * \param node ast node which represents an identifier * \return the qualified identifier determined from \a node */ virtual QualifiedIdentifier identifierForNode( NameT* node ) = 0; /** * Create a new DUContext from the given \a range. * * This exists so that you can create custom DUContext subclasses for your * language if you need to. * * \param range range for the new context to encompass * \returns the newly created context */ virtual DUContext* newContext(const RangeInRevision& range) { return new DUContext(range, currentContext()); } /** * Create a new TopDUContext from the given \a range. * * This exists so that you can create custom TopDUContext subclasses for your * language if you need to. * * \returns the newly created context */ virtual TopDUContext* newTopContext(const RangeInRevision& range, ParsingEnvironmentFile* file = 0) { return new TopDUContext(m_url, range, file); } /// Determine the currently open context. \returns the current context. inline DUContext* currentContext() const { return m_contextStack.top(); } /// Determine the last closed context. \returns the last closed context. inline DUContext* lastContext() const { return m_lastContext; } /// Clears the last closed context. inline void clearLastContext() { m_lastContext = 0; } inline void setLastContext(DUContext* context) { m_lastContext = context; } TopDUContext* topContext() const { return currentContext()->topContext(); } /** * Determine if we are recompiling an existing definition-use chain, or if * a new chain is being created from scratch. * * \returns true if an existing duchain is being updated, otherwise false. */ inline bool recompiling() const { return m_recompiling; } /** * Tell the context builder whether we are recompiling an existing definition-use chain, or if * a new chain is being created from scratch. * * \param recomp set to true if an existing duchain is being updated, otherwise false. */ inline void setRecompiling(bool recomp) { m_recompiling = recomp; } /** * Determine whether this pass will create DUContext instances. * * On the first pass of definition-use chain compiling, DUContext instances * are created to represent contexts in the source code. These contexts are * associated with their AST nodes at the time (see setContextOnNode()). * * On second and subsequent passes, the contexts already exist and thus can be * retrieved through contextFromNode(). * * \returns true if compiling contexts (ie. 1st pass), otherwise false. */ inline bool compilingContexts() const { return m_compilingContexts; } /** * Sets whether we need to create ducontexts, ie. if this is the first pass. * * \sa compilingContexts() */ inline void setCompilingContexts(bool compilingContexts) { m_compilingContexts = compilingContexts; } /** * Create child contexts for only a portion of the document at \a url. * * \param url The url of the document to parse * \param node The AST node which corresponds to the context to parse * \param parent The DUContext which encompasses the \a node. * \returns The DUContext which was reparsed, ie. \a parent. */ DUContext* buildSubContexts( const KUrl& url, T *node, DUContext* parent ) { // m_compilingContexts = true; // m_recompiling = false; setContextOnNode( node, parent ); { openContext( contextFromNode( node ) ); startVisiting( node ); closeContext(); } m_compilingContexts = false; if ( contextFromNode( node ) == parent ) { kDebug() << "Error in AbstractContextBuilder::buildSubContexts(...): du-context was not replaced with new one"; DUChainWriteLocker lock( DUChain::lock() ); deleteContextOnNode( node ); } return contextFromNode( node ); } /** * Delete the DUContext which is associated with the given \a node, * and remove the association. * * \param node Node which is associated with the context to delete. */ void deleteContextOnNode( T* node ) { delete contextFromNode( node ); setContextOnNode( node, 0 ); } /** * Open a context, and create / update it if necessary. * * \param rangeNode The range which encompasses the context. * \param type The type of context to open. * \param identifier The range which encompasses the name of this context, if one exists. * \returns the opened context. */ DUContext* openContext( T* rangeNode, DUContext::ContextType type, NameT* identifier = 0) { if ( m_compilingContexts ) { DUContext* ret = openContextInternal( editorFindRangeForContext( rangeNode, rangeNode ), type, identifier ? identifierForNode( identifier ) : QualifiedIdentifier() ); setContextOnNode( rangeNode, ret ); return ret; } else { openContext( contextFromNode(rangeNode) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param node The range to associate with the context. * \param range A custom range which the context should encompass. * \param type The type of context to open. * \param identifier The range which encompasses the name of this context, if one exists. * \returns the opened context. */ DUContext* openContext(T* node, const KDevelop::RangeInRevision& range, DUContext::ContextType type, NameT* identifier = 0) { if (m_compilingContexts) { DUContext* ret = openContextInternal(range, type, identifier ? identifierForNode(identifier) : QualifiedIdentifier()); setContextOnNode( node, ret ); return ret; } else { openContext( contextFromNode(node) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param node The range to associate with the context. * \param range A custom range which the context should encompass. * \param type The type of context to open. * \param identifier The identifier for this context * \returns the opened context. */ DUContext* openContext(T* node, const KDevelop::RangeInRevision& range, DUContext::ContextType type, QualifiedIdentifier id) { if (m_compilingContexts) { DUContext* ret = openContextInternal(range, type, id); setContextOnNode( node, ret ); return ret; } else { openContext( contextFromNode(node) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param rangeNode The range which encompasses the context. * \param type The type of context to open. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ DUContext* openContext( T* rangeNode, DUContext::ContextType type, const QualifiedIdentifier& identifier ) { if ( m_compilingContexts ) { DUContext* ret = openContextInternal( editorFindRangeForContext( rangeNode, rangeNode ), type, identifier ); setContextOnNode( rangeNode, ret ); return ret; } else { openContext( contextFromNode(rangeNode) ); return currentContext(); } } /** * Open a context, and create / update it if necessary. * * \param fromRange The range which starts the context. * \param toRange The range which ends the context. * \param type The type of context to open. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ DUContext* openContext( T* fromRange, T* toRange, DUContext::ContextType type, const QualifiedIdentifier& identifier = QualifiedIdentifier() ) { if ( m_compilingContexts ) { DUContext* ret = openContextInternal( editorFindRangeForContext( fromRange, toRange ), type, identifier ); setContextOnNode( fromRange, ret ); return ret; } else { openContext( contextFromNode(fromRange) ); return currentContext(); } } /** * Open a newly created or previously existing context. * * The open context is put on the context stack, and becomes the new * currentContext(). * * \warning When you call this, you also have to open a range! If you want to re-use * the range associated to the context, use injectContext * * \param newContext Context to open. */ virtual void openContext( DUContext* newContext ) { m_contextStack.push( newContext ); m_nextContextStack.push( 0 ); } /** * This can be used to temporarily change the current context. * \param range The range that will be used as new current range, or zero(then the range associated to the context is used) * */ void injectContext( DUContext* ctx ) { openContext( ctx ); } /** * Use this to close the context previously injected with injectContext. * */ void closeInjectedContext() { m_contextStack.pop(); m_nextContextStack.pop(); } /** * Close the current DUContext. When recompiling, this function will remove any * contexts that were not encountered in this passing run. * \note The DUChain write lock is already held here. */ virtual void closeContext() { { DUChainWriteLocker lock( DUChain::lock() ); //Remove all slaves that were not encountered while parsing if(m_compilingContexts) currentContext()->cleanIfNotEncountered( m_encountered ); setEncountered( currentContext() ); m_lastContext = currentContext(); } m_contextStack.pop(); m_nextContextStack.pop(); } /** * Remember that a specific item has been encoutered while parsing. * All items that are not encountered will be deleted at some stage. * * \param item duchain item that was encountered. * */ void setEncountered( DUChainBase* item ) { m_encountered.insert( item ); } /** * Determine whether the given \a item is in the set of encountered items. * * @return true if the \a item has been encountered, otherwise false. * */ bool wasEncountered( DUChainBase* item ) { return m_encountered.contains( item ); } /** * Set the current identifier to \a id. * * \param id the new current identifier. */ void setIdentifier( const QString& id ) { m_identifier = Identifier( id ); m_qIdentifier.push( m_identifier ); } /** * Determine the current identifier. * \returns the current identifier. */ QualifiedIdentifier qualifiedIdentifier() const { return m_qIdentifier; } /** * Clears the current identifier. */ void clearQualifiedIdentifier() { m_qIdentifier.clear(); } /** * Retrieve the current context stack. This function is not expected * to be used often and may be phased out. * * \todo Audit whether access to the context stack is still required, and provide * replacement functionality if possible. */ const QStack& contextStack() const { return m_contextStack; } /** * Access the index of the child context which has been encountered. * * \todo further delineate the role of this function and rename / document better. * \todo make private again? */ int& nextContextIndex() { return m_nextContextStack.top(); } /** * Open a context, either creating it if it does not exist, or referencing a previously existing * context if already encountered in a previous duchain parse run (when recompiling()). * * \param range The range of the context. * \param type The type of context to create. * \param identifier The identifier which corresponds to the context. * \returns the opened context. */ virtual DUContext* openContextInternal( const RangeInRevision& range, DUContext::ContextType type, const QualifiedIdentifier& identifier ) { Q_ASSERT( m_compilingContexts ); DUContext* ret = 0L; { if ( recompiling() ) { - - ///@todo We should also somehow make sure we don't get quadratic worst-case effort while updating. - DUChainReadLocker readLock( DUChain::lock() ); const QVector& childContexts = currentContext()->childContexts(); int currentIndex = nextContextIndex(); for ( ; currentIndex < childContexts.count(); ++currentIndex ) { DUContext* child = childContexts.at( currentIndex ); RangeInRevision childRange = child->range(); //For unnamed child-ranges, we still do range-comparison, because we cannot distinguish them in other ways if ( (childRange == range && !childRange.isEmpty()) || (child->type() == type && child->localScopeIdentifier() == identifier && !identifier.isEmpty()) ) { // Match ret = child; readLock.unlock(); DUChainWriteLocker writeLock( DUChain::lock() ); ret->clearImportedParentContexts(); ++currentIndex; break; } } - - if(ret) { + if(ret) nextContextIndex() = currentIndex; //If we had a match, jump forward to that position - } else if (nextContextIndex()) { - // the matching-API expects the contexts to be opened/closed in correct order - // if you hit this assert, make sure that the ranges of your contexts are correct - // if this is not done, any kind of havoc can happen, esp. declarations being deleted - // randomly due to the not-matched (and hence not-encountered) contexts get cleaned up - Q_ASSERT(range.start > childContexts.at(nextContextIndex() - 1)->range().start); - } + ///@todo We should also somehow make sure we don't get quadratic worst-case effort while updating. } if ( !ret ) { DUChainWriteLocker writeLock( DUChain::lock() ); ret = newContext( range ); ret->setType( type ); if ( !identifier.isEmpty() ) ret->setLocalScopeIdentifier( identifier ); setInSymbolTable( ret ); }else{ if(ret->parentContext()) ret->setRange( range ); } } m_encountered.insert( ret ); openContext( ret ); return ret; } ///This function should call context->setInSymbolTable(..) with an appropriate decision. The duchain is write-locked when this is called. virtual void setInSymbolTable(DUContext* context) { if(!context->parentContext()->inSymbolTable()) { context->setInSymbolTable(false); return; } DUContext::ContextType type = context->type(); context->setInSymbolTable(type == DUContext::Class || type == DUContext::Namespace || type == DUContext::Global || type == DUContext::Helper || type == DUContext::Enum); } /// @returns the current url/path ot the document we are parsing IndexedString document() const { return m_url; } private: Identifier m_identifier; IndexedString m_url; QualifiedIdentifier m_qIdentifier; bool m_compilingContexts : 1; bool m_recompiling : 1; QStack m_nextContextStack; DUContext* m_lastContext; //Here all valid declarations/uses/... will be collected QSet m_encountered; QStack m_contextStack; }; } #endif diff --git a/plugins/dashboard/dashboard.cpp b/plugins/dashboard/dashboard.cpp index fc221b7b99..8949367757 100644 --- a/plugins/dashboard/dashboard.cpp +++ b/plugins/dashboard/dashboard.cpp @@ -1,140 +1,127 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dashboard.h" #include #include #include "dashboardcorona.h" #include "dashboardpluginloader.h" #include "appletselector.h" #include #include using namespace Plasma; Dashboard::Dashboard(DashboardCorona* corona, QWidget* parent) : View(corona->containments().first(), parent), corona(corona), m_selector(0) { m_selector=new AppletSelector("KDevelop", QStringList(), this); connect(m_selector, SIGNAL(addApplet(QString)), SLOT(addApplet(QString))); setFocusPolicy(Qt::NoFocus); connect(containment(), SIGNAL(showAddWidgetsInterface(QPointF)), this, SLOT(showAppletsSwitcher())); connect(containment(), SIGNAL(configureRequested(Plasma::Containment*)), this, SLOT(showConfigure())); connect(this, SIGNAL(sceneRectAboutToChange()), this, SLOT(updateView())); connect(corona, SIGNAL(containmentAdded(Plasma::Containment*)), SLOT(setContainment(Plasma::Containment*))); setScene(corona); setScreen(0); QMetaObject::invokeMethod(this, "updateView", Qt::QueuedConnection); } Dashboard::~Dashboard() { m_selector->hide(); + corona->saveLayout(QString()); + foreach (Plasma::Containment *containment, corona->containments()) { + containment->config().deleteEntry("geometry"); + containment->config().deleteEntry("zvalue"); + } } void Dashboard::updateView() { Containment* c=containment(); if (c && c->size().toSize() != size()) { c->scene()->setSceneRect(QRectF(QPointF(0,0), size())); c->resize(size()); ensureVisible(c); - - qDebug() << "pepepepepe" << c->boundingRect() << viewportTransform(); } } void Dashboard::resizeEvent(QResizeEvent* event) { // QGraphicsView::resizeEvent(event); updateView(); } void Dashboard::showAppletsSwitcher() { m_selector->show(); } void Dashboard::addApplet(const QString& name) { Applet* app=containment()->addApplet(name); Q_ASSERT(app); } KConfigGroup configurationDialog(Plasma::Containment* containment, const QString& plugin) { Q_ASSERT(containment); //FIXME: we have details about the structure of the containment config duplicated here! KConfigGroup cfg = containment->config(); cfg = KConfigGroup(&cfg, "Wallpaper"); return KConfigGroup(&cfg, plugin); } void Dashboard::showConfigure() { if(!m_configDialog) { - KConfigSkeleton *nullManager = new KConfigSkeleton(0); + KConfigSkeleton *nullManager = new KConfigSkeleton(QString(), this); m_configDialog = new KConfigDialog(this, "", nullManager); - connect(m_configDialog, SIGNAL(destroyed(QObject*)), nullManager, SLOT(deleteLater())); - } -// -// QWidget* w=containment()->wallpaper()->createConfigurationInterface(m_configDialog); -// ///////////////// - QWidget* w = 0; - Wallpaper* wallpaper = containment()->wallpaper(); - - if (!m_configDialog->layout()) { - new QVBoxLayout(m_configDialog); - } - - if (m_configDialog->layout()->count() > 0) { - delete dynamic_cast(m_configDialog->layout()->takeAt(0))->widget(); - } -/* - if (wallpaper && wallpaper->pluginName() != wallpaperInfo.first) { - delete wallpaper; - wallpaper = 0; - }*/ - - if (wallpaper) { -// wallpaper->setRenderingMode(wallpaperInfo.second); - KConfigGroup cfg = configurationDialog(containment(), wallpaper->name()); -// kDebug() << "making a" << wallpaperInfo.first << "in mode" << wallpaperInfo.second; -// wallpaper->restore(cfg); - w = wallpaper->createConfigurationInterface(m_configDialog); - } - - if (!w) { - w = new QWidget(m_configDialog); + + QWidget* w = 0; + Wallpaper* wallpaper = containment()->wallpaper(); + + if (wallpaper) { + // wallpaper->setRenderingMode(wallpaperInfo.second); + KConfigGroup cfg = configurationDialog(containment(), wallpaper->name()); + // kDebug() << "making a" << wallpaperInfo.first << "in mode" << wallpaperInfo.second; + // wallpaper->restore(cfg); + w = wallpaper->createConfigurationInterface(m_configDialog); + } + + if (!w) { + w = new QWidget(m_configDialog); + } + + m_configDialog->addPage(w, i18n("Background"), "preferences-desktop-wallpaper"); } - - m_configDialog->addPage(w, i18n("Background"), "preferences-desktop-wallpaper"); m_configDialog->show(); } #include "dashboard.moc" diff --git a/plugins/dashboard/dashboardview.cpp b/plugins/dashboard/dashboardview.cpp index db706886e4..766e5077c1 100644 --- a/plugins/dashboard/dashboardview.cpp +++ b/plugins/dashboard/dashboardview.cpp @@ -1,49 +1,55 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dashboardview.h" #include #include "dashboardcorona.h" #include #include "dashboard.h" #include #include #include #include using namespace Plasma; DashboardView::DashboardView(KDevelop::IProject* project, Sublime::Document* doc, Sublime::View::WidgetOwnership ws) : View(doc, ws), m_project(project) {} +DashboardView::~DashboardView() +{ + delete m_dashboard.data(); +} + QWidget* DashboardView::createWidget(QWidget* parent) { KUrl originalUrl=m_project->projectFileUrl().toLocalFile(); KUrl customUrl=originalUrl.upUrl(); customUrl.addPath(".kdev4/_custom.kdev4"); if(!QFile::exists(customUrl.toLocalFile())) { KIO::CopyJob* job=KIO::copy(originalUrl, customUrl); KIO::NetAccess::synchronousRun(job, 0); } DashboardCorona* corona=new DashboardCorona(m_project, this); corona->initializeLayout(customUrl.toLocalFile()); //TODO: decide what to do with remote files - return new Dashboard(corona); + m_dashboard=QWeakPointer(new Dashboard(corona)); + return m_dashboard.data(); } diff --git a/plugins/dashboard/dashboardview.h b/plugins/dashboard/dashboardview.h index 5822df3eee..4e080546e1 100644 --- a/plugins/dashboard/dashboardview.h +++ b/plugins/dashboard/dashboardview.h @@ -1,38 +1,42 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DASHBOARDVIEW_H #define DASHBOARDVIEW_H #include +#include +class Dashboard; namespace KDevelop { class IProject; } class DashboardView : public Sublime::View { public: DashboardView(KDevelop::IProject* project, Sublime::Document* doc, WidgetOwnership ws = DoNotTakeOwnerShip); - + virtual ~DashboardView(); + virtual QWidget* createWidget(QWidget* parent); private: KDevelop::IProject* m_project; + QWeakPointer m_dashboard; }; #endif // DASHBOARDVIEW_H diff --git a/plugins/dashboard/kdevprojectdashboard.desktop b/plugins/dashboard/kdevprojectdashboard.desktop index 69395582a1..03905d6191 100644 --- a/plugins/dashboard/kdevprojectdashboard.desktop +++ b/plugins/dashboard/kdevprojectdashboard.desktop @@ -1,26 +1,35 @@ [Desktop Entry] Type=Service Icon=dashboard-show Exec=blubb Comment=This plugin shows relevant information of some project when it's invoked +Comment[el]=Το πρόσθετο αυτό εμφανίζει σχετικές πληροφορίες κάποιου έργου όταν εκτελείται Comment[es]=Cuando se llama, este complemento muestra información relevante sobre algunos proyectos +Comment[nl]=Deze plugin toont relevante informatie over een project wanneer het wordt geactiveerd +Comment[pt]=Este 'plugin' mostra alguma informação relevante sobre um projecto quando for invocado Comment[uk]=Цей додаток показує дані щодо певного проекту, якщо його буде викликано Comment[x-test]=xxThis plugin shows relevant information of some project when it's invokedxx Name=Project Dashboard +Name[el]=Πίνακας ελέγχου έργου Name[es]=Tablero de mandos del proyecto +Name[nl]=Dashboard van het project +Name[pt]=Quadro do Projecto Name[uk]=Панель приладів проекту Name[x-test]=xxProject Dashboardxx GenericName=Project Dashboard +GenericName[el]=Πίνακας ελέγχου έργου GenericName[es]=Tablero de mandos del proyecto +GenericName[nl]=Dashboard van het project +GenericName[pt]=Quadro do Projecto GenericName[uk]=Панель приладів проекту GenericName[x-test]=xxProject Dashboardxx ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevprojectdashboard X-KDE-PluginInfo-Name=kdevprojectdashboard X-KDE-PluginInfo-Author=Aleix Pol X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-License=LGPL X-KDevelop-Version=12 X-KDevelop-Category=Global X-KDE-PluginInfo-Category=Core X-KDevelop-Mode=GUI diff --git a/plugins/documentview/settings/kcm_documentview_settings.desktop b/plugins/documentview/settings/kcm_documentview_settings.desktop index ae69d13425..ee45e11a7e 100644 --- a/plugins/documentview/settings/kcm_documentview_settings.desktop +++ b/plugins/documentview/settings/kcm_documentview_settings.desktop @@ -1,70 +1,70 @@ [Desktop Entry] Icon=kdevelop Type=Service ServiceTypes=KCModule X-KDE-ModuleType=Library X-KDE-Library=kcm_documentview_settings X-KDE-FactoryName=kcm_documentview_settings X-KDE-ParentApp=kdevelop X-KDE-ParentComponents=kdevelop X-KDE-CfgDlgHierarchy=CORE Name=Document View Name[bg]=Изглед за документи Name[ca]=Visor de document Name[ca@valencia]=Visor de document Name[da]=Dokumentvisning Name[de]=Dokumentansicht Name[el]=Προβολή εγγράφου Name[en_GB]=Document View Name[es]=Vista de documento Name[et]=Dokumendivaade Name[fr]=Vue Document Name[ga]=Amharc Cáipéise Name[gl]=Vista do documento Name[it]=Vista documento Name[ja]=文書ビュー Name[nb]=Dokumentvisning Name[nds]=Dokmentenansicht Name[nl]=Document-overzicht Name[pl]=Widok dokumentu Name[pt]=Área de Documentos Name[pt_BR]=Área de Documentos Name[sl]=Prikaz dokumentov Name[sv]=Dokumentvy Name[tr]=Belge Görünümü Name[ug]=پۈتۈك كۆرۈنۈشى Name[uk]=Перегляд документів Name[x-test]=xxDocument Viewxx Name[zh_CN]=文档视图 Name[zh_TW]=文件檢視 Comment=Configure Document View settings Comment[bg]=Настройки на изгледа за документи Comment[ca]=Configura els arranjaments del visor de document Comment[ca@valencia]=Configura els arranjaments del visor de document Comment[da]=Indtil dokumentvisning Comment[de]=Dokumentansicht einrichten -Comment[el]=Ρυθμίσεις προβολής εγγράφου +Comment[el]=Διαμόρφωση ρυθμίσεων προβολής εγγράφου Comment[en_GB]=Configure Document View settings Comment[es]=Configurar las preferencias de las vistas de documentos Comment[et]=Dokumendivaate valikute seadistamine Comment[fr]=Configurer les paramètres de la vue Document Comment[ga]=Cumraigh an tAmharc Cáipéise Comment[gl]=Configuración da vista do documento Comment[it]=Configura le impostazioni di visualizzazione del documento Comment[ja]=文書ビューのオプションを設定します Comment[nb]=Sett opp innstillinger for dokumentvisning Comment[nds]=Dokmentenansicht instellen Comment[nl]=Documentweergave-instellingen configureren Comment[pl]=Konfiguruj widok dokumentu Comment[pt]=Configurar as opções da Área de Documentos Comment[pt_BR]=Configurar as opções da Área de Documentos Comment[sl]=Nastavite prikaz dokumentov Comment[sv]=Anpassa inställningar av dokumentvy Comment[tr]=Belge Görünümü Ayarlarını Yapılandır Comment[uk]=Налаштувати параметри перегляду документа Comment[x-test]=xxConfigure Document View settingsxx Comment[zh_CN]=配置文档视图设置 Comment[zh_TW]=設定文件檢視 diff --git a/plugins/executescript/kdevexecutescript.desktop b/plugins/executescript/kdevexecutescript.desktop index 91c642bde4..89ef4f46fb 100644 --- a/plugins/executescript/kdevexecutescript.desktop +++ b/plugins/executescript/kdevexecutescript.desktop @@ -1,24 +1,33 @@ [Desktop Entry] Type=Service Exec=blubb Comment=This plugin allows running of scripts. +Comment[el]=Το πρόσθετο αυτό επιτρέπει την εκτέλεση σεναρίων. Comment[es]=Este complemento permite ejecutar scripts. +Comment[nl]=Deze plugin staat het uitvoeren van scripts toe. +Comment[pt]=Este 'plugin' permite a execução de programas. Comment[uk]=За допомогою цього додатка можна запускати скрипти. Comment[x-test]=xxThis plugin allows running of scripts.xx Name=Execute Scripts +Name[el]=Εκτέλεση σεναρίων Name[es]=Ejecutar scripts +Name[nl]=Scripts uitvoeren +Name[pt]=Executar Programas Name[uk]=Виконання скриптів Name[x-test]=xxExecute Scriptsxx GenericName=Script Execution Support +GenericName[el]=Υποστήριξη εκτέλεσης σεναρίων GenericName[es]=Implementación de la ejecución de scripts +GenericName[nl]=Ondersteuning voor uitvoeren van scripts +GenericName[pt]=Suporte à Execução de Programas GenericName[uk]=Підтримка виконання скриптів GenericName[x-test]=xxScript Execution Supportxx ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevexecutescript X-KDevelop-Version=12 X-KDevelop-Category=Global X-KDE-PluginInfo-Name=kdevexecutescript X-KDE-PluginInfo-Author=Niko Sams X-KDE-PluginInfo-License=GPL X-KDevelop-Interfaces=org.kdevelop.IExecuteScriptPlugin X-KDevelop-Mode=NoGUI diff --git a/plugins/genericprojectmanager/kcm_kdev_genericprojectmanagersettings.desktop b/plugins/genericprojectmanager/kcm_kdev_genericprojectmanagersettings.desktop index 6f8cbd25de..549495e790 100644 --- a/plugins/genericprojectmanager/kcm_kdev_genericprojectmanagersettings.desktop +++ b/plugins/genericprojectmanager/kcm_kdev_genericprojectmanagersettings.desktop @@ -1,79 +1,79 @@ [Desktop Entry] Icon=kdevelop Type=Service ServiceTypes=KCModule X-KDE-ModuleType=Library X-KDE-Library=kcm_kdev_genericprojectmanagersettings X-KDE-FactoryName=kcm_kdev_genericprojectmanagersettings X-KDE-ParentApp=kdevplatformproject X-KDE-ParentComponents=KDevGenericManager X-KDE-CfgDlgHierarchy=GENERAL Name=Project Settings Name[bg]=Настройки на проекта Name[ca]=Arranjaments del projecte Name[ca@valencia]=Arranjaments del projecte Name[cs]=Nastavení projektu Name[da]=Projektindstillinger Name[de]=Projekteinstellungen Name[el]=Ρυθμίσεις έργου Name[en_GB]=Project Settings Name[es]=Preferencias del proyecto Name[et]=Projekti seadistused Name[fr]=Paramètres du projet Name[gl]=Configuración do proxecto Name[it]=Impostazioni progetto Name[ja]=プロジェクトの設定 Name[lt]=Projekto nustatymai Name[lv]=Projekta iestatījumi Name[nb]=Prosjektinnstillinger Name[nds]=Projektinstellen Name[nl]=Projectinstellingen Name[pa]=ਪਰੋਜੈਕਟ ਸੈਟਿੰਗ Name[pl]=Ustawienia projektu Name[pt]=Configuração do Projecto Name[pt_BR]=Configurações do projeto Name[ro]=Configurări proiect Name[ru]=Параметры проекта Name[sl]=Nastavitve projekta Name[sv]=Projektinställningar Name[tr]=Proje Ayarları Name[uk]=Параметри проекту Name[x-test]=xxProject Settingsxx Name[zh_CN]=工程设置 Name[zh_TW]=專案設定 Comment=Configure Project Comment[bg]=Конфигуриране на проект Comment[ca]=Configura el projecte Comment[ca@valencia]=Configura el projecte Comment[cs]=Nastavit projekt Comment[da]=Indstil projekt Comment[de]=Projekt einrichten -Comment[el]=Ρύθμιση έργου +Comment[el]=Διαμόρφωση έργου Comment[en_GB]=Configure Project Comment[es]=Configurar proyecto Comment[et]=Projekti seadistamine Comment[fr]=Configurer le projet Comment[gl]=Configurar o proxecto Comment[it]=Configura il progetto Comment[ja]=プロジェクトを設定します Comment[lt]=Konfigūruoti projektą Comment[lv]=Konfigurēt projektu Comment[nb]=Sett opp prosjekt Comment[nds]=Projekt instellen Comment[nl]=Project instellen Comment[pa]=ਪਰੋਜੈੱਕਟ ਸੰਰਚਨਾ Comment[pl]=Konfiguruj projektu Comment[pt]=Configurar o Projecto Comment[pt_BR]=Configurar projeto Comment[ro]=Configurează proiectul Comment[ru]=Настройка проекта Comment[sl]=Nastavi projekt Comment[sv]=Anpassa projekt Comment[tr]=Projeyi Yapılandır Comment[ug]=قۇرۇلۇش سەپلىمىسى Comment[uk]=Налаштування проекту Comment[x-test]=xxConfigure Projectxx Comment[zh_CN]=配置工程 Comment[zh_TW]=設定專案 diff --git a/plugins/patchreview/patchreview.cpp b/plugins/patchreview/patchreview.cpp index 038797efd7..65840a5f65 100644 --- a/plugins/patchreview/patchreview.cpp +++ b/plugins/patchreview/patchreview.cpp @@ -1,1653 +1,1642 @@ /*************************************************************************** Copyright 2006-2009 David Nolden ***************************************************************************/ /*************************************************************************** * * * 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 "patchreview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libdiff2/komparemodellist.h" #include "libdiff2/kompare.h" #include #include #include #include #include #include #include #include #include #include #include #include ///Whether arbitrary exceptions that occurred while diff-parsing within the library should be caught #define CATCHLIBDIFF /* Exclude this file from doublequote_chars check as krazy doesn't understand std::string*/ //krazy:excludeall=doublequote_chars #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "diffsettings.h" #include #include #include "standardpatchexport.h" #include #include #include #include using namespace KDevelop; namespace { // Maximum number of files to open directly within a tab when the review is started const int maximumFilesToOpenDirectly = 15; } Q_DECLARE_METATYPE( const Diff2::DiffModel* ) class PatchFilesModel : public VcsFileChangesModel { public: - PatchFilesModel(QObject *parent, bool allowSelection = false) : VcsFileChangesModel(parent, allowSelection) { }; + PatchFilesModel(QObject *parent, bool allowSelection) : VcsFileChangesModel(parent, allowSelection) { }; enum ItemRoles { HunksNumberRole = VcsStatusInfoRole+1 }; public slots: void updateState(const KDevelop::VcsStatusInfo &status, unsigned hunksNum) { int row = VcsFileChangesModel::updateState(invisibleRootItem(), status); if (row == -1) return; QStandardItem *item = invisibleRootItem()->child(row, 0); setFileInfo(item, hunksNum); item->setData(QVariant(hunksNum), HunksNumberRole); } void updateState(const KDevelop::VcsStatusInfo &status) { int row = VcsFileChangesModel::updateState(invisibleRootItem(), status); if (row == -1) return; QStandardItem *item = invisibleRootItem()->child(row, 0); setFileInfo(invisibleRootItem()->child(row, 0), item->data(HunksNumberRole).toUInt()); } private: void setFileInfo(QStandardItem *item, unsigned int hunksNum) { QString newText = i18ncp("%1: number of changed hunks, %2: file name", "%2 (1 hunk)", "%2 (%1 hunks)", hunksNum, item->text()); item->setText(newText); } }; PatchReviewToolView::PatchReviewToolView( QWidget* parent, PatchReviewPlugin* plugin ) : QWidget( parent ), m_reversed( false ), m_plugin( plugin ) { connect( plugin, SIGNAL(patchChanged()), SLOT(patchChanged()) ); connect( ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), this, SLOT(documentActivated(KDevelop::IDocument*)) ); showEditDialog(); patchChanged(); } void PatchReviewToolView::patchChanged() { fillEditFromPatch(); kompareModelChanged(); } PatchReviewToolView::~PatchReviewToolView() { } LocalPatchSource* PatchReviewToolView::GetLocalPatchSource() { IPatchSource::Ptr ips = m_plugin->patch(); if ( !ips ) return 0; return dynamic_cast(ips.data()); } void PatchReviewToolView::updatePatchFromEdit() { LocalPatchSource* lpatch = GetLocalPatchSource(); if(!lpatch) return; lpatch->m_command = m_editPatch.command->text(); lpatch->m_filename = m_editPatch.filename->url(); lpatch->m_baseDir = m_editPatch.baseDir->url(); - lpatch->m_depth = m_editPatch.depth->value(); lpatch->setAlreadyApplied(m_editPatch.applied->checkState() == Qt::Checked); m_plugin->notifyPatchChanged(); } void PatchReviewToolView::fillEditFromPatch() { IPatchSource::Ptr ipatch = m_plugin->patch(); if ( !ipatch ) return ; disconnect( m_editPatch.patchSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(patchSelectionChanged(int))); m_editPatch.patchSelection->clear(); foreach(IPatchSource::Ptr patch, m_plugin->knownPatches()) { if(!patch) continue; m_editPatch.patchSelection->addItem(patch->icon(), patch->name()); if(patch == ipatch) m_editPatch.patchSelection->setCurrentIndex(m_editPatch.patchSelection->count()-1); } connect( m_editPatch.patchSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(patchSelectionChanged(int))); m_editPatch.cancelReview->setVisible(ipatch->canCancel()); QString finishText = i18n("Finish Review"); if(!ipatch->finishReviewCustomText().isEmpty()) finishText = ipatch->finishReviewCustomText(); kDebug() << "finish-text: " << finishText; m_editPatch.finishReview->setText(finishText); + m_fileModel->setIsCheckbable(m_plugin->patch()->canSelectFiles()); if(m_customWidget) { kDebug() << "removing custom widget"; m_customWidget->hide(); - m_editPatch.verticalLayout->removeWidget(m_customWidget); + m_editPatch.customWidgetsLayout->removeWidget(m_customWidget); } m_customWidget = ipatch->customWidget(); if(m_customWidget) { - m_editPatch.verticalLayout->insertWidget(0, m_customWidget); + m_editPatch.customWidgetsLayout->insertWidget(0, m_customWidget); m_customWidget->show(); kDebug() << "got custom widget"; } LocalPatchSource* lpatch = dynamic_cast(ipatch.data()); m_editPatch.tabWidget->setVisible(lpatch); m_editPatch.baseDir->setVisible(lpatch); m_editPatch.label->setVisible(lpatch); - m_editPatch.depth->setVisible(lpatch); - m_editPatch.depthLabel->setVisible(lpatch); m_editPatch.applied->setVisible(lpatch); if(!lpatch) return; m_editPatch.command->setText( lpatch->m_command ); m_editPatch.filename->setUrl( lpatch->m_filename ); m_editPatch.baseDir->setUrl( lpatch->m_baseDir ); - m_editPatch.depth->setValue( lpatch->m_depth ); m_editPatch.applied->setCheckState(lpatch->isAlreadyApplied() ? Qt::Checked : Qt::Unchecked); if ( lpatch->m_command.isEmpty() ) m_editPatch.tabWidget->setCurrentIndex( m_editPatch.tabWidget->indexOf( m_editPatch.fileTab ) ); else m_editPatch.tabWidget->setCurrentIndex( m_editPatch.tabWidget->indexOf( m_editPatch.commandTab ) ); } void PatchReviewToolView::patchSelectionChanged(int selection) { m_fileModel->removeRows(0, m_fileModel->rowCount()); if(selection >= 0 && selection < m_plugin->knownPatches().size()) { m_plugin->setPatch(m_plugin->knownPatches()[selection]); } } -void PatchReviewToolView::slotDepthChanged(int newDepth) -{ - if (LocalPatchSource* lpatch = GetLocalPatchSource()) { - lpatch->m_depth = newDepth; - m_plugin->notifyPatchChanged(); - } -} - void PatchReviewToolView::slotAppliedChanged(int newState) { if (LocalPatchSource* lpatch = GetLocalPatchSource()) { lpatch->setAlreadyApplied(newState == Qt::Checked); m_plugin->notifyPatchChanged(); } } void PatchReviewToolView::slotEditCommandChanged() { // m_editPatch.filename->lineEdit()->setText( "" ); updatePatchFromEdit(); } void PatchReviewToolView::slotEditFileNameChanged() { // m_editPatch.command->setText( "" ); updatePatchFromEdit(); } void PatchReviewToolView::showEditDialog() { m_editPatch.setupUi( this ); - m_fileModel = new PatchFilesModel(this, true); + m_fileModel = new PatchFilesModel(this, m_plugin->patch()->canSelectFiles()); m_editPatch.filesList->setModel(m_fileModel); m_editPatch.filesList->header()->hide(); m_editPatch.filesList->setRootIsDecorated(false); m_editPatch.previousHunk->setIcon(KIcon("arrow-up")); m_editPatch.nextHunk->setIcon(KIcon("arrow-down")); m_editPatch.cancelReview->setIcon(KIcon("dialog-cancel")); m_editPatch.finishReview->setIcon(KIcon("dialog-ok")); QMenu* exportMenu = new QMenu(m_editPatch.exportReview); StandardPatchExport* stdactions = new StandardPatchExport(m_plugin, this); stdactions->addActions(exportMenu); connect(exportMenu, SIGNAL(triggered(QAction*)), m_plugin, SLOT(exporterSelected(QAction*))); IPluginController* pluginManager = ICore::self()->pluginController(); foreach( IPlugin* p, pluginManager->allPluginsForExtension( "org.kdevelop.IPatchExporter" ) ) { KPluginInfo info=pluginManager->pluginInfo(p); QAction* action=exportMenu->addAction(KIcon(info.icon()), info.name()); action->setData(qVariantFromValue(p)); } m_editPatch.exportReview->setMenu(exportMenu); connect( m_editPatch.previousHunk, SIGNAL( clicked( bool ) ), this, SLOT( prevHunk() ) ); connect( m_editPatch.nextHunk, SIGNAL( clicked( bool ) ), this, SLOT( nextHunk() ) ); connect( m_editPatch.filesList, SIGNAL( doubleClicked( const QModelIndex& ) ), this, SLOT( fileDoubleClicked( const QModelIndex& ) ) ); connect( m_editPatch.cancelReview, SIGNAL(clicked(bool)), m_plugin, SLOT(cancelReview()) ); connect( m_editPatch.finishReview, SIGNAL(clicked(bool)), this, SLOT(finishReview()) ); //connect( m_editPatch.cancelButton, SIGNAL( pressed() ), this, SLOT( slotEditCancel() ) ); //connect( this, SIGNAL( finished( int ) ), this, SLOT( slotEditDialogFinished( int ) ) ); - connect( m_editPatch.depth, SIGNAL(valueChanged(int)), SLOT(slotDepthChanged(int)) ); connect( m_editPatch.applied, SIGNAL(stateChanged(int)), SLOT(slotAppliedChanged(int)) ); connect( m_editPatch.filename, SIGNAL( textChanged( const QString& ) ), SLOT(slotEditFileNameChanged()) ); - connect( m_editPatch.baseDir, SIGNAL(textChanged(QString)), SLOT(updatePatchFromEdit()) ); m_editPatch.baseDir->setMode(KFile::Directory); connect( m_editPatch.command, SIGNAL( textChanged( const QString& ) ), this, SLOT(slotEditCommandChanged()) ); // connect( m_editPatch.commandToFile, SIGNAL( clicked( bool ) ), this, SLOT( slotToFile() ) ); connect( m_editPatch.filename->lineEdit(), SIGNAL( returnPressed() ), this, SLOT(slotEditFileNameChanged()) ); connect( m_editPatch.filename->lineEdit(), SIGNAL( editingFinished() ), this, SLOT(slotEditFileNameChanged()) ); connect( m_editPatch.filename, SIGNAL( urlSelected( const KUrl& ) ), this, SLOT(slotEditFileNameChanged()) ); connect( m_editPatch.command, SIGNAL(textChanged(QString)), this, SLOT(slotEditCommandChanged()) ); // connect( m_editPatch.commandToFile, SIGNAL(clicked(bool)), m_plugin, SLOT(commandToFile()) ); connect( m_editPatch.patchSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(patchSelectionChanged(int))); connect( m_editPatch.updateButton, SIGNAL(clicked(bool)), m_plugin, SLOT(forceUpdate()) ); connect( m_editPatch.showButton, SIGNAL(clicked(bool)), m_plugin, SLOT(showPatch()) ); } void PatchReviewToolView::nextHunk() { // updateKompareModel(); m_plugin->seekHunk( true ); } void PatchReviewToolView::prevHunk() { // updateKompareModel(); m_plugin->seekHunk( false ); } KUrl PatchReviewPlugin::diffFile() { return m_patch->file(); } void PatchReviewPlugin::seekHunk( bool forwards, const KUrl& fileName ) { try { if ( !m_modelList.get() ) throw "no model"; for (int a = 0; a < m_modelList->modelCount(); ++a) { const Diff2::DiffModel* model = m_modelList->modelAt(a); if ( !model || !model->differences() ) continue; KUrl file = urlForFileModel( model ); if ( !fileName.isEmpty() && fileName != file ) continue; IDocument* doc = ICore::self()->documentController()->documentForUrl( file ); if ( doc && doc == ICore::self()->documentController()->activeDocument() && m_highlighters.contains(doc->url()) && m_highlighters[doc->url()] ) { ICore::self()->documentController()->activateDocument( doc ); if ( doc->textDocument() ) { KTextEditor::MovingInterface* moving = dynamic_cast( doc->textDocument() ); Q_ASSERT(moving); const QList< KTextEditor::MovingRange* > ranges = m_highlighters[doc->url()]->ranges(); KTextEditor::View * v = doc->textDocument() ->activeView(); int bestLine = -1; if ( v ) { KTextEditor::Cursor c = v->cursorPosition(); for ( QList< KTextEditor::MovingRange* >::const_iterator it = ranges.begin(); it != ranges.end(); ++it ) { int line; line = (*it)->start().line(); if ( forwards ) { if ( line > c.line() && ( bestLine == -1 || line < bestLine ) ) bestLine = line; } else { if ( line < c.line() && ( bestLine == -1 || line > bestLine ) ) bestLine = line; } } if ( bestLine != -1 ) { v->setCursorPosition( KTextEditor::Cursor( bestLine, 0 ) ); return ; } } } } } } catch ( const QString & str ) { kDebug() << "seekHunk():" << str; } catch ( const char * str ) { kDebug() << "seekHunk():" << str; } kDebug() << "no matching hunk found"; } void PatchReviewPlugin::addHighlighting(const KUrl& highlightFile, IDocument* document) { try { if ( !modelList() ) throw "no model"; for (int a = 0; a < modelList()->modelCount(); ++a) { Diff2::DiffModel* model = modelList()->modelAt(a); if ( !model ) continue; KUrl file = urlForFileModel( model ); if (file != highlightFile) continue; kDebug() << "highlighting" << file.prettyUrl(); IDocument* doc = document; if(!doc) doc = ICore::self()->documentController()->documentForUrl( file ); kDebug() << "highlighting file" << file << "with doc" << doc; if ( !doc || !doc->textDocument() ) continue; removeHighlighting( file ); m_highlighters[ file ] = new PatchHighlighter( model, doc, this ); } } catch ( const QString & str ) { kDebug() << "highlightFile():" << str; } catch ( const char * str ) { kDebug() << "highlightFile():" << str; } } void PatchReviewPlugin::highlightPatch() { try { if ( !modelList() ) throw "no model"; for (int a = 0; a < modelList()->modelCount(); ++a) { const Diff2::DiffModel* model = modelList()->modelAt(a); if ( !model ) continue; KUrl file = urlForFileModel( model ); addHighlighting(file); } } catch ( const QString & str ) { kDebug() << "highlightFile():" << str; } catch ( const char * str ) { kDebug() << "highlightFile():" << str; } } void PatchReviewToolView::finishReview() { QList selectedUrls = m_fileModel->checkedUrls(); kDebug() << "finishing review with" << selectedUrls; m_plugin->finishReview(selectedUrls); } void PatchReviewToolView::fileDoubleClicked( const QModelIndex& i ) { KUrl file = m_fileModel->statusInfo( i ).url(); kDebug() << "opening" << file.toLocalFile(); ICore::self()->documentController()->openDocument( file, KTextEditor::Cursor() ); m_plugin->seekHunk( true, file ); } KUrl PatchReviewPlugin::urlForFileModel(const Diff2::DiffModel* model) { KUrl file = m_patch->baseDir(); file.addPath( model->destinationPath() ); file.addPath( model->destinationFile() ); return file; } void PatchReviewToolView::kompareModelChanged() { - m_fileModel->removeRows(0, m_fileModel->rowCount()); + m_fileModel->clear(); if (!m_plugin->modelList()) return; QMap additionalUrls = m_plugin->patch()->additionalSelectableFiles(); const Diff2::DiffModelList* models = m_plugin->modelList()->models(); if( models ) { Diff2::DiffModelList::const_iterator it = models->constBegin(); for(; it != models->constEnd(); ++it) { Diff2::DifferenceList * diffs = ( *it ) ->differences(); int cnt = 0; if ( diffs ) cnt = diffs->count(); KUrl file = m_plugin->urlForFileModel(*it); if(!QFileInfo(file.toLocalFile()).isReadable()) continue; VcsStatusInfo status; status.setUrl(file); status.setState(VcsStatusInfo::ItemModified); m_fileModel->updateState(status, cnt); } } for(QMap::const_iterator it = additionalUrls.constBegin(); it != additionalUrls.constEnd(); it++) { VcsStatusInfo status; status.setUrl(it.key()); status.setState(it.value()); m_fileModel->updateState(status); } m_editPatch.filesList->resizeColumnToContents(0); } void PatchReviewToolView::documentActivated(IDocument* doc) { - QModelIndexList i = m_editPatch.filesList->selectionModel() ->selectedIndexes(); if ( !m_plugin->modelList() ) return ; + QStandardItem *fileItem = m_fileModel->fileItemForUrl(doc->url()); if (fileItem) { m_editPatch.filesList->setCurrentIndex(fileItem->index()); } else { m_editPatch.filesList->setCurrentIndex(QModelIndex()); } } void PatchHighlighter::aboutToDeleteMovingInterfaceContent(KTextEditor::Document* ) { kDebug() << "about to delete"; clear(); } QSize sizeHintForHtml(QString html, QSize maxSize) { QTextDocument doc; doc.setHtml(html); QSize ret; if(doc.idealWidth() > maxSize.width()) { doc.setPageSize( QSize(maxSize.width(), 30) ); ret.setWidth(maxSize.width()); }else{ ret.setWidth(doc.idealWidth()); } ret.setHeight(doc.size().height()); if(ret.height() > maxSize.height()) ret.setHeight(maxSize.height()); return ret; } namespace { QPointer currentTooltip; KTextEditor::MovingRange* currentTooltipMark; } void PatchHighlighter::showToolTipForMark(QPoint pos, KTextEditor::MovingRange* markRange, QPair< int, int > highlightMark) { if(currentTooltipMark == markRange && currentTooltip) return; delete currentTooltip; //Got the difference Diff2::Difference* diff = m_differencesForRanges[markRange]; QString html; #if 0 if(diff->hasConflict()) html += i18n("Conflict
"); #endif Diff2::DifferenceStringList lines; if(m_plugin->patch()->isAlreadyApplied() && !diff->applied()) html += i18n("Reverted.
"); else if(!m_plugin->patch()->isAlreadyApplied() && diff->applied()) html += i18n("Applied.
"); if(diff->applied()) { if(isInsertion(diff)) { html += i18n("Insertion
"); }else{ if(isRemoval(diff)) html += i18n("Removal
"); html += i18n("Previous:
"); lines = diff->sourceLines(); } }else{ if(isRemoval(diff)) { html += i18n("Removal
"); }else{ if(isInsertion(diff)) html += i18n("Insertion
"); html += i18n("Alternative:
"); lines = diff->destinationLines(); } } for(int a = 0; a < lines.size(); ++a) { Diff2::DifferenceString* line = lines[a]; uint currentPos = 0; QString string = line->string(); Diff2::MarkerList markers = line->markerList(); for(int b = 0; b < markers.size(); ++b) { QString spanText = Qt::escape(string.mid(currentPos, markers[b]->offset() - currentPos)); if(markers[b]->type() == Diff2::Marker::End && (currentPos != 0 || markers[b]->offset() != string.size())) { if(a == highlightMark.first && b == highlightMark.second) html += "" + spanText + ""; else html += "" + spanText + ""; }else{ html += spanText; } currentPos = markers[b]->offset(); } html += Qt::escape(string.mid(currentPos, string.length()-currentPos)); html += "
"; } KTextBrowser* browser = new KTextBrowser; browser->setPalette( QApplication::palette() ); browser->setHtml(html); int maxHeight = 500; browser->setMinimumSize(sizeHintForHtml(html, QSize((ICore::self()->uiController()->activeMainWindow()->width()*2)/3, maxHeight))); browser->setMaximumSize(browser->minimumSize() + QSize(10, 10)); if(browser->minimumHeight() != maxHeight) browser->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QVBoxLayout* layout = new QVBoxLayout; layout->setMargin(0); layout->addWidget(browser); KDevelop::ActiveToolTip* tooltip = new KDevelop::ActiveToolTip(ICore::self()->uiController()->activeMainWindow(), pos + QPoint(5, -browser->sizeHint().height() - 30)); tooltip->setLayout(layout); tooltip->resize( tooltip->sizeHint() + QSize(10, 10) ); tooltip->move(pos - QPoint(0, 20 + tooltip->height())); tooltip->addExtendRect(QRect(pos - QPoint(15, 15), pos + QPoint(15, 15))); currentTooltip = tooltip; currentTooltipMark = markRange; ActiveToolTip::showToolTip(tooltip); } void PatchHighlighter::markClicked(KTextEditor::Document* doc, KTextEditor::Mark mark, bool& handled) { m_applying = true; if(handled) return; handled = true; if(doc->activeView()) ///This is a workaround, if the cursor is somewhere else, the editor will always jump there when a mark was clicked doc->activeView()->setCursorPosition(KTextEditor::Cursor(mark.line, 0)); KTextEditor::MovingRange* range = rangeForMark(mark); if(range) { KTextEditor::MovingInterface* moving = dynamic_cast( doc ); Q_ASSERT(moving); QString currentText = doc->text(range->toRange()); Diff2::Difference* diff = m_differencesForRanges[range]; removeLineMarker(range); QString sourceText; QString targetText; for(int a = 0; a < diff->sourceLineCount(); ++a) { sourceText += diff->sourceLineAt(a)->string(); if(!sourceText.endsWith("\n")) sourceText += "\n"; } for(int a = 0; a < diff->destinationLineCount(); ++a) { targetText += diff->destinationLineAt(a)->string(); if(!targetText.endsWith("\n")) targetText += "\n"; } QString replace; QString replaceWith; if(!diff->applied()) { replace = sourceText; replaceWith = targetText; }else { replace = targetText; replaceWith = sourceText; } if(currentText.simplified() != replace.simplified()) { KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), i18n("Could not apply the change: Text should be \"%1\", but is \"%2\".", replace, currentText)); return; } diff->apply(!diff->applied()); KTextEditor::Cursor start = range->start().toCursor(); range->document()->replaceText(range->toRange(), replaceWith); KTextEditor::Range newRange(start, start); uint replaceWithLines = replaceWith.count('\n'); newRange.end().setLine(newRange.end().line() + replaceWithLines); range->setRange(newRange); addLineMarker(range, diff); } { // After applying the change, show the tooltip again, mainly to update an old tooltip delete currentTooltip; bool h = false; markToolTipRequested(doc, mark, QCursor::pos(), h); } m_applying = false; } KTextEditor::MovingRange* PatchHighlighter::rangeForMark(KTextEditor::Mark mark) { for(QMap< KTextEditor::MovingRange*, Diff2::Difference* >::const_iterator it = m_differencesForRanges.constBegin(); it != m_differencesForRanges.constEnd(); ++it) { if(it.key()->start().line() == mark.line) { return it.key(); } } return 0; } void PatchHighlighter::markToolTipRequested(KTextEditor::Document* , KTextEditor::Mark mark, QPoint pos, bool& handled) { if(handled) return; handled = true; int myMarksPattern = KTextEditor::MarkInterface::markType22 | KTextEditor::MarkInterface::markType23 | KTextEditor::MarkInterface::markType24 | KTextEditor::MarkInterface::markType25 | KTextEditor::MarkInterface::markType26 | KTextEditor::MarkInterface::markType27; if(mark.type & myMarksPattern) { //There is a mark in this line. Show the old text. KTextEditor::MovingRange* range = rangeForMark(mark); if(range) showToolTipForMark(pos, range); } } bool PatchHighlighter::isInsertion(Diff2::Difference* diff) { return diff->sourceLineCount() == 0; } bool PatchHighlighter::isRemoval(Diff2::Difference* diff) { return diff->destinationLineCount() == 0; } QStringList PatchHighlighter::splitAndAddNewlines(const QString& text) const { QStringList result = text.split('\n', QString::KeepEmptyParts); for(QStringList::iterator iter = result.begin(); iter != result.end(); ++iter) { iter->append('\n'); } if (!result.isEmpty()) { QString & last = result.last(); last.remove(last.size() - 1, 1); } return result; } void PatchHighlighter::performContentChange(KTextEditor::Document* doc, const QStringList& oldLines, const QStringList& newLines, int editLineNumber) { QPair, QList > diffChange = m_model->linesChanged(oldLines, newLines, editLineNumber); QList inserted = diffChange.first; QList removed = diffChange.second; // Remove all ranges that are in the same line (the line markers) foreach(KTextEditor::MovingRange* r, m_differencesForRanges.keys()) { Diff2::Difference* diff = m_differencesForRanges[r]; if (removed.contains(diff)) { removeLineMarker(r); m_ranges.remove(r); m_differencesForRanges.remove(r); delete r; delete diff; } } KTextEditor::MovingInterface* moving = dynamic_cast( doc ); if ( !moving ) return; foreach(Diff2::Difference* diff, inserted) { int lineStart = diff->destinationLineNumber(); if (lineStart > 0) { --lineStart; } int lineEnd = diff->destinationLineEnd(); if (lineEnd > 0) { --lineEnd; } KTextEditor::Range newRange(lineStart, 0, lineEnd, 0); KTextEditor::MovingRange * r = moving->newMovingRange( newRange ); m_differencesForRanges[r] = diff; m_ranges.insert(r); addLineMarker(r, diff); } } void PatchHighlighter::textRemoved(KTextEditor::Document* doc, const KTextEditor::Range& range, const QString& oldText) { if (m_applying) { // Do not interfere with patch application return; } kDebug() << "removal range" << range; kDebug() << "removed text" << oldText; QStringList removedLines = splitAndAddNewlines(oldText); int startLine = range.start().line(); QString remainingLine = doc->line(startLine); remainingLine += '\n'; QString prefix = remainingLine.mid(0, range.start().column()); QString suffix = remainingLine.mid(range.start().column()); if (!removedLines.empty()) { removedLines.first() = prefix + removedLines.first(); removedLines.last() = removedLines.last() + suffix; } performContentChange(doc, removedLines, QStringList() << remainingLine, startLine + 1); } void PatchHighlighter::textInserted(KTextEditor::Document* doc, KTextEditor::Range range) { if(range == doc->documentRange()) { kDebug() << "re-doing"; //The document was loaded / reloaded if ( !m_model->differences() ) return ; KTextEditor::MovingInterface* moving = dynamic_cast( doc ); if ( !moving ) return; KTextEditor::MarkInterface* markIface = dynamic_cast( doc ); if( !markIface ) return; clear(); - QColor activeIconColor = QApplication::palette().color(QPalette::Active, QPalette::Highlight); - QColor inActiveIconColor = QApplication::palette().color(QPalette::Active, QPalette::Base); - KColorScheme scheme(QPalette::Active); QImage tintedInsertion = KIcon("insert-text").pixmap(16, 16).toImage(); KIconEffect::colorize(tintedInsertion, scheme.foreground(KColorScheme::NegativeText).color(), 1.0); QImage tintedRemoval = KIcon("edit-delete").pixmap(16, 16).toImage(); KIconEffect::colorize(tintedRemoval, scheme.foreground(KColorScheme::NegativeText).color(), 1.0); QImage tintedChange = KIcon("text-field").pixmap(16, 16).toImage(); KIconEffect::colorize(tintedChange, scheme.foreground(KColorScheme::NegativeText).color(), 1.0); markIface->setMarkDescription(KTextEditor::MarkInterface::markType22, i18n("Insertion")); markIface->setMarkPixmap(KTextEditor::MarkInterface::markType22, QPixmap::fromImage(tintedInsertion)); markIface->setMarkDescription(KTextEditor::MarkInterface::markType23, i18n("Removal")); markIface->setMarkPixmap(KTextEditor::MarkInterface::markType23, QPixmap::fromImage(tintedRemoval)); markIface->setMarkDescription(KTextEditor::MarkInterface::markType24, i18n("Change")); markIface->setMarkPixmap(KTextEditor::MarkInterface::markType24, QPixmap::fromImage(tintedChange)); markIface->setMarkDescription(KTextEditor::MarkInterface::markType25, i18n("Insertion")); markIface->setMarkPixmap(KTextEditor::MarkInterface::markType25, KIcon("insert-text").pixmap(16, 16)); markIface->setMarkDescription(KTextEditor::MarkInterface::markType26, i18n("Removal")); markIface->setMarkPixmap(KTextEditor::MarkInterface::markType26, KIcon("edit-delete").pixmap(16, 16)); markIface->setMarkDescription(KTextEditor::MarkInterface::markType27, i18n("Change")); markIface->setMarkPixmap(KTextEditor::MarkInterface::markType27, KIcon("text-field").pixmap(16, 16)); for ( Diff2::DifferenceList::const_iterator it = m_model->differences() ->constBegin(); it != m_model->differences() ->constEnd(); ++it ) { Diff2::Difference* diff = *it; int line, lineCount; Diff2::DifferenceStringList lines ; if(diff->applied()) { line = diff->destinationLineNumber(); lineCount = diff->destinationLineCount(); lines = diff->destinationLines(); } else { line = diff->sourceLineNumber(); lineCount = diff->sourceLineCount(); lines = diff->sourceLines(); } if ( line > 0 ) line -= 1; KTextEditor::Cursor c( line, 0 ); KTextEditor::Cursor endC( line + lineCount, 0 ); if ( doc->lines() <= c.line() ) c.setLine( doc->lines() - 1 ); if ( doc->lines() <= endC.line() ) endC.setLine( doc->lines() ); if ( endC.isValid() && c.isValid() ) { KTextEditor::MovingRange * r = moving->newMovingRange( KTextEditor::Range(c, endC) ); m_ranges << r; m_differencesForRanges[r] = *it; addLineMarker(r, diff); } } } else { if (m_applying) { // Do not interfere with patch application return; } kDebug() << "insertion range" << range; QString text = doc->text(range); kDebug() << "inserted text" << text; QStringList insertedLines = splitAndAddNewlines(text); int startLine = range.start().line(); int endLine = range.end().line(); QString prefix = doc->line(startLine).mid(0, range.start().column()); QString suffix = doc->line(endLine).mid(range.end().column()); suffix += '\n'; QString removedLine = prefix + suffix; if (!insertedLines.empty()) { insertedLines.first() = prefix + insertedLines.first(); insertedLines.last() = insertedLines.last() + suffix; } performContentChange(doc, QStringList() << removedLine, insertedLines, startLine + 1); } } PatchHighlighter::PatchHighlighter( Diff2::DiffModel* model, IDocument* kdoc, PatchReviewPlugin* plugin ) throw( QString ) : m_doc( kdoc ), m_plugin(plugin), m_model(model), m_applying(false) { // connect( kdoc, SIGNAL( destroyed( QObject* ) ), this, SLOT( documentDestroyed() ) ); connect( kdoc->textDocument(), SIGNAL(textInserted(KTextEditor::Document*,KTextEditor::Range)), this, SLOT(textInserted(KTextEditor::Document*,KTextEditor::Range)) ); connect( kdoc->textDocument(), SIGNAL(textChanged(KTextEditor::Document*, const KTextEditor::Range&, const KTextEditor::Range&)), this, SLOT(textChanged(KTextEditor::Document*, const KTextEditor::Range&, const KTextEditor::Range&)) ); connect( kdoc->textDocument(), SIGNAL(textRemoved(KTextEditor::Document*, const KTextEditor::Range&, const QString&)), this, SLOT(textRemoved(KTextEditor::Document*, const KTextEditor::Range&, const QString&)) ); connect( kdoc->textDocument(), SIGNAL( destroyed( QObject* ) ), this, SLOT( documentDestroyed() ) ); KTextEditor::Document* doc = kdoc->textDocument(); if ( doc->lines() == 0 ) return ; connect(doc, SIGNAL(markToolTipRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&)), this, SLOT(markToolTipRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&))); connect(doc, SIGNAL(markClicked(KTextEditor::Document*,KTextEditor::Mark,bool&)), this, SLOT(markClicked(KTextEditor::Document*,KTextEditor::Mark,bool&))); connect(doc, SIGNAL(aboutToDeleteMovingInterfaceContent (KTextEditor::Document*)), this, SLOT(aboutToDeleteMovingInterfaceContent(KTextEditor::Document*))); textInserted(kdoc->textDocument(), kdoc->textDocument()->documentRange()); } void PatchHighlighter::removeLineMarker(KTextEditor::MovingRange* range) { KTextEditor::MovingInterface* moving = dynamic_cast( range->document() ); if ( !moving ) return; KTextEditor::MarkInterface* markIface = dynamic_cast( range->document() ); if( !markIface ) return; markIface->removeMark(range->start().line(), KTextEditor::MarkInterface::markType22); markIface->removeMark(range->start().line(), KTextEditor::MarkInterface::markType23); markIface->removeMark(range->start().line(), KTextEditor::MarkInterface::markType24); markIface->removeMark(range->start().line(), KTextEditor::MarkInterface::markType25); markIface->removeMark(range->start().line(), KTextEditor::MarkInterface::markType26); markIface->removeMark(range->start().line(), KTextEditor::MarkInterface::markType27); // Remove all ranges that are in the same line (the line markers) foreach(KTextEditor::MovingRange* r, m_ranges) { if(r != range && range->contains(r->toRange())) { delete r; m_ranges.remove(r); m_differencesForRanges.remove(r); } } } void PatchHighlighter::addLineMarker(KTextEditor::MovingRange* range, Diff2::Difference* diff) { KTextEditor::MovingInterface* moving = dynamic_cast( range->document() ); if ( !moving ) return; KTextEditor::MarkInterface* markIface = dynamic_cast( range->document() ); if( !markIface ) return; KSharedPtr t( new KTextEditor::Attribute() ); bool isOriginalState = diff->applied() == m_plugin->patch()->isAlreadyApplied(); if(isOriginalState) { t->setProperty( QTextFormat::BackgroundBrush, QBrush( ColorCache::self()->blendBackground( QColor( 0, 255, 255), 20 ) ) ); }else{ t->setProperty( QTextFormat::BackgroundBrush, QBrush( ColorCache::self()->blendBackground( QColor( 255, 0, 255), 20 ) ) ); } range->setAttribute( t ); range->setZDepth( -500 ); KTextEditor::MarkInterface::MarkTypes mark; if(isOriginalState) { mark = KTextEditor::MarkInterface::markType27; if(isInsertion(diff)) mark = KTextEditor::MarkInterface::markType25; if(isRemoval(diff)) mark = KTextEditor::MarkInterface::markType26; }else{ mark = KTextEditor::MarkInterface::markType24; if(isInsertion(diff)) mark = KTextEditor::MarkInterface::markType22; if(isRemoval(diff)) mark = KTextEditor::MarkInterface::markType23; } markIface->addMark(range->start().line(), mark); Diff2::DifferenceStringList lines; if(diff->applied()) lines = diff->destinationLines(); else lines = diff->sourceLines(); for(int a = 0; a < lines.size(); ++a) { Diff2::DifferenceString* line = lines[a]; int currentPos = 0; QString string = line->string(); Diff2::MarkerList markers = line->markerList(); for(int b = 0; b < markers.size(); ++b) { if(markers[b]->type() == Diff2::Marker::End) { if(currentPos != 0 || markers[b]->offset() != string.size()) { KTextEditor::MovingRange * r2 = moving->newMovingRange( KTextEditor::Range( KTextEditor::Cursor(a + range->start().line(), currentPos), KTextEditor::Cursor(a + range->start().line(), markers[b]->offset()) ) ); m_ranges << r2; KSharedPtr t( new KTextEditor::Attribute() ); t->setProperty( QTextFormat::BackgroundBrush, QBrush( ColorCache::self()->blendBackground( QColor( 255, 0, 0), 70 ) ) ); r2->setAttribute( t ); r2->setZDepth( -600 ); } } currentPos = markers[b]->offset(); } } } void PatchHighlighter::clear() { if(m_ranges.empty()) return; KTextEditor::MovingInterface* moving = dynamic_cast( m_doc->textDocument() ); if ( !moving ) return; KTextEditor::MarkInterface* markIface = dynamic_cast( m_doc->textDocument() ); if( !markIface ) return; QHash< int, KTextEditor::Mark* > marks = markIface->marks(); foreach(int line, marks.keys()) { markIface->removeMark(line, KTextEditor::MarkInterface::markType22); markIface->removeMark(line, KTextEditor::MarkInterface::markType23); markIface->removeMark(line, KTextEditor::MarkInterface::markType24); markIface->removeMark(line, KTextEditor::MarkInterface::markType25); markIface->removeMark(line, KTextEditor::MarkInterface::markType26); markIface->removeMark(line, KTextEditor::MarkInterface::markType27); } qDeleteAll(m_ranges); m_ranges.clear(); m_differencesForRanges.clear(); } PatchHighlighter::~PatchHighlighter() { clear(); } IDocument* PatchHighlighter::doc() { return m_doc; } void PatchHighlighter::documentDestroyed() { kDebug() << "document destroyed"; m_ranges.clear(); m_differencesForRanges.clear(); } void PatchReviewPlugin::removeHighlighting( const KUrl& file ) { if ( file.isEmpty() ) { ///Remove all highlighting qDeleteAll(m_highlighters); m_highlighters.clear(); } else { HighlightMap::iterator it = m_highlighters.find( file ); if ( it != m_highlighters.end() ) { delete * it; m_highlighters.erase( it ); } } } void PatchReviewPlugin::notifyPatchChanged() { kDebug() << "notifying patch change: " << m_patch->file(); m_updateKompareTimer->start(500); } void PatchReviewPlugin::showPatch() { startReview(m_patch, OpenAndRaise); } void PatchReviewPlugin::forceUpdate() { - m_patch->update(); + if(m_patch) { + m_patch->update(); - notifyPatchChanged(); + notifyPatchChanged(); + } } void PatchReviewPlugin::updateKompareModel() { if (!m_patch) { ///TODO: this method should be cleaned up, it can be called by the timer and /// e.g. https://bugs.kde.org/show_bug.cgi?id=267187 shows how it could /// lead to asserts before... return; } kDebug() << "updating model"; try { Q_ASSERT(m_patch); removeHighlighting(); m_modelList.reset( 0 ); delete m_diffSettings; emit patchChanged(); { IDocument* patchDoc = ICore::self()->documentController()->documentForUrl(m_patch->file()); if(patchDoc) patchDoc->reload(); } m_diffSettings = new DiffSettings( 0 ); m_kompareInfo.reset( new Kompare::Info() ); m_kompareInfo->localDestination=m_patch->file().toLocalFile(); m_kompareInfo->localSource=m_patch->baseDir().toLocalFile(); m_kompareInfo->depth = m_patch->depth(); m_kompareInfo->applied = m_patch->isAlreadyApplied(); m_modelList.reset(new Diff2::KompareModelList( m_diffSettings.data(), new QWidget, this )); m_modelList->slotKompareInfo(m_kompareInfo.get()); try { if ( !m_modelList->openDirAndDiff() ) { #if 0 // Don't error out on empty files, as those are valid diffs too if(QFileInfo(m_patch->file().toLocalFile()).size() != 0) throw "could not open diff " + m_patch->file().prettyUrl() + " on " + m_patch->baseDir().prettyUrl(); #endif } } catch ( const QString & str ) { throw; } catch ( ... ) { throw QString( "lib/libdiff2 crashed, memory may be corrupted. Please restart kdevelop." ); } emit patchChanged(); for(int i=0; imodelCount(); i++) { const Diff2::DiffModel* model=m_modelList->modelAt(i); for(int j=0; jdifferences()->count(); j++) { model->differences()->at(j)->apply(m_patch->isAlreadyApplied()); } } highlightPatch(); return; } catch ( const QString & str ) { KMessageBox::error(0, str, i18n("Kompare Model Update")); } catch ( const char * str ) { KMessageBox::error(0, str, i18n("Kompare Model Update")); } removeHighlighting(); m_modelList.reset( 0 ); m_kompareInfo.reset( 0 ); delete m_diffSettings; emit patchChanged(); } K_PLUGIN_FACTORY(KDevProblemReporterFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KDevProblemReporterFactory(KAboutData("kdevpatchreview","kdevpatchreview", ki18n("Patch Review"), "0.1", ki18n("Highlights code affected by a patch"), KAboutData::License_GPL))) class PatchReviewToolViewFactory : public KDevelop::IToolViewFactory { public: PatchReviewToolViewFactory(PatchReviewPlugin *plugin): m_plugin(plugin) {} virtual QWidget* create(QWidget *parent = 0) { return m_plugin->createToolView(parent); } virtual Qt::DockWidgetArea defaultPosition() { return Qt::BottomDockWidgetArea; } virtual QString id() const { return "org.kdevelop.PatchReview"; } private: PatchReviewPlugin *m_plugin; }; PatchReviewPlugin::~PatchReviewPlugin() { removeHighlighting(); delete m_patch; } void PatchReviewPlugin::registerPatch(IPatchSource::Ptr patch) { if(!m_knownPatches.contains(patch)) { m_knownPatches << patch; connect(patch, SIGNAL(destroyed(QObject*)), SLOT(clearPatch(QObject*))); } } void PatchReviewPlugin::clearPatch(QObject* _patch) { kDebug() << "clearing patch" << _patch << "current:" << (QObject*)m_patch; IPatchSource::Ptr patch((IPatchSource*)_patch); m_knownPatches.removeAll(patch); m_knownPatches.removeAll(0); if(patch == m_patch) { kDebug() << "is current patch"; if(!m_knownPatches.empty()) setPatch(m_knownPatches.first()); else setPatch(IPatchSource::Ptr(new LocalPatchSource)); } } #if 0 #if HAVE_KOMPARE void showDiff(const KDevelop::VcsDiff& d) { ICore::self()->uiController()->switchToArea("review", KDevelop::IUiController::ThisWindow); foreach(const VcsLocation& l, d.leftTexts().keys()) { KUrl to; if(d.rightTexts().contains(l)) { KTemporaryFile temp2; temp2.setSuffix("2.patch"); //FIXME: don't leak temp2.setAutoRemove(false); temp2.open(); QTextStream t2(&temp2); t2 << d.rightTexts()[l]; temp2.close(); to=temp2.fileName(); } else to=l.localUrl(); KUrl fakeUrl(to); fakeUrl.setScheme("kdevpatch"); IDocumentFactory* docf=ICore::self()->documentController()->factory("text/x-patch"); IDocument* doc=docf->create(fakeUrl, ICore::self()); IPatchDocument* pdoc=dynamic_cast(doc); Q_ASSERT(pdoc); ICore::self()->documentController()->openDocument(doc); pdoc->setDiff(d.leftTexts()[l], to); } } #endif #endif void PatchReviewPlugin::cancelReview() { if(m_patch) { removeHighlighting(); m_modelList.reset( 0 ); m_patch->cancelReview(); emit patchChanged(); - delete m_patch; + if(!dynamic_cast(m_patch.data())) { + delete m_patch; + // make sure "show" button still openes the file dialog to open a custom patch file + setPatch(new LocalPatchSource); + } Sublime::MainWindow* w = dynamic_cast(ICore::self()->uiController()->activeMainWindow()); if(w->area()->objectName() == "review") { setUniqueWorkingSet(); // Make the working-set unique, so that we don't affect other areas w->area()->clearViews(); ICore::self()->uiController()->switchToArea("code", KDevelop::IUiController::ThisWindow); } } } void PatchReviewPlugin::finishReview(QList< KUrl > selection) { if(m_patch) { if(!m_patch->finishReview(selection)) return; removeHighlighting(); m_modelList.reset( 0 ); emit patchChanged(); if(!dynamic_cast(m_patch.data())) { delete m_patch; // make sure "show" button still openes the file dialog to open a custom patch file setPatch(new LocalPatchSource); } Sublime::MainWindow* w = dynamic_cast(ICore::self()->uiController()->activeMainWindow()); if(w->area()->objectName() == "review") { w->area()->clearViews(); ICore::self()->uiController()->switchToArea("code", KDevelop::IUiController::ThisWindow); } } } void PatchReviewPlugin::startReview(IPatchSource* patch, IPatchReview::ReviewMode mode) { Q_UNUSED(mode); setPatch(patch); QMetaObject::invokeMethod(this, "updateReview", Qt::QueuedConnection); } void PatchReviewPlugin::switchAreaAndMakeWorkingSetUique() { Sublime::MainWindow* w = dynamic_cast(ICore::self()->uiController()->activeMainWindow()); if (w->area()->objectName() != "review") ICore::self()->uiController()->switchToArea("review", KDevelop::IUiController::ThisWindow); setUniqueWorkingSet(); } bool PatchReviewPlugin::isWorkingSetUnique() const { Sublime::MainWindow* w = dynamic_cast(ICore::self()->uiController()->activeMainWindow()); foreach(Sublime::Area* area, w->areas()) if(area != w->area() && area->workingSet() == w->area()->workingSet()) return false; return true; } void PatchReviewPlugin::setUniqueWorkingSet() { Sublime::MainWindow* w = dynamic_cast(ICore::self()->uiController()->activeMainWindow()); if(!w->area()->workingSet().startsWith("review")) w->area()->setWorkingSet("review"); while(!isWorkingSetUnique()) w->area()->setWorkingSet(QString("review_%1").arg(rand() % 10000)); } void PatchReviewPlugin::updateReview() { if(!m_patch) return; m_updateKompareTimer->stop(); updateKompareModel(); switchAreaAndMakeWorkingSetUique(); if(!m_modelList.get()) return; // list of opened documents to prevent flicker QMap documents; foreach(IDocument* doc, ICore::self()->documentController()->openDocuments()) { documents[doc->url()] = doc; } IDocument* futureActiveDoc = 0; //Open the diff itself #ifdef HAVE_KOMPARE KUrl fakeUrl(m_patch->file()); fakeUrl.setScheme("kdevpatch"); IDocumentFactory* docf=ICore::self()->documentController()->factory("text/x-patch"); IDocument* doc=docf->create(fakeUrl, ICore::self()); IPatchDocument* pdoc=dynamic_cast(doc); Q_ASSERT(pdoc); futureActiveDoc = ICore::self()->documentController()->openDocument(doc); //TODO: close kompare doc if available #else if (!documents.contains(m_patch->file())) { futureActiveDoc = ICore::self()->documentController()->openDocument(m_patch->file()); } else { futureActiveDoc = documents.take(m_patch->file()); } #endif if (!futureActiveDoc || !futureActiveDoc->textDocument()) { // might happen if e.g. openDocument dialog was cancelled by user // or under the theoretic possibility of a non-text document getting opened return; } futureActiveDoc->textDocument()->setReadWrite(false); futureActiveDoc->setPrettyName(i18n("Overview")); IDocument* doc=ICore::self()->documentController()->documentForUrl(m_patch->file()); KTextEditor::ModificationInterface* modif=dynamic_cast(doc->textDocument()); modif->setModifiedOnDiskWarning(false); if(m_modelList->modelCount() < maximumFilesToOpenDirectly) { //Open all relates files for(int a = 0; a < m_modelList->modelCount(); ++a) { KUrl absoluteUrl = m_patch->baseDir(); KUrl url(m_modelList->modelAt(a)->destination()); if(url.isRelative()) absoluteUrl.addPath(url.path()); else absoluteUrl = url; if(QFileInfo(absoluteUrl.path()).exists() && absoluteUrl.path() != "/dev/null") { ICore::self()->documentController()->openDocument(absoluteUrl); - if (documents.contains(absoluteUrl)) { - documents.remove(absoluteUrl); - } + documents.remove(absoluteUrl); + seekHunk(true, absoluteUrl); //Jump to the first changed position }else{ // Maybe the file was deleted kDebug() << "could not open" << absoluteUrl << "because it doesn't exist"; } } } Sublime::MainWindow* w = dynamic_cast(ICore::self()->uiController()->activeMainWindow()); // Close views for documents that were loaded from the working set, but are not in the patch QList documentsList = documents.values(); foreach(Sublime::View* view, w->area()->views()) { IDocument* doc = dynamic_cast(view->document()); if(doc && documentsList.contains(doc)) { w->area()->closeView(view); } } Q_ASSERT(futureActiveDoc); ICore::self()->documentController()->activateDocument(futureActiveDoc); bool b = ICore::self()->uiController()->findToolView(i18n("Patch Review"), m_factory); Q_ASSERT(b); } void PatchReviewPlugin::setPatch(IPatchSource* patch) { if (patch == m_patch) { return; } if(m_patch) { disconnect(m_patch, SIGNAL(patchChanged()), this, SLOT(notifyPatchChanged())); if (qobject_cast(m_patch)) { // make sure we don't leak this // TODO: what about other patch sources? delete m_patch; } } m_patch = patch; if(m_patch) { kDebug() << "setting new patch" << patch->name() << "with file" << patch->file(); registerPatch(patch); connect(m_patch, SIGNAL(patchChanged()), this, SLOT(notifyPatchChanged())); } notifyPatchChanged(); } PatchReviewPlugin::PatchReviewPlugin(QObject *parent, const QVariantList &) : KDevelop::IPlugin(KDevProblemReporterFactory::componentData(), parent), m_patch(0), m_factory(new PatchReviewToolViewFactory(this)) { KDEV_USE_EXTENSION_INTERFACE( KDevelop::IPatchReview ) qRegisterMetaType( "const Diff2::DiffModel*" ); core()->uiController()->addToolView(i18n("Patch Review"), m_factory); setXMLFile("kdevpatchreview.rc"); connect(ICore::self()->documentController(), SIGNAL(documentClosed(KDevelop::IDocument*)), this, SLOT(documentClosed(KDevelop::IDocument*))); connect(ICore::self()->documentController(), SIGNAL(textDocumentCreated(KDevelop::IDocument*)), this, SLOT(textDocumentCreated(KDevelop::IDocument*))); connect(ICore::self()->documentController(), SIGNAL(documentSaved(KDevelop::IDocument*)), this, SLOT(forceUpdate())); m_updateKompareTimer = new QTimer( this ); m_updateKompareTimer->setSingleShot( true ); connect( m_updateKompareTimer, SIGNAL( timeout() ), this, SLOT( updateKompareModel() ) ); setPatch(IPatchSource::Ptr(new LocalPatchSource)); } void PatchReviewPlugin::documentClosed(IDocument* doc) { removeHighlighting(doc->url()); } void PatchReviewPlugin::textDocumentCreated(IDocument* doc) { addHighlighting( doc->url(), doc ); } void PatchReviewPlugin::unload() { core()->uiController()->removeToolView(m_factory); KDevelop::IPlugin::unload(); } QWidget* PatchReviewPlugin::createToolView(QWidget* parent) { return new PatchReviewToolView(parent, this); } void PatchReviewPlugin::exporterSelected(QAction* action) { IPlugin* exporter = qobject_cast(action->data().value()); if(exporter) { qDebug() << "exporting patch" << exporter << action->text(); exporter->extension()->exportPatch(patch()); } } #if 0 void PatchReviewPlugin::determineState() { LocalPatchSourcePointer lpatch = m_patch; if ( !lpatch ) { kDebug() <<"determineState(..) could not lock patch"; } try { if ( lpatch->filename.isEmpty() ) throw "state can only be determined for file-patches"; KUrl fileUrl = lpatch->filename; { K3Process proc; ///Try to apply, if it works, the patch is not applied QString cmd = "patch --dry-run -s -f -i " + fileUrl.toLocalFile(); proc << splitArgs( cmd ); kDebug() << "calling " << cmd; if ( !proc.start( K3Process::Block ) ) throw "could not start process"; if ( !proc.normalExit() ) throw "process did not exit normally"; kDebug() << "exit-status:" << proc.exitStatus(); if ( proc.exitStatus() == 0 ) { // lpatch->state = LocalPatchSource::NotApplied; return; } } { ///Try to revert, of it works, the patch is applied K3Process proc; QString cmd = "patch --dry-run -s -f -i --reverse " + fileUrl.toLocalFile(); proc << splitArgs( cmd ); kDebug() << "calling " << cmd; if ( !proc.start( K3Process::Block ) ) throw "could not start process"; if ( !proc.normalExit() ) throw "process did not exit normally"; kDebug() << "exit-status:" << proc.exitStatus(); if ( proc.exitStatus() == 0 ) { // lpatch->state = LocalPatchSource::Applied; return; } } } catch ( const QString& str ) { kWarning() <<"Error:" << str; } catch ( const char* str ) { kWarning() << "Error:" << str; } // lpatch->state = LocalPatchSource::Unknown; } #endif #include "patchreview.moc" // kate: space-indent on; indent-width 2; tab-width 2; replace-tabs on diff --git a/plugins/patchreview/patchreview.h b/plugins/patchreview/patchreview.h index 8e86063697..74b279eb4a 100644 --- a/plugins/patchreview/patchreview.h +++ b/plugins/patchreview/patchreview.h @@ -1,246 +1,245 @@ /*************************************************************************** Copyright 2006 David Nolden ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef PATCHREVIEW_H #define PATCHREVIEW_H #include #include #include #include #include #include "localpatchsource.h" #include "ui_patchreview.h" #include #include #include "libdiff2/diffmodel.h" class PatchReviewToolViewFactory; class PatchReviewPlugin; namespace KParts { class Part; } class QDialog; namespace Diff2 { class KompareModelList; class DiffModel; class DiffModel; } namespace KTextEditor { class Document; class Range; class MovingRange; class Mark; } namespace Kompare { class Info; } namespace KDevelop { class IDocument; class VcsFileChangesModel; } ///Delete itself when the document(or textDocument), or Diff-Model is deleted. class PatchHighlighter : public QObject { Q_OBJECT public: PatchHighlighter( Diff2::DiffModel* model, KDevelop::IDocument* doc, PatchReviewPlugin* plugin ) throw( QString ); ~PatchHighlighter(); KDevelop::IDocument* doc(); QList ranges() const { return m_differencesForRanges.keys(); } private slots: void documentDestroyed(); void aboutToDeleteMovingInterfaceContent(KTextEditor::Document*); private: void addLineMarker(KTextEditor::MovingRange* arg1, Diff2::Difference* arg2); void removeLineMarker(KTextEditor::MovingRange* range); QStringList splitAndAddNewlines(const QString& text) const; void performContentChange(KTextEditor::Document* doc, const QStringList& oldLines, const QStringList& newLines, int editLineNumber); KTextEditor::MovingRange* rangeForMark(KTextEditor::Mark mark); void clear(); QSet m_ranges; QMap m_differencesForRanges; KDevelop::IDocument* m_doc; PatchReviewPlugin* m_plugin; Diff2::DiffModel* m_model; bool m_applying; public slots: void markToolTipRequested(KTextEditor::Document*,KTextEditor::Mark,QPoint,bool&); void showToolTipForMark(QPoint arg1, KTextEditor::MovingRange* arg2, QPair< int, int > highlightMark = qMakePair(-1, -1)); bool isRemoval(Diff2::Difference*); bool isInsertion(Diff2::Difference*); void markClicked(KTextEditor::Document*,KTextEditor::Mark,bool&); void textInserted(KTextEditor::Document*,KTextEditor::Range); void textRemoved(KTextEditor::Document*, const KTextEditor::Range&, const QString& oldText); }; class DiffSettings; class PatchReviewPlugin; class PatchReviewToolView : public QWidget { Q_OBJECT public: PatchReviewToolView( QWidget* parent, PatchReviewPlugin* plugin ); ~PatchReviewToolView(); signals: void dialogClosed( PatchReviewToolView* ); void stateChanged( PatchReviewToolView* ); private slots: void fileDoubleClicked( const QModelIndex& i ); void nextHunk(); void prevHunk(); void patchChanged(); void updatePatchFromEdit(); void slotEditCommandChanged(); void slotEditFileNameChanged(); - void slotDepthChanged(int newDepth); void slotAppliedChanged(int newState); void finishReview(); private: void kompareModelChanged(); void showEditDialog(); ///Fills the editor views from m_editingPatch void fillEditFromPatch(); /// Retrieve the patch from plugin and perform all necessary casts LocalPatchSource* GetLocalPatchSource(); Ui_EditPatch m_editPatch; QTime m_lastDataTime; QString m_lastTerminalData; QPointer m_konsolePart; bool m_reversed; PatchReviewPlugin* m_plugin; QPointer m_customWidget; class PatchFilesModel* m_fileModel; public slots: void documentActivated(KDevelop::IDocument*); void patchSelectionChanged(int); }; class PatchReviewPlugin : public KDevelop::IPlugin, public KDevelop::IPatchReview { Q_OBJECT Q_INTERFACES( KDevelop::IPatchReview ) public: PatchReviewPlugin(QObject *parent, const QVariantList & = QVariantList() ); ~PatchReviewPlugin(); virtual void unload(); QWidget* createToolView(QWidget* parent); KDevelop::IPatchSource::Ptr patch() const { return m_patch; } QList knownPatches() const { return m_knownPatches; } Diff2::KompareModelList* modelList() const { return m_modelList.get(); } void seekHunk( bool forwards, const KUrl& file = KUrl() ); KUrl diffFile(); void setPatch(KDevelop::IPatchSource* patch); void registerPatch(KDevelop::IPatchSource::Ptr patch); virtual void startReview(KDevelop::IPatchSource* patch, ReviewMode mode); void finishReview(QList selection); KUrl urlForFileModel(const Diff2::DiffModel* model); Q_SIGNALS: void patchChanged(); public Q_SLOTS: //Does parts of the review-starting that are problematic to do directly in startReview, as they may open dialogs etc. void updateReview(); void cancelReview(); void clearPatch(QObject* patch); void notifyPatchChanged(); void highlightPatch(); void updateKompareModel(); void showPatch(); void forceUpdate(); private Q_SLOTS: void documentClosed(KDevelop::IDocument*); void textDocumentCreated(KDevelop::IDocument*); void exporterSelected(QAction* action); private: // Switches to the review area, // makes sure that the working set active in the current area starts with "review" and // is not active in any other area. Creates new working sets if required. void switchAreaAndMakeWorkingSetUique(); // Returns whether the current working set is active only in this area bool isWorkingSetUnique() const; // Makes sure that this working set is active only in this area, and that its name starts with "review". void setUniqueWorkingSet(); QList m_knownPatches; void addHighlighting( const KUrl& file, KDevelop::IDocument* document = 0 ); void removeHighlighting( const KUrl& file = KUrl() ); KDevelop::IPatchSource::Ptr m_patch; QTimer* m_updateKompareTimer; PatchReviewToolViewFactory* m_factory; #if 0 void determineState(); #endif QPointer m_diffSettings; std::auto_ptr m_kompareInfo; std::auto_ptr m_modelList; typedef QMap > HighlightMap; HighlightMap m_highlighters; }; #endif // kate: space-indent on; indent-width 2; tab-width 2; replace-tabs on diff --git a/plugins/patchreview/patchreview.ui b/plugins/patchreview/patchreview.ui index 9d52dac374..06e80bb4a9 100644 --- a/plugins/patchreview/patchreview.ui +++ b/plugins/patchreview/patchreview.ui @@ -1,310 +1,228 @@ EditPatch 0 0 908 452 Edit Patch - - - 0 - - - 0 - - + + + + Previous difference + ... + + Next difference + ... Qt::Horizontal 448 20 Show Qt::Horizontal QSizePolicy::Fixed 40 20 Export Diff... QToolButton::InstantPopup Cancel Review Finish Review - - - - - - - - 0 - 0 - - - - Patch - - - - 0 - - - - - - - - Custom Patch - - - - - - - - - 0 - 0 - - - - Update - - - - - - - - - - - Base: - - - - - - - + + + Patch + + + + + + + + + Custom Patch + - - - - 0 + + + + + + + 0 + 0 + + + + Update + + + + + + + + + Base: + + + + + + + Patch base... + + + + + + + 1 + + + + From file + + + + + + Patch location... - - - File - - - - - - <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">File:</span></p></body></html> - - - - - - - - - - - command-output - - - - 9 - - - 6 - - - - - 6 - - - 0 - - - - - <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-size:9pt; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Command:</span></p></body></html> - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Depth: - - - - - - - Patch depth - see "patch -p" - - - 3 - - - - - - - - - Patch is already applied on local version - - - Applied - - - false - + + + From command + + + + + + Command's output... + + + + - - - - - Qt::Vertical - - - - 88 - 37 - - - - - - - - - - Changes - - - - 0 + + + + + + Patch is already applied on local version - - - - QAbstractItemView::NoEditTriggers - - - - - - - + + Already applied + + + false + + + + + + + + + + Changes + + + + + + QAbstractItemView::NoEditTriggers + + + + + + + + KUrlRequester QFrame
kurlrequester.h
diff --git a/plugins/projectmanagerview/kdevdashboardproject.desktop b/plugins/projectmanagerview/kdevdashboardproject.desktop index cc7430bbbb..dbbd0ac6e8 100644 --- a/plugins/projectmanagerview/kdevdashboardproject.desktop +++ b/plugins/projectmanagerview/kdevdashboardproject.desktop @@ -1,20 +1,29 @@ [Desktop Entry] Type=Service Exec=blubb Name=Project Information +Name[el]=Πληροφορίες έργου Name[es]=Información del proyecto +Name[nl]=Projectinformatie +Name[pt]=Informação do Projecto Name[uk]=Параметри проекту Name[x-test]=xxProject Informationxx GenericName=Project Information +GenericName[el]=Πληροφορίες έργου GenericName[es]=Información del proyecto +GenericName[nl]=Projectinformatie +GenericName[pt]=Informação do Projecto GenericName[uk]=Параметри проекту GenericName[x-test]=xxProject Informationxx Comment=Shows information related to the project +Comment[el]=εμφανίζει πληροφορίες σχετικά με το έργο Comment[es]=Muestra información relacionada con el proyecto +Comment[nl]=Toont informatie over het project +Comment[pt]=Mostra informações relacionadas com o projecto Comment[uk]=Показує дані щодо проекту Comment[x-test]=xxShows information related to the projectxx Icon=project-new ServiceTypes=KDevelop/Dashboard X-KDE-PluginInfo-Name=org.kdevelop.project X-KDE-PluginInfo-Author=Aleix Pol X-KDE-PluginInfo-License=LGPL diff --git a/plugins/vcschangesview/kdevvcschangesview.desktop b/plugins/vcschangesview/kdevvcschangesview.desktop index bde3eef5a0..d7449cfcb1 100644 --- a/plugins/vcschangesview/kdevvcschangesview.desktop +++ b/plugins/vcschangesview/kdevvcschangesview.desktop @@ -1,24 +1,33 @@ [Desktop Entry] Type=Service Comment=This plugin provides integration between the projects and their VCS infrastructure +Comment[el]=Το πρόσθετο αυτό παρέχει ολοκλήρωση ανάμεσα στα έργα και της VCS υποδομής τους Comment[es]=Este complemento proporciona integración entre los proyectos y su infraestructura VCS +Comment[nl]=Deze plugin geeft integratie tussen de projecten en hun VCS-infrastructuur +Comment[pt]=Este 'plugin' oferece a integração entre os projectos e a sua infra-estrutura de SCV Comment[uk]=Цей додаток забезпечує інтеграцію між проектами та їхньою структурою системи керування версіями Comment[x-test]=xxThis plugin provides integration between the projects and their VCS infrastructurexx Name=VCS Integration +Name[el]=VCS ολοκλήρωση Name[es]=Integración VCS +Name[nl]=VCS-integratie +Name[pt]=Integração com o SCV Name[uk]=Інтеграція з системами керування версіями Name[x-test]=xxVCS Integrationxx GenericName=VCS Integration +GenericName[el]=VCS ολοκλήρωση GenericName[es]=Integración VCS +GenericName[nl]=VCS-integratie +GenericName[pt]=Integração com o SCV GenericName[uk]=Інтеграція з системами керування версіями GenericName[x-test]=xxVCS Integrationxx ServiceTypes=KDevelop/Plugin X-KDE-Library=kdevvcschangesviewplugin X-KDE-PluginInfo-Name=kdevvcschangesviewplugin X-KDE-PluginInfo-Author=Aleix Pol X-KDE-PluginInfo-Email=aleixpol@kde.org X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-Category=Version Control X-KDevelop-Version=12 X-KDevelop-Category=Global -X-KDevelop-Mode=NoGUI -X-KDevelop-LoadMode=AlwaysOn +X-KDevelop-Mode=GUI diff --git a/project/dashboard/projectfileelement/kdev-dash-projectfileelement.desktop b/project/dashboard/projectfileelement/kdev-dash-projectfileelement.desktop index 451ccfda28..006c4980fe 100644 --- a/project/dashboard/projectfileelement/kdev-dash-projectfileelement.desktop +++ b/project/dashboard/projectfileelement/kdev-dash-projectfileelement.desktop @@ -1,23 +1,29 @@ [Desktop Entry] Name=Project File Element +Name[el]=Αντικείμενο αρχείου του έργου Name[es]=Elemento del archivo del proyecto +Name[nl]=Element van het projectbestand +Name[pt]=Elemento de Ficheiro do Projecto Name[uk]=Елемент файла проекту Name[x-test]=xxProject File Elementxx Icon=kdevelop Comment=Draws a project file into a Dashboard element +Comment[el]=Εμφανίζει ένα αρχείο έργου σε ένα αντικείμενο πίνακα ελέγχου Comment[es]=Muestra un archivo de proyecto en un elemento del tablero de mandos +Comment[nl]=Tekent een projectbestand in een element in Dashboard +Comment[pt]=Desenha um ficheiro do projecto num elemento do Quadro Comment[uk]=Показує файл проекту як елемент панелі приладів Comment[x-test]=xxDraws a project file into a Dashboard elementxx Type=Service ServiceTypes=Plasma/Applet X-KDE-Library=plasma_kdev_projectfileelement X-KDE-PluginInfo-Author=Aleix Pol Gonzalez X-KDE-PluginInfo-Email=aleixpol@kde.org X-KDE-PluginInfo-Name=plasma_kdev_projectfileelement X-KDE-PluginInfo-Version=0.1 X-KDE-PluginInfo-Website=http://kdevelop.org X-KDE-PluginInfo-Category=SDK X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true X-KDE-ParentApp=KDevelop diff --git a/shell/core.cpp b/shell/core.cpp index 756febfba3..50d9351a2f 100644 --- a/shell/core.cpp +++ b/shell/core.cpp @@ -1,485 +1,485 @@ /*************************************************************************** * Copyright 2007 Alexander Dymo * * Copyright 2007 Kris Wong * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "core.h" #include "core_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "shellextension.h" #include "mainwindow.h" #include "sessioncontroller.h" #include "uicontroller.h" #include "plugincontroller.h" #include "projectcontroller.h" #include "partcontroller.h" #include "languagecontroller.h" #include "documentcontroller.h" #include "runcontroller.h" #include "session.h" #include "documentationcontroller.h" #include "sourceformattercontroller.h" #include "progressmanager.h" #include "selectioncontroller.h" #include "debugcontroller.h" #include "kdevplatformversion.h" #include "workingsetcontroller.h" #include #include #include namespace KDevelop { Core *Core::m_self = 0; KAboutData aboutData() { KAboutData aboutData( "kdevplatform", "kdevplatform", ki18n("KDevelop Platform"), KDEVPLATFORM_VERSION_STR, ki18n("Development Platform for IDE-like Applications"), KAboutData::License_LGPL_V2, ki18n( "Copyright 2004-2009, The KDevelop developers" ), KLocalizedString(), "http://www.kdevelop.org" ); aboutData.addAuthor( ki18n("Andreas Pakulat"), ki18n( "Architecture, VCS Support, Project Management Support, QMake Projectmanager" ), "apaku@gmx.de" ); aboutData.addAuthor( ki18n("Alexander Dymo"), ki18n( "Architecture, Sublime UI, Ruby support" ), "adymo@kdevelop.org" ); aboutData.addAuthor( ki18n("David Nolden"), ki18n( "Definition-Use Chain, C++ Support" ), "david.nolden.kdevelop@art-master.de" ); aboutData.addAuthor( ki18n("Aleix Pol Gonzalez"), ki18n( "Co-Maintainer, CMake Support, Run Support, Kross Support" ), "aleixpol@kde.org" ); aboutData.addAuthor( ki18n("Vladimir Prus"), ki18n( "GDB integration" ), "ghost@cs.msu.su" ); aboutData.addAuthor( ki18n("Hamish Rodda"), ki18n( "Text editor integration, definition-use chain" ), "rodda@kde.org" ); aboutData.addCredit( ki18n("Matt Rogers"), KLocalizedString(), "mattr@kde.org"); aboutData.addCredit( ki18n("Cédric Pasteur"), ki18n("astyle and indent support"), "cedric.pasteur@free.fr" ); aboutData.addCredit( ki18n("Evgeniy Ivanov"), ki18n("Distributed VCS, Git, Mercurial"), "powerfox@kde.ru" ); //Veritas is outside in playground currently. //aboutData.addCredit( ki18n("Manuel Breugelmanns"), ki18n( "Veritas, QTest integraton"), "mbr.nxi@gmail.com" ); aboutData.addCredit( ki18n("Robert Gruber") , ki18n( "SnippetPart, debugger and usability patches" ), "rgruber@users.sourceforge.net" ); aboutData.addCredit( ki18n("Dukju Ahn"), ki18n( "Subversion plugin, Custom Make Manager, Overall improvements" ), "dukjuahn@gmail.com" ); aboutData.addAuthor( ki18n("Niko Sams"), ki18n( "GDB integration, Webdevelopment Plugins" ), "niko.sams@gmail.com" ); aboutData.addAuthor( ki18n("Milian Wolff"), ki18n( "Co-Maintainer, Generic manager, Webdevelopment Plugins, Snippets, Performance" ), "mail@milianw.de" ); return aboutData; } CorePrivate::CorePrivate(Core *core): m_componentData( aboutData() ), m_core(core), m_cleanedUp(false), m_shuttingDown(false) { } bool CorePrivate::initialize(Core::Setup mode, QString session ) { m_mode=mode; if( !sessionController ) { sessionController = new SessionController(m_core); } if( !workingSetController && !(mode & Core::NoUi) ) { workingSetController = new WorkingSetController(m_core); } kDebug() << "Creating ui controller"; if( !uiController ) { uiController = new UiController(m_core); } kDebug() << "Creating plugin controller"; if( !pluginController ) { pluginController = new PluginController(m_core); } if( !partController && !(mode & Core::NoUi)) { partController = new PartController(m_core, uiController->defaultMainWindow()); { // check features of kate and report to user if it does not fit KTextEditor::Document* doc = partController->createTextPart(); if ( !qobject_cast< KTextEditor::MovingInterface* >(doc) ) { KMessageBox::error(QApplication::activeWindow(), i18n("The installed Kate version does not support the MovingInterface which is crucial for " "KDevelop starting from version 4.2.\n\n" "To use KDevelop with KDE SC prior to 4.6, where the SmartInterface is used instead " "of the MovingInterface, you need KDevelop 4.1 or lower.")); delete doc; return false; } delete doc; } } if( !projectController ) { projectController = new ProjectController(m_core); } if( !documentController ) { documentController = new DocumentController(m_core); } if( !languageController ) { // Must be initialized after documentController, because the background parser depends // on the document controller. languageController = new LanguageController(m_core); } if( !runController ) { runController = new RunController(m_core); } if( !sourceFormatterController ) { sourceFormatterController = new SourceFormatterController(m_core); } if ( !progressController) { progressController = new ProgressManager(); } if( !selectionController ) { selectionController = new SelectionController(m_core); } if( !documentationController && !(mode & Core::NoUi) ) { documentationController = new DocumentationController(m_core); } if( !debugController ) { debugController = new DebugController(m_core); } kDebug() << "initializing ui controller"; if( !session.isEmpty() && !SessionController::tryLockSession(session) && !(mode & Core::NoUi) ) { QString errmsg = i18n("The session %1 is already active in another running instance. Choose another session or close the running instance.", session ); session = SessionController::showSessionChooserDialog(errmsg); if(session.isEmpty()) return false; } sessionController->initialize( session ); if(!sessionController->lockSession()) { QString errmsg = i18n("Failed to lock the session %1, probably it is already active in another running instance", session ); if( mode & Core::NoUi ) { QTextStream qerr(stderr); qerr << endl << errmsg << endl; } else { KMessageBox::error(0, errmsg); } return false; } // TODO: Is this early enough, or should we put the loading of the session into // the controller construct DUChain::initialize(); if(!(mode & Core::NoUi)) uiController->initialize(); languageController->initialize(); projectController->initialize(); documentController->initialize(); /* This is somewhat messy. We want to load the areas before loading the plugins, so that when each plugin is loaded we know if an area wants some of the tool view from that plugin. OTOH, loading of areas creates documents, and some documents might require that a plugin is already loaded. Probably, the best approach would be to plugins to just add tool views to a list of available tool view, and then grab those tool views when loading an area. */ kDebug() << "loading session plugins"; pluginController->initialize(); if(!(mode & Core::NoUi)) { workingSetController->initialize(); /* Need to do this after everything else is loaded. It's too hard to restore position of views, and toolbars, and whatever that are not created yet. */ uiController->loadAllAreas(KGlobal::config()); uiController->defaultMainWindow()->show(); } runController->initialize(); sourceFormatterController->initialize(); selectionController->initialize(); - documentationController->initialize(); + if (documentationController) documentationController->initialize(); debugController->initialize(); return true; } CorePrivate::~CorePrivate() { delete selectionController; delete projectController; delete languageController; delete pluginController; delete uiController; delete partController; delete documentController; delete runController; delete sessionController; delete sourceFormatterController; delete documentationController; delete debugController; delete workingSetController; } bool Core::initialize(KSplashScreen* splash, Setup mode, const QString& session ) { if (m_self) return true; m_self = new Core(); bool ret = m_self->d->initialize(mode, session); if( splash ) { QTimer::singleShot( 200, splash, SLOT(deleteLater()) ); } if(ret) emit m_self->initializationCompleted(); return ret; } Core *KDevelop::Core::self() { return m_self; } Core::Core(QObject *parent) : ICore(parent) { d = new CorePrivate(this); } Core::Core(CorePrivate* dd, QObject* parent) : ICore(parent), d(dd) { } Core::~Core() { kDebug() ; //Cleanup already called before mass destruction of GUI delete d; } Core::Setup Core::setupFlags() const { return d->m_mode; } bool Core::shuttingDown() const { return d->m_shuttingDown; } void Core::cleanup() { d->m_shuttingDown = true; if (!d->m_cleanedUp) { d->debugController->cleanup(); d->selectionController->cleanup(); // Save the layout of the ui here, so run it first d->uiController->cleanup(); if (d->workingSetController) d->workingSetController->cleanup(); /* Must be called before projectController->cleanup(). */ // Closes all documents (discards, as already saved if the user wished earlier) d->documentController->cleanup(); d->runController->cleanup(); d->projectController->cleanup(); d->sourceFormatterController->cleanup(); d->pluginController->cleanup(); d->sessionController->cleanup(); //Disable the functionality of the language controller d->languageController->cleanup(); } d->m_cleanedUp = true; } KComponentData Core::componentData() const { return d->m_componentData; } IUiController *Core::uiController() { return d->uiController; } ISession* Core::activeSession() { return sessionController()->activeSession(); } SessionController *Core::sessionController() { return d->sessionController; } UiController *Core::uiControllerInternal() { return d->uiController; } IPluginController *Core::pluginController() { return d->pluginController; } PluginController *Core::pluginControllerInternal() { return d->pluginController; } IProjectController *Core::projectController() { return d->projectController; } ProjectController *Core::projectControllerInternal() { return d->projectController; } IPartController *Core::partController() { return d->partController; } PartController *Core::partControllerInternal() { return d->partController; } ILanguageController *Core::languageController() { return d->languageController; } LanguageController *Core::languageControllerInternal() { return d->languageController; } IDocumentController *Core::documentController() { return d->documentController; } DocumentController *Core::documentControllerInternal() { return d->documentController; } IRunController *Core::runController() { return d->runController; } RunController *Core::runControllerInternal() { return d->runController; } ISourceFormatterController* Core::sourceFormatterController() { return d->sourceFormatterController; } SourceFormatterController* Core::sourceFormatterControllerInternal() { return d->sourceFormatterController; } ProgressManager *Core::progressController() { return d->progressController; } ISelectionController* Core::selectionController() { return d->selectionController; } IDocumentationController* Core::documentationController() { return d->documentationController; } DocumentationController* Core::documentationControllerInternal() { return d->documentationController; } IDebugController* Core::debugController() { return d->debugController; } DebugController* Core::debugControllerInternal() { return d->debugController; } WorkingSetController* Core::workingSetControllerInternal() { return d->workingSetController; } QString Core::version() { return KDEVPLATFORM_VERSION_STR; } } diff --git a/shell/projectcontroller.cpp b/shell/projectcontroller.cpp index b706290647..53b6460c2e 100644 --- a/shell/projectcontroller.cpp +++ b/shell/projectcontroller.cpp @@ -1,1007 +1,1011 @@ /* This file is part of KDevelop Copyright 2006 Adam Treat Copyright 2007 Andreas Pakulat This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectcontroller.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core.h" #include "project.h" #include "mainwindow.h" #include "shellextension.h" #include "plugincontroller.h" #include "uicontroller.h" #include "documentcontroller.h" #include "openprojectdialog.h" #include #include #include #include "sessioncontroller.h" #include "session.h" #include #include #include #include namespace KDevelop { class ProjectControllerPrivate { public: QList m_projects; QMap< IProject*, QList > m_projectPlugins; QPointer m_recentAction; Core* m_core; // IProject* m_currentProject; ProjectModel* model; QItemSelectionModel* selectionModel; QPointer m_openProject; QPointer m_fetchProject; QPointer m_closeProject; QPointer m_openConfig; IProjectDialogProvider* dialog; QList m_currentlyOpening; // project-file urls that are being opened IProject* m_configuringProject; ProjectController* q; ProjectBuildSetModel* buildset; bool m_foundProjectFile; //Temporary flag used while searching the hierarchy for a project file bool m_cleaningUp; //Temporary flag enabled while destroying the project-controller QPointer m_changesModel; ProjectControllerPrivate( ProjectController* p ) : m_core(0), model(0), selectionModel(0), dialog(0), m_configuringProject(0), q(p), m_foundProjectFile(false), m_cleaningUp(false) { } void unloadAllProjectPlugins() { if( m_projects.isEmpty() ) m_core->pluginControllerInternal()->unloadProjectPlugins(); } void projectConfig( QObject * obj ) { if( !obj ) return; Project* proj = qobject_cast(obj); if( !proj ) return; //@FIXME: compute a blacklist, based on a query for all KDevelop //plugins implementing IProjectManager, removing from that the //plugin that manages this project. Set this as blacklist on the //dialog //@FIXME: Currently it is important to set a parentApp on the kcms //that's different from the component name of the application, else //the plugin will show up on all projects settings dialogs. QStringList pluginsForPrj = findPluginsForProject( proj ); kDebug() << "Using pluginlist:" << pluginsForPrj; pluginsForPrj << "kdevplatformproject"; // for project-wide env settings. KSettings::Dialog cfgDlg( pluginsForPrj, m_core->uiController()->activeMainWindow() ); cfgDlg.setKCMArguments( QStringList() << proj->developerTempFile() << proj->projectTempFile() << proj->projectFileUrl().url() << proj->developerFileUrl().url() << proj->name() ); m_configuringProject = proj; cfgDlg.setWindowTitle( i18n("Configure Project %1", proj->name()) ); cfgDlg.exec(); proj->projectConfiguration()->sync(); m_configuringProject = 0; } void saveListOfOpenedProjects() { KSharedConfig::Ptr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); KUrl::List openProjects; foreach( IProject* project, m_projects ) { openProjects.append(project->projectFileUrl()); } group.writeEntry( "Open Projects", openProjects.toStringList() ); group.sync(); } QStringList findPluginsForProject( IProject* project ) { QList plugins = m_core->pluginController()->loadedPlugins(); QStringList pluginnames; for( QList::iterator it = plugins.begin(); it != plugins.end(); ++it ) { IPlugin* plugin = *it; IProjectFileManager* iface = plugin->extension(); const KPluginInfo info = m_core->pluginController()->pluginInfo( plugin ); if (info.property("X-KDevelop-Category").toString() != "Project") continue; if( !iface || plugin == project->managerPlugin() ) pluginnames << info.pluginName(); } return pluginnames; } void notifyProjectConfigurationChanged() { if( m_configuringProject ) { emit q->projectConfigurationChanged( m_configuringProject ); } } void updateActionStates( Context* ctx ) { ProjectItemContext* itemctx = dynamic_cast(ctx); m_openConfig->setEnabled( itemctx && itemctx->items().count() == 1 ); m_closeProject->setEnabled( itemctx && itemctx->items().count() > 0 ); } void openProjectConfig() { ProjectItemContext* ctx = dynamic_cast( Core::self()->selectionController()->currentSelection() ); if( ctx && ctx->items().count() == 1 ) { q->configureProject( ctx->items().at(0)->project() ); } } void closeSelectedProjects() { ProjectItemContext* ctx = dynamic_cast( Core::self()->selectionController()->currentSelection() ); if( ctx && ctx->items().count() > 0 ) { QSet projects; foreach( ProjectBaseItem* item, ctx->items() ) { projects.insert( item->project() ); } foreach( IProject* project, projects ) { q->closeProject( project ); } } } void importProject(const KUrl& url_) { KUrl url(url_); if ( url.isLocalFile() ) { QString path = QFileInfo( url.toLocalFile() ).canonicalFilePath(); if ( !path.isEmpty() ) url.setPath( path ); } if ( !url.isValid() ) { KMessageBox::error(Core::self()->uiControllerInternal()->activeMainWindow(), i18n("Invalid Location: %1", url.prettyUrl())); return; } if ( m_currentlyOpening.contains(url)) { kDebug() << "Already opening " << url << ". Aborting."; KPassivePopup::message( i18n( "Project already being opened"), i18n( "Already opening %1, not opening again", url.prettyUrl() ), m_core->uiController()->activeMainWindow() ); return; } foreach( IProject* project, m_projects ) { if ( url == project->projectFileUrl() ) { if ( dialog->userWantsReopen() ) { // close first, then open again by falling through q->closeProject(project); } else { // abort return; } } } m_core->pluginControllerInternal()->loadProjectPlugins(); Project* project = new Project(); emit q->projectAboutToBeOpened( project ); if ( !project->open( url ) ) { q->abortOpeningProject(project); project->deleteLater(); return; } m_currentlyOpening << url; } }; IProjectDialogProvider::IProjectDialogProvider() {} IProjectDialogProvider::~IProjectDialogProvider() {} ProjectDialogProvider::ProjectDialogProvider(ProjectControllerPrivate* const p) : d(p) {} ProjectDialogProvider::~ProjectDialogProvider() {} bool writeNewProjectFile( KSharedConfig::Ptr cfg, const QString& name, const QString& manager ) { if (!cfg->isConfigWritable(true)) { kDebug() << "can't write to configfile"; return false; } KConfigGroup grp = cfg->group( "Project" ); grp.writeEntry( "Name", name ); grp.writeEntry( "Manager", manager ); cfg->sync(); return true; } bool projectFileExists( const KUrl& u ) { if( u.isLocalFile() ) { return QFileInfo( u.toLocalFile() ).exists(); } else { return KIO::NetAccess::exists( u, KIO::NetAccess::DestinationSide, Core::self()->uiControllerInternal()->activeMainWindow() ); } } bool equalProjectFile( const QString& configPath, OpenProjectDialog* dlg ) { KSharedConfig::Ptr cfg = KSharedConfig::openConfig( configPath, KConfig::SimpleConfig ); KConfigGroup grp = cfg->group( "Project" ); QString defaultName = dlg->projectFileUrl().upUrl().fileName(); return (grp.readEntry( "Name", QString() ) == dlg->projectName() || dlg->projectName() == defaultName) && grp.readEntry( "Manager", QString() ) == dlg->projectManager(); } KUrl ProjectDialogProvider::askProjectConfigLocation(bool fetch, const KUrl& startUrl) { Q_ASSERT(d); OpenProjectDialog dlg( fetch, startUrl, Core::self()->uiController()->activeMainWindow() ); if(dlg.exec() == QDialog::Rejected) return KUrl(); KUrl projectFileUrl = dlg.projectFileUrl(); kDebug() << "selected project:" << projectFileUrl << dlg.projectName() << dlg.projectManager(); if( projectFileExists( projectFileUrl ) ) { // check whether config is equal bool shouldAsk = true; if( projectFileUrl.isLocalFile() ) { shouldAsk = !equalProjectFile( projectFileUrl.toLocalFile(), &dlg ); } else { QString tmpFile; if ( KIO::NetAccess::download( projectFileUrl, tmpFile, qApp->activeWindow() ) ) { shouldAsk = !equalProjectFile( tmpFile, &dlg ); QFile::remove(tmpFile); } else { shouldAsk = false; } } if ( shouldAsk ) { KGuiItem yes = KStandardGuiItem::yes(); yes.setText(i18n("Override")); yes.setToolTip(i18n("Continue to open the project and use the just provided project configuration.")); yes.setIcon(KIcon()); KGuiItem no = KStandardGuiItem::no(); no.setText(i18n("Open Existing File")); no.setToolTip(i18n("Continue to open the project but use the existing project configuration.")); no.setIcon(KIcon()); KGuiItem cancel = KStandardGuiItem::cancel(); cancel.setToolTip(i18n("Cancel and do not open the project.")); int ret = KMessageBox::questionYesNoCancel(qApp->activeWindow(), i18n("There already exists a project configuration file at %1.\n" "Do you want to override it or open the existing file?", projectFileUrl.pathOrUrl()), i18n("Override existing project configuration"), yes, no, cancel ); if ( ret == KMessageBox::No ) { // no: reuse existing project file return projectFileUrl; } else if ( ret == KMessageBox::Cancel ) { return KUrl(); } // else fall through and write new file } } if( projectFileUrl.isLocalFile() ) { bool ok = writeNewProjectFile( KSharedConfig::openConfig( projectFileUrl.toLocalFile(), KConfig::SimpleConfig ), dlg.projectName(), dlg.projectManager() ); if (!ok) return KUrl(); } else { KTemporaryFile tmp; tmp.open(); bool ok = writeNewProjectFile( KSharedConfig::openConfig( tmp.fileName(), KConfig::SimpleConfig ), dlg.projectName(), dlg.projectManager() ); if (!ok) return KUrl(); ok = KIO::NetAccess::upload( tmp.fileName(), projectFileUrl, Core::self()->uiControllerInternal()->defaultMainWindow() ); if (!ok) { KMessageBox::error(d->m_core->uiControllerInternal()->defaultMainWindow(), i18n("Unable to create configuration file %1", projectFileUrl.url())); return KUrl(); } } return projectFileUrl; } bool ProjectDialogProvider::userWantsReopen() { Q_ASSERT(d); return (KMessageBox::questionYesNo( d->m_core->uiControllerInternal()->defaultMainWindow(), i18n( "Reopen the current project?" ) ) == KMessageBox::No) ? false : true; } void ProjectController::setDialogProvider(IProjectDialogProvider* dialog) { Q_ASSERT(d->dialog); delete d->dialog; d->dialog = dialog; } ProjectController::ProjectController( Core* core ) : IProjectController( core ), d( new ProjectControllerPrivate( this ) ) { setObjectName("ProjectController"); d->m_core = core; d->model = new ProjectModel(); d->buildset = new ProjectBuildSetModel( this ); connect( this, SIGNAL( projectOpened( KDevelop::IProject* ) ), d->buildset, SLOT( loadFromProject( KDevelop::IProject* ) ) ); connect( this, SIGNAL( projectClosing( KDevelop::IProject* ) ), d->buildset, SLOT( saveToProject( KDevelop::IProject* ) ) ); connect( this, SIGNAL( projectClosed( KDevelop::IProject* ) ), d->buildset, SLOT( projectClosed( KDevelop::IProject* ) ) ); d->selectionModel = new QItemSelectionModel(d->model); if(!(Core::self()->setupFlags() & Core::NoUi)) setupActions(); loadSettings(false); d->dialog = new ProjectDialogProvider(d); KSettings::Dispatcher::registerComponent( KComponentData("kdevplatformproject"), this, "notifyProjectConfigurationChanged" ); } void ProjectController::setupActions() { KActionCollection * ac = d->m_core->uiControllerInternal()->defaultMainWindow()->actionCollection(); KAction *action; d->m_openProject = action = ac->addAction( "project_open" ); action->setText(i18n( "Open / Import Project..." ) ); action->setToolTip( i18n( "Open / Import Project" ) ); action->setWhatsThis( i18n( "Open / Import project

Open an existing KDevelop 4 project or import an existing Project into KDevelop 4. This entry allows to select a KDevelop4 project file or an existing directory to open it in KDevelop. When opening an existing directory that does not yet have a KDevelop4 project file, the file will be created.

" ) ); action->setIcon(KIcon("project-open")); connect( action, SIGNAL( triggered( bool ) ), SLOT( openProject() ) ); d->m_fetchProject = action = ac->addAction( "project_fetch" ); action->setText(i18n( "Fetch Project..." ) ); action->setToolTip( i18n( "Fetch Project" ) ); action->setWhatsThis( i18n( "Fetch project

Guides the user through the project fetch and then imports it into KDevelop 4.

" ) ); // action->setIcon(KIcon("project-open")); connect( action, SIGNAL( triggered( bool ) ), SLOT( fetchProject() ) ); // action = ac->addAction( "project_close" ); // action->setText( i18n( "C&lose Project" ) ); // connect( action, SIGNAL( triggered( bool ) ), SLOT( closeProject() ) ); // action->setToolTip( i18n( "Close project" ) ); // action->setWhatsThis( i18n( "Close project

Closes the current project." ) ); // action->setEnabled( false ); d->m_closeProject = action = ac->addAction( "project_close" ); connect( action, SIGNAL( triggered( bool ) ), SLOT( closeSelectedProjects() ) ); action->setText( i18n( "Close Project(s)" ) ); action->setIcon( KIcon( "project-development-close" ) ); action->setToolTip( i18n( "Closes all currently selected projects" ) ); action->setEnabled( false ); d->m_openConfig = action = ac->addAction( "project_open_config" ); connect( action, SIGNAL( triggered( bool ) ), SLOT( openProjectConfig() ) ); action->setText( i18n( "Open Configuration..." ) ); action->setIcon( KIcon("configure") ); action->setEnabled( false ); action = ac->addAction( "commit_current_project" ); connect( action, SIGNAL( triggered( bool ) ), SLOT( commitCurrentProject() ) ); action->setText( i18n( "Commit Current Project..." ) ); action->setIcon( KIcon("svn-commit") ); KSharedConfig * config = KGlobal::config().data(); // KConfigGroup group = config->group( "General Options" ); d->m_recentAction = new KRecentFilesAction( this ); connect( d->m_recentAction, SIGNAL(urlSelected(const KUrl&)), SLOT( openProject( const KUrl& ) )); ac->addAction( "project_open_recent", d->m_recentAction ); d->m_recentAction->setText( i18n( "Open Recent" ) ); d->m_recentAction->setToolTip( i18n( "Open recent project" ) ); d->m_recentAction->setWhatsThis( i18n( "Open recent project

Opens recently opened project.

" ) ); d->m_recentAction->loadEntries( KConfigGroup(config, "RecentProjects") ); KAction* openProjectForFileAction = new KAction( this ); ac->addAction("project_open_for_file", openProjectForFileAction); openProjectForFileAction->setText(i18n("Open Project for Current File")); connect( openProjectForFileAction, SIGNAL(triggered(bool)), SLOT(openProjectForUrlSlot(bool))); } ProjectController::~ProjectController() { delete d->model; delete d->dialog; delete d; } void ProjectController::cleanup() { d->m_cleaningUp = true; KSharedConfig::Ptr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); foreach( IProject* project, d->m_projects ) { closeProject( project ); } } void ProjectController::initialize() { KSharedConfig::Ptr config = Core::self()->activeSession()->config(); KConfigGroup group = config->group( "General Options" ); KUrl::List openProjects = group.readEntry( "Open Projects", QStringList() ); QMetaObject::invokeMethod(this, "openProjects", Qt::QueuedConnection, Q_ARG(KUrl::List, openProjects)); connect( Core::self()->selectionController(), SIGNAL(selectionChanged(KDevelop::Context*)), SLOT(updateActionStates(KDevelop::Context*)) ); } void ProjectController::openProjects(const KUrl::List& projects) { foreach (const KUrl& url, projects) openProject(url); } void ProjectController::loadSettings( bool projectIsLoaded ) { Q_UNUSED(projectIsLoaded) } void ProjectController::saveSettings( bool projectIsLoaded ) { Q_UNUSED( projectIsLoaded ); } int ProjectController::projectCount() const { return d->m_projects.count(); } IProject* ProjectController::projectAt( int num ) const { if( !d->m_projects.isEmpty() && num >= 0 && num < d->m_projects.count() ) return d->m_projects.at( num ); return 0; } QList ProjectController::projects() const { return d->m_projects; } void ProjectController::eventuallyOpenProjectFile(KIO::Job* _job, KIO::UDSEntryList entries ) { KIO::SimpleJob* job(dynamic_cast(_job)); Q_ASSERT(job); foreach(const KIO::UDSEntry& entry, entries) { if(d->m_foundProjectFile) break; if(!entry.isDir()) { QString name = entry.stringValue( KIO::UDSEntry::UDS_NAME ); if(name.endsWith(".kdev4")) { //We have found a project-file, open it KUrl u(job->url()); u.addPath(name); openProject(u); d->m_foundProjectFile = true; } } } } void ProjectController::openProjectForUrlSlot(bool) { if(ICore::self()->documentController()->activeDocument()) { KUrl url = ICore::self()->documentController()->activeDocument()->url(); IProject* project = ICore::self()->projectController()->findProjectForUrl(url); if(!project) { openProjectForUrl(url); }else{ KMessageBox::error(Core::self()->uiController()->activeMainWindow(), i18n("Project already open: %1", project->name())); } }else{ KMessageBox::error(Core::self()->uiController()->activeMainWindow(), i18n("No active document")); } } void ProjectController::openProjectForUrl(const KUrl& sourceUrl) { KUrl dirUrl = sourceUrl.upUrl(); KUrl testAt = dirUrl; d->m_foundProjectFile = false; while(!testAt.path().isEmpty()) { KUrl testProjectFile(testAt); KIO::ListJob* job = KIO::listDir(testAt); connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), SLOT(eventuallyOpenProjectFile(KIO::Job*,KIO::UDSEntryList))); KIO::NetAccess::synchronousRun(job, ICore::self()->uiController()->activeMainWindow()); if(d->m_foundProjectFile) { //Fine! We have directly opened the project d->m_foundProjectFile = false; return; } KUrl oldTest = testAt; testAt = testAt.upUrl(); if(oldTest == testAt) break; } KUrl askForOpen = d->dialog->askProjectConfigLocation(false, dirUrl); if(askForOpen.isValid()) openProject(askForOpen); } void ProjectController::openProject( const KUrl &projectFile ) { KUrl url = projectFile; if(!Core::self()->sessionController()->activeSession()->containedProjects().contains(url)) { foreach( const Session* session, Core::self()->sessionController()->sessions()) { if(session->containedProjects().contains(url)) { int res = KMessageBox::questionYesNo(Core::self()->uiControllerInternal()->activeMainWindow(), i18n("The project you are opening is part of the session %1, do you want to open the session instead?", session->description())); if(res == KMessageBox::Yes) { Core::self()->sessionController()->loadSession(session->id().toString()); #if 0 ///@todo Think about this! Problem: The session might already contain files, the debugger might be active, etc. //If this session is empty, close it if(Core::self()->sessionController()->activeSession()->description().isEmpty()) { //Terminate this instance of kdevelop if the user agrees foreach(Sublime::MainWindow* window, Core::self()->uiController()->controller()->mainWindows()) window->close(); } #endif return; } } } } if ( url.isEmpty() ) { url = d->dialog->askProjectConfigLocation(false); } if ( !url.isEmpty() ) { d->importProject(url); } } void ProjectController::fetchProject() { KUrl url = d->dialog->askProjectConfigLocation(true); if ( !url.isEmpty() ) { d->importProject(url); } } void ProjectController::projectImportingFinished( IProject* project ) { if( !project ) { kWarning() << "OOOPS: 0-pointer project"; return; } IPlugin *managerPlugin = project->managerPlugin(); QList pluglist; pluglist.append( managerPlugin ); d->m_projectPlugins.insert( project, pluglist ); d->m_projects.append( project ); if(!Core::self()->sessionController()->activeSession()->containedProjects().contains(project->projectFileUrl())) d->saveListOfOpenedProjects(); // KActionCollection * ac = d->m_core->uiController()->defaultMainWindow()->actionCollection(); // QAction * action; //action = ac->action( "project_close" ); //action->setEnabled( true ); if (Core::self()->setupFlags() != Core::NoUi) { d->m_recentAction->addUrl( project->projectFileUrl() ); KSharedConfig * config = KGlobal::config().data(); KConfigGroup recentGroup = config->group("RecentProjects"); d->m_recentAction->saveEntries( recentGroup ); config->sync(); } Q_ASSERT(d->m_currentlyOpening.contains(project->projectFileUrl())); d->m_currentlyOpening.removeAll(project->projectFileUrl()); emit projectOpened( project ); if (parseAllProjectSources()) { KJob* parseProjectJob = new KDevelop::ParseProjectJob(project); ICore::self()->runController()->registerJob(parseProjectJob); } KUrl::List parseList; // Add all currently open files that belong to the project to the background-parser, // since more information may be available for parsing them now(Like include-paths). foreach(IDocument* document, Core::self()->documentController()->openDocuments()) { if(!project->filesForUrl(document->url()).isEmpty()) { parseList.append(document->url()); } } Core::self()->languageController()->backgroundParser()->addDocumentList( parseList, KDevelop::TopDUContext::AllDeclarationsContextsAndUses, 10 ); } // helper method for closeProject() void ProjectController::unloadUnusedProjectPlugins(IProject* proj) { QList pluginsForProj = d->m_projectPlugins.value( proj ); d->m_projectPlugins.remove( proj ); QList otherProjectPlugins; Q_FOREACH( const QList& _list, d->m_projectPlugins ) { otherProjectPlugins << _list; } QSet pluginsForProjSet = QSet::fromList( pluginsForProj ); QSet otherPrjPluginsSet = QSet::fromList( otherProjectPlugins ); // loaded - target = tobe unloaded. QSet tobeRemoved = pluginsForProjSet.subtract( otherPrjPluginsSet ); Q_FOREACH( IPlugin* _plugin, tobeRemoved ) { KPluginInfo _plugInfo = Core::self()->pluginController()->pluginInfo( _plugin ); if( _plugInfo.isValid() ) { QString _plugName = _plugInfo.pluginName(); kDebug() << "about to unloading :" << _plugName; Core::self()->pluginController()->unloadPlugin( _plugName ); } } } // helper method for closeProject() void ProjectController::closeAllOpenedFiles(IProject* proj) { Q_FOREACH( ProjectFileItem *fileItem, proj->files() ) { Core::self()->documentControllerInternal()->closeDocument( fileItem->url() ); } } // helper method for closeProject() void ProjectController::initializePluginCleanup(IProject* proj) { // Unloading (and thus deleting) these plugins is not a good idea just yet // as we're being called by the view part and it gets deleted when we unload the plugin(s) // TODO: find a better place to unload connect(proj, SIGNAL(destroyed(QObject*)), this, SLOT(unloadAllProjectPlugins())); } void ProjectController::closeProject(IProject* proj_) { if (!proj_) { return; } // loading might have failed d->m_currentlyOpening.removeAll(proj_->projectFileUrl()); Project* proj = dynamic_cast( proj_ ); if( !proj ) { kWarning() << "Unknown Project subclass found!"; return; } d->m_projects.removeAll(proj); emit projectClosing(proj); //Core::self()->saveSettings(); // The project file is being closed. // Now we can save settings for all of the Core // objects including this one!! unloadUnusedProjectPlugins(proj); closeAllOpenedFiles(proj); proj->close(); proj->deleteLater(); //be safe when deleting if (d->m_projects.isEmpty()) { initializePluginCleanup(proj); } if(!d->m_cleaningUp) d->saveListOfOpenedProjects(); emit projectClosed(proj); return; } void ProjectController::abortOpeningProject(IProject* proj) { d->m_currentlyOpening.removeAll(proj->projectFileUrl()); emit projectOpeningAborted(proj); } ProjectModel* ProjectController::projectModel() { return d->model; } IProject* ProjectController::findProjectForUrl( const KUrl& url ) const { Q_FOREACH( IProject* proj, d->m_projects ) { if( proj->inProject( url ) ) return proj; } return 0; } IProject* ProjectController::findProjectByName( const QString& name ) { Q_FOREACH( IProject* proj, d->m_projects ) { if( proj->name() == name ) { return proj; } } return 0; } void ProjectController::configureProject( IProject* project ) { d->projectConfig( project ); } void ProjectController::addProject(IProject* project) { d->m_projects.append( project ); } QItemSelectionModel* ProjectController::projectSelectionModel() { return d->selectionModel; } bool ProjectController::isProjectNameUsed( const QString& name ) const { foreach( IProject* p, projects() ) { if( p->name() == name ) { return true; } } return false; } KUrl ProjectController::projectsBaseDirectory() const { KConfigGroup group = Core::self()->activeSession()->config()->group( "Project Manager" ); return group.readEntry( "Projects Base Directory", KUrl( QDir::homePath()+"/projects" ) ); } QString ProjectController::prettyFilePath(KUrl url, FormattingOptions format) const { IProject* project = Core::self()->projectController()->findProjectForUrl(url); if(!project) { // Find a project with the correct base directory at least foreach(IProject* candidateProject, Core::self()->projectController()->projects()) { if(candidateProject->folder().isParentOf(url)) { project = candidateProject; break; } } } QString prefixText = url.upUrl().pathOrUrl(KUrl::AddTrailingSlash); if (project) { if (format == FormatHtml) { prefixText = "" + project->name() + "/"; } else { prefixText = project->name() + '/'; } QString relativePath = project->relativeUrl(url.upUrl()).path(KUrl::AddTrailingSlash); if(relativePath.startsWith("./")) relativePath = relativePath.mid(2); prefixText += relativePath; } return prefixText; } QString ProjectController::prettyFileName(KUrl url, FormattingOptions format) const { IProject* project = Core::self()->projectController()->findProjectForUrl(url); if(project && project->folder().equals(url, KUrl::CompareWithoutTrailingSlash)) { if (format == FormatHtml) { return "" + project->name() + ""; } else { return project->name(); } } QString prefixText = prettyFilePath( url, format ); if (format == FormatHtml) { return prefixText + "" + url.fileName() + ""; } else { return prefixText + url.fileName(); } } ContextMenuExtension ProjectController::contextMenuExtension ( Context* ctx ) { ContextMenuExtension ext; if ( ctx->type() != Context::ProjectItemContext || !static_cast(ctx)->items().isEmpty() ) { return ext; } ext.addAction(ContextMenuExtension::ProjectGroup, d->m_openProject); ext.addAction(ContextMenuExtension::ProjectGroup, d->m_fetchProject); ext.addAction(ContextMenuExtension::ProjectGroup, d->m_recentAction); return ext; } ProjectBuildSetModel* ProjectController::buildSetModel() { return d->buildset; } ProjectChangesModel* ProjectController::changesModel() { if(!d->m_changesModel) d->m_changesModel=new ProjectChangesModel(this); return d->m_changesModel; } void ProjectController::commitCurrentProject() { - KUrl url=ICore::self()->documentController()->activeDocument()->url(); + IDocument* doc=ICore::self()->documentController()->activeDocument(); + if(!doc) + return; + + KUrl url=doc->url(); IProject* project = ICore::self()->projectController()->findProjectForUrl(url); if(project && project->versionControlPlugin()) { KUrl baseUrl=project->projectItem()->url(); IPlugin* plugin = project->versionControlPlugin(); IBasicVersionControl* vcs=plugin->extension(); if(vcs) { ICore::self()->documentController()->saveAllDocuments(KDevelop::IDocument::Silent); VCSCommitDiffPatchSource* patchSource = new VCSCommitDiffPatchSource(new VCSStandardDiffUpdater(vcs, baseUrl), baseUrl, vcs); bool ret = showVcsDiff(patchSource); if(!ret) { VcsCommitDialog *commitDialog = new VcsCommitDialog(patchSource); commitDialog->setCommitCandidates(patchSource->infos()); commitDialog->exec(); } } } } } #include "projectcontroller.moc" diff --git a/sublime/mainwindow.cpp b/sublime/mainwindow.cpp index 578446737c..4cc4250698 100644 --- a/sublime/mainwindow.cpp +++ b/sublime/mainwindow.cpp @@ -1,456 +1,456 @@ /*************************************************************************** * Copyright 2006-2007 Alexander Dymo * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "mainwindow.h" #include "mainwindow_p.h" #include #include #include #include #include #include #include #include #include #include #include #include "area.h" #include "view.h" #include "controller.h" #include "container.h" #include "ideal.h" namespace Sublime { MainWindow::MainWindow(Controller *controller, Qt::WindowFlags flags) : KParts::MainWindow(0, flags), d(new MainWindowPrivate(this, controller)) { connect(this, SIGNAL(destroyed()), controller, SLOT(areaReleased())); loadGeometry(KGlobal::config()->group("Main Window")); d->areaSwitcher = new AreaTabWidget(menuBar()); menuBar()->setCornerWidget(d->areaSwitcher, Qt::TopRightCorner); // don't allow AllowTabbedDocks - that doesn't make sense for "ideal" UI setDockOptions(QMainWindow::AnimatedDocks); } QWidget* MainWindow::customButtonForAreaSwitcher ( Area* /*area*/ ) { return 0; } bool MainWindow::containsView(View* view) const { foreach(Area* area, areas()) if(area->views().contains(view)) return true; return false; } QList< Area* > MainWindow::areas() const { QList< Area* > areas = controller()->areas(const_cast(this)); if(areas.isEmpty()) areas = controller()->defaultAreas(); return areas; } void MainWindow::setupAreaSelector() { disconnect(d->areaSwitcher->tabBar, SIGNAL(currentChanged(int)), d, SLOT(toggleArea(int))); d->areaSwitcher->setUpdatesEnabled(false); d->areaSwitcher->tabBar->clearTabs(); int currentIndex = -1; QList< Area* > areas = this->areas(); QSet hadAreaName; for(int a = 0; a < areas.size(); ++a) { Area* theArea = areas[a]; if(hadAreaName.contains(theArea->objectName())) continue; hadAreaName.insert(theArea->objectName()); if(theArea->objectName() == area()->objectName()) { currentIndex = a; } d->areaSwitcher->tabBar->addCustomTab(theArea->title(), KIcon(theArea->iconName()), currentIndex == a, theArea->objectName(), customButtonForAreaSwitcher(theArea)); } d->areaSwitcher->tabBar->setCurrentIndex(currentIndex); d->areaSwitcher->updateGeometry(); d->areaSwitcher->setUpdatesEnabled(true); connect(d->areaSwitcher->tabBar, SIGNAL(currentChanged(int)), d, SLOT(toggleArea(int))); } MainWindow::~MainWindow() { kDebug() << "destroying mainwindow"; delete d; } void MainWindow::setArea(Area *area) { if (d->area) disconnect(d->area, 0, this, 0); bool wasEnabled = updatesEnabled(); setUpdatesEnabled(false); bool differentArea = (area != d->area); /* All views will be removed from dock area now. However, this does not mean those are removed from area, so prevent slotDockShown from recording those views as no longer shown in the area. */ d->ignoreDockShown = true; if (d->autoAreaSettingsSave && differentArea) saveSettings(); if (d->area) clearArea(); d->area = area; d->reconstruct(); if(d->area->activeView()) activateView(d->area->activeView()); else d->activateFirstVisibleView(); initializeStatusBar(); emit areaChanged(area); d->ignoreDockShown = false; setUpdatesEnabled(wasEnabled); // delay loading settings: we need to finish with area activation // and only then load mainwindow/dockwidget settings // this way dock sizes get properly restored QMetaObject::invokeMethod(this, "loadSettings", Qt::QueuedConnection); connect(area, SIGNAL(viewAdded(Sublime::AreaIndex*, Sublime::View*)), this, SLOT(viewAdded(Sublime::AreaIndex*, Sublime::View*))); connect(area, SIGNAL(viewRemoved(Sublime::AreaIndex*,Sublime::View*)), this, SLOT(viewRemovedInternal(Sublime::AreaIndex*, Sublime::View*))); connect(area, SIGNAL(requestToolViewRaise(Sublime::View*)), this, SLOT(raiseToolView(Sublime::View*))); connect(area, SIGNAL(aboutToRemoveView(Sublime::AreaIndex*, Sublime::View*)), this, SLOT(aboutToRemoveView(Sublime::AreaIndex*, Sublime::View*))); connect(area, SIGNAL(toolViewAdded(Sublime::View*, Sublime::Position)), this, SLOT(toolViewAdded(Sublime::View*, Sublime::Position))); connect(area, SIGNAL(aboutToRemoveToolView(Sublime::View*, Sublime::Position)), this, SLOT(aboutToRemoveToolView(Sublime::View*, Sublime::Position))); connect(area, SIGNAL(toolViewMoved(Sublime::View*, Sublime::Position)), this, SLOT(toolViewMoved(Sublime::View*, Sublime::Position))); connect(area, SIGNAL(changedWorkingSet(Sublime::Area*,QString,QString)), this, SLOT(setupAreaSelector())); } void MainWindow::initializeStatusBar() { //nothing here, reimplement in the subclasses if you want to have status bar //inside the bottom toolview buttons row } void MainWindow::resizeEvent(QResizeEvent* event) { return KParts::MainWindow::resizeEvent(event); } void MainWindow::clearArea() { emit areaCleared(d->area); d->clearArea(); } QList MainWindow::toolDocks() const { return d->docks; } Area *Sublime::MainWindow::area() const { return d->area; } Controller *MainWindow::controller() const { return d->controller; } View *MainWindow::activeView() { return d->activeView; } View *MainWindow::activeToolView() { return d->activeToolView; } void MainWindow::activateView(View *view) { if (!d->viewContainers.contains(view)) return; d->viewContainers[view]->setCurrentWidget(view->widget()); setActiveView(view); d->area->setActiveView(view); } void MainWindow::setActiveView(View *view) { View* oldActiveView = d->activeView; d->activeView = view; if (view && !view->widget()->hasFocus()) view->widget()->setFocus(); if(d->activeView != oldActiveView) emit activeViewChanged(view); } void Sublime::MainWindow::setActiveToolView(View *view) { d->activeToolView = view; emit activeToolViewChanged(view); } void MainWindow::saveSettings() { QString group = "MainWindow"; if (area()) group += '_' + area()->objectName(); KConfigGroup cg = KGlobal::config()->group(group); /* This will try to save window size, too. But it's OK, since we won't use this information when loading. */ saveMainWindowSettings(cg); //debugToolBar visibility is stored separately to allow a area dependent default value foreach (KToolBar* toolbar, toolBars()) { if (toolbar->objectName() == "debugToolBar") { - cg.writeEntry("debugToolBarVisibility", toolbar->isVisible()); + cg.writeEntry("debugToolBarVisibility", toolbar->isVisibleTo(this)); } } cg.sync(); } void MainWindow::loadSettings() { setUpdatesEnabled(false); kDebug(9504) << "loading settings for " << (area() ? area()->objectName() : ""); QString group = "MainWindow"; if (area()) group += '_' + area()->objectName(); KConfigGroup cg = KGlobal::config()->group(group); // What follows is copy-paste from applyMainWindowSettings. Unfortunately, // we don't really want that one to try restoring window size, and we also // cannot stop it from doing that in any clean way. QStatusBar* sb = qFindChild(this); if (sb) { QString entry = cg.readEntry("StatusBar", "Enabled"); if ( entry == "Disabled" ) sb->hide(); else sb->show(); } QMenuBar* mb = qFindChild(this); if (mb) { QString entry = cg.readEntry ("MenuBar", "Enabled"); if ( entry == "Disabled" ) mb->hide(); else mb->show(); } if ( !autoSaveSettings() || cg.name() == autoSaveGroup() ) { QString entry = cg.readEntry ("ToolBarsMovable", "Enabled"); if ( entry == "Disabled" ) KToolBar::setToolBarsLocked(true); else KToolBar::setToolBarsLocked(false); } // Utilise the QMainWindow::restoreState() functionality // Note that we're fixing KMainWindow bug here -- the original // code has this fragment above restoring toolbar properties. // As result, each save/restore would move the toolbar a bit to // the left. if (cg.hasKey("State")) { QByteArray state; state = cg.readEntry("State", state); state = QByteArray::fromBase64(state); // One day will need to load the version number, but for now, assume 0 restoreState(state); } else { // If there's no state we use a default size of 870x650 // Resize only when showing "code" area. If we do that for other areas, // then we'll hit bug https://bugs.kde.org/show_bug.cgi?id=207990 // TODO: adymo: this is more like a hack, we need a proper first-start initialization if (area() && area()->objectName() == "code") resize(870,650); } int n = 1; // Toolbar counter. toolbars are counted from 1, foreach (KToolBar* toolbar, toolBars()) { QString group("Toolbar"); // Give a number to the toolbar, but prefer a name if there is one, // because there's no real guarantee on the ordering of toolbars group += (toolbar->objectName().isEmpty() ? QString::number(n) : QString(" ")+toolbar->objectName()); KConfigGroup toolbarGroup(&cg, group); toolbar->applySettings(toolbarGroup, false); if (toolbar->objectName() == "debugToolBar") { //debugToolBar visibility is stored separately to allow a area dependent default value bool visibility = cg.readEntry("debugToolBarVisibility", area()->objectName() == "debug"); toolbar->setVisible(visibility); } n++; } KConfigGroup uiGroup = KGlobal::config()->group("UiSettings"); foreach (Container *container, findChildren()) { container->setTabBarHidden(uiGroup.readEntry("TabBarVisibility", 1) == 0); } cg.sync(); setUpdatesEnabled(true); emit settingsLoaded(); } bool MainWindow::queryClose() { // saveSettings(); KConfigGroup config(KGlobal::config(), "Main Window"); saveGeometry(config); config.sync(); return KParts::MainWindow::queryClose(); } void MainWindow::saveGeometry(KConfigGroup &config) { int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->screenGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) desk = QApplication::desktop()->screenGeometry(QApplication::desktop()->screen()); QString key = QString::fromLatin1("Desktop %1 %2") .arg(desk.width()).arg(desk.height()); config.writeEntry(key, geometry()); } void MainWindow::loadGeometry(const KConfigGroup &config) { // The below code, essentially, is copy-paste from // KMainWindow::restoreWindowSize. Right now, that code is buggy, // as per http://permalink.gmane.org/gmane.comp.kde.devel.core/52423 // so we implement a less theoretically correct, but working, version // below const int scnum = QApplication::desktop()->screenNumber(parentWidget()); QRect desk = QApplication::desktop()->screenGeometry(scnum); // if the desktop is virtual then use virtual screen size if (QApplication::desktop()->isVirtualDesktop()) desk = QApplication::desktop()->screenGeometry(QApplication::desktop()->screen()); QString key = QString::fromLatin1("Desktop %1 %2") .arg(desk.width()).arg(desk.height()); QRect g = config.readEntry(key, QRect()); if (!g.isEmpty()) setGeometry(g); } void MainWindow::enableAreaSettingsSave() { d->autoAreaSettingsSave = true; } QWidget *MainWindow::statusBarLocation() { return d->idealController->statusBarLocation(); } void MainWindow::setAreaSwitcherCornerWidget(QWidget* widget) { d->areaSwitcher->setTabSideWidget(widget); } void MainWindow::setTabBarLeftCornerWidget(QWidget* widget) { d->setTabBarLeftCornerWidget(widget); } void MainWindow::tabContextMenuRequested(View* , KMenu* ) { // do nothing } void MainWindow::tabToolTipRequested(View*, Container*, int) { // do nothing } void MainWindow::dockBarContextMenuRequested(Qt::DockWidgetArea , const QPoint& ) { // do nothing } View* MainWindow::viewForPosition(QPoint globalPos) const { foreach(Container* container, d->viewContainers.values()) { QRect globalGeom = QRect(container->mapToGlobal(QPoint(0,0)), container->mapToGlobal(QPoint(container->width(), container->height()))); if(globalGeom.contains(globalPos)) { return d->widgetToView[container->currentWidget()]; } } return 0; } } #include "mainwindow.moc" diff --git a/util/activetooltip.cpp b/util/activetooltip.cpp index 3d14f77da7..964552d100 100644 --- a/util/activetooltip.cpp +++ b/util/activetooltip.cpp @@ -1,358 +1,357 @@ /* This file is part of the KDE project Copyright 2007 Vladimir Prus Copyright 2009-2010 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "activetooltip.h" #include #include #include #include #include #include #include -#include #include #include #include #include namespace KDevelop { class ActiveToolTipPrivate { public: uint previousDistance_; QRect rect_; QRegion rectExtensions_; - QList > friendWidgets_; + QList > friendWidgets_; int mouseOut_; }; ActiveToolTip::ActiveToolTip(QWidget *parent, const QPoint& position) : QWidget(parent, Qt::ToolTip), d(new ActiveToolTipPrivate) { Q_ASSERT(parent); d->mouseOut_ = 0; d->previousDistance_ = std::numeric_limits::max(); setMouseTracking(true); d->rect_ = QRect(position, position); d->rect_.adjust(-10, -10, 10, 10); move(position); QPalette p; // adjust background color to use tooltip colors p.setColor(backgroundRole(), p.color(QPalette::ToolTipBase)); p.setColor(QPalette::Base, p.color(QPalette::ToolTipBase)); // adjust foreground color to use tooltip colors p.setColor(foregroundRole(), p.color(QPalette::ToolTipText)); p.setColor(QPalette::Text, p.color(QPalette::ToolTipText)); setPalette(p); qApp->installEventFilter(this); } ActiveToolTip::~ActiveToolTip() { delete d; } bool ActiveToolTip::eventFilter(QObject *object, QEvent *e) { switch (e->type()) { case QEvent::WindowActivate: case QEvent::WindowDeactivate: { if(insideThis(object)) return false; if(isVisible()) kDebug() << "closing because of window activation"; close(); } case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::Wheel: /* If the click is within tooltip, it's fine. Clicks outside close it. */ { //Do not close the widget when NoFocus widgets are used QWidget* widget = dynamic_cast(object); if(widget && widget->focusPolicy() == Qt::NoFocus) return false; } if (!insideThis(object)) { if(isVisible()) kDebug() << "closing because of click into" << object; close(); } // FIXME: revisit this code later. #if 0 case QEvent::FocusIn: case QEvent::FocusOut: close(); break; #endif case QEvent::MouseMove: { QPoint globalPos = static_cast(e)->globalPos(); if (!d->rect_.isNull() && !d->rect_.contains(globalPos) && !d->rectExtensions_.contains(globalPos) && !insideThis(object)) { int distance = (d->rect_.center() - static_cast(e)->globalPos()).manhattanLength(); // On X, when the cursor leaves the tooltip and enters // the parent, we sometimes get some wrong Y coordinate. // Don't know why, so wait for two out-of-range mouse // positions before closing. //Additional test: When the cursor has been moved towards the tooltip, don't close it. if(distance > (int)d->previousDistance_) { ++d->mouseOut_; emit mouseOut(); }else d->previousDistance_ = distance; } else{ d->mouseOut_ = 0; emit mouseIn(); } if (d->mouseOut_ == 2) { kDebug() << "closing because of mouse move"; close(); } } default: break; } return false; } void ActiveToolTip::addFriendWidget(QWidget* widget) { d->friendWidgets_.append((QObject*)widget); } bool ActiveToolTip::insideThis(QObject* object) { while (object) { if(dynamic_cast(object)) return true; if (object == this || d->friendWidgets_.contains(object)) { return true; } object = object->parent(); } return false; } void ActiveToolTip::showEvent(QShowEvent*) { adjustRect(); } void ActiveToolTip::updateMouseDistance() { d->previousDistance_ = (d->rect_.center() - QCursor::pos()).manhattanLength(); } void ActiveToolTip::moveEvent(QMoveEvent* ev) { QWidget::moveEvent(ev); updateMouseDistance(); } void ActiveToolTip::resizeEvent(QResizeEvent*) { adjustRect(); // set mask from style QStyleOptionFrame opt; opt.init(this); QStyleHintReturnMask mask; if( style()->styleHint( QStyle::SH_ToolTip_Mask, &opt, this, &mask ) && !mask.region.isEmpty() ) { setMask( mask.region ); } emit resized(); updateMouseDistance(); } void ActiveToolTip::paintEvent(QPaintEvent* event) { QStylePainter painter( this ); painter.setClipRegion( event->region() ); QStyleOptionFrame opt; opt.init(this); painter.drawPrimitive(QStyle::PE_PanelTipLabel, opt); } void ActiveToolTip::addExtendRect(const QRect& rect) { d->rectExtensions_ += rect; } void ActiveToolTip::adjustRect() { // For tooltip widget, geometry() returns global coordinates. QRect r = geometry(); r.adjust(-10, -10, 10, 10); d->rect_ = r; updateMouseDistance(); } void ActiveToolTip::setBoundingGeometry(const QRect& geometry) { d->rect_ = geometry; d->rect_.adjust(-10, -10, 10, 10); } namespace { - typedef QMultiMap, QString> > ToolTipPriorityMap; + typedef QMultiMap, QString> > ToolTipPriorityMap; static ToolTipPriorityMap registeredToolTips; ActiveToolTipManager manager; QWidget* masterWidget(QWidget* w) { while(w && w->parent() && qobject_cast(w->parent())) w = qobject_cast(w->parent()); return w; } } void ActiveToolTipManager::doVisibility() { bool exclusive = false; int lastBottomPosition = -1; int lastLeftPosition = -1; QRect fullGeometry; //Geometry of all visible tooltips together for(ToolTipPriorityMap::const_iterator it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) { - QPointer< ActiveToolTip > w = (*it).first; + QWeakPointer< ActiveToolTip > w = (*it).first; if(w) { if(exclusive) { - (w)->hide(); + (w.data())->hide(); }else{ - QRect geom = (w)->geometry(); - if((w)->geometry().top() < lastBottomPosition) { + QRect geom = (w.data())->geometry(); + if((w.data())->geometry().top() < lastBottomPosition) { geom.moveTop(lastBottomPosition); } if(lastLeftPosition != -1) geom.moveLeft(lastLeftPosition); - (w)->setGeometry(geom); -// (w)->show(); + (w.data())->setGeometry(geom); +// (w.data())->show(); - lastBottomPosition = (w)->geometry().bottom(); - lastLeftPosition = (w)->geometry().left(); + lastBottomPosition = (w.data())->geometry().bottom(); + lastLeftPosition = (w.data())->geometry().left(); if(it == registeredToolTips.constBegin()) - fullGeometry = (w)->geometry(); + fullGeometry = (w.data())->geometry(); else - fullGeometry = fullGeometry.united((w)->geometry()); + fullGeometry = fullGeometry.united((w.data())->geometry()); } if(it.key() == 0) { exclusive = true; } } } if(!fullGeometry.isEmpty()) { QRect oldFullGeometry = fullGeometry; QRect screenGeometry = QApplication::desktop()->screenGeometry(fullGeometry.topLeft()); if(fullGeometry.bottom() > screenGeometry.bottom()) { //Move up, avoiding the mouse-cursor fullGeometry.moveBottom(fullGeometry.top()-10); if(fullGeometry.adjusted(-20, -20, 20, 20).contains(QCursor::pos())) fullGeometry.moveBottom(QCursor::pos().y() - 20); } if(fullGeometry.right() > screenGeometry.right()) { //Move to left, avoiding the mouse-cursor fullGeometry.moveRight(fullGeometry.left()-10); if(fullGeometry.adjusted(-20, -20, 20, 20).contains(QCursor::pos())) fullGeometry.moveRight(QCursor::pos().x() - 20); } // Now fit this to screen if (fullGeometry.left() < 0) { fullGeometry.setLeft(0); } if (fullGeometry.top() < 0) { fullGeometry.setTop(0); } QPoint offset = fullGeometry.topLeft() - oldFullGeometry.topLeft(); if(!offset.isNull()) { for(ToolTipPriorityMap::const_iterator it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) if((*it).first) { - (*it).first->move((*it).first->pos() + offset); + (*it).first.data()->move((*it).first.data()->pos() + offset); } } } //Set bounding geometry, and remove old tooltips for(ToolTipPriorityMap::iterator it = registeredToolTips.begin(); it != registeredToolTips.end(); ) { if(!(*it).first) { it = registeredToolTips.erase(it); }else{ - (*it).first->setBoundingGeometry(fullGeometry); + (*it).first.data()->setBoundingGeometry(fullGeometry); ++it; } } //Final step: Show tooltips for(ToolTipPriorityMap::const_iterator it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) { - if(it->first && masterWidget(it->first)->isActiveWindow()) - (*it).first->show(); + if(it->first.data() && masterWidget(it->first.data())->isActiveWindow()) + (*it).first.data()->show(); if(exclusive) break; } } void ActiveToolTip::showToolTip(KDevelop::ActiveToolTip* tooltip, float priority, QString uniqueId) { if(!uniqueId.isEmpty()) { - for(QMap< float, QPair< QPointer< ActiveToolTip >, QString > >::const_iterator it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) { + for(QMap< float, QPair< QWeakPointer< ActiveToolTip >, QString > >::const_iterator it = registeredToolTips.constBegin(); it != registeredToolTips.constEnd(); ++it) { if((*it).second == uniqueId) - delete (*it).first; + delete (*it).first.data(); } } - registeredToolTips.insert(priority, qMakePair(QPointer(tooltip), uniqueId)); + registeredToolTips.insert(priority, qMakePair(QWeakPointer(tooltip), uniqueId)); connect(tooltip, SIGNAL(resized()), &manager, SLOT(doVisibility())); QMetaObject::invokeMethod(&manager, "doVisibility", Qt::QueuedConnection); } void ActiveToolTip::closeEvent(QCloseEvent* event) { QWidget::closeEvent(event); deleteLater(); } } #include "activetooltip.moc" diff --git a/vcs/models/vcsfilechangesmodel.cpp b/vcs/models/vcsfilechangesmodel.cpp index ff87e8b8bd..d7f620ebe4 100644 --- a/vcs/models/vcsfilechangesmodel.cpp +++ b/vcs/models/vcsfilechangesmodel.cpp @@ -1,184 +1,186 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Splitted into separate class Copyright 2011 Andrey Batyiev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include "vcsfilechangesmodel.h" namespace KDevelop { static QString stateToString(KDevelop::VcsStatusInfo::State state) { switch(state) { case KDevelop::VcsStatusInfo::ItemAdded: return i18nc("file was added to versioncontrolsystem", "Added"); case KDevelop::VcsStatusInfo::ItemDeleted: return i18nc("file was deleted from versioncontrolsystem", "Deleted"); case KDevelop::VcsStatusInfo::ItemHasConflicts: return i18nc("file is confilicting (versioncontrolsystem)", "Has Conflicts"); case KDevelop::VcsStatusInfo::ItemModified: return i18nc("version controlled file was modified", "Modified"); case KDevelop::VcsStatusInfo::ItemUpToDate: return i18nc("file is up to date in versioncontrolsystem", "Up To Date"); case KDevelop::VcsStatusInfo::ItemUnknown: case KDevelop::VcsStatusInfo::ItemUserState: return i18nc("file is not known to versioncontrolsystem", "Unknown"); } return i18nc("Unknown VCS file status, probably a backend error", "?"); } static KIcon stateToIcon(KDevelop::VcsStatusInfo::State state) { switch(state) { case KDevelop::VcsStatusInfo::ItemAdded: return KIcon("vcs-added"); case KDevelop::VcsStatusInfo::ItemDeleted: return KIcon("vcs-removed"); case KDevelop::VcsStatusInfo::ItemHasConflicts: return KIcon("vcs-conflicting"); case KDevelop::VcsStatusInfo::ItemModified: return KIcon("vcs-locally-modified"); case KDevelop::VcsStatusInfo::ItemUpToDate: return KIcon("vcs-normal"); case KDevelop::VcsStatusInfo::ItemUnknown: case KDevelop::VcsStatusInfo::ItemUserState: return KIcon("unknown"); } return KIcon("dialog-error"); } class VcsFileChangesModelPrivate { public: bool allowSelection; }; VcsFileChangesModel::VcsFileChangesModel(QObject *parent, bool allowSelection) : QStandardItemModel(parent), d(new VcsFileChangesModelPrivate) { setColumnCount(2); setHeaderData(0, Qt::Horizontal, i18n("Filename")); setHeaderData(1, Qt::Horizontal, i18n("Status")); d->allowSelection = allowSelection; } int VcsFileChangesModel::updateState(QStandardItem *parent, const KDevelop::VcsStatusInfo &status) { - QStandardItem* it1=0; + QStandardItem* it1=fileItemForUrl(parent, status.url()); QStandardItem* itStatus; - it1=fileItemForUrl(parent, status.url()); - if(status.state()==VcsStatusInfo::ItemUnknown || status.state()==VcsStatusInfo::ItemUpToDate) { if(it1) parent->removeRow(it1->row()); return -1; } else { if(!it1) { QString path = ICore::self()->projectController()->prettyFileName(status.url(), KDevelop::IProjectController::FormatPlain); KIcon icon(KMimeType::findByUrl(status.url(), 0, false, true)->iconName(status.url())); it1 = new QStandardItem(icon, path); itStatus = new QStandardItem; if(d->allowSelection) { it1->setCheckable(true); it1->setCheckState(status.state() == VcsStatusInfo::ItemUnknown ? Qt::Unchecked : Qt::Checked); } parent->appendRow(QList() << it1 << itStatus); } else { QStandardItem *parent = it1->parent(); if(parent == 0) parent = invisibleRootItem(); itStatus = parent->child(it1->row(), 1); } QString text = stateToString(status.state()); if(itStatus->text()!=text) { itStatus->setText(text); itStatus->setIcon(stateToIcon(status.state())); } it1->setData(qVariantFromValue(status), VcsStatusInfoRole); return it1->row(); } } QStandardItem* VcsFileChangesModel::fileItemForUrl(QStandardItem* parent, const QUrl& url) { for(int i=0; irowCount(); i++) { QStandardItem* curr=parent->child(i); if(curr->data(VcsStatusInfoRole).value().url()==url) { return curr; } } return 0; } QList VcsFileChangesModel::checkedStatuses(QStandardItem *parent) const { QList ret; if(!d->allowSelection) return ret; for(int i = 0; i < parent->rowCount(); i++) { QStandardItem* item = parent->child(i); if(item->checkState() == Qt::Checked) { ret << statusInfo(item); } } return ret; } QList VcsFileChangesModel::checkedUrls(QStandardItem *parent) const { QList ret; if(!d->allowSelection) return ret; for(int i = 0; i < parent->rowCount(); i++) { QStandardItem* item = parent->child(i); if(item->checkState() == Qt::Checked) { ret << statusInfo(item).url(); } } return ret; } +void VcsFileChangesModel::setIsCheckbable(bool checkable) +{ + d->allowSelection = checkable; +} } diff --git a/vcs/models/vcsfilechangesmodel.h b/vcs/models/vcsfilechangesmodel.h index d3aa5dad48..87f34a36b6 100644 --- a/vcs/models/vcsfilechangesmodel.h +++ b/vcs/models/vcsfilechangesmodel.h @@ -1,127 +1,128 @@ /* This file is part of KDevelop Copyright 2010 Aleix Pol Splitted into separate class Copyright 2011 Andrey Batyiev This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VCSFILECHANGESMODEL_H #define VCSFILECHANGESMODEL_H #include #include #include "../vcsexport.h" class KUrl; namespace KDevelop { class VcsStatusInfo; /** * This class holds and represents information about changes in files. * Also it is possible to provide tree like models by inheriting this class, see protected members. * All stuff should be pulled in by @p updateState. */ class KDEVPLATFORMVCS_EXPORT VcsFileChangesModel : public QStandardItemModel { Q_OBJECT public: /** * Constructor for class. - * @param allowSelection if true, model will show checkboxes on items. + * @param isCheckable if true, model will show checkboxes on items. */ - VcsFileChangesModel(QObject *parent, bool allowSelection = false); + VcsFileChangesModel(QObject *parent, bool isCheckable = false); enum ItemRoles { VcsStatusInfoRole = Qt::UserRole+1 }; /** * Returns item for particular url. */ QStandardItem* fileItemForUrl(const QUrl &url) { return fileItemForUrl(invisibleRootItem(), url); } /** * Returns list of currently checked statuses. */ QList checkedStatuses() const { return checkedStatuses(invisibleRootItem()); } /** * Returns list of currently checked urls. */ QList checkedUrls() const { return checkedUrls(invisibleRootItem()); } /** * Simple helper to get VcsStatusInfo. */ static VcsStatusInfo statusInfo(const QModelIndex &i) { return i.data(VcsStatusInfoRole).value(); } /** * Simple helper to get VcsStatusInfo. */ static VcsStatusInfo statusInfo(const QStandardItem *item) { return item->data(VcsStatusInfoRole).value(); } /** * Returns item for particular url. */ static QStandardItem* fileItemForUrl(QStandardItem *parent, const QUrl &url); + void setIsCheckbable(bool checkable); public slots: /** * Used to post update of status of some file. Any status except UpToDate * and Unknown will update (or add) item representation. */ void updateState(const KDevelop::VcsStatusInfo &status) { updateState(invisibleRootItem(), status); } protected: /** * Post update of status of some file. * @return changed row or -1 if row is deleted */ int updateState(QStandardItem *parent, const KDevelop::VcsStatusInfo &status); /** * Returns list of currently checked statuses. */ QList checkedStatuses(QStandardItem *parent) const; /** * Returns list of currently checked urls. */ QList checkedUrls(QStandardItem *parent) const; private: class VcsFileChangesModelPrivate *const d; }; } #endif // FILECHANGESMODEL_H diff --git a/vcs/widgets/vcsdiffpatchsources.cpp b/vcs/widgets/vcsdiffpatchsources.cpp index 4d3d24e51b..66ade13b09 100644 --- a/vcs/widgets/vcsdiffpatchsources.cpp +++ b/vcs/widgets/vcsdiffpatchsources.cpp @@ -1,274 +1,275 @@ /* Copyright 2009 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "vcsdiffpatchsources.h" #include #include #include #include #include #include #include #include #include "vcsjob.h" #include "vcsdiff.h" #include #include #include #include using namespace KDevelop; VCSCommitDiffPatchSource::VCSCommitDiffPatchSource(VCSDiffUpdater* updater, const KUrl& url, IBasicVersionControl* vcs) : VCSDiffPatchSource(updater), m_vcs(vcs) { Q_ASSERT(m_vcs); QScopedPointer statusJob(vcs->status(url)); QVariant varlist; if( statusJob->exec() && statusJob->status() == VcsJob::JobSucceeded ) { varlist = statusJob->fetchResults(); foreach( const QVariant &var, varlist.toList() ) { VcsStatusInfo info = qVariantValue( var ); m_infos += info; if(info.state()!=VcsStatusInfo::ItemUpToDate) m_selectable[info.url()] = info.state(); } } else kDebug() << "Couldn't get status for urls: " << url; m_commitMessageWidget = new QWidget; - QVBoxLayout* layout = new QVBoxLayout(m_commitMessageWidget); + QVBoxLayout* layout = new QVBoxLayout(m_commitMessageWidget.data()); m_commitMessageEdit = new QTextEdit; - m_commitMessageEdit->setFont( KGlobalSettings::fixedFont() ); + m_commitMessageEdit.data()->setFont( KGlobalSettings::fixedFont() ); + m_commitMessageEdit.data()->setLineWrapMode(QTextEdit::NoWrap); QHBoxLayout* titleLayout = new QHBoxLayout; titleLayout->addWidget(new QLabel(i18n("Commit Message:"))); m_oldMessages = new KComboBox; m_oldMessages->addItem(i18n("Old Messages")); foreach(QString message, oldMessages()) m_oldMessages->addItem(message, message); m_oldMessages->setMaximumWidth(200); connect(m_oldMessages, SIGNAL(currentIndexChanged(QString)), this, SLOT(oldMessageChanged(QString))); titleLayout->addWidget(m_oldMessages); layout->addLayout(titleLayout); - layout->addWidget(m_commitMessageEdit); + layout->addWidget(m_commitMessageEdit.data()); } QStringList VCSCommitDiffPatchSource::oldMessages() const { KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); return vcsGroup.readEntry("OldCommitMessages", QStringList()); } void VCSCommitDiffPatchSource::oldMessageChanged(QString text) { if(m_oldMessages->currentIndex() != 0) { m_oldMessages->setCurrentIndex(0); - m_commitMessageEdit->setText(text); + m_commitMessageEdit.data()->setText(text); } } VCSDiffPatchSource::VCSDiffPatchSource(VCSDiffUpdater* updater) : m_updater(updater) { update(); } VCSDiffPatchSource::VCSDiffPatchSource(const KDevelop::VcsDiff& diff) : m_updater(0) { updateFromDiff(diff); } VCSDiffPatchSource::~VCSDiffPatchSource() { QFile::remove(m_file.toLocalFile()); delete m_updater; } KUrl VCSDiffPatchSource::baseDir() const { return m_base; } KUrl VCSDiffPatchSource::file() const { return m_file; } QString VCSDiffPatchSource::name() const { return m_name; } void VCSDiffPatchSource::updateFromDiff(VcsDiff vcsdiff) { if(!m_file.isValid()) { KTemporaryFile temp2; temp2.setSuffix("2.patch"); temp2.setAutoRemove(false); temp2.open(); QTextStream t2(&temp2); t2 << vcsdiff.diff(); kDebug() << "filename:" << temp2.fileName(); m_file = KUrl(temp2.fileName()); temp2.close(); }else{ QFile file(m_file.path()); file.open(QIODevice::WriteOnly); QTextStream t2(&file); t2 << vcsdiff.diff(); } kDebug() << "using file" << m_file << vcsdiff.diff(); m_name = "VCS Diff"; m_base = vcsdiff.baseDiff(); emit patchChanged(); } void VCSDiffPatchSource::update() { if(!m_updater) return; updateFromDiff(m_updater->update()); } VCSCommitDiffPatchSource::~VCSCommitDiffPatchSource() { - delete m_commitMessageWidget; + delete m_commitMessageWidget.data(); } bool VCSCommitDiffPatchSource::canSelectFiles() const { return true; } QMap< KUrl, KDevelop::VcsStatusInfo::State> VCSCommitDiffPatchSource::additionalSelectableFiles() const { return m_selectable; } QWidget* VCSCommitDiffPatchSource::customWidget() const { - return m_commitMessageWidget; + return m_commitMessageWidget.data(); } QString VCSCommitDiffPatchSource::finishReviewCustomText() const { return i18n("Commit"); } bool VCSCommitDiffPatchSource::canCancel() const { return true; } void VCSCommitDiffPatchSource::cancelReview() { QString message; if (m_commitMessageEdit) - message = m_commitMessageEdit->toPlainText(); + message = m_commitMessageEdit.data()->toPlainText(); emit reviewCancelled(message); deleteLater(); } bool VCSCommitDiffPatchSource::finishReview(QList< KUrl > selection) { QString message; if (m_commitMessageEdit) - message = m_commitMessageEdit->toPlainText(); + message = m_commitMessageEdit.data()->toPlainText(); kDebug() << "Finishing with selection" << selection; QString text = i18n("Files will be committed:\n"); foreach(const KUrl& url, selection) text += ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain) + '\n'; text += i18n("\nWith message:\n %1", message); int res = KMessageBox::warningContinueCancel(0, text, i18n("About to commit to repository")); if (res != KMessageBox::Continue) { return false; } emit reviewFinished(message, selection); VcsJob* job=m_vcs->commit(message, selection, KDevelop::IBasicVersionControl::NonRecursive); ICore::self()->runController()->registerJob(job); deleteLater(); return true; } static KDevelop::IPatchSource::Ptr currentShownDiff; bool showVcsDiff(IPatchSource* vcsDiff) { KDevelop::IPatchReview* patchReview = ICore::self()->pluginController()->extensionForPlugin("org.kdevelop.IPatchReview"); //Only give one VCS diff at a time to the patch review plugin delete currentShownDiff; currentShownDiff = vcsDiff; if( patchReview ) { patchReview->startReview(currentShownDiff); return true; } else { kWarning() << "Patch review plugin not found"; return false; } } VcsDiff VCSStandardDiffUpdater::update() const { QScopedPointer diffJob(m_vcs->diff(m_url, KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Base), KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Working))); VcsDiff diff; bool correctDiff = diffJob->exec(); if (correctDiff) diff = diffJob->fetchResults().value(); if (!correctDiff) KMessageBox::error(0, i18n("Could not create a patch for the current version.")); return diff; } VCSStandardDiffUpdater::VCSStandardDiffUpdater(IBasicVersionControl* vcs, KUrl url) : m_vcs(vcs), m_url(url) { } VCSStandardDiffUpdater::~VCSStandardDiffUpdater() { } VCSDiffUpdater::~VCSDiffUpdater() { } #include "vcsdiffpatchsources.moc" diff --git a/vcs/widgets/vcsdiffpatchsources.h b/vcs/widgets/vcsdiffpatchsources.h index 8c60e4413d..f4be887dfa 100644 --- a/vcs/widgets/vcsdiffpatchsources.h +++ b/vcs/widgets/vcsdiffpatchsources.h @@ -1,126 +1,126 @@ /* Copyright 2009 David Nolden This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * This is an internal header */ #ifndef VCSDIFFPATCHSOURCES_H #define VCSDIFFPATCHSOURCES_H #include #include #include #include #include #include "vcs/vcsstatusinfo.h" #include "../vcsexport.h" class KComboBox; namespace KDevelop { class VcsCommitDialog; class IBasicVersionControl; class VcsDiff; } class QWidget; class VCSDiffUpdater { public: virtual ~VCSDiffUpdater(); virtual KDevelop::VcsDiff update() const = 0; }; class KDEVPLATFORMVCS_EXPORT VCSStandardDiffUpdater : public VCSDiffUpdater { public: VCSStandardDiffUpdater(KDevelop::IBasicVersionControl* vcs, KUrl url); virtual ~VCSStandardDiffUpdater(); virtual KDevelop::VcsDiff update() const; private: KDevelop::IBasicVersionControl* m_vcs; KUrl m_url; }; class KDEVPLATFORMVCS_EXPORT VCSDiffPatchSource : public KDevelop::IPatchSource { public: /// The ownership of the updater is taken VCSDiffPatchSource(VCSDiffUpdater* updater); VCSDiffPatchSource(const KDevelop::VcsDiff& diff); virtual ~VCSDiffPatchSource(); virtual KUrl baseDir() const ; virtual KUrl file() const ; virtual QString name() const ; virtual void update() ; virtual bool isAlreadyApplied() const { return true; } KUrl m_base, m_file; QString m_name; VCSDiffUpdater* m_updater; private: void updateFromDiff(KDevelop::VcsDiff diff); }; class KDEVPLATFORMVCS_EXPORT VCSCommitDiffPatchSource : public VCSDiffPatchSource { Q_OBJECT public: /// The ownership of the updater is taken VCSCommitDiffPatchSource(VCSDiffUpdater* updater, const KUrl& base, KDevelop::IBasicVersionControl* vcs); ~VCSCommitDiffPatchSource() ; QStringList oldMessages() const; virtual bool canSelectFiles() const ; QMap additionalSelectableFiles() const ; virtual QWidget* customWidget() const ; virtual QString finishReviewCustomText() const ; virtual bool canCancel() const; virtual void cancelReview(); virtual bool finishReview(QList< KUrl > selection) ; QList infos() const { return m_infos; } Q_SIGNALS: void reviewFinished(QString message, QList selection); void reviewCancelled(QString message); public: - QPointer m_commitMessageWidget; - QPointer m_commitMessageEdit; + QWeakPointer m_commitMessageWidget; + QWeakPointer m_commitMessageEdit; QList m_infos; QMap m_selectable; KDevelop::IBasicVersionControl* m_vcs; KComboBox* m_oldMessages; public slots: void oldMessageChanged(QString); }; ///Sends the diff to the patch-review plugin. ///Returns whether the diff was shown successfully. bool KDEVPLATFORMVCS_EXPORT showVcsDiff(KDevelop::IPatchSource* vcsDiff); #endif // VCSDIFFPATCHSOURCES_H