1 module unde.file_manager.draw_path.draw_elements;
2 
3 import unde.global_state;
4 import unde.lib;
5 import unde.viewers.image_viewer.lib;
6 
7 import derelict.sdl2.sdl;
8 import derelict.sdl2.ttf;
9 import derelict.sdl2.image;
10 
11 import std.stdio;
12 import std.conv;
13 import std.math;
14 import std..string;
15 import std.exception;
16 import std.utf;
17 
18 package class SDL_Rect_With_Not_Visible_And_No_Draw
19 {
20     SDL_Rect sdl_rect;
21     bool not_visible;
22     bool no_draw;
23     bool current_path;
24 }
25 
26 package SDL_Rect_With_Not_Visible_And_No_Draw
27 draw_rect_with_color_by_size(GlobalState gs, ref RectSize rectsize,
28         SortType sort,
29         ref CoordinatesPlusScale surf, bool selected = false,
30         string path = "")
31 {
32     if (rectsize.rect(sort).w == 0) rectsize.rect(sort).w = 1;
33     if (rectsize.rect(sort).h == 0) rectsize.rect(sort).h = 1;
34     //double density = cast(double)(rectsize.size)/(rectsize.w*rectsize.h);
35     //double density_level = log10(density);
36     uint color = 0x80FFFFFF;
37     if (selected)
38     {
39         color = 0x800000FF;
40     }
41     else if (rectsize.size >= 0)
42     {
43         double size_level = log10(rectsize.size);
44 
45         SDL_Color rgba = gs.grad.getColor(size_level);
46         /*writefln("%s. (%s / %sx%s) size_level=%.2f, color=(%d, %d, %d)",
47                   path, rectsize.size, rectsize.w, rectsize.h,
48                   size_level,
49                   rgba.r, rgba.g, rgba.b);*/
50         color = (0x80<<24) | (rgba.r<<16) | (rgba.g<<8) | rgba.b;
51     }
52 
53     auto ret = new SDL_Rect_With_Not_Visible_And_No_Draw;
54 
55     /* EN: Calculate output square coordiantes for path
56        RU: Расчитать координаты квадрата для пути */
57     SDL_Rect sdl_rect = rectsize.rect(sort).to_screen(surf);
58     /*writefln("surf_scale=%s", scale);
59     writefln("%s rectsize(%s, %s, %s, %s) - sdl_rect(%s, %s, %s, %s)",
60             path,
61             rectsize.x, rectsize.y, rectsize.w, rectsize.h,
62             sdl_rect.x, sdl_rect.y, sdl_rect.w, sdl_rect.h);*/
63 
64     /* EN: not visible if it go out of surface
65        RU: не видимый если выходит за рамки surface'а */
66     bool not_visible = (sdl_rect.x+sdl_rect.w)<0 || sdl_rect.x > gs.surf.w ||
67         (sdl_rect.y+sdl_rect.h) < 0 || sdl_rect.y > gs.surf.h;
68     //bool no_draw = (2*sdl_rect.w > width && 2*sdl_rect.h > height);
69 
70     SDL_Rect onscreen_rect = rectsize.rect(sort).to_screen(gs.screen);
71 
72     /* EN: no draw if too big and takes almost all screen
73        RU: не рисовать, если занимает почти весь экран
74          (т.е. прямоугольник исчезает при приближении) */
75     bool no_draw = onscreen_rect.x < gs.screen.w/8 &&
76         (onscreen_rect.x+onscreen_rect.w)>(gs.screen.w*7/8) ||
77         onscreen_rect.y < gs.screen.h/8 &&
78         (onscreen_rect.y+onscreen_rect.h)>(gs.screen.h*7/8) ||
79         not_visible ||
80         sdl_rect.w > 2000 || sdl_rect.h > 2000;
81 
82     bool current_path = (no_draw || not_visible) &&
83         onscreen_rect.x <= gs.screen.w/2 && (onscreen_rect.x+onscreen_rect.w) >= (gs.screen.w*1/2) &&
84         onscreen_rect.y <= gs.screen.h/2 && (onscreen_rect.y+onscreen_rect.h) >= (gs.screen.h*1/2);
85 
86     if ( current_path )
87         gs.current_path_rect = rectsize.rect(sort);
88 
89     if (!no_draw && !not_visible)
90     {
91         SDL_Rect sdl_rect_dup = sdl_rect;
92         unDE_RenderFillRect(gs.renderer, &sdl_rect_dup, color);
93     }
94 
95     ret.sdl_rect = sdl_rect;
96     ret.not_visible = not_visible;
97     ret.no_draw = no_draw;
98     ret.current_path = current_path;
99 
100     return ret;
101 }
102 
103 package void
104 draw_center_rect_with_color(
105         GlobalState gs,
106         SDL_Rect_With_Not_Visible_And_No_Draw rnvnd,
107         uint color)
108 {
109     SDL_Rect sdl_rect = rnvnd.sdl_rect;
110     sdl_rect.x = sdl_rect.x+sdl_rect.w/3;
111     sdl_rect.y = sdl_rect.y+sdl_rect.h/3;
112     sdl_rect.w /= 3;
113     sdl_rect.h /= 3;
114 
115     if (!rnvnd.no_draw && !rnvnd.not_visible)
116     {
117         SDL_Rect sdl_rect_dup = sdl_rect;
118         unDE_RenderFillRect(gs.renderer, &sdl_rect_dup, color);
119     }
120 }
121 
122 /* EN: draw picture - interface element
123    RU: рисует картинку - элемент интерфейса */
124 /*
125 package SDL_Rect
126 draw_interface_picture(GlobalState gs,
127         string path,
128         int x, int y,
129         double scale,
130         in ref SDL_Rect rect)
131 {
132     immutable int text_size = 1024;
133 
134     x = x * rect.w/text_size;
135     y = y * rect.h/text_size;
136 
137     auto st = get_image_from_cache(gs, path);
138     
139     SDL_Rect dst;
140     if (st && st.texture)
141     {
142         dst.x = rect.x + x;
143         dst.y = rect.y + y;
144         dst.w = cast(int)(st.w*rect.w*scale/1024.0);
145         dst.h = cast(int)(st.h*rect.h*scale/1024.0);
146 
147         int r = SDL_RenderCopyEx(gs.renderer, st.texture, null, &dst, 0,
148                     null, SDL_FLIP_NONE);
149         if (r < 0)
150         {
151             writefln( "draw_interface_picture(%s): Error while render copy: %s", 
152                     path, SDL_GetError().to!string() );
153         }
154     }
155     else
156     {
157         writefln("Can't load %s: %s",
158                 path,
159                 to!string(IMG_GetError()));
160     }
161     return dst;
162 }
163 */
164 
165 package SDL_Rect
166 draw_button(GlobalState gs,
167         int x, int y,
168         int w, int h,
169         in ref SDL_Rect rect)
170 {
171     immutable int text_size = 1024;
172 
173     x = x * rect.w/text_size;
174     y = y * rect.h/text_size;
175 
176     SDL_Rect dst;
177     dst.x = rect.x + x;
178     dst.y = rect.y + y;
179     dst.w = cast(int)(w*rect.w/1024.0);
180     dst.h = cast(int)(h*rect.h/1024.0);
181 
182     unDE_RenderFillRect(gs.renderer, &dst, 0xFF000080);
183 
184     return dst;
185 }
186 
187 /* EN: draw file-picture
188    RU: рисует файл-картинку */
189 package bool
190 draw_picture(GlobalState gs,
191         in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd,
192         in string p,
193         in bool fast,
194         ref int ret)
195 {
196     Texture_Tick *st;
197     /* EN: Get picture from cache or load it from file
198        RU: Получить картинку из кеша или загрузить из файла */
199     if ( !p.endsWith(".xcf") )
200     {
201         with (gs.image_viewer)
202         {
203             st = p in image_cache;
204             if (st)
205             {
206                 //writefln("Get from cache %s", p);
207                 st.tick = SDL_GetTicks();
208                 last_image_cache_use = SDL_GetTicks();
209             }
210             else if (!fast)
211             {
212                 //writefln("Load image %s", p);
213                 long tick1 = SDL_GetTicks();
214                 auto surface = IMG_Load(p.toStringz());
215                 long tick2 = SDL_GetTicks();
216                 //writefln("%.3f s", (cast(double)tick2-tick1)/1000);
217                 if (surface)
218                 {
219                     auto image_texture =
220                         SDL_CreateTextureFromSurface(gs.renderer, surface);
221 
222                     image_cache[p] = Texture_Tick(surface.w, surface.h, [], image_texture, SDL_GetTicks());
223                     SDL_FreeSurface(surface);
224                     last_image_cache_use = SDL_GetTicks();
225                     st = p in image_cache;
226                     //writefln("Add to cache %s", p);
227                 }
228 
229                 if (tick2-tick1 > 100)
230                 {
231                     ret = -1;
232                 }
233             }
234         }
235     }
236 
237     if (st && st.texture)
238     {
239         SDL_Rect dst;
240         /* EN: Calculate output rectangle:
241            RU: Рассчитать выходной прямоугольник: */
242         /* EN: If output square for path more than picture
243            RU: Если выходной квадрат для пути больше картинки */
244         if (rnvnd.sdl_rect.w > st.w && rnvnd.sdl_rect.h > st.h)
245         {
246             /* EN: If square of path takes width less 512
247                RU: Если квадрат пути занимает по ширине меньше 512 */
248             if (rnvnd.sdl_rect.w < 512)
249             {
250                 /* EN: Draw picture at the centre without scaling
251                    RU: Рисуем картинку по центру без увеличения */
252                 dst.x = rnvnd.sdl_rect.x + (rnvnd.sdl_rect.w-st.w)/2;
253                 dst.y = rnvnd.sdl_rect.y + (rnvnd.sdl_rect.h-st.h)/2;
254                 dst.w = st.w;
255                 dst.h = st.h;
256             }
257             /* EN: If the picture less by width and height 512
258                RU: Если сама картинка меньше по высоте и ширине 512 */
259             else if ( st.w < 512 && st.h < 512 )
260             {
261                 /* EN: Scale up and center picture
262                    RU: Отцентрировать и увеличить картинку, учитывая
263                     что при размере выходного квадрата <= 512 (см. выше)
264                     она имеет масштаб 100% */
265                 dst.x = cast(int)(rnvnd.sdl_rect.x +
266                         (rnvnd.sdl_rect.w-st.w*rnvnd.sdl_rect.w/512.0)/2);
267                 dst.y = cast(int)(rnvnd.sdl_rect.y +
268                         (rnvnd.sdl_rect.h-st.h*rnvnd.sdl_rect.w/512.0)/2);
269                 dst.w = cast(int)(st.w*rnvnd.sdl_rect.w/512.0);
270                 dst.h = cast(int)(st.h*rnvnd.sdl_rect.w/512.0);
271             }
272             else
273             {
274                 goto other;
275             }
276         }
277         else
278         {
279         other:
280             if (st.w > st.h)
281             {
282                 /* EN: Center and scale down picture to take all width
283                     of path square
284                    RU: Отцентрировать и уменьшить картинку так, чтобы
285                     она заняла всю ширину квадрата */
286                 dst.x = rnvnd.sdl_rect.x;
287                 dst.y = rnvnd.sdl_rect.y+
288                     (rnvnd.sdl_rect.h-rnvnd.sdl_rect.h*st.h/st.w)/2;
289                 dst.w = rnvnd.sdl_rect.w;
290                 dst.h = rnvnd.sdl_rect.h*st.h/st.w;
291             }
292             else
293             {
294                 /* EN: Center and scale down picture to take all height
295                     of path square
296                    RU: Отцентрировать и уменьшить картинку так, чтобы
297                     она заняла всю высоту квадрата */
298                 dst.x = rnvnd.sdl_rect.x+
299                     (rnvnd.sdl_rect.w-rnvnd.sdl_rect.w*st.w/st.h)/2;
300                 dst.y = rnvnd.sdl_rect.y;
301                 dst.w = rnvnd.sdl_rect.w*st.w/st.h;
302                 dst.h = rnvnd.sdl_rect.h;
303             }
304         }
305 
306         int r = SDL_RenderCopyEx(gs.renderer, st.texture, null, &dst, 0,
307                     null, SDL_FLIP_NONE);
308         if (r < 0)
309         {
310             writefln( "draw_picture(%s): Error while render copy: %s", 
311                     p, SDL_GetError().to!string() );
312         }
313 
314         return true;
315     }
316     else
317         return false;
318 }
319 
320 package SDL_Rect
321 draw_line(GlobalState gs, string text, 
322         double x, double y, int size, const SDL_Rect rect)
323 {
324     if (text.length > 0 && text[$-1] == '\r') text = text[0..$-1];
325 
326     immutable int text_size = 1024;
327 
328     x = x * rect.w/text_size;
329     y = y * rect.h/text_size;
330 
331     int line_height = cast(int)(round(SQRT2^^size)*1.2);
332     auto tt = gs.text_viewer.font.get_line_from_cache(text, 
333             size, 0, line_height, SDL_Color(255, 255, 255, 255));
334     if (!tt && !tt.texture)
335     {
336         /*throw new Exception("Can't create text_surface: "~
337                 to!string(TTF_GetError()));*/
338         return SDL_Rect();
339     }
340 
341     auto text_texture = tt.texture;
342 
343     long line_ax = cast(long)(x + rect.x);
344     long line_ay = cast(long)(y + rect.y);
345     /*if (line_x == 0 && line_y == 0)
346     {
347         writefln("sdl_rect.x=%s, line_ax=%s\n",
348                 rnvnd.sdl_rect.x, line_ax);
349     }*/
350 
351     SDL_Rect src;
352     src.x = 0;
353     src.y = 0;
354     src.w = (tt.w > 2*text_size) ?
355         2*text_size :
356         tt.w;
357     src.h = tt.h;
358 
359     SDL_Rect dst;
360     dst.x = cast(int)line_ax;
361     dst.y = cast(int)line_ay;
362     dst.w = (tt.w > 2*text_size) ?
363         cast(int)(rect.w) :
364         cast(int)(tt.w*rect.w/text_size/2.0);
365     dst.h =
366         cast(int)(tt.h*rect.h/text_size/2.0);
367     /*writefln("%s - %s, %s, %s, %s",
368             line, dst.x, dst.y, dst.w, dst.h);*/
369 
370     int r = SDL_RenderCopyEx(gs.renderer, text_texture, null, &dst, 0,
371                 null, SDL_FLIP_NONE);
372     if (r < 0)
373     {
374         writefln( "draw_line(%s): Error while render copy: %s", text, SDL_GetError().to!string() );
375     }
376 
377     return dst;
378 }
379 
380 /* RU: Попытаться найти плохой UTF-8 символ в буфере */
381 private void
382 try_find_bad_utf8_character(in ubyte[] buf, 
383         out bool wrongSymbolFound,
384         out int reason)
385 {
386     int sizeofsymbol = 0;
387 
388     foreach(b; buf)
389     {
390         if (sizeofsymbol > 0)
391         {
392             if ((b & 0b11000000) == 0b10000000)
393             {
394                 sizeofsymbol--;
395             }
396             else
397             {
398                 reason = 1;
399                 wrongSymbolFound = true;
400                 break;
401             }
402         }
403         else
404         {
405             if ((b & 0x10000000) == 0)
406             {
407                 if (b < 32 && b != 0x0a && b != 0x0d && b != 0x09)
408                 {
409                     reason = 2;
410                     wrongSymbolFound = true;
411                     break;
412                 }
413             }
414             else if ((b & 0b11100000) == 0b11000000)
415             {
416                 sizeofsymbol = 1;
417             }
418             else if ((b & 0b11110000) == 0b11100000)
419             {
420                 sizeofsymbol = 2;
421             }
422             else if ((b & 0b11111000) == 0b11110000)
423             {
424                 sizeofsymbol = 3;
425             }
426             else if ((b & 0b11111100) == 0b11111000)
427             {
428                 sizeofsymbol = 4;
429             }
430             else if ((b & 0b11111110) == 0b11111100)
431             {
432                 sizeofsymbol = 5;
433             }
434             else
435             {
436                 reason = 3;
437                 wrongSymbolFound = true;
438                 break;
439             }
440         }
441     }
442 }
443 
444 
445 package void
446 draw_text_file(GlobalState gs,
447         in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd,
448         in string path,
449         ref int ret)
450 {
451     File file;
452     try {
453         file = File(path);
454     }
455     catch (Exception exp)
456     {
457         return;
458     }
459 
460     ubyte[] buf;
461     bool wrongSymbolFound = false;
462     int reason = 0;
463 
464     try {
465         buf = file.byChunk(4096).front();
466         try_find_bad_utf8_character(buf, wrongSymbolFound, reason);
467     }
468     catch (ErrnoException)
469     {
470         reason = 4;
471         wrongSymbolFound = true;
472     }
473 
474     /*if (path.endsWith(".brs"))
475     {
476         writefln("reason=%s, wrongSymbolFound=%s", reason, wrongSymbolFound);
477     }*/
478 
479     if (!wrongSymbolFound && !path.endsWith(".pdf") && !path.endsWith(".ps"))
480     {
481         try{
482             file.seek(0);
483             long lines = 0;
484             foreach(line; file.byLine())
485             {
486                 double line_y = 18*lines;
487                 double line_x = 0;
488                 if (line_y+18 > 1024)
489                 {
490                     break;
491                 }
492 
493                 if (line == "")
494                 {
495                     lines++;
496                     continue;
497                 }
498 
499                 draw_line(gs, line.idup(), line_x, line_y, 10, rnvnd.sdl_rect);
500 
501                 lines++;
502             }
503 
504             //return;
505         }
506         catch(UTFException exc)
507         {
508             //return;
509         }
510     }
511 }
512 
513 package void
514 draw_direntry_name(GlobalState gs, string name,
515         in ref SDL_Rect_With_Not_Visible_And_No_Draw rnvnd,
516         bool force=false)
517 {
518     try{
519         if (name > "" && (!rnvnd.no_draw || force))
520         {
521             int size = rnvnd.sdl_rect.w/16;
522             int f = cast(int)(floor(log2(size)*2)+2);
523             if (f < 5) return;
524             if (f > 14) f = 14;
525 
526             //writefln("name=%s", name);
527             /* EN: Render text
528                RU: Рендерим текст */
529             int line_height = cast(int)(round(SQRT2^^f)*1.2);
530             auto tt = gs.text_viewer.font.get_line_from_cache(name, 
531                     f, rnvnd.sdl_rect.w, line_height, SDL_Color(255, 255, 255, 255));
532             if (!tt && !tt.texture)
533             {
534                 throw new Exception("Can't create text_surface: "~
535                         to!string(TTF_GetError()));
536             }
537 
538             auto ttb = gs.text_viewer.font.get_line_from_cache(name, 
539                     f, rnvnd.sdl_rect.w, line_height, SDL_Color(0, 0, 0, 255));
540             if (!tt && !tt.texture)
541             {
542                 throw new Exception("Can't create text_surface: "~
543                         to!string(TTF_GetError()));
544             }
545 
546             auto text_texture = tt.texture;
547             auto text_texture_black = ttb.texture;
548 
549             /* EN: Rerender if it's too wide
550                RU: Перерендериваем меньшим шрифтом, если
551                     надпись слишком велика */
552             /*
553             while ( text_surface.w > rnvnd.sdl_rect.w && f > 6 )
554             {
555                 f--;
556                 SDL_FreeSurface(text_surface);
557                 text_surface = TTF_RenderUTF8_Blended(
558                         gs.text_viewer.font.font[0][f], name.toStringz(),
559                         SDL_Color(255, 255, 255, 255));
560                 if (!text_surface)
561                 {
562                     throw new Exception("Can't create text_surface: "~
563                             to!string(TTF_GetError()));
564                 }
565             }*/
566 
567             /* EN: calculate output coordinates
568                RU: расчитывает выходные координаты */
569             SDL_Rect sdl_rect;
570             sdl_rect.x = cast(int)(rnvnd.sdl_rect.x +
571                     (rnvnd.sdl_rect.w - tt.w)/2);
572             sdl_rect.y = cast(int)(rnvnd.sdl_rect.y +
573                     (rnvnd.sdl_rect.h - tt.h)/2);
574             sdl_rect.w = tt.w+1;
575             sdl_rect.h = tt.h+1;
576 
577             /* EN: Draw
578                RU: Рисуем */
579             auto r = SDL_RenderCopyEx(gs.renderer, text_texture_black, null, &sdl_rect, 0,
580                         null, SDL_FLIP_NONE);
581             if (r < 0)
582             {
583                 writefln( "draw_direntry_name(): Error while render copy: %s", SDL_GetError().to!string() );
584             }
585 
586             sdl_rect.x -= 1;
587             sdl_rect.y -= 1;
588 
589             r = SDL_RenderCopyEx(gs.renderer, text_texture, null, &sdl_rect, 0,
590                         null, SDL_FLIP_NONE);
591             if (r < 0)
592             {
593                 writefln( "draw_direntry_name(): Error while render copy: %s", SDL_GetError().to!string() );
594             }
595 
596 
597         }
598     } catch (Exception e)
599     {
600     }
601 }
602