Merge remote branch 'origin/trunk' into trac-8658
[xbmc:xbmc-antiquated.git] / xbmc / lib / libPython / xbmcmodule / winxml.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #if (defined HAVE_CONFIG_H) && (!defined WIN32)
23   #include "config.h"
24 #endif
25 #include "winxml.h"
26 #if (defined USE_EXTERNAL_PYTHON)
27   #if (defined HAVE_LIBPYTHON2_6)
28     #include <python2.6/Python.h>
29   #elif (defined HAVE_LIBPYTHON2_5)
30     #include <python2.5/Python.h>
31   #elif (defined HAVE_LIBPYTHON2_4)
32     #include <python2.4/Python.h>
33   #else
34     #error "Could not determine version of Python to use."
35   #endif
36 #else
37   #include "lib/libPython/Python/Include/Python.h"
38 #endif
39 #include "../XBPythonDll.h"
40 #include "pyutil.h"
41 #include "GUIPythonWindowXML.h"
42 #include "addons/Skin.h"
43 #include "Util.h"
44 #include "FileSystem/File.h"
45
46 using namespace std;
47 using namespace ADDON;
48
49 #define ACTIVE_WINDOW g_windowManager.GetActiveWindow()
50
51 #ifndef __GNUC__
52 #pragma code_seg("PY_TEXT")
53 #pragma data_seg("PY_DATA")
54 #pragma bss_seg("PY_BSS")
55 #pragma const_seg("PY_RDATA")
56 #endif
57
58 #ifdef __cplusplus
59 extern "C" {
60 #endif
61
62 namespace PYXBMC
63 {
64   PyObject* WindowXML_New(PyTypeObject *type, PyObject *args, PyObject *kwds)
65   {
66     WindowXML *self;
67
68     self = (WindowXML*)type->tp_alloc(type, 0);
69     if (!self) return NULL;
70
71     new(&self->sXMLFileName) string();
72     new(&self->sFallBackPath) string();
73     new(&self->vecControls) std::vector<Control*>();
74
75     self->iWindowId = -1;
76     PyObject* pyOXMLname = NULL;
77     PyObject* pyOname = NULL;
78     PyObject* pyDName = NULL;
79     PyObject* pyRes = NULL;
80
81     string strXMLname, strFallbackPath;
82     string strDefault = "Default";
83     string resolution = "720p";
84
85     if (!PyArg_ParseTuple(args, (char*)"OO|OO", &pyOXMLname, &pyOname, &pyDName, &pyRes)) return NULL;
86
87     PyXBMCGetUnicodeString(strXMLname, pyOXMLname);
88     PyXBMCGetUnicodeString(strFallbackPath, pyOname);
89     if (pyDName) PyXBMCGetUnicodeString(strDefault, pyDName);
90     if (pyRes) PyXBMCGetUnicodeString(resolution, pyRes);
91
92     // Check to see if the XML file exists in current skin. If not use fallback path to find a skin for the script
93     RESOLUTION res = RES_INVALID;
94     CStdString strSkinPath = g_SkinInfo->GetSkinPath(strXMLname, &res);
95
96     if (!XFILE::CFile::Exists(strSkinPath))
97     {
98       // Check for the matching folder for the skin in the fallback skins folder
99       CStdString fallbackPath = CUtil::AddFileToFolder(strFallbackPath, "resources");
100       fallbackPath = CUtil::AddFileToFolder(fallbackPath, "skins");
101       CStdString basePath = CUtil::AddFileToFolder(fallbackPath, g_SkinInfo->ID());
102       strSkinPath = g_SkinInfo->GetSkinPath(strXMLname, &res, basePath);
103       if (!XFILE::CFile::Exists(strSkinPath))
104       {
105         // Finally fallback to the DefaultSkin as it didn't exist in either the XBMC Skin folder or the fallback skin folder
106         CStdString str("none");
107         AddonProps props(str, ADDON_SKIN, str);
108         CSkinInfo skinInfo(props, CSkinInfo::TranslateResolution(resolution, RES_HDTV_720p));
109         basePath = CUtil::AddFileToFolder(fallbackPath, strDefault);
110         
111         skinInfo.Start(basePath);
112         strSkinPath = skinInfo.GetSkinPath(strXMLname, &res, basePath);
113         if (!XFILE::CFile::Exists(strSkinPath))
114         {
115           PyErr_SetString(PyExc_TypeError, "XML File for Window is missing");
116           return NULL;
117         }
118       }
119     }
120
121     self->sFallBackPath = strFallbackPath;
122     self->sXMLFileName = strSkinPath;
123     self->bUsingXML = true;
124
125     // create new GUIWindow
126     if (!Window_CreateNewWindow((Window*)self, false))
127     {
128       // error is already set by Window_CreateNewWindow, just release the memory
129       self->vecControls.clear();
130       self->vecControls.~vector();
131       self->sFallBackPath.~string();
132       self->sXMLFileName.~string();
133       self->ob_type->tp_free((PyObject*)self);
134       return NULL;
135     }
136     ((CGUIWindow*)(self->pWindow))->SetCoordsRes(res);
137     return (PyObject*)self;
138   }
139
140   // removeItem() method
141   PyDoc_STRVAR(removeItem__doc__,
142     "removeItem(position) -- Removes a specified item based on position, from the Window List.\n"
143     "\n"
144     "position        : integer - position of item to remove.\n"
145     "\n"
146     "example:\n"
147     "  - self.removeItem(5)\n");
148
149   PyObject* WindowXML_RemoveItem(WindowXML *self, PyObject *args)
150   {
151     if (!self->pWindow) return NULL;
152
153     int itemPosition;
154     if (!PyArg_ParseTuple(args, (char*)"i", &itemPosition)) return NULL;
155
156     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
157
158     // Tells the window to remove the item at the specified position from the FileItem vector
159     PyXBMCGUILock();
160     pwx->RemoveItem(itemPosition);
161     PyXBMCGUIUnlock();
162
163     Py_INCREF(Py_None);
164     return Py_None;
165   }
166
167   // addItem() method
168   PyDoc_STRVAR(addItem__doc__,
169     "addItem(item[, position]) -- Add a new item to this Window List.\n"
170     "\n"
171     "item            : string, unicode or ListItem - item to add.\n"
172     "position        : [opt] integer - position of item to add. (NO Int = Adds to bottom,0 adds to top, 1 adds to one below from top,-1 adds to one above from bottom etc etc )\n"
173     "                                - If integer positions are greater than list size, negative positions will add to top of list, positive positions will add to bottom of list\n"
174     "example:\n"
175     "  - self.addItem('Reboot XBMC', 0)\n");
176
177   PyObject* WindowXML_AddItem(WindowXML *self, PyObject *args)
178   {
179     if (!self->pWindow) return NULL;
180
181     PyObject *pObject;
182     int itemPosition = INT_MAX;
183     if (!PyArg_ParseTuple(args, (char*)"O|i", &pObject, &itemPosition)) return NULL;
184
185     string strText;
186     ListItem* pListItem = NULL;
187
188     if (ListItem_CheckExact(pObject))
189     {
190       // object is a listitem
191       pListItem = (ListItem*)pObject;
192       Py_INCREF(pListItem);
193     }
194     else
195     {
196       // object is probably a text item
197       if (!PyXBMCGetUnicodeString(strText, pObject, 1)) return NULL;
198       // object is a unicode string now, create a new ListItem
199       pListItem = ListItem_FromString(strText);
200     }
201
202     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
203
204     // Tells the window to add the item to FileItem vector
205     PyXBMCGUILock();
206     pwx->AddItem(pListItem->item, itemPosition);
207     PyXBMCGUIUnlock();
208
209     Py_INCREF(Py_None);
210     return Py_None;
211   }
212
213   // refreshList() method
214   /*PyDoc_STRVAR(refreshList__doc__,
215     "refreshList() -- Updates this Window List. (any new items will be shown once this command is ran.\n"
216     "\n"
217     "example:\n"
218     "  - self.refrestList()\n");*/
219
220   // clearList() method
221   PyDoc_STRVAR(clearList__doc__,
222     "clearList() -- Clear the Window List.\n"
223     "\n"
224     "example:\n"
225     "  - self.clearList()\n");
226
227   PyObject* WindowXML_ClearList(WindowXML *self, PyObject *args)
228   {
229     if (!self->pWindow) return NULL;
230
231     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
232
233     PyXBMCGUILock();
234     pwx->ClearList();
235     PyXBMCGUIUnlock();
236
237     Py_INCREF(Py_None);
238     return Py_None;
239   }
240
241   // setCurrentListPosition() method
242   PyDoc_STRVAR(setCurrentListPosition__doc__,
243     "setCurrentListPosition(position) -- Set the current position in the Window List.\n"
244     "\n"
245     "position        : integer - position of item to set.\n"
246     "\n"
247     "example:\n"
248     "  - self.setCurrentListPosition(5)\n");
249
250   PyObject* WindowXML_SetCurrentListPosition(WindowXML *self, PyObject *args)
251   {
252     if (!self->pWindow) return NULL;
253
254     int listPos = -1;
255     if (!PyArg_ParseTuple(args, (char*)"i", &listPos)) return NULL;
256
257     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
258
259     PyXBMCGUILock();
260     pwx->SetCurrentListPosition(listPos);
261     PyXBMCGUIUnlock();
262
263     Py_INCREF(Py_None);
264     return Py_None;
265   }
266
267   // getCurrentListPosition() method
268   PyDoc_STRVAR(getCurrentListPosition__doc__,
269     "getCurrentListPosition() -- Gets the current position in the Window List.\n"
270     "\n"
271     "example:\n"
272     "  - pos = self.getCurrentListPosition()\n");
273
274   PyObject* WindowXML_GetCurrentListPosition(WindowXML *self, PyObject *args)
275   {
276     if (!self->pWindow) return NULL;
277
278     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
279
280     PyXBMCGUILock();
281     int listPos = pwx->GetCurrentListPosition();
282     PyXBMCGUIUnlock();
283
284     Py_INCREF(Py_None);
285     return Py_BuildValue((char*)"l", listPos);
286   }
287
288   // getListItem() method
289   PyDoc_STRVAR(getListItem__doc__,
290     "getListItem(position) -- Returns a given ListItem in this Window List.\n"
291     "\n"
292     "position        : integer - position of item to return.\n"
293     "\n"
294     "example:\n"
295     "  - listitem = self.getListItem(6)\n");
296
297   PyObject* WindowXML_GetListItem(WindowXML *self, PyObject *args)
298   {
299     if (!self->pWindow) return NULL;
300
301     int listPos = -1;
302     if (!PyArg_ParseTuple(args, (char*)"i", &listPos)) return NULL;
303
304     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
305
306     PyXBMCGUILock();
307     CFileItemPtr fi = pwx->GetListItem(listPos);
308
309     if (fi == NULL)
310     {
311       PyXBMCGUIUnlock();
312       PyErr_SetString(PyExc_TypeError, "Index out of range");
313       return NULL;
314     }
315
316     ListItem* sListItem = (ListItem*)ListItem_Type.tp_alloc(&ListItem_Type, 0);
317     sListItem->item = fi;
318     PyXBMCGUIUnlock();
319
320     Py_INCREF(sListItem);
321     return (PyObject *)sListItem;
322   }
323
324   // getListSize() method
325   PyDoc_STRVAR(getListSize__doc__,
326     "getListSize() -- Returns the number of items in this Window List.\n"
327     "\n"
328     "example:\n"
329     "  - listSize = self.getListSize()\n");
330
331   PyObject* WindowXML_GetListSize(WindowXML *self, PyObject *args)
332   {
333     if (!self->pWindow) return NULL;
334
335     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
336
337     PyXBMCGUILock();
338     int listSize = pwx->GetListSize();
339     PyXBMCGUIUnlock();
340
341     Py_INCREF(Py_None);
342     return Py_BuildValue((char*)"l", listSize);
343   }
344
345   // setProperty() method
346   PyDoc_STRVAR(setProperty__doc__,
347     "setProperty(key, value) -- Sets a container property, similar to an infolabel.\n"
348     "\n"
349     "key            : string - property name.\n"
350     "value          : string or unicode - value of property.\n"
351     "\n"
352     "*Note, Key is NOT case sensitive.\n"
353     "       You can use the above as keywords for arguments and skip certain optional arguments.\n"
354     "       Once you use a keyword, all following arguments require the keyword.\n"
355     "\n"
356     "example:\n"
357     "  - self.setProperty('Category', 'Newest')\n");
358
359   PyObject* WindowXML_SetProperty(WindowXML *self, PyObject *args, PyObject *kwds)
360   {
361     static const char *keywords[] = { "key", "value", NULL };
362     char *key = NULL;
363     PyObject *value = NULL;
364
365     if (!PyArg_ParseTupleAndKeywords(
366       args,
367       kwds,
368       (char*)"sO",
369       (char**)keywords,
370       &key,
371       &value))
372     {
373       return NULL;
374     }
375     if (!key || !value) return NULL;
376
377     CStdString uText;
378     if (!PyXBMCGetUnicodeString(uText, value, 1))
379       return NULL;
380
381     CGUIPythonWindowXML * pwx = (CGUIPythonWindowXML*)self->pWindow;
382     CStdString lowerKey = key;
383
384     PyXBMCGUILock();
385     pwx->SetProperty(lowerKey.ToLower(), uText.c_str());
386     PyXBMCGUIUnlock();
387
388     Py_INCREF(Py_None);
389     return Py_None;
390   }
391
392   PyDoc_STRVAR(windowXML__doc__,
393     "WindowXML class.\n"
394     "\n"
395     "WindowXML(self, xmlFilename, scriptPath[, defaultSkin, defaultRes]) -- Create a new WindowXML script.\n"
396     "\n"
397     "xmlFilename     : string - the name of the xml file to look for.\n"
398     "scriptPath      : string - path to script. used to fallback to if the xml doesn't exist in the current skin. (eg os.getcwd())\n"
399     "defaultSkin     : [opt] string - name of the folder in the skins path to look in for the xml. (default='Default')\n"
400     "defaultRes      : [opt] string - default skins resolution. (default='720p')\n"
401     "\n"
402     "*Note, skin folder structure is eg(resources/skins/Default/720p)\n"
403     "\n"
404     "example:\n"
405     " - ui = GUI('script-Lyrics-main.xml', os.getcwd(), 'LCARS', 'PAL')\n"
406     "   ui.doModal()\n"
407     "   del ui\n");
408
409   PyMethodDef WindowXML_methods[] = {
410     {(char*)"addItem", (PyCFunction)WindowXML_AddItem, METH_VARARGS, addItem__doc__},
411     {(char*)"removeItem", (PyCFunction)WindowXML_RemoveItem, METH_VARARGS, removeItem__doc__},
412     {(char*)"getCurrentListPosition", (PyCFunction)WindowXML_GetCurrentListPosition, METH_VARARGS, getCurrentListPosition__doc__},
413     {(char*)"setCurrentListPosition", (PyCFunction)WindowXML_SetCurrentListPosition, METH_VARARGS, setCurrentListPosition__doc__},
414     {(char*)"getListItem", (PyCFunction)WindowXML_GetListItem, METH_VARARGS, getListItem__doc__},
415     {(char*)"getListSize", (PyCFunction)WindowXML_GetListSize, METH_VARARGS, getListSize__doc__},
416     {(char*)"clearList", (PyCFunction)WindowXML_ClearList, METH_VARARGS, clearList__doc__},
417     {(char*)"setProperty", (PyCFunction)WindowXML_SetProperty, METH_VARARGS|METH_KEYWORDS, setProperty__doc__},
418     {NULL, NULL, 0, NULL}
419   };
420 // Restore code and data sections to normal.
421 #ifndef __GNUC__
422 #pragma code_seg()
423 #pragma data_seg()
424 #pragma bss_seg()
425 #pragma const_seg()
426 #endif
427
428   PyTypeObject WindowXML_Type;
429
430   void initWindowXML_Type()
431   {
432     PyXBMCInitializeTypeObject(&WindowXML_Type);
433
434     WindowXML_Type.tp_name = (char*)"xbmcgui.WindowXML";
435     WindowXML_Type.tp_basicsize = sizeof(WindowXML);
436     WindowXML_Type.tp_dealloc = (destructor)Window_Dealloc;
437     WindowXML_Type.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
438     WindowXML_Type.tp_doc = windowXML__doc__;
439     WindowXML_Type.tp_methods = WindowXML_methods;
440     WindowXML_Type.tp_base = &Window_Type;
441     WindowXML_Type.tp_new = WindowXML_New;
442   }
443 }
444
445 #ifdef __cplusplus
446 }
447 #endif
448
449