strip added smb:// shares of their user/pass when adding, and instead store that...
[vuplus_xbmc] / xbmc / input / ButtonTranslator.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "system.h"
23 #include "interfaces/Builtins.h"
24 #include "ButtonTranslator.h"
25 #include "utils/URIUtils.h"
26 #include "settings/Settings.h"
27 #include "guilib/Key.h"
28 #include "input/XBMC_keysym.h"
29 #include "input/XBMC_keytable.h"
30 #include "filesystem/File.h"
31 #include "filesystem/Directory.h"
32 #include "FileItem.h"
33 #include "utils/StringUtils.h"
34 #include "utils/log.h"
35 #include "utils/XBMCTinyXML.h"
36 #include "XBIRRemote.h"
37
38 #if defined(TARGET_WINDOWS)
39 #include "input/windows/WINJoystick.h"
40 #elif defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
41 #include "SDLJoystick.h"
42 #endif
43
44 using namespace std;
45 using namespace XFILE;
46
47 typedef struct
48 {
49   const char* name;
50   int action;
51 } ActionMapping;
52
53 static const ActionMapping actions[] =
54 {
55         {"left"              , ACTION_MOVE_LEFT },
56         {"right"             , ACTION_MOVE_RIGHT},
57         {"up"                , ACTION_MOVE_UP   },
58         {"down"              , ACTION_MOVE_DOWN },
59         {"pageup"            , ACTION_PAGE_UP   },
60         {"pagedown"          , ACTION_PAGE_DOWN},
61         {"select"            , ACTION_SELECT_ITEM},
62         {"highlight"         , ACTION_HIGHLIGHT_ITEM},
63         {"parentdir"         , ACTION_NAV_BACK},       // backward compatibility
64         {"parentfolder"      , ACTION_PARENT_DIR},
65         {"back"              , ACTION_NAV_BACK},
66         {"previousmenu"      , ACTION_PREVIOUS_MENU},
67         {"info"              , ACTION_SHOW_INFO},
68         {"pause"             , ACTION_PAUSE},
69         {"stop"              , ACTION_STOP},
70         {"skipnext"          , ACTION_NEXT_ITEM},
71         {"skipprevious"      , ACTION_PREV_ITEM},
72         {"fullscreen"        , ACTION_SHOW_GUI},
73         {"aspectratio"       , ACTION_ASPECT_RATIO},
74         {"stepforward"       , ACTION_STEP_FORWARD},
75         {"stepback"          , ACTION_STEP_BACK},
76         {"bigstepforward"    , ACTION_BIG_STEP_FORWARD},
77         {"bigstepback"       , ACTION_BIG_STEP_BACK},
78         {"osd"               , ACTION_SHOW_OSD},
79         {"showsubtitles"     , ACTION_SHOW_SUBTITLES},
80         {"nextsubtitle"      , ACTION_NEXT_SUBTITLE},
81         {"codecinfo"         , ACTION_SHOW_CODEC},
82         {"nextpicture"       , ACTION_NEXT_PICTURE},
83         {"previouspicture"   , ACTION_PREV_PICTURE},
84         {"zoomout"           , ACTION_ZOOM_OUT},
85         {"zoomin"            , ACTION_ZOOM_IN},
86         {"playlist"          , ACTION_SHOW_PLAYLIST},
87         {"queue"             , ACTION_QUEUE_ITEM},
88         {"zoomnormal"        , ACTION_ZOOM_LEVEL_NORMAL},
89         {"zoomlevel1"        , ACTION_ZOOM_LEVEL_1},
90         {"zoomlevel2"        , ACTION_ZOOM_LEVEL_2},
91         {"zoomlevel3"        , ACTION_ZOOM_LEVEL_3},
92         {"zoomlevel4"        , ACTION_ZOOM_LEVEL_4},
93         {"zoomlevel5"        , ACTION_ZOOM_LEVEL_5},
94         {"zoomlevel6"        , ACTION_ZOOM_LEVEL_6},
95         {"zoomlevel7"        , ACTION_ZOOM_LEVEL_7},
96         {"zoomlevel8"        , ACTION_ZOOM_LEVEL_8},
97         {"zoomlevel9"        , ACTION_ZOOM_LEVEL_9},
98         {"nextcalibration"   , ACTION_CALIBRATE_SWAP_ARROWS},
99         {"resetcalibration"  , ACTION_CALIBRATE_RESET},
100         {"analogmove"        , ACTION_ANALOG_MOVE},
101         {"rotate"            , ACTION_ROTATE_PICTURE},
102         {"close"             , ACTION_NAV_BACK}, // backwards compatibility
103         {"subtitledelayminus", ACTION_SUBTITLE_DELAY_MIN},
104         {"subtitledelay"     , ACTION_SUBTITLE_DELAY},
105         {"subtitledelayplus" , ACTION_SUBTITLE_DELAY_PLUS},
106         {"audiodelayminus"   , ACTION_AUDIO_DELAY_MIN},
107         {"audiodelay"        , ACTION_AUDIO_DELAY},
108         {"audiodelayplus"    , ACTION_AUDIO_DELAY_PLUS},
109         {"subtitleshiftup"   , ACTION_SUBTITLE_VSHIFT_UP},
110         {"subtitleshiftdown" , ACTION_SUBTITLE_VSHIFT_DOWN},
111         {"subtitlealign"     , ACTION_SUBTITLE_ALIGN},
112         {"audionextlanguage" , ACTION_AUDIO_NEXT_LANGUAGE},
113         {"verticalshiftup"   , ACTION_VSHIFT_UP},
114         {"verticalshiftdown" , ACTION_VSHIFT_DOWN},
115         {"nextresolution"    , ACTION_CHANGE_RESOLUTION},
116         {"audiotoggledigital", ACTION_TOGGLE_DIGITAL_ANALOG},
117         {"number0"           , REMOTE_0},
118         {"number1"           , REMOTE_1},
119         {"number2"           , REMOTE_2},
120         {"number3"           , REMOTE_3},
121         {"number4"           , REMOTE_4},
122         {"number5"           , REMOTE_5},
123         {"number6"           , REMOTE_6},
124         {"number7"           , REMOTE_7},
125         {"number8"           , REMOTE_8},
126         {"number9"           , REMOTE_9},
127         {"osdleft"           , ACTION_OSD_SHOW_LEFT},
128         {"osdright"          , ACTION_OSD_SHOW_RIGHT},
129         {"osdup"             , ACTION_OSD_SHOW_UP},
130         {"osddown"           , ACTION_OSD_SHOW_DOWN},
131         {"osdselect"         , ACTION_OSD_SHOW_SELECT},
132         {"osdvalueplus"      , ACTION_OSD_SHOW_VALUE_PLUS},
133         {"osdvalueminus"     , ACTION_OSD_SHOW_VALUE_MIN},
134         {"smallstepback"     , ACTION_SMALL_STEP_BACK},
135         {"fastforward"       , ACTION_PLAYER_FORWARD},
136         {"rewind"            , ACTION_PLAYER_REWIND},
137         {"play"              , ACTION_PLAYER_PLAY},
138         {"playpause"         , ACTION_PLAYER_PLAYPAUSE},
139         {"delete"            , ACTION_DELETE_ITEM},
140         {"copy"              , ACTION_COPY_ITEM},
141         {"move"              , ACTION_MOVE_ITEM},
142         {"mplayerosd"        , ACTION_SHOW_MPLAYER_OSD},
143         {"hidesubmenu"       , ACTION_OSD_HIDESUBMENU},
144         {"screenshot"        , ACTION_TAKE_SCREENSHOT},
145         {"rename"            , ACTION_RENAME_ITEM},
146         {"togglewatched"     , ACTION_TOGGLE_WATCHED},
147         {"scanitem"          , ACTION_SCAN_ITEM},
148         {"reloadkeymaps"     , ACTION_RELOAD_KEYMAPS},
149         {"volumeup"          , ACTION_VOLUME_UP},
150         {"volumedown"        , ACTION_VOLUME_DOWN},
151         {"mute"              , ACTION_MUTE},
152         {"backspace"         , ACTION_BACKSPACE},
153         {"scrollup"          , ACTION_SCROLL_UP},
154         {"scrolldown"        , ACTION_SCROLL_DOWN},
155         {"analogfastforward" , ACTION_ANALOG_FORWARD},
156         {"analogrewind"      , ACTION_ANALOG_REWIND},
157         {"moveitemup"        , ACTION_MOVE_ITEM_UP},
158         {"moveitemdown"      , ACTION_MOVE_ITEM_DOWN},
159         {"contextmenu"       , ACTION_CONTEXT_MENU},
160         {"shift"             , ACTION_SHIFT},
161         {"symbols"           , ACTION_SYMBOLS},
162         {"cursorleft"        , ACTION_CURSOR_LEFT},
163         {"cursorright"       , ACTION_CURSOR_RIGHT},
164         {"showtime"          , ACTION_SHOW_OSD_TIME},
165         {"analogseekforward" , ACTION_ANALOG_SEEK_FORWARD},
166         {"analogseekback"    , ACTION_ANALOG_SEEK_BACK},
167         {"showpreset"        , ACTION_VIS_PRESET_SHOW},
168         {"presetlist"        , ACTION_VIS_PRESET_LIST},
169         {"nextpreset"        , ACTION_VIS_PRESET_NEXT},
170         {"previouspreset"    , ACTION_VIS_PRESET_PREV},
171         {"lockpreset"        , ACTION_VIS_PRESET_LOCK},
172         {"randompreset"      , ACTION_VIS_PRESET_RANDOM},
173         {"increasevisrating" , ACTION_VIS_RATE_PRESET_PLUS},
174         {"decreasevisrating" , ACTION_VIS_RATE_PRESET_MINUS},
175         {"showvideomenu"     , ACTION_SHOW_VIDEOMENU},
176         {"enter"             , ACTION_ENTER},
177         {"increaserating"    , ACTION_INCREASE_RATING},
178         {"decreaserating"    , ACTION_DECREASE_RATING},
179         {"togglefullscreen"  , ACTION_TOGGLE_FULLSCREEN},
180         {"nextscene"         , ACTION_NEXT_SCENE},
181         {"previousscene"     , ACTION_PREV_SCENE},
182         {"nextletter"        , ACTION_NEXT_LETTER},
183         {"prevletter"        , ACTION_PREV_LETTER},
184         {"jumpsms2"          , ACTION_JUMP_SMS2},
185         {"jumpsms3"          , ACTION_JUMP_SMS3},
186         {"jumpsms4"          , ACTION_JUMP_SMS4},
187         {"jumpsms5"          , ACTION_JUMP_SMS5},
188         {"jumpsms6"          , ACTION_JUMP_SMS6},
189         {"jumpsms7"          , ACTION_JUMP_SMS7},
190         {"jumpsms8"          , ACTION_JUMP_SMS8},
191         {"jumpsms9"          , ACTION_JUMP_SMS9},
192         {"filterclear"       , ACTION_FILTER_CLEAR},
193         {"filtersms2"        , ACTION_FILTER_SMS2},
194         {"filtersms3"        , ACTION_FILTER_SMS3},
195         {"filtersms4"        , ACTION_FILTER_SMS4},
196         {"filtersms5"        , ACTION_FILTER_SMS5},
197         {"filtersms6"        , ACTION_FILTER_SMS6},
198         {"filtersms7"        , ACTION_FILTER_SMS7},
199         {"filtersms8"        , ACTION_FILTER_SMS8},
200         {"filtersms9"        , ACTION_FILTER_SMS9},
201         {"firstpage"         , ACTION_FIRST_PAGE},
202         {"lastpage"          , ACTION_LAST_PAGE},
203         {"guiprofile"        , ACTION_GUIPROFILE_BEGIN},
204         {"red"               , ACTION_TELETEXT_RED},
205         {"green"             , ACTION_TELETEXT_GREEN},
206         {"yellow"            , ACTION_TELETEXT_YELLOW},
207         {"blue"              , ACTION_TELETEXT_BLUE},
208         {"increasepar"       , ACTION_INCREASE_PAR},
209         {"decreasepar"       , ACTION_DECREASE_PAR},
210         {"volampup"          , ACTION_VOLAMP_UP},
211         {"volampdown"        , ACTION_VOLAMP_DOWN},
212
213         // Mouse actions
214         {"leftclick"         , ACTION_MOUSE_LEFT_CLICK},
215         {"rightclick"        , ACTION_MOUSE_RIGHT_CLICK},
216         {"middleclick"       , ACTION_MOUSE_MIDDLE_CLICK},
217         {"doubleclick"       , ACTION_MOUSE_DOUBLE_CLICK},
218         {"wheelup"           , ACTION_MOUSE_WHEEL_UP},
219         {"wheeldown"         , ACTION_MOUSE_WHEEL_DOWN},
220         {"mousedrag"         , ACTION_MOUSE_DRAG},
221         {"mousemove"         , ACTION_MOUSE_MOVE},
222
223         // Do nothing action
224         { "noop"             , ACTION_NOOP}
225 };
226
227 static const ActionMapping windows[] =
228        {{"home"                     , WINDOW_HOME},
229         {"programs"                 , WINDOW_PROGRAMS},
230         {"pictures"                 , WINDOW_PICTURES},
231         {"filemanager"              , WINDOW_FILES},
232         {"files"                    , WINDOW_FILES}, // backward compat
233         {"settings"                 , WINDOW_SETTINGS_MENU},
234         {"music"                    , WINDOW_MUSIC},
235         {"video"                    , WINDOW_VIDEOS},
236         {"videos"                   , WINDOW_VIDEO_NAV},
237         {"systeminfo"               , WINDOW_SYSTEM_INFORMATION},
238         {"testpattern"              , WINDOW_TEST_PATTERN},
239         {"screencalibration"        , WINDOW_SCREEN_CALIBRATION},
240         {"guicalibration"           , WINDOW_SCREEN_CALIBRATION}, // backward compat
241         {"picturessettings"         , WINDOW_SETTINGS_MYPICTURES},
242         {"programssettings"         , WINDOW_SETTINGS_MYPROGRAMS},
243         {"weathersettings"          , WINDOW_SETTINGS_MYWEATHER},
244         {"musicsettings"            , WINDOW_SETTINGS_MYMUSIC},
245         {"systemsettings"           , WINDOW_SETTINGS_SYSTEM},
246         {"videossettings"           , WINDOW_SETTINGS_MYVIDEOS},
247         {"networksettings"          , WINDOW_SETTINGS_SERVICE}, // backward compat
248         {"servicesettings"          , WINDOW_SETTINGS_SERVICE},
249         {"appearancesettings"       , WINDOW_SETTINGS_APPEARANCE},
250         {"scripts"                  , WINDOW_PROGRAMS}, // backward compat
251         {"videofiles"               , WINDOW_VIDEO_FILES},
252         {"videolibrary"             , WINDOW_VIDEO_NAV},
253         {"videoplaylist"            , WINDOW_VIDEO_PLAYLIST},
254         {"loginscreen"              , WINDOW_LOGIN_SCREEN},
255         {"profiles"                 , WINDOW_SETTINGS_PROFILES},
256         {"addonbrowser"             , WINDOW_ADDON_BROWSER},
257         {"yesnodialog"              , WINDOW_DIALOG_YES_NO},
258         {"progressdialog"           , WINDOW_DIALOG_PROGRESS},
259         {"virtualkeyboard"          , WINDOW_DIALOG_KEYBOARD},
260         {"volumebar"                , WINDOW_DIALOG_VOLUME_BAR},
261         {"submenu"                  , WINDOW_DIALOG_SUB_MENU},
262         {"favourites"               , WINDOW_DIALOG_FAVOURITES},
263         {"contextmenu"              , WINDOW_DIALOG_CONTEXT_MENU},
264         {"infodialog"               , WINDOW_DIALOG_KAI_TOAST},
265         {"numericinput"             , WINDOW_DIALOG_NUMERIC},
266         {"gamepadinput"             , WINDOW_DIALOG_GAMEPAD},
267         {"shutdownmenu"             , WINDOW_DIALOG_BUTTON_MENU},
268         {"musicscan"                , WINDOW_DIALOG_MUSIC_SCAN},
269         {"mutebug"                  , WINDOW_DIALOG_MUTE_BUG},
270         {"playercontrols"           , WINDOW_DIALOG_PLAYER_CONTROLS},
271         {"seekbar"                  , WINDOW_DIALOG_SEEK_BAR},
272         {"musicosd"                 , WINDOW_DIALOG_MUSIC_OSD},
273         {"addonsettings"            , WINDOW_DIALOG_ADDON_SETTINGS},
274         {"visualisationsettings"    , WINDOW_DIALOG_ADDON_SETTINGS}, // backward compat
275         {"visualisationpresetlist"  , WINDOW_DIALOG_VIS_PRESET_LIST},
276         {"osdvideosettings"         , WINDOW_DIALOG_VIDEO_OSD_SETTINGS},
277         {"osdaudiosettings"         , WINDOW_DIALOG_AUDIO_OSD_SETTINGS},
278         {"videobookmarks"           , WINDOW_DIALOG_VIDEO_BOOKMARKS},
279         {"filebrowser"              , WINDOW_DIALOG_FILE_BROWSER},
280         {"networksetup"             , WINDOW_DIALOG_NETWORK_SETUP},
281         {"mediasource"              , WINDOW_DIALOG_MEDIA_SOURCE},
282         {"profilesettings"          , WINDOW_DIALOG_PROFILE_SETTINGS},
283         {"locksettings"             , WINDOW_DIALOG_LOCK_SETTINGS},
284         {"contentsettings"          , WINDOW_DIALOG_CONTENT_SETTINGS},
285         {"videoscan"                , WINDOW_DIALOG_VIDEO_SCAN},
286         {"favourites"               , WINDOW_DIALOG_FAVOURITES},
287         {"songinformation"          , WINDOW_DIALOG_SONG_INFO},
288         {"smartplaylisteditor"      , WINDOW_DIALOG_SMART_PLAYLIST_EDITOR},
289         {"smartplaylistrule"        , WINDOW_DIALOG_SMART_PLAYLIST_RULE},
290         {"busydialog"               , WINDOW_DIALOG_BUSY},
291         {"pictureinfo"              , WINDOW_DIALOG_PICTURE_INFO},
292         {"accesspoints"             , WINDOW_DIALOG_ACCESS_POINTS},
293         {"fullscreeninfo"           , WINDOW_DIALOG_FULLSCREEN_INFO},
294         {"karaokeselector"          , WINDOW_DIALOG_KARAOKE_SONGSELECT},
295         {"karaokelargeselector"     , WINDOW_DIALOG_KARAOKE_SELECTOR},
296         {"sliderdialog"             , WINDOW_DIALOG_SLIDER},
297         {"addoninformation"         , WINDOW_DIALOG_ADDON_INFO},
298         {"musicplaylist"            , WINDOW_MUSIC_PLAYLIST},
299         {"musicfiles"               , WINDOW_MUSIC_FILES},
300         {"musiclibrary"             , WINDOW_MUSIC_NAV},
301         {"musicplaylisteditor"      , WINDOW_MUSIC_PLAYLIST_EDITOR},
302         {"teletext"                 , WINDOW_DIALOG_OSD_TELETEXT},
303         {"selectdialog"             , WINDOW_DIALOG_SELECT},
304         {"musicinformation"         , WINDOW_DIALOG_MUSIC_INFO},
305         {"okdialog"                 , WINDOW_DIALOG_OK},
306         {"movieinformation"         , WINDOW_DIALOG_VIDEO_INFO},
307         {"textviewer"               , WINDOW_DIALOG_TEXT_VIEWER},
308         {"fullscreenvideo"          , WINDOW_FULLSCREEN_VIDEO},
309         {"visualisation"            , WINDOW_VISUALISATION},
310         {"slideshow"                , WINDOW_SLIDESHOW},
311         {"filestackingdialog"       , WINDOW_DIALOG_FILESTACKING},
312         {"karaoke"                  , WINDOW_KARAOKELYRICS},
313         {"weather"                  , WINDOW_WEATHER},
314         {"screensaver"              , WINDOW_SCREENSAVER},
315         {"videoosd"                 , WINDOW_DIALOG_VIDEO_OSD},
316         {"videomenu"                , WINDOW_VIDEO_MENU},
317         {"videotimeseek"            , WINDOW_VIDEO_TIME_SEEK},
318         {"musicoverlay"             , WINDOW_DIALOG_MUSIC_OVERLAY},
319         {"videooverlay"             , WINDOW_DIALOG_VIDEO_OVERLAY},
320         {"startwindow"              , WINDOW_START},
321         {"startup"                  , WINDOW_STARTUP_ANIM},
322         {"peripherals"              , WINDOW_DIALOG_PERIPHERAL_MANAGER},
323         {"peripheralsettings"       , WINDOW_DIALOG_PERIPHERAL_SETTINGS}};
324
325 static const ActionMapping mousecommands[] =
326 {
327   { "leftclick",   ACTION_MOUSE_LEFT_CLICK },
328   { "rightclick",  ACTION_MOUSE_RIGHT_CLICK },
329   { "middleclick", ACTION_MOUSE_MIDDLE_CLICK },
330   { "doubleclick", ACTION_MOUSE_DOUBLE_CLICK },
331   { "wheelup",     ACTION_MOUSE_WHEEL_UP },
332   { "wheeldown",   ACTION_MOUSE_WHEEL_DOWN },
333   { "mousedrag",   ACTION_MOUSE_DRAG },
334   { "mousemove",   ACTION_MOUSE_MOVE }
335 };
336
337 #ifdef WIN32
338 static const ActionMapping appcommands[] =
339 {
340   { "browser_back",        APPCOMMAND_BROWSER_BACKWARD },
341   { "browser_forward",     APPCOMMAND_BROWSER_FORWARD },
342   { "browser_refresh",     APPCOMMAND_BROWSER_REFRESH },
343   { "browser_stop",        APPCOMMAND_BROWSER_STOP },
344   { "browser_search",      APPCOMMAND_BROWSER_SEARCH },
345   { "browser_favorites",   APPCOMMAND_BROWSER_FAVORITES },
346   { "browser_home",        APPCOMMAND_BROWSER_HOME },
347   { "volume_mute",         APPCOMMAND_VOLUME_MUTE },
348   { "volume_down",         APPCOMMAND_VOLUME_DOWN },
349   { "volume_up",           APPCOMMAND_VOLUME_UP },
350   { "next_track",          APPCOMMAND_MEDIA_NEXTTRACK },
351   { "prev_track",          APPCOMMAND_MEDIA_PREVIOUSTRACK },
352   { "stop",                APPCOMMAND_MEDIA_STOP },
353   { "play_pause",          APPCOMMAND_MEDIA_PLAY_PAUSE },
354   { "launch_mail",         APPCOMMAND_LAUNCH_MAIL },
355   { "launch_media_select", APPCOMMAND_LAUNCH_MEDIA_SELECT },
356   { "launch_app1",         APPCOMMAND_LAUNCH_APP1 },
357   { "launch_app2",         APPCOMMAND_LAUNCH_APP2 },
358   { "play",                APPCOMMAND_MEDIA_PLAY },
359   { "pause",               APPCOMMAND_MEDIA_PAUSE },
360   { "fastforward",         APPCOMMAND_MEDIA_FAST_FORWARD },
361   { "rewind",              APPCOMMAND_MEDIA_REWIND },
362   { "channelup",           APPCOMMAND_MEDIA_CHANNEL_UP },
363   { "channeldown",         APPCOMMAND_MEDIA_CHANNEL_DOWN }
364 };
365 #endif
366
367 CButtonTranslator& CButtonTranslator::GetInstance()
368 {
369   static CButtonTranslator sl_instance;
370   return sl_instance;
371 }
372
373 CButtonTranslator::CButtonTranslator()
374 {
375   m_deviceList.clear();
376   m_Loaded = false;
377 }
378
379 CButtonTranslator::~CButtonTranslator()
380 {
381 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
382   vector<lircButtonMap*> maps;
383   for (map<CStdString,lircButtonMap*>::iterator it  = lircRemotesMap.begin();
384                                                 it != lircRemotesMap.end();++it)
385     maps.push_back(it->second);
386   sort(maps.begin(),maps.end());
387   vector<lircButtonMap*>::iterator itend = unique(maps.begin(),maps.end());
388   for (vector<lircButtonMap*>::iterator it = maps.begin(); it != itend;++it)
389     delete *it;
390 #endif
391 }
392
393 // Add the supplied device name to the list of connected devices
394 void CButtonTranslator::AddDevice(CStdString& strDevice)
395 {
396   // Only add the device if it isn't already in the list
397   std::list<CStdString>::iterator it;
398   for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
399     if (*it == strDevice)
400       return;
401
402   // Add the device
403   m_deviceList.push_back(strDevice);
404   m_deviceList.sort();
405
406   // New device added so reload the key mappings
407   Load();
408 }
409
410 void CButtonTranslator::RemoveDevice(CStdString& strDevice)
411 {
412   // Find the device
413   std::list<CStdString>::iterator it;
414   for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
415     if (*it == strDevice)
416       break;
417   if (it == m_deviceList.end())
418     return;
419
420   // Remove the device
421   m_deviceList.remove(strDevice);
422
423   // Device removed so reload the key mappings
424   Load();
425 }
426
427 bool CButtonTranslator::Load(bool AlwaysLoad)
428 {
429   m_translatorMap.clear();
430
431   // Directories to search for keymaps. They're applied in this order,
432   // so keymaps in profile/keymaps/ override e.g. system/keymaps
433   static const char* DIRS_TO_CHECK[] = {
434     "special://xbmc/system/keymaps/",
435     "special://masterprofile/keymaps/",
436     "special://profile/keymaps/"
437   };
438   bool success = false;
439
440   for (unsigned int dirIndex = 0; dirIndex < sizeof(DIRS_TO_CHECK)/sizeof(DIRS_TO_CHECK[0]); ++dirIndex)
441   {
442     if (XFILE::CDirectory::Exists(DIRS_TO_CHECK[dirIndex]))
443     {
444       CFileItemList files;
445       XFILE::CDirectory::GetDirectory(DIRS_TO_CHECK[dirIndex], files, ".xml");
446       // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
447       files.Sort(SORT_METHOD_FILE, SortOrderAscending);
448       for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
449       {
450         if (!files[fileIndex]->m_bIsFolder)
451           success |= LoadKeymap(files[fileIndex]->GetPath());
452       }
453
454       // Load mappings for any HID devices we have connected
455       std::list<CStdString>::iterator it;
456       for (it = m_deviceList.begin(); it != m_deviceList.end(); it++)
457       {
458         CStdString devicedir = DIRS_TO_CHECK[dirIndex];
459         devicedir.append(*it);
460         devicedir.append("/");
461         if( XFILE::CDirectory::Exists(devicedir) )
462         {
463           CFileItemList files;
464           XFILE::CDirectory::GetDirectory(devicedir, files, ".xml");
465           // Sort the list for filesystem based priorities, e.g. 01-keymap.xml, 02-keymap-overrides.xml
466           files.Sort(SORT_METHOD_FILE, SortOrderAscending);
467           for(int fileIndex = 0; fileIndex<files.Size(); ++fileIndex)
468           {
469             if (!files[fileIndex]->m_bIsFolder)
470               success |= LoadKeymap(files[fileIndex]->GetPath());
471           }
472         }
473       }
474     }
475   }
476
477   if (!success)
478   {
479     CLog::Log(LOGERROR, "Error loading keymaps from: %s or %s or %s", DIRS_TO_CHECK[0], DIRS_TO_CHECK[1], DIRS_TO_CHECK[2]);
480     return false;
481   }
482
483 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
484 #ifdef _LINUX
485 #define REMOTEMAP "Lircmap.xml"
486 #else
487 #define REMOTEMAP "IRSSmap.xml"
488 #endif
489   CStdString lircmapPath;
490   URIUtils::AddFileToFolder("special://xbmc/system/", REMOTEMAP, lircmapPath);
491   lircRemotesMap.clear();
492   if(CFile::Exists(lircmapPath))
493     success |= LoadLircMap(lircmapPath);
494   else
495     CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no system %s found, skipping", REMOTEMAP);
496
497   lircmapPath = g_settings.GetUserDataItem(REMOTEMAP);
498   if(CFile::Exists(lircmapPath))
499     success |= LoadLircMap(lircmapPath);
500   else
501     CLog::Log(LOGDEBUG, "CButtonTranslator::Load - no userdata %s found, skipping", REMOTEMAP);
502
503   if (!success)
504     CLog::Log(LOGERROR, "CButtonTranslator::Load - unable to load remote map %s", REMOTEMAP);
505   // don't return false - it is to only indicate a fatal error (which this is not)
506 #endif
507
508   // Done!
509   m_Loaded = true;
510   return true;
511 }
512
513 bool CButtonTranslator::LoadKeymap(const CStdString &keymapPath)
514 {
515   CXBMCTinyXML xmlDoc;
516
517   CLog::Log(LOGINFO, "Loading %s", keymapPath.c_str());
518   if (!xmlDoc.LoadFile(keymapPath))
519   {
520     CLog::Log(LOGERROR, "Error loading keymap: %s, Line %d\n%s", keymapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
521     return false;
522   }
523   TiXmlElement* pRoot = xmlDoc.RootElement();
524   CStdString strValue = pRoot->Value();
525   if ( strValue != "keymap")
526   {
527     CLog::Log(LOGERROR, "%s Doesn't contain <keymap>", keymapPath.c_str());
528     return false;
529   }
530   // run through our window groups
531   TiXmlNode* pWindow = pRoot->FirstChild();
532   while (pWindow)
533   {
534     if (pWindow->Type() == TiXmlNode::TINYXML_ELEMENT)
535     {
536       int windowID = WINDOW_INVALID;
537       const char *szWindow = pWindow->Value();
538       if (szWindow)
539       {
540         if (strcmpi(szWindow, "global") == 0)
541           windowID = -1;
542         else
543           windowID = TranslateWindow(szWindow);
544       }
545       MapWindowActions(pWindow, windowID);
546     }
547     pWindow = pWindow->NextSibling();
548   }
549
550   return true;
551 }
552
553 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
554 bool CButtonTranslator::LoadLircMap(const CStdString &lircmapPath)
555 {
556 #ifdef _LINUX
557 #define REMOTEMAPTAG "lircmap"
558 #else
559 #define REMOTEMAPTAG "irssmap"
560 #endif
561   // load our xml file, and fill up our mapping tables
562   CXBMCTinyXML xmlDoc;
563
564   // Load the config file
565   CLog::Log(LOGINFO, "Loading %s", lircmapPath.c_str());
566   if (!xmlDoc.LoadFile(lircmapPath))
567   {
568     CLog::Log(LOGERROR, "%s, Line %d\n%s", lircmapPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
569     return false; // This is so people who don't have the file won't fail, just warn
570   }
571
572   TiXmlElement* pRoot = xmlDoc.RootElement();
573   CStdString strValue = pRoot->Value();
574   if (strValue != REMOTEMAPTAG)
575   {
576     CLog::Log(LOGERROR, "%sl Doesn't contain <%s>", lircmapPath.c_str(), REMOTEMAPTAG);
577     return false;
578   }
579
580   // run through our window groups
581   TiXmlNode* pRemote = pRoot->FirstChild();
582   while (pRemote)
583   {
584     if (pRemote->Type() == TiXmlNode::TINYXML_ELEMENT)
585     {
586       const char *szRemote = pRemote->Value();
587       if (szRemote)
588       {
589         TiXmlAttribute* pAttr = pRemote->ToElement()->FirstAttribute();
590         const char* szDeviceName = pAttr->Value();
591         MapRemote(pRemote, szDeviceName);
592       }
593     }
594     pRemote = pRemote->NextSibling();
595   }
596
597   return true;
598 }
599
600 void CButtonTranslator::MapRemote(TiXmlNode *pRemote, const char* szDevice)
601 {
602   CLog::Log(LOGINFO, "* Adding remote mapping for device '%s'", szDevice);
603   vector<string> RemoteNames;
604   map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
605   if (it == lircRemotesMap.end())
606     lircRemotesMap[szDevice] = new lircButtonMap;
607   lircButtonMap& buttons = *lircRemotesMap[szDevice];
608
609   TiXmlElement *pButton = pRemote->FirstChildElement();
610   while (pButton)
611   {
612     if (strcmpi(pButton->Value(), "altname")==0)
613       RemoteNames.push_back(string(pButton->GetText()));
614     else
615     {
616       if (pButton->FirstChild() && pButton->FirstChild()->Value())
617         buttons[pButton->FirstChild()->Value()] = pButton->Value();
618     }
619
620     pButton = pButton->NextSiblingElement();
621   }
622   for (vector<string>::iterator it  = RemoteNames.begin();
623                                 it != RemoteNames.end();++it)
624   {
625     CLog::Log(LOGINFO, "* Linking remote mapping for '%s' to '%s'", szDevice, it->c_str());
626     lircRemotesMap[*it] = &buttons;
627   }
628 }
629
630 int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const char *szButton)
631 {
632   // Find the device
633   map<CStdString, lircButtonMap*>::iterator it = lircRemotesMap.find(szDevice);
634   if (it == lircRemotesMap.end())
635     return 0;
636
637   // Find the button
638   lircButtonMap::iterator it2 = (*it).second->find(szButton);
639   if (it2 == (*it).second->end())
640     return 0;
641
642   // Convert the button to code
643   if (strnicmp((*it2).second.c_str(), "obc", 3) == 0)
644     return TranslateUniversalRemoteString((*it2).second.c_str());
645
646   return TranslateRemoteString((*it2).second.c_str());
647 }
648 #endif
649
650 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
651 void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick)
652 {
653   string joyname = "_xbmc_"; // default global map name
654   vector<string> joynames;
655   map<int, string> buttonMap;
656   map<int, string> axisMap;
657   map<int, string> hatMap;
658
659   TiXmlElement *pJoy = pJoystick->ToElement();
660   if (pJoy && pJoy->Attribute("name"))
661     joyname = pJoy->Attribute("name");
662   else
663     CLog::Log(LOGNOTICE, "No Joystick name specified, loading default map");
664
665   joynames.push_back(joyname);
666
667   // parse map
668   TiXmlElement *pButton = pJoystick->FirstChildElement();
669   int id = 0;
670   //char* szId;
671   const char* szType;
672   const char *szAction;
673   while (pButton)
674   {
675     szType = pButton->Value();
676     szAction = pButton->GetText();
677     if (szAction == NULL)
678       szAction = "";
679     if (szType)
680     {
681       if ((pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS) && id>=0 && id<=256)
682       {
683         if (strcmpi(szType, "button")==0)
684         {
685           buttonMap[id] = string(szAction);
686         }
687         else if (strcmpi(szType, "axis")==0)
688         {
689           int limit = 0;
690           if (pButton->QueryIntAttribute("limit", &limit) == TIXML_SUCCESS)
691           {
692             if (limit==-1)
693               axisMap[-id] = string(szAction);
694             else if (limit==1)
695               axisMap[id] = string(szAction);
696             else if (limit==0)
697               axisMap[id|0xFFFF0000] = string(szAction);
698             else
699             {
700               axisMap[id] = string(szAction);
701               axisMap[-id] = string(szAction);
702               CLog::Log(LOGERROR, "Error in joystick map, invalid limit specified %d for axis %d", limit, id);
703             }
704           }
705           else
706           {
707             axisMap[id] = string(szAction);
708             axisMap[-id] = string(szAction);
709           }
710         }
711         else if (strcmpi(szType, "hat")==0)
712         {
713           string position;
714           if (pButton->QueryValueAttribute("position", &position) == TIXML_SUCCESS)
715           {
716             uint32_t hatID = id|0xFFF00000;
717             if (position.compare("up") == 0)
718               hatMap[(JACTIVE_HAT_UP<<16)|hatID] = string(szAction);
719             else if (position.compare("down") == 0)
720               hatMap[(JACTIVE_HAT_DOWN<<16)|hatID] = string(szAction);
721             else if (position.compare("right") == 0)
722               hatMap[(JACTIVE_HAT_RIGHT<<16)|hatID] = string(szAction);
723             else if (position.compare("left") == 0)
724               hatMap[(JACTIVE_HAT_LEFT<<16)|hatID] = string(szAction);
725             else
726               CLog::Log(LOGERROR, "Error in joystick map, invalid position specified %s for axis %d", position.c_str(), id);
727           }
728         }
729         else
730           CLog::Log(LOGERROR, "Error reading joystick map element, unknown button type: %s", szType);
731       }
732       else if (strcmpi(szType, "altname")==0)
733         joynames.push_back(string(szAction));
734       else
735         CLog::Log(LOGERROR, "Error reading joystick map element, Invalid id: %d", id);
736     }
737     else
738       CLog::Log(LOGERROR, "Error reading joystick map element, skipping");
739
740     pButton = pButton->NextSiblingElement();
741   }
742   vector<string>::iterator it = joynames.begin();
743   while (it!=joynames.end())
744   {
745     m_joystickButtonMap[*it][windowID] = buttonMap;
746     m_joystickAxisMap[*it][windowID] = axisMap;
747     m_joystickHatMap[*it][windowID] = hatMap;
748 //    CLog::Log(LOGDEBUG, "Found Joystick map for window %d using %s", windowID, it->c_str());
749     it++;
750   }
751 }
752
753 bool CButtonTranslator::TranslateJoystickString(int window, const char* szDevice, int id, short inputType, int& action, CStdString& strAction, bool &fullrange)
754 {
755   bool found = false;
756
757   map<string, JoystickMap>::iterator it;
758   map<string, JoystickMap> *jmap;
759
760   fullrange = false;
761   if (inputType == JACTIVE_AXIS)
762     jmap = &m_joystickAxisMap;
763   else if (inputType == JACTIVE_BUTTON)
764     jmap = &m_joystickButtonMap;
765   else if (inputType == JACTIVE_HAT)
766         jmap = &m_joystickHatMap;
767   else
768   {
769     CLog::Log(LOGERROR, "Error reading joystick input type");
770     return false;
771   }
772
773   it = jmap->find(szDevice);
774   if (it==jmap->end())
775     return false;
776
777   JoystickMap wmap = it->second;
778   JoystickMap::iterator it2;
779   map<int, string> windowbmap;
780   map<int, string> globalbmap;
781   map<int, string>::iterator it3;
782
783   it2 = wmap.find(window);
784
785   // first try local window map
786   if (it2!=wmap.end())
787   {
788     windowbmap = it2->second;
789     it3 = windowbmap.find(id);
790     if (it3 != windowbmap.end())
791     {
792       strAction = (it3->second).c_str();
793       found = true;
794     }
795     it3 = windowbmap.find(abs(id)|0xFFFF0000);
796     if (it3 != windowbmap.end())
797     {
798       strAction = (it3->second).c_str();
799       found = true;
800       fullrange = true;
801     }
802     // Hats joystick
803     it3 = windowbmap.find(id|0xFFF00000);
804     if (it3 != windowbmap.end())
805     {
806       strAction = (it3->second).c_str();
807       found = true;
808     }
809   }
810
811   // if not found, try global map
812   if (!found)
813   {
814     it2 = wmap.find(-1);
815     if (it2 != wmap.end())
816     {
817       globalbmap = it2->second;
818       it3 = globalbmap.find(id);
819       if (it3 != globalbmap.end())
820       {
821         strAction = (it3->second).c_str();
822         found = true;
823       }
824       it3 = globalbmap.find(abs(id)|0xFFFF0000);
825       if (it3 != globalbmap.end())
826       {
827         strAction = (it3->second).c_str();
828         found = true;
829         fullrange = true;
830       }
831       it3 = globalbmap.find(id|0xFFF00000);
832       if (it3 != globalbmap.end())
833       {
834         strAction = (it3->second).c_str();
835         found = true;
836       }
837     }
838   }
839
840   // translated found action
841   if (found)
842     return TranslateActionString(strAction.c_str(), action);
843
844   return false;
845 }
846 #endif
847
848 CAction CButtonTranslator::GetAction(int window, const CKey &key, bool fallback)
849 {
850   CStdString strAction;
851   // try to get the action from the current window
852   int actionID = GetActionCode(window, key, strAction);
853   // if it's invalid, try to get it from the global map
854   if (actionID == 0 && fallback)
855     actionID = GetActionCode( -1, key, strAction);
856   // Now fill our action structure
857   CAction action(actionID, strAction, key);
858   return action;
859 }
860
861 int CButtonTranslator::GetActionCode(int window, const CKey &key, CStdString &strAction) const
862 {
863   uint32_t code = key.GetButtonCode();
864
865   map<int, buttonMap>::const_iterator it = m_translatorMap.find(window);
866   if (it == m_translatorMap.end())
867     return 0;
868   buttonMap::const_iterator it2 = (*it).second.find(code);
869   int action = 0;
870   while (it2 != (*it).second.end())
871   {
872     action = (*it2).second.id;
873     strAction = (*it2).second.strID;
874     it2 = (*it).second.end();
875   }
876 #ifdef _LINUX
877   // Some buttoncodes changed in Hardy
878   if (action == 0 && (code & KEY_VKEY) == KEY_VKEY && (code & 0x0F00))
879   {
880     CLog::Log(LOGDEBUG, "%s: Trying Hardy keycode for %#04x", __FUNCTION__, code);
881     code &= ~0x0F00;
882     buttonMap::const_iterator it2 = (*it).second.find(code);
883     while (it2 != (*it).second.end())
884     {
885       action = (*it2).second.id;
886       strAction = (*it2).second.strID;
887       it2 = (*it).second.end();
888     }
889   }
890 #endif
891   return action;
892 }
893
894 void CButtonTranslator::MapAction(uint32_t buttonCode, const char *szAction, buttonMap &map)
895 {
896   int action = ACTION_NONE;
897   if (!TranslateActionString(szAction, action) || !buttonCode)
898     return;   // no valid action, or an invalid buttoncode
899   // have a valid action, and a valid button - map it.
900   // check to see if we've already got this (button,action) pair defined
901   buttonMap::iterator it = map.find(buttonCode);
902   if (it == map.end() || (*it).second.id != action || (*it).second.strID != szAction)
903   {
904     // NOTE: This multimap is only being used as a normal map at this point (no support
905     //       for multiple actions per key)
906     if (it != map.end())
907       map.erase(it);
908     CButtonAction button;
909     button.id = action;
910     button.strID = szAction;
911     map.insert(pair<uint32_t, CButtonAction>(buttonCode, button));
912   }
913 }
914
915 bool CButtonTranslator::HasDeviceType(TiXmlNode *pWindow, CStdString type)
916 {
917   return pWindow->FirstChild(type) != NULL;
918 }
919
920 void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID)
921 {
922   if (!pWindow || windowID == WINDOW_INVALID) 
923     return;
924
925   TiXmlNode* pDevice;
926
927   const char* types[] = {"gamepad", "remote", "universalremote", "keyboard", "mouse", "appcommand", NULL};
928   for (int i = 0; types[i]; ++i)
929   {
930     CStdString type(types[i]);
931     if (HasDeviceType(pWindow, type))
932     {
933       buttonMap map;
934       std::map<int, buttonMap>::iterator it = m_translatorMap.find(windowID);
935       if (it != m_translatorMap.end())
936       {
937         map = it->second;
938         m_translatorMap.erase(it);
939       }
940
941       pDevice = pWindow->FirstChild(type);
942
943       TiXmlElement *pButton = pDevice->FirstChildElement();
944
945       while (pButton)
946       {
947         uint32_t buttonCode=0;
948         if (type == "gamepad")
949             buttonCode = TranslateGamepadString(pButton->Value());
950         else if (type == "remote")
951             buttonCode = TranslateRemoteString(pButton->Value());
952         else if (type == "universalremote")
953             buttonCode = TranslateUniversalRemoteString(pButton->Value());
954         else if (type == "keyboard")
955             buttonCode = TranslateKeyboardButton(pButton);
956         else if (type == "mouse")
957             buttonCode = TranslateMouseCommand(pButton->Value());
958         else if (type == "appcommand")
959             buttonCode = TranslateAppCommand(pButton->Value());
960
961         if (buttonCode && pButton->FirstChild())
962           MapAction(buttonCode, pButton->FirstChild()->Value(), map);
963         pButton = pButton->NextSiblingElement();
964       }
965
966       // add our map to our table
967       if (map.size() > 0)
968         m_translatorMap.insert(pair<int, buttonMap>( windowID, map));
969     }
970   }
971
972 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
973   if ((pDevice = pWindow->FirstChild("joystick")) != NULL)
974   {
975     // map joystick actions
976     while (pDevice)
977     {
978       MapJoystickActions(windowID, pDevice);
979       pDevice = pDevice->NextSibling("joystick");
980     }
981   }
982 #endif
983 }
984
985 bool CButtonTranslator::TranslateActionString(const char *szAction, int &action)
986 {
987   action = ACTION_NONE;
988   CStdString strAction = szAction;
989   strAction.ToLower();
990   if (CBuiltins::HasCommand(strAction)) 
991     action = ACTION_BUILT_IN_FUNCTION;
992
993   for (unsigned int index=0;index < sizeof(actions)/sizeof(actions[0]);++index)
994   {
995     if (strAction.Equals(actions[index].name))
996     {
997       action = actions[index].action;
998       break;
999     }
1000   }
1001
1002   if (action == ACTION_NONE)
1003   {
1004     CLog::Log(LOGERROR, "Keymapping error: no such action '%s' defined", strAction.c_str());
1005     return false;
1006   }
1007
1008   return true;
1009 }
1010
1011 CStdString CButtonTranslator::TranslateWindow(int windowID)
1012 {
1013   for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1014   {
1015     if (windows[index].action == windowID)
1016       return windows[index].name;
1017   }
1018   return "";
1019 }
1020
1021 int CButtonTranslator::TranslateWindow(const CStdString &window)
1022 {
1023   CStdString strWindow(window);
1024   if (strWindow.IsEmpty()) 
1025     return WINDOW_INVALID;
1026   strWindow.ToLower();
1027   // eliminate .xml
1028   if (strWindow.Mid(strWindow.GetLength() - 4) == ".xml" )
1029     strWindow = strWindow.Mid(0, strWindow.GetLength() - 4);
1030
1031   // window12345, for custom window to be keymapped
1032   if (strWindow.length() > 6 && strWindow.Left(6).Equals("window"))
1033     strWindow = strWindow.Mid(6);
1034   if (strWindow.Left(2) == "my")  // drop "my" prefix
1035     strWindow = strWindow.Mid(2);
1036   if (StringUtils::IsNaturalNumber(strWindow))
1037   {
1038     // allow a full window id or a delta id
1039     int iWindow = atoi(strWindow.c_str());
1040     if (iWindow > WINDOW_INVALID)
1041       return iWindow;
1042     return WINDOW_HOME + iWindow;
1043   }
1044
1045   // run through the window structure
1046   for (unsigned int index = 0; index < sizeof(windows) / sizeof(windows[0]); ++index)
1047   {
1048     if (strWindow.Equals(windows[index].name))
1049       return windows[index].action;
1050   }
1051
1052   CLog::Log(LOGERROR, "Window Translator: Can't find window %s", strWindow.c_str());
1053   return WINDOW_INVALID;
1054 }
1055
1056 uint32_t CButtonTranslator::TranslateGamepadString(const char *szButton)
1057 {
1058   if (!szButton) 
1059     return 0;
1060   uint32_t buttonCode = 0;
1061   CStdString strButton = szButton;
1062   strButton.ToLower();
1063   if (strButton.Equals("a")) buttonCode = KEY_BUTTON_A;
1064   else if (strButton.Equals("b")) buttonCode = KEY_BUTTON_B;
1065   else if (strButton.Equals("x")) buttonCode = KEY_BUTTON_X;
1066   else if (strButton.Equals("y")) buttonCode = KEY_BUTTON_Y;
1067   else if (strButton.Equals("white")) buttonCode = KEY_BUTTON_WHITE;
1068   else if (strButton.Equals("black")) buttonCode = KEY_BUTTON_BLACK;
1069   else if (strButton.Equals("start")) buttonCode = KEY_BUTTON_START;
1070   else if (strButton.Equals("back")) buttonCode = KEY_BUTTON_BACK;
1071   else if (strButton.Equals("leftthumbbutton")) buttonCode = KEY_BUTTON_LEFT_THUMB_BUTTON;
1072   else if (strButton.Equals("rightthumbbutton")) buttonCode = KEY_BUTTON_RIGHT_THUMB_BUTTON;
1073   else if (strButton.Equals("leftthumbstick")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK;
1074   else if (strButton.Equals("leftthumbstickup")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_UP;
1075   else if (strButton.Equals("leftthumbstickdown")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
1076   else if (strButton.Equals("leftthumbstickleft")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
1077   else if (strButton.Equals("leftthumbstickright")) buttonCode = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
1078   else if (strButton.Equals("rightthumbstick")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK;
1079   else if (strButton.Equals("rightthumbstickup")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
1080   else if (strButton.Equals("rightthumbstickdown")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
1081   else if (strButton.Equals("rightthumbstickleft")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
1082   else if (strButton.Equals("rightthumbstickright")) buttonCode = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
1083   else if (strButton.Equals("lefttrigger")) buttonCode = KEY_BUTTON_LEFT_TRIGGER;
1084   else if (strButton.Equals("righttrigger")) buttonCode = KEY_BUTTON_RIGHT_TRIGGER;
1085   else if (strButton.Equals("leftanalogtrigger")) buttonCode = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
1086   else if (strButton.Equals("rightanalogtrigger")) buttonCode = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
1087   else if (strButton.Equals("dpadleft")) buttonCode = KEY_BUTTON_DPAD_LEFT;
1088   else if (strButton.Equals("dpadright")) buttonCode = KEY_BUTTON_DPAD_RIGHT;
1089   else if (strButton.Equals("dpadup")) buttonCode = KEY_BUTTON_DPAD_UP;
1090   else if (strButton.Equals("dpaddown")) buttonCode = KEY_BUTTON_DPAD_DOWN;
1091   else CLog::Log(LOGERROR, "Gamepad Translator: Can't find button %s", strButton.c_str());
1092   return buttonCode;
1093 }
1094
1095 uint32_t CButtonTranslator::TranslateRemoteString(const char *szButton)
1096 {
1097   if (!szButton) 
1098     return 0;
1099   uint32_t buttonCode = 0;
1100   CStdString strButton = szButton;
1101   strButton.ToLower();
1102   if (strButton.Equals("left")) buttonCode = XINPUT_IR_REMOTE_LEFT;
1103   else if (strButton.Equals("right")) buttonCode = XINPUT_IR_REMOTE_RIGHT;
1104   else if (strButton.Equals("up")) buttonCode = XINPUT_IR_REMOTE_UP;
1105   else if (strButton.Equals("down")) buttonCode = XINPUT_IR_REMOTE_DOWN;
1106   else if (strButton.Equals("select")) buttonCode = XINPUT_IR_REMOTE_SELECT;
1107   else if (strButton.Equals("back")) buttonCode = XINPUT_IR_REMOTE_BACK;
1108   else if (strButton.Equals("menu")) buttonCode = XINPUT_IR_REMOTE_MENU;
1109   else if (strButton.Equals("info")) buttonCode = XINPUT_IR_REMOTE_INFO;
1110   else if (strButton.Equals("display")) buttonCode = XINPUT_IR_REMOTE_DISPLAY;
1111   else if (strButton.Equals("title")) buttonCode = XINPUT_IR_REMOTE_TITLE;
1112   else if (strButton.Equals("play")) buttonCode = XINPUT_IR_REMOTE_PLAY;
1113   else if (strButton.Equals("pause")) buttonCode = XINPUT_IR_REMOTE_PAUSE;
1114   else if (strButton.Equals("reverse")) buttonCode = XINPUT_IR_REMOTE_REVERSE;
1115   else if (strButton.Equals("forward")) buttonCode = XINPUT_IR_REMOTE_FORWARD;
1116   else if (strButton.Equals("skipplus")) buttonCode = XINPUT_IR_REMOTE_SKIP_PLUS;
1117   else if (strButton.Equals("skipminus")) buttonCode = XINPUT_IR_REMOTE_SKIP_MINUS;
1118   else if (strButton.Equals("stop")) buttonCode = XINPUT_IR_REMOTE_STOP;
1119   else if (strButton.Equals("zero")) buttonCode = XINPUT_IR_REMOTE_0;
1120   else if (strButton.Equals("one")) buttonCode = XINPUT_IR_REMOTE_1;
1121   else if (strButton.Equals("two")) buttonCode = XINPUT_IR_REMOTE_2;
1122   else if (strButton.Equals("three")) buttonCode = XINPUT_IR_REMOTE_3;
1123   else if (strButton.Equals("four")) buttonCode = XINPUT_IR_REMOTE_4;
1124   else if (strButton.Equals("five")) buttonCode = XINPUT_IR_REMOTE_5;
1125   else if (strButton.Equals("six")) buttonCode = XINPUT_IR_REMOTE_6;
1126   else if (strButton.Equals("seven")) buttonCode = XINPUT_IR_REMOTE_7;
1127   else if (strButton.Equals("eight")) buttonCode = XINPUT_IR_REMOTE_8;
1128   else if (strButton.Equals("nine")) buttonCode = XINPUT_IR_REMOTE_9;
1129   // additional keys from the media center extender for xbox remote
1130   else if (strButton.Equals("power")) buttonCode = XINPUT_IR_REMOTE_POWER;
1131   else if (strButton.Equals("mytv")) buttonCode = XINPUT_IR_REMOTE_MY_TV;
1132   else if (strButton.Equals("mymusic")) buttonCode = XINPUT_IR_REMOTE_MY_MUSIC;
1133   else if (strButton.Equals("mypictures")) buttonCode = XINPUT_IR_REMOTE_MY_PICTURES;
1134   else if (strButton.Equals("myvideo")) buttonCode = XINPUT_IR_REMOTE_MY_VIDEOS;
1135   else if (strButton.Equals("record")) buttonCode = XINPUT_IR_REMOTE_RECORD;
1136   else if (strButton.Equals("start")) buttonCode = XINPUT_IR_REMOTE_START;
1137   else if (strButton.Equals("volumeplus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_PLUS;
1138   else if (strButton.Equals("volumeminus")) buttonCode = XINPUT_IR_REMOTE_VOLUME_MINUS;
1139   else if (strButton.Equals("channelplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1140   else if (strButton.Equals("channelminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1141   else if (strButton.Equals("pageplus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_PLUS;
1142   else if (strButton.Equals("pageminus")) buttonCode = XINPUT_IR_REMOTE_CHANNEL_MINUS;
1143   else if (strButton.Equals("mute")) buttonCode = XINPUT_IR_REMOTE_MUTE;
1144   else if (strButton.Equals("recordedtv")) buttonCode = XINPUT_IR_REMOTE_RECORDED_TV;
1145   else if (strButton.Equals("guide")) buttonCode = XINPUT_IR_REMOTE_TITLE;   // same as title
1146   else if (strButton.Equals("livetv")) buttonCode = XINPUT_IR_REMOTE_LIVE_TV;
1147   else if (strButton.Equals("star")) buttonCode = XINPUT_IR_REMOTE_STAR;
1148   else if (strButton.Equals("hash")) buttonCode = XINPUT_IR_REMOTE_HASH;
1149   else if (strButton.Equals("clear")) buttonCode = XINPUT_IR_REMOTE_CLEAR;
1150   else if (strButton.Equals("enter")) buttonCode = XINPUT_IR_REMOTE_ENTER;
1151   else if (strButton.Equals("xbox")) buttonCode = XINPUT_IR_REMOTE_DISPLAY; // same as display
1152   else if (strButton.Equals("teletext")) buttonCode = XINPUT_IR_REMOTE_TELETEXT;
1153   else if (strButton.Equals("red")) buttonCode = XINPUT_IR_REMOTE_RED;
1154   else if (strButton.Equals("green")) buttonCode = XINPUT_IR_REMOTE_GREEN;
1155   else if (strButton.Equals("yellow")) buttonCode = XINPUT_IR_REMOTE_YELLOW;
1156   else if (strButton.Equals("blue")) buttonCode = XINPUT_IR_REMOTE_BLUE;
1157   else if (strButton.Equals("subtitle")) buttonCode = XINPUT_IR_REMOTE_SUBTITLE;
1158   else if (strButton.Equals("language")) buttonCode = XINPUT_IR_REMOTE_LANGUAGE;
1159   else CLog::Log(LOGERROR, "Remote Translator: Can't find button %s", strButton.c_str());
1160   return buttonCode;
1161 }
1162
1163 uint32_t CButtonTranslator::TranslateUniversalRemoteString(const char *szButton)
1164 {
1165   if (!szButton || strlen(szButton) < 4 || strnicmp(szButton, "obc", 3)) 
1166     return 0;
1167   const char *szCode = szButton + 3;
1168   // Button Code is 255 - OBC (Original Button Code) of the button
1169   uint32_t buttonCode = 255 - atol(szCode);
1170   if (buttonCode > 255) 
1171     buttonCode = 0;
1172   return buttonCode;
1173 }
1174
1175 uint32_t CButtonTranslator::TranslateKeyboardString(const char *szButton)
1176 {
1177   uint32_t buttonCode = 0;
1178   XBMCKEYTABLE keytable;
1179
1180   // Look up the key name
1181   if (KeyTableLookupName(szButton, &keytable))
1182   {
1183     buttonCode = keytable.vkey;
1184   }
1185
1186   // The lookup failed i.e. the key name wasn't found
1187   else
1188   {
1189     CLog::Log(LOGERROR, "Keyboard Translator: Can't find button %s", szButton);
1190   }
1191
1192   buttonCode |= KEY_VKEY;
1193
1194   return buttonCode;
1195 }
1196
1197 uint32_t CButtonTranslator::TranslateKeyboardButton(TiXmlElement *pButton)
1198 {
1199   uint32_t button_id = 0;
1200   const char *szButton = pButton->Value();
1201
1202   if (!szButton) 
1203     return 0;
1204   CStdString strKey = szButton;
1205   if (strKey.Equals("key"))
1206   {
1207     int id = 0;
1208     if (pButton->QueryIntAttribute("id", &id) == TIXML_SUCCESS)
1209       button_id = (uint32_t)id;
1210     else
1211       CLog::Log(LOGERROR, "Keyboard Translator: `key' button has no id");
1212   }
1213   else
1214     button_id = TranslateKeyboardString(szButton);
1215
1216   // Process the ctrl/shift/alt modifiers
1217   CStdString strMod;
1218   if (pButton->QueryValueAttribute("mod", &strMod) == TIXML_SUCCESS)
1219   {
1220     strMod.ToLower();
1221
1222     CStdStringArray modArray;
1223     StringUtils::SplitString(strMod, ",", modArray);
1224     for (unsigned int i = 0; i < modArray.size(); i++)
1225     {
1226       CStdString& substr = modArray[i];
1227       substr.Trim();
1228
1229       if (substr == "ctrl" || substr == "control")
1230         button_id |= CKey::MODIFIER_CTRL;
1231       else if (substr == "shift")
1232         button_id |= CKey::MODIFIER_SHIFT;
1233       else if (substr == "alt")
1234         button_id |= CKey::MODIFIER_ALT;
1235       else if (substr == "super" || substr == "win")
1236         button_id |= CKey::MODIFIER_SUPER;
1237       else
1238         CLog::Log(LOGERROR, "Keyboard Translator: Unknown key modifier %s in %s", substr.c_str(), strMod.c_str());
1239      }
1240   }
1241
1242   return button_id;
1243 }
1244
1245 uint32_t CButtonTranslator::TranslateAppCommand(const char *szButton)
1246 {
1247 #ifdef WIN32
1248   CStdString strAppCommand = szButton;
1249   strAppCommand.ToLower();
1250
1251   for (int i = 0; i < sizeof(appcommands)/sizeof(appcommands[0]); i++)
1252     if (strAppCommand.Equals(appcommands[i].name))
1253       return appcommands[i].action | KEY_APPCOMMAND;
1254
1255   CLog::Log(LOGERROR, "%s: Can't find appcommand %s", __FUNCTION__, szButton);
1256 #endif
1257
1258   return 0;
1259 }
1260
1261 uint32_t CButtonTranslator::TranslateMouseCommand(const char *szButton)
1262 {
1263   CStdString strMouseCommand = szButton;
1264   strMouseCommand.ToLower();
1265
1266   for (unsigned int i = 0; i < sizeof(mousecommands)/sizeof(mousecommands[0]); i++)
1267     if (strMouseCommand.Equals(mousecommands[i].name))
1268       return mousecommands[i].action | KEY_MOUSE;
1269
1270   CLog::Log(LOGERROR, "%s: Can't find mouse command %s", __FUNCTION__, szButton);
1271
1272   return 0;
1273 }
1274
1275 void CButtonTranslator::Clear()
1276 {
1277   m_translatorMap.clear();
1278 #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE)
1279   lircRemotesMap.clear();
1280 #endif
1281
1282 #if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER)
1283   m_joystickButtonMap.clear();
1284   m_joystickAxisMap.clear();
1285   m_joystickHatMap.clear();
1286 #endif
1287
1288   m_Loaded = false;
1289 }