1 module unde.file_manager.events;
2 
3 import unde.global_state;
4 import unde.clickable;
5 import unde.lib;
6 import unde.file_manager.remove_paths;
7 import unde.file_manager.copy_paths;
8 import unde.file_manager.move_paths;
9 import unde.command_line.events;
10 import unde.viewers.image_viewer.events;
11 import unde.viewers.text_viewer.events;
12 import unde.keybar.settings;
13 import unde.keybar.lib;
14 import unde.marks;
15 import unde.tick;
16 import unde.scan;
17 import unde.path_mnt;
18 import unde.slash;
19 import unde.translations.lib;
20 
21 import berkeleydb.all;
22 import derelict.sdl2.sdl;
23 
24 import std.utf;
25 import std.stdio;
26 import std..string;
27 import std.conv;
28 import std.format;
29 import std.datetime;
30 import std.functional;
31 
32 import std.file;
33 
34 void nothing(GlobalState gs)
35 {
36 }
37 
38 void quit(GlobalState gs)
39 {
40     gs.finish=true;
41 }
42 
43 void restart(GlobalState gs)
44 {
45     gs.finish=true;
46     gs.restart=true;
47 }
48 
49 void mark(GlobalState gs)
50 {
51     gs.mark=true;
52 }
53 
54 void unmark(GlobalState gs)
55 {
56     gs.unmark=true;
57 }
58 
59 void gomark(GlobalState gs)
60 {
61     gs.gomark=true;
62 }
63 
64 void cancel_mark(GlobalState gs)
65 {
66     gs.mark=false;
67     gs.unmark=false;
68     gs.gomark=false;
69 }
70 
71 void
72 rescan(GlobalState gs)
73 {
74     rescan_path(gs, PathMnt(gs.lsblk, gs.full_current_path));
75 }
76 
77 void
78 deselect_all(GlobalState gs)
79 {
80     gs.selection_hash = null;
81     calculate_selection_sub(gs);
82     gs.dirty = true;
83 }
84 
85 void clear_messages(GlobalState gs)
86 {
87     gs.msg_stamp = Clock.currTime().toUnixTime() - 10;
88     gs.dirty = true;
89 }
90 
91 auto get_copy_or_move(GlobalState gs, string copy_or_move)
92 {
93     return (GlobalState gs)
94     {
95         int copy_or_move_to_the_same_directory = 0;
96         int copy_or_move_to_subdirectory = 0;
97         foreach (selection; gs.selection_hash.byKey())
98         {
99             string parent = 
100                 getParent(selection);
101             if (parent == "") parent = SL;
102             if (parent == gs.full_current_path)
103                 copy_or_move_to_the_same_directory++;
104 
105             if ( gs.full_current_path.startsWith(selection) )
106                 copy_or_move_to_subdirectory++;
107         }
108 
109         if (copy_or_move_to_the_same_directory &&
110                 gs.selection_hash.length > 1)
111         {
112             string msg;
113             if (copy_or_move == "copy")
114                 msg = format(_("Copy to the same directory works only for exactly 1 selection"));
115             else
116                 msg = format(_("Rename (move to the same directory) works only for exactly 1 selection"));
117             gs.messages ~= ConsoleMessage(
118                     SDL_Color(0xFF, 0xFF, 0xFF, 0xFF),
119                     msg,
120                     SDL_GetTicks() );
121             writeln(msg);
122         }
123         else if (copy_or_move_to_subdirectory)
124         {
125             string msg;
126             if (copy_or_move == "copy")
127                 msg = format(_("Can't copy to a subdirectory"));
128             else
129                 msg = format(_("Can't move to a subdirectory"));
130             gs.messages ~= ConsoleMessage(
131                     SDL_Color(0xFF, 0x00, 0x00, 0xFF),
132                     msg,
133                     SDL_GetTicks() );
134             writeln(msg);
135         }
136         else
137         {
138             if (copy_or_move_to_the_same_directory &&
139                     gs.selection_hash.length == 1)
140             {
141                 gs.shift_copy_or_move = gs.shift;
142                 string path = gs.selection_hash.keys[0];
143                 gs.animation_info[path] =
144                     AnimationInfo();
145                 gs.animation_info[path].parent =
146                     gs.full_current_path;
147                 if (copy_or_move == "copy")
148                     gs.animation_info[path].type =
149                         NameType.Copy;
150                 else
151                     gs.animation_info[path].type =
152                         NameType.Move;
153 
154                 gs.keybar.input_mode = true;
155                 setup_keybar(gs);
156                 gs.redraw_fast = true;
157                 change_current_dir(gs, 
158                         (ref RectSize rectsize)
159                         {
160                         if (rectsize.show_info != 
161                                 InfoType.CreateDirectory &&
162                                 rectsize.show_info != InfoType.Copy &&
163                                 rectsize.show_info != InfoType.Move)
164                         {
165                         rectsize.show_info = InfoType.Copy;
166                         string name = 
167                         path[path.lastIndexOf(SL)+1..$];
168                         if (copy_or_move == "copy")
169                         gs.enter_names[gs.current_path] =
170                         EnterName(NameType.Copy, name, 
171                                 cast(int)name.length);
172                         else
173                         gs.enter_names[gs.current_path] =
174                         EnterName(NameType.Move, name, 
175                                 cast(int)name.length);
176                         }
177                         } );
178             }
179             else
180             {
181                 // copy_or_move_paths algorithm wants SL at the end
182                 string path = gs.full_current_path ~ SL;
183                 if (copy_or_move == "copy")
184                     copy_paths(gs, gs.selection_hash.keys, path, gs.shift);
185                 else
186                     move_paths(gs, gs.selection_hash.keys, path, gs.shift);
187                 gs.dirty = true;
188             }
189         }
190     };
191 }
192 
193 void start_create_directory(GlobalState gs)
194 {
195     gs.keybar.input_mode = true;
196     setup_keybar(gs);
197     gs.redraw_fast = true;
198     change_current_dir(gs, 
199             (ref RectSize rectsize)
200             {
201             if (rectsize.show_info != InfoType.CreateDirectory &&
202                     rectsize.show_info != InfoType.Copy &&
203                     rectsize.show_info != InfoType.Move)
204             {
205             rectsize.show_info = InfoType.CreateDirectory;
206             gs.enter_names[gs.current_path] = 
207             EnterName(NameType.CreateDirectory, "", 0);
208             }
209             } );
210 }
211 
212 void remove_selection(GlobalState gs)
213 {
214     bool not_fit_on_screen = false;
215     foreach(path, ref rect; gs.selection_hash)
216     {
217         SDL_Rect sdl_rect = rect.to_screen(gs.screen);
218 
219         if (sdl_rect.x < 0 || sdl_rect.y < 0 ||
220                 (sdl_rect.x+sdl_rect.w) > gs.screen.w ||
221                 (sdl_rect.y+sdl_rect.h) > gs.screen.h)
222         {
223             not_fit_on_screen = true;
224             break;
225         }
226     }
227 
228     if (not_fit_on_screen)
229     {
230         string msg = format(_("Go (up) to directory which fully covers selection to confirm removing %d items"),
231                 gs.selection_hash.length);
232         gs.messages ~= ConsoleMessage(
233                 SDL_Color(0xFF, 0xFF, 0xFF, 0xFF),
234                 msg,
235                 SDL_GetTicks() );
236         writeln(msg);
237     }
238     else
239     {
240         remove_paths(gs, gs.selection_hash.keys);
241     }
242 }
243 
244 void change_sorting(GlobalState gs)
245 {
246     change_current_dir(gs, 
247             (ref RectSize rectsize)
248             {
249             rectsize.sort = cast(SortType)( (rectsize.sort+1)%(SortType.max+1) );
250             } );
251 }
252 
253 void filemanager_left(GlobalState gs)
254 {
255     with(gs.enter_names[gs.current_path])
256     {
257         if (pos > 0)
258             pos -= name.strideBack(pos);
259     }
260     gs.dirty = true;
261 }
262 
263 void filemanager_right(GlobalState gs)
264 {
265     with(gs.enter_names[gs.current_path])
266     {
267         if (pos < name.length)
268             pos += name.stride(pos);
269     }
270     gs.dirty = true;
271 }
272 
273 void filemanager_enter(GlobalState gs)
274 {
275     with(gs.enter_names[gs.current_path])
276     {
277         if (name != "")
278         {
279             final switch (type)
280             {
281                 case NameType.CreateDirectory:
282                     try{
283                         string path = gs.full_current_path ~ SL ~
284                             name;
285                         mkdir(path);
286                         gs.animation_info[path] =
287                             AnimationInfo();
288                         gs.animation_info[path].parent =
289                             gs.full_current_path;
290                         gs.animation_info[path].type =
291                             NameType.CreateDirectory;
292                         gs.dirty = true;
293                     }
294                     catch (FileException exp)
295                     {
296                         string msg = format("Failed Create Directory: %s", exp.msg);
297                         gs.messages ~= ConsoleMessage(
298                                 SDL_Color(0xFF, 0x00, 0x00, 0xFF),
299                                 msg,
300                                 SDL_GetTicks()
301                                 );
302                         writeln(msg);
303                     }
304                     break;
305 
306                 case NameType.Copy:
307                     goto case;
308                 case NameType.Move:
309                     string path = gs.full_current_path ~ SL ~
310                         name;
311                     string[] selection = gs.selection_hash.keys;
312                     try
313                     {
314                         auto de = DirEntry(selection[0]);
315                         if (!de.isSymlink() && de.isDir())
316                         {
317                             selection[0] ~= SL;//It is the single entry
318                         }
319                     }
320                     catch(Exception e)
321                     {
322                         // Ignore errors
323                     }
324 
325                     if (type == NameType.Copy)
326                     { 
327                         copy_paths(gs, selection.idup(), path, gs.shift_copy_or_move);
328                     }
329                     else
330                     {
331                         move_paths(gs, selection.idup(), path, gs.shift_copy_or_move);
332                     }
333                     gs.animation_info[path] =
334                         AnimationInfo();
335                     gs.animation_info[path].parent =
336                         gs.full_current_path;
337                     gs.animation_info[path].type =
338                         type;
339                     gs.animation_info[path].stage =
340                         1;
341                     gs.dirty = true;
342                     break;
343             }
344         }
345         filemanager_escape(gs);
346     }
347 }
348 
349 void filemanager_escape(GlobalState gs)
350 {
351     gs.keybar.input_mode = false;
352 
353     gs.redraw_fast = false;
354     change_current_dir(gs, 
355             (ref RectSize rectsize)
356             {
357             rectsize.show_info = InfoType.None;
358             gs.enter_names.remove(gs.current_path);
359             } );
360 
361     gs.last_escape = SDL_GetTicks();
362 }
363 
364 void filemanager_backscape(GlobalState gs)
365 {
366     RectSize rectsize = getRectSize(gs);
367     with(gs.enter_names[gs.current_path])
368     {
369         if ( (rectsize.show_info == InfoType.CreateDirectory ||
370                     rectsize.show_info == InfoType.Copy ||
371                     rectsize.show_info == InfoType.Move) &&
372                name > "" && pos > 0 )
373         {
374             int sb = name.strideBack(pos);
375             name = (name[0..pos-sb] ~ name[pos..$]).idup();
376             pos -= sb;
377             gs.dirty = true;
378         }
379     }
380 }
381 
382 auto get_mark(string mark)
383 {
384     return (GlobalState gs)
385     {
386         unde.marks.mark(gs, mark);
387         gs.mark = false;
388     };
389 }
390 
391 auto get_unmark(string mark)
392 {
393     return (GlobalState gs)
394     {
395         unde.marks.unmark(gs, mark);
396         gs.unmark = false;
397     };
398 }
399 
400 auto get_gomark(string mark)
401 {
402     return (GlobalState gs)
403     {
404         unde.marks.go_mark(gs, mark);
405         gs.gomark = false;
406     };
407 }
408 
409 void process_event(GlobalState gs, ref SDL_Event event)
410 {
411     switch( event.type )
412     {
413         case SDL_TEXTINPUT:
414             RectSize rectsize = getRectSize(gs);
415             if (rectsize.show_info == InfoType.CreateDirectory ||
416                     rectsize.show_info == InfoType.Copy ||
417                     rectsize.show_info == InfoType.Move)
418             {
419                 with(gs.enter_names[gs.current_path])
420                 {
421                     char[] input = fromStringz(cast(char*)event.text.text);
422                     name = 
423                         (name[0..pos] ~
424                         input ~
425                         name[pos..$]).idup();
426                     pos += input.length;
427                     gs.dirty = true;
428                 }
429             }
430             break;
431 
432         case SDL_MOUSEMOTION:
433             if (gs.mouse_buttons & unDE_MouseButtons.Left &&
434                     event.motion.x < gs.screen.w)
435             {
436                 gs.screen.x -= cast(double)(event.motion.xrel)*gs.screen.scale;
437                 gs.screen.y -= cast(double)(event.motion.yrel)*gs.screen.scale;
438             }
439 
440             gs.mousex = event.motion.x * gs.screen.scale + gs.screen.x;
441             gs.mousey = event.motion.y * gs.screen.scale + gs.screen.y;
442             gs.mouse_screen_x = event.motion.x;
443             gs.mouse_screen_y = event.motion.y;
444             gs.moved_while_click++;
445 
446             if (gs.mouse_buttons & unDE_MouseButtons.Right)
447             {
448                 process_click(gs.right_clickable_list, gs.mouse_screen_x, gs.mouse_screen_y, 1);
449             }
450             break;
451             
452         case SDL_MOUSEBUTTONDOWN:
453             switch (event.button.button)
454             {
455                 case SDL_BUTTON_LEFT:
456                     gs.mouse_buttons |= unDE_MouseButtons.Left;
457                     gs.moved_while_click = 0;
458                     break;
459                 case SDL_BUTTON_MIDDLE:
460                     gs.mouse_buttons |= unDE_MouseButtons.Middle;
461                     break;
462                 case SDL_BUTTON_RIGHT:
463                     gs.mouse_buttons |= unDE_MouseButtons.Right;
464                     process_click(gs.right_clickable_list, gs.mouse_screen_x, gs.mouse_screen_y, 0);
465                     break;
466                 default:
467                     break;
468             }
469             break;
470             
471         case SDL_MOUSEBUTTONUP:
472             switch (event.button.button)
473             {
474                 case SDL_BUTTON_LEFT:
475                     gs.mouse_buttons &= ~unDE_MouseButtons.Left;
476                     if (!gs.moved_while_click)
477                     {
478                         if (SDL_GetTicks() - gs.last_left_click < DOUBLE_DELAY)
479                         {
480                             process_click(gs.double_clickable_list, gs.mouse_screen_x, gs.mouse_screen_y);
481                         }
482                         else
483                         {
484                             process_click(gs.clickable_list, gs.mouse_screen_x, gs.mouse_screen_y);
485                         }
486                         gs.last_left_click = SDL_GetTicks();
487                     }
488                     break;
489                 case SDL_BUTTON_MIDDLE:
490                     gs.mouse_buttons &= ~unDE_MouseButtons.Middle;
491                     process_click(gs.middle_clickable_list, gs.mouse_screen_x, gs.mouse_screen_y, 2);
492                     break;
493                 case SDL_BUTTON_RIGHT:
494                     gs.mouse_buttons &= ~unDE_MouseButtons.Right;
495                     if (SDL_GetTicks() - gs.last_right_click < DOUBLE_DELAY)
496                     {
497                         process_click(gs.double_right_clickable_list, gs.mouse_screen_x, gs.mouse_screen_y);
498                     }
499                     process_click(gs.right_clickable_list, gs.mouse_screen_x, gs.mouse_screen_y, 2);
500                     gs.last_right_click = SDL_GetTicks();
501                     break;
502                 default:
503                     break;
504             }
505             break;
506 
507         case SDL_MOUSEWHEEL:
508             while (event.wheel.y > 0)
509             {
510                 gs.screen.scale /= 1.0905;
511                 event.wheel.y--;
512             }
513             while (event.wheel.y < 0)
514             {
515                 gs.screen.scale *= 1.0905;
516                 event.wheel.y++;
517             }
518             gs.screen.x = gs.mousex - gs.mouse_screen_x*gs.screen.scale;
519             gs.screen.y = gs.mousey - gs.mouse_screen_y*gs.screen.scale;
520             //writeln("scale=", gs.screen.scale);
521             break;
522             
523         case SDL_JOYAXISMOTION:
524             /* Do something with event.jaxis */
525             break;
526             
527         case SDL_JOYBALLMOTION:
528             /* Do something with event.jball */
529             break;
530             
531         case SDL_JOYHATMOTION:
532             /* Do something with event.jhat */
533             break;
534             
535         case SDL_JOYBUTTONDOWN:
536             /* Do something with event.jbutton */
537             break;
538             
539         case SDL_JOYBUTTONUP:
540             /* Do something event.jbutton */
541             break;
542 
543         case SDL_QUIT:
544             /* Do something event.quit */
545             gs.finish=true;
546             break;
547             
548         case SDL_SYSWMEVENT:
549             /* Do something with event.syswm */
550             break;
551             
552         case SDL_WINDOWEVENT:
553             /*if (event.window.event == SDL_WINDOWEVENT_RESIZED)
554                 diz_setvideomode(event.window.data1, event.window.data2, 
555                   dizvideomode.flags & SDL_WINDOW_FULLSCREEN_DESKTOP);*/
556             break;
557         default:
558             writeln("Ignored event: "~to!string(event.type));
559             break;
560     }
561 }
562 
563 bool setup_keybar_mark(GlobalState gs)
564 {
565     if (gs.mark || gs.unmark || gs.gomark)
566     {
567         gs.keybar.handlers.clear();
568         gs.keybar.handlers_down.clear();
569         gs.keybar.handlers_double.clear();
570 
571         gs.keybar.handlers[SDL_SCANCODE_ESCAPE] = KeyHandler(toDelegate(&cancel_mark), "Cancel", "Esc");
572         for (ssize_t i = 0; i < 3; i++)
573         {
574             for (ssize_t pos = 0; pos < (*gs.keybar.letters)[i].length; pos++)
575             {
576                 string mark = (*gs.keybar.letters)[i][pos];
577                 if ( mark.length == 1 &&
578                         (mark[0] >= '0' && 
579                          mark[0] <= '9' ||
580                          mark[0] >= 'A' && 
581                          mark[0] <= 'Z') )
582                 {
583                     if (gs.mark)
584                     {
585                         gs.keybar.handlers[(*gs.keybar.scans_cur)[i][pos]] = 
586                             KeyHandler(get_mark(mark), "Mark "~mark, mark);
587                     }
588                     else if (gs.unmark && check_mark(gs, mark))
589                     {
590                         gs.keybar.handlers[(*gs.keybar.scans_cur)[i][pos]] = 
591                             KeyHandler(get_unmark(mark), "Unmark "~mark, mark);
592                     }
593                     else if (gs.gomark && 
594                             (mark[0] >= '0' && mark[0] <= '9' || 
595                              check_mark(gs, mark)))
596                     {
597                         gs.keybar.handlers[(*gs.keybar.scans_cur)[i][pos]] = 
598                             KeyHandler(get_gomark(mark), "Go to Mark "~mark, mark);
599                     }
600                 }
601             }
602         }
603 
604         if (!gs.shift)
605         {
606             gs.keybar.handlers_down[SDL_SCANCODE_LSHIFT] = KeyHandler(toDelegate(&nothing), "Global Mark", "Shift");
607             gs.keybar.handlers_down[SDL_SCANCODE_RSHIFT] = KeyHandler(toDelegate(&nothing), "", "");
608         }
609         return true;
610     }
611     else return false;
612 }
613 
614 void setup_keybar(GlobalState gs)
615 {
616     foreach (uipage; gs.uipages)
617     {
618         if (uipage.show)
619         {
620             uipage.set_keybar(gs);
621             return;
622         }
623     }
624 
625     final switch (gs.state)
626     {
627         case State.FileManager:
628             if (setup_keybar_mark(gs))
629             {
630             }
631             else if (gs.command_line.enter)
632             {
633                 if (gs.ctrl)
634                 {
635                     setup_keybar_command_line_ctrl(gs);
636                 }
637                 else
638                 {
639                     setup_keybar_command_line_default(gs);
640                 }
641             }
642             else if (gs.command_line.terminal)
643             {
644                 if (gs.command_line.command_in_focus_id > 0)
645                 {
646                     setup_keybar_terminal_command_focus_in(gs);
647                 }
648                 else
649                 {
650                     if (gs.ctrl)
651                     {
652                         setup_keybar_terminal_ctrl(gs);
653                     }
654                     else
655                     {
656                         setup_keybar_terminal(gs);
657                     }
658                 }
659             }
660             else if (gs.current_path in gs.enter_names)
661             {
662                 gs.keybar.input_mode = true;
663                 gs.keybar.handlers.clear();
664                 gs.keybar.handlers_down.clear();
665                 gs.keybar.handlers_double.clear();
666                 gs.keybar.handlers_down[SDL_SCANCODE_LEFT] = KeyHandler(toDelegate(&filemanager_left), "Left", "←");
667                 gs.keybar.handlers_down[SDL_SCANCODE_RIGHT] = KeyHandler(toDelegate(&filemanager_right), "Right", "→");
668                 gs.keybar.handlers[SDL_SCANCODE_RETURN] = KeyHandler(toDelegate(&filemanager_enter), "Finish Enter", "Enter");
669                 gs.keybar.handlers[SDL_SCANCODE_ESCAPE] = KeyHandler(toDelegate(&filemanager_escape), "Cancel", "Esc");
670                 gs.keybar.handlers_down[SDL_SCANCODE_BACKSPACE] = KeyHandler(toDelegate(&filemanager_backscape), "Backspace", "<--");
671 
672             }
673             else
674             {
675                 gs.keybar.input_mode = false;
676                 if (gs.ctrl)
677                     setup_keybar_filemanager_ctrl(gs);
678                 else if (gs.shift)
679                     setup_keybar_filemanager_shift(gs);
680                 else
681                     setup_keybar_filemanager_default(gs);
682             }
683             break;
684 
685         case State.ImageViewer:
686             if (setup_keybar_mark(gs))
687             {
688             }
689             else if (gs.shift)
690                 setup_keybar_imageviewer_shift(gs);
691             else
692                 setup_keybar_imageviewer_default(gs);
693             break;
694 
695         case State.TextViewer:
696             if (setup_keybar_mark(gs))
697             {
698             }
699             else if (gs.shift)
700                 setup_keybar_textviewer_shift(gs);
701             else
702                 setup_keybar_textviewer_default(gs);
703             break;
704     }
705 }
706 
707 void
708 setup_keybar_filemanager_default(GlobalState gs)
709 {
710     gs.keybar.handlers.clear();
711     gs.keybar.handlers_down.clear();
712     gs.keybar.handlers_double.clear();
713 
714     gs.keybar.handlers[SDL_SCANCODE_Q] = KeyHandler(toDelegate(&quit), _("Quit"), "exit.png");
715     gs.keybar.handlers[SDL_SCANCODE_PRINTSCREEN] = KeyHandler(toDelegate(&make_screenshot), _("Make screenshot"), "Prt Sc");
716     gs.keybar.handlers[SDL_SCANCODE_M] = KeyHandler(toDelegate(&mark), _("Make Mark"), "mark.png");
717     gs.keybar.handlers[SDL_SCANCODE_APOSTROPHE] = KeyHandler(toDelegate(&gomark), _("Go To Mark"), "gomark.png");
718     gs.keybar.handlers[SDL_SCANCODE_R] = KeyHandler(toDelegate(&rescan), _("Rescan directory"), "rescan.png");
719     gs.keybar.handlers[SDL_SCANCODE_A] = KeyHandler(toDelegate(&deselect_all), _("Clear selection"), "deselect.png");
720     gs.keybar.handlers_down[SDL_SCANCODE_LSHIFT] = KeyHandler(toDelegate(&setup_keybar_filemanager_shift), "", "Shift");
721     gs.keybar.handlers_down[SDL_SCANCODE_RSHIFT] = KeyHandler(toDelegate(&setup_keybar_filemanager_shift), "", "");
722     gs.keybar.handlers_down[SDL_SCANCODE_LCTRL] = KeyHandler(toDelegate(&setup_keybar_filemanager_ctrl), "", "Ctrl");
723     gs.keybar.handlers_down[SDL_SCANCODE_RCTRL] = KeyHandler(toDelegate(&setup_keybar_filemanager_ctrl), "", "");
724     gs.keybar.handlers_double[SDL_SCANCODE_ESCAPE] = KeyHandler(toDelegate(&clear_messages), _("Clear error messages in directories"), "clear_errors.png");
725     gs.keybar.handlers[SDL_SCANCODE_C] = KeyHandler(get_copy_or_move(gs, "copy"), _("Copy selection to current directory"), "copy.png");
726     gs.keybar.handlers[SDL_SCANCODE_V] = KeyHandler(get_copy_or_move(gs, "move"), _("Move selection to current directory"), "move.png");
727     gs.keybar.handlers[SDL_SCANCODE_D] = KeyHandler(toDelegate(&start_create_directory), _("Create Directory"), "create_directory.png");
728     gs.keybar.handlers[SDL_SCANCODE_E] = KeyHandler(toDelegate(&remove_selection), _("Remove Selection"), "remove.png");
729     gs.keybar.handlers[SDL_SCANCODE_S] = KeyHandler(toDelegate(&change_sorting), _("Change Sort Order"), "sort.png");
730     gs.keybar.handlers_double[SDL_SCANCODE_RETURN] = KeyHandler(toDelegate(&turn_on_terminal), _("Open Terminal"), "terminal.png");
731 }
732 
733 void
734 setup_keybar_filemanager_ctrl(GlobalState gs)
735 {
736     gs.keybar.handlers.clear();
737     gs.keybar.handlers_down.clear();
738     gs.keybar.handlers_double.clear();
739 
740     gs.keybar.handlers[SDL_SCANCODE_Q] = KeyHandler(toDelegate(&restart), _("Restart"), "exit.png");
741     gs.keybar.handlers[SDL_SCANCODE_SEMICOLON] = KeyHandler(toDelegate(&turn_on_command_line), _("Command line"), "command_line.png");
742     gs.keybar.handlers[SDL_SCANCODE_L] = KeyHandler(toDelegate(&turn_on_keybar_settings), _("Keyboard layouts settings"), "keybar");
743     gs.keybar.handlers[SDL_SCANCODE_LCTRL] = KeyHandler(toDelegate(&setup_keybar_filemanager_default), "", "Ctrl");
744     gs.keybar.handlers[SDL_SCANCODE_RCTRL] = KeyHandler(toDelegate(&setup_keybar_filemanager_default), "", "");
745 }
746 
747 void
748 setup_keybar_filemanager_shift(GlobalState gs)
749 {
750     gs.keybar.handlers.clear();
751     gs.keybar.handlers_down.clear();
752     gs.keybar.handlers_double.clear();
753 
754     gs.keybar.handlers[SDL_SCANCODE_M] = KeyHandler(toDelegate(&unmark), _("Delete Mark"), "unmark.png");
755     gs.keybar.handlers[SDL_SCANCODE_C] = KeyHandler(get_copy_or_move(gs, "copy"), _("Copy selection to current directory with deleting not exists files in the source directory"), "copy.png");
756     gs.keybar.handlers[SDL_SCANCODE_V] = KeyHandler(get_copy_or_move(gs, "move"), _("Move selection to current directory with deleting not exists files in the source directory"), "move.png");
757     gs.keybar.handlers[SDL_SCANCODE_LSHIFT] = KeyHandler(toDelegate(&setup_keybar_filemanager_default), "", "Shift");
758     gs.keybar.handlers[SDL_SCANCODE_RSHIFT] = KeyHandler(toDelegate(&setup_keybar_filemanager_default), "", "");
759 }