]> git.sesse.net Git - vlc/blob - modules/gui/skins2/parser/interpreter.cpp
f3613aadd8f0ba449575fd6ec3a0ca12103eb892
[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., 59 Temple Place - Suite 330, Boston, MA  02111, 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_layout.hpp"
34 #include "../commands/cmd_quit.hpp"
35 #include "../commands/cmd_minimize.hpp"
36 #include "../commands/cmd_input.hpp"
37 #include "../commands/cmd_fullscreen.hpp"
38 #include "../commands/cmd_on_top.hpp"
39 #include "../commands/cmd_show_window.hpp"
40 #include "../src/theme.hpp"
41 #include "../src/var_manager.hpp"
42 #include "../src/vlcproc.hpp"
43
44
45 Interpreter::Interpreter( intf_thread_t *pIntf ): SkinObject( pIntf )
46 {
47     /// Create the generic commands
48 #define REGISTER_CMD( name, cmd ) \
49     m_commandMap[name] = CmdGenericPtr( new cmd( getIntf() ) );
50
51     REGISTER_CMD( "none", CmdDummy )
52     REGISTER_CMD( "dialogs.changeSkin()", CmdDlgChangeSkin )
53     REGISTER_CMD( "dialogs.fileSimple()", CmdDlgFileSimple )
54     REGISTER_CMD( "dialogs.file()", CmdDlgFile )
55     REGISTER_CMD( "dialogs.directory()", CmdDlgDirectory )
56     REGISTER_CMD( "dialogs.disc()", CmdDlgDisc )
57     REGISTER_CMD( "dialogs.net()", CmdDlgNet )
58     REGISTER_CMD( "dialogs.messages()", CmdDlgMessages )
59     REGISTER_CMD( "dialogs.prefs()", CmdDlgPrefs )
60     REGISTER_CMD( "dialogs.fileInfo()", CmdDlgFileInfo )
61     REGISTER_CMD( "dialogs.streamingWizard()", CmdDlgStreamingWizard )
62     REGISTER_CMD( "dialogs.popup()", CmdDlgShowPopupMenu )
63     REGISTER_CMD( "playlist.load()", CmdDlgPlaylistLoad )
64     REGISTER_CMD( "playlist.save()", CmdDlgPlaylistSave )
65     REGISTER_CMD( "playlist.add()", CmdDlgAdd )
66     VarList &rVar = VlcProc::instance( getIntf() )->getPlaylistVar();
67     m_commandMap["playlist.del()"] =
68         CmdGenericPtr( new CmdPlaylistDel( getIntf(), rVar ) );
69     REGISTER_CMD( "playlist.next()", CmdPlaylistNext )
70     REGISTER_CMD( "playlist.previous()", CmdPlaylistPrevious )
71     REGISTER_CMD( "playlist.sort()", CmdPlaylistSort )
72     m_commandMap["playlist.setRandom(true)"] =
73         CmdGenericPtr( new CmdPlaylistRandom( getIntf(), true ) );
74     m_commandMap["playlist.setRandom(false)"] =
75         CmdGenericPtr( new CmdPlaylistRandom( getIntf(), false ) );
76     m_commandMap["playlist.setLoop(true)"] =
77         CmdGenericPtr( new CmdPlaylistLoop( getIntf(), true ) );
78     m_commandMap["playlist.setLoop(false)"] =
79         CmdGenericPtr( new CmdPlaylistLoop( getIntf(), false ) );
80     m_commandMap["playlist.setRepeat(true)"] =
81         CmdGenericPtr( new CmdPlaylistRepeat( getIntf(), true ) );
82     m_commandMap["playlist.setRepeat(false)"] =
83         CmdGenericPtr( new CmdPlaylistRepeat( getIntf(), false ) );
84     REGISTER_CMD( "playtree.load()", CmdDlgPlaytreeLoad )
85     REGISTER_CMD( "playtree.save()", CmdDlgPlaytreeSave )
86     REGISTER_CMD( "playtree.add()", CmdDlgAdd )
87     VarTree &rVarTree = VlcProc::instance( getIntf() )->getPlaytreeVar();
88     m_commandMap["playtree.del()"] =
89         CmdGenericPtr( new CmdPlaytreeDel( getIntf(), rVarTree ) );
90     REGISTER_CMD( "playtree.next()", CmdPlaytreeNext )
91     REGISTER_CMD( "playtree.previous()", CmdPlaytreePrevious )
92     REGISTER_CMD( "playtree.sort()", CmdPlaytreeSort )
93     m_commandMap["playtree.setRandom(true)"] =
94         CmdGenericPtr( new CmdPlaytreeRandom( getIntf(), true ) );
95     m_commandMap["playtree.setRandom(false)"] =
96         CmdGenericPtr( new CmdPlaytreeRandom( getIntf(), false ) );
97     m_commandMap["playtree.setLoop(true)"] =
98         CmdGenericPtr( new CmdPlaytreeLoop( getIntf(), true ) );
99     m_commandMap["playtree.setLoop(false)"] =
100         CmdGenericPtr( new CmdPlaytreeLoop( getIntf(), false ) );
101     m_commandMap["playtree.setRepeat(true)"] =
102         CmdGenericPtr( new CmdPlaytreeRepeat( getIntf(), true ) );
103     m_commandMap["playtree.setRepeat(false)"] =
104         CmdGenericPtr( new CmdPlaytreeRepeat( getIntf(), false ) );
105     REGISTER_CMD( "vlc.fullscreen()", CmdFullscreen )
106     REGISTER_CMD( "vlc.play()", CmdPlay )
107     REGISTER_CMD( "vlc.pause()", CmdPause )
108     REGISTER_CMD( "vlc.stop()", CmdStop )
109     REGISTER_CMD( "vlc.faster()", CmdFaster )
110     REGISTER_CMD( "vlc.slower()", CmdSlower )
111     REGISTER_CMD( "vlc.mute()", CmdMute )
112     REGISTER_CMD( "vlc.volumeUp()", CmdVolumeUp )
113     REGISTER_CMD( "vlc.volumeDown()", CmdVolumeDown )
114     REGISTER_CMD( "vlc.minimize()", CmdMinimize )
115     REGISTER_CMD( "vlc.onTop()", CmdOnTop )
116     REGISTER_CMD( "vlc.quit()", CmdQuit )
117     m_commandMap["equalizer.enable()"] =
118         CmdGenericPtr( new CmdSetEqualizer( getIntf(), true ) );
119     m_commandMap["equalizer.disable()"] =
120         CmdGenericPtr( new CmdSetEqualizer( getIntf(), false ) );
121
122     // Register the constant bool variables in the var manager
123     VarManager *pVarManager = VarManager::instance( getIntf() );
124     VarBool *pVarTrue = new VarBoolTrue( getIntf() );
125     pVarManager->registerVar( VariablePtr( pVarTrue ), "true" );
126     VarBool *pVarFalse = new VarBoolFalse( getIntf() );
127     pVarManager->registerVar( VariablePtr( pVarFalse ), "false" );
128 }
129
130
131 Interpreter *Interpreter::instance( intf_thread_t *pIntf )
132 {
133     if( ! pIntf->p_sys->p_interpreter )
134     {
135         Interpreter *pInterpreter;
136         pInterpreter = new Interpreter( pIntf );
137         if( pInterpreter )
138         {
139             pIntf->p_sys->p_interpreter = pInterpreter;
140         }
141     }
142     return pIntf->p_sys->p_interpreter;
143 }
144
145
146 void Interpreter::destroy( intf_thread_t *pIntf )
147 {
148     if( pIntf->p_sys->p_interpreter )
149     {
150         delete pIntf->p_sys->p_interpreter;
151         pIntf->p_sys->p_interpreter = NULL;
152     }
153 }
154
155
156 CmdGeneric *Interpreter::parseAction( const string &rAction, Theme *pTheme )
157 {
158     // Try to find the command in the global command map
159     if( m_commandMap.find( rAction ) != m_commandMap.end() )
160     {
161         return m_commandMap[rAction].get();
162     }
163
164     CmdGeneric *pCommand = NULL;
165
166     if( rAction.find( ";" ) != string::npos )
167     {
168         // Several actions are defined...
169         list<CmdGeneric *> actionList;
170         string rightPart = rAction;
171         string::size_type pos = rightPart.find( ";" );
172         while( pos != string::npos )
173         {
174             string leftPart = rightPart.substr( 0, pos );
175             // Remove any whitespace at the end of the left part, and parse it
176             leftPart =
177                 leftPart.substr( 0, leftPart.find_last_not_of( " \t" ) + 1 );
178             actionList.push_back( parseAction( leftPart, pTheme ) );
179             // Now remove any whitespace at the beginning of the right part,
180             // and go on checking for further actions in it...
181             rightPart = rightPart.substr( pos, rightPart.size() );
182             rightPart =
183                 rightPart.substr( rightPart.find_first_not_of( " \t;" ),
184                                   rightPart.size() );
185             pos = rightPart.find( ";" );
186         }
187         actionList.push_back( parseAction( rightPart, pTheme ) );
188
189         // The list is filled, we remove NULL pointers from it, just in case...
190         actionList.remove( NULL );
191
192         pCommand = new CmdMuxer( getIntf(), actionList );
193     }
194     else if( rAction.find( ".setLayout(" ) != string::npos )
195     {
196         int leftPos = rAction.find( ".setLayout(" );
197         string windowId = rAction.substr( 0, leftPos );
198         // 11 is the size of ".setLayout("
199         int rightPos = rAction.find( ")", windowId.size() + 11 );
200         string layoutId = rAction.substr( windowId.size() + 11,
201                                           rightPos - (windowId.size() + 11) );
202
203         TopWindow *pWin = pTheme->getWindowById( windowId );
204         GenericLayout *pLayout = pTheme->getLayoutById( layoutId );
205         if( !pWin )
206         {
207             msg_Err( getIntf(), "Unknown window (%s)", windowId.c_str() );
208         }
209         else if( !pLayout )
210         {
211             msg_Err( getIntf(), "Unknown layout (%s)", layoutId.c_str() );
212         }
213         // Check that the layout doesn't correspond to another window
214         else if( pWin != pLayout->getWindow() )
215         {
216             msg_Err( getIntf(), "Layout %s is not associated to window %s",
217                      layoutId.c_str(), windowId.c_str() );
218         }
219         else
220         {
221             pCommand = new CmdLayout( getIntf(), *pWin, *pLayout );
222         }
223     }
224     else if( rAction.find( ".show()" ) != string::npos )
225     {
226         int leftPos = rAction.find( ".show()" );
227         string windowId = rAction.substr( 0, leftPos );
228         TopWindow *pWin = pTheme->getWindowById( windowId );
229         if( pWin )
230         {
231             pCommand = new CmdShowWindow( getIntf(), pTheme->getWindowManager(),
232                                           *pWin );
233         }
234         else
235         {
236             msg_Err( getIntf(), "Unknown window (%s)", windowId.c_str() );
237         }
238     }
239     else if( rAction.find( ".hide()" ) != string::npos )
240     {
241         int leftPos = rAction.find( ".hide()" );
242         string windowId = rAction.substr( 0, leftPos );
243         TopWindow *pWin = pTheme->getWindowById( windowId );
244         if( pWin )
245         {
246             pCommand = new CmdHideWindow( getIntf(), pTheme->getWindowManager(),
247                                           *pWin );
248         }
249         else
250         {
251             msg_Err( getIntf(), "Unknown window (%s)", windowId.c_str() );
252         }
253     }
254
255     if( pCommand )
256     {
257         // Add the command in the pool
258         pTheme->m_commands.push_back( CmdGenericPtr( pCommand ) );
259     }
260     else
261     {
262         msg_Warn( getIntf(), "Unknown action: %s", rAction.c_str() );
263     }
264
265     return pCommand;
266 }
267
268
269 VarBool *Interpreter::getVarBool( const string &rName, Theme *pTheme )
270 {
271     VarManager *pVarManager = VarManager::instance( getIntf() );
272
273     // Convert the expression into Reverse Polish Notation
274     ExprEvaluator evaluator( getIntf() );
275     evaluator.parse( rName );
276
277     list<VarBool*> varStack;
278
279     // Get the first token from the RPN stack
280     string token = evaluator.getToken();
281     while( !token.empty() )
282     {
283         if( token == "and" )
284         {
285             // Get the 2 last variables on the stack
286             if( varStack.empty() )
287             {
288                 msg_Err( getIntf(), "Invalid boolean expression: %s",
289                          rName.c_str());
290                 return NULL;
291             }
292             VarBool *pVar1 = varStack.back();
293             varStack.pop_back();
294             if( varStack.empty() )
295             {
296                 msg_Err( getIntf(), "Invalid boolean expression: %s",
297                          rName.c_str());
298                 return NULL;
299             }
300             VarBool *pVar2 = varStack.back();
301             varStack.pop_back();
302
303             // Create a composite boolean variable
304             VarBool *pNewVar = new VarBoolAndBool( getIntf(), *pVar1, *pVar2 );
305             varStack.push_back( pNewVar );
306             // Register this variable in the manager
307             pVarManager->registerVar( VariablePtr( pNewVar ) );
308         }
309         else if( token == "or" )
310         {
311             // Get the 2 last variables on the stack
312             if( varStack.empty() )
313             {
314                 msg_Err( getIntf(), "Invalid boolean expression: %s",
315                          rName.c_str());
316                 return NULL;
317             }
318             VarBool *pVar1 = varStack.back();
319             varStack.pop_back();
320             if( varStack.empty() )
321             {
322                 msg_Err( getIntf(), "Invalid boolean expression: %s",
323                          rName.c_str());
324                 return NULL;
325             }
326             VarBool *pVar2 = varStack.back();
327             varStack.pop_back();
328
329             // Create a composite boolean variable
330             VarBool *pNewVar = new VarBoolOrBool( getIntf(), *pVar1, *pVar2 );
331             varStack.push_back( pNewVar );
332             // Register this variable in the manager
333             pVarManager->registerVar( VariablePtr( pNewVar ) );
334         }
335         else if( token == "not" )
336         {
337             // Get the last variable on the stack
338             if( varStack.empty() )
339             {
340                 msg_Err( getIntf(), "Invalid boolean expression: %s",
341                          rName.c_str());
342                 return NULL;
343             }
344             VarBool *pVar = varStack.back();
345             varStack.pop_back();
346
347             // Create a composite boolean variable
348             VarBool *pNewVar = new VarNotBool( getIntf(), *pVar );
349             varStack.push_back( pNewVar );
350             // Register this variable in the manager
351             pVarManager->registerVar( VariablePtr( pNewVar ) );
352         }
353         else if( token.find( ".isVisible" ) != string::npos )
354         {
355             int leftPos = token.find( ".isVisible" );
356             string windowId = token.substr( 0, leftPos );
357             TopWindow *pWin = pTheme->getWindowById( windowId );
358             if( pWin )
359             {
360                 // Push the visibility variable on the stack
361                 varStack.push_back( &pWin->getVisibleVar() );
362             }
363             else
364             {
365                 msg_Err( getIntf(), "Unknown window (%s)", windowId.c_str() );
366                 return NULL;
367             }
368         }
369         else
370         {
371             // Try to get the variable from the variable manager
372             VarBool *pVar = (VarBool*)pVarManager->getVar( token, "bool" );
373             if( !pVar )
374             {
375                 msg_Err( getIntf(), "Cannot resolve boolean variable: %s",
376                          token.c_str());
377                 return NULL;
378             }
379             varStack.push_back( pVar );
380         }
381         // Get the first token from the RPN stack
382         token = evaluator.getToken();
383     }
384
385     // The stack should contain a single variable
386     if( varStack.size() != 1 )
387     {
388         msg_Err( getIntf(), "Invalid boolean expression: %s", rName.c_str() );
389         return NULL;
390     }
391     return varStack.back();
392 }
393
394
395 VarPercent *Interpreter::getVarPercent( const string &rName, Theme *pTheme )
396 {
397     // Try to get the variable from the variable manager
398     VarManager *pVarManager = VarManager::instance( getIntf() );
399     VarPercent *pVar = (VarPercent*)pVarManager->getVar( rName, "percent" );
400     return pVar;
401 }
402
403
404 VarList *Interpreter::getVarList( const string &rName, Theme *pTheme )
405 {
406     // Try to get the variable from the variable manager
407     VarManager *pVarManager = VarManager::instance( getIntf() );
408     VarList *pVar = (VarList*)pVarManager->getVar( rName, "list" );
409     return pVar;
410 }
411
412 VarTree *Interpreter::getVarTree( const string &rName, Theme *pTheme )
413 {
414     // Try to get the variable from the variable manager
415     VarManager *pVarManager = VarManager::instance( getIntf() );
416     VarTree *pVar = (VarTree*)pVarManager->getVar( rName, "tree" );
417     return pVar;
418 }