1 //-----------------------------------------------------------------------------
2 // Helper functions for the text-based browser window.
3 //
4 // Copyright 2008-2013 Jonathan Westhues.
5 //-----------------------------------------------------------------------------
6 #include "solvespace.h"
7 #include "generated/icons.h"
8
9 const TextWindow::Color TextWindow::fgColors[] = {
10 { 'd', RGBi(255, 255, 255) },
11 { 'l', RGBi(100, 100, 255) },
12 { 't', RGBi(255, 200, 0) },
13 { 'h', RGBi( 90, 90, 90) },
14 { 's', RGBi( 40, 255, 40) },
15 { 'm', RGBi(200, 200, 0) },
16 { 'r', RGBi( 0, 0, 0) },
17 { 'x', RGBi(255, 20, 20) },
18 { 'i', RGBi( 0, 255, 255) },
19 { 'g', RGBi(160, 160, 160) },
20 { 'b', RGBi(200, 200, 200) },
21 { 0, RGBi( 0, 0, 0) }
22 };
23 const TextWindow::Color TextWindow::bgColors[] = {
24 { 'd', RGBi( 0, 0, 0) },
25 { 't', RGBi( 34, 15, 15) },
26 { 'a', RGBi( 25, 25, 25) },
27 { 'r', RGBi(255, 255, 255) },
28 { 0, RGBi( 0, 0, 0) }
29 };
30
31 bool TextWindow::SPACER = false;
32 TextWindow::HideShowIcon TextWindow::hideShowIcons[] = {
33 { &(SS.GW.showWorkplanes), Icon_workplane, "workplanes from inactive groups"},
34 { &(SS.GW.showNormals), Icon_normal, "normals" },
35 { &(SS.GW.showPoints), Icon_point, "points" },
36 { &(SS.GW.showConstraints), Icon_constraint, "constraints and dimensions" },
37 { &(SS.GW.showFaces), Icon_faces, "XXX - special cased" },
38 { &SPACER, 0, 0 },
39 { &(SS.GW.showShaded), Icon_shaded, "shaded view of solid model" },
40 { &(SS.GW.showEdges), Icon_edges, "edges of solid model" },
41 { &(SS.GW.showOutlines), Icon_outlines, "outline of solid model" },
42 { &(SS.GW.showMesh), Icon_mesh, "triangle mesh of solid model" },
43 { &SPACER, 0, 0 },
44 { &(SS.GW.showHdnLines), Icon_hidden_lines, "hidden lines" },
45 { 0, 0, 0 }
46 };
47
MakeColorTable(const Color * in,float * out)48 void TextWindow::MakeColorTable(const Color *in, float *out) {
49 int i;
50 for(i = 0; in[i].c != 0; i++) {
51 int c = in[i].c;
52 if(c < 0 || c > 255) oops();
53 out[c*3 + 0] = in[i].color.redF();
54 out[c*3 + 1] = in[i].color.greenF();
55 out[c*3 + 2] = in[i].color.blueF();
56 }
57 }
58
Init(void)59 void TextWindow::Init(void) {
60 ClearSuper();
61 }
62
ClearSuper(void)63 void TextWindow::ClearSuper(void) {
64 HideEditControl();
65
66 // Cannot use *this = {} here because TextWindow instances
67 // are 2.4MB long; this causes stack overflows in prologue
68 // when built with MSVC, even with optimizations.
69 memset(this, 0, sizeof(*this));
70
71 MakeColorTable(fgColors, fgColorTable);
72 MakeColorTable(bgColors, bgColorTable);
73
74 ClearScreen();
75 Show();
76 }
77
HideEditControl(void)78 void TextWindow::HideEditControl(void) {
79 editControl.colorPicker.show = false;
80 HideTextEditControl();
81 }
82
ShowEditControl(int col,const std::string & str,int halfRow)83 void TextWindow::ShowEditControl(int col, const std::string &str, int halfRow) {
84 if(halfRow < 0) halfRow = top[hoveredRow];
85 editControl.halfRow = halfRow;
86 editControl.col = col;
87
88 int x = LEFT_MARGIN + CHAR_WIDTH*col;
89 int y = (halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
90
91 ShowTextEditControl(x, y + 18, str);
92 }
93
ShowEditControlWithColorPicker(int col,RgbaColor rgb)94 void TextWindow::ShowEditControlWithColorPicker(int col, RgbaColor rgb)
95 {
96 SS.ScheduleShowTW();
97
98 editControl.colorPicker.show = true;
99 editControl.colorPicker.rgb = rgb;
100 editControl.colorPicker.h = 0;
101 editControl.colorPicker.s = 0;
102 editControl.colorPicker.v = 1;
103 ShowEditControl(col, ssprintf("%.2f, %.2f, %.2f", rgb.redF(), rgb.greenF(), rgb.blueF()));
104 }
105
ClearScreen(void)106 void TextWindow::ClearScreen(void) {
107 int i, j;
108 for(i = 0; i < MAX_ROWS; i++) {
109 for(j = 0; j < MAX_COLS; j++) {
110 text[i][j] = ' ';
111 meta[i][j].fg = 'd';
112 meta[i][j].bg = 'd';
113 meta[i][j].link = NOT_A_LINK;
114 }
115 top[i] = i*2;
116 }
117 rows = 0;
118 }
119
Printf(bool halfLine,const char * fmt,...)120 void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
121 va_list vl;
122 va_start(vl, fmt);
123
124 if(rows >= MAX_ROWS) return;
125
126 int r, c;
127 r = rows;
128 top[r] = (r == 0) ? 0 : (top[r-1] + (halfLine ? 3 : 2));
129 rows++;
130
131 for(c = 0; c < MAX_COLS; c++) {
132 text[r][c] = ' ';
133 meta[r][c].link = NOT_A_LINK;
134 }
135
136 char fg = 'd';
137 char bg = 'd';
138 RgbaColor bgRgb = RGBi(0, 0, 0);
139 int link = NOT_A_LINK;
140 uint32_t data = 0;
141 LinkFunction *f = NULL, *h = NULL;
142
143 c = 0;
144 while(*fmt) {
145 char buf[1024];
146
147 if(*fmt == '%') {
148 fmt++;
149 if(*fmt == '\0') goto done;
150 strcpy(buf, "");
151 switch(*fmt) {
152 case 'd': {
153 int v = va_arg(vl, int);
154 sprintf(buf, "%d", v);
155 break;
156 }
157 case 'x': {
158 unsigned int v = va_arg(vl, unsigned int);
159 sprintf(buf, "%08x", v);
160 break;
161 }
162 case '@': {
163 double v = va_arg(vl, double);
164 sprintf(buf, "%.2f", v);
165 break;
166 }
167 case '2': {
168 double v = va_arg(vl, double);
169 sprintf(buf, "%s%.2f", v < 0 ? "" : " ", v);
170 break;
171 }
172 case '3': {
173 double v = va_arg(vl, double);
174 sprintf(buf, "%s%.3f", v < 0 ? "" : " ", v);
175 break;
176 }
177 case '#': {
178 double v = va_arg(vl, double);
179 sprintf(buf, "%.3f", v);
180 break;
181 }
182 case 's': {
183 char *s = va_arg(vl, char *);
184 memcpy(buf, s, min(sizeof(buf), strlen(s)+1));
185 break;
186 }
187 case 'c': {
188 // 'char' is promoted to 'int' when passed through '...'
189 int v = va_arg(vl, int);
190 if(v == 0) {
191 strcpy(buf, "");
192 } else {
193 sprintf(buf, "%c", v);
194 }
195 break;
196 }
197 case 'E':
198 fg = 'd';
199 // leave the background, though
200 link = NOT_A_LINK;
201 data = 0;
202 f = NULL;
203 h = NULL;
204 break;
205
206 case 'F':
207 case 'B': {
208 char cc = fmt[1]; // color code
209 RgbaColor *rgbPtr = NULL;
210 switch(cc) {
211 case 0: goto done; // truncated directive
212 case 'p': cc = (char)va_arg(vl, int); break;
213 case 'z': rgbPtr = va_arg(vl, RgbaColor *); break;
214 }
215 if(*fmt == 'F') {
216 fg = cc;
217 } else {
218 bg = cc;
219 if(rgbPtr) bgRgb = *rgbPtr;
220 }
221 fmt++;
222 break;
223 }
224 case 'L':
225 if(fmt[1] == '\0') goto done;
226 fmt++;
227 if(*fmt == 'p') {
228 link = va_arg(vl, int);
229 } else {
230 link = *fmt;
231 }
232 break;
233
234 case 'f':
235 f = va_arg(vl, LinkFunction *);
236 break;
237
238 case 'h':
239 h = va_arg(vl, LinkFunction *);
240 break;
241
242 case 'D': {
243 unsigned int v = va_arg(vl, unsigned int);
244 data = (uint32_t)v;
245 break;
246 }
247 case '%':
248 strcpy(buf, "%");
249 break;
250 }
251 } else {
252 utf8_iterator it2(fmt), it1 = it2++;
253 strncpy(buf, fmt, it2 - it1);
254 buf[it2 - it1] = '\0';
255 }
256
257 for(utf8_iterator it(buf); *it; ++it) {
258 for(int i = 0; i < ssglBitmapCharWidth(*it); i++) {
259 if(c >= MAX_COLS) goto done;
260 text[r][c] = (i == 0) ? *it : ' ';
261 meta[r][c].fg = fg;
262 meta[r][c].bg = bg;
263 meta[r][c].bgRgb = bgRgb;
264 meta[r][c].link = link;
265 meta[r][c].data = data;
266 meta[r][c].f = f;
267 meta[r][c].h = h;
268 c++;
269 }
270 }
271
272 fmt++;
273 }
274 while(c < MAX_COLS) {
275 meta[r][c].fg = fg;
276 meta[r][c].bg = bg;
277 meta[r][c].bgRgb = bgRgb;
278 c++;
279 }
280
281 done:
282 va_end(vl);
283 }
284
285 #define gs (SS.GW.gs)
Show(void)286 void TextWindow::Show(void) {
287 if(!(SS.GW.pending.operation)) SS.GW.ClearPending();
288
289 SS.GW.GroupSelection();
290
291 // Make sure these tests agree with test used to draw indicator line on
292 // main list of groups screen.
293 if(SS.GW.pending.description) {
294 // A pending operation (that must be completed with the mouse in
295 // the graphics window) will preempt our usual display.
296 HideEditControl();
297 ShowHeader(false);
298 Printf(false, "");
299 Printf(false, "%s", SS.GW.pending.description);
300 Printf(true, "%Fl%f%Ll(cancel operation)%E",
301 &TextWindow::ScreenUnselectAll);
302 } else if((gs.n > 0 || gs.constraints > 0) &&
303 shown.screen != SCREEN_PASTE_TRANSFORMED)
304 {
305 if(edit.meaning != EDIT_TTF_TEXT) HideEditControl();
306 ShowHeader(false);
307 DescribeSelection();
308 } else {
309 if(edit.meaning == EDIT_TTF_TEXT) HideEditControl();
310 ShowHeader(true);
311 switch(shown.screen) {
312 default:
313 shown.screen = SCREEN_LIST_OF_GROUPS;
314 // fall through
315 case SCREEN_LIST_OF_GROUPS: ShowListOfGroups(); break;
316 case SCREEN_GROUP_INFO: ShowGroupInfo(); break;
317 case SCREEN_GROUP_SOLVE_INFO: ShowGroupSolveInfo(); break;
318 case SCREEN_CONFIGURATION: ShowConfiguration(); break;
319 case SCREEN_STEP_DIMENSION: ShowStepDimension(); break;
320 case SCREEN_LIST_OF_STYLES: ShowListOfStyles(); break;
321 case SCREEN_STYLE_INFO: ShowStyleInfo(); break;
322 case SCREEN_PASTE_TRANSFORMED: ShowPasteTransformed(); break;
323 case SCREEN_EDIT_VIEW: ShowEditView(); break;
324 case SCREEN_TANGENT_ARC: ShowTangentArc(); break;
325 }
326 }
327 Printf(false, "");
328
329 // Make sure there's room for the color picker
330 if(editControl.colorPicker.show) {
331 int pickerHeight = 25;
332 int halfRow = editControl.halfRow;
333 if(top[rows-1] - halfRow < pickerHeight && rows < MAX_ROWS) {
334 rows++;
335 top[rows-1] = halfRow + pickerHeight;
336 }
337 }
338
339 InvalidateText();
340 }
341
TimerCallback(void)342 void TextWindow::TimerCallback(void)
343 {
344 tooltippedIcon = hoveredIcon;
345 InvalidateText();
346 }
347
DrawOrHitTestIcons(int how,double mx,double my)348 void TextWindow::DrawOrHitTestIcons(int how, double mx, double my)
349 {
350 int width, height;
351 GetTextWindowSize(&width, &height);
352
353 int x = 20, y = 33 + LINE_HEIGHT;
354 y -= scrollPos*(LINE_HEIGHT/2);
355
356 if(how == PAINT) {
357 double grey = 30.0/255;
358 double top = y - 28, bot = y + 4;
359 glColor4d(grey, grey, grey, 1.0);
360 ssglAxisAlignedQuad(0, width, top, bot);
361 }
362
363 HideShowIcon *oldHovered = hoveredIcon;
364 if(how != PAINT) {
365 hoveredIcon = NULL;
366 }
367
368 HideShowIcon *hsi;
369 for(hsi = &(hideShowIcons[0]); hsi->var; hsi++) {
370 if(hsi->var == &SPACER) {
371 // Draw a darker-grey spacer in between the groups of icons.
372 if(how == PAINT) {
373 int l = x, r = l + 4,
374 t = y, b = t - 24;
375 glColor4d(0.17, 0.17, 0.17, 1);
376 ssglAxisAlignedQuad(l, r, t, b);
377 }
378 x += 12;
379 continue;
380 }
381
382 if(how == PAINT) {
383 glPushMatrix();
384 glTranslated(x, y-24, 0);
385 // Only thing that matters about the color is the alpha,
386 // should be one for no transparency
387 glColor3d(0, 0, 0);
388 ssglDrawPixelsWithTexture(hsi->icon, 24, 24);
389 glPopMatrix();
390
391 if(hsi == hoveredIcon) {
392 glColor4d(1, 1, 0, 0.3);
393 ssglAxisAlignedQuad(x - 2, x + 26, y + 2, y - 26);
394 }
395 if(!*(hsi->var)) {
396 glColor4d(1, 0, 0, 0.6);
397 glLineWidth(2);
398 int s = 0, f = 24;
399 glBegin(GL_LINES);
400 glVertex2d(x+s, y-s);
401 glVertex2d(x+f, y-f);
402 glVertex2d(x+s, y-f);
403 glVertex2d(x+f, y-s);
404 glEnd();
405 }
406 } else {
407 if(mx > x - 2 && mx < x + 26 &&
408 my < y + 2 && my > y - 26)
409 {
410 // The mouse is hovered over this icon, so do the tooltip
411 // stuff.
412 if(hsi != tooltippedIcon) {
413 oldMousePos = Point2d::From(mx, my);
414 }
415 if(hsi != oldHovered || how == CLICK) {
416 SetTimerFor(1000);
417 }
418 hoveredIcon = hsi;
419 if(how == CLICK) {
420 SS.GW.ToggleBool(hsi->var);
421 }
422 }
423 }
424
425 x += 32;
426 }
427
428 if(how != PAINT && hoveredIcon != oldHovered) {
429 InvalidateText();
430 }
431
432 if(tooltippedIcon) {
433 if(how == PAINT) {
434 std::string str;
435
436 if(tooltippedIcon->icon == Icon_faces) {
437 if(SS.GW.showFaces) {
438 str = "Don't make faces selectable with mouse";
439 } else {
440 str = "Make faces selectable with mouse";
441 }
442 } else {
443 str = ssprintf("%s %s", *(tooltippedIcon->var) ? "Hide" : "Show",
444 tooltippedIcon->tip);
445 }
446
447 double ox = oldMousePos.x, oy = oldMousePos.y - LINE_HEIGHT;
448 ox += 3;
449 oy -= 3;
450 int tw = (str.length() + 1)*(CHAR_WIDTH - 1);
451 ox = min(ox, (double) (width - 25) - tw);
452 oy = max(oy, 5.0);
453
454 ssglInitializeBitmapFont();
455 glLineWidth(1);
456 glColor4d(1.0, 1.0, 0.6, 1.0);
457 ssglAxisAlignedQuad(ox, ox+tw, oy, oy+LINE_HEIGHT);
458 glColor4d(0.0, 0.0, 0.0, 1.0);
459 ssglAxisAlignedLineLoop(ox, ox+tw, oy, oy+LINE_HEIGHT);
460
461 glColor4d(0, 0, 0, 1);
462 ssglBitmapText(str, Vector::From(ox+5, oy-3+LINE_HEIGHT, 0));
463 } else {
464 if(!hoveredIcon ||
465 (hoveredIcon != tooltippedIcon))
466 {
467 tooltippedIcon = NULL;
468 InvalidateGraphics();
469 }
470 // And if we're hovered, then we've set a timer that will cause
471 // us to show the tool tip later.
472 }
473 }
474 }
475
476 //----------------------------------------------------------------------------
477 // Given (x, y, z) = (h, s, v) in [0,6), [0,1], [0,1], return (x, y, z) =
478 // (r, g, b) all in [0, 1].
479 //----------------------------------------------------------------------------
HsvToRgb(Vector hsv)480 Vector TextWindow::HsvToRgb(Vector hsv) {
481 if(hsv.x >= 6) hsv.x -= 6;
482
483 Vector rgb;
484 double hmod2 = hsv.x;
485 while(hmod2 >= 2) hmod2 -= 2;
486 double x = (1 - fabs(hmod2 - 1));
487 if(hsv.x < 1) {
488 rgb = Vector::From(1, x, 0);
489 } else if(hsv.x < 2) {
490 rgb = Vector::From(x, 1, 0);
491 } else if(hsv.x < 3) {
492 rgb = Vector::From(0, 1, x);
493 } else if(hsv.x < 4) {
494 rgb = Vector::From(0, x, 1);
495 } else if(hsv.x < 5) {
496 rgb = Vector::From(x, 0, 1);
497 } else {
498 rgb = Vector::From(1, 0, x);
499 }
500 double c = hsv.y*hsv.z;
501 double m = 1 - hsv.z;
502 rgb = rgb.ScaledBy(c);
503 rgb = rgb.Plus(Vector::From(m, m, m));
504
505 return rgb;
506 }
507
HsvPattern2d(void)508 uint8_t *TextWindow::HsvPattern2d(void) {
509 static uint8_t Texture[256*256*3];
510 static bool Init;
511
512 if(!Init) {
513 int i, j, p;
514 p = 0;
515 for(i = 0; i < 256; i++) {
516 for(j = 0; j < 256; j++) {
517 Vector hsv = Vector::From(6.0*i/255.0, 1.0*j/255.0, 1);
518 Vector rgb = HsvToRgb(hsv);
519 rgb = rgb.ScaledBy(255);
520 Texture[p++] = (uint8_t)rgb.x;
521 Texture[p++] = (uint8_t)rgb.y;
522 Texture[p++] = (uint8_t)rgb.z;
523 }
524 }
525 Init = true;
526 }
527 return Texture;
528 }
529
HsvPattern1d(double h,double s)530 uint8_t *TextWindow::HsvPattern1d(double h, double s) {
531 static uint8_t Texture[256*4];
532
533 int i, p;
534 p = 0;
535 for(i = 0; i < 256; i++) {
536 Vector hsv = Vector::From(6*h, s, 1.0*(255 - i)/255.0);
537 Vector rgb = HsvToRgb(hsv);
538 rgb = rgb.ScaledBy(255);
539 Texture[p++] = (uint8_t)rgb.x;
540 Texture[p++] = (uint8_t)rgb.y;
541 Texture[p++] = (uint8_t)rgb.z;
542 // Needs a padding byte, to make things four-aligned
543 p++;
544 }
545 return Texture;
546 }
547
ColorPickerDone(void)548 void TextWindow::ColorPickerDone(void) {
549 RgbaColor rgb = editControl.colorPicker.rgb;
550 EditControlDone(ssprintf("%.2f, %.2f, %.3f", rgb.redF(), rgb.greenF(), rgb.blueF()).c_str());
551 }
552
DrawOrHitTestColorPicker(int how,bool leftDown,double x,double y)553 bool TextWindow::DrawOrHitTestColorPicker(int how, bool leftDown,
554 double x, double y)
555 {
556 bool mousePointerAsHand = false;
557
558 if(how == HOVER && !leftDown) {
559 editControl.colorPicker.picker1dActive = false;
560 editControl.colorPicker.picker2dActive = false;
561 }
562
563 if(!editControl.colorPicker.show) return false;
564 if(how == CLICK || (how == HOVER && leftDown)) InvalidateText();
565
566 static const RgbaColor BaseColor[12] = {
567 RGBi(255, 0, 0),
568 RGBi( 0, 255, 0),
569 RGBi( 0, 0, 255),
570
571 RGBi( 0, 255, 255),
572 RGBi(255, 0, 255),
573 RGBi(255, 255, 0),
574
575 RGBi(255, 127, 0),
576 RGBi(255, 0, 127),
577 RGBi( 0, 255, 127),
578 RGBi(127, 255, 0),
579 RGBi(127, 0, 255),
580 RGBi( 0, 127, 255),
581 };
582
583 int width, height;
584 GetTextWindowSize(&width, &height);
585
586 int px = LEFT_MARGIN + CHAR_WIDTH*editControl.col;
587 int py = (editControl.halfRow - SS.TW.scrollPos)*(LINE_HEIGHT/2);
588
589 py += LINE_HEIGHT + 5;
590
591 static const int WIDTH = 16, HEIGHT = 12;
592 static const int PITCH = 18, SIZE = 15;
593
594 px = min(px, width - (WIDTH*PITCH + 40));
595
596 int pxm = px + WIDTH*PITCH + 11,
597 pym = py + HEIGHT*PITCH + 7;
598
599 int bw = 6;
600 if(how == PAINT) {
601 glColor4d(0.2, 0.2, 0.2, 1);
602 ssglAxisAlignedQuad(px, pxm+bw, py, pym+bw);
603 glColor4d(0.0, 0.0, 0.0, 1);
604 ssglAxisAlignedQuad(px+(bw/2), pxm+(bw/2), py+(bw/2), pym+(bw/2));
605 } else {
606 if(x < px || x > pxm+(bw/2) ||
607 y < py || y > pym+(bw/2))
608 {
609 return false;
610 }
611 }
612 px += (bw/2);
613 py += (bw/2);
614
615 int i, j;
616 for(i = 0; i < WIDTH/2; i++) {
617 for(j = 0; j < HEIGHT; j++) {
618 Vector rgb;
619 RgbaColor d;
620 if(i == 0 && j < 8) {
621 d = SS.modelColor[j];
622 rgb = Vector::From(d.redF(), d.greenF(), d.blueF());
623 } else if(i == 0) {
624 double a = (j - 8.0)/3.0;
625 rgb = Vector::From(a, a, a);
626 } else {
627 d = BaseColor[j];
628 rgb = Vector::From(d.redF(), d.greenF(), d.blueF());
629 if(i >= 2 && i <= 4) {
630 double a = (i == 2) ? 0.2 : (i == 3) ? 0.3 : 0.4;
631 rgb = rgb.Plus(Vector::From(a, a, a));
632 }
633 if(i >= 5 && i <= 7) {
634 double a = (i == 5) ? 0.7 : (i == 6) ? 0.4 : 0.18;
635 rgb = rgb.ScaledBy(a);
636 }
637 }
638
639 rgb = rgb.ClampWithin(0, 1);
640 int sx = px + 5 + PITCH*(i + 8) + 4, sy = py + 5 + PITCH*j;
641
642 if(how == PAINT) {
643 glColor4d(CO(rgb), 1);
644 ssglAxisAlignedQuad(sx, sx+SIZE, sy, sy+SIZE);
645 } else if(how == CLICK) {
646 if(x >= sx && x <= sx+SIZE && y >= sy && y <= sy+SIZE) {
647 editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z);
648 ColorPickerDone();
649 }
650 } else if(how == HOVER) {
651 if(x >= sx && x <= sx+SIZE && y >= sy && y <= sy+SIZE) {
652 mousePointerAsHand = true;
653 }
654 }
655 }
656 }
657
658 int hxm, hym;
659 int hx = px + 5, hy = py + 5;
660 hxm = hx + PITCH*7 + SIZE;
661 hym = hy + PITCH*2 + SIZE;
662 if(how == PAINT) {
663 ssglColorRGB(editControl.colorPicker.rgb);
664 ssglAxisAlignedQuad(hx, hxm, hy, hym);
665 } else if(how == CLICK) {
666 if(x >= hx && x <= hxm && y >= hy && y <= hym) {
667 ColorPickerDone();
668 }
669 } else if(how == HOVER) {
670 if(x >= hx && x <= hxm && y >= hy && y <= hym) {
671 mousePointerAsHand = true;
672 }
673 }
674
675 hy += PITCH*3;
676
677 hxm = hx + PITCH*7 + SIZE;
678 hym = hy + PITCH*1 + SIZE;
679 // The one-dimensional thing to pick the color's value
680 if(how == PAINT) {
681 glBindTexture(GL_TEXTURE_2D, TEXTURE_COLOR_PICKER_1D);
682 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
683 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
684 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
685 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
686 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
687
688 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 256, 0,
689 GL_RGB, GL_UNSIGNED_BYTE,
690 HsvPattern1d(editControl.colorPicker.h,
691 editControl.colorPicker.s));
692
693 glEnable(GL_TEXTURE_2D);
694 glBegin(GL_QUADS);
695 glTexCoord2d(0, 0);
696 glVertex2d(hx, hy);
697
698 glTexCoord2d(1, 0);
699 glVertex2d(hx, hym);
700
701 glTexCoord2d(1, 1);
702 glVertex2d(hxm, hym);
703
704 glTexCoord2d(0, 1);
705 glVertex2d(hxm, hy);
706 glEnd();
707 glDisable(GL_TEXTURE_2D);
708
709 double cx = hx+(hxm-hx)*(1 - editControl.colorPicker.v);
710 glColor4d(0, 0, 0, 1);
711 glLineWidth(1);
712 glBegin(GL_LINES);
713 glVertex2d(cx, hy);
714 glVertex2d(cx, hym);
715 glEnd();
716 } else if(how == CLICK ||
717 (how == HOVER && leftDown && editControl.colorPicker.picker1dActive))
718 {
719 if(x >= hx && x <= hxm && y >= hy && y <= hym) {
720 editControl.colorPicker.v = 1 - (x - hx)/(hxm - hx);
721
722 Vector rgb = HsvToRgb(Vector::From(
723 6*editControl.colorPicker.h,
724 editControl.colorPicker.s,
725 editControl.colorPicker.v));
726 editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z);
727
728 editControl.colorPicker.picker1dActive = true;
729 }
730 }
731 // and advance our vertical position
732 hy += PITCH*2;
733
734 hxm = hx + PITCH*7 + SIZE;
735 hym = hy + PITCH*6 + SIZE;
736 // Two-dimensional thing to pick a color by hue and saturation
737 if(how == PAINT) {
738 glBindTexture(GL_TEXTURE_2D, TEXTURE_COLOR_PICKER_2D);
739 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
740 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
741 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
742 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
743 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
744
745 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 256, 0,
746 GL_RGB, GL_UNSIGNED_BYTE, HsvPattern2d());
747
748 glEnable(GL_TEXTURE_2D);
749 glBegin(GL_QUADS);
750 glTexCoord2d(0, 0);
751 glVertex2d(hx, hy);
752
753 glTexCoord2d(1, 0);
754 glVertex2d(hx, hym);
755
756 glTexCoord2d(1, 1);
757 glVertex2d(hxm, hym);
758
759 glTexCoord2d(0, 1);
760 glVertex2d(hxm, hy);
761 glEnd();
762 glDisable(GL_TEXTURE_2D);
763
764 glColor4d(1, 1, 1, 1);
765 glLineWidth(1);
766 double cx = hx+(hxm-hx)*editControl.colorPicker.h,
767 cy = hy+(hym-hy)*editControl.colorPicker.s;
768 glBegin(GL_LINES);
769 glVertex2d(cx - 5, cy);
770 glVertex2d(cx + 4, cy);
771 glVertex2d(cx, cy - 5);
772 glVertex2d(cx, cy + 4);
773 glEnd();
774 } else if(how == CLICK ||
775 (how == HOVER && leftDown && editControl.colorPicker.picker2dActive))
776 {
777 if(x >= hx && x <= hxm && y >= hy && y <= hym) {
778 double h = (x - hx)/(hxm - hx),
779 s = (y - hy)/(hym - hy);
780 editControl.colorPicker.h = h;
781 editControl.colorPicker.s = s;
782
783 Vector rgb = HsvToRgb(Vector::From(
784 6*editControl.colorPicker.h,
785 editControl.colorPicker.s,
786 editControl.colorPicker.v));
787 editControl.colorPicker.rgb = RGBf(rgb.x, rgb.y, rgb.z);
788
789 editControl.colorPicker.picker2dActive = true;
790 }
791 }
792
793 SetMousePointerToHand(mousePointerAsHand);
794 return true;
795 }
796
Paint(void)797 void TextWindow::Paint(void) {
798 int width, height;
799 GetTextWindowSize(&width, &height);
800
801 // We would like things pixel-exact, to avoid shimmering.
802 glViewport(0, 0, width, height);
803 glMatrixMode(GL_PROJECTION);
804 glLoadIdentity();
805 glMatrixMode(GL_MODELVIEW);
806 glLoadIdentity();
807 glClearColor(0, 0, 0, 1);
808 glClear(GL_COLOR_BUFFER_BIT);
809 glColor3d(1, 1, 1);
810
811 glTranslated(-1, 1, 0);
812 glScaled(2.0/width, -2.0/height, 1);
813 // Make things round consistently, avoiding exact integer boundary
814 glTranslated(-0.1, -0.1, 0);
815
816 halfRows = height / (LINE_HEIGHT/2);
817
818 int bottom = top[rows-1] + 2;
819 scrollPos = min(scrollPos, bottom - halfRows);
820 scrollPos = max(scrollPos, 0);
821
822 // Let's set up the scroll bar first
823 MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
824
825 // Create the bitmap font that we're going to use.
826 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
827 glEnable(GL_BLEND);
828
829 // Now paint the window.
830 int r, c, a;
831 for(a = 0; a < 2; a++) {
832 if(a == 0) {
833 glBegin(GL_QUADS);
834 } else if(a == 1) {
835 glEnable(GL_TEXTURE_2D);
836 ssglInitializeBitmapFont();
837 glBegin(GL_QUADS);
838 }
839
840 for(r = 0; r < rows; r++) {
841 int ltop = top[r];
842 if(ltop < (scrollPos-1)) continue;
843 if(ltop > scrollPos+halfRows) break;
844
845 for(c = 0; c < min((width/CHAR_WIDTH)+1, (int) MAX_COLS); c++) {
846 int x = LEFT_MARGIN + c*CHAR_WIDTH;
847 int y = (ltop-scrollPos)*(LINE_HEIGHT/2) + 4;
848
849 int fg = meta[r][c].fg;
850 int bg = meta[r][c].bg;
851 RgbaColor bgRgb = meta[r][c].bgRgb;
852
853 // On the first pass, all the background quads; on the next
854 // pass, all the foreground (i.e., font) quads.
855 if(a == 0) {
856 int bh = LINE_HEIGHT, adj = -2;
857 if(bg == 'z') {
858 glColor3f(bgRgb.redF(), bgRgb.greenF(), bgRgb.blueF());
859 bh = CHAR_HEIGHT;
860 adj += 2;
861 } else {
862 glColor3fv(&(bgColorTable[bg*3]));
863 }
864
865 if(bg != 'd') {
866 // Move the quad down a bit, so that the descenders
867 // still have the correct background.
868 y += adj;
869 ssglAxisAlignedQuad(x, x + CHAR_WIDTH, y, y + bh, false);
870 y -= adj;
871 }
872 } else if(a == 1) {
873 glColor3fv(&(fgColorTable[fg*3]));
874 ssglBitmapCharQuad(text[r][c], x, y + CHAR_HEIGHT);
875
876 // If this is a link and it's hovered, then draw the
877 // underline
878 if(meta[r][c].link && meta[r][c].link != 'n' &&
879 (r == hoveredRow && c == hoveredCol))
880 {
881 int cs = c, cf = c;
882 while(cs >= 0 && meta[r][cs].link &&
883 meta[r][cs].f == meta[r][c].f &&
884 meta[r][cs].data == meta[r][c].data)
885 {
886 cs--;
887 }
888 cs++;
889
890 while( meta[r][cf].link &&
891 meta[r][cf].f == meta[r][c].f &&
892 meta[r][cf].data == meta[r][c].data)
893 {
894 cf++;
895 }
896
897 // But don't underline checkboxes or radio buttons
898 while(((text[r][cs] >= 0xe000 && text[r][cs] <= 0xefff) ||
899 text[r][cs] == ' ') &&
900 cs < cf)
901 {
902 cs++;
903 }
904
905 glEnd();
906
907 // Always use the color of the rightmost character
908 // in the link, so that underline is consistent color
909 fg = meta[r][cf-1].fg;
910 glColor3fv(&(fgColorTable[fg*3]));
911 glDisable(GL_TEXTURE_2D);
912 glLineWidth(1);
913 glBegin(GL_LINES);
914 int yp = y + CHAR_HEIGHT;
915 glVertex2d(LEFT_MARGIN + cs*CHAR_WIDTH, yp);
916 glVertex2d(LEFT_MARGIN + cf*CHAR_WIDTH, yp);
917 glEnd();
918
919 glEnable(GL_TEXTURE_2D);
920 glBegin(GL_QUADS);
921 }
922 }
923 }
924 }
925
926 glEnd();
927 glDisable(GL_TEXTURE_2D);
928 }
929
930 // The line to indicate the column of radio buttons that indicates the
931 // active group.
932 SS.GW.GroupSelection();
933 // Make sure this test agrees with test to determine which screen is drawn
934 if(!SS.GW.pending.description && gs.n == 0 && gs.constraints == 0 &&
935 shown.screen == SCREEN_LIST_OF_GROUPS)
936 {
937 int x = 29, y = 70 + LINE_HEIGHT;
938 y -= scrollPos*(LINE_HEIGHT/2);
939
940 glLineWidth(1);
941 glColor3fv(&(fgColorTable['t'*3]));
942 glBegin(GL_LINES);
943 glVertex2d(x, y);
944 glVertex2d(x, y+40);
945 glEnd();
946 }
947
948 // The header has some icons that are drawn separately from the text
949 DrawOrHitTestIcons(PAINT, 0, 0);
950
951 // And we may show a color picker for certain editable fields
952 DrawOrHitTestColorPicker(PAINT, false, 0, 0);
953 }
954
MouseEvent(bool leftClick,bool leftDown,double x,double y)955 void TextWindow::MouseEvent(bool leftClick, bool leftDown, double x, double y) {
956 if(TextEditControlIsVisible() || GraphicsEditControlIsVisible()) {
957 if(DrawOrHitTestColorPicker(leftClick ? CLICK : HOVER, leftDown, x, y))
958 {
959 return;
960 }
961
962 if(leftClick) {
963 HideEditControl();
964 HideGraphicsEditControl();
965 } else {
966 SetMousePointerToHand(false);
967 }
968 return;
969 }
970
971 DrawOrHitTestIcons(leftClick ? CLICK : HOVER, x, y);
972
973 GraphicsWindow::Selection ps = SS.GW.hover;
974 SS.GW.hover.Clear();
975
976 int prevHoveredRow = hoveredRow,
977 prevHoveredCol = hoveredCol;
978 hoveredRow = 0;
979 hoveredCol = 0;
980
981 // Find the corresponding character in the text buffer
982 int c = (int)((x - LEFT_MARGIN) / CHAR_WIDTH);
983 int hh = (LINE_HEIGHT)/2;
984 y += scrollPos*hh;
985 int r;
986 for(r = 0; r < rows; r++) {
987 if(y >= top[r]*hh && y <= (top[r]+2)*hh) {
988 break;
989 }
990 }
991 if(r < 0 || c < 0 || r >= rows || c >= MAX_COLS) {
992 SetMousePointerToHand(false);
993 goto done;
994 }
995
996 hoveredRow = r;
997 hoveredCol = c;
998
999 #define META (meta[r][c])
1000 if(leftClick) {
1001 if(META.link && META.f) {
1002 (META.f)(META.link, META.data);
1003 Show();
1004 InvalidateGraphics();
1005 }
1006 } else {
1007 if(META.link) {
1008 SetMousePointerToHand(true);
1009 if(META.h) {
1010 (META.h)(META.link, META.data);
1011 }
1012 } else {
1013 SetMousePointerToHand(false);
1014 }
1015 }
1016 #undef META
1017
1018 done:
1019 if((!ps.Equals(&(SS.GW.hover))) ||
1020 prevHoveredRow != hoveredRow ||
1021 prevHoveredCol != hoveredCol)
1022 {
1023 InvalidateGraphics();
1024 InvalidateText();
1025 }
1026 }
1027
MouseLeave(void)1028 void TextWindow::MouseLeave(void) {
1029 tooltippedIcon = NULL;
1030 hoveredIcon = NULL;
1031 hoveredRow = 0;
1032 hoveredCol = 0;
1033 InvalidateText();
1034 }
1035
ScrollbarEvent(int newPos)1036 void TextWindow::ScrollbarEvent(int newPos) {
1037 if(TextEditControlIsVisible())
1038 return;
1039
1040 int bottom = top[rows-1] + 2;
1041 newPos = min(newPos, bottom - halfRows);
1042 newPos = max(newPos, 0);
1043
1044 if(newPos != scrollPos) {
1045 scrollPos = newPos;
1046 MoveTextScrollbarTo(scrollPos, top[rows - 1] + 1, halfRows);
1047 InvalidateText();
1048 }
1049 }
1050
1051