""" Widgets for Retromancer GUI: Quest mode! """ import sys import os import wx import time import traceback import thread import wx.lib.mixins.listctrl as listmix import Config import GameInfo import Global from Global import Log, LogException class QuestListCtrl(wx.ListCtrl): """ A ListCtrl, with some column sizing behavior. """ def __init__(self, parent, ID, pos = wx.DefaultPosition, size = wx.DefaultSize, style = 0): wx.ListCtrl.__init__(self, parent, ID, pos, size, style) #self.ResizeColumnMinWidth = None #listmix.ListCtrlAutoWidthMixin.__init__(self) #self.Bind(wx.EVT_SIZE, self.HandleResize) #self.Bind(wx.EVT_LIST_COL_END_DRAG, self.HandleResize, self) def HandleResize(self, Event): if 'gtk2' in wx.PlatformInfo: self.DoResize() else: self.DoResize() #wx.CallAfter(self.DoResize) #Event.Skip() def DoResize(self): """ Special resizing method. """ if not self: # avoid a PyDeadObject error return if self.GetSize().height < 32: return # avoid an endless update bug when the height is small. ColumnCount = self.GetColumnCount() if ColumnCount == 0: return # Nothing to resize. ResizeColumnNumber = ColumnCount ResizeColumnIndex = ResizeColumnNumber - 1 if self.ResizeColumnMinWidth == None: self.ResizeColumnMinWidth = self.GetColumnWidth(ResizeColumnIndex) / 2 # We're showing the vertical scrollbar -> allow for scrollbar width # NOTE: on GTK, the scrollbar is included in the client size, but on # Windows it is not included ListWidth = self.GetClientSize().width if wx.Platform != '__WXMSW__': if self.GetItemCount() > self.GetCountPerPage(): ScrollWidth = wx.SystemSettings_GetMetric(wx.SYS_VSCROLL_X) ListWidth = ListWidth - ScrollWidth ListWidth -= 10 #%%% HACK TotalColumnWidth = 0 # Width of all columns except last one. for ColumnIndex in range(ColumnCount): if ColumnIndex != ResizeColumnIndex: TotalColumnWidth += self.GetColumnWidth(ColumnIndex) ResizeColWidth = self.GetColumnWidth(ResizeColumnIndex) if ResizeColWidth + self.ResizeColumnMinWidth > ListWidth: # We haven't got the width to show the last column at its minimum # width -> set it to its minimum width and allow the horizontal # scrollbar to show. self.SetColumnWidth(ResizeColumnIndex, self.ResizeColumnMinWidth) return # Resize the last column to take up the remaining available space. self.SetColumnWidth(ResizeColumnIndex, ListWidth - TotalColumnWidth) Log("Size column %s to %s-%s=%s"%(ResizeColumnIndex, ListWidth, TotalColumnWidth, ListWidth - TotalColumnWidth)) class QuestListPanel(wx.SashLayoutWindow, listmix.ColumnSorterMixin): """ Upper pane of the QuestPanel; container for QuestList. """ def __init__(self, *args, **kw): wx.SashLayoutWindow.__init__(self, *args, **kw) self.ImageList = wx.ImageList(16, 16) #self.ListSortDownImage = self.ImageList.Add(UIImages.GetSmallDnArrowBitmap()) #self.ListSortUpImage = self.ImageList.Add(UIImages.GetSmallUpArrowBitmap()) def GetListCtrl(self): "Used by the ColumnSorterMixin" return self.QuestList def PrepareColumnSorting(self): listmix.ColumnSorterMixin.__init__(self, 4) class QuestPanel(wx.Panel): """ Panel with two sub-panes: Left pane for quest list, right pane for instructions! """ def __init__(self, Parent, ID): wx.Panel.__init__(self, Parent, ID) self.SelectedQuest = None self.SelectedQuestIndex = None self.QuestGroup = None self.Bind(wx.EVT_SIZE, self.OnSize) def OnSize(self, Event): wx.LayoutAlgorithm().LayoutWindow(self) self.Refresh() def OnSashDrag(self, Event): if Event.GetDragStatus() == wx.SASH_STATUS_OUT_OF_RANGE: Log("drag is out of range") return EventObject = Event.GetEventObject() if EventObject is self.PaneA: self.PaneA.SetDefaultSize((Event.GetDragRect().width, 1000)) Log("PaneA width-> %s"%Event.GetDragRect().height) elif EventObject is self.PaneB: self.PaneB.SetDefaultSize((Event.GetDragRect().width, 1000)) Log("PaneB default height -> %s"%Event.GetDragRect().height) else: Log("wtf?") wx.LayoutAlgorithm().LayoutWindow(self) self.Refresh() def BuildWidgets(self): # Put some widgets in the panel: PaneIDs = [] ############################################################ # FIRST half: self.PaneA = QuestListPanel(self, -1, wx.DefaultPosition, (800, 30), wx.NO_BORDER | wx.SW_3D) self.PaneA.SetDefaultSize((600, 30)) self.PaneA.SetOrientation(wx.LAYOUT_VERTICAL) self.PaneA.SetAlignment(wx.LAYOUT_LEFT) #self.PaneA.SetOrientation(wx.LAYOUT_HORIZONTAL) #self.PaneA.SetAlignment(wx.LAYOUT_TOP) self.PaneA.SetBackgroundColour(wx.Colour(55, 55, 55)) #self.PaneA.SetSashVisible(wx.SASH_BOTTOM, True) self.PaneA.SetSashVisible(wx.SASH_RIGHT, True) PaneIDs.append(self.PaneA.GetId()) self.BuildQuestList(self.PaneA) ############################################################ # SECOND half: self.PaneB = wx.SashLayoutWindow(self, -1, wx.DefaultPosition, (150, 30), wx.NO_BORDER|wx.SW_3D) self.PaneB.SetDefaultSize((200, 30)) self.PaneB.SetOrientation(wx.LAYOUT_VERTICAL) self.PaneB.SetAlignment(wx.LAYOUT_RIGHT) #self.PaneB.SetOrientation(wx.LAYOUT_HORIZONTAL) #self.PaneB.SetAlignment(wx.LAYOUT_BOTTOM) #self.PaneB.SetBackgroundColour(wx.Colour(155, 55, 55)) # Note: You can't have two sash-layout-windows which abut and both have visible sashes! # (If you do, then the "bottom" sash is forbidden to drag above the "top" sash) #self.PaneB.SetSashVisible(wx.SASH_TOP, True) PaneIDs.append(self.PaneA.GetId()) TextStyle = wx.TE_READONLY | wx.TE_MULTILINE ###################################################### InstructionsHolder = wx.Panel(self.PaneB, -1) GameInstructionsStatic = wx.StaticText(InstructionsHolder, -1, "Game Instructions:") self.GameInstructionsText = wx.TextCtrl(InstructionsHolder, -1, "", wx.DefaultPosition, (300, 150), style = TextStyle) self.GameInstructionsText.SetBackgroundColour(wx.Color(195, 195, 195)) QuestInstructionsStatic = wx.StaticText(InstructionsHolder, -1, "Quest Instructions:") self.QuestInstructionsText = wx.TextCtrl(InstructionsHolder, -1, "", wx.DefaultPosition, (300, 150), style = TextStyle) self.QuestInstructionsText.SetBackgroundColour(wx.Color(195, 195, 195)) #self.InstructionsText = wx.TextCtrl(self.PaneB, -1, style = TextStyle) # Bind events: self.Bind(wx.EVT_SASH_DRAGGED_RANGE, self.OnSashDrag, id = min(PaneIDs), id2 = max(PaneIDs)) # Layout: Box = wx.BoxSizer(wx.VERTICAL) #Box.AddSpacer(10) Box.Add(GameInstructionsStatic, 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.BOTTOM, 4) Box.Add(self.GameInstructionsText, 1, wx.EXPAND | wx.LEFT, 10) Box.Add(QuestInstructionsStatic, 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.BOTTOM, 4) Box.Add(self.QuestInstructionsText, 1, wx.EXPAND | wx.LEFT, 10) InstructionsHolder.SetAutoLayout(True) InstructionsHolder.SetSizer(Box) InstructionsHolder.Layout() def BuildQuestList(self, Parent): """ Build QuestListCtrl: """ #self.QuestList = self.QuestPanel.TopWindow.BuildListControl(self) Style = wx.LC_REPORT | wx.BORDER_NONE | wx.LC_SORT_ASCENDING self.QuestList = QuestListCtrl(Parent, -1, style = Style) self.PaneA.QuestList = self.QuestList # bind events: self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnQuestSelected, self.QuestList) self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnQuestDeselected, self.QuestList) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnQuestDoubleClick, self.QuestList) #self.QuestList.Bind(wx.EVT_LEFT_DCLICK, self.OnQuestDoubleClick) self.QuestList.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.OnQuestRightClick) # for wxMSW self.QuestList.Bind(wx.EVT_RIGHT_UP, self.OnQuestRightClick) # for wxGTK # Add columns: self.QuestList.InsertColumn(0, "Quest") self.QuestList.InsertColumn(1, "Type") self.QuestList.InsertColumn(2, "Driver") self.QuestList.InsertColumn(3, "Completion") self.QuestList.SetColumnWidth(0, 250) self.QuestList.SetColumnWidth(1, 50) self.QuestList.SetColumnWidth(2, 150) self.QuestList.SetColumnWidth(3, 90) self.PaneA.PrepareColumnSorting() self.PopulateQuestList(None) # *After* populating the quest list, we init the column sorter mixin: def PopulateQuestList(self, QuestGroup): """ Populate self.QuestList, our list control. """ self.QuestGroup = QuestGroup self.QuestList.DeleteAllItems() ItemDataMap = {} if QuestGroup: Log("PopulateQuestList!") for QuestIndex in range(len(QuestGroup.Quests)): Quest = QuestGroup.Quests[QuestIndex] Index = self.QuestList.InsertStringItem(sys.maxint, Quest.Name) GameTypeStr = Quest.GetGameType() self.QuestList.SetStringItem(Index, 1, GameTypeStr) DriverNameStr = Quest.GetDisplayDriverName() self.QuestList.SetStringItem(Index, 2, DriverNameStr) CompletionStr = Quest.GetBestCompletionString() self.QuestList.SetStringItem(Index, 3, CompletionStr) self.QuestList.SetItemData(Index, QuestIndex) DataValues = (Quest.Name, GameTypeStr, DriverNameStr, CompletionStr) ItemDataMap[QuestIndex] = tuple(DataValues) self.PaneA.itemDataMap = ItemDataMap #self.QuestPanel.TopWindow.PrepareColumnSorting() def UpdateQuestList(self, Index, Quest, QuestCompletionString): try: QuestIndex = self.QuestGroup.Quests.index(Quest) except: LogException() Log("**Warning: Quest '%s' not found in global group"%Quest) return DataValues = list(self.PaneA.itemDataMap[QuestIndex]) DataValues[3] = QuestCompletionString self.PaneA.itemDataMap[QuestIndex] = tuple(DataValues) self.QuestList.SetStringItem(Index, 3, QuestCompletionString) def OnQuestSelected(self, Event): OldIndex = self.SelectedQuestIndex self.SelectedQuestIndex = Event.m_itemIndex if (self.SelectedQuestIndex != OldIndex): self.SelectedQuest = self.GetQuestByIndex(self.SelectedQuestIndex) Game = self.SelectedQuest.Game GameInstructions = Config.GetFixedInstructions(self.SelectedQuest.Game.Instructions, self.App.Config) self.GameInstructionsText.ChangeValue(GameInstructions) QuestInstructions = Config.GetFixedInstructions(self.SelectedQuest.Instructions, self.App.Config) self.QuestInstructionsText.ChangeValue(QuestInstructions) Event.Skip() #%%% def GetQuestByIndex(self, Index): QuestName = self.QuestList.GetItemText(self.SelectedQuestIndex) Quest = self.QuestGroup.GetQuestByName(QuestName) return Quest def OnQuestDeselected(self, Event): self.SelectedQuestIndex = None self.SelectedQuest = None def LaunchQuestArcade(self, Quest): return Global.App.LaunchQuestArcade(Quest) def LaunchQuestNES(self, Quest): return Global.App.LaunchQuestNES(Quest) def LaunchQuestGBA(self, Quest): return Global.App.LaunchQuestGBA(Quest) def OnQuestDoubleClick(self, Event): Log("Double-click quest %s"%self.SelectedQuestIndex) Event.Skip() # Let's launch a quest: if not self.SelectedQuest: Log("** Unknown quest selected!") return Quest = self.SelectedQuest OldQuestCompletionString = Global.Player.GetBestCompletionString(Quest.ID) #OldQuestCompletionString = Quest.GetBestCompletionString() ResultsList = Global.App.LaunchQuest(Quest, None) if ResultsList: Log("List of %s result returned!"%len(ResultsList)) Log("Get best completion:") QuestCompletionString = Global.Player.GetBestCompletionString(Quest.ID) # Update the UI, if we can check off a quest from the list: if QuestCompletionString != OldQuestCompletionString: Log("Update quest list:") self.UpdateQuestList(self.SelectedQuestIndex, Quest, QuestCompletionString) def OnQuestRightClick(self, Event): Log("Right-click quest %s"%self.SelectedQuestIndex)