Launcher, based on PickleLauncher
[mupen64plus-pandora.git] / source / mupen64launcher / src / cselector.cpp
1 /**
2  *  @section LICENSE
3  *
4  *  PickleLauncher
5  *  Copyright (C) 2010-2011 Scott Smith
6  *
7  *  This program is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  *  @section LOCATION
21  */
22
23 #include "cselector.h"
24
25 CSelector::CSelector() : CBase(),
26         Redraw              (true),
27         SkipFrame           (false),
28         Rescan              (true),
29         RefreshList         (true),
30         SetOneEntryValue    (false),
31         SetAllEntryValue    (false),
32         TextScrollDir       (true),
33         ExtractAllFiles     (false),
34         DrawState_Title     (true),
35         DrawState_About     (true),
36         DrawState_Filter    (true),
37         DrawState_FilePath  (true),
38         DrawState_Index     (true),
39         DrawState_ZipMode   (true),
40         DrawState_Preview   (true),
41         DrawState_ButtonL   (true),
42         DrawState_ButtonR   (true),
43         Mode                (MODE_SELECT_ENTRY),
44         LastSelectedEntry   (0),
45         TextScrollOffset    (0),
46         CurScrollSpeed      (0),
47         CurScrollPause      (0),
48         ListNameHeight      (0),
49         FramesDrawn         (0),
50         FramesSkipped       (0),
51         FramesSleep         (0),
52 #if defined(DEBUG)
53         FPSDrawn            (0),
54         FPSSkip             (0),
55         FPSSleep            (0),
56         FrameCountTime      (0),
57         LoopTimeAverage     (0),
58 #endif
59         FrameEndTime        (0),
60         FrameStartTime      (0),
61         FrameDelay          (0),
62         Mouse               (),
63         Joystick            (NULL),
64         Screen              (NULL),
65         ImageBackground     (NULL),
66         ImagePointer        (NULL),
67         ImageSelectPointer  (NULL),
68         ImagePreview        (NULL),
69         ImageTitle          (NULL),
70         ImageAbout          (NULL),
71         ImageFilePath       (NULL),
72         ImageFilter         (NULL),
73         ImageIndex          (NULL),
74         ImageZipMode        (NULL),
75 #if defined(DEBUG)
76         ImageDebug          (NULL),
77 #endif
78         ImageButtons        (),
79         Fonts               (),
80         Config              (),
81         Profile             (),
82         System              (),
83         ConfigPath          (DEF_CONFIG),
84         ProfilePath         (DEF_PROFILE),
85         ZipListPath         (DEF_ZIPLIST),
86         EventReleased       (),
87         EventPressCount     (),
88         ButtonModesLeft     (),
89         ButtonModesRight    (),
90         DisplayList         (),
91         LabelButtons        (),
92         ListNames           (),
93         ItemsEntry          (),
94         ItemsArgument       (),
95         ItemsValue          (),
96         ItemsDefPlugin      (),
97         WhichPlugin         (0),
98         ItemsRomOption      (),
99         ItemsRomPlugin      (),
100         WhichRomPlugin      (0),
101         RectEntries         (),
102         RectButtonsLeft     (),
103         RectButtonsRight    (),
104         ScreenRectsDirty    ()
105 {
106     Fonts.resize( FONT_SIZE_TOTAL, NULL );
107
108     ButtonModesLeft.resize( BUTTONS_MAX_LEFT );
109     ButtonModesRight.resize( BUTTONS_MAX_RIGHT );
110     RectButtonsLeft.resize( BUTTONS_MAX_LEFT );
111     RectButtonsRight.resize( BUTTONS_MAX_RIGHT );
112     ImageButtons.resize( EVENT_TOTAL, NULL );
113     LabelButtons.resize( EVENT_TOTAL, "" );
114
115     LabelButtons.at(EVENT_ONE_UP)       = BUTTON_LABEL_ONE_UP;
116     LabelButtons.at(EVENT_ONE_DOWN)     = BUTTON_LABEL_ONE_DOWN;
117     LabelButtons.at(EVENT_PAGE_UP)      = BUTTON_LABEL_PAGE_UP;
118     LabelButtons.at(EVENT_PAGE_DOWN)    = BUTTON_LABEL_PAGE_DOWN;
119     LabelButtons.at(EVENT_DIR_UP)       = BUTTON_LABEL_DIR_UP;
120     LabelButtons.at(EVENT_DIR_DOWN)     = BUTTON_LABEL_DIR_DOWN;
121     LabelButtons.at(EVENT_ZIP_MODE)     = BUTTON_LABEL_ZIP_MODE;
122     LabelButtons.at(EVENT_CFG_APP)      = BUTTON_LABEL_CONFIG;
123     LabelButtons.at(EVENT_CFG_ITEM)     = BUTTON_LABEL_EDIT;
124     LabelButtons.at(EVENT_SET_ONE)      = BUTTON_LABEL_SET_ONE;
125     LabelButtons.at(EVENT_SET_ALL)      = BUTTON_LABEL_SET_ALL;
126     LabelButtons.at(EVENT_BACK)         = BUTTON_LABEL_BACK;
127     LabelButtons.at(EVENT_SELECT)       = BUTTON_LABEL_SELECT;
128     LabelButtons.at(EVENT_QUIT)         = BUTTON_LABEL_QUIT;
129
130     DisplayList.resize( MODE_TOTAL );
131
132     EventPressCount.resize( EVENT_TOTAL, EVENT_LOOPS_OFF );
133     EventReleased.resize( EVENT_TOTAL, false );
134 }
135
136 CSelector::~CSelector()
137 {
138 }
139
140 int8_t CSelector::Run( int32_t argc, char** argv )
141 {
142     int8_t result;
143     int16_t selection;
144
145     result = 0;
146
147     ProcessArguments( argc, argv );
148
149     System.SetCPUClock( Config.CPUClock );
150
151     // Load video,input,profile resources
152     if (OpenResources())
153     {
154         result = 1;
155     }
156
157     // Display and poll the user for a selection
158     if (result == 0)
159     {
160         selection = DisplayScreen();
161
162         // Setup a exec script for execution following termination of this application
163         if (selection >= 0)
164         {
165             if (RunExec( selection ))
166             {
167                 result = 1;
168             }
169         }
170         else if (selection < -1)
171         {
172             result = 1;
173         }
174         else
175         {
176             result = 0;
177         }
178     }
179
180     // Release resources
181     CloseResources( result );
182
183     return result;
184 }
185
186 void CSelector::ProcessArguments( int argc, char** argv )
187 {
188     uint8_t arg_index;
189     string launcher;
190     string argument;
191
192     launcher = string(argv[0]);
193     Profile.LauncherName = launcher.substr( launcher.find_last_of('/')+1 );
194     Profile.LauncherPath = launcher.substr( 0, launcher.find_last_of('/')+1 );
195     if (Profile.LauncherPath.compare("./") == 0 || Profile.LauncherPath.length() == 0)
196     {
197         Profile.LauncherPath = string(getenv("PWD"))+"/";
198     }
199
200 #if defined(DEBUG)
201     Log( "Running from '%s'\n", launcher.c_str() );
202 #endif
203     Log( "Running from '%s' as '%s'\n", Profile.LauncherPath.c_str(), Profile.LauncherName.c_str() );
204
205     for (arg_index=0; arg_index<argc; arg_index++ )
206     {
207         argument = string(argv[arg_index]);
208
209         if (argument.compare( ARG_RESETGUI ) == 0)
210         {
211             Config.ResetGUI = true;
212         }
213         else
214         if (argument.compare( ARG_PROFILE ) == 0)
215         {
216             ProfilePath = string(argv[++arg_index]);
217         }
218         else
219         if (argument.compare( ARG_CONFIG ) == 0)
220         {
221             ConfigPath = string(argv[++arg_index]);
222         }
223         else
224         if (argument.compare( ARG_ZIPLIST ) == 0)
225         {
226             ZipListPath = string(argv[++arg_index]);
227         }
228     }
229 }
230
231 int8_t CSelector::OpenResources( void )
232 {
233     uint8_t button_index;
234     uint32_t flags;
235     string text;
236
237     Log( "Loading config.\n" );
238     if (Config.Load( ConfigPath ))
239     {
240         Log( "Failed to load config\n" );
241         return 1;
242     }
243
244     Log( "Loading ziplist.\n" );
245     if (Config.UseZipSupport == true && Profile.Minizip.LoadUnzipList( ZipListPath ))
246     {
247         Log( "Failed to load ziplist\n" );
248         return 1;
249     }
250
251     // Initialize defaults, Video and Audio subsystems
252     Log( "Initializing SDL.\n" );
253     if (SDL_Init( SDL_INIT_VIDEO|SDL_INIT_AUDIO|SDL_INIT_TIMER|SDL_INIT_JOYSTICK )==-1)
254     {
255         Log( "Failed to initialize SDL: %s.\n", SDL_GetError() );
256         return 1;
257     }
258     Log( "SDL initialized.\n" );
259
260     // Setup SDL Screen
261     flags = SCREEN_FLAGS;
262     if (Config.Fullscreen == true)
263     {
264         flags |= SDL_FULLSCREEN;
265     }
266     Screen = SDL_SetVideoMode( Config.ScreenWidth, Config.ScreenHeight, Config.ScreenDepth, flags );
267     if (Screen == NULL)
268     {
269         Log( "Failed to %dx%dx%d video mode: %s\n", Config.ScreenWidth, Config.ScreenHeight, Config.ScreenDepth, SDL_GetError() );
270         return 1;
271     }
272
273     // Refresh entire screen for the first frame
274     UpdateRect( 0, 0, Config.ScreenWidth, Config.ScreenHeight );
275
276     // Load joystick
277 #if !defined(PANDORA) && !defined(X86)
278     Joystick = SDL_JoystickOpen(0);
279     if (Joystick == NULL)
280     {
281         Log( "Warning failed to open first joystick: %s\n", SDL_GetError() );
282     }
283 #endif
284
285     // Setup TTF SDL
286     if (TTF_Init() == -1)
287     {
288         Log( "Failed to init TTF_Init: %s\n", TTF_GetError() );
289         return 1;
290     }
291
292     // Load ttf font
293     Fonts.at(FONT_SIZE_SMALL) = TTF_OpenFont( Config.PathFont.c_str(), Config.FontSizes.at(FONT_SIZE_SMALL) );
294     if (!Fonts.at(FONT_SIZE_SMALL))
295     {
296         Log( "Failed to open small TTF_OpenFont: %s\n", TTF_GetError() );
297         return 1;
298     }
299     Fonts.at(FONT_SIZE_MEDIUM) = TTF_OpenFont( Config.PathFont.c_str(), Config.FontSizes.at(FONT_SIZE_MEDIUM) );
300     if (!Fonts.at(FONT_SIZE_MEDIUM))
301     {
302         Log( "Failed to open medium TTF_OpenFont: %s\n", TTF_GetError() );
303         return 1;
304     }
305     Fonts.at(FONT_SIZE_LARGE) = TTF_OpenFont( Config.PathFont.c_str(), Config.FontSizes.at(FONT_SIZE_LARGE) );
306     if (!Fonts.at(FONT_SIZE_LARGE))
307     {
308         Log( "Failed to open large TTF_OpenFont: %s\n", TTF_GetError() );
309         return 1;
310     }
311
312     Log( "Loading profile.\n" );
313     if (Profile.Load( ProfilePath, Config.Delimiter ))
314     {
315         Log( "Failed to load profile\n" );
316         return 1;
317     }
318
319     // Load images
320     ImageBackground = LoadImage( Config.PathBackground );
321     for (button_index=0; button_index<Config.PathButtons.size(); button_index++)
322     {
323         ImageButtons.at(button_index) = LoadImage( Config.PathButtons.at(button_index) );
324     }
325
326     //      Mouse pointer
327     if (Config.ShowPointer==true)
328     {
329         ImagePointer = LoadImage( Config.PathPointer );
330         if (ImagePointer == NULL)
331         {
332             SDL_ShowCursor( SDL_ENABLE );
333         }
334         else
335         {
336             SDL_ShowCursor( SDL_DISABLE );
337         }
338     }
339     else
340     {
341         SDL_ShowCursor( SDL_DISABLE );
342     }
343
344     //      List selector pointer
345     ImageSelectPointer = LoadImage( Config.PathSelectPointer );
346     if (ImageSelectPointer == NULL)
347     {
348         ImageSelectPointer = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_MEDIUM), ENTRY_ARROW, Config.Colors.at(COLOR_BLACK) );
349     }
350
351     //      Title text
352     text = string(APPNAME) + " " + string(APPVERSION);
353     if (Profile.TargetApp.length() > 0)
354     {
355         text += " for " + Profile.TargetApp;
356     }
357     ImageTitle = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_LARGE), text.c_str(), Config.Colors.at(Config.ColorFontFiles) );
358     if (ImageTitle == NULL)
359     {
360         Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
361         return 1;
362     }
363
364     //      About text
365     text = "Written by " + string(APPAUTHOR) + " " + string(APPCOPYRIGHT);
366     ImageAbout = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_SMALL), text.c_str(), Config.Colors.at(Config.ColorFontFiles) );
367     if (ImageAbout == NULL)
368     {
369         Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
370         return 1;
371     }
372
373
374     return 0;
375 }
376
377 void CSelector::CloseResources( int8_t result )
378 {
379     uint8_t button_index;
380
381     if (result == 0)
382     {
383         Config.Save( ConfigPath );
384         Profile.Save( ProfilePath, Config.Delimiter );
385     }
386
387     if (Config.UseZipSupport == true)
388     {
389         Profile.Minizip.SaveUnzipList( ZipListPath );
390     }
391
392     // Close joystick
393     if (Joystick != NULL)
394     {
395         Log( "Closing SDL Joystick.\n" );
396         SDL_JoystickClose( Joystick );
397         Joystick = NULL;
398     }
399
400     // Close fonts
401     Log( "Closing TTF fonts.\n" );
402     if (Fonts.at(FONT_SIZE_SMALL) != NULL)
403     {
404         TTF_CloseFont( Fonts.at(FONT_SIZE_SMALL) );
405         Fonts.at(FONT_SIZE_SMALL) = NULL;
406     }
407     if (Fonts.at(FONT_SIZE_MEDIUM) != NULL)
408     {
409         TTF_CloseFont( Fonts.at(FONT_SIZE_MEDIUM) );
410         Fonts.at(FONT_SIZE_MEDIUM) = NULL;
411     }
412     if (Fonts.at(FONT_SIZE_LARGE) != NULL)
413     {
414         TTF_CloseFont( Fonts.at(FONT_SIZE_LARGE) );
415         Fonts.at(FONT_SIZE_LARGE) = NULL;
416     }
417
418     // Free images
419     FREE_IMAGE( ImageBackground );
420     FREE_IMAGE( ImagePointer );
421     FREE_IMAGE( ImageSelectPointer );
422     FREE_IMAGE( ImagePreview );
423     FREE_IMAGE( ImageTitle );
424     FREE_IMAGE( ImageAbout );
425     FREE_IMAGE( ImageFilePath );
426     FREE_IMAGE( ImageFilter );
427     FREE_IMAGE( ImageIndex );
428     FREE_IMAGE( ImageZipMode );
429 #if defined(DEBUG)
430     FREE_IMAGE( ImageDebug );
431 #endif
432     for (button_index=0; button_index<ImageButtons.size(); button_index++)
433     {
434         FREE_IMAGE( ImageButtons.at(button_index) );
435     }
436
437     Log( "Quitting TTF.\n" );
438     TTF_Quit();
439
440     Log( "Quitting SDL.\n" );
441     SDL_Quit();
442
443     // Flush all std buffers before exit
444     fflush( stdout );
445     fflush( stderr );
446 }
447
448 int16_t CSelector::DisplayScreen( void )
449 {
450     while (IsEventOff(EVENT_QUIT) == true && (IsEventOff(EVENT_SELECT) == true || Mode != MODE_SELECT_ENTRY) )
451     {
452         // Get user input
453         if (PollInputs())
454         {
455             return -2;
456         }
457
458         // Select the mode
459         SelectMode();
460
461         // Configure the buttons according to the mode
462         if (ConfigureButtons())
463         {
464             return -2;
465         }
466
467         // Draw the selector
468         if (DisplaySelector())
469         {
470             return -2;
471         }
472
473         // Update the screen
474         UpdateScreen();
475     }
476
477     if (IsEventOn( EVENT_QUIT ) == true)
478     {
479         // Detete any files exracted from zip
480         Profile.Minizip.DelUnzipFiles();
481
482         return -1;
483     }
484     else
485     {
486         return DisplayList.at(MODE_SELECT_ENTRY).absolute;
487     }
488 }
489
490 void CSelector::UpdateRect( int16_t x, int16_t y, int16_t w, int16_t h )
491 {
492     SDL_Rect rect;
493
494     if (Config.ScreenFlip == false)
495     {
496         // Safety Checks
497         if( x < 0 )
498         {
499             x = 0;
500             Log( "ERROR: UpdateRect X was out of bounds\n" );
501         }
502
503         if( y < 0 )
504         {
505             y = 0;
506             Log( "ERROR: UpdateRect Y was out of bounds\n" );
507         }
508
509         if( h < 0 )
510         {
511             h = 0;
512             Log( "ERROR: UpdateRect X was out of bounds\n" );
513         }
514
515         if( w < 0 )
516         {
517             w = 0;
518             Log( "ERROR: UpdateRect Y was out of bounds\n" );
519         }
520
521         if( x > Config.ScreenWidth )
522         {
523             x = Config.ScreenWidth-1;
524             Log( "ERROR: UpdateRect X was out of bounds\n" );
525         }
526
527         if( y > Config.ScreenHeight )
528         {
529             y = Config.ScreenHeight-1;
530             Log( "ERROR: UpdateRect Y was out of bounds\n" );
531         }
532
533         if( x + w > Config.ScreenWidth )
534         {
535             w = Config.ScreenWidth-x;
536             Log( "ERROR: UpdateRect W was out of bounds\n" );
537         }
538
539         if( y + h > Config.ScreenHeight )
540         {
541             h = Config.ScreenHeight-y;
542             Log( "ERROR: UpdateRect H was out of bounds\n" );
543         }
544
545         rect.x = x;
546         rect.y = y;
547         rect.w = w;
548         rect.h = h;
549
550         ScreenRectsDirty.push_back( rect );
551     }
552 }
553
554 void CSelector::UpdateScreen( void )
555 {
556 #if defined(DEBUG_FORCE_REDRAW)
557     Redraw = true;
558 #endif
559
560     if (SkipFrame == false && Redraw == true)
561     {
562         if (Config.ScreenFlip == true)
563         {
564             if (SDL_Flip( Screen ) != 0)
565             {
566                 Log( "Failed to swap the buffers: %s\n", SDL_GetError() );
567             }
568         }
569         else
570         {
571             SDL_UpdateRects( Screen, ScreenRectsDirty.size(), &ScreenRectsDirty[0] );
572         }
573
574         Redraw = false;
575         FramesDrawn++;
576     }
577     else
578     {
579         if (SkipFrame == true)
580         {
581             FramesSkipped++;
582         }
583         else
584         {
585             FramesSleep++;
586         }
587     }
588     ScreenRectsDirty.clear();
589
590     FrameEndTime = SDL_GetTicks();
591     FrameDelay   = (MS_PER_SEC/FRAMES_PER_SEC) - (FrameEndTime - FrameStartTime);
592
593 #if defined(DEBUG_FPS)
594     LoopTimeAverage = (LoopTimeAverage + (FrameEndTime - FrameStartTime))/2;
595 #endif
596
597     if (FrameDelay < 0)
598     {
599         if (FramesSkipped/FramesDrawn < FRAME_SKIP_RATIO)
600         {
601             SkipFrame = true;
602         }
603         else // Force a frame to be drawn
604         {
605             SkipFrame = false;
606         }
607     }
608     else
609     {
610         SkipFrame = false;
611         SDL_Delay( MIN(FrameDelay, MS_PER_SEC) );
612     }
613     FrameStartTime = SDL_GetTicks();
614
615 #if defined(DEBUG_FPS)
616     if (FrameStartTime - FrameCountTime >= MS_PER_SEC)
617     {
618         FrameCountTime  = FrameStartTime;
619         FPSDrawn        = FramesDrawn;
620         FPSSkip         = FramesSkipped;
621         FPSSleep        = FramesSleep;
622         FramesDrawn     = 1;
623         FramesSkipped   = 0;
624         FramesSleep     = 0;
625
626         cout << "DEBUG total " << i_to_a(FPSDrawn+FPSSkip+FPSSleep)
627              << " fps " << i_to_a(FPSDrawn) << " skip " << i_to_a(FPSSkip) << " slp " << i_to_a(FPSSleep)
628              << " loop " << i_to_a(LoopTimeAverage) << endl;
629     }
630 #endif
631 }
632
633 void CSelector::SelectMode( void )
634 {
635     uint8_t old_mode;
636
637     old_mode = Mode;
638
639     switch (Mode)
640     {
641         case MODE_SELECT_ENTRY:
642             if (IsEventOn( EVENT_CFG_ITEM ) == true)
643             {
644                 if (ItemsEntry.size()>0)
645                 {
646                     if (ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Type == TYPE_FILE )
647 //                    || (ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Type == TYPE_DIR && Profile.LaunchableDirs == true))
648                     {
649 //                        Profile.ScanRomPlugins(Profile.FilePath+ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Name, ItemsRomOption);
650                         // Per Rom Plugin selection. Find ROMCRC and Load Plugins for this rom
651                         Mode = MODE_ROM_OPTION;
652                     }
653                 }
654             }
655             else if (IsEventOn( EVENT_CFG_APP ) == true)
656             {
657                 Mode = MODE_SELECT_OPTION;
658             }
659             break;
660         case MODE_SELECT_ARGUMENT:
661             if (IsEventOn( EVENT_BACK ) == true)
662             {
663                 Mode = MODE_SELECT_ENTRY;
664             }
665             if (IsEventOn( EVENT_SELECT ) == true)
666             {
667                 Mode = MODE_SELECT_VALUE;
668             }
669             break;
670         case MODE_SELECT_VALUE:
671             if (IsEventOn( EVENT_BACK ) == true)
672             {
673                 Mode = MODE_SELECT_ARGUMENT;
674             }
675             break;
676         case MODE_SELECT_OPTION:
677             if (IsEventOn( EVENT_BACK ) == true)
678             {
679                 Mode = MODE_SELECT_ENTRY;
680             }
681             if (IsEventOn( EVENT_SELECT ) == true)
682             {
683                 WhichPlugin = DisplayList.at(MODE_SELECT_OPTION).absolute;
684                 Mode = MODE_SELECT_PLUGIN;
685             }
686             break;
687         case MODE_ROM_OPTION:
688             if (IsEventOn( EVENT_BACK ) == true)
689             {
690                 Mode = MODE_SELECT_ENTRY;
691             }
692             if (IsEventOn( EVENT_SELECT ) == true)
693             {
694                 WhichPlugin = DisplayList.at(MODE_ROM_OPTION).absolute;
695                 Mode = MODE_ROM_PLUGIN;
696             }
697             break;
698         case MODE_SELECT_PLUGIN:
699             if (IsEventOn( EVENT_BACK ) == true)
700             {
701                 Mode = MODE_SELECT_OPTION;
702             }
703             if (IsEventOn( EVENT_SELECT ) == true)
704             {
705                 Profile.SaveDef1Plugin(WhichPlugin, DisplayList.at(MODE_SELECT_PLUGIN).absolute);
706                 Mode = MODE_SELECT_OPTION;
707             }
708             break;
709        case MODE_ROM_PLUGIN:
710             if (IsEventOn( EVENT_BACK ) == true)
711             {
712                 Mode = MODE_ROM_OPTION;
713             }
714             if (IsEventOn( EVENT_SELECT ) == true)
715             {
716                 Profile.SaveRom1Plugin(WhichPlugin, ItemsRomPlugin[DisplayList.at(MODE_ROM_PLUGIN).absolute].Entry);
717                 Mode = MODE_ROM_OPTION;
718             }
719             break;
720         default:
721             Mode = MODE_SELECT_ENTRY;
722             Log( "Error: Unknown Mode\n" );
723             break;
724     }
725
726     if (Mode != old_mode)
727     {
728         DrawState_ButtonL  = true;
729         DrawState_ButtonR  = true;
730         Rescan = true;
731     }
732 }
733
734 int8_t CSelector::DisplaySelector( void )
735 {
736     SDL_Rect rect_pos = { Config.EntryXOffset, Config.EntryYOffset, 0 ,0 };
737
738     if (Rescan)
739     {
740         RescanItems();
741         RefreshList = true;
742         Rescan      = false;
743     }
744
745     if (RefreshList)
746     {
747         PopulateList();
748         DrawState_Index = true;
749         Redraw          = true;
750         RefreshList     = false;
751     }
752
753     if (Redraw == true || CurScrollPause != 0 || CurScrollSpeed != 0 || TextScrollOffset != 0)
754     {
755         if (Config.ScreenFlip == true)
756         {
757             DrawState_Title     = true;
758             DrawState_About     = true;
759             DrawState_Filter    = true;
760             DrawState_FilePath  = true;
761             DrawState_Index     = true;
762             DrawState_ZipMode   = true;
763             DrawState_Preview   = true;
764             DrawState_ButtonL   = true;
765             DrawState_ButtonR   = true;
766         }
767 #if defined(DEBUG_DRAW_STATES)
768         else
769         {
770             cout << "DEBUG "
771                  << " " << i_to_a(DrawState_Title)
772                  << " " << i_to_a(DrawState_About)
773                  << " " << i_to_a(DrawState_Filter)
774                  << " " << i_to_a(DrawState_FilePath)
775                  << " " << i_to_a(DrawState_Index)
776                  << " " << i_to_a(DrawState_ZipMode)
777                  << " " << i_to_a(DrawState_Preview)
778                  << " " << i_to_a(DrawState_ButtonL)
779                  << " " << i_to_a(DrawState_ButtonR) << endl;
780         }
781 #endif
782
783         // Draw background or clear screen
784         DrawBackground();
785
786         // Draw text titles to the screen
787         if (DrawText( rect_pos ))
788         {
789             return 1;
790         }
791
792         // Draw the buttons for touchscreen
793         if (DrawButtons( rect_pos ))
794         {
795             return 1;
796         }
797
798         // Draw the names for the items for display
799         if (DrawNames( rect_pos ))
800         {
801             return 1;
802         }
803
804         // Custom mouse pointer
805         if (Config.ShowPointer == true && ImagePointer != NULL)
806         {
807             ApplyImage( Mouse.x, Mouse.y, ImagePointer, Screen, NULL );
808         }
809     }
810
811     return 0;
812 }
813
814 void CSelector::DirectoryUp( void )
815 {
816     if (Profile.FilePath.length() > 0)
817     {
818         if (Profile.FilePath.at( Profile.FilePath.length()-1) == '/')
819         {
820             Profile.FilePath.erase( Profile.FilePath.length()-1 );
821         }
822         Profile.FilePath = Profile.FilePath.substr( 0, Profile.FilePath.find_last_of('/', Profile.FilePath.length()-1) ) + '/';
823         DrawState_FilePath  = true;
824         Rescan              = true;
825     }
826     else
827     {
828         Log( "Error: Filepath is empty\n" );
829     }
830 }
831
832 void CSelector::DirectoryDown( void )
833 {
834     if (DisplayList.at(MODE_SELECT_ENTRY).absolute < (int16_t)ItemsEntry.size() )
835     {
836         if (ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Type == TYPE_DIR )
837         {
838             Profile.FilePath += ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Name + '/';
839             DrawState_FilePath  = true;
840             Rescan              = true;
841
842             EventPressCount.at(EVENT_SELECT) = EVENT_LOOPS_OFF;
843         }
844     }
845     else
846     {
847         Log( "Error: Item index of %d too large for size of scanitems %d\n", DisplayList.at(MODE_SELECT_ENTRY).absolute, ItemsEntry.size() );
848     }
849 }
850
851 void CSelector::ZipUp( void )
852 {
853     DrawState_FilePath  = true;
854     DrawState_ZipMode   = true;
855     DrawState_ButtonL   = true;
856     Rescan              = true;
857     Profile.ZipFile     = "";
858 }
859
860 void CSelector::ZipDown( void )
861 {
862     DrawState_FilePath  = true;
863     DrawState_ZipMode   = true;
864     DrawState_ButtonL   = true;
865     Rescan              = true;
866     Profile.ZipFile = ItemsEntry.at(DisplayList.at(Mode).absolute).Name;
867     EventPressCount.at( EVENT_SELECT ) = EVENT_LOOPS_OFF;
868 }
869
870 void CSelector::RescanItems( void )
871 {
872     uint16_t total;
873
874     switch (Mode)
875     {
876         case MODE_SELECT_ENTRY:
877             Profile.ScanDir( Profile.FilePath, Config.ShowHidden, Config.UseZipSupport, ItemsEntry );
878             total = ItemsEntry.size();
879             break;
880         case MODE_SELECT_ARGUMENT:
881             Profile.ScanEntry( ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute), ItemsArgument );
882             total = ItemsArgument.size();
883             break;
884         case MODE_SELECT_VALUE:
885             Profile.ScanArgument( ItemsArgument.at(DisplayList.at(MODE_SELECT_ARGUMENT).absolute), ItemsValue );
886             total = ItemsValue.size();
887             break;
888         case MODE_SELECT_OPTION:
889             //Profile.ScanOptions( ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute), ItemsArgument );
890             Profile.ScanDefPlugins( ItemsArgument );
891             total = ItemsArgument.size();
892             break;
893         case MODE_ROM_OPTION:
894             //Profile.ScanOptions( ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute), ItemsArgument );
895             Profile.ScanRomPlugins(Profile.FilePath+ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Name, ItemsRomOption);
896             total = ItemsRomOption.size();
897             break;
898         case MODE_SELECT_PLUGIN:
899             //Profile.ScanOptions( ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute), ItemsArgument );
900             Profile.ScanDef1Plugins( WhichPlugin, ItemsDefPlugin );
901             total = ItemsDefPlugin.size();
902             break;
903         case MODE_ROM_PLUGIN:
904             //Profile.ScanOptions( ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute), ItemsArgument );
905             Profile.ScanRom1Plugins( WhichPlugin, ItemsRomPlugin );
906             total = ItemsRomPlugin.size();
907             break;
908         default:
909             total = 0;
910             Log( "Error: Unknown Mode\n" );
911             break;
912     }
913
914     if (total > Config.MaxEntries)
915     {
916         RectEntries.resize( Config.MaxEntries );
917     }
918     else
919     {
920         RectEntries.resize( total );
921     }
922     ListNames.resize( RectEntries.size() );
923
924     DisplayList.at(Mode).absolute  = 0;
925     DisplayList.at(Mode).relative  = 0;
926     DisplayList.at(Mode).first     = 0;
927     DisplayList.at(Mode).last      = 0;
928     DisplayList.at(Mode).total     = total;
929 }
930
931 void CSelector::PopulateList( void )
932 {
933     // Set limits
934     SelectionLimits( DisplayList.at( Mode ) );
935
936     switch (Mode)
937     {
938         case MODE_SELECT_ENTRY:
939             PopModeEntry();
940             break;
941         case MODE_SELECT_ARGUMENT:
942             PopModeArgument();
943             break;
944         case MODE_SELECT_VALUE:
945             PopModeValue();
946             break;
947         case MODE_SELECT_OPTION:
948             PopModeOption();
949             break;
950         case MODE_ROM_OPTION:
951             PopModeRomOption();
952             break;
953         case MODE_SELECT_PLUGIN:
954             PopModePlugin();
955             break;
956         case MODE_ROM_PLUGIN:
957             PopModeRomPlugin();
958             break;
959         default:
960             Log( "Error: CSelector::PopulateList Unknown Mode\n" );
961             break;
962     }
963 }
964
965 void CSelector::PopModeEntry( void )
966 {
967     uint16_t i;
968     uint16_t index;
969
970     for (i=0; i<ListNames.size(); i++)
971     {
972         index = DisplayList.at( MODE_SELECT_ENTRY ).first+i;
973         if (CheckRange( index, ItemsEntry.size() ))
974         {
975             ListNames.at(i).text.clear();
976             if (ItemsEntry.at(index).Entry >= 0)
977             {
978                 ListNames.at(i).text = Profile.Entries.at(ItemsEntry.at(index).Entry).Alias;
979             }
980             if (ListNames.at(i).text.length() == 0)
981             {
982                 ListNames.at(i).text = ItemsEntry.at(index).Name;
983             }
984
985             if (Config.ShowExts == false)
986             {
987                 ListNames.at(i).text = ListNames.at(i).text.substr( 0, ListNames.at(i).text.find_last_of(".") );
988             }
989             ListNames.at(i).text = ListNames.at(i).text;
990
991             if (index == DisplayList.at(Mode).absolute)
992             {
993                 ListNames.at(i).font = FONT_SIZE_LARGE;
994                 LoadPreview( ListNames.at(i).text );        // Load preview
995             }
996             else
997             {
998                 ListNames.at(i).font = FONT_SIZE_MEDIUM;
999             }
1000
1001             ListNames.at(i).color = Config.ColorFontFiles;
1002             if (ItemsEntry.at(index).Type == TYPE_DIR)
1003             {
1004                 ListNames.at(i).color = Config.ColorFontFolders;
1005             }
1006         }
1007         else
1008         {
1009             Log( "Error: CSelector::PopulateModeSelectEntry Index Error\n" );
1010         }
1011     }
1012 }
1013
1014 void CSelector::PopModeArgument( void )
1015 {
1016     uint16_t i;
1017     uint16_t index;
1018
1019     for (i=0; i<ListNames.size(); i++)
1020     {
1021         index = DisplayList.at(MODE_SELECT_ARGUMENT).first+i;
1022
1023         if (CheckRange( index, ItemsArgument.size() ))
1024         {
1025             ListNames.at(i).text = ItemsArgument.at(index).Name;
1026
1027             if (i == DisplayList.at(MODE_SELECT_ARGUMENT).absolute)
1028             {
1029                 ListNames.at(i).font = FONT_SIZE_LARGE;
1030             }
1031             else
1032             {
1033                 ListNames.at(i).font = FONT_SIZE_MEDIUM;
1034             }
1035
1036             ListNames.at(i).color = Config.ColorFontFiles;
1037         }
1038         else
1039         {
1040             Log( "Error: PopModeArgument index is out of range\n" );
1041         }
1042     }
1043 }
1044
1045 void CSelector::PopModeValue( void )
1046 {
1047     uint16_t i;
1048     uint16_t index;
1049     int16_t defvalue;
1050     int16_t entry;
1051     listoption_t argument;
1052
1053     if (CheckRange(DisplayList.at(MODE_SELECT_ARGUMENT).absolute, ItemsArgument.size() ))
1054     {
1055         argument = ItemsArgument.at(DisplayList.at(MODE_SELECT_ARGUMENT).absolute);
1056
1057         for (i=0; i<ListNames.size(); i++)
1058         {
1059             index = DisplayList.at(MODE_SELECT_VALUE).first+i;
1060
1061             if (CheckRange( index, ItemsValue.size() ))
1062             {
1063                 ListNames.at(i).text = ItemsValue.at(index);
1064
1065                 if (index == DisplayList.at(MODE_SELECT_VALUE).absolute)
1066                 {
1067                     ListNames.at(i).font = FONT_SIZE_LARGE;
1068                 }
1069                 else
1070                 {
1071                     ListNames.at(i).font = FONT_SIZE_MEDIUM;
1072                 }
1073
1074                 // Detect the default value
1075                 if (ItemsArgument.at(DisplayList.at(MODE_SELECT_ARGUMENT).absolute).Command >= 0)
1076                 {
1077                     if (SetAllEntryValue == true)
1078                     {
1079                         Profile.Commands.at(argument.Command).Arguments.at(argument.Argument).Default = DisplayList.at(MODE_SELECT_VALUE).absolute;
1080                     }
1081                     defvalue = Profile.Commands.at(argument.Command).Arguments.at(argument.Argument).Default;
1082                 }
1083                 else
1084                 {
1085                     if (SetAllEntryValue == true)
1086                     {
1087                         Profile.Extensions.at(argument.Extension).Arguments.at(argument.Argument).Default = DisplayList.at(MODE_SELECT_VALUE).absolute;
1088                     }
1089                     defvalue = Profile.Extensions.at(argument.Extension).Arguments.at(argument.Argument).Default;
1090                 }
1091                 if (index==defvalue)
1092                 {
1093                     ListNames.at(i).text += '*';
1094                 }
1095
1096                 // Set the color for the selected item for the entry
1097                 ListNames.at(i).color = Config.ColorFontFiles;
1098                 if (ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry<0)
1099                 {
1100                     // A custom value has been selected, so create a new entry
1101                     if (SetOneEntryValue == true)
1102                     {
1103                         entry = Profile.AddEntry( argument, ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Name );
1104                         if (entry>0)
1105                         {
1106                             ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry = entry;
1107                         }
1108                         else
1109                         {
1110                             Log( "Error: Could not create new entry\n" );
1111                         }
1112                     }
1113                     else if (index==defvalue)
1114                     {
1115                         ListNames.at(i).color = COLOR_RED;
1116                     }
1117                 }
1118
1119                 if (ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry>=0)
1120                 {
1121                     if (ItemsArgument.at(DisplayList.at(MODE_SELECT_ARGUMENT).absolute).Command >= 0)
1122                     {
1123                         if (SetOneEntryValue == true || SetAllEntryValue == true)
1124                         {
1125                             Profile.Entries.at(ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry).CmdValues.at(argument.Command+argument.Argument) = DisplayList.at(MODE_SELECT_VALUE).absolute;
1126                         }
1127                         if (index == Profile.Entries.at(ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry).CmdValues.at(argument.Command+argument.Argument))
1128                         {
1129                             ListNames.at(i).color = COLOR_RED;
1130                         }
1131                     }
1132                     else
1133                     {
1134                         if (SetOneEntryValue == true || SetAllEntryValue == true)
1135                         {
1136                             Profile.Entries.at(ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry).ArgValues.at(argument.Argument) = DisplayList.at(MODE_SELECT_VALUE).absolute;
1137                         }
1138                         if (index == Profile.Entries.at(ItemsEntry.at(DisplayList.at(MODE_SELECT_ENTRY).absolute).Entry).ArgValues.at(argument.Argument))
1139                         {
1140                             ListNames.at(i).color = COLOR_RED;
1141                         }
1142                     }
1143                 }
1144             }
1145             else
1146             {
1147                 Log( "Error: PopModeValue index is out of range\n" );
1148             }
1149         }
1150     }
1151     else
1152     {
1153         Log( "Error: PopModeValue argument index out of range\n" );
1154     }
1155 }
1156
1157 void CSelector::PopModeOption( void )
1158 {
1159     uint16_t i;
1160     uint16_t index;
1161
1162     for (i=0; i<ListNames.size(); i++)
1163     {
1164         index = DisplayList.at(MODE_SELECT_OPTION).first+i;
1165
1166         if (CheckRange( index, ItemsArgument.size() ))
1167         {
1168             ListNames.at(i).text = ItemsArgument.at(index).Name;
1169
1170             if (i == DisplayList.at(MODE_SELECT_OPTION).absolute)
1171             {
1172                 ListNames.at(i).font = FONT_SIZE_LARGE;
1173             }
1174             else
1175             {
1176                 ListNames.at(i).font = FONT_SIZE_MEDIUM;
1177             }
1178
1179             ListNames.at(i).color = Config.ColorFontFiles;
1180         }
1181         else
1182         {
1183             Log( "Error: PopModeOption index is out of range\n" );
1184         }
1185     }
1186 }
1187
1188 void CSelector::PopModePlugin( void )
1189 {
1190     uint16_t i;
1191     uint16_t index;
1192
1193     for (i=0; i<ListNames.size(); i++)
1194     {
1195         index = DisplayList.at(MODE_SELECT_PLUGIN).first+i;
1196
1197         if (CheckRange( index, ItemsDefPlugin.size() ))
1198         {
1199             ListNames.at(i).text = ItemsDefPlugin.at(index).Name;
1200
1201             if (i == DisplayList.at(MODE_SELECT_PLUGIN).absolute)
1202             {
1203                 ListNames.at(i).font = FONT_SIZE_LARGE;
1204             }
1205             else
1206             {
1207                 ListNames.at(i).font = FONT_SIZE_MEDIUM;
1208             }
1209
1210             if (ItemsDefPlugin.at(index).Type == TYPE_DIR)
1211                 ListNames.at(i).color = COLOR_RED;
1212             else
1213                 ListNames.at(i).color = Config.ColorFontFiles;
1214         }
1215         else
1216         {
1217             Log( "Error: PopModePlugin index is out of range\n" );
1218         }
1219     }
1220 }
1221 void CSelector::PopModeRomOption( void )
1222 {
1223     uint16_t i;
1224     uint16_t index;
1225
1226     for (i=0; i<ListNames.size(); i++)
1227     {
1228         index = DisplayList.at(MODE_ROM_OPTION).first+i;
1229
1230         if (CheckRange( index, ItemsRomOption.size() ))
1231         {
1232             ListNames.at(i).text = ItemsRomOption.at(index).Name;
1233
1234             if (i == DisplayList.at(MODE_ROM_OPTION).absolute)
1235             {
1236                 ListNames.at(i).font = FONT_SIZE_LARGE;
1237             }
1238             else
1239             {
1240                 ListNames.at(i).font = FONT_SIZE_MEDIUM;
1241             }
1242
1243             ListNames.at(i).color = Config.ColorFontFiles;
1244         }
1245         else
1246         {
1247             Log( "Error: PopModeOption index is out of range\n" );
1248         }
1249     }
1250 }
1251
1252 void CSelector::PopModeRomPlugin( void )
1253 {
1254     uint16_t i;
1255     uint16_t index;
1256
1257     for (i=0; i<ListNames.size(); i++)
1258     {
1259         index = DisplayList.at(MODE_ROM_PLUGIN).first+i;
1260
1261         if (CheckRange( index, ItemsRomPlugin.size() ))
1262         {
1263             ListNames.at(i).text = ItemsRomPlugin.at(index).Name;
1264
1265             if (i == DisplayList.at(MODE_ROM_PLUGIN).absolute)
1266             {
1267                 ListNames.at(i).font = FONT_SIZE_LARGE;
1268             }
1269             else
1270             {
1271                 ListNames.at(i).font = FONT_SIZE_MEDIUM;
1272             }
1273
1274             if (ItemsRomPlugin.at(index).Type == TYPE_DIR)
1275                 ListNames.at(i).color = COLOR_RED;
1276             else
1277                 ListNames.at(i).color = Config.ColorFontFiles;
1278         }
1279         else
1280         {
1281             Log( "Error: PopModePlugin index is out of range\n" );
1282         }
1283     }
1284 }
1285
1286 void CSelector::LoadPreview( const string& name )
1287 {
1288     string filename;
1289     SDL_Surface* preview = NULL;
1290
1291     if (ImagePreview != NULL)
1292     {
1293         DrawState_Preview = true;
1294     }
1295
1296     FREE_IMAGE( ImagePreview );
1297
1298     filename = Config.PreviewsPath + "/" + name.substr( 0, name.find_last_of(".")) + ".png";
1299     preview = LoadImage( filename.c_str() );
1300     if (preview != NULL)
1301     {
1302         ImagePreview = ScaleSurface( preview, Config.PreviewWidth, Config.PreviewHeight );
1303         FREE_IMAGE( preview );
1304         DrawState_Preview = true;
1305     }
1306 }
1307
1308 int8_t CSelector::DrawNames( SDL_Rect& location )
1309 {
1310     uint16_t entry_index;
1311     uint16_t startx, starty;
1312     int16_t offset;
1313     SDL_Rect rect_clip;
1314     SDL_Surface* text_surface = NULL;
1315
1316     if (Config.AutoLayout == false)
1317     {
1318         location.x = Config.PosX_ListNames;
1319         location.y = Config.PosY_ListNames;
1320     }
1321
1322     startx = location.x;
1323     starty = location.y;
1324
1325     if (ListNames.size() <= 0)
1326     {
1327         // Empty directories or zip files
1328         if (Config.UseZipSupport == true && Profile.ZipFile.length() > 0)
1329         {
1330             text_surface = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_MEDIUM), EMPTY_ZIP_LABEL, Config.Colors.at(COLOR_BLACK) );
1331         }
1332         else
1333         {
1334             text_surface = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_MEDIUM), EMPTY_DIR_LABEL, Config.Colors.at(COLOR_BLACK) );
1335         }
1336
1337         if (text_surface != NULL)
1338         {
1339             location.x += ImageSelectPointer->w;
1340
1341             rect_clip.x = 0;
1342             rect_clip.y = 0;
1343             rect_clip.w = Config.DisplayListMaxWidth-location.x;
1344             rect_clip.h = text_surface->h;
1345
1346             ApplyImage( location.x, location.y, text_surface, Screen, &rect_clip );
1347
1348             ListNameHeight = MAX(ListNameHeight, location.y+text_surface->h );
1349             location.x -= ImageSelectPointer->w;
1350             location.y += text_surface->h + Config.EntryYDelta;
1351
1352             FREE_IMAGE( text_surface );
1353         }
1354         else
1355         {
1356             Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
1357             return 1;
1358         }
1359     }
1360     else
1361     {
1362         for (entry_index=0; entry_index<ListNames.size(); entry_index++)
1363         {
1364             offset = 0;
1365
1366             // Draw the selector pointer
1367             if (entry_index == DisplayList.at(Mode).relative)
1368             {
1369                 ApplyImage( location.x, location.y,  ImageSelectPointer, Screen, NULL );
1370
1371                 // Reset scroll settings
1372                 if (entry_index != LastSelectedEntry)
1373                 {
1374                     CurScrollPause      = 0;
1375                     CurScrollSpeed      = 0;
1376                     TextScrollOffset    = 0;
1377                     TextScrollDir       = true;
1378                 }
1379                 LastSelectedEntry = entry_index;
1380             }
1381
1382             text_surface = TTF_RenderText_Solid( Fonts.at(ListNames.at(entry_index).font), ListNames.at(entry_index).text.c_str(), Config.Colors.at(ListNames.at(entry_index).color) );
1383
1384             if (text_surface != NULL)
1385             {
1386                 location.x += ImageSelectPointer->w;
1387                 RectEntries.at(entry_index).x = location.x;
1388                 RectEntries.at(entry_index).y = location.y;
1389                 RectEntries.at(entry_index).w = text_surface->w;
1390                 RectEntries.at(entry_index).h = text_surface->h;
1391
1392                 if (text_surface->w > (Config.DisplayListMaxWidth-location.x) )
1393                 {
1394                     RectEntries.at(entry_index).w = Config.DisplayListMaxWidth-location.x;
1395
1396                     if (Config.TextScrollOption == true && DisplayList.at(Mode).relative == entry_index)
1397                     {
1398                         offset = TextScrollOffset;
1399
1400                         if (CurScrollPause > 1)
1401                         {
1402                             CurScrollPause++;
1403                             if (CurScrollPause >= Config.ScrollPauseSpeed)
1404                             {
1405                                 CurScrollPause = 1;
1406                             }
1407                         }
1408                         else
1409                         {
1410                             CurScrollSpeed++;
1411                             if (CurScrollSpeed >= Config.ScrollSpeed)
1412                             {
1413                                 CurScrollSpeed = 1;
1414                                 if (TextScrollDir == true)
1415                                 {
1416                                     TextScrollOffset += Config.ScreenRatioW;
1417                                 }
1418                                 else
1419                                 {
1420                                     TextScrollOffset -= Config.ScreenRatioW;
1421                                 }
1422                                 Redraw = true;
1423                             }
1424
1425                             if (RectEntries.at(entry_index).w+TextScrollOffset >= text_surface->w)
1426                             {
1427                                 TextScrollDir   = false;
1428                                 CurScrollPause  = 2;
1429                             }
1430                             else if (TextScrollOffset <= 0)
1431                             {
1432                                 TextScrollDir   = true;
1433                                 CurScrollPause  = 2;
1434                             }
1435                         }
1436                     }
1437                 }
1438
1439                 rect_clip.w = Config.DisplayListMaxWidth-location.x;
1440                 rect_clip.h = text_surface->h;
1441                 rect_clip.x = offset;
1442                 rect_clip.y = 0;
1443
1444                 ApplyImage( location.x, location.y,  text_surface, Screen, &rect_clip );
1445
1446                 ListNameHeight = MAX(ListNameHeight, location.y+text_surface->h );
1447                 location.x -= ImageSelectPointer->w;
1448                 location.y += text_surface->h + Config.EntryYDelta;
1449
1450                 FREE_IMAGE( text_surface );
1451             }
1452             else
1453             {
1454                 Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
1455                 return 1;
1456             }
1457         }
1458     }
1459
1460     UpdateRect( startx, starty, Config.DisplayListMaxWidth-startx, ListNameHeight-starty );
1461
1462     return 0;
1463 }
1464
1465 void CSelector::SelectionLimits( item_pos_t& pos )
1466 {
1467     if (pos.absolute <= pos.first)
1468     {
1469         pos.relative = 0;
1470         if (pos.absolute < 0)
1471         {
1472             pos.absolute = 0;
1473         }
1474
1475         pos.first = pos.absolute;
1476         if (pos.total < Config.MaxEntries)
1477         {
1478             pos.last = (pos.total-1);
1479         }
1480         else
1481         {
1482             pos.last = pos.absolute+(Config.MaxEntries-1);
1483         }
1484     }
1485     else if (pos.absolute >= pos.last)
1486     {
1487         if (pos.absolute > (int16_t)(pos.total-1))
1488         {
1489             pos.absolute = (pos.total-1);
1490         }
1491
1492         pos.first   = pos.absolute-(Config.MaxEntries-1);
1493         pos.last    = pos.absolute;
1494         if (pos.total < Config.MaxEntries)
1495         {
1496             pos.relative = (pos.total-1);
1497         }
1498         else
1499         {
1500             pos.relative = Config.MaxEntries-1;
1501         }
1502
1503         if (pos.first < 0)
1504         {
1505             pos.first = 0;
1506         }
1507     }
1508 }
1509
1510 void CSelector::DrawBackground( void )
1511 {
1512     if (ImageBackground != NULL)
1513     {
1514         ApplyImage( 0, 0, ImageBackground, Screen, NULL );
1515     }
1516     else
1517     {
1518         SDL_FillRect( Screen, NULL, rgb_to_int(Config.Colors.at(Config.ColorBackground)) );
1519     }
1520 }
1521
1522 int8_t CSelector::ConfigureButtons( void )
1523 {
1524     uint16_t i;
1525
1526     // Common button mappings
1527     ButtonModesLeft.at(0) = EVENT_ONE_UP;
1528     ButtonModesLeft.at(1) = EVENT_PAGE_UP;
1529     ButtonModesLeft.at(2) = EVENT_PAGE_DOWN;
1530     ButtonModesLeft.at(3) = EVENT_ONE_DOWN;
1531
1532     ButtonModesRight.at(0) = EVENT_QUIT;
1533
1534     // Specific button mappings
1535     switch (Mode)
1536     {
1537         case MODE_SELECT_ENTRY:
1538             ButtonModesLeft.at(4) = EVENT_DIR_UP;
1539             ButtonModesLeft.at(5) = EVENT_DIR_DOWN;
1540             if (Config.UseZipSupport == true && Profile.ZipFile.length() > 0)
1541             {
1542                 ButtonModesLeft.at(6) = EVENT_ZIP_MODE;
1543             }
1544             else
1545             {
1546                 ButtonModesLeft.at(6) = EVENT_NONE;
1547             }
1548
1549             ButtonModesRight.at(1) = EVENT_SELECT;
1550             ButtonModesRight.at(2) = EVENT_CFG_ITEM;
1551             ButtonModesRight.at(3) = EVENT_NONE;
1552             ButtonModesRight.at(3) = EVENT_CFG_APP;   // TODO
1553             break;
1554         case MODE_SELECT_ARGUMENT:
1555             ButtonModesLeft.at(4) = EVENT_NONE;
1556             ButtonModesLeft.at(5) = EVENT_NONE;
1557             ButtonModesLeft.at(6) = EVENT_NONE;
1558
1559             ButtonModesRight.at(1) = EVENT_BACK;
1560             ButtonModesRight.at(2) = EVENT_SELECT;
1561             ButtonModesRight.at(3) = EVENT_NONE;
1562             break;
1563         case MODE_SELECT_VALUE:
1564             ButtonModesLeft.at(4) = EVENT_NONE;
1565             ButtonModesLeft.at(5) = EVENT_NONE;
1566             ButtonModesLeft.at(6) = EVENT_NONE;
1567
1568             ButtonModesRight.at(1) = EVENT_BACK;
1569             ButtonModesRight.at(2) = EVENT_SET_ALL;
1570             ButtonModesRight.at(3) = EVENT_SET_ONE;
1571             break;
1572         case MODE_SELECT_OPTION:
1573             ButtonModesLeft.at(4) = EVENT_NONE;
1574             ButtonModesLeft.at(5) = EVENT_NONE;
1575             ButtonModesLeft.at(6) = EVENT_NONE;
1576
1577             ButtonModesRight.at(1) = EVENT_BACK;
1578             ButtonModesRight.at(2) = EVENT_SELECT;
1579             ButtonModesRight.at(3) = EVENT_NONE;
1580             break;
1581         case MODE_SELECT_PLUGIN:
1582             ButtonModesLeft.at(4) = EVENT_NONE;
1583             ButtonModesLeft.at(5) = EVENT_NONE;
1584             ButtonModesLeft.at(6) = EVENT_NONE;
1585
1586             ButtonModesRight.at(1) = EVENT_BACK;
1587             ButtonModesRight.at(2) = EVENT_SELECT;
1588             ButtonModesRight.at(3) = EVENT_NONE;
1589             break;
1590         case MODE_ROM_OPTION:
1591             ButtonModesLeft.at(4) = EVENT_NONE;
1592             ButtonModesLeft.at(5) = EVENT_NONE;
1593             ButtonModesLeft.at(6) = EVENT_NONE;
1594
1595             ButtonModesRight.at(1) = EVENT_BACK;
1596             ButtonModesRight.at(2) = EVENT_SELECT;
1597             ButtonModesRight.at(3) = EVENT_NONE;
1598             break;
1599         case MODE_ROM_PLUGIN:
1600             ButtonModesLeft.at(4) = EVENT_NONE;
1601             ButtonModesLeft.at(5) = EVENT_NONE;
1602             ButtonModesLeft.at(6) = EVENT_NONE;
1603
1604             ButtonModesRight.at(1) = EVENT_BACK;
1605             ButtonModesRight.at(2) = EVENT_SELECT;
1606             ButtonModesRight.at(3) = EVENT_NONE;
1607             break;
1608         default:
1609             Log( "Error: Unknown Mode\n" );
1610             return 1;
1611             break;
1612     }
1613
1614     // Overides for button driven by config options
1615     for (i=0; i<ButtonModesLeft.size(); i++ )
1616     {
1617         if (Config.ButtonModesLeftEnable.at(i) == false)
1618         {
1619             ButtonModesLeft.at(i) = EVENT_NONE;
1620         }
1621     }
1622
1623     for (i=0; i<ButtonModesRight.size(); i++ )
1624     {
1625         if (Config.ButtonModesRightEnable.at(i) == false)
1626         {
1627             ButtonModesRight.at(i) = EVENT_NONE;
1628         }
1629     }
1630
1631     return 0;
1632 }
1633
1634 int8_t CSelector::DrawButtons( SDL_Rect& location )
1635 {
1636     uint8_t button;
1637     SDL_Rect preview;
1638
1639     if (Config.AutoLayout == false)
1640     {
1641         location.x = Config.PosX_ButtonLeft;
1642         location.y = Config.PosY_ButtonLeft;
1643     }
1644
1645     // Draw buttons on left
1646     if (DrawState_ButtonL == true)
1647     {
1648         for (button=0; button<BUTTONS_MAX_LEFT; button++)
1649         {
1650             RectButtonsLeft.at(button).x = location.x;
1651             RectButtonsLeft.at(button).y = location.y+(Config.ButtonHeightLeft*button)+(Config.EntryYOffset*button);
1652             RectButtonsLeft.at(button).w = Config.ButtonWidthLeft;
1653             RectButtonsLeft.at(button).h = Config.ButtonHeightLeft;
1654
1655             if (ButtonModesLeft.at(button) != EVENT_NONE)
1656             {
1657                 if (DrawButton( ButtonModesLeft.at(button), Fonts.at(FONT_SIZE_LARGE), RectButtonsLeft.at(button) ))
1658                 {
1659                     return 1;
1660                 }
1661             }
1662             UpdateRect( RectButtonsLeft.at(button).x, RectButtonsLeft.at(button).y, RectButtonsLeft.at(button).w, RectButtonsLeft.at(button).h );
1663         }
1664         DrawState_ButtonL = false;
1665     }
1666     location.x += Config.ButtonWidthLeft + Config.EntryXOffset;
1667
1668     // Draw buttons on right
1669     if (DrawState_ButtonR == true)
1670     {
1671         for (button=0; button<BUTTONS_MAX_RIGHT; button++)
1672         {
1673             if (Config.AutoLayout == false)
1674             {
1675                 RectButtonsRight.at(button).x = Config.PosX_ButtonRight;
1676                 RectButtonsRight.at(button).y = Config.PosY_ButtonRight-(Config.ButtonHeightRight*(button+1))-(Config.EntryYOffset*(button+3));
1677             }
1678             else
1679             {
1680                 RectButtonsRight.at(button).x = Config.ScreenWidth-Config.ButtonWidthRight-Config.EntryXOffset;
1681                 RectButtonsRight.at(button).y = Config.ScreenHeight-(Config.ButtonHeightRight*(button+1))-(Config.EntryYOffset*(button+3));
1682             }
1683             RectButtonsRight.at(button).w = Config.ButtonWidthRight;
1684             RectButtonsRight.at(button).h = Config.ButtonHeightRight;
1685
1686             if (ButtonModesRight.at(button) != EVENT_NONE)
1687             {
1688                 if (DrawButton( ButtonModesRight.at(button), Fonts.at(FONT_SIZE_LARGE), RectButtonsRight.at(button) ))
1689                 {
1690                     return 1;
1691                 }
1692             }
1693             UpdateRect( RectButtonsRight.at(button).x, RectButtonsRight.at(button).y, RectButtonsRight.at(button).w, RectButtonsRight.at(button).h );
1694         }
1695         DrawState_ButtonR = false;
1696     }
1697
1698     //Display the preview graphic
1699     if (Mode == MODE_SELECT_ENTRY && DrawState_Preview == true)
1700     {
1701         preview.x = Config.ScreenWidth-Config.PreviewWidth-Config.EntryXOffset;
1702         preview.y = Config.ScreenHeight-Config.PreviewHeight-(Config.ButtonHeightRight*3)-(Config.EntryYOffset*6);
1703
1704         if (ImagePreview != NULL)
1705         {
1706             ApplyImage( preview.x, preview.y, ImagePreview, Screen, NULL );
1707         }
1708         UpdateRect( preview.x, preview.y, Config.PreviewWidth, Config.PreviewHeight );
1709         DrawState_Preview = false;
1710     }
1711
1712     return 0;
1713 }
1714
1715 int8_t CSelector::DrawButton( uint8_t button, TTF_Font* font, SDL_Rect& location )
1716 {
1717     SDL_Surface* text_surface = NULL;
1718     SDL_Rect rect_text;
1719
1720     if (ImageButtons.at(button) != NULL)
1721     {
1722         ApplyImage( location.x, location.y, ImageButtons.at(button), Screen, NULL );
1723     }
1724     else
1725     {
1726         SDL_FillRect( Screen, &location, rgb_to_int(Config.Colors.at(Config.ColorButton)) );
1727     }
1728
1729
1730     if (Config.ShowLabels == true)
1731     {
1732         text_surface = TTF_RenderText_Solid( font, LabelButtons.at(button).c_str(), Config.Colors.at(Config.ColorFontButton) );
1733
1734         rect_text.x = location.x + ((location.w-text_surface->w)/2);
1735         rect_text.y = location.y + ((location.h-text_surface->h)/2);
1736
1737         if (text_surface != NULL)
1738         {
1739             ApplyImage( rect_text.x, rect_text.y, text_surface, Screen, NULL );
1740
1741             FREE_IMAGE( text_surface );
1742         }
1743         else
1744         {
1745             Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
1746             return 1;
1747         }
1748     }
1749     return 0;
1750 }
1751
1752 int8_t CSelector::DrawText( SDL_Rect& location )
1753 {
1754     int16_t         total;
1755     int16_t         prev_width;
1756     int16_t         prev_height;
1757     int16_t         max_height;
1758     string          text;
1759     SDL_Rect        box, clip;
1760
1761     prev_width  = 0;
1762     prev_height = 0;
1763
1764     if (Config.AutoLayout == false)
1765     {
1766         location.x = Config.PosX_Title;
1767         location.y = Config.PosY_Title;
1768     }
1769
1770     // Title text
1771     if (ImageTitle != NULL)
1772     {
1773         if (DrawState_Title == true)
1774         {
1775             ApplyImage( location.x, location.y, ImageTitle, Screen, NULL );
1776             UpdateRect( location.x, location.y, ImageTitle->w, ImageTitle->h );
1777             DrawState_Title = false;
1778         }
1779         location.y += ImageTitle->h + Config.EntryYDelta;
1780     }
1781
1782     // Entry Filter and Filepath (they can overlap so both are drawn when either change)
1783     if (Mode == MODE_SELECT_ENTRY)
1784     {
1785         if (DrawState_FilePath == true || DrawState_Filter == true )
1786         {
1787             // Entry Filter
1788             if (ImageFilter != NULL)
1789             {
1790                 prev_width  = ImageFilter->w;
1791                 prev_height = ImageFilter->h;
1792             }
1793             else
1794             {
1795                 prev_width  = 0;
1796                 prev_height = 0;
1797             }
1798             max_height = prev_height;
1799
1800             FREE_IMAGE( ImageFilter );
1801
1802             if (Profile.EntryFilter.length() > 0)
1803             {
1804                 ImageFilter = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_MEDIUM), Profile.EntryFilter.c_str(), Config.Colors.at(Config.ColorFontFiles) );
1805                 if (ImageFilter != NULL)
1806                 {
1807                     clip.x = 0;
1808                     clip.y = 0;
1809                     clip.w = Config.FilePathMaxWidth;
1810                     clip.h = ImageFilter->h;
1811                     if (ImageFilter->w > Config.FilePathMaxWidth)
1812                     {
1813                         clip.x = ImageFilter->w-Config.FilePathMaxWidth;
1814                     }
1815
1816                     location.x = Config.ScreenWidth - ImageFilter->w - Config.EntryXOffset;
1817
1818                     ApplyImage( location.x, location.y, ImageFilter, Screen, &clip );
1819
1820                     max_height = MAX( max_height, ImageFilePath->h );
1821                 }
1822                 else
1823                 {
1824                     Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
1825                     return 1;
1826                 }
1827             }
1828             location.x = Config.EntryXOffset;
1829
1830             // File path
1831             if (ImageFilePath != NULL)
1832             {
1833                 prev_width  = ImageFilePath->w;
1834                 prev_height = ImageFilePath->h;
1835             }
1836             else
1837             {
1838                 prev_width  = 0;
1839                 prev_height = 0;
1840             }
1841             max_height = MAX( max_height, prev_height );
1842
1843             FREE_IMAGE(ImageFilePath);
1844
1845             text = Profile.FilePath;
1846             if (Profile.ZipFile.length())
1847             {
1848                 text += "->" + Profile.ZipFile;
1849             }
1850
1851             ImageFilePath = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_MEDIUM), text.c_str(), Config.Colors.at(Config.ColorFontFiles) );
1852             if (ImageFilePath != NULL)
1853             {
1854                 clip.x = 0;
1855                 clip.y = 0;
1856                 clip.w = Config.FilePathMaxWidth;
1857                 clip.h = ImageFilePath->h;
1858
1859                 if (ImageFilePath->w > Config.FilePathMaxWidth)
1860                 {
1861                     clip.x = ImageFilePath->w-Config.FilePathMaxWidth;
1862                 }
1863
1864                 ApplyImage( location.x, location.y, ImageFilePath, Screen, &clip );
1865
1866                 max_height = MAX( max_height, ImageFilePath->h );
1867             }
1868             else
1869             {
1870                 Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
1871                 return 1;
1872             }
1873
1874             UpdateRect( 0, location.y, Config.ScreenWidth, max_height );
1875
1876             DrawState_FilePath = false;
1877             DrawState_Filter = false;
1878         }
1879     }
1880
1881     if (ImageFilePath != NULL)
1882     {
1883         location.y += ImageFilePath->h + Config.EntryYOffset;
1884     }
1885
1886     // About text
1887     if (ImageAbout != NULL)
1888     {
1889         if (DrawState_About == true)
1890         {
1891             box.x = Config.ScreenWidth  - ImageAbout->w - Config.EntryXOffset;
1892             box.y = Config.ScreenHeight - ImageAbout->h - Config.EntryYOffset;
1893             ApplyImage( box.x, box.y, ImageAbout, Screen, NULL );
1894             UpdateRect( box.x, box.y, ImageAbout->w, ImageAbout->h );
1895             DrawState_About = false;
1896         }
1897     }
1898
1899     // Item count
1900     switch (Mode)
1901     {
1902         case MODE_SELECT_ENTRY:
1903             total = ItemsEntry.size();
1904             break;
1905         case MODE_SELECT_ARGUMENT: // fall through
1906         case MODE_SELECT_OPTION:
1907             total = ItemsArgument.size();
1908             break;
1909         case MODE_SELECT_VALUE:
1910             total = ItemsValue.size();
1911             break;
1912         case MODE_SELECT_PLUGIN:
1913             total = ItemsDefPlugin.size();
1914             break;
1915         default:
1916             total = 0;
1917             text = "Error: Unknown mode";
1918             break;
1919     }
1920
1921     // Draw index
1922     if (DrawState_Index == true)
1923     {
1924         if (ImageIndex != NULL)
1925         {
1926             prev_width  = ImageIndex->w;
1927             prev_height = ImageIndex->h;
1928         }
1929         else
1930         {
1931             prev_width  = 0;
1932             prev_height = 0;
1933         }
1934
1935         FREE_IMAGE( ImageIndex );
1936
1937         text = "0 of 0";
1938         if (total > 0)
1939         {
1940             text = i_to_a(DisplayList.at(Mode).absolute+1) + " of " + i_to_a(total);
1941         }
1942         ImageIndex = TTF_RenderText_Solid(Fonts.at(FONT_SIZE_SMALL), text.c_str(), Config.Colors.at(Config.ColorFontFiles));
1943
1944         if (ImageIndex != NULL)
1945         {
1946             box.x = Config.EntryXOffset;
1947             box.y = Config.ScreenHeight - ImageIndex->h - Config.EntryYOffset;
1948
1949             ApplyImage( box.x, box.y, ImageIndex, Screen, NULL );
1950             UpdateRect( box.x, box.y, MAX(ImageIndex->w, prev_width), MAX(ImageIndex->h, prev_height) );
1951         }
1952         else
1953         {
1954             Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError());
1955             return 1;
1956         }
1957         DrawState_Index = false;
1958     }
1959
1960     // Zip extract option
1961     if (DrawState_ZipMode == true)
1962     {
1963         if (Config.UseZipSupport == true && Profile.ZipFile.length() > 0)
1964         {
1965             if (ImageZipMode != NULL)
1966             {
1967                 prev_width  = ImageZipMode->w;
1968                 prev_height = ImageZipMode->h;
1969             }
1970             else
1971             {
1972                 prev_width  = 0;
1973                 prev_height = 0;
1974             }
1975
1976             FREE_IMAGE( ImageZipMode );
1977
1978             if (ExtractAllFiles == true)
1979             {
1980                 text = "Extract All";
1981             }
1982             else
1983             {
1984                 text = "Extract Selection";
1985             }
1986
1987             ImageZipMode = TTF_RenderText_Solid(Fonts.at(FONT_SIZE_SMALL), text.c_str(), Config.Colors.at(Config.ColorFontFiles));
1988             if (ImageZipMode == NULL)
1989             {
1990                 Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError());
1991                 return 1;
1992             }
1993         }
1994
1995         if (ImageZipMode != NULL)
1996         {
1997             box.x = 5*Config.EntryXOffset;
1998             box.y = Config.ScreenHeight - ImageZipMode->h - Config.EntryYOffset;
1999
2000             if (Config.UseZipSupport == true && Profile.ZipFile.length() > 0)
2001             {
2002                 ApplyImage( box.x, box.y, ImageZipMode, Screen, NULL );
2003             }
2004             UpdateRect( box.x, box.y, MAX(ImageZipMode->w, prev_width), MAX(ImageZipMode->h, prev_height) );
2005         }
2006         DrawState_ZipMode = false;
2007     }
2008
2009 #if defined(DEBUG)
2010     if (ImageDebug != NULL)
2011     {
2012         prev_width  = ImageDebug->w;
2013         prev_height = ImageDebug->h;
2014     }
2015     else
2016     {
2017         prev_width  = 0;
2018         prev_height = 0;
2019     }
2020
2021     FREE_IMAGE( ImageDebug );
2022
2023     text = "DEBUG abs " + i_to_a(DisplayList.at(Mode).absolute)  + " rel " + i_to_a(DisplayList.at(Mode).relative)
2024          + " F " + i_to_a(DisplayList.at(Mode).first) + " L " + i_to_a(DisplayList.at(Mode).last)
2025          + " T " + i_to_a(DisplayList.at(Mode).total)
2026          + " fps " + i_to_a(FPSDrawn) + " skp " + i_to_a(FPSSkip) + " slp " + i_to_a(FPSSleep)
2027          + " lp " + i_to_a(LoopTimeAverage);
2028
2029     ImageDebug = TTF_RenderText_Solid( Fonts.at(FONT_SIZE_SMALL), text.c_str(), Config.Colors.at(Config.ColorFontFiles) );
2030
2031     if (ImageDebug != NULL)
2032     {
2033         box.x = Config.EntryXOffset;
2034         box.y = Config.ScreenHeight - ImageDebug->h;
2035
2036         ApplyImage( box.x, box.y, ImageDebug, Screen, NULL );
2037         UpdateRect( box.x, box.y, MAX(ImageDebug->w, prev_width), MAX(ImageDebug->h, prev_height) );
2038     }
2039     else
2040     {
2041         Log( "Failed to create TTF surface with TTF_RenderText_Solid: %s\n", TTF_GetError() );
2042         return 1;
2043     }
2044 #endif
2045
2046     return 0;
2047 }
2048
2049 int8_t CSelector::RunExec( uint16_t selection )
2050 {
2051     bool entry_found;
2052     uint16_t i, j, k;
2053     int16_t ext_index;
2054     string filename;
2055     string filepath;
2056     string command;
2057     string extension;
2058     string value;
2059     string cmdpath, cmdname;
2060     entry_t* entry = NULL;
2061     argforce_t* argforce = NULL;
2062     exeforce_t* exeforce = NULL;
2063     argument_t* argument = NULL;
2064
2065     // Find a entry for argument values
2066     entry_found = false;
2067
2068     if (!CheckRange( selection, ItemsEntry.size() ))
2069     {
2070         Log( "Error: RunExec selection is out of range\n" );
2071         return 1;
2072     }
2073
2074     if (ItemsEntry.at(selection).Entry >= 0)
2075     {
2076         entry = &Profile.Entries.at(ItemsEntry.at(selection).Entry);
2077         entry_found = true;
2078     }
2079
2080     // Find a executable for file extension
2081     filename = ItemsEntry.at(selection).Name;
2082     if (ItemsEntry.at(selection).Type == TYPE_DIR)
2083     {
2084         extension = EXT_DIRS;
2085     }
2086     else
2087     {
2088         extension = filename.substr( filename.find_last_of(".")+1 );
2089     }
2090
2091     ext_index = Profile.FindExtension( extension );
2092
2093     if (CheckRange( ext_index, Profile.Extensions.size() ))
2094     {
2095         command.clear();
2096
2097         // Unzip if needed
2098         if (Config.UseZipSupport == true && Profile.ZipFile.length() > 0)
2099         {
2100             mkdir( Config.ZipPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
2101             if (ExtractAllFiles == true)    // Extract all
2102             {
2103                 Profile.Minizip.ExtractFiles( Profile.FilePath + Profile.ZipFile, Config.ZipPath );
2104             }
2105             else                            // Extract one
2106             {
2107                 Profile.Minizip.ExtractFile( Profile.FilePath + Profile.ZipFile, Config.ZipPath, filename );
2108             }
2109
2110             filepath = Config.ZipPath + "/";
2111         }
2112         else
2113         {
2114             filepath = Profile.FilePath;
2115         }
2116
2117
2118         // Setup commands
2119         for (i=0; i<Profile.Commands.size(); i++)
2120         {
2121             command += "cd " + Profile.Commands.at(i).Path + "; ";
2122             command += Profile.Commands.at(i).Command;
2123
2124             for (j=0; j<Profile.Commands.at(i).Arguments.size(); j++)
2125             {
2126                 if (Profile.Commands.at(i).Arguments.at(j).Flag.compare(VALUE_NOVALUE) != 0)
2127                 {
2128                     command += " " + Profile.Commands.at(i).Arguments.at(j).Flag;
2129                 }
2130
2131                 if (Profile.Commands.at(i).Arguments.at(j).Flag.compare(VALUE_FLAGONLY) !=0 )
2132                 {
2133                     if (entry_found==true)
2134                     {
2135                         command += " " + Profile.Commands.at(i).Arguments.at(j).Values.at(entry->CmdValues.at(j));
2136                     }
2137                     else
2138                     {
2139                         command += " " + Profile.Commands.at(i).Arguments.at(j).Values.at(Profile.Commands.at(i).Arguments.at(j).Default);
2140                     }
2141                 }
2142             }
2143             command += "; ";
2144         }
2145
2146         // Check exe forces
2147         cmdpath = Profile.Extensions.at(ext_index).exePath;
2148         cmdname = Profile.Extensions.at(ext_index).exeName;
2149         for (j=0; j<Profile.Extensions.at(ext_index).ExeForces.size(); j++)
2150         {
2151             exeforce = &Profile.Extensions.at(ext_index).ExeForces.at(j);
2152             for (k=0; k<exeforce->Files.size(); k++)
2153             {
2154                 if (exeforce->Files.at(k).compare( lowercase(filename) ) == 0)
2155                 {
2156                     cmdpath = exeforce->exePath;
2157                     cmdname = exeforce->exeName;
2158                     break;
2159                 }
2160             }
2161         }
2162
2163         // Add Executable to command
2164         command += "cd " + cmdpath + "; ";
2165         command += "LD_LIBRARY_PATH=./; export LD_LIBRARY_PATH; ";
2166         command += "./" + cmdname;
2167
2168         // Setup arguments
2169         for (i=0; i<Profile.Extensions.at(ext_index).Arguments.size(); i++)
2170         {
2171             value.clear();
2172             argument = &Profile.Extensions.at(ext_index).Arguments.at(i);
2173
2174             // Check arg forces
2175             for (j=0; j<Profile.Extensions.at(ext_index).ArgForces.size(); j++)
2176             {
2177                 argforce = &Profile.Extensions.at(ext_index).ArgForces.at(j);
2178                 if (argforce->Path.compare( Profile.FilePath ) == 0)
2179                 {
2180                     if (i == argforce->Argument)
2181                     {
2182                         Log( "Setting argforce on arg %d\n", i );
2183                         value = argforce->Value;
2184                         break;
2185                     }
2186                 }
2187             }
2188             // Check arguments for default value or custom entry value
2189             if (value.length() <= 0 )
2190             {
2191                 if (entry_found==true)
2192                 {
2193                     if (CheckRange( i, entry->ArgValues.size() ))
2194                     {
2195                         value = argument->Values.at( entry->ArgValues.at(i) );
2196                     }
2197                     else
2198                     {
2199                         Log( "Error: RunExec i is out of range\n" );
2200                         return 1;
2201                     }
2202                 }
2203                 else
2204                 {
2205                     if (CheckRange( argument->Default, argument->Values.size() ))
2206                     {
2207                         value = argument->Values.at( argument->Default );
2208                     }
2209                     else
2210                     {
2211                         Log( "Error: RunExec argument->Default is out of range\n" );
2212                         return 1;
2213                     }
2214                 }
2215             }
2216             // Add the argument if used
2217             if (value.length() > 0)
2218             {
2219                 if (value.compare( VALUE_NOVALUE ) != 0 )
2220                 {
2221                     command += " " + argument->Flag + " ";
2222
2223                     if (value.compare( VALUE_FLAGONLY ) !=0 )
2224                     {
2225                         if (value.compare( VALUE_FILENAME ) == 0)
2226                         {
2227                             if (Config.FilenameArgNoExt == true)
2228                             {
2229                                 filename = filename.substr( 0, filename.find_last_of(".") );
2230                             }
2231
2232                             if (entry_found==true)
2233                             {
2234                                 command += '\"';
2235                                 if (Config.FilenameAbsPath == true)
2236                                 {
2237                                     command += entry->Path;
2238                                 }
2239                                 command += entry->Name + '\"';
2240                             }
2241                             else
2242                             {
2243                                 command += '\"';
2244                                 if (Config.FilenameAbsPath == true)
2245                                 {
2246                                     command += filepath;
2247                                 }
2248                                 command += filename + '\"';
2249                             }
2250                         }
2251                         else
2252                         {
2253                             command += value;
2254                         }
2255                     }
2256                 }
2257             }
2258         }
2259     }
2260     else
2261     {
2262         Log( "Warning no extension was found for this file type\n" );
2263         return 1;
2264     }
2265
2266     command += "; sync;";
2267     if (Config.ReloadLauncher == true)
2268     {
2269         command += " cd " + Profile.LauncherPath + ";";
2270         command += " exec ./" + Profile.LauncherName ;
2271         // Arguments
2272         command += " " + string(ARG_PROFILE) + " " + ProfilePath;
2273         command += " " + string(ARG_CONFIG)  + " " + ConfigPath;
2274         command += " " + string(ARG_ZIPLIST) + " " + ZipListPath;
2275     }
2276
2277     Log( "Running command: '%s'\n", command.c_str());
2278
2279     CloseResources(0);
2280
2281     execlp( "/bin/sh", "/bin/sh", "-c", command.c_str(), NULL );
2282
2283     //if execution continues then something went wrong and as we already called SDL_Quit we cannot continue, try reloading
2284     Log( "Error executing selected application, re-launching %s\n", APPNAME);
2285
2286     chdir( Profile.LauncherPath.c_str() );
2287     execlp( Profile.LauncherName.c_str(), Profile.LauncherName.c_str(), NULL );
2288
2289     return 0;
2290 }
2291
2292 int8_t CSelector::PollInputs( void )
2293 {
2294     int16_t     newsel;
2295     uint16_t    index;
2296     string      keyname;
2297     SDL_Event   event;
2298
2299     for (index=0; index<EventReleased.size(); index++)
2300     {
2301         if (EventReleased.at(index) == true)
2302         {
2303             EventReleased.at(index)     = false;
2304             EventPressCount.at(index)   = EVENT_LOOPS_OFF;
2305 #if defined(DEBUG)
2306             //Log( "DEBUG EventReleased %d\n", index );
2307 #endif
2308         }
2309         else if (EventPressCount.at(index) != EVENT_LOOPS_OFF)
2310         {
2311             EventPressCount.at(index)--;
2312             if (EventPressCount.at(index) < EVENT_LOOPS_ON)
2313             {
2314                 EventPressCount.at(index) = EVENT_LOOPS;
2315             }
2316         }
2317     }
2318
2319     // Sanity check
2320     if (!CheckRange( Mode, DisplayList.size() ))
2321     {
2322         Log( "Error: PollInputs Mode out of range\n" );
2323         return 1;
2324     }
2325
2326     while (SDL_PollEvent( &event ))
2327     {
2328         switch( event.type )
2329         {
2330             case SDL_KEYDOWN:
2331                 keyname = SDL_GetKeyName( event.key.keysym.sym );
2332                 if (keyname.length() == 1 || event.key.keysym.sym == SDLK_BACKSPACE)
2333                 {
2334                     if (Config.EntryFastMode == ENTRY_FAST_MODE_ALPHA)
2335                     {
2336                         DisplayList.at(Mode).absolute = Profile.AlphabeticIndices.at(event.key.keysym.sym-SDLK_a);
2337                         Profile.EntryFilter.clear();
2338                     }
2339                     else if (Config.EntryFastMode == ENTRY_FAST_MODE_FILTER)
2340                     {
2341                         if (event.key.keysym.sym==SDLK_BACKSPACE || event.key.keysym.sym==SDLK_DELETE)
2342                         {
2343                             if (Profile.EntryFilter.length() > 0)
2344                             {
2345                                 Profile.EntryFilter.erase( Profile.EntryFilter.length()-1 );
2346                             }
2347                         }
2348                         else
2349                         {
2350                             Profile.EntryFilter += keyname;
2351                         }
2352                         DrawState_Filter    = true;
2353                         Rescan              = true;
2354                     }
2355                 }
2356                 else
2357                 {
2358                     bool used=false;
2359                     for (index=0; index<EventPressCount.size(); index++)
2360                     {
2361                         if (event.key.keysym.sym == Config.KeyMaps.at(index))
2362                         {
2363                             EventPressCount.at(index) = EVENT_LOOPS_ON;
2364                             used = true;
2365                         }
2366                     }
2367 // some hardcoded aternates keys
2368                     if (!used)
2369                     {
2370                         if (event.key.keysym.sym == SDLK_END)
2371                         {
2372                             EventPressCount.at(EVENT_SELECT) = EVENT_LOOPS_ON;
2373                             used = true;
2374                         }
2375                     }
2376                     if (!used && Config.UnusedKeysLaunch)
2377                     {
2378                         EventPressCount.at(EVENT_SELECT) = EVENT_LOOPS_ON;
2379                     }
2380                 }
2381                 break;
2382
2383             case SDL_KEYUP:
2384                 {
2385                 bool used=false;
2386                 for (index=0; index<EventReleased.size(); index++)
2387                 {
2388                     if (event.key.keysym.sym == Config.KeyMaps.at(index))
2389                     {
2390                         EventReleased.at(index) = true;
2391                         used = true;
2392                     }           
2393                 }
2394 // some hardcoded aternates keys
2395                 if (!used)
2396                 {
2397                     if (event.key.keysym.sym == SDLK_END)
2398                     {
2399                         EventReleased.at(EVENT_SELECT) = true;
2400                         used = true;
2401                     }
2402                 }
2403                 if (!used && Config.UnusedKeysLaunch)
2404                 {
2405                     EventReleased.at(EVENT_SELECT) = true;
2406                 }
2407                 }
2408                 break;
2409
2410             case SDL_JOYBUTTONDOWN:
2411                 for (index=0; index<EventPressCount.size(); index++)
2412                 {
2413                     if (event.jbutton.button == Config.JoyMaps.at(index))
2414                     {
2415                         EventPressCount.at(index) = EVENT_LOOPS_ON;
2416                     }
2417                     else if (Config.UnusedJoysLaunch)
2418                     {
2419                         EventPressCount.at(EVENT_SELECT) = EVENT_LOOPS_ON;
2420                     }
2421                 }
2422                 break;
2423
2424             case SDL_JOYBUTTONUP:
2425                 for (index=0; index<EventReleased.size(); index++)
2426                 {
2427                     if (event.jbutton.button == Config.JoyMaps.at(index))
2428                     {
2429                         EventReleased.at(index) = true;
2430                     }
2431                     else if (Config.UnusedJoysLaunch)
2432                     {
2433                         EventReleased.at(EVENT_SELECT) = true;
2434                     }
2435                 }
2436                 break;
2437
2438             case SDL_JOYAXISMOTION:
2439                 if (event.jaxis.value < -Config.AnalogDeadZone)
2440                 {
2441                     if (event.jaxis.axis == 0)
2442                     {
2443                         if (IsEventOff(EVENT_PAGE_UP))
2444                         {
2445                             EventPressCount.at(EVENT_PAGE_UP) = EVENT_LOOPS_ON;
2446                         }
2447                         EventReleased.at(EVENT_PAGE_DOWN) = true;
2448                     }
2449
2450                     if (event.jaxis.axis == 1)
2451                     {
2452                         if (IsEventOff(EVENT_ONE_UP))
2453                         {
2454                             EventPressCount.at(EVENT_ONE_UP) = EVENT_LOOPS_ON;
2455                         }
2456                         EventReleased.at(EVENT_ONE_DOWN) = true;
2457                     }
2458                 }
2459                 else if (event.jaxis.value > Config.AnalogDeadZone)
2460                 {
2461                     if (event.jaxis.axis == 0)
2462                     {
2463                         if (IsEventOff(EVENT_PAGE_DOWN))
2464                         {
2465                             EventPressCount.at(EVENT_PAGE_DOWN) = EVENT_LOOPS_ON;
2466                         }
2467                         EventReleased.at(EVENT_PAGE_UP) = true;
2468                     }
2469
2470                     if (event.jaxis.axis == 1)
2471                     {
2472                         if (IsEventOff(EVENT_ONE_DOWN))
2473                         {
2474                             EventPressCount.at(EVENT_ONE_DOWN) = EVENT_LOOPS_ON;
2475                         }
2476                         EventReleased.at(EVENT_ONE_UP) = true;
2477                     }
2478                 }
2479                 else
2480                 {
2481                     EventReleased.at(EVENT_ONE_UP)      = true;
2482                     EventReleased.at(EVENT_ONE_DOWN)    = true;
2483                     EventReleased.at(EVENT_PAGE_UP)     = true;
2484                     EventReleased.at(EVENT_PAGE_DOWN)   = true;
2485                 }
2486                 break;
2487
2488             case SDL_MOUSEMOTION:
2489                 Mouse.x  = event.motion.x;
2490                 Mouse.y  = event.motion.y;
2491
2492                 for (index=0; index<RectEntries.size(); index++)
2493                 {
2494                     if (CheckRectCollision( &Mouse, &RectEntries.at(index) ))
2495                     {
2496                         // Determine item being hovered over by the pointer
2497                         newsel = DisplayList.at(Mode).first+index;
2498                         // Only need to refresh the names when a new entry is hovered
2499                         if (newsel != DisplayList.at(Mode).absolute)
2500                         {
2501                             DisplayList.at(Mode).absolute = newsel;
2502                             DisplayList.at(Mode).relative = index;
2503                             RefreshList = true;
2504                         }
2505                         break;
2506                     }
2507                 }
2508                 break;
2509
2510             case SDL_MOUSEBUTTONDOWN:
2511                 switch (event.button.button)
2512                 {
2513                     case SDL_BUTTON_LEFT:
2514                     case SDL_BUTTON_MIDDLE:
2515                     case SDL_BUTTON_RIGHT:;
2516                         Mouse.x  = event.button.x;
2517                         Mouse.y  = event.button.y;
2518                         for (index=0; index<RectButtonsLeft.size(); index++)
2519                         {
2520                             if (ButtonModesLeft.at(index)!=EVENT_NONE)
2521                             {
2522                                 if (CheckRectCollision( &Mouse, &RectButtonsLeft.at(index) ))
2523                                 {
2524                                     EventPressCount.at(ButtonModesLeft.at(index)) = EVENT_LOOPS_ON;
2525 #if defined(DEBUG)
2526                                     Log( "DEBUG LeftButton Active %d %d\n", ButtonModesLeft.at(index), index );
2527 #endif
2528                                 }
2529                             }
2530                         }
2531                         for (index=0; index<RectButtonsRight.size(); index++)
2532                         {
2533                             if (ButtonModesRight.at(index)!=EVENT_NONE)
2534                             {
2535                                 if (CheckRectCollision( &Mouse, &RectButtonsRight.at(index) ))
2536                                 {
2537                                     EventPressCount.at(ButtonModesRight.at(index)) = EVENT_LOOPS_ON;
2538 #if defined(DEBUG)
2539                                     Log( "DEBUG RightButton Active %d %d\n", ButtonModesRight.at(index), index );
2540 #endif
2541                                 }
2542                             }
2543                         }
2544                         break;
2545                     case SDL_BUTTON_WHEELUP:
2546                         EventPressCount.at(EVENT_ONE_UP) = EVENT_LOOPS_ON;
2547                         break;
2548                     case SDL_BUTTON_WHEELDOWN:
2549                         EventPressCount.at(EVENT_ONE_DOWN) = EVENT_LOOPS_ON;
2550                         break;
2551                     default:
2552                         break;
2553                 }
2554                 break;
2555             case SDL_MOUSEBUTTONUP:
2556                 switch (event.button.button)
2557                 {
2558                     case SDL_BUTTON_LEFT:
2559                     case SDL_BUTTON_MIDDLE:
2560                     case SDL_BUTTON_RIGHT:
2561                     case SDL_BUTTON_WHEELUP:
2562                     case SDL_BUTTON_WHEELDOWN:
2563                         Mouse.x  = event.button.x;
2564                         Mouse.y  = event.button.y;
2565
2566                         for (index=0; index<EventReleased.size(); index++)
2567                         {
2568                             EventReleased.at(index) = true;
2569                         }
2570 #if defined(DEBUG)
2571                         Log( "DEBUG Releasing all events\n" );
2572 #endif
2573                         break;
2574                     default:
2575                         break;
2576                 }
2577                 break;
2578             default:
2579                 break;
2580         }
2581     }
2582
2583     // List navigation
2584     if (IsEventOn(EVENT_ONE_UP)==true)
2585     {
2586         DisplayList.at(Mode).absolute--;
2587         DisplayList.at(Mode).relative--;
2588         RefreshList = true;
2589     }
2590     if (IsEventOn(EVENT_ONE_DOWN)==true)
2591     {
2592         DisplayList.at(Mode).absolute++;
2593         DisplayList.at(Mode).relative++;
2594         RefreshList = true;
2595     }
2596     if (IsEventOn(EVENT_PAGE_UP)==true)
2597     {
2598         DisplayList.at(Mode).absolute -= Config.MaxEntries;
2599         DisplayList.at(Mode).relative = 0;
2600         RefreshList = true;
2601     }
2602     if (IsEventOn(EVENT_PAGE_DOWN)==true)
2603     {
2604         DisplayList.at(Mode).absolute += Config.MaxEntries;
2605         DisplayList.at(Mode).relative = Config.MaxEntries-1;
2606         RefreshList = true;
2607     }
2608
2609     // Path navigation
2610     if (Mode == MODE_SELECT_ENTRY)
2611     {
2612         // Go up into a dir
2613         if (Rescan == false && IsEventOn(EVENT_DIR_UP) == true)
2614         {
2615             if (Config.UseZipSupport == 1 && Profile.ZipFile.length() > 0)
2616             {
2617                 ZipUp();
2618             }
2619             else
2620             {
2621                 if (Profile.LaunchableDirs == false)
2622                 {
2623                     DirectoryUp();
2624                 }
2625             }
2626         }
2627
2628         // Go down into a dir
2629         if (Rescan == false && ((IsEventOn(EVENT_DIR_DOWN) == true) || (IsEventOn(EVENT_SELECT) == true)))
2630         {
2631             if (ItemsEntry.size()>0)
2632             {
2633                 if (ItemsEntry.at(DisplayList.at(Mode).absolute).Type == TYPE_DIR)
2634                 {
2635                     if (Profile.LaunchableDirs == false)
2636                     {
2637                         DirectoryDown();
2638                     }
2639                 }
2640                 else if (Config.UseZipSupport == 1 && ItemsEntry.at(DisplayList.at(Mode).absolute).Type == TYPE_ZIP)
2641                 {
2642                     ZipDown();
2643                 }
2644             }
2645             else
2646             {
2647                 EventPressCount.at( EVENT_SELECT ) = EVENT_LOOPS_OFF;
2648             }
2649         }
2650     }
2651
2652     // Value configuration
2653     if (Mode == MODE_SELECT_VALUE)
2654     {
2655         SetOneEntryValue = false;
2656         SetAllEntryValue = false;
2657         if (IsEventOn(EVENT_SET_ONE) == true)
2658         {
2659             RefreshList         = true;
2660             SetOneEntryValue    = true;
2661         }
2662         if (IsEventOn(EVENT_SET_ALL) == true)
2663         {
2664             RefreshList         = true;
2665             SetAllEntryValue    = true;
2666         }
2667     }
2668
2669     if (IsEventOn(EVENT_ZIP_MODE) == true)
2670     {
2671         Redraw              = true;
2672         DrawState_ZipMode   = true;
2673         ExtractAllFiles = !ExtractAllFiles;
2674     }
2675
2676     return 0;
2677 }
2678