2
* This file is part of wxSmith plugin for Code::Blocks Studio
3
* Copyright (C) 2006-2007 Bartlomiej Swiecki
5
* wxSmith is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* wxSmith is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with wxSmith. If not, see <http://www.gnu.org/licenses/>.
19
* $Id: wxscoder.cpp 4850 2008-01-29 21:45:49Z byo $
20
* $HeadURL: svn://svn.berlios.de/codeblocks/tags/8.02/src/plugins/contrib/wxSmith/wxscoder.cpp $
26
#include <editormanager.h>
27
#include <configmanager.h>
28
#include <logmanager.h>
29
#include <encodingdetector.h>
33
#include "cbstyledtextctrl.h"
37
bool ReadFileContentWithProperEncoding(const wxString& FileName,wxString& Content,wxFontEncoding& Encoding,bool& UseBOM)
39
EncodingDetector Detector(FileName);
40
if ( !Detector.IsOK() ) return false;
41
Encoding = Detector.GetFontEncoding();
42
if ( Encoding == wxFONTENCODING_ISO8859_1 )
44
wxString enc_name = Manager::Get()->GetConfigManager(_T("editor"))->Read(_T("/default_encoding"), wxLocale::GetSystemEncodingName());
45
Encoding = wxFontMapper::GetEncodingFromName(enc_name);
47
UseBOM = Detector.UsesBOM();
48
wxFile Fl(FileName,wxFile::read);
49
if ( !Fl.IsOpened() ) return false;
50
if ( !cbRead(Fl,Content,Encoding) ) return false;
51
Content.Remove(0,Detector.GetBOMSizeInBytes() / sizeof(wxChar));
56
static wxsCoder SingletonObject;
57
wxsCoder* wxsCoder::Singleton = &SingletonObject;
61
FlushTimer.SetOwner(this,1);
62
Connect(wxEVT_TIMER,(wxObjectEventFunction)&wxsCoder::FlushTimerEvent);
70
void wxsCoder::AddCode(const wxString& FileName,const wxString& Header,const wxString& End,const wxString& Code,bool Immediately,bool CodeHasHeader,bool CodeHasEnd)
72
wxMutexLocker Lock(DataMutex);
74
wxString FixedFileName = NormalizeFileName(FileName);
75
if ( FixedFileName.IsEmpty() )
81
int Index = CodeChangesFiles.Index(FileName);
82
if ( Index==wxNOT_FOUND )
84
Index = CodeChangesFiles.Count();
85
CodeChangesFiles.Add(FileName);
89
// Add entry to list of waiting changes
90
CodeChange* Change = new CodeChange;
91
Change->Header = Header;
94
Change->CodeHasHeader = CodeHasHeader;
95
Change->CodeHasEnd = CodeHasEnd;
96
Change->Next = CodeChanges[Index];
97
CodeChanges[Index] = Change;
99
// If the change has already been put onto queue, delete it
100
for ( CodeChange *Prev=Change, *This=Prev->Next; This; Prev=This, This=This->Next )
102
if ( This->Header==Header && This->End==End )
104
Prev->Next = This->Next;
112
FlushFile(FixedFileName);
116
wxString wxsCoder::GetCode(const wxString& FileName,const wxString& Header,const wxString& End,bool IncludeHeader,bool IncludeEnd)
118
wxMutexLocker Lock(DataMutex);
120
wxString FixedFileName = NormalizeFileName(FileName);
121
FlushFile(FixedFileName);
123
int TabSize = Manager::Get()->GetConfigManager(_T("editor"))->ReadInt(_T("/tab_size"), 4);
125
// Checking if editor is opened
126
EditorManager* EM = Manager::Get()->GetEditorManager();
128
cbEditor* Editor = EM->GetBuiltinEditor(FixedFileName);
132
cbStyledTextCtrl* Ctrl = Editor->GetControl();
133
Ctrl->SetSearchFlags(wxSCI_FIND_MATCHCASE);
134
Ctrl->SetTargetStart(0);
135
Ctrl->SetTargetEnd(Ctrl->GetLength());
136
int Position = Ctrl->SearchInTarget(Header);
137
if ( Position == -1 ) return _T("");
139
// Counting number of indentation spaces which will be removed at
140
// the beginning of each line
142
int SpacesPos = Position;
143
while ( --SpacesPos >= 0 )
145
wxChar ch = Ctrl->GetCharAt(SpacesPos);
146
if ( ch == _T('\t') ) SpacesCut += TabSize;
147
else if ( (ch==_T('\n')) || (ch==_T('\r')) ) break;
151
Ctrl->SetTargetStart(Position);
152
Ctrl->SetTargetEnd(Ctrl->GetLength());
153
int EndPosition = Ctrl->SearchInTarget(End);
154
if ( EndPosition == -1 ) return _T("");
156
// Fixing up positions to include / exclude header and/or ending sequence
157
if ( !IncludeHeader ) Position += Header.Length();
158
if ( IncludeEnd ) EndPosition += End.Length();
159
return CutSpaces(Ctrl->GetTextRange(Position,EndPosition),SpacesCut);
164
wxFontEncoding Encoding;
166
if ( !ReadFileContentWithProperEncoding(FixedFileName,Content,Encoding,UseBOM) ) return _T("");
168
int Position = Content.First(Header);
169
if ( Position == -1 ) return _T("");
171
int SpacesPos = Position;
172
while ( --SpacesPos >= 0 )
174
wxChar ch = Content.GetChar(SpacesPos);
175
if ( ch == _T('\t') ) SpacesCut += TabSize;
176
else if ( (ch==_T('\n')) || (ch==_T('\r')) ) break;
180
if ( !IncludeHeader ) Position += Header.Length();
181
Content.Remove(0,Position);
182
int EndPosition = Content.First(End);
183
if ( EndPosition == -1 ) return _T("");
184
if ( IncludeEnd ) EndPosition += End.Length();
185
Content.Remove(EndPosition);
186
return CutSpaces(Content,SpacesCut);
190
wxString wxsCoder::GetFullCode(const wxString& FileName,wxFontEncoding& Encoding,bool &UseBOM)
192
wxMutexLocker Lock(DataMutex);
194
wxString FixedFileName = NormalizeFileName(FileName);
195
FlushFile(FixedFileName);
197
// Checking if editor is opened
198
EditorManager* EM = Manager::Get()->GetEditorManager();
200
cbEditor* Editor = EM->GetBuiltinEditor(FixedFileName);
204
Encoding = Editor->GetEncoding();
205
UseBOM = Editor->GetUseBom();
206
cbStyledTextCtrl* Ctrl = Editor->GetControl();
207
return Ctrl->GetText();
212
if ( !ReadFileContentWithProperEncoding(FixedFileName,Content,Encoding,UseBOM) ) return _T("");
217
void wxsCoder::PutFullCode(const wxString& FileName,const wxString& Code,wxFontEncoding Encoding,bool UseBOM)
219
wxMutexLocker Lock(DataMutex);
221
wxString FixedFileName = NormalizeFileName(FileName);
222
int Index = CodeChangesFiles.Index(FixedFileName);
223
if ( Index != wxNOT_FOUND )
225
for ( CodeChange* Change=CodeChanges[Index]; Change; )
227
CodeChange* Next = Change->Next;
231
CodeChanges[Index] = 0;
234
// Searching for file in opened file list
235
EditorManager* EM = Manager::Get()->GetEditorManager();
237
cbEditor* Editor = EM->GetBuiltinEditor(FixedFileName);
241
Editor->GetControl()->SetText(Code);
245
cbSaveToFile(FixedFileName,Code,Encoding,UseBOM);
249
void wxsCoder::FlushFile(const wxString& FileName)
251
int Index = CodeChangesFiles.Index(FileName);
252
if ( Index == wxNOT_FOUND ) return;
254
CodeChange* Changes = CodeChanges[Index];
255
if ( !Changes ) return;
257
// Searching for file in opened file list
258
EditorManager* EM = Manager::Get()->GetEditorManager();
260
cbEditor* Editor = EM->GetBuiltinEditor(FileName);
267
CodeChange* Next = Changes->Next;
268
ApplyChangesEditor(Editor,Changes->Header,Changes->End,Changes->Code,Changes->CodeHasHeader,Changes->CodeHasEnd,EOL);
275
// Reading file content
277
wxFontEncoding Encoding;
280
bool HasChanged = false;
283
if ( !ReadFileContentWithProperEncoding(FileName,Content,Encoding,UseBOM) )
285
Manager::Get()->GetLogManager()->DebugLog(F(_("wxSmith: Couldn't open file '%s'"),FileName.c_str()));
288
//Manager::Get()->GetLogManager()->DebugLog(F(_T("File read time: %d ms"),SW.Time()));
292
CodeChange* Next = Changes->Next;
293
ApplyChangesString(Content,Changes->Header,Changes->End,Changes->Code,Changes->CodeHasHeader,Changes->CodeHasEnd,HasChanged,EOL);
300
// Storing the result
302
cbSaveToFile(FileName,Content,Encoding,UseBOM);
303
//Manager::Get()->GetLogManager()->DebugLog(F(_T("File write time: %d ms"),SW.Time()));
307
CodeChanges[Index] = 0;
310
bool wxsCoder::ApplyChangesEditor(cbEditor* Editor,const wxString& Header,const wxString& End,wxString& Code,bool CodeHasHeader,bool CodeHasEnd,wxString& EOL)
312
cbStyledTextCtrl* Ctrl = Editor->GetControl();
313
int FullLength = Ctrl->GetLength();
317
// Detecting EOL style in source
318
for ( int i=0; i<FullLength; i++ )
320
wxChar ch = Ctrl->GetCharAt(i);
321
if ( ch==_T('\n') || ch==_T('\r') )
324
if ( ++i < FullLength )
326
wxChar ch2 = Ctrl->GetCharAt(i);
327
if ( (ch2==_T('\n') || ch2==_T('\r')) && ch!=ch2 )
337
// Searching for beginning of section to replace
338
Ctrl->SetSearchFlags(wxSCI_FIND_MATCHCASE);
339
Ctrl->SetTargetStart(0);
340
Ctrl->SetTargetEnd(FullLength);
341
int Position = Ctrl->SearchInTarget(Header);
343
if ( Position == -1 )
345
Manager::Get()->GetLogManager()->DebugLog(F(_("wxSmith: Couldn't find code with header:\n\t\"%s\"\nin file '%s'"),
347
Editor->GetFilename().c_str()));
351
// Beginning of this code block is in Position, now searching for end
352
Ctrl->SetTargetStart(Position);
353
Ctrl->SetTargetEnd(FullLength);
354
int EndPosition = Ctrl->SearchInTarget(End);
355
if ( EndPosition == -1 )
357
Manager::Get()->GetLogManager()->DebugLog(F(_("wxSmith: Unfinished block of auto-generated code with header:\n\t\"%s\"\nin file '%s'"),
359
Editor->GetFilename().c_str()));
363
// Fetching indentation
364
wxString BaseIndentation;
365
int IndentPos = Position;
366
while ( --IndentPos >= 0 )
368
wxChar ch = Ctrl->GetCharAt(IndentPos);
369
if ( (ch == _T('\n')) || (ch == _T('\r')) ) break;
371
while ( ++IndentPos < Position )
373
wxChar ch = Ctrl->GetCharAt(IndentPos);
374
BaseIndentation.Append(
375
( ch == _T('\t') ) ? _T('\t') : _T(' '));
378
Code = RebuildCode(BaseIndentation,Code.c_str(),(int)Code.Length(),EOL);
380
// Fixing up positions to contain or not header / ending sequence
381
if ( !CodeHasHeader ) Position += Header.Length();
382
if ( CodeHasEnd ) EndPosition += End.Length();
384
// Checking of code has really changed
385
if ( Ctrl->GetTextRange(Position,EndPosition) == Code )
391
Ctrl->SetTargetStart(Position);
392
Ctrl->SetTargetEnd(EndPosition);
393
Ctrl->ReplaceTarget(Code);
394
Editor->SetModified();
396
// TODO: Update fooldings
400
bool wxsCoder::ApplyChangesString(wxString& BaseContent,const wxString& Header,const wxString& End,wxString& Code,bool CodeHasHeader,bool CodeHasEnd,bool& HasChanged,wxString& EOL)
402
wxString Content = BaseContent;
405
// Detecting EOL in this sources
406
for ( size_t i=0; i<Content.Length(); i++ )
408
wxChar ch = Content.GetChar(i);
409
if ( ch==_T('\n') || ch==_T('\r') )
412
if ( ++i < Content.Length() )
414
wxChar ch2 = Content.GetChar(i);
415
if ( (ch2==_T('\n') || ch2==_T('\r')) && ch!=ch2 )
426
int Position = Content.First(Header);
428
if ( Position == -1 )
430
Manager::Get()->GetLogManager()->DebugLog(F(_("wxSmith: Couldn't find code with header:\n\t\"%s\""),Header.c_str()));
434
// Skipping header if necessary
435
int IndentPos = Position;
436
int IndentMax = Position;
437
if ( !CodeHasHeader ) Position += Header.Length();
439
wxString Result = Content.Left(Position);
440
Content.Remove(0,Position);
442
int EndPosition = Content.First(End);
443
if ( EndPosition == -1 )
445
Manager::Get()->GetLogManager()->DebugLog(F(_("wxSmith: Unfinished block of auto-generated code with header:\n\t\"%s\""),Header.c_str()));
449
// Including ending sequence if necessary
450
if ( CodeHasEnd ) EndPosition += End.Length();
452
// Fetching indentation
453
wxString BaseIndentation;
454
while ( --IndentPos >= 0 )
456
wxChar ch = Result.GetChar(IndentPos);
457
if ( (ch == _T('\n')) || (ch == _T('\r')) ) break;
459
while ( ++IndentPos < IndentMax )
461
wxChar ch = Result.GetChar(IndentPos);
462
BaseIndentation.Append(
463
( ch == _T('\t') ) ? _T('\t') : _T(' '));
466
Code = RebuildCode(BaseIndentation,Code.c_str(),Code.Length(),EOL);
468
// Checking if code has really changed
469
if ( Content.Mid(0,EndPosition) == Code )
476
Result += Content.Remove(0,EndPosition);
477
BaseContent = Result;
482
wxString wxsCoder::RebuildCode(wxString& BaseIndentation,const wchar_t* Code,int CodeLen,wxString& EOL)
485
bool UseTab = Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/use_tab"), false);
486
int TabSize = Manager::Get()->GetConfigManager(_T("editor"))->ReadInt(_T("/tab_size"), 4);
489
Tab.Append(_T(' '),TabSize);
494
int EolMode = Manager::Get()->GetConfigManager(_T("editor"))->ReadInt(_T("/eol/eolmode"), 0);
497
case 1: EOL = _T("\r"); break;
498
case 2: EOL = _T("\n"); break;
499
default: EOL = _T("\r\n");
503
BaseIndentation.Prepend(EOL);
506
Result.reserve(CodeLen+10);
512
case _T('\n'): Result << BaseIndentation; break;
513
case _T('\t'): if ( UseTab ) { Result << Tab; break; }
514
default: Result << *Code;
522
wxString wxsCoder::CutSpaces(wxString Code,int Count)
524
int TabSize = Manager::Get()->GetConfigManager(_T("editor"))->ReadInt(_T("/tab_size"), 4);
525
if ( TabSize < 1 ) TabSize = 4;
527
// Changing to \n line end mode
532
int PosN = Code.Find(_T("\n"));
533
int PosR = Code.Find(_T("\r"));
535
if ( ( PosN < 0 ) && ( PosR < 0 ) ) break;
538
if ( PosN < 0 ) Pos = PosR;
539
else if ( PosR < 0 ) Pos = PosN;
540
else Pos = (PosN < PosR) ? PosN : PosR;
542
Result.Append(Code.Left(Pos));
544
while ( Code.Length() )
546
if ( ( Code[0] != _T('\n') ) &&
547
( Code[0] != _T('\r') ) ) break;
550
int LeftSpaces = Count;
551
while ( Code.Length() && LeftSpaces > 0 )
553
if ( Code[0] == _T(' ') ) LeftSpaces--;
554
else if ( Code[0] == _T('\t') ) LeftSpaces -= TabSize;
558
Result.Append(_T('\n'));
565
wxString wxsCoder::NormalizeFileName(const wxString& FileName)
567
// Updating the file name in case there are some ".." or "." enteries which prevents from finding
568
// opened editor for file
569
wxFileName FixedNameObject(FileName);
570
FixedNameObject.Normalize(wxPATH_NORM_DOTS);
571
return FixedNameObject.GetFullPath();
574
void wxsCoder::Flush(int Delay)
583
FlushTimer.Start(Delay,true);
587
void wxsCoder::FlushAll()
590
for ( int i=0; i<(int)CodeChangesFiles.Count(); i++ )
592
FlushFile(CodeChangesFiles[i]);
595
CodeChangesFiles.Clear();
596
//Manager::Get()->GetLogManager()->DebugLog(F(_T("wxSmith: Flushing of code done in %d ms"),SW.Time()));
599
void wxsCoder::FlushTimerEvent(wxTimerEvent& event)