1 /*
2  * This file is part of the FortranProject plugin for Code::Blocks IDE
3  * and licensed under the GNU General Public License, version 3
4  * http://www.gnu.org/licenses/gpl-3.0.html
5  *
6  * Author: Darius Markauskas
7  *
8  */
9 
10 #include <sdk.h> // Code::Blocks SDK
11 #ifndef CB_PRECOMP
12     #include <wx/filename.h>
13     #include <wx/tokenzr.h>
14     #include <wx/gdicmn.h>
15     #include <wx/xrc/xmlres.h>
16     #include <wx/event.h>
17     #include <wx/menu.h>
18     #include <wx/toolbar.h>
19     #include <wx/choicdlg.h>
20 
21     #include <configurationpanel.h>
22     #include <manager.h>
23     #include <ccmanager.h>
24     #include <editorcolourset.h>
25     #include <editormanager.h>
26     #include <logmanager.h>
27     #include <projectmanager.h>
28     #include <cbstyledtextctrl.h>
29     #include <projectloader_hooks.h>
30     #include <editor_hooks.h>
31     #include <cbeditor.h>
32 #endif
33 #include <vector>
34 
35 #include "fortranproject.h"
36 #include "fpoptionsdlg.h"
37 #include "fpoptionsprjdlg.h"
38 #include "jumptracker.h"
39 #include "changecase.h"
40 #include "tab2space.h"
41 #include "docblock.h"
42 #include "formatindent.h"
43 #include "bindto.h"
44 #include "ccsmartfilter.h"
45 
46 // this auto-registers the plugin
47 namespace
48 {
49     PluginRegistrant<FortranProject> reg(_T("FortranProject"));
50 }
51 
52 
53 int idGotoDeclaration      = wxNewId();
54 int idCodeCompleteTimer    = wxNewId();
55 int idMenuJump             = wxNewId();
56 int idMenuGotoDeclaration  = wxNewId();
57 int idMenuJumpBack         = wxNewId();
58 int idMenuJumpHome         = wxNewId();
59 int idMenuJumpForward      = wxNewId();
60 int idViewSymbolsBrowser   = wxNewId();
61 int idMenuGenerateMakefile = wxNewId();
62 int idMenuChangeCase       = wxNewId();
63 int idMenuTab2Space        = wxNewId();
64 int idMenuFormatIndent     = wxNewId();
65 int idMenuBindTo           = wxNewId();
66 int idReparseEditorTimer   = wxNewId();
67 int idShowCallTree         = wxNewId();
68 int idShowCalledByTree     = wxNewId();
69 int idViewCallTree         = wxNewId();
70 
71 #ifndef __WXMSW__
72 int idMenuEditPaste = XRCID("idEditPaste");
73 #endif
74 
BEGIN_EVENT_TABLE(FortranProject,cbCodeCompletionPlugin)75 BEGIN_EVENT_TABLE(FortranProject, cbCodeCompletionPlugin)
76     EVT_UPDATE_UI(idViewSymbolsBrowser, FortranProject::OnUpdateUI)
77     EVT_UPDATE_UI(idViewCallTree, FortranProject::OnUpdateUICallTree)
78     EVT_MENU(idMenuGotoDeclaration, FortranProject::OnGotoDeclaration)
79     EVT_MENU(idMenuJumpBack, FortranProject::OnJumpBack)
80     EVT_MENU(idMenuJumpHome, FortranProject::OnJumpHome)
81     EVT_MENU(idMenuJumpForward, FortranProject::OnJumpForward)
82     EVT_MENU(idGotoDeclaration, FortranProject::OnGotoDeclaration)
83     EVT_MENU(idViewSymbolsBrowser, FortranProject::OnViewWorkspaceBrowser)
84     EVT_MENU(idViewCallTree, FortranProject::OnShowCallTreeView)
85     EVT_MENU(idMenuGenerateMakefile, FortranProject::OnGenerateMakefile)
86     EVT_MENU(idMenuChangeCase, FortranProject::OnChangeCase)
87     EVT_MENU(idMenuTab2Space, FortranProject::OnTab2Space)
88     EVT_MENU(idMenuFormatIndent, FortranProject::OnFormatIndent)
89     EVT_MENU(idMenuBindTo, FortranProject::OnBindTo)
90     EVT_MENU(idShowCallTree, FortranProject::OnShowCallTree)
91     EVT_MENU(idShowCalledByTree, FortranProject::OnShowCallTree)
92 #ifndef __WXMSW__
93     EVT_MENU(idMenuEditPaste, FortranProject::OnMenuEditPaste)
94 #endif
95     EVT_TIMER(idReparseEditorTimer, FortranProject::OnReparseEditorTimer)
96     EVT_TOOL(XRCID("idFortProjBack"), FortranProject::OnJumpBack)
97     EVT_TOOL(XRCID("idFortProjHome"), FortranProject::OnJumpHome)
98     EVT_TOOL(XRCID("idFortProjForward"), FortranProject::OnJumpForward)
99 END_EVENT_TABLE()
100 
101 FortranProject::FortranProject() :
102     m_pNativeParser(0),
103     m_EditorHookId(0),
104     m_TimerCodeCompletion(this, idCodeCompleteTimer),
105     m_pCodeCompletionLastEditor(0),
106     m_pToolbar(0L),
107     m_ShowedCallTip(false),
108     m_WasCallTipActive(false),
109     m_IsAutoPopup(false),
110     m_ActiveCalltipsNest(0),
111     m_ActiveCalltipsPosition(-1),
112     m_CurrentLine(0),
113     m_pFortranLog(0L),
114     m_TimerReparseEditor(this, idReparseEditorTimer)
115 {
116     if(!Manager::LoadResource(_T("FortranProject.zip")))
117     {
118         NotifyMissingFile(_T("FortranProject.zip"));
119     }
120 }
121 
~FortranProject()122 FortranProject::~FortranProject()
123 {
124 }
125 
126 
OnAttach()127 void FortranProject::OnAttach()
128 {
129     m_ViewMenu = 0;
130     m_FortranToolsMenu = 0;
131 
132     m_pNativeParser = new NativeParserF(this);
133     m_pNativeParser->CreateWorkspaceBrowser();
134     m_LastPosForCodeCompletion = -1;
135 
136     m_pKeywordsParser = new KeywordsParserF();
137 
138     m_pCallTree = new CallTree(this);
139 
140     RereadOptions();
141     LoadFortranKeywordImages();
142 
143     // hook to project loading procedure
144     ProjectLoaderHooks::HookFunctorBase* fp_hook =
145         new ProjectLoaderHooks::HookFunctor<FortranProject>(this, &FortranProject::OnProjectLoadingHook);
146     m_ProjectLoadingHookID = ProjectLoaderHooks::RegisterHook(fp_hook);
147 
148     // hook to editors
149     EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<FortranProject>(this, &FortranProject::EditorEventHook);
150     m_EditorHookId = EditorHooks::RegisterHook(myhook);
151 
152     // register event sinks
153     Manager* pm = Manager::Get();
154 
155     pm->RegisterEventSink(cbEVT_EDITOR_SAVE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorSave));
156     pm->RegisterEventSink(cbEVT_EDITOR_ACTIVATED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorActivated));
157     pm->RegisterEventSink(cbEVT_EDITOR_DEACTIVATED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorDeactivated));
158     pm->RegisterEventSink(cbEVT_EDITOR_CLOSE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnEditorClose));
159 
160     pm->RegisterEventSink(cbEVT_APP_STARTUP_DONE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnAppDoneStartup));
161     pm->RegisterEventSink(cbEVT_WORKSPACE_CHANGED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnWorkspaceChanged));
162     pm->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectActivated));
163     pm->RegisterEventSink(cbEVT_PROJECT_CLOSE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectClosed));
164     pm->RegisterEventSink(cbEVT_PROJECT_SAVE, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectSaved));
165     pm->RegisterEventSink(cbEVT_PROJECT_FILE_ADDED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectFileAdded));
166     pm->RegisterEventSink(cbEVT_PROJECT_FILE_REMOVED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnProjectFileRemoved));
167     pm->RegisterEventSink(cbEVT_COMPILER_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnCompilerStarted));
168     pm->RegisterEventSink(cbEVT_CLEAN_PROJECT_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnCleanProjectStarted));
169     pm->RegisterEventSink(cbEVT_CLEAN_WORKSPACE_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnCleanWorkspaceStarted));
170 
171     pm->RegisterEventSink(cbEVT_DEBUGGER_STARTED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnDebuggerStarted));
172     pm->RegisterEventSink(cbEVT_DEBUGGER_FINISHED, new cbEventFunctor<FortranProject, CodeBlocksEvent>(this, &FortranProject::OnDebuggerFinished));
173 
174     pm->GetCCManager()->RegisterAutoLaunchChars(wxT("%"), this);
175 
176     m_IsDebugging = false;
177     m_InitDone = true;
178 }
179 
OnRelease(bool appShutDown)180 void FortranProject::OnRelease(bool appShutDown)
181 {
182     // unregister hook
183     // 'true' will delete the functor too
184     ProjectLoaderHooks::UnregisterHook(m_ProjectLoadingHookID, true);
185     EditorHooks::UnregisterHook(m_EditorHookId, true);
186 
187     // remove registered event sinks
188     Manager::Get()->RemoveAllEventSinksFor(this);
189 
190     if (m_pNativeParser)
191     {
192         delete m_pNativeParser;
193     }
194     if (m_pKeywordsParser)
195     {
196         delete m_pKeywordsParser;
197     }
198     if (m_pCallTree)
199     {
200         delete m_pCallTree;
201     }
202 
203     RemoveLogWindow(appShutDown);
204 
205     if (m_ViewMenu)
206     {
207         m_ViewMenu->Delete(idViewSymbolsBrowser);
208         m_ViewMenu->Delete(idViewCallTree);
209     }
210 
211     if (m_FortranToolsMenu)
212     {
213         m_FortranToolsMenu->Delete(idMenuJump);
214         m_FortranToolsMenu->Delete(idMenuGenerateMakefile);
215         m_FortranToolsMenu->Delete(idMenuChangeCase);
216         m_FortranToolsMenu->Delete(idMenuTab2Space);
217         m_FortranToolsMenu->Delete(idMenuFormatIndent);
218         m_FortranToolsMenu->Delete(idMenuBindTo);
219     }
220 } // end of OnRelease
221 
222 
OnUpdateUI(wxUpdateUIEvent & event)223 void FortranProject::OnUpdateUI(wxUpdateUIEvent& event)
224 {
225     if (m_ViewMenu)
226     {
227         bool isVis = IsWindowReallyShown((wxWindow*)m_pNativeParser->GetWorkspaceBrowser());
228         m_ViewMenu->Check(idViewSymbolsBrowser, isVis);
229     }
230 
231     event.Skip();
232 }
233 
234 
OnUpdateUICallTree(wxUpdateUIEvent & event)235 void FortranProject::OnUpdateUICallTree(wxUpdateUIEvent& event)
236 {
237     if (m_ViewMenu)
238     {
239         bool isVis = IsWindowReallyShown((wxWindow*)m_pCallTree->GetCallTreeView());
240         m_ViewMenu->Check(idViewCallTree, isVis);
241     }
242 
243     event.Skip();
244 }
245 
246 
OnAppDoneStartup(CodeBlocksEvent & event)247 void FortranProject::OnAppDoneStartup(CodeBlocksEvent& event)
248 {
249     if (IsAttached())
250     {
251         m_InitDone = false;
252         // parse any projects opened through DDE or the command-line
253         m_pNativeParser->ForceReparseWorkspace();
254         m_InitDone = true;
255     }
256 
257     if (m_pNativeParser->GetWorkspaceBrowser())
258     {
259         m_pNativeParser->GetWorkspaceBrowser()->UpdateSash();
260     }
261     event.Skip();
262 }
263 
OnWorkspaceChanged(CodeBlocksEvent & event)264 void FortranProject::OnWorkspaceChanged(CodeBlocksEvent& event)
265 {
266     // EVT_WORKSPACE_CHANGED is a powerful event, it's sent after any project
267     // has finished loading or closing. It's the *LAST* event to be sent when
268     // the workspace has been changed, and it's not sent if the application is
269     // shutting down. So it's the ideal time to parse files and update your
270     // widgets.
271     if (IsAttached() && m_InitDone && !Manager::IsAppShuttingDown())
272     {
273         m_InitDone = false;
274         // Parse the projects
275         m_pNativeParser->ForceReparseWorkspace();
276         m_InitDone = true;
277     }
278     event.Skip();
279 }
280 
OnProjectActivated(CodeBlocksEvent & event)281 void FortranProject::OnProjectActivated(CodeBlocksEvent& event)
282 {
283     // The Class browser shouldn't be updated if we're in the middle of loading/closing
284     // a project/workspace, because the class browser would need to be updated again.
285     // So we need to update it with the EVT_WORKSPACE_CHANGED event, which gets
286     // triggered after everything's finished loading/closing.
287 
288     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
289     {
290         m_pNativeParser->OnProjectActivated(event.GetProject());
291     }
292     event.Skip();
293 }
294 
OnProjectClosed(CodeBlocksEvent & event)295 void FortranProject::OnProjectClosed(CodeBlocksEvent& event)
296 {
297     // After this, the Class Browser needs to be updated. It will happen
298     // when we receive the next EVT_PROJECT_ACTIVATED event.
299 //    if (IsAttached() && m_InitDone)
300 //    {
301 //        m_pNativeParser->RemoveFromParser(event.GetProject());
302 //    }
303 
304     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
305     {
306         m_pNativeParser->DelProjectSearchDirs(event.GetProject());
307     }
308 
309     event.Skip();
310 }
311 
OnProjectSaved(CodeBlocksEvent & event)312 void FortranProject::OnProjectSaved(CodeBlocksEvent& event)
313 {
314     // Do we need it for Fortran?
315     event.Skip();
316 }
317 
OnProjectFileAdded(CodeBlocksEvent & event)318 void FortranProject::OnProjectFileAdded(CodeBlocksEvent& event)
319 {
320     if (IsAttached() && m_InitDone)
321     {
322         cbProject* cbp = event.GetProject();
323         if (cbp)
324         {
325             wxString pfn = cbp->GetFilename();
326             m_pNativeParser->AddFileToParser(pfn, event.GetString());
327             m_pNativeParser->UpdateWorkspaceBrowser();
328         }
329     }
330     event.Skip();
331 }
332 
OnProjectFileRemoved(CodeBlocksEvent & event)333 void FortranProject::OnProjectFileRemoved(CodeBlocksEvent& event)
334 {
335     if (IsAttached() && m_InitDone)
336     {
337         m_pNativeParser->RemoveFileFromParser(event.GetString());
338         m_pNativeParser->UpdateWorkspaceBrowser();
339     }
340     event.Skip();
341 }
342 
OnEditorSave(CodeBlocksEvent & event)343 void FortranProject::OnEditorSave(CodeBlocksEvent& event)
344 {
345     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
346     {
347         EditorBase* eb = event.GetEditor();
348         cbEditor* editor = (eb && eb->IsBuiltinEditor()) ? static_cast<cbEditor*>(eb) : 0;
349         if (editor)
350         {
351             wxString projFN;
352             ProjectFile* pf = editor->GetProjectFile();
353             if (pf)
354             {
355                 cbProject* cbp = pf->GetParentProject();
356                 projFN = cbp->GetFilename();
357             }
358             m_pNativeParser->ReparseFile(projFN, editor->GetFilename());
359             m_pNativeParser->UpdateWorkspaceBrowser();
360         }
361     }
362     event.Skip();
363 }
364 
OnEditorActivated(CodeBlocksEvent & event)365 void FortranProject::OnEditorActivated(CodeBlocksEvent& event)
366 {
367     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
368     {
369         EditorBase* eb = event.GetEditor();
370         m_pNativeParser->OnEditorActivated(eb);
371 
372         if (m_TimerReparseEditor.IsRunning())
373             m_TimerReparseEditor.Stop();
374         cbEditor* editor = (eb && eb->IsBuiltinEditor()) ? static_cast<cbEditor*>(eb) : 0;
375         if (editor && editor->GetModified())
376             m_TimerReparseEditor.Start(1500, wxTIMER_ONE_SHOT);
377 
378         FortranSourceForm fsForm;
379         if (editor && m_pNativeParser->IsFileFortran(editor->GetShortName(), fsForm))
380         {
381             cbStyledTextCtrl* control = editor->GetControl();
382             m_ConstrHighlighter.ClearHighlighting(control, true);
383             m_ConstrHighlighter.DoWork(editor, fsForm);
384         }
385     }
386 
387     event.Skip();
388 }
389 
OnEditorDeactivated(CodeBlocksEvent & event)390 void FortranProject::OnEditorDeactivated(CodeBlocksEvent& event)
391 {
392     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
393     {
394         EditorBase* eb = event.GetEditor();
395         cbEditor* editor = (eb && eb->IsBuiltinEditor()) ? static_cast<cbEditor*>(eb) : 0;
396         if (editor)
397         {
398             cbStyledTextCtrl* control = editor->GetControl();
399             m_ConstrHighlighter.ClearHighlighting(control);
400         }
401     }
402     event.Skip();
403 }
404 
OnEditorClose(CodeBlocksEvent & event)405 void FortranProject::OnEditorClose(CodeBlocksEvent& event)
406 {
407     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
408     {
409         EditorBase* eb = event.GetEditor();
410         m_pNativeParser->OnEditorClose(eb);
411     }
412 
413     event.Skip();
414 }
415 
OnCompilerStarted(CodeBlocksEvent & event)416 void FortranProject::OnCompilerStarted(CodeBlocksEvent& event)
417 {
418     event.Skip();
419     m_pNativeParser->UpdateWorkspaceFilesDependency();
420 }
421 
OnCleanProjectStarted(CodeBlocksEvent & event)422 void FortranProject::OnCleanProjectStarted(CodeBlocksEvent& event)
423 {
424     event.Skip();
425     //Remove all *.mod files from obj folder
426     wxString targetName = event.GetBuildTargetName();
427     cbProject* pr = event.GetProject();
428     if (!pr)
429         return;
430     if (pr->IsMakefileCustom())
431         return;
432     ProjectBuildTarget* bTarget = pr->GetBuildTarget(targetName);
433     if (bTarget)
434     {
435         ProjectDependencies::RemoveModFiles(pr, bTarget, m_pNativeParser);
436     }
437 }
438 
OnCleanWorkspaceStarted(CodeBlocksEvent & event)439 void FortranProject::OnCleanWorkspaceStarted(CodeBlocksEvent& event)
440 {
441     event.Skip();
442     //Remove all *.mod files from obj folder
443     ProjectDependencies::RemoveModFilesWS(m_pNativeParser);
444 }
445 
BuildMenu(wxMenuBar * menuBar)446 void FortranProject::BuildMenu(wxMenuBar* menuBar)
447 {
448     if (!IsAttached())
449         return;
450 
451     // add the fsymbolsbrowser window in the "View" menu
452     int idx = menuBar->FindMenu(_("&View"));
453     if (idx != wxNOT_FOUND)
454     {
455         m_ViewMenu = menuBar->GetMenu(idx);
456         wxMenuItemList& items = m_ViewMenu->GetMenuItems();
457         bool inserted = false;
458 
459         // find the first separator and insert before it
460         for (size_t i = 0; i < items.GetCount(); ++i)
461         {
462             if (items[i]->IsSeparator())
463             {
464                 m_ViewMenu->InsertCheckItem(i, idViewSymbolsBrowser, _("Fortran symbols browser"), _("Toggle displaying the fortran symbols browser"));
465                 m_ViewMenu->InsertCheckItem(i, idViewCallTree, _("Fortran Call/Called-By tree"), _("Toggle displaying the fortran Call/Called-By tree window"));
466                 inserted = true;
467                 break;
468             }
469         }
470 
471         // not found, just append
472         if (!inserted)
473         {
474             m_ViewMenu->AppendCheckItem(idViewSymbolsBrowser, _("Fortran symbols browser"), _("Toggle displaying the fortran symbols browser"));
475             m_ViewMenu->AppendCheckItem(idViewCallTree, _("Fortran call tree"), _("Toggle displaying the fortran Call/Called-By tree window"));
476         }
477     }
478     else
479         Manager::Get()->GetLogManager()->DebugLog(_T("FortranProject: Could not find View menu!"));
480 
481 
482     int pos = menuBar->FindMenu(_("Fortra&n"));
483     if (pos == wxNOT_FOUND)
484     {
485         pos = menuBar->FindMenu(_("&Tools"));
486         if (pos != wxNOT_FOUND)
487         {
488             m_FortranToolsMenu = new wxMenu();
489             menuBar->Insert(pos, m_FortranToolsMenu, _("Fortra&n"));
490         }
491         else
492             Manager::Get()->GetLogManager()->DebugLog(_T("FortranProject: Could not find Tools menu!"));
493     }
494     else
495     {
496         m_FortranToolsMenu = menuBar->GetMenu(pos);
497     }
498     if (m_FortranToolsMenu)
499     {
500         wxMenu* submenuJump = new wxMenu();
501         submenuJump->Append(idMenuGotoDeclaration, _("Jump to declaration"));
502         const int imageSize = Manager::Get()->GetImageSize(Manager::UIComponent::Menus);
503         const int uiScaleFactor = Manager::Get()->GetUIScaleFactor(Manager::UIComponent::Menus);
504         wxString prefix = ConfigManager::GetDataFolder() +
505                           wxString::Format(_T("/images/fortranproject/%dx%d/"), imageSize, imageSize);
506 
507         wxBitmap bmp_back = cbLoadBitmapScaled(prefix + _T("fprojectjumpback.png"), wxBITMAP_TYPE_PNG, uiScaleFactor);
508         wxBitmap bmp_home = cbLoadBitmapScaled(prefix + _T("fprojectjumphome.png"), wxBITMAP_TYPE_PNG, uiScaleFactor);
509         wxBitmap bmp_forward = cbLoadBitmapScaled(prefix + _T("fprojectjumpforward.png"), wxBITMAP_TYPE_PNG, uiScaleFactor);
510         wxMenuItem* itemJumpBack = new wxMenuItem(submenuJump, idMenuJumpBack, _("Jump back"));
511         itemJumpBack->SetBitmap(bmp_back);
512         wxMenuItem* itemJumpHome = new wxMenuItem(submenuJump, idMenuJumpHome, _("Jump last"));
513         itemJumpHome->SetBitmap(bmp_home);
514         wxMenuItem* itemJumpForward = new wxMenuItem(submenuJump, idMenuJumpForward, _("Jump forward"));
515         itemJumpForward->SetBitmap(bmp_forward);
516         submenuJump->Append(itemJumpBack);
517         submenuJump->Append(itemJumpHome);
518         submenuJump->Append(itemJumpForward);
519         submenuJump->Enable(idMenuJumpBack, false);
520         submenuJump->Enable(idMenuJumpHome, false);
521         submenuJump->Enable(idMenuJumpForward, false);
522 
523         m_FortranToolsMenu->Insert(0, idMenuBindTo, _("Bind To..."));
524         m_FortranToolsMenu->Insert(0, idMenuFormatIndent, _("Format indent..."));
525         m_FortranToolsMenu->Insert(0, idMenuTab2Space, _("Tab2space..."));
526         m_FortranToolsMenu->Insert(0, idMenuChangeCase, _("Change case..."));
527         m_FortranToolsMenu->Insert(0, idMenuGenerateMakefile, _("Generate Makefile..."));
528         m_FortranToolsMenu->Insert(0, idMenuJump, _("Jump"), submenuJump);
529     }
530 }
531 
CalcStcFontSize(cbStyledTextCtrl * stc)532 static int CalcStcFontSize(cbStyledTextCtrl *stc)
533 {
534     wxFont defaultFont = stc->StyleGetFont(wxSCI_STYLE_DEFAULT);
535     defaultFont.SetPointSize(defaultFont.GetPointSize() + stc->GetZoom());
536     int fontSize;
537     stc->GetTextExtent(wxT("A"), nullptr, &fontSize, nullptr, nullptr, &defaultFont);
538     return fontSize;
539 }
540 
541 // invariant : on return true : NameUnderCursor is NOT empty
EditorHasNameUnderCursor(wxString & NameUnderCursor,bool & isOperator)542 static bool EditorHasNameUnderCursor(wxString& NameUnderCursor, bool& isOperator)
543 {
544     if(cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
545     {
546         isOperator = false;
547         cbStyledTextCtrl* control = ed->GetControl();
548         const int pos = control->GetCurrentPos();
549 
550         int ws = control->WordStartPosition(pos, true);
551         int we = control->WordEndPosition(pos, true);
552         if (ws < we && ws > 0 && control->GetCharAt(ws-1) == '.'
553             && we < control->GetLength() && control->GetCharAt(we) == '.')
554         {
555             // maybe we have user defined operator .name. Take it as one word.
556             ws--;
557             we++;
558             isOperator = true;
559         }
560         const wxString txt = control->GetTextRange(ws, we);
561         if (!txt.IsEmpty())
562         {
563             NameUnderCursor = txt;
564             return true;
565         }
566         // Check if we at operator
567         wxString operatorsTxt = _T("=*/+-<>");
568         int opStart = pos;
569         for (int i=1; i<3 && pos-i>0; i++)
570         {
571             wxChar txt1 = control->GetCharAt(pos-i);
572             if (operatorsTxt.Contains(txt1))
573                 opStart = pos-i;
574             else
575                 break;
576         }
577         int opEnd = pos;
578         for (int i=0; i<3 && pos+i<control->GetLength(); i++)
579         {
580             wxChar txt1 = control->GetCharAt(pos+i);
581             if (operatorsTxt.Contains(txt1))
582                 opEnd = pos+i+1;
583             else
584                 break;
585         }
586         wxString opStr = control->GetTextRange(opStart, opEnd);
587         if (!opStr.IsEmpty())
588         {
589             NameUnderCursor = opStr;
590             isOperator = true;
591             return true;
592         }
593 
594     }
595     return false;
596 } // end of EditorHasNameUnderCursor
597 
BuildModuleMenu(const ModuleType type,wxMenu * menu,const FileTreeData * data)598 void FortranProject::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
599 {
600     if (!menu || !IsAttached() || !m_InitDone)
601         return;
602     if (type == mtEditorManager)
603     {
604         cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
605         if (!ed || !m_pNativeParser->IsFileFortran(ed->GetFilename()))
606             return;
607 
608         wxString NameUnderCursor;
609         bool isOperator;
610         if(EditorHasNameUnderCursor(NameUnderCursor, isOperator))
611         {
612             wxString msg;
613             msg.Printf(_("Jump to '%s'"), NameUnderCursor.c_str());
614             menu->Insert(0, idGotoDeclaration, msg);
615 
616             menu->Insert(1, wxID_SEPARATOR, wxEmptyString);
617             Manager::Get()->GetPluginManager()->RegisterFindMenuItems(true, 2);
618 
619             if (!isOperator)
620             {
621                 wxMenu* showsubmenu = new wxMenu();
622                 showsubmenu->Append(idShowCallTree, _T("Call tree"));
623                 showsubmenu->Append(idShowCalledByTree, _T("Called-By tree"));
624                 menu->Insert(1, wxID_ANY, _("Show"), showsubmenu);
625                 Manager::Get()->GetPluginManager()->RegisterFindMenuItems(true, 1);
626             }
627         }
628     }
629 
630 }
631 
OnGotoDeclaration(wxCommandEvent & event)632 void FortranProject::OnGotoDeclaration(wxCommandEvent& event)
633 {
634     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
635     if (!ed)
636         return;
637 
638     cbStyledTextCtrl* control = ed->GetControl();
639     if (!control)
640         return;
641 
642     wxString NameUnderCursor;
643     bool isOperator;
644     if(!EditorHasNameUnderCursor(NameUnderCursor, isOperator))
645         return;
646 
647     // get the matching set
648     ParserF* pParser = m_pNativeParser->GetParser();
649     TokensArrayFlatClass tokensTmp;
650     TokensArrayFlat* result = tokensTmp.GetTokens();
651 
652     wxString includeFilename = GetIncludeFilename(ed->GetControl());
653     if (!includeFilename.IsEmpty())
654     {
655         // was asked to jump to include file.
656         pParser->FindFile(includeFilename, *result);
657     }
658     else if (isOperator)
659     {
660         pParser->FindMatchOperatorTokensForJump(NameUnderCursor, *result);
661     }
662     else
663     {
664         pParser->FindMatchTokensForJump(ed, m_LogOnlyUseAssoc, false, *result);
665         // don't jump to intrinsic module
666         size_t ri = 0;
667         while (ri<result->GetCount())
668         {
669             if (result->Item(ri)->m_Filename.EndsWith(UnixFilename(_T("/images/fortranproject/fortran_intrinsic_modules.f90"))))
670             {
671                 result->Item(ri)->Clear();
672                 delete result->Item(ri);
673                 result->RemoveAt(ri);
674             }
675             else
676                 ri++;
677         }
678     }
679 
680     if (result->GetCount() > 1)
681     {
682         // Remove result with the current line
683         wxString curfname = UnixFilename(ed->GetFilename());
684         unsigned int curlineIdx = control->LineFromPosition(control->GetCurrentPos());
685         for (size_t ri=0; ri<result->GetCount(); ri++)
686         {
687             if (result->Item(ri)->m_Filename.IsSameAs(curfname) &&
688                 result->Item(ri)->m_LineStart == curlineIdx + 1)
689             {
690                 result->Item(ri)->Clear();
691                 delete result->Item(ri);
692                 result->RemoveAt(ri);
693                 break;
694             }
695         }
696     }
697 
698     size_t count = std::min(result->GetCount(),m_MaxMatch);
699 
700     TokenFlat* pToken = 0;
701     // one match
702     if (count == 1)
703     {
704 		pToken = result->Item(0);
705     }
706     // if more than one match, display a selection dialog
707     else if (count > 1)
708     {
709         wxArrayString selections;
710         std::vector<int> idxItems;
711         for (size_t i=0; i<count; ++i)
712         {
713             wxFileName fn = wxFileName(result->Item(i)->m_Filename);
714             wxString inf;
715             if (result->Item(i)->m_TokenKind == tkUse && !result->Item(i)->m_Rename.IsEmpty())
716             {
717                 inf = _T("use :: ") + result->Item(i)->m_DisplayName + _T(", ") + result->Item(i)->m_Rename;
718             }
719             else
720             {
721                 inf = result->Item(i)->m_DisplayName + _T(" :: ") + result->Item(i)->GetTokenKindString();
722             }
723             inf += _T(" : ") + fn.GetFullName() + _T(" : ");
724             inf += wxString::Format(_T("%d"), int(result->Item(i)->m_LineStart));
725 
726             if (selections.Index(inf) == wxNOT_FOUND)
727             {
728                 selections.Add(inf);
729                 idxItems.push_back(i);
730             }
731         }
732         if (selections.Count() > 1)
733         {
734             int sel = wxGetSingleChoiceIndex(_("Please make a selection:"), _("Multiple matches"), selections);
735             if (sel == -1)
736                 return;
737             pToken = result->Item(idxItems[sel]);
738         }
739         else
740         {
741             pToken = result->Item(0);
742         }
743     }
744 
745     if (pToken)
746     {
747         if (!GotoToken(pToken, ed))
748             cbMessageBox(wxString::Format(_("Declaration not found: %s"), NameUnderCursor.c_str()), _("Warning"), wxICON_WARNING);
749     }
750     else
751     {
752         cbMessageBox(wxString::Format(_("Not found: %s"), NameUnderCursor.c_str()), _("Warning"), wxICON_WARNING);
753     }
754 
755 } // end of OnGotoDeclaration
756 
757 
GotoToken(TokenFlat * pToken,cbEditor * cured)758 bool FortranProject::GotoToken(TokenFlat* pToken, cbEditor* cured)
759 {
760     LineAddress jumpStart;
761     LineAddress jumpFinish;
762     if(cured)
763     {
764         cbStyledTextCtrl* control = cured->GetControl();
765         int curLine = control->LineFromPosition(control->GetCurrentPos());
766         jumpStart.Init(cured->GetFilename(), curLine, false);
767     }
768 
769     if (cbEditor* newed = Manager::Get()->GetEditorManager()->Open(pToken->m_Filename))
770     {
771         newed->GotoLine(pToken->m_LineStart - 1);
772 
773         // Track jump history
774         cbStyledTextCtrl* control = newed->GetControl();
775         int curLine = control->LineFromPosition(control->GetCurrentPos());
776         jumpFinish.Init(newed->GetFilename(), curLine, true);
777         m_pNativeParser->GetJumpTracker()->TakeJump(jumpStart, jumpFinish);
778         CheckEnableToolbar();
779     }
780     else
781     {
782         return false;
783     }
784     return true;
785 } // end of OnGotoToken
786 
787 
CodeCompletePreprocessor(int tknStart,int tknEnd,cbEditor * ed,std::vector<CCToken> & tokens)788 void FortranProject::CodeCompletePreprocessor(int tknStart, int tknEnd, cbEditor* ed, std::vector<CCToken>& tokens)
789 {
790     if (!IsAttached() || !m_InitDone)
791         return;
792 
793     cbStyledTextCtrl* stc = ed->GetControl();
794     const wxString text = stc->GetTextRange(tknStart, tknEnd);
795 
796     TokenF tp;
797     tp.m_TokenKind = tkPreprocessor;
798     int iidx = m_pNativeParser->GetTokenKindImageIdx(&tp);
799 
800     wxStringVec macros;
801     macros.push_back(wxT("define"));
802     macros.push_back(wxT("elif"));
803     macros.push_back(wxT("elifdef"));
804     macros.push_back(wxT("elifndef"));
805     macros.push_back(wxT("else"));
806     macros.push_back(wxT("endif"));
807     macros.push_back(wxT("error"));
808     macros.push_back(wxT("if"));
809     macros.push_back(wxT("ifdef"));
810     macros.push_back(wxT("ifndef"));
811     macros.push_back(wxT("include"));
812     macros.push_back(wxT("line"));
813     macros.push_back(wxT("pragma"));
814     macros.push_back(wxT("undef"));
815     for (size_t i = 0; i < macros.size(); ++i)
816     {
817         if (text.IsEmpty() || macros[i][0] == text[0]) // ignore tokens that start with a different letter
818             tokens.push_back(CCToken(wxNOT_FOUND, macros[i], iidx));
819     }
820     stc->ClearRegisteredImages();
821     int fontSize = CalcStcFontSize(stc);
822     FPImageList fpImList(fontSize);
823     wxImageList* ilist = fpImList.GetImageList();
824     if (!ilist)
825         return;
826 
827     stc->RegisterImage(iidx, ilist->GetBitmap(iidx));
828 }
829 
DoCodeComplete(int caretPos,cbEditor * ed,std::vector<CCToken> & tokens)830 void FortranProject::DoCodeComplete(int caretPos, cbEditor* ed, std::vector<CCToken>& tokens)
831 {
832     if (!ed)
833         return;
834 
835     cbStyledTextCtrl* control = ed->GetControl();
836     const int pos = control->GetCurrentPos();
837     const int lineIndentPos = control->GetLineIndentPosition(control->GetCurrentLine());
838     const wxChar lineFirstChar = control->GetCharAt(lineIndentPos);
839 
840     int lineCur = control->LineFromPosition(pos);
841     int lineStartPos = control->PositionFromLine(lineCur);
842     wxString curLine = control->GetTextRange(lineStartPos,pos).Trim(false);
843 
844     if (lineFirstChar == _T('!'))
845     {
846         wxString curLineLw = curLine.Lower();
847         if (!curLineLw.StartsWith(_T("!$ ")) && !curLineLw.StartsWith(_T("!$\t")) && !curLineLw.StartsWith(_T("!$omp")) && !curLineLw.StartsWith(_T("!$acc")))
848             return;
849     }
850     else
851     {
852         if (curLine.Find('!') != wxNOT_FOUND) // we are in comments
853             return;
854     }
855 
856     int style = control->GetStyleAt(control->GetCurrentPos());
857     if (style != wxSCI_F_DEFAULT && style != wxSCI_F_WORD && style != wxSCI_F_WORD2 && style != wxSCI_F_WORD3
858         && style != wxSCI_F_OPERATOR && style != wxSCI_F_IDENTIFIER && style != wxSCI_F_OPERATOR2
859         && style != wxSCI_F_PREPROCESSOR )
860         return;
861 
862     CodeComplete(caretPos, ed, tokens);
863 }
864 
865 
GetAutocompList(bool isAuto,cbEditor * ed,int & tknStart,int & tknEnd)866 std::vector<FortranProject::CCToken> FortranProject::GetAutocompList(bool isAuto, cbEditor* ed, int& tknStart, int& tknEnd)
867 {
868     std::vector<CCToken> tokens;
869 
870     if (!IsAttached() || !m_InitDone)
871         return tokens;
872 
873     if (   !ed
874         || !m_pNativeParser->IsFileFortran(ed->GetShortName())
875         || !Manager::Get()->GetConfigManager(_T("fortran_project"))->ReadBool(_T("/use_code_completion"), true))
876         return tokens;
877 
878     cbStyledTextCtrl* stc = ed->GetControl();
879     const int style = stc->GetStyleAt(tknEnd);
880     const wxChar curChar = stc->GetCharAt(tknEnd - 1);
881 
882     if (isAuto && curChar != wxT('%'))
883         return tokens;
884 
885     const int lineIndentPos = stc->GetLineIndentPosition(stc->GetCurrentLine());
886     const wxChar lineFirstChar = stc->GetCharAt(lineIndentPos);
887 
888     if (lineFirstChar == wxT('#'))
889     {
890         const int startPos = stc->WordStartPosition(lineIndentPos + 1, true);
891         const int endPos = stc->WordEndPosition(lineIndentPos + 1, true);
892         const wxString str = stc->GetTextRange(startPos, endPos);
893 
894         if (endPos >= tknEnd && tknEnd > lineIndentPos)
895             CodeCompletePreprocessor(tknStart, tknEnd, ed, tokens);
896         return tokens;
897     }
898 
899     if (   stc->IsString(style)
900         || stc->IsCharacter(style) )
901     {
902         return tokens;
903     }
904 
905     DoCodeComplete(tknEnd, ed, tokens);
906     return tokens;
907 }
908 
DoAutocomplete(const CCToken & token,cbEditor * ed)909 void FortranProject::DoAutocomplete(const CCToken& token, cbEditor* ed)
910 {
911     cbStyledTextCtrl* control = ed->GetControl();
912 
913     wxString itemText = token.displayName.BeforeFirst(':');
914     control->AutoCompCancel();
915     int pos = control->GetCurrentPos();
916     int start = control->WordStartPosition(pos, true);
917     int endPos = control->WordEndPosition(pos, true);
918     const wxString& textUnder = control->GetTextRange(start, endPos);
919     bool replaceWord = false;
920     if (!textUnder.IsEmpty() && (start != pos || (pos != 0 && control->GetCharAt(pos-1) == _T('%'))))
921     {
922         TokensArrayFlat* ts = m_TokensCCList.GetTokens();
923         for (size_t i=0; i < ts->size(); ++i)
924         {
925             if (ts->Item(i)->m_DisplayName.IsSameAs(textUnder, false))
926             {
927                 replaceWord = true;
928                 break;
929             }
930         }
931     }
932     if (!replaceWord && pos != endPos)
933     {
934         const wxString& textUnderEnd = control->GetTextRange(pos, endPos);
935         if (itemText.EndsWith(textUnderEnd))
936         {
937             replaceWord = true;
938         }
939     }
940     if (textUnder.IsEmpty() || !textUnder.IsSameAs(itemText))
941     {
942         if (!replaceWord)
943             endPos = pos;
944         control->SetTargetStart(start);
945         control->SetTargetEnd(endPos);
946         control->ReplaceTarget(itemText);
947     }
948     control->GotoPos(start + itemText.size());
949 
950     if (m_WasCallTipActive)
951     {
952         m_WasCallTipActive = false;
953         CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
954         Manager::Get()->ProcessEvent(evt);
955     }
956 }
957 
958 
EditorEventHook(cbEditor * editor,wxScintillaEvent & event)959 void FortranProject::EditorEventHook(cbEditor* editor, wxScintillaEvent& event)
960 {
961     if (!IsAttached() || !m_InitDone)
962     {
963         event.Skip();
964         return;
965     }
966 
967     FortranSourceForm fsForm;
968     if (!m_pNativeParser->IsFileFortran(editor->GetShortName(), fsForm))
969     {
970         event.Skip();
971         return;
972     }
973 
974     cbStyledTextCtrl* control = editor->GetControl();
975     wxEventType etyp = event.GetEventType();
976 
977     if (   (etyp == wxEVT_SCI_CHARADDED)
978         && (!control->AutoCompActive()) ) // not already active autocompletion
979     {
980         wxChar ch = event.GetKey();
981         // update calltip highlight while we type
982         if (!control->CallTipActive())
983             m_ActiveCalltipsNest = 0;
984 
985         // start calltip
986         if (ch == _T('('))
987         {
988             if (control->CallTipActive())
989                 ++m_ActiveCalltipsNest;
990         }
991         // end calltip
992         else if (ch == _T(')'))
993         {
994             control->CallTipCancel();
995             if (m_ActiveCalltipsNest > 0)
996             {
997                 --m_ActiveCalltipsNest;
998                 CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
999                 Manager::Get()->ProcessEvent(evt);
1000             }
1001         }
1002     }
1003     else if (etyp != wxEVT_SCI_CHARADDED && etyp != wxEVT_SCI_MODIFIED)
1004     {
1005         if (control->CallTipActive())
1006         {
1007             int curPos = control->GetCurrentPos();
1008             if (m_ActiveCalltipsPosition == -1)
1009             {
1010                 m_ActiveCalltipsPosition = curPos;
1011             }
1012             else if (m_ActiveCalltipsPosition != curPos)
1013             {
1014                 bool ctipsUpdate = true;
1015                 if (m_ActiveCalltipsPosition+1 == curPos)
1016                 {
1017                     wxChar prevChar = control->GetCharAt(m_ActiveCalltipsPosition);
1018                     wxChar curChar  = control->GetCharAt(curPos);
1019                     if (isalpha(prevChar) || prevChar == '_' || isdigit(prevChar))
1020                         ctipsUpdate = false;
1021                     else if (prevChar == ' ' && curChar == ' ')
1022                         ctipsUpdate = false;
1023                 }
1024                 else if (m_ActiveCalltipsPosition-1 == curPos)
1025                 {
1026                     wxChar curChar  = control->GetCharAt(curPos);
1027                     if (isalpha(curChar) || curChar == '_' || isdigit(curChar) || curChar == ' ')
1028                         ctipsUpdate = false;
1029                 }
1030 
1031                 m_ActiveCalltipsPosition = curPos;
1032                 if (ctipsUpdate)
1033                 {
1034                     CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
1035                     Manager::Get()->ProcessEvent(evt);
1036                 }
1037             }
1038         }
1039         else
1040             m_ActiveCalltipsPosition = -1;
1041     }
1042 
1043     if( control->GetCurrentLine() != m_CurrentLine )
1044     {
1045         m_CurrentLine = control->GetCurrentLine();
1046         m_pNativeParser->MarkCurrentSymbol(false);
1047     }
1048 
1049     if (etyp == wxEVT_SCI_MODIFIED && !m_TimerReparseEditor.IsRunning())
1050         m_TimerReparseEditor.Start(1500, wxTIMER_ONE_SHOT);
1051 
1052     if (m_AutoInsertEnabled && etyp == wxEVT_SCI_CHARADDED)
1053     {
1054         wxChar ch = event.GetKey();
1055         if ((ch == _T('\n')) || ( (control->GetEOLMode() == wxSCI_EOL_CR) && (ch == _T('\r')) ))
1056             m_AutoInsert.MakeAutoInsert(editor);
1057     }
1058 
1059     m_ConstrHighlighter.DoWork(editor, fsForm);
1060 
1061     // allow others to handle this event
1062     event.Skip();
1063 }
1064 
1065 
OnViewWorkspaceBrowser(wxCommandEvent & event)1066 void FortranProject::OnViewWorkspaceBrowser(wxCommandEvent& event)
1067 {
1068     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1069     if (!cfg->ReadBool(_T("/use_symbols_browser"), true))
1070     {
1071         cbMessageBox(_("The Fortran symbols browser is disabled in FortranProject options.\n"
1072                         "Please enable it there first..."), _("Information"), wxICON_INFORMATION);
1073         return;
1074     }
1075     CodeBlocksDockEvent evt(event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
1076     evt.pWindow = (wxWindow*)m_pNativeParser->GetWorkspaceBrowser();
1077     Manager::Get()->ProcessEvent(evt);
1078 }
1079 
1080 
BuildToolBar(wxToolBar * toolBar)1081 bool FortranProject::BuildToolBar(wxToolBar* toolBar)
1082 {
1083     //The application is offering its toolbar for your plugin,
1084     //to add any toolbar items you want...
1085     //Append any items you need on the toolbar...
1086     //NotImplemented(_T("FortranProject::BuildToolBar()"));
1087 
1088     //Build toolbar
1089     if (!IsAttached() || !toolBar)
1090     {
1091         return false;
1092     }
1093     int imSize = Manager::Get()->GetImageSize(Manager::UIComponent::Toolbars);
1094     wxString tbSStr;
1095     if (imSize <= 16)
1096         tbSStr = _T("_16x16");
1097     else if (imSize <= 20)
1098         tbSStr = _T("_20x20");
1099     else if (imSize <= 24)
1100         tbSStr = _T("_24x24");
1101     else if (imSize <= 28)
1102         tbSStr = _T("_28x28");
1103     else if (imSize <= 32)
1104         tbSStr = _T("_32x32");
1105     else if (imSize <= 40)
1106         tbSStr = _T("_40x40");
1107     else if (imSize <= 48)
1108         tbSStr = _T("_48x48");
1109     else if (imSize <= 56)
1110         tbSStr = _T("_56x56");
1111     else
1112         tbSStr = _T("_64x64");
1113 
1114 
1115     Manager::Get()->AddonToolBar(toolBar,_T("fortran_project_toolbar") + tbSStr);
1116     toolBar->Realize();
1117     m_pToolbar = toolBar;
1118     m_pToolbar->EnableTool(XRCID("idFortProjBack"), false);
1119     m_pToolbar->EnableTool(XRCID("idFortProjHome"), false);
1120     m_pToolbar->EnableTool(XRCID("idFortProjForward"), false);
1121     m_pToolbar->SetInitialSize();
1122 
1123     return true;
1124 }
1125 
1126 
SortCCList(const wxString & first,const wxString & second)1127 static int SortCCList(const wxString& first, const wxString& second)
1128 {
1129     const wxChar* a = first.c_str();
1130     const wxChar* b = second.c_str();
1131     while (*a && *b)
1132     {
1133         if (*a != *b)
1134         {
1135             if      ((*a == _T('?')) && (*b != _T('?')))
1136                 return -1;
1137             else if ((*a != _T('?')) && (*b == _T('?')))
1138                 return 1;
1139             else if ((*a == _T('?')) && (*b == _T('?')))
1140                 return 0;
1141 
1142             if      ((*a == _T('_')) && (*b != _T('_')))
1143                 return 1;
1144             else if ((*a != _T('_')) && (*b == _T('_')))
1145                 return -1;
1146 
1147             wxChar lowerA = wxTolower(*a);
1148             wxChar lowerB = wxTolower(*b);
1149 
1150             if (lowerA != lowerB)
1151                 return lowerA - lowerB;
1152         }
1153         a++;
1154         b++;
1155     }
1156     // Either *a or *b is null
1157     return *a - *b;
1158 }
1159 
1160 
CodeComplete(const int pos,cbEditor * ed,std::vector<CCToken> & tokens)1161 void FortranProject::CodeComplete(const int pos, cbEditor* ed, std::vector<CCToken>& tokens)
1162 {
1163     if (!IsAttached() || !m_InitDone)
1164         return;
1165 
1166     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1167 
1168     ParserF* pParser = m_pNativeParser->GetParser();
1169     m_TokensCCList.Clear();
1170     TokensArrayFlat* result = m_TokensCCList.GetTokens();
1171 
1172     cbStyledTextCtrl* control = ed->GetControl();
1173     const int start = control->WordStartPosition(pos, true);
1174     wxString NameUnderCursor = control->GetTextRange(start,pos);
1175     wxString NameUnderCursorLw = NameUnderCursor.Lower();
1176 
1177     CompilerDirective pdir = cdNone;
1178     int lineCur = control->LineFromPosition(pos);
1179     int lineStartPos = control->PositionFromLine(lineCur);
1180     wxString curLine = control->GetTextRange(lineStartPos,pos).Trim(false).Lower();
1181 
1182     if (curLine.StartsWith(_T("!$")))
1183     {
1184         if ((NameUnderCursorLw.IsSameAs(_T("omp")) || NameUnderCursorLw.IsSameAs(_T("acc"))) && start >= 2)
1185         {
1186             // Check if cursor is not direct after !$omp or !$acc
1187             wxString word = control->GetTextRange(start-2,pos).Lower();
1188             if (word.IsSameAs(_T("!$omp")) || word.IsSameAs(_T("!$acc")))
1189                 return;
1190         }
1191         if (curLine.StartsWith(_T("!$omp")))
1192             pdir = cdOpenMP;
1193         else if (curLine.StartsWith(_T("!$acc")))
1194             pdir = cdOpenACC;
1195         else
1196             pdir = cdOther;
1197     }
1198 
1199     bool isAfterPercent;
1200     int tokenKind;
1201     wxArrayString firstWords;
1202 
1203     if (!pParser->FindMatchTokensForCodeCompletion(m_UseSmartCC, m_LogOnlyUseAssoc, m_LogOnlyPublicNames,
1204                                                    NameUnderCursor, ed, *result, isAfterPercent, tokenKind, firstWords))
1205         return;
1206 
1207     if (result->size() <= m_MaxMatch)
1208     {
1209         int fontSize = CalcStcFontSize(control);
1210 
1211         FPImageList fpImList(fontSize);
1212         wxImageList* ilist = fpImList.GetImageList();
1213         if (!ilist)
1214             return;
1215         control->ClearRegisteredImages();
1216         int ilistImWidth = 0;
1217         int ilistImHeight = 0;
1218         ilist->GetSize(0, ilistImWidth, ilistImHeight);
1219 
1220         tokens.reserve(result->size());
1221         wxArrayString items; items.Alloc(result->size());
1222         std::set<int> already_registered;
1223         std::set< wxString, std::less<wxString> > unique_strings; // check against this before inserting a new string in the list
1224         for (size_t i=0; i<result->GetCount(); ++i)
1225         {
1226             TokenFlat* token = result->Item(i);
1227             if (token->m_Name.StartsWith(_T("%%")) || token->m_Name.IsEmpty())
1228                 continue;
1229 
1230             wxString tmpstr = token->m_Name;
1231             if (m_LogShowTypeVariables && token->m_TokenKind == tkVariable)
1232                 tmpstr << _T(": ") << token->m_PartFirst; // add type of variable
1233 
1234             // check for unique_strings
1235             if (unique_strings.find(tmpstr) != unique_strings.end())
1236                 continue;
1237 
1238             unique_strings.insert(tmpstr);
1239             int iidx = m_pNativeParser->GetTokenKindImageIdx(token);
1240             if (already_registered.find(iidx) == already_registered.end())
1241             {
1242                 if (iidx != -1)
1243                 {
1244                     control->RegisterImage(iidx, ilist->GetBitmap(iidx));
1245                     already_registered.insert(iidx);
1246                 }
1247             }
1248             wxString tmp;
1249             if (iidx != -1)
1250             {
1251                 if (m_LogShowTypeVariables && token->m_TokenKind == tkVariable)
1252                     tmp << token->m_DisplayName << _T(": ") << token->m_PartFirst;
1253                 else
1254                     tmp << token->m_DisplayName;
1255             }
1256             else
1257                 tmp << token->m_DisplayName;
1258 
1259             tokens.push_back(CCToken(i, tmp, token->m_DisplayName, 5, iidx));
1260         }
1261 
1262         EditorColourSet* theme = ed->GetColourSet();
1263         if (theme && !isAfterPercent && (pdir == cdNone || pdir == cdOther) )
1264         {
1265             int iidx = ilist->GetImageCount();
1266             control->RegisterImage(iidx, GetFortranKeywordImage(ilistImHeight));
1267             // theme keywords
1268             HighlightLanguage lang = theme->GetLanguageForFilename(_T(".")+wxFileName(ed->GetFilename()).GetExt());
1269 
1270             int kwcase = cfg->ReadInt(_T("/keywords_case"), 0);
1271             for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
1272             {
1273                 if (!m_LexerKeywordsToInclude[i])
1274                     continue;
1275 
1276                 int oldSize = result->size();
1277                 wxString keywords = theme->GetKeywords(lang, i);
1278                 wxStringTokenizer tkz(keywords, _T(" \t\r\n"), wxTOKEN_STRTOK);
1279                 while (tkz.HasMoreTokens())
1280                 {
1281                     wxString kw = tkz.GetNextToken();
1282 
1283                     if ( (m_UseSmartCC && kw.Lower().StartsWith(NameUnderCursorLw) && m_pKeywordsParser->HasTokenSuitableKind(kw,tokenKind) &&
1284                           CCSmartFilter::FitsToContext(kw, firstWords))
1285                          || (!m_UseSmartCC && kw.Lower().StartsWith(NameUnderCursorLw)) )
1286                     {
1287                         // check for unique_strings
1288                         if (unique_strings.find(kw) != unique_strings.end())
1289                             continue;
1290                         unique_strings.insert(kw);
1291 
1292                         switch (kwcase)
1293                         {
1294                             case 0:
1295                             {
1296                                 break;
1297                             }
1298                             case 1:
1299                             {
1300                                 kw = kw.MakeUpper();
1301                                 break;
1302                             }
1303                             case 2:
1304                             {
1305                                 kw = kw.Mid(0,1).MakeUpper() + kw.Mid(1).MakeLower();
1306                                 break;
1307                             }
1308                             default :
1309                             {
1310                                 kw = kw.MakeLower();
1311                                 break;
1312                             }
1313                         }
1314                         m_pKeywordsParser->FindTokens(kw, *result);
1315                         int newSize = result->size();
1316                         if (newSize > oldSize && (result->Item(newSize-1)->m_TokenKind & tokenKind))
1317                         {
1318                             tokens.push_back(CCToken(newSize-1, kw, iidx));
1319                             oldSize = newSize;
1320                         }
1321                         else
1322                             tokens.push_back(CCToken(wxNOT_FOUND, kw, iidx));
1323                     }
1324                 }
1325             }
1326         }
1327         else if (pdir == cdOpenMP || pdir == cdOpenACC)
1328         {
1329             int iidx = ilist->GetImageCount();
1330             control->RegisterImage(iidx, GetFortranKeywordImage(ilistImHeight));
1331 
1332             int kwcase = cfg->ReadInt(_T("/keywords_case"), 0);
1333             const wxArrayString* kwOMP = m_pKeywordsParser->GetKeywords(pdir);
1334             for (size_t i=0; i<kwOMP->size(); i++)
1335             {
1336                 wxString kw = kwOMP->Item(i);
1337 
1338                 if (kw.Lower().StartsWith(NameUnderCursorLw))
1339                 {
1340                     switch (kwcase)
1341                     {
1342                         case 0:
1343                         {
1344                             break;
1345                         }
1346                         case 1:
1347                         {
1348                             kw = kw.MakeUpper();
1349                             break;
1350                         }
1351                         case 2:
1352                         {
1353                             kw = kw.Mid(0,1).MakeUpper() + kw.Mid(1).MakeLower();
1354                             break;
1355                         }
1356                         default :
1357                         {
1358                             kw = kw.MakeLower();
1359                             break;
1360                         }
1361                     }
1362                     tokens.push_back(CCToken(wxNOT_FOUND, kw, iidx));
1363                 }
1364             }
1365         }
1366 
1367         if (items.GetCount() == 0)
1368         {
1369             return;
1370         }
1371         items.Sort(SortCCList);
1372 
1373         // Remove duplicate items
1374         size_t i=0;
1375         size_t count=items.Count() - 1;
1376         while (i < count)
1377         {
1378             if (items.Item(i)==items.Item(i+1))
1379             {
1380                 items.RemoveAt(i);
1381                 count--;
1382             }
1383             else
1384                 i++;
1385         }
1386 
1387         if (control->CallTipActive())
1388         {
1389             m_WasCallTipActive = true;
1390         }
1391 
1392         control->AutoCompSetIgnoreCase(true);
1393         control->AutoCompSetCancelAtStart(true);
1394         control->AutoCompSetFillUps(wxEmptyString);
1395         control->AutoCompSetAutoHide(true);
1396         control->AutoCompSetDropRestOfWord(m_IsAutoPopup ? false : true);
1397         ed->GetControl()->AutoCompSetSeparator('\n');
1398         ed->GetControl()->AutoCompSetMaxWidth(80);
1399         ed->GetControl()->AutoCompSetMaxHeight(16);
1400         wxString final = GetStringFromArray(items, _T("\n"));
1401         final.Trim();
1402 
1403         control->AutoCompShow(pos - start, final);
1404 
1405         return;
1406     }
1407     else if (!control->CallTipActive())
1408     {
1409         wxString msg = _("Too many results.\n"
1410                              "Please edit results' limit in code-completion options,\n"
1411                              "or type at least one more character to narrow the scope down.");
1412         control->CallTipShow(control->GetCurrentPos(), msg);
1413         return;
1414     }
1415 
1416     return;
1417 }
1418 
GetProviderStatusFor(cbEditor * ed)1419 FortranProject::CCProviderStatus FortranProject::GetProviderStatusFor(cbEditor* ed)
1420 {
1421     if (ed && m_pNativeParser->IsFileFortran(ed->GetShortName()))
1422         return ccpsActive;
1423     return ccpsInactive;
1424 }
1425 
GetCallTips(int pos,int style,cbEditor * ed,int & argsPos)1426 std::vector<FortranProject::CCCallTip> FortranProject::GetCallTips(int pos, int style, cbEditor* ed, int& argsPos)
1427 {
1428     argsPos = wxSCI_INVALID_POSITION;
1429     std::vector<FortranProject::CCCallTip> tips;
1430     if (!IsAttached() || !m_InitDone || !ed)
1431         return tips;
1432 
1433     int hlStart = wxSCI_INVALID_POSITION;
1434     int hlEnd   = wxSCI_INVALID_POSITION;
1435     int commas; // how many commas has the user typed so far?
1436     int commasPos; // how many commas until current position?
1437     wxArrayString callTips;
1438     wxArrayInt idxFuncSub;
1439     TokensArrayFlatClass tokensTmp;
1440     TokensArrayFlat* result = tokensTmp.GetTokens();
1441     TokenFlat* token = NULL;
1442     bool isAfterPercent = false;
1443 
1444     wxString lastName;
1445     wxString argNameUnderCursor;
1446     m_pNativeParser->CollectInformationForCallTip(commas, commasPos, argNameUnderCursor, lastName, isAfterPercent, argsPos, result);
1447 
1448     if (isAfterPercent && result->GetCount() > 0)
1449     {
1450         if (result->Item(0)->m_TokenKind == tkProcedure)
1451         {
1452             m_pNativeParser->GetCallTipsForTypeBoundProc(result, callTips);
1453             idxFuncSub.Add(1);
1454         }
1455         else if (result->Item(0)->m_TokenKind == tkInterface)
1456             m_pNativeParser->GetCallTipsForGenericTypeBoundProc(result, callTips, idxFuncSub);
1457         else if (result->Item(0)->m_TokenKind == tkVariable &&
1458                  Manager::Get()->GetConfigManager(_T("fortran_project"))->ReadBool(_T("/call_tip_arrays"), true))
1459         {
1460             wxString callTip;
1461             m_pNativeParser->GetCallTipsForVariable(result->Item(0), callTip);
1462             if (!callTip.IsEmpty())
1463                 callTips.Add(callTip);
1464         }
1465     }
1466     else if (!lastName.IsEmpty())
1467     {
1468         m_pNativeParser->GetCallTips(lastName, m_LogOnlyUseAssoc, m_LogOnlyPublicNames, callTips, result);
1469 
1470         wxString kwName;
1471         if (lastName.IsSameAs(_T("open")))
1472             kwName = _T("__fortran_statement_") + lastName;
1473         else
1474             kwName = lastName;
1475         m_pKeywordsParser->GetCallTips(kwName, callTips, result);
1476     }
1477 
1478     bool isUnique = true;
1479     wxString definition;
1480     for (unsigned int i = 0; i < callTips.GetCount(); ++i)
1481     {
1482         if (!callTips[i].IsEmpty()) // non-empty
1483         {
1484             if (!definition.IsEmpty())
1485             {
1486                 isUnique = false;
1487                 break;
1488             }
1489             definition << callTips[i];
1490             token = result->Item(i);
1491 
1492             int nCommas = m_pNativeParser->CountCommas(callTips[i], 1, false);
1493             int commasDif = commas - nCommas;
1494             if (commasDif > 0)
1495             {
1496                 for (int idif=0; idif< commasDif; idif++)
1497                 {
1498                     definition << _T(", *???*");
1499                 }
1500                 definition << _T(" ");
1501             }
1502         }
1503     }
1504 
1505     if (isUnique && !argNameUnderCursor.IsEmpty())
1506     {
1507         // Determine number of commas before argNameUnderCursor
1508         int argidx = definition.Lower().Find(argNameUnderCursor.Lower());
1509         if (argidx != wxNOT_FOUND)
1510         {
1511             commasPos = m_pNativeParser->CountCommas(definition.Mid(0, argidx), 1, false);
1512         }
1513     }
1514 
1515     if (!definition.IsEmpty() && isUnique && token && token->m_TokenKind == tkVariable)
1516     {
1517         m_pNativeParser->GetCallTipHighlight(definition, commasPos, hlStart, hlEnd);
1518     }
1519     else if (!definition.IsEmpty() && isUnique &&
1520         (!isAfterPercent || ( isAfterPercent && result->GetCount() >= 2 && (result->Item(0)->m_TokenKind == tkProcedure) )))
1521     {
1522         m_pNativeParser->GetCallTipHighlight(definition, commasPos, hlStart, hlEnd);
1523         if (isAfterPercent)
1524             token = result->Item(1);
1525 
1526         if (token->m_TokenKind == tkSubroutine || token->m_TokenKind == tkFunction || token->m_TokenKind == tkType)
1527         {
1528             wxString argName = definition.Mid(hlStart,hlEnd-hlStart);
1529             argName = argName.BeforeFirst(_T(','));
1530             argName = argName.BeforeFirst(_T(')'));
1531             argName.Replace(_T("["),_T(" "));
1532             argName.Replace(_T("]"),_T(" "));
1533             argName.Trim().Trim(false);
1534 
1535             wxString argDecl;
1536             wxString argDescription;
1537             bool found = m_pNativeParser->GetParser()->FindTokenDeclaration(*token, argName, argDecl, argDescription);
1538             if (!found)
1539                 found = m_pKeywordsParser->GetParser()->FindTokenDeclaration(*token, argName, argDecl, argDescription);
1540             if (found)
1541             {
1542                 definition << _T('\n') << argDecl;
1543                 if (!argDescription.IsEmpty())
1544                     definition << _T('\n') << _T("! ") << argDescription;
1545             }
1546         }
1547     }
1548     else if(!isUnique)
1549     {
1550         if (   (isAfterPercent && (callTips.GetCount() != idxFuncSub.GetCount()))
1551             || (!isAfterPercent && (callTips.GetCount() != result->GetCount())) )
1552             return tips;
1553 
1554         if (lastName.IsEmpty())
1555             return tips;
1556 
1557         for (size_t i=0; i < callTips.GetCount(); ++i)
1558         {
1559             definition = _T("");
1560             if (isAfterPercent)
1561                 definition << result->Item(idxFuncSub[i])->m_DisplayName << _T('\n');
1562             else
1563                 definition << result->Item(i)->m_DisplayName << _T('\n');
1564             int mStart = definition.length();
1565 
1566             wxString ctdef = callTips.Item(i);
1567             int nCommas = m_pNativeParser->CountCommas(ctdef, 1, false);
1568             int commasDif = commas - nCommas;
1569             if (commasDif > 0)
1570             {
1571                 for (int j=0; j<commasDif; j++)
1572                 {
1573                     ctdef << _T(", *???*");
1574                 }
1575                 ctdef << _T(" ");
1576             }
1577             definition << ctdef;
1578 
1579             m_pNativeParser->GetCallTipHighlight(ctdef, commasPos, hlStart, hlEnd);
1580             if (isAfterPercent)
1581                 token = result->Item(idxFuncSub[i]);
1582             else
1583                 token = result->Item(i);
1584             if (token->m_TokenKind == tkSubroutine || token->m_TokenKind == tkFunction)
1585             {
1586                 wxString argName = callTips.Item(i).Mid(hlStart,hlEnd-hlStart);
1587                 argName = argName.BeforeFirst(_T(','));
1588                 argName = argName.BeforeFirst(_T(')'));
1589                 argName.Replace(_T("["),_T(" "));
1590                 argName.Replace(_T("]"),_T(" "));
1591                 argName.Trim().Trim(false);
1592 
1593                 wxString argDecl;
1594                 wxString argDescription;
1595                 if (m_pNativeParser->GetParser()->FindTokenDeclaration(*token, argName, argDecl, argDescription))
1596                 {
1597                     definition << _T('\n') << argDecl;
1598                     if (!argDescription.IsEmpty())
1599                         definition << _T('\n') << _T("! ") << argDescription;
1600                 }
1601             }
1602             hlStart += mStart;
1603             hlEnd   += mStart;
1604             if (!definition.IsEmpty())
1605                 tips.push_back(CCCallTip(definition, hlStart, hlEnd));
1606         }
1607     }
1608 
1609     if (isUnique && !definition.IsEmpty())
1610         tips.push_back(CCCallTip(definition, hlStart, hlEnd));
1611 
1612     if (!tips.empty())
1613     {
1614         if (m_LogUseWindow && (!m_WasCallTipInfoLog || !m_LastCallTipName.IsSameAs(lastName)))
1615             ShowInfoLog(result, isAfterPercent);
1616 
1617         m_ShowedCallTip = true;
1618         m_LastCallTipName = lastName;
1619         m_WasCallTipInfoLog = true;
1620     }
1621 
1622     return tips;
1623 }
1624 
GetTokenAt(int position,cbEditor * ed,bool & allowCallTip)1625 std::vector<FortranProject::CCToken> FortranProject::GetTokenAt(int position, cbEditor* ed, bool& allowCallTip)
1626 {
1627     // Get tokens for tooltip.
1628     std::vector<CCToken> tokens;
1629     if (!IsAttached() || !m_InitDone)
1630         return tokens;
1631 
1632     cbEditor* edLoc = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
1633     if (ed != edLoc)
1634         return tokens;
1635 
1636     if (!m_pNativeParser->IsFileFortran(ed->GetShortName()))
1637         return tokens;
1638 
1639     allowCallTip = false;
1640 
1641     cbStyledTextCtrl* control = ed->GetControl();
1642 
1643     if ((m_ShowedCallTip && control->CallTipActive()) || control->AutoCompActive())
1644         return tokens;
1645 
1646     const int style = control->GetStyleAt(position);
1647     if (style != wxSCI_F_DEFAULT && style != wxSCI_F_OPERATOR && style != wxSCI_F_IDENTIFIER
1648             && style != wxSCI_F_OPERATOR2 && style != wxSCI_F_WORD && style != wxSCI_F_WORD2
1649             && style != wxSCI_F_WORD3)
1650         return tokens;
1651 
1652     int endOfWord = control->WordEndPosition(position, true);
1653     int startOfWord = control->WordStartPosition(position, true);
1654     wxString nameUnder = control->GetTextRange(startOfWord, endOfWord);
1655     if (nameUnder.IsEmpty())
1656         return tokens;
1657 
1658     ParserF* pParser = m_pNativeParser->GetParser();
1659     TokensArrayFlatClass tokensTmp;
1660     TokensArrayFlat* result = tokensTmp.GetTokens();
1661 
1662     bool isAfterPercent = false;
1663     pParser->FindMatchTokensForToolTip(nameUnder, endOfWord, ed, m_LogOnlyUseAssoc, m_LogOnlyPublicNames, *result, isAfterPercent);
1664     if (result->IsEmpty())
1665         m_pKeywordsParser->FindTokens(nameUnder, *result);
1666 
1667     if (result->size() > 32 || result->size() == 0)
1668         return tokens;
1669 
1670     bool type_bound = false;
1671     wxString msg;
1672     for (size_t i=0; i<result->GetCount(); ++i)
1673     {
1674         TokenFlat* token = result->Item(i);
1675         if (!token->m_Rename.IsEmpty())
1676         {
1677             msg << token->m_Rename << _T(" => ") << token->m_DisplayName << _T("\n");
1678         }
1679         if (token->m_TokenKind == tkVariable)
1680         {
1681             msg << token->m_TypeDefinition << _T(" :: ") << token->m_DisplayName << token->m_Args << _T("\n");
1682         }
1683         else if (token->m_TokenKind == tkType)
1684         {
1685             msg << _T("type: ") << token->m_DisplayName << _T("\n");
1686         }
1687         else if (token->m_TokenKind == tkSubroutine)
1688         {
1689             msg << _T("subroutine ") << token->m_DisplayName << token->m_Args << _T("\n");
1690         }
1691         else if (token->m_TokenKind == tkFunction)
1692         {
1693             if (!token->m_PartFirst.IsEmpty())
1694             {
1695                 msg << token->m_PartFirst << _T(" ");
1696             }
1697             msg << _T("function ") << token->m_DisplayName << token->m_Args << _T("\n");
1698         }
1699         else if (token->m_TokenKind == tkProcedure)
1700         {
1701             TokenFlat* token2 = 0;
1702             if (result->GetCount() > i+1)
1703             {
1704                 i++;
1705                 token2 = result->Item(i);
1706             }
1707             pParser->FindTooltipForTypeBoundProc(msg, token, token2);
1708             type_bound = true;
1709         }
1710         else if (isAfterPercent && token->m_TokenKind == tkInterface)
1711         {
1712             while (i < result->GetCount())
1713             {
1714                 token = result->Item(i);
1715                 if (token->m_TokenKind == tkInterface)
1716                 {
1717                     wxString specNames = token->m_PartLast;
1718                     specNames.Replace(_T(" "),_T(", "));
1719                     msg << _T("generic :: ") << token->m_DisplayName << _T(" => ") << specNames << _T("\n");
1720                 }
1721                 i++;
1722             }
1723             type_bound = true;
1724         }
1725         else
1726         {
1727             msg << token->GetTokenKindString() << _T(" ") << token->m_DisplayName << token->m_Args << _T("\n");
1728         }
1729         wxString doc = HtmlDoc::GetDocForTooltip(token);
1730         if (!doc.IsEmpty())
1731             msg << _T("! ") << doc << _T("\n");
1732     }
1733     if (result->GetCount() == 1 && !type_bound)
1734     {
1735         if (!result->Item(0)->m_Filename.IsEmpty())
1736         {
1737             if (result->Item(0)->m_ParentTokenKind == tkModule)
1738             {
1739                 msg << result->Item(0)->m_ParentDisplayName << _T(", ");
1740             }
1741             msg << result->Item(0)->m_Filename.AfterLast(wxFILE_SEP_PATH) << _T(":") << result->Item(0)->m_LineStart;
1742         }
1743         else
1744             msg.Trim();
1745     }
1746     else
1747         msg.Trim();
1748 
1749     if (!m_IsDebugging)
1750     {
1751         tokens.push_back(CCToken(wxNOT_FOUND, msg));
1752         m_ShowedCallTip = false;
1753     }
1754 
1755     if (result->GetCount() >= 1 && m_LogUseWindow)
1756     {
1757         ShowInfoLog(result, isAfterPercent);
1758         m_WasCallTipInfoLog = false;
1759     }
1760 
1761     return tokens;
1762 }
1763 
ShowInfoLog(TokensArrayFlat * result,bool isAfterPercent)1764 void FortranProject::ShowInfoLog(TokensArrayFlat* result, bool isAfterPercent)
1765 {
1766     if (!m_LogUseWindow)
1767         return;
1768     if (result->GetCount() == 0)
1769         return;
1770 
1771     wxString logMsg;
1772     wxString fileNameOld;
1773 
1774     if (!isAfterPercent)
1775     {
1776         unsigned int countMax = 20<result->GetCount() ? 20 : result->GetCount();
1777         for (unsigned int i=0; i < countMax; i++)
1778         {
1779             TokenFlat* token = result->Item(i);
1780             wxString logMsg1;
1781             bool readFile;
1782             if (token->m_Filename.IsSameAs(fileNameOld))
1783             {
1784                 readFile = false;
1785             }
1786             else
1787             {
1788                 readFile = true;
1789                 fileNameOld = token->m_Filename;
1790             }
1791 
1792             if (!token->m_Rename.IsEmpty())
1793             {
1794                 logMsg << token->m_Rename << _T(" => ") << token->m_DisplayName << _T("\n");
1795             }
1796 
1797             if (token->m_TokenKind == tkSubroutine || token->m_TokenKind == tkFunction || token->m_TokenKind == tkType)
1798             {
1799                 if (m_pNativeParser->GetParser()->FindInfoLog(*token,m_LogComAbove,m_LogComBelow,m_LogDeclar,m_LogComVariab,logMsg1,readFile))
1800                 {
1801                     logMsg << logMsg1 << _T("\n\n");
1802                 }
1803             }
1804             else if (token->m_TokenKind == tkInterface)
1805             {
1806                 if (m_pNativeParser->GetParser()->GetTokenStr(*token, logMsg1))
1807                     logMsg << logMsg1 << _T("\n\n");
1808 
1809                 fileNameOld.Empty();
1810             }
1811         }
1812         if (!logMsg.IsEmpty())
1813         {
1814             if (countMax < result->GetCount())
1815             {
1816                 logMsg << wxString::Format(_T("!*********** %d more interfaces was not showed *************"),int(result->GetCount())-int(countMax));
1817             }
1818             WriteToLog(logMsg);
1819         }
1820     }
1821     else //isAfterPercent
1822     {
1823         if (result->Item(0)->m_TokenKind == tkProcedure)
1824             m_pNativeParser->GetParser()->FindInfoLogForTypeBoundProc(*result,m_LogComAbove,m_LogComBelow,m_LogDeclar,m_LogComVariab,logMsg);
1825         else if (result->Item(0)->m_TokenKind == tkInterface)
1826             m_pNativeParser->GetParser()->FindInfoLogForGenericTBProc(*result,m_LogComAbove,m_LogComBelow,m_LogDeclar,m_LogComVariab,logMsg);
1827         if (!logMsg.IsEmpty())
1828         {
1829             WriteToLog(logMsg);
1830         }
1831     }
1832 }
1833 
1834 
GetConfigurationPanel(wxWindow * parent)1835 cbConfigurationPanel* FortranProject::GetConfigurationPanel(wxWindow* parent)
1836 {
1837     FPOptionsDlg* dlg = new FPOptionsDlg(parent, m_pNativeParser, this);
1838     return dlg;
1839 }
1840 
GetProjectConfigurationPanel(wxWindow * parent,cbProject * project)1841 cbConfigurationPanel* FortranProject::GetProjectConfigurationPanel(wxWindow* parent, cbProject* project)
1842 {
1843     if (!m_pNativeParser)
1844         return nullptr;
1845 
1846     if (m_pNativeParser->HasFortranFiles(project))
1847     {
1848         FPOptionsProjectDlg* dlg = new FPOptionsProjectDlg(parent, project, m_pNativeParser);
1849         return dlg;
1850     }
1851     return nullptr;
1852 }
1853 
OnProjectLoadingHook(cbProject * prj,TiXmlElement * elem,bool loading)1854 void FortranProject::OnProjectLoadingHook(cbProject* prj, TiXmlElement* elem, bool loading)
1855 {
1856     if (!prj || !elem)
1857         return; // ?! Should actually NOT happen...
1858 
1859     if (loading)
1860     {
1861         // Project is loaded
1862         wxArrayString adirs;
1863         TiXmlElement* node = elem->FirstChildElement("fortran_project");
1864         if (node)
1865         {
1866             for(TiXmlElement* e = node->FirstChildElement("additional_dir"); e != NULL; e = e->NextSiblingElement("additional_dir"))
1867             {
1868                 adirs.Add(cbC2U(e->Attribute("dir")));
1869             }
1870         }
1871         m_pNativeParser->SetProjectSearchDirs(prj, adirs);
1872     }
1873     else
1874     {
1875         // Hook called when saving project file
1876 
1877         TiXmlElement* node = elem->FirstChildElement("fortran_project");
1878         if (!node)
1879             node = elem->InsertEndChild(TiXmlElement("fortran_project"))->ToElement();
1880         node->Clear();
1881 
1882         wxArrayString adirs = m_pNativeParser->GetProjectSearchDirs(prj);
1883         for (size_t i=0; i<adirs.size(); ++i)
1884         {
1885             TiXmlElement* e = node->InsertEndChild(TiXmlElement("additional_dir"))->ToElement();
1886             e->SetAttribute("dir", cbU2C(adirs.Item(i)));
1887         }
1888     }
1889 }
1890 
RereadOptions()1891 void FortranProject::RereadOptions()
1892 {
1893     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("fortran_project"));
1894 
1895     m_LexerKeywordsToInclude[0] = cfg->ReadBool(_T("/lexer_keywords_set1"), true);
1896     m_LexerKeywordsToInclude[1] = cfg->ReadBool(_T("/lexer_keywords_set2"), true);
1897     m_LexerKeywordsToInclude[2] = cfg->ReadBool(_T("/lexer_keywords_set3"), false);
1898     m_LexerKeywordsToInclude[3] = cfg->ReadBool(_T("/lexer_keywords_set4"), false);
1899     m_LexerKeywordsToInclude[4] = cfg->ReadBool(_T("/lexer_keywords_set5"), false);
1900     m_LexerKeywordsToInclude[5] = cfg->ReadBool(_T("/lexer_keywords_set6"), false);
1901     m_LexerKeywordsToInclude[6] = cfg->ReadBool(_T("/lexer_keywords_set7"), false);
1902     m_LexerKeywordsToInclude[7] = cfg->ReadBool(_T("/lexer_keywords_set8"), false);
1903     m_LexerKeywordsToInclude[8] = cfg->ReadBool(_T("/lexer_keywords_set9"), false);
1904 
1905     m_MaxMatch = cfg->ReadInt(_T("/max_matches"), 1000);
1906     if (m_MaxMatch < 1)
1907         m_MaxMatch = 1;
1908 
1909     m_UseSmartCC = cfg->ReadBool(_T("/use_smart_code_completion"), true);
1910     m_LogOnlyUseAssoc = cfg->ReadBool(_T("/only_use_associated"), true);
1911     m_LogOnlyPublicNames = !cfg->ReadBool(_T("/show_hidden_entities"), false);
1912     m_LogShowTypeVariables = cfg->ReadBool(_T("/show_type_variables"), true);
1913 
1914     m_LogUseWindow = cfg->ReadBool(_T("/use_log_window"), true);
1915     m_LogComAbove = cfg->ReadBool(_T("/include_comments_above"), true);
1916     m_LogComBelow = cfg->ReadBool(_T("/include_comments_below"), true);
1917     m_LogDeclar = cfg->ReadBool(_T("/include_declarations_log"), true);
1918     m_LogComVariab = cfg->ReadBool(_T("/include_log_comments_variable"), true);
1919 
1920     int docsOpt = cfg->ReadInt(_T("/show_docs_window"), 1);
1921     if (docsOpt == 0)
1922         m_DocsShowOption = dsoAlways;
1923     else if (docsOpt == 1)
1924         m_DocsShowOption = dsoOnly;
1925     else
1926         m_DocsShowOption = dsoNot;
1927 
1928     m_AutoInsertEnabled = cfg->ReadBool(_T("/auto_insert_enabled"), true);
1929     m_AutoInsert.ReadAIOptions();
1930 
1931     if (!m_pFortranLog && m_LogUseWindow)
1932     {
1933         CreateLogWindow();
1934     }
1935     else if (m_pFortranLog && !m_LogUseWindow)
1936     {
1937         RemoveLogWindow(false);
1938     }
1939 
1940     m_ConstrHighlighter.ReadOptions();
1941 }
1942 
WriteToLog(const wxString & text)1943 void FortranProject::WriteToLog(const wxString& text)
1944 {
1945     if (m_pFortranLog)
1946     {
1947         m_pFortranLog->WriteToInfoWindow(text);
1948     }
1949 }
1950 
CreateLogWindow()1951 void FortranProject::CreateLogWindow()
1952 {
1953     m_pFortranLog = new FInfoWindow();
1954 }
1955 
RemoveLogWindow(bool appShutDown)1956 void FortranProject::RemoveLogWindow(bool appShutDown)
1957 {
1958     if (appShutDown)
1959         return;
1960 
1961     if (m_pFortranLog)
1962     {
1963         m_pFortranLog->RemoveFromNotebook();
1964         m_pFortranLog = 0L;
1965     }
1966 }
1967 
OnJumpBack(wxCommandEvent & event)1968 void FortranProject::OnJumpBack(wxCommandEvent& event)
1969 {
1970     JumpTracker* jTr = m_pNativeParser->GetJumpTracker();
1971 
1972     if (!jTr->IsJumpBackEmpty())
1973     {
1974         jTr->MakeJumpBack();
1975         CheckEnableToolbar();
1976         JumpToLine(jTr->GetHomeAddress());
1977     }
1978 }
1979 
OnJumpHome(wxCommandEvent & event)1980 void FortranProject::OnJumpHome(wxCommandEvent& event)
1981 {
1982     JumpTracker* jTr = m_pNativeParser->GetJumpTracker();
1983 
1984     if (!jTr->IsJumpHomeEmpty())
1985         JumpToLine(jTr->GetHomeAddress());
1986 }
1987 
OnJumpForward(wxCommandEvent & event)1988 void FortranProject::OnJumpForward(wxCommandEvent& event)
1989 {
1990     JumpTracker* jTr = m_pNativeParser->GetJumpTracker();
1991     if (!jTr->IsJumpForwardEmpty())
1992     {
1993         jTr->MakeJumpForward();
1994         CheckEnableToolbar();
1995         JumpToLine(jTr->GetHomeAddress());
1996     }
1997 }
1998 
CheckEnableToolbar()1999 void FortranProject::CheckEnableToolbar()
2000 {
2001     m_pToolbar->EnableTool(XRCID("idFortProjBack"), !m_pNativeParser->GetJumpTracker()->IsJumpBackEmpty());
2002     m_pToolbar->EnableTool(XRCID("idFortProjHome"), !m_pNativeParser->GetJumpTracker()->IsJumpHomeEmpty());
2003     m_pToolbar->EnableTool(XRCID("idFortProjForward"), !m_pNativeParser->GetJumpTracker()->IsJumpForwardEmpty());
2004 
2005     wxMenuItem* pJumpBack = m_FortranToolsMenu->FindItem(idMenuJumpBack);
2006     pJumpBack->Enable(!m_pNativeParser->GetJumpTracker()->IsJumpBackEmpty());
2007     wxMenuItem* pJumpHome = m_FortranToolsMenu->FindItem(idMenuJumpHome);
2008     pJumpHome->Enable(!m_pNativeParser->GetJumpTracker()->IsJumpHomeEmpty());
2009     wxMenuItem* pJumpForward = m_FortranToolsMenu->FindItem(idMenuJumpForward);
2010     pJumpForward->Enable(!m_pNativeParser->GetJumpTracker()->IsJumpForwardEmpty());
2011 }
2012 
JumpToLine(const LineAddress & adr)2013 void FortranProject::JumpToLine(const LineAddress& adr)
2014 {
2015     if (!IsAttached() || Manager::IsAppShuttingDown())
2016         return;
2017 
2018     EditorManager* edMan = Manager::Get()->GetEditorManager();
2019     if (cbEditor* ed = edMan->Open(adr.GetFilename()))
2020     {
2021         ed->GotoLine(adr.GetLineNumber());
2022     }
2023 }
2024 
OnDebuggerStarted(CodeBlocksEvent & event)2025 void FortranProject::OnDebuggerStarted(CodeBlocksEvent& event)
2026 {
2027     event.Skip();
2028     m_IsDebugging = true;
2029 }
2030 
OnDebuggerFinished(CodeBlocksEvent & event)2031 void FortranProject::OnDebuggerFinished(CodeBlocksEvent& event)
2032 {
2033     event.Skip();
2034     m_IsDebugging = false;
2035 }
2036 
OnGenerateMakefile(wxCommandEvent & event)2037 void FortranProject::OnGenerateMakefile(wxCommandEvent& event)
2038 {
2039     m_pNativeParser->GenMakefile();
2040 }
2041 
OnChangeCase(wxCommandEvent & event)2042 void FortranProject::OnChangeCase(wxCommandEvent& event)
2043 {
2044     ChangeCase changCaseDlg(Manager::Get()->GetAppWindow());
2045     changCaseDlg.ShowModal();
2046 }
2047 
OnTab2Space(wxCommandEvent & event)2048 void FortranProject::OnTab2Space(wxCommandEvent& event)
2049 {
2050     Tab2Space tab2SpaceDlg(Manager::Get()->GetAppWindow());
2051     tab2SpaceDlg.ShowModal();
2052 }
2053 
OnBindTo(wxCommandEvent & event)2054 void FortranProject::OnBindTo(wxCommandEvent& event)
2055 {
2056     cbProject* pr = Manager::Get()->GetProjectManager()->GetActiveProject();
2057     if (pr)
2058         pr->SaveAllFiles();
2059 
2060     Bindto bindto(Manager::Get()->GetAppWindow(), m_pNativeParser->GetParser());
2061     bindto.ShowModal();
2062 }
2063 
OnFormatIndent(wxCommandEvent & event)2064 void FortranProject::OnFormatIndent(wxCommandEvent& event)
2065 {
2066     FormatIndent indent;
2067     indent.Format();
2068 }
2069 
OnReparseEditorTimer(wxTimerEvent & event)2070 void FortranProject::OnReparseEditorTimer(wxTimerEvent& event)
2071 {
2072     m_pNativeParser->ReparseCurrentEditor();
2073 }
2074 
GetIncludeFilename(cbStyledTextCtrl * control)2075 wxString FortranProject::GetIncludeFilename(cbStyledTextCtrl* control)
2076 {
2077     if (!control)
2078         return wxEmptyString;
2079     wxString strName;
2080     int style = control->GetStyleAt(control->GetCurrentPos());
2081     if (style == wxSCI_F_STRING1 || style == wxSCI_F_STRING2 || style == wxSCI_F_PREPROCESSOR)
2082     {
2083         wxString curLine = control->GetCurLine().Lower();
2084         if (!curLine.Trim(false).StartsWith(_T("include")) &&
2085             !curLine.Trim(false).StartsWith(_T("#include")))
2086             return wxEmptyString;
2087 
2088         int pos   = control->GetCurrentPos();
2089         int lineCur = control->LineFromPosition(pos);
2090         int lineStartPos = control->PositionFromLine(lineCur);
2091         wxString strBefore = control->GetTextRange(lineStartPos, pos).Lower().Trim(false);
2092         int idx1 = strBefore.Find('"', true);
2093         int idx2 = strBefore.Find('\'', true);
2094         int idx3 = strBefore.Find('<', true);
2095         if ((idx1 == wxNOT_FOUND && idx2 == wxNOT_FOUND && idx3 == wxNOT_FOUND) ||
2096             (idx1 != wxNOT_FOUND && idx2 != wxNOT_FOUND) ||
2097             (idx1 != wxNOT_FOUND && idx3 != wxNOT_FOUND) ||
2098             (idx2 != wxNOT_FOUND && idx3 != wxNOT_FOUND))
2099             return wxEmptyString;
2100         int idx = (idx1 != wxNOT_FOUND) ? idx1 : idx2;
2101         idx = (idx != wxNOT_FOUND) ? idx : idx3;
2102         if (strBefore.Mid(0,idx).Trim().Trim(false) != _T("include") &&
2103             strBefore.Mid(0,idx).Trim().Trim(false) != _T("#include"))
2104             return wxEmptyString;
2105 
2106         wxChar ch = curLine[idx];
2107         if (ch == '<')
2108             ch = '>';
2109         wxString strLast = curLine.Mid(strBefore.size());
2110         int idxL = strLast.Find(ch);
2111         if (idxL == wxNOT_FOUND)
2112             return wxEmptyString;
2113         int idxE = strBefore.size() + idxL;
2114         strName = curLine.Mid(idx+1, idxE-(idx+1)).Trim().Trim(false);
2115     }
2116     return strName;
2117 }
2118 
OnMenuEditPaste(wxCommandEvent & event)2119 void FortranProject::OnMenuEditPaste(wxCommandEvent& event)
2120 {
2121     // Process clipboard data only if we have the focus
2122     if (!IsAttached() || !m_InitDone)
2123     {
2124         event.Skip();
2125         return;
2126     }
2127     if (m_pNativeParser->GetWorkspaceBrowser())
2128         m_pNativeParser->GetWorkspaceBrowser()->OnMenuEditPaste(event);
2129     else
2130         event.Skip();
2131 }
2132 
GetDocumentation(const CCToken & tok)2133 wxString FortranProject::GetDocumentation(const CCToken& tok)
2134 {
2135     if (tok.id == wxNOT_FOUND || m_DocsShowOption == dsoNot)
2136         return wxEmptyString;
2137 
2138     TokensArrayFlat* tokens = m_TokensCCList.GetTokens();
2139     if (tok.id >= int(tokens->GetCount()))
2140         return wxEmptyString;
2141 
2142     wxString doc;
2143     TokenFlat* token = tokens->Item(tok.id);
2144 
2145     bool hasDoc;
2146     doc = HtmlDoc::GenerateHtmlDoc(token, tok.id, hasDoc);
2147     if (m_DocsShowOption == dsoOnly && !hasDoc)
2148         return wxEmptyString;
2149 
2150     return doc;
2151 }
2152 
OnDocumentationLink(wxHtmlLinkEvent & event,bool & dismissPopup)2153 wxString FortranProject::OnDocumentationLink(wxHtmlLinkEvent& event, bool& dismissPopup)
2154 {
2155     bool isGoto = false;
2156     long int tokenIdx;
2157     wxString doc = HtmlDoc::OnDocumentationLink(event, dismissPopup, isGoto, tokenIdx);
2158 
2159     if (isGoto)
2160     {
2161         TokensArrayFlat* tokens = m_TokensCCList.GetTokens();
2162         if (tokenIdx >= long(tokens->GetCount()))
2163             return wxEmptyString;
2164 
2165         TokenFlat* pToken = tokens->Item(tokenIdx);
2166         if ( pToken->m_Filename.EndsWith(UnixFilename(_T("/fortranproject/fortran_intrinsic_modules.f90")))
2167           || pToken->m_Filename.EndsWith(UnixFilename(_T("/fortranproject/fortran_procedures.f90"))) )
2168         {
2169             // don't go to fortran_intrinsic_modules.f90
2170             dismissPopup = false;
2171             return doc;
2172         }
2173         cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2174         if (!ed)
2175             return doc;
2176         if (!GotoToken(pToken, ed))
2177             dismissPopup = false;
2178     }
2179     return doc;
2180 }
2181 
OnShowCallTreeView(wxCommandEvent & event)2182 void FortranProject::OnShowCallTreeView(wxCommandEvent& event)
2183 {
2184     CodeBlocksDockEvent evt(event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
2185     evt.pWindow = (wxWindow*)m_pCallTree->GetCallTreeView();
2186     Manager::Get()->ProcessEvent(evt);
2187 }
2188 
OnShowCallTree(wxCommandEvent & event)2189 void FortranProject::OnShowCallTree(wxCommandEvent& event)
2190 {
2191     bool showCallTree = false;
2192     if (event.GetId() == idShowCallTree)
2193         showCallTree = true;
2194 
2195     ShowCallTree(showCallTree);
2196 }
2197 
ShowCallTree(bool showCallTree)2198 void FortranProject::ShowCallTree(bool showCallTree)
2199 {
2200     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2201     if (!ed)
2202         return;
2203 
2204     if (!m_pNativeParser->IsFileFortran(ed->GetFilename()))
2205         return;
2206 
2207     // Make fortran keyword set
2208     std::set< wxString> keywordSet;
2209     EditorColourSet* theme = ed->GetColourSet();
2210     if (!theme)
2211         return;
2212 
2213     HighlightLanguage lang = theme->GetLanguageForFilename(_T(".")+wxFileName(ed->GetFilename()).GetExt());
2214     for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
2215     {
2216         wxString keywords = theme->GetKeywords(lang, i);
2217         wxStringTokenizer tkz(keywords, _T(" \t\r\n"), wxTOKEN_STRTOK);
2218         while (tkz.HasMoreTokens())
2219         {
2220             wxString kw = tkz.GetNextToken();
2221             if (keywordSet.find(kw) != keywordSet.end())
2222                 continue;
2223             keywordSet.insert(kw);
2224         }
2225     }
2226 
2227     wxString NameUnderCursor;
2228     bool isOperator;
2229     if(!EditorHasNameUnderCursor(NameUnderCursor, isOperator))
2230         return;
2231     if (isOperator)
2232         return;
2233 
2234     ParserF* pParser = m_pNativeParser->GetParser();
2235     m_pCallTree->BuildCallTree(ed, NameUnderCursor, pParser, keywordSet, showCallTree);
2236 }
2237 
LoadFortranKeywordImages()2238 void FortranProject::LoadFortranKeywordImages()
2239 {
2240     wxString prefix = ConfigManager::GetDataFolder() + _T("/images/fortranproject/");
2241     m_FKImages[16] = cbLoadBitmap(prefix + _T("16x16/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2242     m_FKImages[20] = cbLoadBitmap(prefix + _T("20x20/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2243     m_FKImages[24] = cbLoadBitmap(prefix + _T("24x24/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2244     m_FKImages[28] = cbLoadBitmap(prefix + _T("28x28/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2245     m_FKImages[32] = cbLoadBitmap(prefix + _T("32x32/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2246     m_FKImages[40] = cbLoadBitmap(prefix + _T("40x40/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2247     m_FKImages[48] = cbLoadBitmap(prefix + _T("48x48/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2248     m_FKImages[56] = cbLoadBitmap(prefix + _T("56x56/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2249     m_FKImages[64] = cbLoadBitmap(prefix + _T("64x64/fortran_keyword.png"), wxBITMAP_TYPE_PNG);
2250 }
2251 
GetFortranKeywordImage(int height)2252 wxBitmap FortranProject::GetFortranKeywordImage(int height)
2253 {
2254     if (height <= 16)
2255         return m_FKImages[16];
2256     else if (height <= 20)
2257         return m_FKImages[20];
2258     else if (height <= 24)
2259         return m_FKImages[24];
2260     else if (height <= 28)
2261         return m_FKImages[28];
2262     else if (height <= 32)
2263         return m_FKImages[32];
2264     else if (height <= 40)
2265         return m_FKImages[40];
2266     else if (height <= 48)
2267         return m_FKImages[48];
2268     else if (height <= 56)
2269         return m_FKImages[56];
2270 
2271     return m_FKImages[64];
2272 }
2273