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