]> git.sesse.net Git - vlc/blob - modules/gui/skins2/parser/interpreter.cpp
b09e910e675174bba9ee37fa0f05c5a1cccaea10
[vlc] / modules / gui / skins2 / parser / interpreter.cpp
1 /*****************************************************************************
2  * interpreter.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "interpreter.hpp"
26 #include "expr_evaluator.hpp"
27 #include "../commands/cmd_audio.hpp"
28 #include "../commands/cmd_muxer.hpp"
29 #include "../commands/cmd_playlist.hpp"
30 #include "../commands/cmd_playtree.hpp"
31 #include "../commands/cmd_dialogs.hpp"
32 #include "../commands/cmd_dummy.hpp"
33 #include "../commands/cmd_dvd.hpp"
34 #include "../commands/cmd_layout.hpp"
35 #include "../commands/cmd_quit.hpp"
36 #include "../commands/cmd_minimize.hpp"
37 #include "../commands/cmd_input.hpp"
38 #include "../commands/cmd_fullscreen.hpp"
39 #include "../commands/cmd_on_top.hpp"
40 #include "../commands/cmd_show_window.hpp"
41 #include "../commands/cmd_snapshot.hpp"
42 #include "../src/theme.hpp"
43 #include "../src/var_manager.hpp"
44 #include "../src/vlcproc.hpp"
45
46
47 Interpreter::Interpreter( intf_thread_t *pIntf ): SkinObject( pIntf )
48 {
49     /// Create the generic commands
50 #define REGISTER_CMD( name, cmd ) \
51     m_commandMap[name] = CmdGenericPtr( new cmd( getIntf() ) );
52
53     REGISTER_CMD( "none", CmdDummy )
54     REGISTER_CMD( "dialogs.changeSkin()", CmdDlgChangeSkin )
55     REGISTER_CMD( "dialogs.fileSimple()", CmdDlgFileSimple )
56     REGISTER_CMD( "dialogs.file()", CmdDlgFile )
57     REGISTER_CMD( "dialogs.directory()", CmdDlgDirectory )
58     REGISTER_CMD( "dialogs.disc()", CmdDlgDisc )
59     REGISTER_CMD( "dialogs.net()", CmdDlgNet )
60     REGISTER_CMD( "dialogs.playlist()", CmdDlgPlaylist )
61     REGISTER_CMD( "dialogs.messages()", CmdDlgMessages )
62     REGISTER_CMD( "dialogs.prefs()", CmdDlgPrefs )
63     REGISTER_CMD( "dialogs.fileInfo()", CmdDlgFileInfo )
64     REGISTER_CMD( "dialogs.streamingWizard()", CmdDlgStreamingWizard )
65
66     REGISTER_CMD( "dialogs.popup()", CmdDlgShowPopupMenu )
67     REGISTER_CMD( "dialogs.audioPopup()", CmdDlgShowAudioPopupMenu )
68     REGISTER_CMD( "dialogs.videoPopup()", CmdDlgShowVideoPopupMenu )
69     REGISTER_CMD( "dialogs.miscPopup()", CmdDlgShowMiscPopupMenu )
70
71     REGISTER_CMD( "dvd.nextTitle()", CmdDvdNextTitle )
72     REGISTER_CMD( "dvd.previousTitle()", CmdDvdPreviousTitle )
73     REGISTER_CMD( "dvd.nextChapter()", CmdDvdNextChapter )
74     REGISTER_CMD( "dvd.previousChapter()", CmdDvdPreviousChapter )
75     REGISTER_CMD( "dvd.rootMenu()", CmdDvdRootMenu )
76     REGISTER_CMD( "playlist.load()", CmdDlgPlaylistLoad )
77     REGISTER_CMD( "playlist.save()", CmdDlgPlaylistSave )
78     REGISTER_CMD( "playlist.add()", CmdDlgAdd )
79     VarList &rVar = VlcProc::instance( getIntf() )->getPlaylistVar();
80     m_commandMap["playlist.del()"] =
81         CmdGenericPtr( new CmdPlaylistDel( getIntf(), rVar ) );
82     REGISTER_CMD( "playlist.next()", CmdPlaylistNext )
83     REGISTER_CMD( "playlist.previous()", CmdPlaylistPrevious )
84     REGISTER_CMD( "playlist.sort()", CmdPlaylistSort )
85     m_commandMap["playlist.setRandom(true)"] =
86         CmdGenericPtr( new CmdPlaylistRandom( getIntf(), true ) );
87     m_commandMap["playlist.setRandom(false)"] =
88         CmdGenericPtr( new CmdPlaylistRandom( getIntf(), false ) );
89     m_commandMap["playlist.setLoop(true)"] =
90         CmdGenericPtr( new CmdPlaylistLoop( getIntf(), true ) );
91     m_commandMap["playlist.setLoop(false)"] =
92         CmdGenericPtr( new CmdPlaylistLoop( getIntf(), false ) );
93     m_commandMap["playlist.setRepeat(true)"] =
94         CmdGenericPtr( new CmdPlaylistRepeat( getIntf(), true ) );
95     m_commandMap["playlist.setRepeat(false)"] =
96         CmdGenericPtr( new CmdPlaylistRepeat( getIntf(), false ) );
97     VarTree &rVarTree = VlcProc::instance( getIntf() )->getPlaytreeVar();
98     m_commandMap["playtree.del()"] =
99         CmdGenericPtr( new CmdPlaytreeDel( getIntf(), rVarTree ) );
100     REGISTER_CMD( "playtree.sort()", CmdPlaytreeSort )
101     REGISTER_CMD( "vlc.fullscreen()", CmdFullscreen )
102     REGISTER_CMD( "vlc.play()", CmdPlay )
103     REGISTER_CMD( "vlc.pause()", CmdPause )
104     REGISTER_CMD( "vlc.stop()", CmdStop )
105     REGISTER_CMD( "vlc.faster()", CmdFaster )
106     REGISTER_CMD( "vlc.slower()", CmdSlower )
107     REGISTER_CMD( "vlc.mute()", CmdMute )
108     REGISTER_CMD( "vlc.volumeUp()", CmdVolumeUp )
109     REGISTER_CMD( "vlc.volumeDown()", CmdVolumeDown )
110     REGISTER_CMD( "vlc.minimize()", CmdMinimize )
111     REGISTER_CMD( "vlc.onTop()", CmdOnTop )
112     REGISTER_CMD( "vlc.snapshot()", CmdSnapshot )
113     REGISTER_CMD( "vlc.quit()", CmdQuit )
114     m_commandMap["equalizer.enable()"] =
115         CmdGenericPtr( new CmdSetEqualizer( getIntf(), true ) );
116     m_commandMap["equalizer.disable()"] =
117         CmdGenericPtr( new CmdSetEqualizer( getIntf(), false ) );
118
119     // Register the constant bool variables in the var manager
120     VarManager *pVarManager = VarManager::instance( getIntf() );
121     VarBool *pVarTrue = new VarBoolTrue( getIntf() );
122     pVarManager->registerVar( VariablePtr( pVarTrue ), "true" );
123     VarBool *pVarFalse = new VarBoolFalse( getIntf() );
124     pVarManager->registerVar( VariablePtr( pVarFalse ), "false" );
125 }
126
127
128 Interpreter *Interpreter::instance( intf_thread_t *pIntf )
129 {
130     if( ! pIntf->p_sys->p_interpreter )
131     {
132         Interpreter *pInterpreter;
133         pInterpreter = new Interpreter( pIntf );
134         if( pInterpreter )
135         {
136             pIntf->p_sys->p_interpreter = pInterpreter;
137         }
138     }
139     return pIntf->p_sys->p_interpreter;
140 }
141
142
143 void Interpreter::destroy( intf_thread_t *pIntf )
144 {
145     if( pIntf->p_sys->p_interpreter )
146     {
147         delete pIntf->p_sys->p_interpreter;
148         pIntf->p_sys->p_interpreter = NULL;
149     }
150 }
151
152
153 CmdGeneric *Interpreter::parseAction( const string &rAction, Theme *pTheme )
154 {
155     // Try to find the command in the global command map
156     if( m_commandMap.find( rAction ) != m_commandMap.end() )
157     {
158         return m_commandMap[rAction].get();
159     }
160
161     CmdGeneric *pCommand = NULL;
162
163     if( rAction.find( ";" ) != string::npos )
164     {
165         // Several actions are defined...
166         list<CmdGeneric *> actionList;
167         string rightPart = rAction;
168         string::size_type pos = rightPart.find( ";" );
169         while( pos != string::npos )
170         {
171             string leftPart = rightPart.substr( 0, pos );
172             // Remove any whitespace at the end of the left part, and parse it
173             leftPart =
174                 leftPart.substr( 0, leftPart.find_last_not_of( " \t" ) + 1 );
175             actionList.push_back( parseAction( leftPart, pTheme ) );
176             // Now remove any whitespace at the beginning of the right part,
177             // and go on checking for further actions in it...
178             rightPart = rightPart.substr( pos, rightPart.size() );
179             if ( rightPart.find_first_not_of( " \t;" ) == string::npos )
180             {
181                 // The right part is completely buggy, it's time to leave the
182                 // loop...
183                 rightPart = "";
184                 break;
185             }
186
187             rightPart =
188                 rightPart.substr( rightPart.find_first_not_of( " \t;" ),
189                                   rightPart.size() );
190             pos = rightPart.find( ";" );
191         }
192         actionList.push_back( parseAction( rightPart, pTheme ) );
193
194         // The list is filled, we remove NULL pointers from it, just in case...
195         actionList.remove( NULL );
196
197         pCommand = new CmdMuxer( getIntf(), actionList );
198     }
199     else if( rAction.find( ".setLayout(" ) != string::npos )
200     {
201         int leftPos = rAction.find( ".setLayout(" );
202         string windowId = rAction.substr( 0, leftPos );
203         // 11 is the size of ".setLayout("
204         int rightPos = rAction.find( ")", windowId.size() + 11 );
205         string layoutId = rAction.substr( windowId.size() + 11,
206                                           rightPos - (windowId.size() + 11) );
207
208         TopWindow *pWin = pTheme->getWindowById( windowId );
209         GenericLayout *pLayout = pTheme->getLayoutById( layoutId );
210         if( !pWin )
211         {
212             msg_Err( getIntf(), "unknown window (%s)", windowId.c_str() );
213         }
214         else if( !pLayout )
215         {
216             msg_Err( getIntf(), "unknown layout (%s)", layoutId.c_str() );
217         }
218         // Check that the layout doesn't correspond to another window
219         else if( pWin != pLayout->getWindow() )
220         {
221             msg_Err( getIntf(), "layout %s is not associated to window %s",
222                      layoutId.c_str(), windowId.c_str() );
223         }
224         else
225         {
226             pCommand = new CmdLayout( getIntf(), *pWin, *pLayout );
227         }
228     }
229     else if( rAction.find( ".show()" ) != string::npos )
230     {
231         int leftPos = rAction.find( ".show()" );
232         string windowId = rAction.substr( 0, leftPos );
233
234         if( windowId == "playlist_window" &&
235             !config_GetInt( getIntf(), "skinned-playlist") )
236         {
237             list<CmdGeneric *> list;
238             list.push_back( new CmdDlgPlaylist( getIntf() ) );
239             TopWindow *pWin = pTheme->getWindowById( windowId );
240             if( pWin )
241                 list.push_back( new CmdHideWindow( getIntf(),
242                                                    pTheme->getWindowManager(),
243                                                    *pWin ) );
244             pCommand = new CmdMuxer( getIntf(), list );
245         }
246         else
247         {
248             TopWindow *pWin = pTheme->getWindowById( windowId );
249             if( pWin )
250             {
251                 pCommand = new CmdShowWindow( getIntf(),
252                                               pTheme->getWindowManager(),
253                                               *pWin );
254             }
255             else
256             {
257                 // It was maybe the id of a popup
258                 Popup *pPopup = pTheme->getPopupById( windowId );
259                 if( pPopup )
260                 {
261                     pCommand = new CmdShowPopup( getIntf(), *pPopup );
262                 }
263                 else
264                 {
265                     msg_Err( getIntf(), "unknown window or popup (%s)",
266                              windowId.c_str() );
267                 }
268             }
269         }
270     }
271     else if( rAction.find( ".hide()" ) != string::npos )
272     {
273         int leftPos = rAction.find( ".hide()" );
274         string windowId = rAction.substr( 0, leftPos );
275         if( windowId == "playlist_window" &&
276            ! config_GetInt( getIntf(), "skinned-playlist") )
277         {
278             list<CmdGeneric *> list;
279             list.push_back( new CmdDlgPlaylist( getIntf() ) );
280             TopWindow *pWin = pTheme->getWindowById( windowId );
281             if( pWin )
282                 list.push_back( new CmdHideWindow( getIntf(),
283                                                    pTheme->getWindowManager(),
284                                                    *pWin ) );
285             pCommand = new CmdMuxer( getIntf(), list );
286         }
287         else
288         {
289             TopWindow *pWin = pTheme->getWindowById( windowId );
290             if( pWin )
291             {
292                 pCommand = new CmdHideWindow( getIntf(),
293                                               pTheme->getWindowManager(),
294                                               *pWin );
295             }
296             else
297             {
298                 msg_Err( getIntf(), "unknown window (%s)", windowId.c_str() );
299             }
300         }
301     }
302
303     if( pCommand )
304     {
305         // Add the command in the pool
306         pTheme->m_commands.push_back( CmdGenericPtr( pCommand ) );
307     }
308     else
309     {
310         msg_Warn( getIntf(), "unknown action: %s", rAction.c_str() );
311     }
312
313     return pCommand;
314 }
315
316
317 VarBool *Interpreter::getVarBool( const string &rName, Theme *pTheme )
318 {
319     VarManager *pVarManager = VarManager::instance( getIntf() );
320
321     // Convert the expression into Reverse Polish Notation
322     ExprEvaluator evaluator( getIntf() );
323     evaluator.parse( rName );
324
325     list<VarBool*> varStack;
326
327     // Get the first token from the RPN stack
328     string token = evaluator.getToken();
329     while( !token.empty() )
330     {
331         if( token == "and" )
332         {
333             // Get the 2 last variables on the stack
334             if( varStack.empty() )
335             {
336                 msg_Err( getIntf(), "invalid boolean expression: %s",
337                          rName.c_str());
338                 return NULL;
339             }
340             VarBool *pVar1 = varStack.back();
341             varStack.pop_back();
342             if( varStack.empty() )
343             {
344                 msg_Err( getIntf(), "invalid boolean expression: %s",
345                          rName.c_str());
346                 return NULL;
347             }
348             VarBool *pVar2 = varStack.back();
349             varStack.pop_back();
350
351             // Create a composite boolean variable
352             VarBool *pNewVar = new VarBoolAndBool( getIntf(), *pVar1, *pVar2 );
353             varStack.push_back( pNewVar );
354             // Register this variable in the manager
355             pVarManager->registerVar( VariablePtr( pNewVar ) );
356         }
357         else if( token == "or" )
358         {
359             // Get the 2 last variables on the stack
360             if( varStack.empty() )
361             {
362                 msg_Err( getIntf(), "invalid boolean expression: %s",
363                          rName.c_str());
364                 return NULL;
365             }
366             VarBool *pVar1 = varStack.back();
367             varStack.pop_back();
368             if( varStack.empty() )
369             {
370                 msg_Err( getIntf(), "invalid boolean expression: %s",
371                          rName.c_str());
372                 return NULL;
373             }
374             VarBool *pVar2 = varStack.back();
375             varStack.pop_back();
376
377             // Create a composite boolean variable
378             VarBool *pNewVar = new VarBoolOrBool( getIntf(), *pVar1, *pVar2 );
379             varStack.push_back( pNewVar );
380             // Register this variable in the manager
381             pVarManager->registerVar( VariablePtr( pNewVar ) );
382         }
383         else if( token == "not" )
384         {
385             // Get the last variable on the stack
386             if( varStack.empty() )
387             {
388                 msg_Err( getIntf(), "invalid boolean expression: %s",
389                          rName.c_str());
390                 return NULL;
391             }
392             VarBool *pVar = varStack.back();
393             varStack.pop_back();
394
395             // Create a composite boolean variable
396             VarBool *pNewVar = new VarNotBool( getIntf(), *pVar );
397             varStack.push_back( pNewVar );
398             // Register this variable in the manager
399             pVarManager->registerVar( VariablePtr( pNewVar ) );
400         }
401         else if( token.find( ".isVisible" ) != string::npos )
402         {
403             int leftPos = token.find( ".isVisible" );
404             string windowId = token.substr( 0, leftPos );
405             TopWindow *pWin = pTheme->getWindowById( windowId );
406             if( pWin )
407             {
408                 // Push the visibility variable on the stack
409                 varStack.push_back( &pWin->getVisibleVar() );
410             }
411             else
412             {
413                 msg_Err( getIntf(), "unknown window (%s)", windowId.c_str() );
414                 return NULL;
415             }
416         }
417         else
418         {
419             // Try to get the variable from the variable manager
420             VarBool *pVar = (VarBool*)pVarManager->getVar( token, "bool" );
421             if( !pVar )
422             {
423                 msg_Err( getIntf(), "cannot resolve boolean variable: %s",
424                          token.c_str());
425                 return NULL;
426             }
427             varStack.push_back( pVar );
428         }
429         // Get the first token from the RPN stack
430         token = evaluator.getToken();
431     }
432
433     // The stack should contain a single variable
434     if( varStack.size() != 1 )
435     {
436         msg_Err( getIntf(), "invalid boolean expression: %s", rName.c_str() );
437         return NULL;
438     }
439     return varStack.back();
440 }
441
442
443 VarPercent *Interpreter::getVarPercent( const string &rName, Theme *pTheme )
444 {
445     // Try to get the variable from the variable manager
446     VarManager *pVarManager = VarManager::instance( getIntf() );
447     VarPercent *pVar = (VarPercent*)pVarManager->getVar( rName, "percent" );
448     return pVar;
449 }
450
451
452 VarList *Interpreter::getVarList( const string &rName, Theme *pTheme )
453 {
454     // Try to get the variable from the variable manager
455     VarManager *pVarManager = VarManager::instance( getIntf() );
456     VarList *pVar = (VarList*)pVarManager->getVar( rName, "list" );
457     return pVar;
458 }
459
460
461 VarTree *Interpreter::getVarTree( const string &rName, Theme *pTheme )
462 {
463     // Try to get the variable from the variable manager
464     VarManager *pVarManager = VarManager::instance( getIntf() );
465     VarTree *pVar = (VarTree*)pVarManager->getVar( rName, "tree" );
466     return pVar;
467 }
468
469
470 string Interpreter::getConstant( const string &rValue )
471 {
472     // Check if the value is a registered constant
473     string val = VarManager::instance( getIntf() )->getConst( rValue );
474     if( val.empty() )
475     {
476         // if not, keep the value as is
477         val = rValue;
478     }
479     return val;
480 }
481