1 // ----------------------------------------------------------------------------
2 //  JumpTracker.cpp
3 // ----------------------------------------------------------------------------
4 #include <sdk.h> // Code::Blocks SDK
5 #include <configurationpanel.h>
6 #include <cbstyledtextctrl.h>
7 #include <projectmanager.h>
8 #include <editormanager.h>
9 #include <cbeditor.h>
10 #include <wx/xrc/xmlres.h>
11 
12 #include "Version.h"
13 #include "JumpTracker.h"
14 #include "JumpData.h"
15 
16 // now that we have ArrayOfJumpData declaration in scope we may finish the
17 // definition -- note that this expands into some C++
18 // code and so should only be compiled once (i.e., don't put this in the
19 // header, but into a source file or you will get linking errors)
20 #include <wx/arrimpl.cpp> // this is a magic incantation which must be done!
21 WX_DEFINE_OBJARRAY(ArrayOfJumpData);
22 
23 // ----------------------------------------------------------------------------
24 //  namespace
25 // ----------------------------------------------------------------------------
26 // Register the plugin with Code::Blocks.
27 // We are using an anonymous namespace so we don't litter the global one.
28 namespace
29 {
30     //-PluginRegistrant<JumpTracker> reg(_T("JumpTracker"));
31     int idMenuJumpView = wxNewId();
32     int idMenuJumpBack = wxNewId();
33     int idMenuJumpNext = wxNewId();
34     int idMenuJumpClear = wxNewId();
35     int idMenuJumpDump = wxNewId();
36 
37     int idToolJumpPrev = XRCID("idJumpPrev");
38     int idToolJumpNext = XRCID("idJumpNext");
39 }
40 
41 // ----------------------------------------------------------------------------
42 //  Events Table
43 // ----------------------------------------------------------------------------
44 // events handling
BEGIN_EVENT_TABLE(JumpTracker,cbPlugin)45 BEGIN_EVENT_TABLE(JumpTracker, cbPlugin)
46     // add any events you want to handle here
47 //    Now doing Connect() in BuildMenu()
48 //    EVT_MENU( idMenuJumpBack,  JumpTracker::OnMenuJumpBack)
49 //    EVT_MENU( idMenuJumpNext,  JumpTracker::OnMenuJumpNext)
50 //    EVT_MENU( idMenuJumpClear, JumpTracker::OnMenuJumpClear)
51 //    EVT_MENU( idMenuJumpDump,  JumpTracker::OnMenuJumpDump)
52 
53 END_EVENT_TABLE()
54 
55 // constructor
56 // ----------------------------------------------------------------------------
57 JumpTracker::JumpTracker()
58 // ----------------------------------------------------------------------------
59 {
60     // Make sure our resources are available.
61     // In the generated boilerplate code we have no resources but when
62     // we add some, it will be nice that this code is in place already ;)
63 //    if(!Manager::LoadResource(_T("JumpTracker.zip")))
64 //    {
65 //        NotifyMissingFile(_T("JumpTracker.zip"));
66 //    }
67 
68     m_bShuttingDown = false;
69     m_FilenameLast = wxEmptyString;
70     m_PosnLast = 0;
71     m_cursor = 0;
72     m_insertNext = maxJumpEntries;
73     m_ArrayOfJumpData.Clear();
74     m_bProjectClosing = false;
75     m_IsAttached = false;
76     m_bJumpInProgress = false;
77     m_bWrapJumpEntries = false;
78     m_pToolBar = 0;
79 }
80 // ----------------------------------------------------------------------------
~JumpTracker()81 JumpTracker::~JumpTracker()
82 // ----------------------------------------------------------------------------
83 {
84     // destructor
85 }
86 // ----------------------------------------------------------------------------
OnAttach()87 void JumpTracker::OnAttach()
88 // ----------------------------------------------------------------------------
89 {
90     // do whatever initialization you need for your plugin
91     // :NOTE: after this function, the inherited member variable
92     // m_IsAttached will be TRUE...
93     // You should check for it in other functions, because if it
94     // is FALSE, it means that the application did *not* "load"
95     // (see: does not need) this plugin...
96 
97     // --------------------------------------------------
98     // Initialize a debugging log/version control
99     // --------------------------------------------------
100     //    PlgnVersion plgnVersion;
101     //    #if LOGGING
102     //     wxLog::EnableLogging(true);
103     //     m_pPlgnLog = new wxLogWindow( Manager::Get()->GetAppWindow(), _T(" JumpTracker"),true,false);
104     //     wxLog::SetActiveTarget( m_pPlgnLog);
105     //     m_pPlgnLog->GetFrame()->SetSize(20,30,600,300);
106     //     LOGIT( _T("JT JumpTracker Logging Started[%s]"),plgnVersion.GetVersion().c_str());
107     //     m_pPlgnLog->Flush();
108     //    #endif
109 
110     //    // Set current plugin version
111     //    PluginInfo* pInfo = (PluginInfo*)(Manager::Get()->GetPluginManager()->GetPluginInfo(this));
112     //    pInfo->version = plgnVersion.GetVersion();
113 
114     m_bJumpInProgress = false;
115 
116     wxWindow* appWin = Manager::Get()->GetAppWindow();
117     appWin->PushEventHandler(this);
118     appWin->Connect(idMenuJumpBack, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpBack), 0, this);
119     appWin->Connect(idMenuJumpNext, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpNext), 0, this);
120     appWin->Connect(idMenuJumpClear, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpClear), 0, this);
121     appWin->Connect(idMenuJumpDump, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpDump), 0, this);
122 
123     appWin->Connect(idToolJumpPrev, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(JumpTracker::OnMenuJumpBack), 0, this);
124     appWin->Connect(idToolJumpNext, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(JumpTracker::OnMenuJumpNext), 0, this);
125     appWin->Connect(idToolJumpPrev, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(JumpTracker::OnUpdateUI), 0, this);
126     appWin->Connect(idToolJumpNext, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(JumpTracker::OnUpdateUI), 0, this);
127 
128 
129     // Codeblocks Events registration
130     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_UPDATE_UI   , new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnEditorUpdateEvent));
131    //-Manager::Get()->RegisterEventSink(cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnEditorActivated));
132     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_DEACTIVATED, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnEditorDeactivated));
133     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_CLOSE, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnEditorClosed));
134 
135     Manager::Get()->RegisterEventSink(cbEVT_APP_START_SHUTDOWN, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnStartShutdown));
136     // -- Project events
137     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnProjectActivatedEvent));
138     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_CLOSE, new cbEventFunctor<JumpTracker, CodeBlocksEvent>(this, &JumpTracker::OnProjectClosing));
139 
140 }
141 // ----------------------------------------------------------------------------
OnRelease(bool appShutDown)142 void JumpTracker::OnRelease(bool appShutDown)
143 // ----------------------------------------------------------------------------
144 {
145     // do de-initialization for your plugin
146     // if appShutDown is true, the plugin is unloaded because Code::Blocks is being shut down,
147     // which means you must not use any of the SDK Managers
148     // NOTE: after this function, the inherited member variable
149     // m_IsAttached will be FALSE...
150 
151     wxWindow* appWin = Manager::Get()->GetAppWindow();
152 
153     //If appShutdown leave the event handler, else wxWidgets asserts on linux
154     if (not appShutDown)
155         appWin->RemoveEventHandler(this); //2017/11/23 stop uninstall crash 2017/12/6 crashes linux
156 
157     // Free JumpData memory
158     wxCommandEvent evt;
159     OnMenuJumpClear(evt);
160 
161     appWin->Disconnect(idMenuJumpBack, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpBack), 0, this);
162     appWin->Disconnect(idMenuJumpNext, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpNext), 0, this);
163     appWin->Disconnect(idMenuJumpClear, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpClear), 0, this);
164     appWin->Disconnect(idMenuJumpDump, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(JumpTracker::OnMenuJumpDump), 0, this);
165 
166     appWin->Disconnect(idToolJumpPrev, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(JumpTracker::OnMenuJumpBack), 0, this);
167     appWin->Disconnect(idToolJumpNext, wxEVT_COMMAND_TOOL_CLICKED, wxCommandEventHandler(JumpTracker::OnMenuJumpNext), 0, this);
168     appWin->Disconnect(idToolJumpPrev, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(JumpTracker::OnUpdateUI), 0, this);
169     appWin->Disconnect(idToolJumpNext, wxEVT_UPDATE_UI, wxUpdateUIEventHandler(JumpTracker::OnUpdateUI), 0, this);
170 
171     Manager::Get()->    Manager::Get()->RemoveAllEventSinksFor(this); //2017/11/23
172 
173 }
174 // ----------------------------------------------------------------------------
BuildMenu(wxMenuBar * menuBar)175 void JumpTracker::BuildMenu(wxMenuBar* menuBar)
176 // ----------------------------------------------------------------------------
177 {
178     //The application is offering its menubar for your plugin,
179     //to add any menu items you want...
180     //Append any items you need in the menu...
181     //NOTE: Be careful in here... The application's menubar is at your disposal.
182     //-NotImplemented(_T("JumpTracker::BuildMenu()"));
183 
184      // insert menu items
185     wxMenu* jump_submenu = new wxMenu;
186     jump_submenu->Append(idMenuJumpBack,  _("Jump Back"),   _("Jump back to previous ed position"));
187     jump_submenu->Append(idMenuJumpNext,  _("Jump Frwd"),   _("Jump to next ed position"));
188     jump_submenu->Append(idMenuJumpClear, _("Jump Clear"),  _("Jump Clear History"));
189     #if defined(LOGGING)
190     jump_submenu->Append(idMenuJumpDump,  _("Jump Dump"),   _("Jump Dump Array"));
191     #endif
192 
193     int viewPos = menuBar->FindMenu(_("&View"));
194 
195     if ( viewPos == wxNOT_FOUND )
196         return;
197 
198     // Place the menu inside the View Menu
199     wxMenu* pViewMenu = menuBar->GetMenu(viewPos);
200     pViewMenu->Append(idMenuJumpView, _("Jump"), jump_submenu, _("Jump"));
201 
202 }
203 // ----------------------------------------------------------------------------
BuildModuleMenu(const ModuleType,wxMenu *,const FileTreeData *)204 void JumpTracker::BuildModuleMenu(const ModuleType /*type*/, wxMenu* /*menu*/, const FileTreeData* /*data*/)
205 // ----------------------------------------------------------------------------
206 {
207     //Some library module is ready to display a pop-up menu.
208     //Check the parameter \"type\" and see which module it is
209     //and append any items you need in the menu...
210     //TIP: for consistency, add a separator as the first item...
211     //-NotImplemented(_T("JumpTracker::BuildModuleMenu()"));
212 }
213 // ----------------------------------------------------------------------------
BuildToolBar(wxToolBar * toolBar)214 bool JumpTracker::BuildToolBar(wxToolBar* toolBar)
215 // ----------------------------------------------------------------------------
216 {
217     m_pToolBar = toolBar;
218 
219     //The application is offering its toolbar for your plugin,
220     //to add any toolbar items you want...
221     //Append any items you need on the toolbar...
222     //-NotImplemented(_T("JumpTracker::BuildToolBar()"));
223 
224     // return true if you add toolbar items
225     return false;
226 }
227 // ----------------------------------------------------------------------------
OnEditorUpdateEvent(CodeBlocksEvent & event)228 void JumpTracker::OnEditorUpdateEvent(CodeBlocksEvent& event)
229 // ----------------------------------------------------------------------------
230 {
231     event.Skip();
232 
233     if ( m_bShuttingDown )
234         return;
235     if (m_bJumpInProgress)
236         return;
237 
238     cbEditor* ed =  Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
239     if(not ed)
240         return;
241 
242     wxString edFilename = ed->GetFilename();
243     cbStyledTextCtrl* edstc = ed->GetControl();
244     if(edstc->GetCurrentLine() == wxSCI_INVALID_POSITION)
245         return;
246 
247     long edLine = edstc->GetCurrentLine();
248     long edPosn = edstc->GetCurrentPos();
249 
250     long topLine = edstc->GetFirstVisibleLine();
251     long scnSize = edstc->LinesOnScreen();
252     long botLine = (topLine+scnSize)-1; // In keeping with top line == 0 origin
253     botLine = (botLine < 0) ? 0 : botLine;
254     botLine = (botLine > edstc->GetLineCount()) ? edstc->GetLineCount() : botLine;
255 
256     #if defined(LOGGING)
257     //LOGIT( _T("JT OnEditorUpdateEvent Filename[%s] line[%ld] pos[%ld] "), edFilename.c_str(), edLine, edPosn);
258     //LOGIT( _T("JT \ttopLine[%ld] botLine[%ld] OnScrn[%ld] "), topLine, botLine, edstc->LinesOnScreen());
259     #endif
260 
261     // New editor activated?
262     if (m_FilenameLast not_eq edFilename)
263     {
264         m_PosnLast = edPosn;
265         m_FilenameLast = edFilename;
266         //if ( m_cursor not_eq JumpDataContains(edFilename, edPosn) )
267             JumpDataAdd(edFilename, edPosn, edLine);
268     }
269 
270     // If new line within half screen of old line, don't record current line
271     //-long lineLast = edstc->LineFromPosition(m_PosnLast);
272 
273     //-bool bIsVis = ( edstc->GetLineVisible(lineLast) ); doesnt work
274     //-if ( (lineLast >= topLine) &&                      doesnt work
275     //-    (lineLast < botLine) )
276     //-int halfPageSize = edstc->LinesOnScreen()>>1;
277     //-if ( halfPageSize > abs(edLine - lineLast))
278     //-    ;//return;
279 
280     // if user has not moved cursor, ignore this update
281     if (m_PosnLast == edPosn)
282         return;
283 
284     // record new posn
285     m_PosnLast = edPosn;
286     m_FilenameLast = edFilename;
287     //if ( m_cursor not_eq JumpDataContains(edFilename, edPosn) )
288         JumpDataAdd(edFilename, edPosn, edLine);
289 
290     return;
291 }//OnEditorUpdateEvent
292 // ----------------------------------------------------------------------------
OnEditorActivated(CodeBlocksEvent & event)293 void JumpTracker::OnEditorActivated(CodeBlocksEvent& event)
294 // ----------------------------------------------------------------------------
295 {
296     // Record this activation event and place activation in history
297     event.Skip();
298 
299     if (m_bShuttingDown) return;
300     if (not IsAttached()) return;
301 
302     // Don't record closing editor activations
303     if (m_bProjectClosing)
304         return;
305 
306     EditorBase* eb = event.GetEditor();
307     wxString edFilename = eb->GetFilename();
308     cbEditor* cbed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(eb);
309 
310     if (not cbed)
311     {
312         // Since wxAuiNotebook added, there's no cbEditor associated during
313         // an initial cbEVT_EDITOR_ACTIVATED event. So we ignore the inital
314         // call and get OnEditorOpened() to re-issue OnEditorActivated() when
315         // it does have a cbEditor, but no cbProject associated;
316         #if defined(LOGGING)
317         LOGIT( _T("JT [OnEditorActivated ignored:no cbEditor[%s]"), edFilename.c_str());
318         #endif
319         return;
320     }
321 
322     #if defined(LOGGING)
323     LOGIT( _T("JT Editor Activated[%s]"), eb->GetShortName().c_str() );
324     #endif
325 
326     cbStyledTextCtrl* edstc = cbed->GetControl();
327     if(edstc->GetCurrentLine() == wxSCI_INVALID_POSITION)
328         return;
329 
330     long edPosn = edstc->GetCurrentPos();
331     //if ( m_cursor not_eq JumpDataContains(edFilename, edPosn) )
332         JumpDataAdd(edFilename, edPosn, edstc->GetCurrentLine());
333     return;
334 }//OnEditorActivated
335 // ----------------------------------------------------------------------------
OnEditorDeactivated(CodeBlocksEvent & event)336 void JumpTracker::OnEditorDeactivated(CodeBlocksEvent& event)
337 // ----------------------------------------------------------------------------
338 {
339     // Record this deactivation event and place deactivation location in history
340     event.Skip();
341 
342     if (m_bShuttingDown) return;
343     if (not IsAttached()) return;
344 
345     // Don't record closing editor deactivations
346     if (m_bProjectClosing)
347         return;
348 
349     if ( Manager::Get()->GetProjectManager()->IsLoading() )
350         return;
351 
352     EditorBase* eb = event.GetEditor();
353     wxString edFilename = eb->GetFilename();
354     cbEditor* cbed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(eb);
355 
356     if (not cbed)
357     {
358         // We ignore events with no associated editor
359         #if defined(LOGGING)
360         LOGIT( _T("JT [OnEditorDeactivated ignored:no cbEditor[%s]"), edFilename.c_str());
361         #endif
362         return;
363     }
364 
365     #if defined(LOGGING)
366     LOGIT( _T("JT Editor DeActivated[%s]"), eb->GetShortName().c_str() );
367     #endif
368 
369     cbStyledTextCtrl* edstc = cbed->GetControl();
370     if(edstc->GetCurrentLine() == wxSCI_INVALID_POSITION)
371         return;
372 
373     long edPosn = edstc->GetCurrentPos();
374     //if ( m_cursor not_eq JumpDataContains(edFilename, edPosn) )
375         JumpDataAdd(edFilename, edPosn, edstc->GetCurrentLine());
376     return;
377 }//OnEditorDeactivated
378 // ----------------------------------------------------------------------------
OnEditorClosed(CodeBlocksEvent & event)379 void JumpTracker::OnEditorClosed(CodeBlocksEvent& event)
380 // ----------------------------------------------------------------------------
381 {
382     // clear this editor out of our arrays and pointers
383 
384     //_NOTE: using Manager::Get->GetEditorManager()->GetEditor... etc will
385     //      fail in this codeblocks event.
386     //      The cbEditors are nolonger available
387 
388     event.Skip();
389 
390     if (not IsAttached())
391         return;
392 
393     wxString filePath = event.GetString();
394 
395     #if defined(LOGGING)
396         EditorBase* eb = event.GetEditor();
397         LOGIT( _T("JT OnEditorClosed Eb[%p][%s]"), eb, eb->GetShortName().c_str() );
398     #endif
399 
400 
401     for (int ii=m_ArrayOfJumpData.GetCount()-1; ii > -1; --ii)
402     {
403         if (m_ArrayOfJumpData[ii].GetFilename() == filePath)
404             m_ArrayOfJumpData.RemoveAt(ii);
405         if (((int)m_ArrayOfJumpData.GetCount()-1) < m_cursor)
406             m_cursor = GetPreviousIndex(m_cursor);
407         if (((int)m_ArrayOfJumpData.GetCount()-1) < m_insertNext)
408             m_insertNext = GetPreviousIndex(m_insertNext);
409     }
410     #if defined(LOGGING)
411     wxCommandEvent evt;
412     OnMenuJumpDump(evt);
413     #endif
414 
415 }//OnEditorClosed
416 
417 // ----------------------------------------------------------------------------
OnStartShutdown(CodeBlocksEvent &)418 void JumpTracker::OnStartShutdown(CodeBlocksEvent& /*event*/)
419 // ----------------------------------------------------------------------------
420 {
421     m_bShuttingDown = true;
422 }
423 // ----------------------------------------------------------------------------
OnProjectClosing(CodeBlocksEvent & event)424 void JumpTracker::OnProjectClosing(CodeBlocksEvent& event)
425 // ----------------------------------------------------------------------------
426 {
427     event.Skip();
428 
429     // This hook occurs before the editors are closed. That allows
430     // us to reference CB project and editor related data before CB
431     // deletes it all.
432 
433     if (m_bShuttingDown) return;
434     if (not IsAttached()) return;
435 
436     //cbProject* pProject = event.GetProject();
437     //if (not pProject) return; //It happens!
438 
439     m_bProjectClosing = true;
440 }
441 // ----------------------------------------------------------------------------
OnProjectActivatedEvent(CodeBlocksEvent & event)442 void JumpTracker::OnProjectActivatedEvent(CodeBlocksEvent& event)
443 // ----------------------------------------------------------------------------
444 {
445     event.Skip();
446 
447     // NB: EVT_PROJECT_ACTIVATE is occuring before EVT_PROJECT_OPEN
448     // NB: EVT_EDITOR_ACTIVATE event occur before EVT_PROJECT_ACTIVATE or EVT_PROJECT_OPEN
449 
450     event.Skip();
451     if (m_bShuttingDown) return;
452     if (not IsAttached()) return;
453 
454     // Previous project was closing
455     if (m_bProjectClosing)
456         m_bProjectClosing = false;
457 
458 }//OnProjectActivatedEvent
459 // ----------------------------------------------------------------------------
OnUpdateUI(wxUpdateUIEvent & event)460 void JumpTracker::OnUpdateUI(wxUpdateUIEvent& event)
461 // ----------------------------------------------------------------------------
462 {
463     int count = m_ArrayOfJumpData.GetCount();
464     //-m_pToolBar->EnableTool(idToolJumpNext, count > 0 /*&& m_cursor != m_insertNext - 1*/);
465     //-m_pToolBar->EnableTool(idToolJumpPrev, count > 0 /*&& m_cursor != m_insertNext*/);
466 
467     // If not  wrapping && we're about to advance into the insert index, diable
468     bool enableNext = (count > 0);
469     if (not m_bWrapJumpEntries)
470         if (m_cursor == m_insertNext)
471             enableNext = false;
472 
473     bool enablePrev = count > 0;
474     // If not wrapping && we're about to backup into the insert index, disable
475     if (not m_bWrapJumpEntries)
476         if (GetPreviousIndex(m_cursor) == m_insertNext)
477             enablePrev = false;
478 
479     m_pToolBar->EnableTool( idToolJumpNext, enableNext);
480     m_pToolBar->EnableTool( idToolJumpPrev, enablePrev);
481 
482     event.Skip();
483 }
484 // ----------------------------------------------------------------------------
JumpDataAdd(const wxString & filename,const long posn,const long lineNum)485 void JumpTracker::JumpDataAdd(const wxString& filename, const long posn, const long lineNum)
486 // ----------------------------------------------------------------------------
487 {
488     // Do not record old jump locations when a jump is in progress
489     // Caused by activating an editor inside the jump routines
490     if (m_bJumpInProgress)
491         return;
492 
493     // Dont record position if line number is < 1 since a newly loaded
494     // file always reports an event for line 0
495      if (lineNum < 1)       // user requested feature 2010/06/1
496         return;
497 
498     // if current entry is identical edit line, just update position
499     if ( JumpDataContains(m_cursor,filename, posn))
500     {
501         m_ArrayOfJumpData[m_cursor].SetPosition(posn);
502         return;
503     }
504     if ( JumpDataContains(GetPreviousIndex(m_insertNext), filename, posn))
505     {
506         m_ArrayOfJumpData[GetPreviousIndex(m_insertNext)].SetPosition(posn);
507         return;
508     }
509 
510     //
511     // record new jump entry
512     //
513 
514     size_t kount = m_ArrayOfJumpData.GetCount();
515     if ( m_insertNext > maxJumpEntries-1 )
516             m_insertNext = 0;
517 
518     #if defined(LOGGING)
519     LOGIT( _T("JT JumpDataAdd[%s][%ld][%d]"), filename.c_str(), posn, m_insertNext);
520     #endif
521 
522     if ( kount == maxJumpEntries )
523     {   //remove last item in the array
524         m_ArrayOfJumpData.RemoveAt(maxJumpEntries-1);
525     }
526 
527    //initialize new item
528     m_insertNext = GetNextIndex(m_insertNext);
529     m_ArrayOfJumpData.Insert(new JumpData(filename, posn), m_insertNext);
530     m_cursor = m_insertNext;
531 
532     #if defined(LOGGING)
533     wxCommandEvent evt;
534     OnMenuJumpDump(evt);
535     #endif
536     return;
537 }
538 // ----------------------------------------------------------------------------
FindJumpDataContaining(const wxString & filename,const long posn)539 int JumpTracker::FindJumpDataContaining(const wxString& filename, const long posn)
540 // ----------------------------------------------------------------------------
541 {
542     size_t kount = m_ArrayOfJumpData.GetCount();
543     if (not kount) return wxNOT_FOUND;
544 
545     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(filename);
546     if (not ed) return wxNOT_FOUND;
547 
548     cbStyledTextCtrl* pstc = ed->GetControl();
549     if (not pstc) return wxNOT_FOUND;
550 
551     int halfPageSize = pstc->LinesOnScreen()>>1;
552 
553     // search from array insertion point so we check last entered entry first
554     size_t j = m_insertNext;
555     for (size_t i=0; i<kount; ++i, ++j)
556     {
557         j = GetPreviousIndex(j);
558         JumpData& jumpData = m_ArrayOfJumpData.Item(j);
559         if ( jumpData.GetFilename() not_eq filename )
560             continue;
561         long jumpLine = pstc->LineFromPosition(jumpData.GetPosition());
562         long currLine = pstc->LineFromPosition(posn);
563         if ( halfPageSize > abs(jumpLine - currLine))
564            return j;
565     }
566 
567     return wxNOT_FOUND;
568 }
569 // ----------------------------------------------------------------------------
JumpDataContains(const int indx,const wxString & filename,const long posn)570 bool JumpTracker::JumpDataContains(const int indx, const wxString& filename, const long posn)
571 // ----------------------------------------------------------------------------
572 {
573     size_t kount = m_ArrayOfJumpData.GetCount();
574     if (not kount) return false;
575 
576     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(filename);
577     if (not ed) return false;
578 
579     cbStyledTextCtrl* pstc = ed->GetControl();
580     if (not pstc) return false;
581 
582     int halfPageSize = pstc->LinesOnScreen()>>1;
583 
584     JumpData& jumpData = m_ArrayOfJumpData.Item(indx);
585     if ( jumpData.GetFilename() not_eq filename )
586         return false;
587     long jumpLine = pstc->LineFromPosition(jumpData.GetPosition());
588     long currLine = pstc->LineFromPosition(posn);
589     if ( halfPageSize > abs(jumpLine - currLine))
590        return true;
591 
592 
593     return false;
594 }
595 // ----------------------------------------------------------------------------
OnMenuJumpBack(wxCommandEvent &)596 void JumpTracker::OnMenuJumpBack(wxCommandEvent &/*event*/)
597 // ----------------------------------------------------------------------------
598 {
599     #if defined(LOGGING)
600     LOGIT( _T("JT [%s]"), _T("OnMenuJumpBack"));
601     #endif
602 
603     int knt = m_ArrayOfJumpData.GetCount();
604     if (0 == knt)
605         return;
606 
607     // If not wrapping && we're about to backup into the insert index, return
608     if (not m_bWrapJumpEntries)
609         if (GetPreviousIndex(m_cursor) == m_insertNext)
610             return;
611 
612 
613     EditorManager* edmgr = Manager::Get()->GetEditorManager();
614     EditorBase* eb = edmgr->GetActiveEditor();
615     cbEditor* cbed = (eb ? edmgr->GetBuiltinEditor(eb) : nullptr);
616 
617     if (not cbed)
618         return;
619 
620     m_bJumpInProgress = true;
621 
622     //-long activeEdLine = 0;
623     long activeEdPosn = 0;
624     wxString activeEdFilename = wxEmptyString;
625     if (cbed)
626     {
627         //-activeEdLine = cbed->GetControl()->GetCurrentLine();
628         activeEdPosn = cbed->GetControl()->GetCurrentPos();
629         activeEdFilename = cbed->GetFilename();
630     }
631 
632     // if active editor line == m_cursor, back up the m_cursor
633     // until finding an entry that does not match this m_cursor entry.
634     // else
635     // find the previous m_insertNext entry that does not match this editor or line number
636 
637     do {    // find the previous appropriate jump position
638 
639         // if current cursor position is also current editor and line,
640         // return the previous m_cursor entry
641         if ( JumpDataContains(m_cursor, cbed->GetFilename(), activeEdPosn))
642         {
643             // FIXME (ph#): a dead editor can be returned here
644             m_cursor = GetPreviousIndex(m_cursor);
645         }
646         else    //find an entry (backward) from the insertion point
647         {
648             int idx = m_insertNext;
649 
650             for (int ii=0; ii<knt; ++ii)
651             {
652                 idx = GetPreviousIndex(idx);
653                 if ( idx == wxNOT_FOUND)
654                     break;
655                 JumpData& jumpdata = m_ArrayOfJumpData[idx];
656                 if (not edmgr->IsOpen(jumpdata.GetFilename()))
657                     continue;
658                 // skip entry when same as current position
659                 if ( JumpDataContains(idx, activeEdFilename, activeEdPosn))
660                     continue;
661                 else
662                 {
663                     m_cursor = idx;
664                     break; //for
665                 }
666             }//for
667         }//else
668 
669         JumpData& jumpData = m_ArrayOfJumpData[m_cursor];
670         wxString edFilename = jumpData.GetFilename();
671         long edPosn = jumpData.GetPosition();
672         #if defined(LOGGING)
673         LOGIT( _T("JT OnMenuJumpBack [%s][%ld]curs[%d]"), edFilename.wx_str(), edPosn, m_cursor);
674         #endif
675 
676         // activate editor
677         eb = edmgr->GetEditor(edFilename);
678         if (not eb) break;
679 
680         edmgr->SetActiveEditor(eb); // cause a cbEVT_EditorActivated event
681         // position to editor line
682         cbed = edmgr->GetBuiltinEditor(eb);
683         if (not cbed) break;
684 
685         cbed->GotoLine(cbed->GetControl()->LineFromPosition(edPosn)); //center on scrn
686         cbed->GetControl()->GotoPos(edPosn);
687 
688     }while(0);
689 
690     #if defined(LOGGING)
691     LOGIT( _T("JT [%s]"), _T("END OnMenuJumpBack"));
692     wxCommandEvent evt;
693     OnMenuJumpDump(evt);
694     #endif
695 
696     m_bJumpInProgress = false;
697     return;
698 }
699 // ----------------------------------------------------------------------------
OnMenuJumpNext(wxCommandEvent &)700 void JumpTracker::OnMenuJumpNext(wxCommandEvent &/*event*/)
701 // ----------------------------------------------------------------------------
702 {
703     #if defined(LOGGING)
704     LOGIT( _T("JT [%s]"), _T("OnMenuJumpNext"));
705     #endif
706 
707     int knt = m_ArrayOfJumpData.GetCount();
708     if (0 == knt)
709         return;
710 
711     // If not  wrapping && we're about to advance into the insert index, return
712     //-if (GetNextIndex(m_cursor) == m_insertNext)
713     if (not m_bWrapJumpEntries)
714         if (m_cursor == m_insertNext)
715             return;
716 
717 
718     EditorManager* edmgr = Manager::Get()->GetEditorManager();
719     EditorBase* eb = edmgr->GetActiveEditor();
720     cbEditor* cbed = (eb ? edmgr->GetBuiltinEditor(eb) : nullptr);
721 
722     if (not cbed)
723         return;
724 
725     m_bJumpInProgress = true;
726 
727     //-long activeEdLine = 0;
728     long activeEdPosn = 0;
729     wxString activeEdFilename = wxEmptyString;
730     if (cbed)
731     {
732         //-activeEdLine = cbed->GetControl()->GetCurrentLine();
733         activeEdPosn = cbed->GetControl()->GetCurrentPos();
734         activeEdFilename = cbed->GetFilename();
735     }
736 
737     // if active editor line == m_cursor, advance up the m_cursor
738     // until finding an entry that does not match this m_cursor entry.
739     // else
740     // find the next m_insertNext entry that does not match this editor or line number
741 
742     do {    // find the next appropriate jump position
743 
744         // if current cursor position is also current editor and line,
745         // return the next m_cursor entry
746         if ( JumpDataContains(m_cursor, cbed->GetFilename(), activeEdPosn))
747         {
748             m_cursor = GetNextIndex(m_cursor);
749         }
750         else    //find an entry (forward) from the insertion point
751         {
752             int idx = m_insertNext;
753 
754             for (int ii=0; ii<knt; ++ii)
755             {
756                 idx = GetNextIndex(idx);
757                 if ( idx == wxNOT_FOUND)
758                     break;
759                 JumpData& jumpdata = m_ArrayOfJumpData[idx];
760                 if (not edmgr->IsOpen(jumpdata.GetFilename()))
761                     continue;
762 
763                 if ( not JumpDataContains(idx, activeEdFilename, activeEdPosn))
764                 {
765                     m_cursor = idx;
766                     break; //for
767                 }
768             }//for
769         }
770 
771         JumpData& jumpData = m_ArrayOfJumpData[m_cursor];
772         wxString edFilename = jumpData.GetFilename();
773         long edPosn = jumpData.GetPosition();
774         #if defined(LOGGING)
775         LOGIT( _T("JT OnMenuJumpBack [%s][%ld]curs[%d]"), edFilename.wx_str(), edPosn, m_cursor);
776         #endif
777 
778         // activate editor
779         eb = edmgr->GetEditor(edFilename);
780         if (not eb) break;
781 
782         edmgr->SetActiveEditor(eb); // cause a cbEVT_EditorActivated event
783         // position to editor line
784         cbed = edmgr->GetBuiltinEditor(eb);
785         if (not cbed) break;
786 
787         cbed->GotoLine(cbed->GetControl()->LineFromPosition(edPosn)); //center on scrn
788         cbed->GetControl()->GotoPos(edPosn);
789 
790     }while(0);
791 
792     #if defined(LOGGING)
793     LOGIT( _T("JT [%s]"), _T("END OnMenuJumpBack"));
794     wxCommandEvent evt;
795     OnMenuJumpDump(evt);
796     #endif
797 
798     m_bJumpInProgress = false;
799     return;
800 }
801 // ----------------------------------------------------------------------------
OnMenuJumpClear(wxCommandEvent &)802 void JumpTracker::OnMenuJumpClear(wxCommandEvent &/*event*/)
803 // ----------------------------------------------------------------------------
804 {
805     m_insertNext = m_cursor = maxJumpEntries;
806     m_ArrayOfJumpData.Clear();
807 }
808 // ----------------------------------------------------------------------------
OnMenuJumpDump(wxCommandEvent &)809 void JumpTracker::OnMenuJumpDump(wxCommandEvent &/*event*/)
810 // ----------------------------------------------------------------------------
811 {
812     #if defined(LOGGING)
813     if (not m_ArrayOfJumpData.GetCount())
814         LOGIT( _T("JumpDump Empty"));
815 
816     for (size_t count = 0; count < m_ArrayOfJumpData.GetCount(); ++count)
817     {
818         JumpData& jumpData = m_ArrayOfJumpData.Item(count);
819         wxString edFilename = jumpData.GetFilename();
820         long edLine = wxNOT_FOUND;
821         long edPosn = jumpData.GetPosition();
822         cbStyledTextCtrl* pstc = 0;
823         cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(edFilename);
824         if ( ed )
825         {
826             pstc = ed->GetControl();
827             if (not pstc) pstc = 0;
828             if (pstc)
829             edLine = pstc->LineFromPosition(jumpData.GetPosition());
830             edLine +=1; //editors are 0 origin
831         }
832 
833         wxString msg = wxString::Format(_T("[%d][%s][%ld][%ld]"), count, edFilename.c_str(), edPosn, edLine);
834         if (count == (size_t)m_cursor)
835             msg.Append(_T("<--c"));
836         if (count == (size_t)m_insertNext)
837             msg.Append(_T("<--i"));
838         LOGIT( msg );
839     }
840 
841     #endif
842 }
843 // ----------------------------------------------------------------------------
SetWrapJumpEntries(const bool tf)844 void JumpTracker::SetWrapJumpEntries(const bool tf)
845 // ----------------------------------------------------------------------------
846 {
847     m_bWrapJumpEntries = tf;
848 }
849 // ----------------------------------------------------------------------------
GetPreviousIndex(const int idx)850 int JumpTracker::GetPreviousIndex(const int idx)
851 // ----------------------------------------------------------------------------
852 {
853     int prev = idx;
854     int knt = m_ArrayOfJumpData.GetCount();
855     prev--;
856     if ( prev < 0 )
857         prev = knt-1;
858     if ( prev < 0 )
859         prev = 0;
860     return prev;
861 }
862 // ----------------------------------------------------------------------------
GetNextIndex(const int idx)863 int JumpTracker::GetNextIndex(const int idx)
864 // ----------------------------------------------------------------------------
865 {
866     int next = idx;
867     int knt = m_ArrayOfJumpData.GetCount();
868     next++;
869     if ( next > (knt-1) )
870         next = 0;
871     return next;
872 }
873