00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "sourcenav_part.h"
00014
00015 #include <kdebug.h>
00016 #include <ktexteditor/viewcursorinterface.h>
00017 #include <ktexteditor/document.h>
00018 #include <ktexteditor/editinterface.h>
00019 #include <kgenericfactory.h>
00020 #include <kaction.h>
00021 #include <kguiitem.h>
00022 #include <kpopupmenu.h>
00023
00024 #include "kdevcore.h"
00025 #include "kdevpartcontroller.h"
00026
00027
00028 static const int MAX_HISTORY = 50;
00029
00030 static const int MAX_CLEANUP = 200;
00031
00032 static const int MAX_ITEMS = 20;
00033
00034 static int anchorID = 0;
00035
00036 int Anchor::nextID()
00037 {
00038 return anchorID++;
00039 }
00040
00041 typedef KGenericFactory<SourceNavPart> SourceNavFactory;
00042 K_EXPORT_COMPONENT_FACTORY( libkdevsourcenav, SourceNavFactory( "kdevsourcenav" ) );
00043
00044 SourceNavPart::SourceNavPart(QObject *parent, const char *name, const QStringList& )
00045 : KDevPlugin("SourceNav", "sourcenav", parent, name ? name : "SourceNavPart")
00046 {
00047 setInstance(SourceNavFactory::instance());
00048 setXMLFile("kdevpart_sourcenav.rc");
00049
00050 backPopupVisible = false;
00051 forwardPopupVisible = false;
00052
00053 connect( partController(), SIGNAL(partAdded(KParts::Part*)), this, SLOT(slotPartAdded(KParts::Part*)) );
00054
00055 navForward = new KToolBarPopupAction( KGuiItem( i18n("Navigate Forward"), "1rightarrow", i18n( "ToolTip" ), i18n( "What's This" ) ),
00056 0, this, SLOT(slotNavForward()), actionCollection(), "navForward" );
00057
00058 navBack = new KToolBarPopupAction( KGuiItem( i18n("Navigate Backwards"), "1leftarrow", i18n( "ToolTip" ), i18n( "What's This" ) ),
00059 0, this, SLOT(slotNavBack()), actionCollection(), "navBack" );
00060
00061 connect( navForward->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(fillForwardPopup()) );
00062 connect( navForward->popupMenu(), SIGNAL(activated(int)), this, SLOT(forwardPopupClicked(int)) );
00063 connect( navBack->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(fillBackPopup()) );
00064 connect( navBack->popupMenu(), SIGNAL(activated(int)), this, SLOT(backPopupClicked(int)) );
00065
00066 navForward->setEnabled( false );
00067 navBack->setEnabled( false );
00068 }
00069
00070
00071 SourceNavPart::~SourceNavPart()
00072 {
00073 }
00074
00075 void SourceNavPart::backPopupClicked( int id )
00076 {
00077 navigate( id, navList, forwardList );
00078 }
00079
00080 void SourceNavPart::forwardPopupClicked( int id )
00081 {
00082 navigate( id, forwardList, navList );
00083 }
00084
00085 void SourceNavPart::fillPopup( const AnchorList& list, QPopupMenu* pop )
00086 {
00087 if ( !pop )
00088 return;
00089
00090 QString item;
00091 int i = 0;
00092 pop->clear();
00093 AnchorList::ConstIterator it = list.begin();
00094 while ( it != list.end() && i < MAX_ITEMS ) {
00095 if ( (*it).url().isLocalFile() ) {
00096 item = (*it).url().fileName();
00097 } else {
00098 item = (*it).url().prettyURL();
00099 }
00100 item += ":" + QString::number( (*it).line() );
00101 pop->insertItem( item, (*it).id() );
00102 ++i;
00103 ++it;
00104 }
00105 }
00106
00107 void SourceNavPart::fillBackPopup()
00108 {
00109 fillPopup( navList, navBack->popupMenu() );
00110 }
00111
00112 void SourceNavPart::fillForwardPopup()
00113 {
00114 fillPopup( forwardList, navForward->popupMenu() );
00115 }
00116
00117
00118 void SourceNavPart::slotPartAdded( KParts::Part *part )
00119 {
00120 if ( !part )
00121 return;
00122
00123 KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>(part);
00124 if ( !doc )
00125 return;
00126
00127 KTextEditor::EditInterface *ed = dynamic_cast<KTextEditor::EditInterface*>(doc);
00128 if ( ed ) {
00129 connect( doc, SIGNAL(textChanged()), this, SLOT(slotTextChanged()));
00130 }
00131
00132 }
00133
00134 Anchor SourceNavPart::getCurrentPos()
00135 {
00136 uint line = 0;
00137 uint col = 0;
00138
00139 if ( !partController() )
00140 return Anchor();
00141
00142 KTextEditor::Document *doc = dynamic_cast<KTextEditor::Document*>(partController()->activePart());
00143 QWidget *view = partController()->activeWidget();
00144 if ( !doc || !view )
00145 return Anchor();
00146
00147
00148 KTextEditor::ViewCursorInterface *cur = dynamic_cast<KTextEditor::ViewCursorInterface*>(view);
00149 if ( cur ) {
00150 cur->cursorPosition( &line, &col );
00151 }
00152
00153 return Anchor( doc->url(), line, col );
00154 }
00155
00156 void SourceNavPart::gotoPos( const Anchor& ankh )
00157 {
00158 if ( !partController() )
00159 return;
00160 partController()->editDocument( ankh.url(), ankh.line() );
00161 }
00162
00163
00164 bool SourceNavPart::isNearby( const Anchor& pos1, const Anchor& pos2 )
00165 {
00166
00167 if ( pos1.isValid() && pos2.isValid() && pos1.url() == pos2.url() && QABS((int)pos1.line() - (int)pos2.line()) < 5 )
00168 return true;
00169 return false;
00170 }
00171
00172 void SourceNavPart::slotTextChanged()
00173 {
00174 const Anchor cur = getCurrentPos();
00175 const Anchor old = navList.isEmpty() ? Anchor() : navList.first();
00176
00177 if ( !isNearby( cur, old ) ) {
00178 navList.push_front( cur );
00179 }
00180
00181 if ( cur.id() % MAX_CLEANUP == 0 ) {
00182 cleanupList( navList );
00183 cleanupList( forwardList );
00184 }
00185
00186 enableActions();
00187 }
00188
00189 void SourceNavPart::navigate( int id, AnchorList& list1, AnchorList& list2 )
00190 {
00191 Anchor ankh;
00192
00193 AnchorList::Iterator it = list1.begin();
00194 while ( it != list1.end() ) {
00195 ankh = (*it);
00196 list2.push_front( *it );
00197 it = list1.remove( it );
00198
00199 if ( ankh.id() == id ) {
00200
00201 it = list1.end();
00202 }
00203 }
00204
00205 if ( ankh.isValid() ) {
00206 gotoPos( ankh );
00207 }
00208 enableActions();
00209 }
00210
00211 void SourceNavPart::navigate( AnchorList& list1, AnchorList& list2 )
00212 {
00213 if ( list1.isEmpty() )
00214 return;
00215
00216 const Anchor cur = getCurrentPos();
00217 Anchor last = list1.first();
00218
00219 list2.push_front( last );
00220 list1.pop_front();
00221
00222 if ( isNearby( cur, last ) ) {
00223
00224 if ( list1.isEmpty() ) {
00225
00226 enableActions();
00227 return;
00228 }
00229 last = list1.first();
00230 list2.push_front( last );
00231 list1.pop_front();
00232 }
00233
00234 gotoPos( last );
00235 enableActions();
00236 }
00237
00238 void SourceNavPart::slotNavBack()
00239 {
00240 navigate( navList, forwardList );
00241 }
00242
00243 void SourceNavPart::slotNavForward()
00244 {
00245 navigate( forwardList, navList );
00246 }
00247
00248 void SourceNavPart::enableActions()
00249 {
00250 navForward->setEnabled( !forwardList.isEmpty() );
00251 navBack->setEnabled( !navList.isEmpty() );
00252 }
00253
00254 void SourceNavPart::cleanupList( AnchorList& list )
00255 {
00256 AnchorList::Iterator it = list.at( MAX_HISTORY );
00257 while ( it != list.end() ) {
00258 it = list.remove( it );
00259 }
00260 }
00261
00262 #include "sourcenav_part.moc"
00263