1 module unde.draw;
2 
3 import unde.global_state;
4 import unde.lib;
5 import unde.scan;
6 import unde.file_manager.remove_paths;
7 import unde.file_manager.copy_paths;
8 import unde.file_manager.draw_path.draw_path;
9 import unde.marks;
10 import unde.file_manager.find_path;
11 import unde.file_manager.events;
12 import unde.viewers.image_viewer.lib;
13 import unde.viewers.text_viewer.lib;
14 import unde.command_line.lib;
15 import unde.keybar.lib;
16 import unde.keybar.settings;
17 import unde.tick;
18 import unde.path_mnt;
19 import unde.clickable;
20 
21 import berkeleydb.all;
22 import core.thread;
23 
24 import derelict.sdl2.sdl;
25 import derelict.sdl2.ttf;
26 
27 import std.math;
28 import std.stdio;
29 import std..string;
30 import std.conv;
31 import std.container.slist;
32 
33 enum unDE_Flags {
34     Magnify   = 0x01,
35     Unmagnify = 0x02,
36     Left      = 0x04,
37     Right     = 0x08,
38     Up        = 0x10,
39     Down      = 0x20,
40 }
41 
42 
43 class DrawPathFiber : Fiber
44 {
45         GlobalState gs;
46         CoordinatesPlusScale[] surface;
47         int result;
48 
49         this(GlobalState gs, DbTxn txn, PathMnt path,
50                 DRect apply_rect,
51                 SortType sort,
52                 CoordinatesPlusScale[] surface)
53         {
54             this.gs = gs;
55             this.txn = txn;
56             this.path = path;
57             this.apply_rect = apply_rect;
58             this.sort = sort;
59             this.surface = surface;
60 
61             super(&run, 65536);
62         }
63 
64     private:
65         DbTxn txn;
66         PathMnt path;
67         DRect apply_rect;
68         SortType sort;
69 
70         void run()
71         {
72             result = draw_path(gs, txn, path, surface[0], apply_rect, sort);
73             draw_marks(gs, surface[0]);
74         }
75 }
76 
77 /* EN: The difference of unDE_SDL_RenderCopy from SDL_RenderCopy is
78     that x, y coordinates of srcrect, dstrect maybe negative
79     and width, height maybe also more than texture or window size.
80     And this works as expected.
81    RU: Отличие unDE_SDL_RenderCopy от SDL_RenderCopy в том,
82     что координвты x и y прямоугольников srcrect, dstrect могут
83     быть отрицательными и ширина/высота также может выходить
84     за рамки текстуры или окна и это работает как ожидается
85  */
86 void unDE_RenderCopy(GlobalState gs,
87         ref SDL_Rect srcrect, ref SDL_Rect dstrect)
88 {
89     if (srcrect.x < 0)
90     {
91         dstrect.x = cast(int)(-srcrect.x*gs.surf.scale/gs.screen.scale);
92         srcrect.x = 0;
93     }
94     if (srcrect.y < 0)
95     {
96         dstrect.y = cast(int)(-srcrect.y*gs.surf.scale/gs.screen.scale);
97         srcrect.y = 0;
98     }
99     if ((srcrect.x+srcrect.w) > 2*gs.screen.w)
100     {
101         dstrect.w = cast(int)(gs.screen.w -
102                 (srcrect.x+srcrect.w - 2*gs.screen.w)*gs.surf.scale/gs.screen.scale);
103         srcrect.w = cast(int)(2*gs.screen.w - srcrect.x);
104     }
105     if ((srcrect.y+srcrect.h) > 2*gs.screen.h)
106     {
107         dstrect.h = cast(int)(gs.screen.h -
108                 (srcrect.y+srcrect.h - 2*gs.screen.h)*gs.surf.scale/gs.screen.scale);
109         srcrect.h = cast(int)(2*gs.screen.h - srcrect.y);
110     }
111 
112     int r = SDL_RenderCopy(gs.renderer, gs.texture, &srcrect, &dstrect);
113     if (r < 0)
114     {
115         writefln( "unDE_RenderCopy(): Error while render copy: %s", SDL_GetError().to!string() );
116     }
117 }
118 
119 
120 /*RU: Рассчитать прямоугольник который занимает screen и surface
121     и изменить размер surface если screen вышел за его пределы*/
122 CoordinatesPlusScale[]
123 calculate_rectangles_of_surf_and_screen_and_change_surf_size_if_needed(
124         GlobalState gs, DrawPathFiber draw_path_fiber, ref bool redraw)
125 {
126     DRect surf_rect = gs.surf.getRect();
127     DRect scr_rect = gs.screen.getRect();
128 
129     int tries = 0;
130     if ( draw_path_fiber is null &&
131             ( (!scr_rect.In(surf_rect) ||
132             gs.screen.scale < gs.surf.scale || 
133             gs.screen.scale/gs.surf.scale > 2) || redraw || gs.dirty) )
134     {
135 begin:
136         tries++;
137         redraw = false;
138         gs.dirty = false;
139         if (gs.selection_finish == 1 || 
140                 (gs.selection_finish == 0 && gs.selection_stage == 2))
141         {
142             gs.selection_lsof = cast(int)gs.selection_list.length;
143             gs.selection_finish = 2;
144         }
145 
146         int r = SDL_SetRenderTarget(gs.renderer, gs.surf_texture);
147         if (r < 0)
148         {
149             throw new Exception(format("Error while set render target gs.surf_texture: %s",
150                         SDL_GetError().to!string() ));
151         }
152 
153         r = SDL_RenderClear(gs.renderer);
154         if (r < 0)
155         {
156             throw new Exception(format("crosasacssin: Error while clear renderer: %s",
157                     SDL_GetError().to!string() ));
158         }
159 
160         r = SDL_SetRenderTarget(gs.renderer, null);
161         if (r < 0)
162         {
163             throw new Exception(format("Error while restore render target: %s",
164                     SDL_GetError().to!string() ));
165         }
166 
167         auto surface = new CoordinatesPlusScale[1];
168         surface[0].scale = gs.screen.scale/sqrt(2.0);
169 
170         surface[0].x = gs.screen.x - 
171             surface[0].scale*(gs.screen.w*sqrt(2.0) - gs.screen.w)/2;
172         surface[0].y = gs.screen.y - 
173             surface[0].scale*(gs.screen.h*sqrt(2.0) - gs.screen.h)/2;
174         surface[0].w = gs.surf.w;
175         surface[0].h = gs.surf.h;
176 
177         {
178             surf_rect = surface[0].getRect();
179             if ((!scr_rect.In(surf_rect) ||
180                         gs.screen.scale < surface[0].scale || 
181                         gs.screen.scale/surface[0].scale > 2))
182             {
183                 scr_rect = DRect(0, 0, 0, 0);
184                 gs.initScreenAndSurf();
185                 if (tries < 2)
186                     goto begin;
187             }
188             assert(!(!scr_rect.In(surf_rect) ||
189                         gs.screen.scale < surface[0].scale || 
190                         gs.screen.scale/surface[0].scale > 2));
191         }
192         return surface;
193     }
194     else
195         return null;
196 }
197 
198 /* RU: рисовать "путь" не больше 100 мс и если успели дорисовать
199     преобразовать surface в текстуру */
200 void draw_path_while_there_is_time_and_create_texture_if_it_is_finished(
201         GlobalState gs, ref DrawPathFiber draw_path_fiber, ref bool redraw)
202 {
203     int r = SDL_SetRenderTarget(gs.renderer, gs.surf_texture);
204     if (r < 0)
205     {
206         throw new Exception(format("Error while set render target gs.surf_texture: %s",
207                     SDL_GetError().to!string() ));
208     }
209 
210     uint max_draw_tick = SDL_GetTicks() + 100;
211     while (draw_path_fiber.state != Fiber.State.TERM &&
212             (SDL_GetTicks() < max_draw_tick || 
213              gs.texture == null) )
214     {
215         draw_path_fiber.call();
216     }
217 
218     r = SDL_SetRenderTarget(gs.renderer, null);
219     if (r < 0)
220     {
221         throw new Exception(format("Error while restore render target: %s",
222                 SDL_GetError().to!string() ));
223     }
224 
225     if (draw_path_fiber.state == Fiber.State.TERM)
226     {
227         if (draw_path_fiber.result < 0)
228             redraw = true;
229 
230         gs.surf = draw_path_fiber.surface[0];
231         draw_path_fiber = null;
232 
233         gs.clickable_list = gs.new_clickable_list;
234         gs.new_clickable_list = SList!Clickable();
235 
236         gs.double_clickable_list = gs.new_double_clickable_list;
237         gs.new_double_clickable_list = SList!Clickable();
238 
239         gs.right_clickable_list = gs.new_right_clickable_list;
240         gs.new_right_clickable_list = SList!Clickable();
241 
242         gs.double_right_clickable_list = gs.new_double_right_clickable_list;
243         gs.new_double_right_clickable_list = SList!Clickable();
244 
245         gs.middle_clickable_list = gs.new_middle_clickable_list;
246         gs.new_middle_clickable_list = SList!Clickable();
247 
248         if (gs.selection_finish == 2)
249         {
250             gs.selection_list = gs.selection_list[gs.selection_lsof..$];
251             calculate_selection_sub(gs);
252                 
253             gs.selection_lsof = 0;
254             gs.selection_finish = 0;
255             /*writefln("Selected:");
256             foreach(key; gs.selection_hash.byKey())
257             {
258                 writefln("%s", key);
259             }*/
260         }
261 
262         auto texture = gs.texture;
263         gs.texture = gs.surf_texture;
264         gs.surf_texture = texture;
265     }
266     else
267     {
268         process_events(gs);
269     }
270 }
271 
272 void draw_anim(GlobalState gs)
273 {
274     foreach (path, ref created_directory; gs.animation_info)
275     {
276         if (created_directory.from_calculated &&
277                 created_directory.to_calculated)
278         {
279             with (created_directory)
280             {
281                 if (frame >= 100)
282                 {
283                     gs.animation_info.remove(path);
284                     continue;
285                 }
286                 SDL_Rect rect;
287                 rect.x = from.x + (to.x - from.x)*cast(int)frame/100;
288                 rect.y = from.y + (to.y - from.y)*cast(int)frame/100;
289                 rect.w = from.w + (to.w - from.w)*cast(int)frame/100;
290                 rect.h = from.h + (to.h - from.h)*cast(int)frame/100;
291                 int r = SDL_RenderCopy(gs.renderer, gs.texture_white, null, &rect);
292                 if (r < 0)
293                 {
294                     writefln( "draw_anim(): Error while render copy: %s", SDL_GetError().to!string() );
295                 }
296 
297                 if (last_frame_time)
298                 {
299                     frame += cast(double)(SDL_GetTicks() - last_frame_time)/10;
300                 }
301                 last_frame_time = SDL_GetTicks();
302             }
303         }
304     }
305 }
306 
307 void draw_messages(GlobalState gs)
308 {
309     ssize_t first = -1;
310     int line = 24;
311     ulong y_off;
312     foreach(i, mes; gs.messages)
313     {
314         if (SDL_GetTicks() - mes.from < 5000)
315         {
316             if (first == -1) 
317             {
318                 first = i;
319 
320                 y_off = gs.screen.h - line*3 - line*(gs.messages.length - first) - 8;
321 
322                 /* EN: render background of console messages
323                    RU: рендерим фон консоли сообщений */
324                 SDL_Rect rect;
325                 rect.x = 32;
326                 rect.y = cast(int)y_off;
327                 rect.w = gs.screen.w - 32*2;
328                 rect.h = cast(int)(line*(gs.messages.length - first) + 8);
329 
330                 int r = SDL_RenderCopy(gs.renderer, gs.texture_black, null, &rect);
331                 if (r < 0)
332                 {
333                     writefln( "draw_messages(), 1: Error while render copy: %s",
334                             SDL_GetError().to!string() );
335                 }
336             }
337 
338             if (mes.texture == null && mes.message != "")
339             {
340                 int line_height = cast(int)(round(SQRT2^^9)*1.2);
341                 auto tt = gs.text_viewer.font.get_line_from_cache(mes.message, 
342                         9, gs.screen.w - 80, line_height, mes.color);
343                 if (!tt && !tt.texture)
344                 {
345                     throw new Exception("Can't create text_surface: "~
346                             to!string(TTF_GetError()));
347                 }
348 
349                 mes.w = tt.w;
350                 mes.h = tt.h;
351 
352                 mes.texture = tt.texture;
353             }
354 
355             if (mes.texture != null)
356             {
357                 /* EN: Render text to screeb
358                    RU: Рендерим текст на экран */
359                 SDL_Rect rect;
360                 rect.x = 40;
361                 rect.y = cast(int)(y_off + 4 + line*(i-first));
362                 rect.w = mes.w;
363                 rect.h = mes.h;
364 
365                 int r = SDL_RenderCopy(gs.renderer, mes.texture, null, &rect);
366                 if (r < 0)
367                 {
368                     writefln(
369                         "draw_messages(), 2: Error while render copy: %s", 
370                         SDL_GetError().to!string() );
371                 }
372             }
373         }
374         else
375         {
376             SDL_DestroyTexture(mes.texture);
377         }
378     }
379     if (first > 0)
380         gs.messages = gs.messages[first..$];
381 }
382 
383 void draw_screen(GlobalState gs, DbTxn txn)
384 {
385     SDL_SetRenderDrawColor(gs.renderer, 0, 0, 0, 0);
386     SDL_RenderClear(gs.renderer);
387 
388     static DrawPathFiber draw_path_fiber;
389     static bool redraw;
390 
391     check_scanners(gs);
392 
393     final switch (gs.state)
394     {
395         case State.FileManager:
396             auto new_surface_coordinates =
397             calculate_rectangles_of_surf_and_screen_and_change_surf_size_if_needed(
398                     gs, draw_path_fiber, redraw);
399             if (new_surface_coordinates)
400             {
401                 find_path(gs, txn, new_surface_coordinates[0], gs.path, gs.apply_rect, gs.sort);
402                 //writefln("find_path: path=%s", gs.path);
403                 draw_path_fiber = new DrawPathFiber(
404                         gs, txn, gs.path, gs.apply_rect, gs.sort,
405                         new_surface_coordinates);
406             }
407 
408             if (draw_path_fiber !is null)
409             {
410                 draw_path_while_there_is_time_and_create_texture_if_it_is_finished(
411                         gs, draw_path_fiber, redraw);
412             }
413 
414             SDL_Rect srcrect;
415             srcrect.x = cast(int)((gs.screen.x - gs.surf.x)/gs.surf.scale);
416             srcrect.y = cast(int)((gs.screen.y - gs.surf.y)/gs.surf.scale);
417             srcrect.w = cast(int)(gs.screen.w*gs.screen.scale/gs.surf.scale);
418             srcrect.h = cast(int)(gs.screen.h*gs.screen.scale/gs.surf.scale);
419 
420             SDL_Rect dstrect;
421             dstrect.x = 0;
422             dstrect.y = 0;
423             dstrect.w = gs.screen.w;
424             dstrect.h = gs.screen.h;
425 
426             unDE_RenderCopy(gs, srcrect, dstrect);
427 
428             draw_anim(gs);
429             break;
430 
431         case State.ImageViewer:
432             draw_image(gs);
433             break;
434 
435         case State.TextViewer:
436             draw_text(gs);
437             break;
438     }
439 
440     setup_keybar(gs);
441     draw_messages(gs);
442     draw_command_line(gs);
443     foreach (uipage; gs.uipages)
444     {
445         if (uipage.show)
446             uipage.on_draw(gs);
447     }
448     draw_keybar(gs);
449     remark_desktop(gs);
450 
451     SDL_SetRenderTarget(gs.renderer, null);
452     SDL_RenderPresent(gs.renderer);
453 }