UPDATED 2012-12-15: Patch was in https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2775&group_id=5358
Some related discussion in Re: Splitting debugger in two - specific debugger and common GUI (http://forums.codeblocks.org/index.php/topic,10908.msg82193.html#msg82193)
Hi, all.
I'm thinking to save the breakpoints in debuggergdb plugin. (I don't think I have the ability to do the whole job, just plan to do this .... :()
After reading the related source code, I think the best time to "load the breakpoints from a project" is in:
void DebuggerGDB::OnProjectLoadingHook(cbProject* project, TiXmlElement* elem, bool loading)
I think both "breakpoints" list and "watch variables list" can be automatically saved to hard disk, and loaded when a project opened.
Also, we can take the source code from "browsetracker.cpp" as a reference (BrowseTracker plugin).
// ----------------------------------------------------------------------------
void ProjectData::LoadLayout()
// ----------------------------------------------------------------------------
{
// Load a layout file for this project
#if defined(LOGGING)
LOGIT( _T("ProjectData::LoadLayout()for[%s]"),m_ProjectFilename.c_str() );
#endif
if (m_ProjectFilename.IsEmpty())
return ;
wxFileName fname(m_ProjectFilename);
fname.SetExt(_T("bmarks"));
BrowseTrackerLayout layout( m_pCBProject );
layout.Open(fname.GetFullPath(), m_FileBrowse_MarksArchive, m_FileBook_MarksArchive);
m_bLayoutLoaded = true;
}
// ----------------------------------------------------------------------------
void ProjectData::SaveLayout()
// ----------------------------------------------------------------------------
{
// Write a layout file for this project
#if defined(LOGGING)
LOGIT( _T("ProjectData::SAVELayout()") );
#endif
if (m_ProjectFilename.IsEmpty())
return ;
wxFileName fname(m_ProjectFilename);
fname.SetExt(_T("bmarks"));
BrowseTrackerLayout layout( m_pCBProject );
////DumpBrowse_Marks(wxT("BookMarks"));
////DumpBrowse_Marks(wxT("BrowseMarks"));
layout.Save(fname.GetFullPath(), m_FileBrowse_MarksArchive, m_FileBook_MarksArchive);
//// *Testing* See if cbEditor is actually there
//EditorBase* eb = m_EditorBaseArray[1];
//cbEditor* cbed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(eb);
//cbStyledTextCtrl* control = cbed->GetControl();
//#if defined(LOGGING)
//LOGIT( _T("eb[%p]cbed[%p]control[%p]"), eb, cbed, control );
//#endif
//// *Testing* Check against our array
//eb = m_EditorBaseArray[1];
//cbed = m_cbEditorArray[1];
//control = m_cbSTCArray[1];
//#if defined(LOGGING)
//LOGIT( _T("eb[%p]cbed[%p]control[%p]"), eb, cbed, control );
//#endif
}
But I can't determine which Event is good for loading breakpoints.
// -- Project events
// EVT_PROJECT_OPEN( BrowseTracker::OnProjectOpened)
Manager::Get()->RegisterEventSink(cbEVT_PROJECT_OPEN, new cbEventFunctor<BrowseTracker, CodeBlocksEvent>(this, &BrowseTracker::OnProjectOpened));
// EVT_PROJECT_CLOSE( BrowseTracker::OnProjectOpened)
Manager::Get()->RegisterEventSink(cbEVT_PROJECT_CLOSE, new cbEventFunctor<BrowseTracker, CodeBlocksEvent>(this, &BrowseTracker::OnProjectClosing));
// EVT_PROJECT_ACTIVATE( BrowseTracker::OnProjectActivated)
Manager::Get()->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<BrowseTracker, CodeBlocksEvent>(this, &BrowseTracker::OnProjectActivatedEvent));
// hook to project loading procedure
// This hook only occurs if the project has an "extension" xml entry
ProjectLoaderHooks::HookFunctorBase* myProjhook = new ProjectLoaderHooks::HookFunctor<BrowseTracker>(this, &BrowseTracker::OnProjectLoadingHook);
m_ProjectHookId = ProjectLoaderHooks::RegisterHook(myProjhook);
EVT_PROJECT_ACTIVATE?
EVT_PROJECT_OPEN?
Any Comments?
Thanks!
In the browsetracker plugin, all the "browse_Marks" will be recorded in a your_project_name.bmarks file.
It is a XML file format. For example:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<BrowseTracker_layout_file>
<ActiveTarget name="Debug" />
<File name="LearnWX1App.cpp" open="1" top="0" tabpos="4">
<Cursor position="646" topLine="0" />
<BrowseMarks positions="472,646" />
<Book_Marks positions="" />
</File>
<File name="LearnWX1Main.cpp" open="1" top="0" tabpos="1">
<Cursor position="3467" topLine="67" />
<BrowseMarks positions="1728,2413,3467" />
<Book_Marks positions="" />
</File>
<File name="LearnWX1Main.h" open="1" top="0" tabpos="2">
<Cursor position="1476" topLine="24" />
</File>
<File name="resource.rc" open="0" top="0" tabpos="0">
<Cursor position="48" topLine="0" />
</File>
<File name="wx_pch.h" open="1" top="0" tabpos="3">
<Cursor position="396" topLine="0" />
</File>
</BrowseTracker_layout_file>
Every files will be recorded, including the "tab position", the Cursor position, the "BrowseMarks positions".
Seems we can just add another entry like below
<File name="LearnWX1App.cpp" open="1" top="0" tabpos="4">
<Cursor position="646" topLine="0" />
<BrowseMarks positions="472,646" />
<Book_Marks positions="" />
<Break_Points positions= "XXXXXX, XXXXXX, XXXXXX"
</File>
Edit: This will be mess up :(, debuggerGDB plugin should have its own layout file like
your_project_name.bps
:D
Having 100 files in the project's directory is bad and ugly.
There should be only one file looking like:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<cb_project_setting>
<layout> ala bala </layout>
<debugger_plugin> ala bala </debugger_plugin>
<browse_tracker_plugin> </browse_tracker_plugin>
.
.
</cb_project_setting>
And there should be an easy way to get your data:
CProjData *data = some_manager->GetProjectSettings()->GetData("layout");
do something with the data....
some_manager->GetProjectSettings()->SetData("layout", data);
p.s. by the way, I don't know what is the API for dealing with xml inside C::B :)
We talk about data per project.
If a plugin needs some global configuration, the core can(must) still provide an interface for storing settings.
Yes, There are many *.ini files for plugin global settings in APPDATA folder.
Edit:
Some global settings were using XML format, such as "default.conf"
Others use INI file format.
For example the BrowseTracker.ini
BrowseMarksEnabled=1
BrowseMarksStyle=0
BrowseMarksToggleKey=0
LeftMouseDelay=400
BrowseMarksClearAllMethod=0
I'm not sure the core supply method to access these ini files.
Edit2
I found in cbproject.cpp, there are some code do the "your_project_name.layout" file IO.
bool cbProject::SaveLayout()
{
if (m_Filename.IsEmpty())
return false;
wxFileName fname(m_Filename);
fname.SetExt(_T("layout"));
ProjectLayoutLoader loader(this);
return loader.Save(fname.GetFullPath());
}
bool cbProject::LoadLayout()
{
if (m_Filename.IsEmpty())
return false;
int openmode = Manager::Get()->GetConfigManager(_T("project_manager"))->ReadInt(_T("/open_files"), (long int)1);
bool result = false;
if(openmode==2)
{
// Do not open any files
result = true;
}
else
{
Manager::Get()->GetEditorManager()->HideNotebook();
if(openmode == 0) // Open all files
{
FilesList::Node* node = m_Files.GetFirst();
while(node)
{
ProjectFile* f = node->GetData();
Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath(),0,f);
node = node->GetNext();
}
result = true;
}
else if(openmode == 1)// Open last open files
{
wxFileName fname(m_Filename);
fname.SetExt(_T("layout"));
ProjectLayoutLoader loader(this);
if (loader.Open(fname.GetFullPath()))
{
typedef std::map<int, ProjectFile*> open_files_map;
open_files_map open_files;
// Get all files to open and sort them according to their tab-position:
FilesList::Node* node = m_Files.GetFirst();
while(node)
{
ProjectFile* f = node->GetData();
if (f->editorOpen)
open_files[f->editorTabPos] = f;
node = node->GetNext();
}
// Load all requested files
std::vector<LoaderBase*> filesInMemory;
for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
{
filesInMemory.push_back(Manager::Get()->GetFileManager()->Load((*it).second->file.GetFullPath()));
}
// Open all requested files:
size_t i = 0;
for (open_files_map::iterator it = open_files.begin(); it != open_files.end(); ++it)
{
cbEditor* ed = Manager::Get()->GetEditorManager()->Open(filesInMemory[i], (*it).second->file.GetFullPath(),0,(*it).second);
if (ed)
ed->SetProjectFile((*it).second);
++i;
}
ProjectFile* f = loader.GetTopProjectFile();
if (f)
{
Manager::Get()->GetLogManager()->DebugLog(_T("Top Editor: ") + f->file.GetFullPath());
EditorBase* eb = Manager::Get()->GetEditorManager()->Open(f->file.GetFullPath());
if(eb)
eb->Activate();
}
// Manager::Get()->GetAppWindow()->Thaw();
}
result = true;
}
else
result = false;
Manager::Get()->GetEditorManager()->ShowNotebook();
}
return result;
}
Hi, I'm doing a simple test.
Here is a breakpoints layout file
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<BreakPoints_layout_file>
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="58" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="74" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="71" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="68" />
<BreakPoint file="C:/test/test_for_cb_forum/main.cpp" position="66" />
</BreakPoints_layout_file>
My question is:
Is there any way I can change the absolute file name to relative file name?
Since my code is like this:
// ----------------------------------------------------------------------------
bool BreakPointsLayout::Save(const wxString& filename, BreakpointsList &breakPointsList)
// ----------------------------------------------------------------------------
{
const char* ROOT_TAG = "BreakPoints_layout_file";
TiXmlDocument doc;
doc.SetCondenseWhiteSpace(false);
doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
TiXmlElement* rootnode = static_cast<TiXmlElement*>(doc.InsertEndChild(TiXmlElement(ROOT_TAG)));
if (!rootnode)
return false;
for (int i = breakPointsList.GetCount() - 1; i >= 0; --i)
{
DebuggerBreakpoint* bp = breakPointsList[i];
//Only save the breakpoints belong to the current project
if (bp->userData == m_pProject)
{
Manager::Get()->GetLogManager()->DebugLog(F(_T("Got one")));
TiXmlElement* node = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("BreakPoint")));
node->SetAttribute("file", cbU2C(bp->filename));
node->SetAttribute("position", bp->line);
//RemoveBreakpoint(i, true);
}
}
return cbSaveTinyXMLDocument(&doc, filename);
}
bp->filename is a full name.
But in debugger_def.h
struct DebuggerBreakpoint
{
enum BreakpointType
{
bptCode = 0, ///< Normal file/line breakpoint
bptFunction, ///< Function signature breakpoint
bptData ///< Data breakpoint
};
/** Constructor.
* Sets default values for members.
*/
DebuggerBreakpoint()
: type(bptCode),
line(0),
index(-1),
temporary(false),
enabled(true),
active(true),
useIgnoreCount(false),
ignoreCount(0),
useCondition(false),
wantsCondition(false),
address(0),
alreadySet(false),
breakOnRead(false),
breakOnWrite(true),
userData(0)
{}
BreakpointType type; ///< The type of this breakpoint.
wxString filename; ///< The filename for the breakpoint (kept as relative).
It said the file name should keep as relative. :(
Any Comments?
Thanks
You can use the MakeRelativeTo-function of wxFileName.
The code below is not tested !
wxFileName fname;
for (int i = breakPointsList.GetCount() - 1; i >= 0; --i)
{
DebuggerBreakpoint* bp = breakPointsList[i];
//Only save the breakpoints belong to the current project
if (bp->userData == m_pProject)
{
Manager::Get()->GetLogManager()->DebugLog(F(_T("Got one")));
TiXmlElement* node = static_cast<TiXmlElement*>(rootnode->InsertEndChild(TiXmlElement("BreakPoint")));
fname.Assign(bp->filename);
if (fname.IsAbsolute())
{
fname.MakeRelativeTo(m_pProject->GetBasePath());
}
node->SetAttribute("file", cbU2C(fname.GetFullPath()));
node->SetAttribute("position", bp->line);
//RemoveBreakpoint(i, true);
}
}
Instead of m_pProject->GetBasePath() it might also make sense to use m_pProject->GetCommonTopLevelPath().
Depends on what the debugger-plugin wants.
No time to look into it at themoment.
Sorry, I can't figure how to check if the current file was opened in cbEditor. Can someone help me?
here is the patch.( The patch has include some patches from oBFusCATed adding a command entry)
http://sites.google.com/site/studycodeblocks/imagescb/breakpoints.patch This patch has been updated in
https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2775&group_id=5358
Here is some explanation about this patch:
1, I add two files: breakpointslayout.cpp and breakpointslayout.h(They are similar with code in BrowserTracker plugin)
2, I add some code in doing DebuggerGDB::OnProjectOpened and DebuggerGDB::OnProjectClosed
3, When a project is opened, I will use Addbreakpoints function in debuggerstate structure.
4, when a project is closed, I just read the breakpointsList in debuggerstate, then save them to a xml file.
5, I still can't find a way to notify cbEditor, so, the breakpoints add silently. :(
By the way, I can't upload an attachment any more(upload folder is FULL), can someone help me? :(
Thanks
Edit:
Here are some ideas:
class ProjectFile : public BlockAllocated<ProjectFile, 1000>
{
......
/** If true, the file is open inside an editor. */
bool editorOpen; // layout info
Can we use "ProjectFile" related method?
You can try:
EditorBase* eb = Manager::Get()->GetEditorManager()->IsOpen(filename);
or
cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filename );
Both return NULL if not opened.
@jens
Thanks very much! I will try it....So excited!!!
Edit:
Problem solved!!!! Works perfectly! Just change this function to:
// ----------------------------------------------------------------------------
bool BreakpointsLayout::Load(const wxString& filename, DebuggerState * pDebuggerState)
// ----------------------------------------------------------------------------
{
TiXmlDocument doc;
if (!TinyXML::LoadDocument(filename, &doc))
return false;
TiXmlElement* root;
TiXmlElement* elem;
wxString fname;
ProjectFile* pf;
root = doc.FirstChildElement("Breakpoints_layout_file");
if (!root)
{
return false;
}
elem = root->FirstChildElement("Breakpoint");
while (elem)
{
wxString fname = cbC2U(elem->Attribute("file"));
ProjectFile* pf;
if (fname.IsEmpty())
{
break;
}
else
pf = m_pProject->GetFileByFilename(fname);
wxString filenamePath = pf->file.GetFullPath();
int line = 0;
if (not elem->QueryIntAttribute("position", &line) == TIXML_SUCCESS)
break;
//Manager::Get()->GetLogManager()->DebugLog(F(_T("file='%s', line='%d'"), filenamePath.c_str(), line));
cbEditor* ed = Manager::Get()->GetEditorManager()->IsBuiltinOpen( filenamePath );
if (ed==NULL){
pDebuggerState->AddBreakpoint(filenamePath,line); //add breakpoints silently
Manager::Get()->GetLogManager()->DebugLog(F(_T("silently add bp file='%s', line='%d'"), filenamePath.c_str(), line));
}
else{
ed->AddBreakpoint(line);
Manager::Get()->GetLogManager()->DebugLog(F(_T("add bp from editor file='%s', line='%d'"), filenamePath.c_str(), line));
}
elem = elem->NextSiblingElement();
}
return true;
}//Load
Hi, every one.
I have finished the finally patch.
Both watches and breakpoints can be persistent with the current project.
I add the patch here:
https://developer.berlios.de/patch/index.php?func=detailpatch&patch_id=2775&group_id=5358
Here is the example of a projectname.bps file
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Debugger_layout_file>
<BreakpointsList>
<Breakpoint file="main.cpp" position="67" />
<Breakpoint file="main.cpp" position="69" />
<Breakpoint file="main.cpp" position="62" />
<Breakpoint file="main.cpp" position="58" />
</BreakpointsList>
<WatchesList>
<Watch variable="abcdefg" />
<Watch variable="llll" />
</WatchesList>
</Debugger_layout_file>
Any comments and enhancements are welcome!!
Thanks. :D
The same is for watches: might be watches as character, hex ..., or as array with begin and count.
Notice that watches can already be saved / loaded (including this info).
Where can I find the code? (save watches with other info?)
The only code I can find is:
void DebuggerTree::OnSaveWatchFile(wxCommandEvent& event)
{
// Verify that there ARE watches to save
size_t wc = m_Watches.GetCount();
if (wc<1)
{
cbMessageBox(_("There are no watches in the list to save."),
_("Save Watches"), wxICON_ERROR);
return;
}
wxString fname;
wxFileDialog dlg (Manager::Get()->GetAppWindow(),
_T("Save debugger watch file"),
_T(""),
_T(""),
_T("Watch files (*.watch)|*.watch|Any file (*)|*"),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
PlaceWindow(&dlg);
if (dlg.ShowModal() != wxID_OK)
return;
wxTextFile tf(dlg.GetPath());
bool bSuccess = false;
// Create() will fail if the file exist -> must use Open() if file exist
if (tf.Exists())
{
bSuccess = tf.Open();
if (bSuccess) tf.Clear(); // remove old content (if any)
}
else
{
bSuccess = tf.Create();
}
if (bSuccess)
{
// iterate over each watch and write them to the file buffer
for (size_t i = 0; i < wc; ++i)
{
Watch& w = m_Watches[i];
tf.AddLine(w.keyword);
}
tf.Write(); // Write buffer to file
tf.Close(); // release file handle
}
else
Manager::Get()->GetLogManager()->DebugLog(_T("Error opening debugger watch file: ") + fname);
}
Seems no other info was saved :(
What about the code used to save .depend, .layout, .bmarks files?
Al-right... now I know what you mean. I thought you only think about the .layout stuff and additional stuff.
I was thinking of providing a function like GetUserFilePath(...)
Well what about if you return a proper file instead and simply change the the methods to create its own node in this fiel instead of creating an new one? The file would look then like:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_meta_data_file>
<debugger>
<bmarks>
</bmarks>
<connectivity>
</connectivity>
</debugger>
<core>
<layout>
</layout>
<stuff_from_core_like_project_settings>
</stuff_from_core_like_project_settings>
</core>
</CodeBlocks_project_meta_data_file>
Note that with the .depend file it is different: It comes from a 3rd party lib and we cannot change it to something else, nor integrate it,not even move it most likely without changing the depends lib.
...whatever we choose it requires some refactoring, for sure.
Well what about if you return a proper file instead and simply change the the methods to create its own node in this fiel instead of creating an new one? The file would look then like:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<CodeBlocks_project_meta_data_file>
<debugger>
<bmarks>
</bmarks>
<connectivity>
</connectivity>
</debugger>
<core>
<layout>
</layout>
<stuff_from_core_like_project_settings>
</stuff_from_core_like_project_settings>
</core>
</CodeBlocks_project_meta_data_file>
The sdk can have a interface like:
Manager::Get()->GetProjectMetaDataManager(_T("core")) //Get the "core" Node.
which return a Node pointer, then the client plugins can write and wrote on these Node/blocks.