1
|
#include "CodeEditor.h"
|
2
|
|
3
|
namespace Upp {
|
4
|
|
5
|
void Renumber(LineInfo& lf)
|
6
|
{
|
7
|
LineInfo tf;
|
8
|
int l = 0;
|
9
|
if(lf.GetCount()) {
|
10
|
LineInfoRecord& t = tf.Add();
|
11
|
t.breakpoint = lf[0].breakpoint;
|
12
|
t.lineno = 0;
|
13
|
t.count = lf[0].count;
|
14
|
t.error = lf[0].error;
|
15
|
t.firstedited = lf[0].firstedited;
|
16
|
t.edited = lf[0].edited;
|
17
|
l += t.count;
|
18
|
}
|
19
|
for(int i = 1; i < lf.GetCount(); i++) {
|
20
|
LineInfoRecord& r = lf[i];
|
21
|
if(r.breakpoint.IsEmpty() && r.error == 0 && r.edited == 0 &&
|
22
|
tf.Top().breakpoint.IsEmpty() && tf.Top().error == 0 && tf.Top().edited == 0)
|
23
|
tf.Top().count += r.count;
|
24
|
else {
|
25
|
LineInfoRecord& t = tf.Add();
|
26
|
t.breakpoint = r.breakpoint;
|
27
|
t.error = r.error;
|
28
|
t.firstedited = r.firstedited;
|
29
|
t.edited = r.edited;
|
30
|
t.count = r.count;
|
31
|
t.lineno = l;
|
32
|
}
|
33
|
l += r.count;
|
34
|
}
|
35
|
lf = pick(tf);
|
36
|
}
|
37
|
|
38
|
void ClearBreakpoints(LineInfo& lf)
|
39
|
{
|
40
|
for(int i = 0; i < lf.GetCount(); i++)
|
41
|
lf[i].breakpoint.Clear();
|
42
|
}
|
43
|
|
44
|
void ValidateBreakpoints(LineInfo& lf)
|
45
|
{
|
46
|
for(int i = 0; i < lf.GetCount(); i++)
|
47
|
if(lf[i].breakpoint[0] == 0xe)
|
48
|
lf[i].breakpoint = "1";
|
49
|
}
|
50
|
|
51
|
void EditorBar::sPaintImage(Draw& w, int x, int y, int fy, const Image& img)
|
52
|
{
|
53
|
w.DrawImage(x, y + (fy - img.GetSize().cy) / 2, img);
|
54
|
}
|
55
|
|
56
|
void EditorBar::Paint(Draw& w)
|
57
|
{
|
58
|
Size sz = GetSize();
|
59
|
w.DrawRect(0, 0, sz.cx, sz.cy, SColorLtFace);
|
60
|
if(!editor) return;
|
61
|
int fy = editor->GetFontSize().cy;
|
62
|
int hy = fy >> 1;
|
63
|
int y = 0;
|
64
|
int i = editor->GetScrollPos().y;
|
65
|
int cy = GetSize().cy;
|
66
|
String hl = editor->GetHighlight();
|
67
|
bool hi_if = (hilite_if_endif && findarg(hl, "cpp", "cs", "java") >= 0);
|
68
|
Vector<IfState> previf;
|
69
|
if(hi_if)
|
70
|
previf <<= editor->GetIfStack(i);
|
71
|
int ptri[2];
|
72
|
for(int q = 0; q < 2; q++)
|
73
|
ptri[q] = ptrline[q] >= 0 ? GetLineNo(ptrline[q]) : -1;
|
74
|
while(y < cy) {
|
75
|
String b;
|
76
|
int err = 0;
|
77
|
int edit = 0;
|
78
|
String ann;
|
79
|
Image icon;
|
80
|
if(i < li.GetCount()) {
|
81
|
const LnInfo& l = li[i];
|
82
|
b = l.breakpoint;
|
83
|
err = l.error;
|
84
|
edit = l.edited;
|
85
|
icon = l.icon;
|
86
|
ann = l.annotation;
|
87
|
}
|
88
|
if(editor->GetCaret().top == y && editor->barline)
|
89
|
w.DrawRect(0, y, sz.cx, fy, Blend(SColorHighlight(), SColorLtFace(), 200));
|
90
|
if(line_numbers && i < editor->GetLineCount()) {
|
91
|
String n = AsString((i + 1) % 1000000);
|
92
|
Font fnt = editor->GetFont();
|
93
|
Size tsz = GetTextSize(n, fnt);
|
94
|
w.DrawText(sz.cx - 2 - (hi_if ? 3 : 0) - tsz.cx - annotations, y + (fy - tsz.cy) / 2, n, fnt, Brown);
|
95
|
}
|
96
|
if(hi_if) {
|
97
|
Vector<IfState> nextif;
|
98
|
if(i < li.GetCount())
|
99
|
nextif <<= editor->GetIfStack(i + 1);
|
100
|
int pifl = previf.GetCount(), nifl = nextif.GetCount();
|
101
|
int dif = max(pifl, nifl);
|
102
|
if(--dif >= 0) {
|
103
|
char p = (dif < pifl ? previf[dif].state : 0);
|
104
|
char n = (dif < nifl ? nextif[dif].state : 0);
|
105
|
int wd = min(2 * (dif + 1), sz.cx);
|
106
|
int x = sz.cx - wd;
|
107
|
Color cn = EditorSyntax::IfColor(n);
|
108
|
if(p == n)
|
109
|
w.DrawRect(x, y, 1, fy, cn);
|
110
|
else {
|
111
|
Color cp = EditorSyntax::IfColor(p);
|
112
|
w.DrawRect(x, y, 1, hy, cp);
|
113
|
w.DrawRect(x, y + hy, wd, 1, Nvl(cn, cp));
|
114
|
w.DrawRect(x, y + hy, 1, fy - hy, cn);
|
115
|
if(--dif >= 0) {
|
116
|
x = sz.cx - min(2 * (dif + 1), sz.cx);
|
117
|
if(!p)
|
118
|
w.DrawRect(x, y, 1, hy, EditorSyntax::IfColor(dif < pifl ? previf[dif].state : 0));
|
119
|
if(!n)
|
120
|
w.DrawRect(x, y + hy, 1, fy - hy, EditorSyntax::IfColor(dif < nifl ? nextif[dif].state : 0));
|
121
|
}
|
122
|
}
|
123
|
}
|
124
|
previf = pick(nextif);
|
125
|
}
|
126
|
if(editor->GetMarkLines()) {
|
127
|
int width = 6;
|
128
|
if(edit)
|
129
|
{
|
130
|
int age = (int)(log((double)(editor->GetUndoCount() + 1 - edit)) * 30);
|
131
|
w.DrawRect(0, y, width, fy, Blend(LtBlue, SColorLtFace(), min(220, age)));
|
132
|
}
|
133
|
if(err)
|
134
|
w.DrawRect(width, y, width, fy, err == 1 ? LtRed : (err == 2 ? Color(255, 175, 0) : Green));
|
135
|
}
|
136
|
|
137
|
if(!b.IsEmpty())
|
138
|
sPaintImage(w, check_edited ? 7: 0, y, fy, b == "1" ? CodeEditorImg::Breakpoint() :
|
139
|
b == "\xe" ? CodeEditorImg::InvalidBreakpoint() :
|
140
|
CodeEditorImg::CondBreakpoint());
|
141
|
for(int q = 0; q < 2; q++)
|
142
|
if(ptri[q] == i)
|
143
|
sPaintImage(w, 0, y, fy, ptrimg[q]);
|
144
|
|
145
|
if(annotations && !IsNull(icon))
|
146
|
w.DrawImage(sz.cx - annotations, y + (fy - icon.GetSize().cy) / 2, icon);
|
147
|
|
148
|
y += fy;
|
149
|
i++;
|
150
|
}
|
151
|
}
|
152
|
|
153
|
void EditorBar::MouseMove(Point p, dword flags)
|
154
|
{
|
155
|
int pa = active_annotation;
|
156
|
if(p.x > GetSize().cx - annotations)
|
157
|
active_annotation = p.y / editor->GetFont().Info().GetHeight() + editor->GetScrollPos().y;
|
158
|
else
|
159
|
active_annotation = -1;
|
160
|
if(active_annotation >= editor->GetLineCount())
|
161
|
active_annotation = -1;
|
162
|
if(pa != active_annotation)
|
163
|
WhenAnnotationMove();
|
164
|
if(editor)
|
165
|
editor->MouseMove(Point(0, p.y), flags);
|
166
|
}
|
167
|
|
168
|
void EditorBar::MouseLeave()
|
169
|
{
|
170
|
int pa = active_annotation;
|
171
|
active_annotation = -1;
|
172
|
if(pa != active_annotation)
|
173
|
WhenAnnotationMove();
|
174
|
}
|
175
|
|
176
|
void EditorBar::LeftDown(Point p, dword flags)
|
177
|
{
|
178
|
if(p.x > GetSize().cx - annotations)
|
179
|
WhenAnnotationClick();
|
180
|
else
|
181
|
if(editor) {
|
182
|
editor->LeftDown(Point(0, p.y), flags);
|
183
|
ReleaseCtrlCapture();
|
184
|
}
|
185
|
}
|
186
|
|
187
|
String& EditorBar::PointBreak(int& y)
|
188
|
{
|
189
|
y = minmax(y / editor->GetFont().Info().GetHeight()
|
190
|
+ editor->GetScrollPos().y, 0, editor->GetLineCount());
|
191
|
return li.At(y).breakpoint;
|
192
|
}
|
193
|
|
194
|
void EditorBar::LeftDouble(Point p, dword flags)
|
195
|
{
|
196
|
if(!editor || !bingenabled) return;
|
197
|
String& b = PointBreak(p.y);
|
198
|
if(b.IsEmpty())
|
199
|
b = "1";
|
200
|
else
|
201
|
b.Clear();
|
202
|
WhenBreakpoint(p.y);
|
203
|
Refresh();
|
204
|
}
|
205
|
|
206
|
void EditorBar::RightDown(Point p, dword flags)
|
207
|
{
|
208
|
if(p.x > GetSize().cx - annotations)
|
209
|
WhenAnnotationRightClick();
|
210
|
}
|
211
|
|
212
|
void EditorBar::MouseWheel(Point p, int zdelta, dword keyflags)
|
213
|
{
|
214
|
if(editor) {
|
215
|
int i = editor->GetScrollPos().y;
|
216
|
editor->MouseWheel(p, zdelta, keyflags);
|
217
|
if(i != editor->GetScrollPos().y)
|
218
|
MouseMove(p, keyflags);
|
219
|
}
|
220
|
}
|
221
|
|
222
|
void EditorBar::InsertLines(int i, int count)
|
223
|
{
|
224
|
li.InsertN(minmax(i + 1, 0, li.GetCount()), max(count, 0));
|
225
|
if(editor->GetCheckEdited()) {
|
226
|
if(editor->IsUndoOp() && li_removed.GetCount() >= count) {
|
227
|
for(int t = 0; t < count; t++) {
|
228
|
li.At(i + t).firstedited = li_removed[li_removed.GetCount() - count + t].firstedited;
|
229
|
li[i + t].edited = li_removed[li_removed.GetCount() - count + t].edited;
|
230
|
}
|
231
|
li_removed.Drop(count);
|
232
|
SetEdited(i + count, 1);
|
233
|
ignored_next_edit = true;
|
234
|
}
|
235
|
else {
|
236
|
if (li[i].firstedited == 0) {
|
237
|
bool fe = li[i].firstedited;
|
238
|
li.At(i + count).firstedited = fe;
|
239
|
}
|
240
|
SetEdited(i + 1, count);
|
241
|
}
|
242
|
}
|
243
|
Refresh();
|
244
|
}
|
245
|
|
246
|
void EditorBar::RemoveLines(int i, int count)
|
247
|
{
|
248
|
if(editor->GetCheckEdited() && !editor->IsUndoOp()) {
|
249
|
for(int t = i - 1; t < i + count - 1; t++) {
|
250
|
LineInfoRemRecord& rm = li_removed.Add();
|
251
|
rm.firstedited = li[t].firstedited;
|
252
|
rm.edited = li[t].edited;
|
253
|
}
|
254
|
if(li.At(i + count - 1).firstedited)
|
255
|
next_age = li[i + count - 1].firstedited;
|
256
|
else
|
257
|
next_age = editor->GetUndoCount();
|
258
|
}
|
259
|
i = minmax(i, 0, li.GetCount());
|
260
|
li.Remove(i, minmax(count, 0, li.GetCount() - i));
|
261
|
Refresh();
|
262
|
}
|
263
|
|
264
|
void EditorBar::ClearLines()
|
265
|
{
|
266
|
li.Clear();
|
267
|
li.Shrink();
|
268
|
li_removed.Clear();
|
269
|
li_removed.Shrink();
|
270
|
Refresh();
|
271
|
}
|
272
|
|
273
|
LineInfo EditorBar::GetLineInfo() const
|
274
|
{
|
275
|
LineInfo lf;
|
276
|
int l = -2;
|
277
|
for(int i = 0; i < li.GetCount(); i++) {
|
278
|
const LnInfo& ln = li[i];
|
279
|
if(!ln.breakpoint.IsEmpty() || ln.error || ln.edited) {
|
280
|
LineInfoRecord& r = lf.Add();
|
281
|
r.lineno = ln.lineno;
|
282
|
r.count = 1;
|
283
|
r.breakpoint = ln.breakpoint;
|
284
|
r.error = ln.error;
|
285
|
r.firstedited = ln.firstedited;
|
286
|
r.edited = ln.edited;
|
287
|
l = -2;
|
288
|
}
|
289
|
else
|
290
|
if(ln.lineno != l) {
|
291
|
LineInfoRecord& r = lf.Add();
|
292
|
r.lineno = l = ln.lineno;
|
293
|
r.count = 1;
|
294
|
}
|
295
|
else
|
296
|
lf.Top().count++;
|
297
|
if(l >= 0) l++;
|
298
|
}
|
299
|
return lf;
|
300
|
}
|
301
|
|
302
|
void EditorBar::SetLineInfo(const LineInfo& lf, int total)
|
303
|
{
|
304
|
li.Clear();
|
305
|
if(lf.GetCount() == 0) {
|
306
|
for(int i = 0; i < total; i++)
|
307
|
li.Add().lineno = i;
|
308
|
}
|
309
|
else {
|
310
|
for(int i = 0; i < lf.GetCount() && (total < 0 || li.GetCount() < total); i++) {
|
311
|
const LineInfoRecord& r = lf[i];
|
312
|
int l = r.lineno;
|
313
|
for(int j = r.count; j-- && li.GetCount() < total;) {
|
314
|
LnInfo& ln = li.Add();
|
315
|
ln.lineno = l;
|
316
|
ln.breakpoint = r.breakpoint;
|
317
|
ln.error = r.error;
|
318
|
ln.firstedited = r.firstedited;
|
319
|
ln.edited = r.edited;
|
320
|
if(l >= 0) l++;
|
321
|
}
|
322
|
}
|
323
|
while(li.GetCount() < total)
|
324
|
li.Add().lineno = -1;
|
325
|
}
|
326
|
}
|
327
|
|
328
|
void EditorBar::Renumber(int linecount)
|
329
|
{
|
330
|
li.SetCount(linecount);
|
331
|
for(int i = 0; i < linecount; i++)
|
332
|
li[i].lineno = i;
|
333
|
}
|
334
|
|
335
|
void EditorBar::ClearBreakpoints()
|
336
|
{
|
337
|
for(int i = 0; i < li.GetCount(); i++)
|
338
|
li[i].breakpoint.Clear();
|
339
|
Refresh();
|
340
|
}
|
341
|
|
342
|
void EditorBar::ValidateBreakpoints()
|
343
|
{
|
344
|
for(int i = 0; i < li.GetCount(); i++)
|
345
|
if(li[i].breakpoint[0] == 0xe)
|
346
|
li[i].breakpoint = "1";
|
347
|
Refresh();
|
348
|
}
|
349
|
|
350
|
String EditorBar::GetBreakpoint(int ln)
|
351
|
{
|
352
|
return ln < li.GetCount() ? li[ln].breakpoint : Null;
|
353
|
}
|
354
|
|
355
|
void EditorBar::ClearAnnotations()
|
356
|
{
|
357
|
for(int i = 0; i < li.GetCount(); i++) {
|
358
|
li[i].icon.Clear();
|
359
|
li[i].annotation.Clear();
|
360
|
}
|
361
|
}
|
362
|
|
363
|
void EditorBar::SetAnnotation(int line, const Image& img, const String& ann)
|
364
|
{
|
365
|
if(line >= 0 && line < li.GetCount()) {
|
366
|
li[line].icon = img;
|
367
|
li[line].annotation = ann;
|
368
|
}
|
369
|
Refresh();
|
370
|
}
|
371
|
|
372
|
String EditorBar::GetAnnotation(int line) const
|
373
|
{
|
374
|
return line >= 0 && line < li.GetCount() ? li[line].annotation : String();
|
375
|
}
|
376
|
|
377
|
void EditorBar::SetBreakpoint(int ln, const String& s)
|
378
|
{
|
379
|
li.At(ln).breakpoint = s;
|
380
|
WhenBreakpoint(ln);
|
381
|
}
|
382
|
|
383
|
void EditorBar::SetEdited(int ln, int count)
|
384
|
{
|
385
|
if(ignored_next_edit) {
|
386
|
ignored_next_edit = false;
|
387
|
return;
|
388
|
}
|
389
|
int age = editor->GetUndoCount() + 1;
|
390
|
bool undo = editor->IsUndoOp();
|
391
|
for(int i = 0; i < count; i++) {
|
392
|
if(undo) {
|
393
|
if (li.At(ln + i).firstedited >= age - 1) {
|
394
|
li[ln + i].firstedited = 0;
|
395
|
li[ln + i].edited = 0;
|
396
|
}
|
397
|
}
|
398
|
else {
|
399
|
if(next_age) {
|
400
|
li[ln + i].firstedited = next_age;
|
401
|
li[ln + i].edited = age;
|
402
|
next_age = 0;
|
403
|
}
|
404
|
else {
|
405
|
if(li.At(ln + i).firstedited == 0)
|
406
|
li[ln + i].firstedited = age;
|
407
|
li[ln + i].edited = age;
|
408
|
}
|
409
|
}
|
410
|
}
|
411
|
Refresh();
|
412
|
}
|
413
|
|
414
|
void EditorBar::ClearEdited()
|
415
|
{
|
416
|
for(int i = 0; i < li.GetCount(); i++) {
|
417
|
li.At(i).firstedited = 0;
|
418
|
li[i].edited = 0;
|
419
|
}
|
420
|
li_removed.Clear();
|
421
|
li_removed.Shrink();
|
422
|
Refresh();
|
423
|
}
|
424
|
|
425
|
void EditorBar::SetError(int ln, int err)
|
426
|
{
|
427
|
li.At(ln).error = err;
|
428
|
}
|
429
|
|
430
|
void EditorBar::ClearErrors(int line)
|
431
|
{
|
432
|
int count;
|
433
|
if(line < 0) {
|
434
|
line = 0;
|
435
|
count = li.GetCount();
|
436
|
}
|
437
|
else
|
438
|
if(line >= li.GetCount())
|
439
|
return;
|
440
|
else
|
441
|
count = line + 1;
|
442
|
|
443
|
for(int i = line; i < count; i++)
|
444
|
li[i].error = 0;
|
445
|
}
|
446
|
|
447
|
int EditorBar::GetLineNo(int lineno) const {
|
448
|
for(int i = 0; i < li.GetCount(); i++) {
|
449
|
if(lineno <= li[i].lineno)
|
450
|
return i;
|
451
|
}
|
452
|
return lineno;
|
453
|
}
|
454
|
|
455
|
int EditorBar::GetNoLine(int line) const {
|
456
|
int n = 0;
|
457
|
for(int i = 0; i < li.GetCount(); i++) {
|
458
|
if(li[i].lineno >= 0)
|
459
|
n = li[i].lineno;
|
460
|
if(i == line) return n;
|
461
|
}
|
462
|
return n;
|
463
|
}
|
464
|
|
465
|
void EditorBar::SetPtr(int line, const Image& img, int i)
|
466
|
{
|
467
|
ASSERT(i >= 0 && i < 2);
|
468
|
ptrline[i] = line;
|
469
|
ptrimg[i] = img;
|
470
|
Refresh();
|
471
|
}
|
472
|
|
473
|
void EditorBar::HidePtr()
|
474
|
{
|
475
|
ptrline[0] = ptrline[1] = -1;
|
476
|
Refresh();
|
477
|
}
|
478
|
|
479
|
void EditorBar::SyncSize()
|
480
|
{
|
481
|
int n = editor ? editor->GetLineCount() : 0;
|
482
|
int i = 0;
|
483
|
while(n) {
|
484
|
i++;
|
485
|
n /= 10;
|
486
|
}
|
487
|
|
488
|
bool hi_if = hilite_if_endif;
|
489
|
if (editor && hi_if) {
|
490
|
String hl = editor->GetHighlight();
|
491
|
hi_if = (hilite_if_endif && findarg(hl, "cpp", "cs", "java") >= 0);
|
492
|
}
|
493
|
|
494
|
int w = annotations;
|
495
|
if (line_numbers && editor)
|
496
|
w += editor->GetFont()['0'] * i + 4;
|
497
|
if (check_edited)
|
498
|
w += 6 + 1;
|
499
|
if (hi_if)
|
500
|
w += 3;
|
501
|
if (bingenabled)
|
502
|
w += CodeEditorImg::Breakpoint().GetWidth() + 2 + 1;
|
503
|
if(w != GetWidth()) {
|
504
|
Width(w);
|
505
|
Show(w);
|
506
|
}
|
507
|
|
508
|
Refresh();
|
509
|
}
|
510
|
|
511
|
void EditorBar::LineNumbers(bool b)
|
512
|
{
|
513
|
line_numbers = b;
|
514
|
SyncSize();
|
515
|
}
|
516
|
|
517
|
void EditorBar::Annotations(int width)
|
518
|
{
|
519
|
annotations = width;
|
520
|
SyncSize();
|
521
|
}
|
522
|
|
523
|
EditorBar::EditorBar()
|
524
|
{
|
525
|
editor = NULL;
|
526
|
check_edited = false;
|
527
|
|
528
|
bingenabled = true;
|
529
|
hilite_if_endif = true;
|
530
|
line_numbers = false;
|
531
|
annotations = 0;
|
532
|
ignored_next_edit = false;
|
533
|
next_age = 0;
|
534
|
SyncSize();
|
535
|
Show(GetWidth());
|
536
|
}
|
537
|
|
538
|
EditorBar::~EditorBar()
|
539
|
{
|
540
|
}
|
541
|
|
542
|
void ClearErrors(LineInfo& li)
|
543
|
{
|
544
|
for(int i = 0; i < li.GetCount(); i++)
|
545
|
li[i].error = 0;
|
546
|
}
|
547
|
|
548
|
void SetError(LineInfo& li, int line, int err)
|
549
|
{
|
550
|
li.At(line).error = err;
|
551
|
}
|
552
|
|
553
|
}
|