1 #include "calltreeview.h"
2 
3 #include <sdk.h>
4 #ifndef CB_PRECOMP
5     #include <wx/sizer.h>
6     #include <wx/xrc/xmlres.h>
7     #include <wx/menu.h>
8 
9     #include <sdk_events.h>
10     #include <manager.h>
11     #include <editormanager.h>
12     #include <logmanager.h>
13     #include <configmanager.h>
14     #include <cbeditor.h>
15 #endif
16 #include <cmath>
17 
18 #include "fortranproject.h"
19 #include "lineaddress.h"
20 
CallTreeToken(TokenFlat * tf,CallTreeToken * parent)21 CallTreeToken::CallTreeToken(TokenFlat* tf, CallTreeToken* parent)
22 {
23     m_TokenKind   = tf->m_TokenKind;
24     m_DisplayName = tf->m_DisplayName;
25     m_Name        = tf->m_Name;
26     m_Filename    = tf->m_Filename;
27     m_LineStart   = tf->m_LineStart;
28     m_LineEnd     = tf->m_LineEnd;
29     m_TokenAccess = tf->m_TokenAccess;
30 
31     m_pParent     = parent;
32 }
33 
CallTreeToken(TokenF * tf,CallTreeToken * parent)34 CallTreeToken::CallTreeToken(TokenF* tf, CallTreeToken* parent)
35 {
36     m_TokenKind   = tf->m_TokenKind;
37     m_DisplayName = tf->m_DisplayName;
38     m_Name        = tf->m_Name;
39     m_Filename    = tf->m_Filename;
40     m_LineStart   = tf->m_LineStart;
41     m_LineEnd     = tf->m_LineEnd;
42     m_TokenAccess = tf->m_TokenAccess;
43 
44     m_pParent     = parent;
45 }
46 
CTVData(TokenF * token)47 CTVData::CTVData(TokenF* token)
48 {
49     if (token)
50     {
51         CallTreeToken* cttok = static_cast<CallTreeToken*>(token);
52         m_DefFilename  = cttok->m_Filename;
53         m_DefLineStart = cttok->m_LineStart;
54         m_DefTokenKind = cttok->m_TokenKind;
55 
56         m_CallFilename = cttok->m_CallFilename;
57         m_CallLine     = cttok->m_CallLine;
58     }
59 }
60 
61 namespace
62 {
63     int idMenuRefreshTree = wxNewId();
64     int idMenuDoNotSort = wxNewId();
65     int idMenuSortAlphabetically = wxNewId();
66     int idMenuGoToProcedure = wxNewId();
67     int idMenuGoToCall = wxNewId();
68 };
69 
BEGIN_EVENT_TABLE(CallTreeView,wxPanel)70 BEGIN_EVENT_TABLE(CallTreeView, wxPanel)
71     EVT_TREE_ITEM_ACTIVATED(XRCID("treeCallTreeView"), CallTreeView::OnTreeDoubleClick)
72     EVT_TREE_ITEM_RIGHT_CLICK(XRCID("treeCallTreeView"), CallTreeView::OnTreeItemRightClick)
73 
74     EVT_MENU(idMenuRefreshTree, CallTreeView::OnRefreshTree)
75     EVT_MENU(idMenuDoNotSort, CallTreeView::OnChangeSort)
76     EVT_MENU(idMenuSortAlphabetically, CallTreeView::OnChangeSort)
77     EVT_MENU(idMenuGoToProcedure, CallTreeView::OnGoToProcedure)
78     EVT_MENU(idMenuGoToCall, CallTreeView::OnGoToCall)
79 END_EVENT_TABLE()
80 
81 CallTreeView::CallTreeView(wxWindow* parentWindow, FortranProject* forproj)
82 {
83     wxXmlResource::Get()->LoadPanel(this, parentWindow, _T("pnlCallTreeView"));
84     m_pTree = XRCCTRL(*this, "treeCallTreeView", wxTreeCtrl);
85 
86     int targetHeight = floor(16 * cbGetActualContentScaleFactor(*parentWindow));
87     m_pImgList = new FPImageList(targetHeight);
88     m_pTree->SetImageList(m_pImgList->GetImageList());
89 
90     m_pFortranProject = forproj;
91     m_IsCallTree = true;
92 }
93 
~CallTreeView()94 CallTreeView::~CallTreeView()
95 {
96     //dtor
97     delete m_pImgList;
98 }
99 
ShowCallTree(TokensArrayF * tokArr)100 void CallTreeView::ShowCallTree(TokensArrayF* tokArr)
101 {
102     RereadOptions();
103     m_IsCallTree = true;
104     m_pTree->DeleteAllItems();
105     wxTreeItemId root = m_pTree->AddRoot(_("Call Tree"));
106 
107     ShowCallTreeChildren(tokArr, root, 0);
108 }
109 
ShowCalledByTree(TokensArrayF * tokArr)110 void CallTreeView::ShowCalledByTree(TokensArrayF* tokArr)
111 {
112     RereadOptions();
113     m_IsCallTree = false;
114     m_pTree->DeleteAllItems();
115     wxTreeItemId root = m_pTree->AddRoot(_("Called-By Tree"));
116 
117     ShowCallTreeChildren(tokArr, root, 0);
118 }
119 
ShowCallTreeChildren(TokensArrayF * tokArr,wxTreeItemId & parent,int callLevel)120 void CallTreeView::ShowCallTreeChildren(TokensArrayF* tokArr, wxTreeItemId& parent, int callLevel)
121 {
122     callLevel += 1;
123     for (size_t i=0; i<tokArr->size(); i++)
124     {
125         wxTreeItemData* tidata = new CTVData(tokArr->Item(i));
126         int iind;
127         if (callLevel == 1)
128         {
129             // show for first level different image
130             if (m_IsCallTree)
131             {
132                 if (tokArr->Item(i)->m_TokenKind == tkFunction)
133                     iind = m_pImgList->GetImageIdx("function_call");
134                 else
135                     iind = m_pImgList->GetImageIdx("subroutine_call");
136             }
137             else // CalledBy tree
138             {
139                 if (tokArr->Item(i)->m_TokenKind == tkFunction)
140                     iind = m_pImgList->GetImageIdx("function_calledby");
141                 else
142                     iind = m_pImgList->GetImageIdx("subroutine_calledby");
143             }
144         }
145         else
146         {
147             iind = m_pImgList->GetTokenKindImageIdx(tokArr->Item(i));
148         }
149         wxTreeItemId addedId = InsertTreeItem(parent, tokArr->Item(i)->m_DisplayName, iind, tidata);
150 
151         ShowCallTreeChildren(&tokArr->Item(i)->m_Children, addedId, callLevel);
152         if (callLevel == 1)
153             m_pTree->Expand(addedId);
154     }
155 }
156 
InsertTreeItem(wxTreeItemId & parent,const wxString & displayName,int imageIdx,wxTreeItemData * tidata)157 wxTreeItemId CallTreeView::InsertTreeItem(wxTreeItemId& parent, const wxString& displayName, int imageIdx, wxTreeItemData* tidata)
158 {
159     if (!m_SortAlphabetically)
160         return m_pTree->AppendItem(parent, displayName, imageIdx, -1, tidata);
161 
162     wxTreeItemId insertAfter;
163     wxTreeItemIdValue cookie;
164     wxTreeItemId item = m_pTree->GetFirstChild(parent, cookie);
165 
166     while (item.IsOk())
167     {
168         wxString itemText = m_pTree->GetItemText(item);
169         if (displayName.CmpNoCase(itemText) >= 0)
170         {
171             insertAfter = item;
172         }
173         else
174         {
175             break;
176         }
177         item = m_pTree->GetNextChild(parent, cookie);
178     }
179 
180     wxTreeItemId addedId;
181     if (insertAfter.IsOk())
182         addedId = m_pTree->InsertItem(parent, insertAfter, displayName, imageIdx, -1, tidata);
183     else
184         addedId = m_pTree->InsertItem(parent, 0, displayName, imageIdx, -1, tidata);
185 
186     return addedId;
187 }
188 
OnTreeDoubleClick(wxTreeEvent & event)189 void CallTreeView::OnTreeDoubleClick(wxTreeEvent& event)
190 {
191     wxTreeItemId id = event.GetItem();
192     if (!id.IsOk())
193         return;
194 
195     CTVData* ctd = (CTVData*)m_pTree->GetItemData(id);
196     if (!ctd)
197         return;
198 
199     if (ctd->m_DefFilename == wxEmptyString)
200         return;
201 
202     GoToLine(ctd->m_DefFilename, ctd->m_DefLineStart);
203 }
204 
GoToLine(wxString & filename,unsigned int line)205 void CallTreeView::GoToLine(wxString& filename, unsigned int line)
206 {
207     TokenFlat token;
208     token.m_Filename = filename;
209     token.m_LineStart = line;
210     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
211     m_pFortranProject->GotoToken(&token, ed);
212 
213     ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
214     if (ed)
215     {
216         wxFocusEvent ev(wxEVT_SET_FOCUS);
217         ev.SetWindow(this);
218 #if wxCHECK_VERSION(3, 0, 0)
219         ed->GetControl()->GetEventHandler()->AddPendingEvent(ev);
220 #else
221         ed->GetControl()->AddPendingEvent(ev);
222 #endif
223     }
224 }
225 
ShowMenu(wxTreeItemId id,const wxPoint & pt,bool isFirstLevelItem)226 void CallTreeView::ShowMenu(wxTreeItemId id, const wxPoint& pt, bool isFirstLevelItem)
227 {
228     if ( !id.IsOk() )
229         return;
230 
231 #if wxUSE_MENUS
232     wxString caption;
233     wxMenu *menu=new wxMenu(wxEmptyString);
234 
235     if (isFirstLevelItem)
236     {
237         menu->Append(idMenuRefreshTree, _("&Refresh tree"));
238 
239         menu->AppendCheckItem(idMenuDoNotSort, _("Do not sort"));
240         menu->Check(idMenuDoNotSort, !m_SortAlphabetically);
241         menu->AppendCheckItem(idMenuSortAlphabetically, _("Sort alphabetically"));
242         menu->Check(idMenuSortAlphabetically, m_SortAlphabetically);
243     }
244     else
245     {
246         menu->Append(idMenuGoToProcedure, _("Go to &procedure"));
247         menu->Append(idMenuGoToCall, _("Go to &caller"));
248     }
249 
250     if (menu->GetMenuItemCount() != 0)
251         PopupMenu(menu);
252     delete menu; // Prevents memory leak
253 #endif // wxUSE_MENUS
254 }
255 
OnTreeItemRightClick(wxTreeEvent & event)256 void CallTreeView::OnTreeItemRightClick(wxTreeEvent& event)
257 {
258     wxTreeItemId idRoot = m_pTree->GetRootItem();
259     if (!idRoot.IsOk())
260         return;
261 
262     bool isFirstLevelItem = false;
263     wxTreeItemIdValue cookie;
264     wxTreeItemId item = m_pTree->GetFirstChild(idRoot, cookie);
265     while (item.IsOk())
266     {
267         if (item == event.GetItem())
268         {
269             isFirstLevelItem = true;
270             break;
271         }
272         item = m_pTree->GetNextChild(idRoot, cookie);
273     }
274 
275     m_pTree->SelectItem(event.GetItem());
276     ShowMenu(event.GetItem(), event.GetPoint(), isFirstLevelItem);
277 }
278 
OnRefreshTree(wxCommandEvent & event)279 void CallTreeView::OnRefreshTree(wxCommandEvent& event)
280 {
281     UpdateView();
282 }
283 
OnChangeSort(wxCommandEvent & event)284 void CallTreeView::OnChangeSort(wxCommandEvent& event)
285 {
286     if (event.GetId() == idMenuDoNotSort)
287         m_SortAlphabetically = !event.IsChecked();
288     else if (event.GetId() == idMenuSortAlphabetically)
289         m_SortAlphabetically = event.IsChecked();
290 
291     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
292     cfg->Write(_T("/calltree_sort_alphabetically"), m_SortAlphabetically);
293 
294     UpdateView();
295 }
296 
OnGoToProcedure(wxCommandEvent & event)297 void CallTreeView::OnGoToProcedure(wxCommandEvent& event)
298 {
299     wxTreeItemId id = m_pTree->GetSelection();
300     if (!id.IsOk())
301         return;
302 
303     CTVData* ctd = (CTVData*)m_pTree->GetItemData(id);
304     if (!ctd)
305         return;
306 
307     if (ctd->m_DefFilename == wxEmptyString)
308         return;
309 
310     GoToLine(ctd->m_DefFilename, ctd->m_DefLineStart);
311 }
312 
OnGoToCall(wxCommandEvent & event)313 void CallTreeView::OnGoToCall(wxCommandEvent& event)
314 {
315     wxTreeItemId id = m_pTree->GetSelection();
316     if (!id.IsOk())
317         return;
318 
319     CTVData* ctd = (CTVData*)m_pTree->GetItemData(id);
320     if (!ctd)
321         return;
322 
323     if (ctd->m_CallFilename == wxEmptyString)
324         return;
325 
326     GoToLine(ctd->m_CallFilename, ctd->m_CallLine);
327 }
328 
RereadOptions()329 void CallTreeView::RereadOptions()
330 {
331     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
332     m_SortAlphabetically = cfg->ReadBool(_("/calltree_sort_alphabetically"), true);
333 }
334 
UpdateView()335 void CallTreeView::UpdateView()
336 {
337 
338     wxTreeItemId id = m_pTree->GetSelection();
339     if (!id.IsOk())
340         return;
341 
342     CTVData* ctd = (CTVData*)m_pTree->GetItemData(id);
343     if (!ctd)
344         return;
345 
346     if (ctd->m_DefFilename == wxEmptyString)
347         return;
348 
349     TokenFlat token;
350     token.m_Filename = ctd->m_DefFilename;
351     token.m_LineStart = ctd->m_DefLineStart;
352     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
353     if (!ed)
354         return;
355     m_pFortranProject->GotoToken(&token, ed);
356 
357     ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
358     if (!ed)
359         return;
360     cbStyledTextCtrl* control = ed->GetControl();
361     const int pos = control->GetCurrentPos();
362     int curLine = control->LineFromPosition(pos);
363     int lineStart;
364     if (curLine == 0)
365         lineStart = 0;
366     else
367         lineStart = control->GetLineEndPosition(curLine-1);
368     int lineEnd = control->GetLineEndPosition(curLine);
369     int wordStart = control->FindText(lineStart, lineEnd, m_pTree->GetItemText(id), wxSCI_FIND_WHOLEWORD);
370     control->GotoPos(wordStart+1);
371 
372     m_pFortranProject->ShowCallTree(m_IsCallTree);
373 }
374