Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/tutoriallevel/data/gui/scripts/QuestGUI.lua @ 7830

Last change on this file since 7830 was 7830, checked in by dafrick, 13 years ago

New implementation of QuestGUI. Should be much more usable now, however there still remain some adjustments to be made.

  • Property svn:eol-style set to native
File size: 20.5 KB
Line 
1-- QuestGUI.lua
2
3local P = createMenuSheet("QuestGUI")
4
5P.questManager = nil -- The QuestManager.
6P.showActive = true -- Whether the active or finished quest list is displayed.
7P.currentQuest = nil -- The quest that is currently displayed.
8P.player = nil -- The player the quests are displayed for.
9P.quests = {}
10P.subquests = {}
11
12-- design parameters
13P.scrollbarWidth = 13
14P.frameHeigth = 18
15P.borderSize = 5
16P.titleHeight = 26
17
18-- old:
19P.rootWindow = nil
20P.detailsWindows = {}
21P.quests = {}
22P.hints = {}
23P.player = nil
24
25-- design parameters
26P.indentWidth = 20
27P.buttonHeight = 30
28P.borderWidth = 5
29
30--TODO:
31-- Highlight whether we are currently looking at active or finished quests
32-- Distinguish completet from failed quests
33-- Add hints
34
35function P.onLoad()
36    P.questManager = orxonox.QuestManager:getInstance()
37end
38
39function P.onShow()
40    -- Get the player.
41    P.player = orxonox.GUIManager:getInstance():getPlayer(P.name)
42
43    -- Load the list of quests to be displayed.
44    P.loadQuestsList(P.currentQuest)
45end
46
47function P.onHide()
48    --P.cleanup()
49end
50
51function P.loadQuestsList(selectQuest)
52    local list = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/QuestsList"))
53    P.clearQuestList()
54
55    local selectQuestId = nil
56    if selectQuest ~= nil then
57        selectQuestId = P.questManager:getId(selectQuest)
58    end
59
60    -- Iterate through all root-quests.
61    local numRootQuests = P.questManager:getNumRootQuests(P.player)
62    if numRootQuests > 0 then
63        local i = 0
64        while i <= numRootQuests-1 do
65            local quest = P.questManager:getRootQuest(P.player, i)
66            local item = P.insertQuest(list, quest)
67            if selectQuestId ~= nil and item ~= nil and selectQuestId == P.questManager:getId(quest) then
68                list:setItemSelectState(item, true)
69            end
70            P.insertSubQuests(list, quest, selectQuestId)
71            i = i+1
72        end
73        if list:getItemCount() > 0 then
74            if selectQuestId == nil then
75                list:setItemSelectState(list:getListboxItemFromIndex(0), true)  -- Select first quest.
76            end
77        else
78            P.loadQuest()
79        end
80    end
81end
82
83function P.insertSubQuests(list, quest, selectQuestId)
84    -- Iterate through all sub-quests.
85    local numQuests = P.questManager:getNumSubQuests(quest, P.player)
86    if numQuests > 0 then
87        local i = 0
88        while i <= numQuests-1 do
89            local subquest = P.questManager:getSubQuest(quest, P.player, i)
90            local item = P.insertQuest(list, subquest)
91            if selectQuestId ~= nil and item ~= nil and selectQuestId == P.questManager:getId(subquest) then
92                list:setItemSelectState(item, true)
93            end
94            i = i+1
95        end
96    end
97end
98
99function P.insertQuest(list, quest)
100    if P.showActive == quest:isActive(P.player) then
101        local item = CEGUI.createListboxTextItem(P.questManager:getDescription(quest):getTitle())
102        item:setSelectionBrushImage(menuImageSet, "MultiListSelectionBrush")
103        list:addItem(item)
104        table.insert(P.quests, quest)
105        return item
106    end
107    return nil
108end
109
110function P.loadQuest(quest)
111
112    P.clearQuest()
113    if quest == nil then
114        return
115    else
116        local offset = 0
117
118        -- Load title and description
119        local description = P.questManager:getDescription(quest)
120        local titleWindow = winMgr:getWindow("orxonox/QuestGUI/Quest/Title")
121        titleWindow:setText(description:getTitle())
122        local descriptionWindow = winMgr:getWindow("orxonox/QuestGUI/Quest/Description")
123        descriptionWindow:setText(description:getDescription())
124        descriptionWindow:setSize(CEGUI.UVector2(CEGUI.UDim(1, -P.scrollbarWidth-P.borderSize), CEGUI.UDim(1, 0)))
125        descriptionWindow:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.borderSize), CEGUI.UDim(0, P.borderSize)))
126        local height = getStaticTextWindowHeight(descriptionWindow)
127        descriptionWindow:setHeight(CEGUI.UDim(0, height))
128        offset = offset + height
129
130        -- Load subquests
131        local list = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/Quest/SubquestsList"))
132        local numQuests = P.questManager:getNumSubQuests(quest, P.player)
133        local i = 0
134        while i <= numQuests-1 do
135            local quest = P.questManager:getSubQuest(quest, P.player, i)
136            --if P.showActive == quest:isActive(P.player) then
137                local item = CEGUI.createListboxTextItem(P.questManager:getDescription(quest):getTitle())
138                item:setSelectionBrushImage(menuImageSet, "MultiListSelectionBrush")
139                list:addItem(item)
140                table.insert(P.subquests, quest)
141            --end
142            i = i+1
143        end
144        height = list:getTotalItemsHeight()
145        if height > 0 then
146            height = height+P.frameHeigth
147        end
148        list:setSize(CEGUI.UVector2(CEGUI.UDim(1, -P.scrollbarWidth-P.borderSize), CEGUI.UDim(0, height)))
149        list:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.borderSize), CEGUI.UDim(0, offset)))
150        offset = offset + height
151
152        -- Load hints
153        local hintsWindow = winMgr:getWindow("orxonox/QuestGUI/Quest/Hints")
154        hintsWindow:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.borderSize), CEGUI.UDim(0, offset)))
155        hintsWindow:setWidth(CEGUI.UDim(1, -P.scrollbarWidth-P.borderSize))
156        height = P.titleHeight
157        local numHints = P.questManager:getNumHints(quest, P.player)
158        local i = 0
159        while i <= numHints-1 do
160            local hint = P.questManager:getHints(quest, P.player, i)
161            height = height + P.insertHint(hintsWindow, hint, i, height)
162            i = i+1
163        end
164        hintsWindow:setHeight(CEGUI.UDim(0, height))
165    end
166
167    P.currentQuest = quest
168end
169
170function P.clearQuest()
171    -- clear title
172    local titleWindow = winMgr:getWindow("orxonox/QuestGUI/Quest/Title")
173    titleWindow:setText("no Quests")
174
175    -- clear description
176    local descriptionWindow = winMgr:getWindow("orxonox/QuestGUI/Quest/Description")
177    descriptionWindow:setText("There is currently no quest that can be displayed.")
178
179    -- clear list fo subquests
180    local list = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/Quest/SubquestsList"))
181    list:resetList()
182    list:setHeight(CEGUI.UDim(0, 0))
183    P.subquests = {}
184
185    -- clear hints
186    local hints = winMgr:getWindow("orxonox/QuestGUI/Quest/Hints")
187    local numChildren = hints:getChildCount()-2 -- TODO: HACK
188    local i = 0
189    while i < numChildren do
190        local hint = hints:getChild("orxonox/QuestGUI/Quest/Hints/" .. i)
191        if hint ~= nil then
192            hints:removeChildWindow(hint)
193            winMgr:destroyWindow(hint)
194        end
195        i = i+1
196    end
197    hints:setSize(CEGUI.UVector2(CEGUI.UDim(1, -P.scrollbarWidth-P.borderSize), CEGUI.UDim(0, 0)))
198
199    P.currentQuest = nil
200end
201
202function P.clearQuestList()
203    local list = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/QuestsList"))
204    list:resetList()
205    P.quests = {}
206end
207
208function P.selectQuest(list, quest)
209    if quest == nil then
210        cout(1, "Error in QuestGUI: selectQuest(), input quest is nil. Selecting first.")
211        list:setItemSelectState(list:getListboxItemFromIndex(0), true) -- Select first
212        return
213    end
214
215    local questId = P.questManager:getId(quest)
216    local found = false
217    local index = 0
218    for k,v in pairs(P.quests) do
219        if P.questManager:getId(v) == questId then
220            found = true
221            index = k-1
222        end
223    end
224    cout(0, questId .. " " .. index)
225    if found then
226        list:setItemSelectState(list:getListboxItemFromIndex(index), true)
227    else
228        cout(1, "Error in QuestGUI: selectQuest(), input quest is not in list. Selecting first.")
229        list:setItemSelectState(list:getListboxItemFromIndex(0), true) -- Select first
230    end
231end
232
233function P.insertHint(hintsWindow, hint, index, offset)
234    local window = winMgr:createWindow("MenuWidgets/StaticText", "orxonox/QuestGUI/Quest/Hints/" .. index)
235    window:setProperty("HorzFormatting", "WordWrapLeftAligned")
236    window:setProperty("VertFormatting", "TopAligned")
237    window:setProperty("FrameEnabled", "false")
238    window:setID(index)
239    hintsWindow:addChildWindow(window)
240    local description = P.questManager:getDescription(hint)
241    window:setText(description:getDescription())
242    window:setSize(CEGUI.UVector2(CEGUI.UDim(1, -P.scrollbarWidth-P.borderSize), CEGUI.UDim(1, 0)))
243    local height = getStaticTextWindowHeight(window)
244    window:setHeight(CEGUI.UDim(0, height))
245    window:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.borderSize), CEGUI.UDim(0, offset)))
246    return height+P.borderSize
247end
248
249function P.showActiveQuestsButton_clicked(e)
250    if P.showActive == false then
251        P.showActive = true
252        P.loadQuestsList()
253    end
254end
255
256function P.showFinishedQuestsButton_clicked(e)
257    if P.showActive == true then
258        P.showActive = false
259        P.loadQuestsList()
260    end
261end
262
263function P.changeQuest_clicked(e)
264    local list = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/QuestsList"))
265    local choice = list:getFirstSelectedItem()
266    if choice ~= nil then
267        local index = list:getItemIndex(choice)
268        local quest = P.quests[index+1]
269        if quest ~= nil then
270            P.loadQuest(quest)
271        end
272    end
273end
274
275function P.changeToSubquest_clicked(e)
276    local list = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/Quest/SubquestsList"))
277    local questsList = CEGUI.toListbox(winMgr:getWindow("orxonox/QuestGUI/QuestsList"))
278    local choice = list:getFirstSelectedItem()
279    if choice ~= nil then
280        local index = list:getItemIndex(choice)
281        local quest = P.subquests[index+1]
282        if quest ~= nil then
283            if quest:isActive(P.player) == P.showActive then
284                P.selectQuest(questsList, quest)
285            else
286                P.showActive = quest:isActive(P.player)
287                P.loadQuestsList(quest)
288            end
289        else
290            cout(1, "Error in QuestGUI: changeToSubquest(), quest was nil. Ignoring...")
291        end
292    end
293end
294
295-- old:
296--[[
297function P.createQuestGUI()
298    local questManager = orxonox.QuestManager:getInstance()
299
300    local depth = 0
301    local index = 0
302
303    local questWindow = winMgr:createWindow("MenuWidgets/ScrollablePane", "orxonox/QuestGUI/Quests")
304    questWindow:setSize(CEGUI.UVector2(CEGUI.UDim(1, 0),CEGUI.UDim(1, 0)))
305
306    -- Iterate through all root-quests.
307    local numRootQuests = orxonox.QuestManager:getInstance():getNumRootQuests(P.player)
308    local i = 0
309    while i <= numRootQuests-1 do
310        local quest = orxonox.QuestManager:getInstance():getRootQuest(P.player, i)
311        index = P.createQuestNodes(questWindow, quest, depth, index)
312        i = i+1
313    end
314
315    return questWindow
316end
317
318function P.createQuestNodes(root, parent, depth, index)
319    local number = table.getn(P.quests)+1
320    local name = "orxonox/QuestGUI/Quests/" .. number
321    local node = winMgr:createWindow("MenuWidgets/TabButton", name)
322    node:setText(orxonox.QuestManager:getInstance():getDescription(parent):getTitle())
323    node:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.indentWidth*depth), CEGUI.UDim(0, P.buttonHeight*index)))
324    node:setSize(CEGUI.UVector2(CEGUI.UDim(1, -P.indentWidth*depth-P.scrollbarWidth), CEGUI.UDim(0, P.buttonHeight)))
325    orxonox.GUIManager:subscribeEventHelper(node, "Clicked", P.name .. ".openDetails_clicked")
326    root:addChildWindow(node)
327   
328    table.insert(P.quests, parent)
329
330    index = index+1
331
332    -- Iterate through all sub-quests.
333    local numQuests = orxonox.QuestManager:getInstance():getNumSubQuests(parent, P.player)
334    local i = 0
335    while i <= numQuests-1 do
336        local quest = orxonox.QuestManager:getInstance():getSubQuest(parent, P.player, i)
337        index = P.createQuestNodes(root, quest, depth+1, index)
338        i = i+1
339    end
340
341    return index
342end
343
344function P.cleanup()
345    winMgr:destroyWindow(P.rootWindow)
346    for k,v in pairs(P.detailsWindows) do
347        if v ~= nil then
348            winMgr:destroyWindow(v)
349            P.detailsWindows[k] = nil
350        end
351    end
352    P.detailsWindows = {}
353
354    P.quests = {}
355    P.hints = {}
356    P.player = nil
357
358    winMgr:destroyWindow(P.rootWindow)
359    P.rootWindow = nil
360end
361
362function P.openDetails_clicked(e)
363    --Get some numbers from the window
364    local we = CEGUI.toWindowEventArgs(e)
365    local name = we.window:getName()
366    local match = string.gmatch(name, "%d+")
367    local questNr = tonumber(match())
368
369    name = name .. "/Details" .. P.getNewDetailNumber()
370    quest = P.quests[questNr]
371
372    local details = winMgr:createWindow("MenuWidgets/FrameWindow", name)
373    details:setSize(CEGUI.UVector2(CEGUI.UDim(0.7, 0), CEGUI.UDim(0.7, 0)))
374    details:setPosition(CEGUI.UVector2(CEGUI.UDim(0.1, 0), CEGUI.UDim(0.1, 0)))
375    details:setText(orxonox.QuestManager:getInstance():getDescription(quest):getTitle())
376    details:setProperty("Alpha", 1.0)
377    details:setProperty("InheritsAlpha", "setFalse")
378    orxonox.GUIManager:subscribeEventHelper(details, "CloseClicked", P.name .. ".closeDetails_clicked")
379
380    table.insert(P.detailsWindows, details)
381
382    name = name .. "/Scrollable"
383    local window = winMgr:createWindow("MenuWidgets/ScrollablePane", name)
384    window:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -2*P.borderWidth),CEGUI.UDim(1.0, -P.titleHeight)))
385    window:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.borderWidth), CEGUI.UDim(0, P.titleHeight)))
386    details:addChildWindow(window)
387
388    local offset = 0
389
390    local status = winMgr:createWindow("MenuWidgets/StaticText", name .. "/Status")
391    window:addChildWindow(status)
392    status:setProperty("HorzFormatting", "WordWrapLeftAligned")
393    status:setProperty("VertFormatting", "TopAligned")
394    if quest:isActive(P.player) then
395        status:setText("This quest is active.")
396    elseif quest:isCompleted(P.player) then
397        status:setText("This quest was completed.")
398    elseif quest:isFailed(P.player) then
399        status:setText("This quest was failed.")
400    end
401    status:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
402    status:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -P.scrollbarWidth), CEGUI.UDim(1.0, 0)))
403    local height = getStaticTextWindowHeight(status)
404    status:setHeight(CEGUI.UDim(0, height))
405    offset = offset + height
406
407    local descriptionTitle = winMgr:createWindow("MenuWidgets/StaticText", name .. "/Description/Title")
408    window:addChildWindow(descriptionTitle)
409    descriptionTitle:setProperty("HorzFormatting", "HorzCentred")
410    descriptionTitle:setProperty("VertFormatting", "TopAligned")
411    descriptionTitle:setText("Description:")
412    descriptionTitle:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
413    descriptionTitle:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -P.scrollbarWidth), CEGUI.UDim(1.0, 0)))
414    height = getStaticTextWindowHeight(descriptionTitle)
415    descriptionTitle:setHeight(CEGUI.UDim(0, height))
416    offset = offset + height
417
418    local description = winMgr:createWindow("MenuWidgets/StaticText", name .. "/Description")
419    window:addChildWindow(description)
420    description:setProperty("HorzFormatting", "WordWrapLeftAligned")
421    description:setProperty("VertFormatting", "TopAligned")
422    description:setText(orxonox.QuestManager:getInstance():getDescription(quest):getDescription())
423    description:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
424    description:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -P.scrollbarWidth), CEGUI.UDim(1.0, 0)))
425    height = getStaticTextWindowHeight(description)
426    description:setHeight(CEGUI.UDim(0, height))
427    offset = offset + height
428
429    -- Display the hints of this quest
430    local numHints = orxonox.QuestManager:getInstance():getNumHints(quest, P.player)
431    if numHints > 0 then
432        local hintsTitle = winMgr:createWindow("MenuWidgets/StaticText", name .. "/Hints/Title")
433        window:addChildWindow(hintsTitle)
434        hintsTitle:setProperty("HorzFormatting", "HorzCentred")
435        hintsTitle:setProperty("VertFormatting", "TopAligned")
436        hintsTitle:setText("Hints:")
437        hintsTitle:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
438        hintsTitle:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -P.scrollbarWidth), CEGUI.UDim(1.0, 0)))
439        height = getStaticTextWindowHeight(hintsTitle)
440        hintsTitle:setHeight(CEGUI.UDim(0, height))
441        offset = offset + height
442    end
443    local i = 0
444    while i <= numHints-1 do
445        local hint = orxonox.QuestManager:getInstance():getHints(quest, P.player, i)
446        table.insert(P.hints, hint)
447        local number = table.getn(P.hints)
448        local node = winMgr:createWindow("MenuWidgets/TabButton", name .. "/Hints" .. number)
449        node:setText(orxonox.QuestManager:getInstance():getDescription(hint):getTitle())
450        node:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
451        node:setSize(CEGUI.UVector2(CEGUI.UDim(1, -P.scrollbarWidth), CEGUI.UDim(0, P.buttonHeight)))
452        window:addChildWindow(node)
453        offset = offset + P.buttonHeight
454
455        orxonox.GUIManager:subscribeEventHelper(node, "Clicked", P.name .. ".openHintDetails_clicked")
456        i = i+1
457    end
458
459    local window = winMgr:getWindow("orxonox/QuestGUI/Background")
460    window:addChildWindow(details)
461end
462
463function P.getNewDetailNumber()
464    local number = table.getn(P.detailsWindows)
465    for k,v in pairs(P.detailsWindows) do
466        if v == nil then
467            number = k-1
468        end
469    end
470    return number+1
471end
472
473function P.closeDetails_clicked(e)
474    local we = CEGUI.toWindowEventArgs(e)
475    local name = we.window:getName()
476    local match = string.gmatch(name, "%d+")
477    match()
478    local detailsNr = tonumber(match())
479
480    winMgr:destroyWindow(P.detailsWindows[detailsNr])
481    P.detailsWindows[detailsNr] = nil
482end
483
484function P.openHintDetails_clicked(e)
485    --Get some numbers from the window
486    local we = CEGUI.toWindowEventArgs(e)
487    local name = we.window:getName()
488    local match = string.gmatch(name, "%d+")
489    match()
490    match()
491    local hintNr = tonumber(match())
492
493    name = name .. "/Details" .. P.getNewDetailNumber()
494    hint = P.hints[hintNr]
495
496    local details = winMgr:createWindow("MenuWidgets/FrameWindow", name)
497    details:setSize(CEGUI.UVector2(CEGUI.UDim(0.7, 0), CEGUI.UDim(0.7, 0)))
498    details:setPosition(CEGUI.UVector2(CEGUI.UDim(0.1, 0), CEGUI.UDim(0.1, 0)))
499    details:setText(orxonox.QuestManager:getInstance():getDescription(hint):getTitle())
500    details:setProperty("Alpha", 1.0)
501    details:setProperty("InheritsAlpha", "setFalse")
502    orxonox.GUIManager:subscribeEventHelper(details, "CloseClicked", P.name .. ".closeHintDetails_clicked")
503
504    table.insert(P.detailsWindows, details)
505
506    name = name .. "/Scrollable"
507    local window = winMgr:createWindow("MenuWidgets/ScrollablePane", name)
508    window:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -2*P.borderWidth),CEGUI.UDim(1.0, -P.titleHeight)))
509    window:setPosition(CEGUI.UVector2(CEGUI.UDim(0, P.borderWidth), CEGUI.UDim(0, P.titleHeight)))
510    details:addChildWindow(window)
511
512    local offset = 0
513   
514    local descriptionTitle = winMgr:createWindow("MenuWidgets/StaticText", name .. "/Description/Title")
515    window:addChildWindow(descriptionTitle)
516    descriptionTitle:setProperty("HorzFormatting", "HorzCentred")
517    descriptionTitle:setProperty("VertFormatting", "TopAligned")
518    descriptionTitle:setText("Description:")
519    descriptionTitle:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
520    descriptionTitle:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -P.scrollbarWidth), CEGUI.UDim(1.0, 0)))
521    height = getStaticTextWindowHeight(descriptionTitle)
522    descriptionTitle:setHeight(CEGUI.UDim(0, height))
523    offset = offset + height
524
525    local description = winMgr:createWindow("MenuWidgets/StaticText", name .. "/Description")
526    window:addChildWindow(description)
527    description:setProperty("HorzFormatting", "WordWrapLeftAligned")
528    description:setProperty("VertFormatting", "TopAligned")
529    description:setText(orxonox.QuestManager:getInstance():getDescription(hint):getDescription())
530    description:setPosition(CEGUI.UVector2(CEGUI.UDim(0, 0), CEGUI.UDim(0, offset)))
531    description:setSize(CEGUI.UVector2(CEGUI.UDim(1.0, -P.scrollbarWidth), CEGUI.UDim(1.0, 0)))
532    height = getStaticTextWindowHeight(description)
533    description:setHeight(CEGUI.UDim(0, height))
534
535    local window = winMgr:getWindow("orxonox/QuestGUI/Background")
536    window:addChildWindow(details)
537end
538
539function P.closeHintDetails_clicked(e)
540    local we = CEGUI.toWindowEventArgs(e)
541    local name = we.window:getName()
542    local match = string.gmatch(name, "%d+")
543    match()
544    match()
545    match()
546    local detailsNr = tonumber(match())
547
548    winMgr:destroyWindow(P.detailsWindows[detailsNr])
549    P.detailsWindows[detailsNr] = nil
550end --]]
551
552return P
553
Note: See TracBrowser for help on using the repository browser.