""" This file is part of RuneOptimizer. RuneOptimizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RuneOptimizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RuneOptimizer. If not, see . """ class PanelTeams(wx.Panel): """ The team management Panel. Parameters ---------- selectedTeamId : string Currently selected team ID (default None). selectedTeamName : string Currently selected team name (default None). selectedTeamPriority : int Currently selected team priority (default None). titleChangePending : boolean Indicates if there is any change on the selected team title to save in the database. priorityChangePending : boolean Indicates if there is any change on the selected team priority to save in the database. teamList : wx.ListCtrl Selectable list with all the teams. detailsSizer : wx.BoxSizer Holds all the details. Hidden until a team is selected. titleText : wx.TextCtrl Text editor to change the team name. priorityText : wx.TextCtrl Text editor to change the team priority. teamUnitList : wx.ListCtrl Selectable list with all the units in the selected team. allUnitList : wx.ListCtrl Selectable list with all the units not in the selected team. filterNameText : wx.TextCtrl Text input to filter units names. filterNameTexts : wx.CheckBox Checkbox to include or exclude units in storage. filterNoRunesCheck : wx.CheckBox Checkbox to include or exclude units without runes. filterNoTeamsCheck : wx.CheckBox Checkbox to include or exclude units in no teams. titleTimer : wx.Timer Timer to defer database updates on team name changes. priorityTimer : wx.Timer Timer to defer database updates on team priority changes. Methods ------- """ selectedTeamId = None selectedTeamName = None selectetdTeamPriority = None titleChangePending = False priorityChangePending = False teamList = None detailsSizer = None titleText = None priorityText = None teamUnitList = None allUnitList = None filterNameText = None filterNameTexts = None filterNoRunesCheck = None filterNoTeamsCheck = None titleTimer = None priorityTimer = None def __init__(self, parent, id=wx.ID_ANY): """Initializes the panel. Sets upt all the widgets. Parameters ---------- parent : wx.* Panel parent. id : int, optional ID for the panel (default wx.ID_ANY). """ # Parent constructor wx.Panel.__init__(self, parent=parent, id=id) wx.StaticText( parent=self, id=wx.ID_ANY, label="Name Prio.", pos=(10, 10), size=(240, 20) ) self.teamList = wx.ListCtrl( parent=self, id=wx.ID_ANY, pos=(10, 30), size=(240, 460), style=wx.LC_REPORT|wx.LC_NO_HEADER ) self.teamList.InsertColumn(0, "Name", width=200) self.teamList.InsertColumn(1, "Prio.", width=40) self.populateTeamList(event=None) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.teamSelected, self.teamList) createTeamButton = wx.Button( parent=self, id=wx.ID_ANY, pos=(50, 500), size=(170, 40), style=wx.LC_REPORT, label="New team" ) self.Bind(wx.EVT_BUTTON, self.createTeam, createTeamButton) # Sizer for all team details. Will be hidden until a team is selected self.detailsSizer = wx.BoxSizer(wx.VERTICAL) # Team title and priority editors self.titleText = wx.TextCtrl( parent=self, id=wx.ID_ANY, pos=(270, 30), size=(200, 25), style=wx.TE_RICH|wx.TE_MULTILINE ) self.detailsSizer.Add(self.titleText) self.Bind(wx.EVT_TEXT, self.changeTitle, self.titleText); priortiyLabel = wx.StaticText( parent=self, id=wx.ID_ANY, label="Priority:", pos=(270, 80), size=(80, 30) ) self.detailsSizer.Add(priortiyLabel) self.priorityText = wx.TextCtrl( parent=self, id=wx.ID_ANY, pos=(330, 80), size=(30, 25), style=wx.TE_RICH|wx.TE_MULTILINE) self.detailsSizer.Add(self.priorityText) self.Bind(wx.EVT_TEXT, self.changePriority, self.priorityText); # Team unit list self.teamUnitList = wx.ListCtrl( parent=self, id=wx.ID_ANY, pos=(270, 130), size=(140, 360), style=wx.LC_REPORT|wx.LC_NO_HEADER ) self.detailsSizer.Add(self.teamUnitList) self.teamUnitList.InsertColumn(0, "Name", width=140) self.teamUnitList.InsertColumn(1, "Level", width=140) # All unit selector allUnitLabel = wx.StaticText( parent=self, id=wx.ID_ANY, label="Name Prio. Sto.", pos=(500, 10), size=(190, 20) ) self.detailsSizer.Add(allUnitLabel) self.allUnitList = wx.ListCtrl( parent=self, id=wx.ID_ANY, pos=(500, 30), size=(190, 350), style=wx.LC_REPORT|wx.LC_NO_HEADER ) self.detailsSizer.Add(self.allUnitList) self.allUnitList.InsertColumn(0, "Name", width=120) self.allUnitList.InsertColumn(1, "Prio.", width=40) self.allUnitList.InsertColumn(2, "Sto.", width=30) # List filters filterBox = wx.StaticBox( parent=self, label="Filters:",id=wx.ID_ANY, pos=(500, 390), size=(190, 150) ) self.detailsSizer.Add(filterBox) wx.StaticText( parent=filterBox, label="Monster name", pos=(5, 5), size=(180, 20) ) self.filterNameText = wx.TextCtrl( parent=filterBox, id=wx.ID_ANY, value="", pos=(5, 25), size=(177, 20), style=wx.TE_PROCESS_ENTER|wx.TE_PROCESS_TAB ) self.Bind(wx.EVT_TEXT, self.populateUnitList, self.filterNameText) self.filterStorageCheck = wx.CheckBox( parent=filterBox, id=wx.ID_ANY, label="Monsters in storage", pos=(5, 55), size=(180, 20) ) self.filterStorageCheck.SetValue(True) self.Bind( wx.EVT_CHECKBOX, self.populateUnitList, self.filterStorageCheck ) self.filterNoRunesCheck = wx.CheckBox( parent=filterBox, id=wx.ID_ANY, label="Monsters without runes", pos=(5, 75), size=(180, 20) ) self.filterNoRunesCheck.SetValue(True) self.Bind( wx.EVT_CHECKBOX, self.populateUnitList, self.filterNoRunesCheck ) self.filterNoTeamsCheck = wx.CheckBox( parent=filterBox, id=wx.ID_ANY, label="Monsters not in teams", pos=(5, 95), size=(180, 20) ) self.filterNoTeamsCheck.SetValue(True) self.Bind( wx.EVT_CHECKBOX, self.populateUnitList, self.filterNoTeamsCheck ) self.populateUnitList(None) addButton = wx.Button( parent=self, id=wx.ID_ANY, pos=(420, 300), size=(70, 40), style=wx.LC_REPORT, label="<<<" ) self.detailsSizer.Add(addButton) self.Bind(wx.EVT_BUTTON, self.addToTeam, addButton) removeButton = wx.Button( parent=self, id=wx.ID_ANY, pos=(420, 350), size=(70, 40), style=wx.LC_REPORT, label=">>>" ) self.detailsSizer.Add(removeButton) self.Bind(wx.EVT_BUTTON, self.removeFromTeam, removeButton) deleteButton = wx.Button( parent=self, id=wx.ID_ANY, pos=(270, 500), size=(140, 40), style=wx.LC_REPORT, label="Delete team" ) self.detailsSizer.Add(deleteButton) self.Bind(wx.EVT_BUTTON, self.deleteTeam, deleteButton) # By default, hide all details self.detailsSizer.ShowItems(False) def populateTeamList(self, event=None): """Populates the team list. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ self.teamList.DeleteAllItems() query = """ SELECT id, name, priority FROM teams ORDER BY priority DESC """ cursor = conn.execute(query) i = 0 for row in cursor: self.teamList.InsertItem(i, row[1]) self.teamList.SetItem(i, 1, str(row[2])) self.teamList.SetItemData(i, int(row[0])) i = i + 1 def populateUnitList(self, event=None): """Populates the list of all units. Uses the filters. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ # Get units from db name = self.filterNameText.GetValue() query = """ SELECT id, name, ( SELECT cast(total(teams.priority) as int) FROM teams, units_teams WHERE teams.id = units_teams.team AND units_teams.unit = units.id ) as priority, storage FROM units WHERE name LIKE '%""" + name + """%' """ if self.filterStorageCheck.GetValue() == False: query += " AND storage = 0 " if self.filterNoRunesCheck.GetValue() == False: query += " AND id IN (SELECT DISTINCT unit FROM runes) " if self.filterNoTeamsCheck.GetValue() == False: query += " AND id IN (SELECT DISTINCT unit FROM units_teams) " query += " ORDER BY priority DESC; "; cursor = conn.execute(query) i = 0 self.allUnitList.DeleteAllItems() for row in cursor: self.allUnitList.InsertItem(i, row[1]) self.allUnitList.SetItem(i, 1, str(row[2])) self.allUnitList.SetItem(i, 2, "") self.allUnitList.SetItemData(i, int(row[0])) if (row[3] == 1): self.allUnitList.SetItem(i, 2, "X") else: self.allUnitList.SetItem(i, 2, " ") i = i + 1 def populateTeamUnitList(self, event=None): """Populates the list of units in the selected team. Uses the filters. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ # Get units from db name = self.filterNameText.GetValue() query = """ SELECT id, name, level FROM units WHERE id IN (SELECT DISTINCT unit FROM units_teams WHERE team = '""" + self.selectedTeamId + """') """ cursor = conn.execute(query) i = 0 self.teamUnitList.DeleteAllItems() for row in cursor: self.teamUnitList.InsertItem(i, row[1]) self.teamUnitList.SetItem(i, 0, str(row[1])) self.teamUnitList.SetItem(i, 1, "Lv." + str(row[2])) self.teamUnitList.SetItemData(i, int(row[0])) i = i + 1 def createTeam(self, event=None): """Opens a dialog to create a new team. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ with DialogNewTeam(parent=self) as dlg: if dlg.ShowModal() == wx.ID_OK: self.populateTeamList() def deleteTeam(self, event=None): """Deletes a team. Shows a confirmation dialog first. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ message = "Are you sure you want to delete the team " + \ self.selectedTeamName + "?\n\nThis can't be undone." with DialogConfirm(parent=self, message=message) as dlg: if dlg.ShowModal() == wx.ID_OK: cursor = conn.execute( "DELETE FROM units_teams WHERE team = ?", (self.selectedTeamId,)) cursor = conn.execute( "DELETE FROM teams WHERE id = ?", (self.selectedTeamId,) ) conn.commit() # Clear the selection and hide the details self.selectedIndex = None self.detailsSizer.ShowItems(False) self.populateTeamList(event=None) self.GetParent().frameUnits.populateUnitList(event=None) def addToTeam(self, event=None): """Adds unit to the currently selected team. Adds all the units selected in self.allUnitList. Refreshes both unit lists and also the unit list in the units panel. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ selectedIndex = self.allUnitList.GetFirstSelected() while (selectedIndex != -1): unitId = self.allUnitList.GetItemData(selectedIndex) query = "INSERT INTO units_teams (team, unit) VALUES (?, ?)"; cursor = conn.execute(query, (self.selectedTeamId, unitId)) selectedIndex = self.allUnitList.GetNextSelected(selectedIndex) conn.commit() self.populateTeamUnitList(event=None) self.populateUnitList(event=None) self.GetParent().frameUnits.populateUnitList(event=None) def removeFromTeam(self, event=None): """Removes units the currently selected team. Removes all the units selected in self.teamUnitList. Refreshes both unit lists and also the unit list in the units panel. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ selectedIndex = self.teamUnitList.GetFirstSelected() while (selectedIndex != -1): unitId = self.teamUnitList.GetItemData(selectedIndex) query = "DELETE FROM units_teams WHERE team = ? AND unit = ?"; cursor = conn.execute(query, (self.selectedTeamId, unitId)) selectedIndex = self.teamUnitList.GetNextSelected(selectedIndex) conn.commit() self.populateTeamUnitList(event=None) self.populateUnitList(event=None) self.GetParent().frameUnits.populateUnitList(event=None) def teamSelected(self, event=None): """Shows the team details. Called when a team is selected. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ self.selectedTeamId = \ str(self.teamList.GetItemData(self.teamList.GetFirstSelected())) self.selectedTeamName = \ self.teamList.GetItem(self.teamList.GetFirstSelected(), 0).GetText() self.selectedTeamPriority = \ self.teamList.GetItem(self.teamList.GetFirstSelected(), 1).GetText() self.populateTeamUnitList(event=None) # Unbind for the automatic change self.Unbind(wx.EVT_TEXT, self.titleText) self.titleText.SetValue(self.selectedTeamName) # Rebind self.Bind(wx.EVT_TEXT, self.changeTitle, self.titleText); # Unbind for the automatic change self.Unbind(wx.EVT_TEXT, self.priorityText); self.priorityText.SetValue(self.selectedTeamPriority) # Rebind self.Bind(wx.EVT_TEXT, self.changePriority, self.priorityText); # Show details self.detailsSizer.ShowItems(True) def changeTitle(self, event): """Prepares for a title change. Called everytime the selected team name is changed. It schedules a call to self.saveNewTitle in two seconds, to give the user time to enter the full title. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ newTitle = self.titleText.GetValue() self.titleChangePending = True self.titleTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.saveNewTitle, self.titleTimer) self.titleTimer.StartOnce(2000) def saveNewTitle(self, event=None): """Saves the team name to the database. Called two seconds after the last change in self.titleText. If the name is not valid (i.e. less than two characters), it doesn't apply the change and sets the font color to red to indicate error. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ if (self.titleChangePending): newTitle = self.titleText.GetValue().replace("\n", "").strip() self.titleChangePending = False if len(newTitle.replace(" ", "")) > 2: self.titleText.SetStyle( 0, len(self.titleText.GetValue()), wx.TextAttr(colText=wx.BLACK) ) query = "UPDATE teams SET name = ? WHERE id = ?"; cursor = conn.execute(query, (newTitle, self.selectedTeamId)) conn.commit() self.populateTeamList(event=None) else: monospaceFont = wx.Font( pointSize=8, family=wx.FONTFAMILY_TELETYPE, style=wx.FONTSTYLE_NORMAL, weight=wx.FONTWEIGHT_NORMAL ) self.titleText.SetStyle( 0, len(self.titleText.GetValue()), wx.TextAttr(colText=wx.RED) ) def changePriority(self, event): """Prepares for a priority change. Called everytime the selected team priority is changed. It schedules a call to self.saveNewPriority in two seconds, to give the user time to enter the full text. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ newPriority = self.priorityText.GetValue() self.priorityChangePending = True self.priorityTimer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.saveNewPriority, self.priorityTimer) self.priorityTimer.StartOnce(2000) def saveNewPriority(self, event=None): """Saves the team priority to the database. Called two seconds after the last change in self.priorityText. If the priority is not valid (i.e. not a number, lower than 0 or greater than 50), it doesn't apply the change and sets the font color to red to indicate error. Parameters ---------- event : wx.Event, optional The event that triggered the call (default is None). """ if (self.priorityChangePending): newPriority = self.priorityText.GetValue().replace("\n", "").strip() self.priorityChangePending = False if newPriority.isnumeric() and \ int(newPriority) >= 0 and int(newPriority) <= 50: self.priorityText.SetStyle( 0, len(self.priorityText.GetValue()), wx.TextAttr(colText=wx.BLACK) ) query = "UPDATE teams SET priority = ? WHERE id = ?"; cursor = conn.execute(query, (newPriority, self.selectedTeamId)) conn.commit() self.populateTeamList(event=None) self.GetParent().frameUnits.populateUnitList(event=None) else: self.priorityText.SetStyle( 0, len(self.priorityText.GetValue()), wx.TextAttr(colText=wx.RED) )