NovaBryan/EVE_Dump
Log | Files | 📦 下载最新源码 (.zip) | Refs | README

notepad.py (29255B)


      1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\shared\neocom\notepad.py
      2 import blue
      3 import evelink
      4 import uthread
      5 import localization
      6 from bannedwords.client import bannedwords
      7 from carbonui import uiconst
      8 from carbon.common.script.util.timerstuff import AutoTimer
      9 from carbonui.primitives.container import Container
     10 from carbonui.util.various_unsorted import SortListOfTuples
     11 from carbonui.uicore import uicore
     12 from carbonui.control.button import Button
     13 from eve.client.script.ui.control.divider import Divider
     14 from eve.client.script.ui.control.entries.generic import Generic
     15 from eve.client.script.ui.control.entries.util import GetFromClass
     16 from eve.client.script.ui.control.eveEditPlainText import EditPlainText
     17 from eve.client.script.ui.control.eveIcon import Icon
     18 from eve.client.script.ui.control.eveLabel import EveLabelMedium
     19 from eve.client.script.ui.control.eveScroll import Scroll
     20 from carbonui.control.window import Window
     21 from eve.client.script.ui.control.listgroup import ListGroup
     22 from eve.client.script.ui.shared.userentry import User
     23 from eve.client.script.ui.util.utilWindows import NamePopup
     24 from menu import MenuLabel
     25 from player_messaging.client.ui_message import prompt_player_fsd_dialog
     26 from utillib import KeyVal
     27 from eve.common.lib import appConst as const
     28 
     29 class NotepadWindow(Window):
     30     __guid__ = 'form.Notepad'
     31     __notifyevents__ = []
     32     default_width = 400
     33     default_height = 300
     34     default_windowID = 'notepad'
     35     default_captionLabelPath = 'UI/Notepad/Notepad'
     36     default_iconNum = 'res:/ui/Texture/WindowIcons/notepad.png'
     37 
     38     def ApplyAttributes(self, attributes):
     39         Window.ApplyAttributes(self, attributes)
     40         self.SetMinSize((350, 200))
     41         self.sr.main.padding = const.defaultPadding
     42         self.sr.nav = Container(name='nav', align=uiconst.TOLEFT, width=settings.user.ui.Get('notepadscrollistwidth', 128), parent=self.sr.main, idx=0)
     43         divider = Divider(name='divider', align=uiconst.TOLEFT, idx=1, width=const.defaultPadding, parent=self.sr.main, state=uiconst.UI_NORMAL)
     44         divider.Startup(self.sr.nav, 'width', 'x', 100, 256)
     45         self.sr.navbuttons = Container(name='navbuttons', align=uiconst.TOBOTTOM, parent=self.sr.nav, height=38)
     46         self.sr.newfolder = Button(parent=self.sr.navbuttons, label=localization.GetByLabel('UI/Notepad/NewFolder'), padTop=4, align=uiconst.TOTOP, func=self.NewFolderClick)
     47         self.sr.newnote = Button(parent=self.sr.navbuttons, label=localization.GetByLabel('UI/Notepad/NewNote'), padTop=4, align=uiconst.TOTOP, func=self.NewNote, args=(0,))
     48         self.sr.navbuttons.height = sum([ each.height + each.padTop + each.padBottom for each in self.sr.navbuttons.children ]) + 4
     49         self.sr.senderlist = Scroll(name='senderlist', parent=self.sr.nav)
     50         self.sr.senderlist.OnDelete = self.OnDelete
     51         self.sr.senderlist.multiSelect = 0
     52         self.sr.note = Container(name='notecontainer', align=uiconst.TOALL, parent=self.sr.main, pos=(0, 0, 0, 0))
     53         self.sr.titlecont = Container(name='titlecontainer', align=uiconst.TOTOP, height=48, parent=self.sr.note)
     54         self.sr.icon = Icon(parent=self.sr.titlecont, pos=(0, 0, 32, 32))
     55         self.sr.titletext = EveLabelMedium(parent=self.sr.titlecont, align=uiconst.TOTOP, padding=(40, 5, 5, 0))
     56         self.sr.titletext.OnSizeChanged = self.OnTitleSizeChanged
     57         self.sr.browser = EditPlainText(parent=self.sr.note, showattributepanel=1, counterMax=3900, countWithTags=True)
     58         self.sr.browser.sr.window = self
     59         self.sr.browser.allowPrivateDrops = 1
     60         self.OnScale_ = self._OnResize
     61         divider.OnSizeChanging = self.OnDividerMove
     62         self.starting = 1
     63         import log
     64         log.LogInfo('Starting Notepad')
     65         self.folders = {}
     66         self.notes = {}
     67         self.charnotes = {}
     68         self.settings = {}
     69         self.settingStrs = {}
     70         self.notedata = {}
     71         self.lastid = 0
     72         uthread.new(self.LoadNotesData)
     73 
     74     def OnTitleSizeChanged(self, *args, **kwds):
     75         self.sr.titlecont.height = max(48, self.sr.titletext.textheight + self.sr.titletext.padTop * 2)
     76 
     77     def LoadNotesData(self):
     78         notes = sm.RemoteSvc('charMgr').GetOwnerNoteLabels()
     79         self.folderIDs = []
     80         badNotes = [ note for note in notes if note.label[0] not in ('I', 'N', 'S') ]
     81         for entry in notes:
     82             if entry.label[0] in ('I', 'N') and entry.label[1] == ':':
     83                 n = KeyVal()
     84                 n.noteID = entry.noteID
     85                 n.text = None
     86                 if entry.label[0] == 'N':
     87                     n.label = entry.label[2:]
     88                     self.notes['N:' + str(entry.noteID)] = n
     89             if entry.label.startswith('S:'):
     90                 label = entry.label[2:]
     91                 if label in self.settings:
     92                     self.settings[label].append(entry.noteID)
     93                 else:
     94                     self.settings[label] = [entry.noteID]
     95 
     96         parallelCalls = []
     97         for note in badNotes:
     98             parallelCalls.append((sm.RemoteSvc('charMgr').RemoveOwnerNote, (note.noteID,)))
     99 
    100         if len(parallelCalls):
    101             uthread.parallel(parallelCalls)
    102         folders = self.GetSetting('Folders')
    103         if folders is not None:
    104             for entry in folders.split('|'):
    105                 tmp = entry.split('::')
    106                 if len(tmp) != 4:
    107                     continue
    108                 id, _type, parentID, label = tmp
    109                 n = KeyVal()
    110                 n.type = _type
    111                 if parentID == 'None':
    112                     parentID = 0
    113                 n.parent = int(parentID)
    114                 n.data = label
    115                 self.folders[int(id)] = n
    116                 self.lastid = max(self.lastid, int(id))
    117 
    118         else:
    119             n = KeyVal()
    120             n.type = 'F'
    121             n.parent = 0
    122             n.data = localization.GetByLabel('UI/Notepad/Main')
    123             self.folders[1] = n
    124             self.lastid = 1
    125         self.starting = 0
    126         self.LoadNotes()
    127         self.sr.autosaveTimer = AutoTimer(60000, self.SaveNote)
    128         self.activeNode = None
    129         self.ShowNote(settings.char.notepad.Get('activeNote', None))
    130 
    131     def CloseByUser(self, *args):
    132         if not self.SaveNote():
    133             return
    134         settings.user.ui.Set('notepadscrollistwidth', self.sr.nav.width)
    135         self.SaveFolders()
    136         Window.CloseByUser(self)
    137 
    138     def SaveFolders(self):
    139         folderstr = ''
    140         for key, value in self.folders.items():
    141             folderstr += '%s::%s::%s::%s|' % (key,
    142              value.type,
    143              value.parent,
    144              value.data)
    145 
    146         uthread.new(self.SetSetting, 'Folders', folderstr)
    147 
    148     def SetSetting(self, id, string):
    149         if id in self.settingStrs and self.settingStrs[id] == string:
    150             return
    151         if id in self.settings:
    152             dbIDs = self.settings[id]
    153             dbIDs.sort()
    154         else:
    155             dbIDs = []
    156         idStr = 'S:' + str(id)
    157         newIDs = []
    158         while len(string) > 0:
    159             part = string[:6000]
    160             string = string[6000:]
    161             if len(dbIDs):
    162                 noteID = int(dbIDs.pop(0))
    163                 sm.RemoteSvc('charMgr').EditOwnerNote(noteID, idStr, part)
    164             else:
    165                 noteID = sm.RemoteSvc('charMgr').AddOwnerNote(idStr, part)
    166             newIDs.append(noteID)
    167 
    168         parallelCalls = []
    169         for noteID in dbIDs:
    170             parallelCalls.append((sm.RemoteSvc('charMgr').RemoveOwnerNote, (int(noteID),)))
    171 
    172         if len(parallelCalls):
    173             uthread.parallel(parallelCalls)
    174         self.settings[id] = newIDs
    175 
    176     def GetSetting(self, id):
    177         if id in self.settingStrs:
    178             return self.settingStrs[id]
    179         if id not in self.settings:
    180             return None
    181         parallelCalls = []
    182         for noteID in self.settings[id]:
    183             parallelCalls.append((sm.RemoteSvc('charMgr').GetOwnerNote, (int(noteID),)))
    184 
    185         if len(parallelCalls):
    186             notes = uthread.parallel(parallelCalls)
    187         self.settingStrs[id] = ''
    188         for note in notes:
    189             self.settingStrs[id] += note[0].note
    190 
    191         return self.settingStrs[id]
    192 
    193     def WriteNote(self, name, text, folderID, noteID = None):
    194         if not noteID:
    195             noteID = sm.RemoteSvc('charMgr').AddOwnerNote('N:' + name, '<br>')
    196         self.AddNote(folderID, 'N', noteID)
    197         n = KeyVal()
    198         n.noteID = noteID
    199         n.label = name
    200         n.text = text
    201         self.notes['N:' + str(noteID)] = n
    202         uthread.pool('notepad::SetNote', sm.RemoteSvc('charMgr').EditOwnerNote, int(noteID), 'N:' + self.notes['N:' + str(noteID)].label, text)
    203         return noteID
    204 
    205     def GetNotesByType(self, t = 'N'):
    206         notes = []
    207         for n in self.notes:
    208             if n.find(t + ':') == 0:
    209                 v = self.notes[n]
    210                 v.parentID = self.GetParentFolder(v.noteID)
    211                 v.parentName = ''
    212                 parent = self.folders.get(v.parentID)
    213                 if parent:
    214                     v.parentName = parent.data
    215                 notes.append(v)
    216 
    217         return notes
    218 
    219     def GetFolders(self):
    220         folders = []
    221         for f in self.folders:
    222             folder = self.folders[f]
    223             if folder.type == 'F':
    224                 v = KeyVal()
    225                 v.noteID = f
    226                 v.label = folder.data
    227                 folders.append(v)
    228 
    229         return folders
    230 
    231     def GetNoteText(self, noteID):
    232         ret = None
    233         ret = note = self.notes.get('N:' + str(noteID), None)
    234         if not note or note.text is None:
    235             _note = sm.RemoteSvc('charMgr').GetOwnerNote(noteID)
    236             note = KeyVal()
    237             note.text = _note[0].note.strip()
    238             note.label = _note[0].label[2:]
    239             self.notes['N:' + str(noteID)].text = note.text
    240             self.notes['N:' + str(noteID)].label = note.label
    241             ret = note
    242         return ret
    243 
    244     def _OnResize(self, *etc):
    245         self.OnDividerMove()
    246 
    247     def OnDividerMove(self, *etc):
    248         if not self.destroyed:
    249             self.sr.nav.width = max(100, min(self.sr.nav.width, self.displayWidth - 154))
    250 
    251     def GoTo(self, URL, data = None, args = {}, scrollTo = None):
    252         uicore.cmd.OpenBrowser(URL, data=data, args=args)
    253 
    254     def LoadNotes(self):
    255         while self.starting:
    256             blue.pyos.BeNice()
    257 
    258         scrolllist = []
    259         notes = self.GetNotes(0)
    260         for id, n in notes.items():
    261             groupID = ('notepad', id)
    262             scrolllist.append((n.data.lower(), GetFromClass(ListGroup, {'GetSubContent': self.GetGroupSubContent,
    263               'label': n.data,
    264               'id': groupID,
    265               'groupItems': self.GroupGetContentIDList(groupID),
    266               'iconMargin': 18,
    267               'showlen': 1,
    268               'state': 0,
    269               'sublevel': 0,
    270               'MenuFunction': self.GroupMenu,
    271               'allowGuids': ['listentry.User',
    272                              'listentry.Sender',
    273                              'listentry.PlaceEntry',
    274                              'listentry.NoteItem'],
    275               'DropData': self.GroupDropNode,
    276               'ChangeLabel': self.GroupChangeLabel,
    277               'DeleteFolder': self.GroupDeleteFolder,
    278               'GetContentIDList': self.GroupGetContentIDList,
    279               'CreateEntry': self.GroupCreateEntry,
    280               'RefreshScroll': self.RefreshNotes})))
    281 
    282         scrolllist = SortListOfTuples(scrolllist)
    283         scrolllist.append(GetFromClass(ListGroup, {'GetSubContent': self.GetAllFolderContent,
    284          'label': localization.GetByLabel('UI/Notepad/AllNotes'),
    285          'id': ('All Notes', 0),
    286          'groupItems': self.notes,
    287          'iconMargin': 18,
    288          'showlen': 1,
    289          'state': 0,
    290          'sublevel': 0,
    291          'showicon': 'res:/ui/Texture/WindowIcons/notegroup.png',
    292          'state': 'locked'}))
    293         if not self.destroyed:
    294             self.sr.senderlist.Load(fixedEntryHeight=17, contentList=scrolllist, scrollTo=self.sr.senderlist.GetScrollProportion())
    295             self.sr.senderlist.hiliteSorted = 0
    296 
    297     def RefreshNotes(self):
    298         self.LoadNotes()
    299 
    300     def GetAllFolderContent(self, nodedata, newitems = 0):
    301         scrolllist = []
    302         for key, note in self.notes.iteritems():
    303             noteID = 'N:' + str(note.noteID)
    304             entry = GetFromClass(NoteItem, self.GetNoteItemData(noteID=noteID, noteType='NA'))
    305             if entry is not None:
    306                 scrolllist.append((note.label.lower(), entry))
    307 
    308         scrolllist = SortListOfTuples(scrolllist)
    309         return scrolllist
    310 
    311     def GetGroupSubContent(self, nodedata, newitems = 0):
    312         scrolllist = []
    313         notelist = self.GetNotes(nodedata.id[1])
    314         if len(notelist):
    315             qi = 1
    316             NoteListLength = len(notelist)
    317             for id, note in notelist.items():
    318                 entry = self.GroupCreateEntry((note.data, id), nodedata.sublevel + 1)
    319                 if entry is not None:
    320                     scrolllist.append((entry.label.lower(), entry))
    321 
    322             if len(nodedata.groupItems) != len(scrolllist):
    323                 nodedata.groupItems = self.GroupGetContentIDList(nodedata.id)
    324         scrolllist = SortListOfTuples(scrolllist)
    325         return scrolllist
    326 
    327     def GroupMenu(self, node):
    328         m = []
    329         if node.sublevel < 5:
    330             m.append((MenuLabel('UI/Notepad/NewFolder'), self.NewFolder, (node.id[1], node)))
    331         m.append((MenuLabel('UI/Notepad/NewNote'), self.NewNote, (node.id[1], node)))
    332         return m
    333 
    334     def GroupDropNode(self, id, nodes):
    335         for node in nodes:
    336             nodeMoved = False
    337             if node.__guid__ in ('listentry.User', 'listentry.Sender') and node.charID != session.charid:
    338                 self.AddNote(id[1], 'I', node.charID)
    339                 nodeMoved = True
    340             if node.__guid__ == 'listentry.NoteItem' and not self.NoteAlreadyExistsInFolder(node.noteID, id[1]):
    341                 noteID = node.noteID
    342                 if noteID in self.notes:
    343                     noteID = self.notes[noteID].noteID
    344                 self.AddNote(id[1], 'N', str(noteID))
    345                 nodeMoved = True
    346             if nodeMoved and not uicore.uilib.Key(uiconst.VK_SHIFT):
    347                 if node.id:
    348                     self.RemoveFolder(node.id[1], ask=0)
    349 
    350         self.LoadNotes()
    351 
    352     def GroupChangeLabel(self, id, newname):
    353         self.RenameFolder(id[1], name=newname)
    354 
    355     def GroupDeleteFolder(self, id):
    356         noteID = id[1]
    357         parentID = self.GetParentFolder(noteID)
    358         notes = self.GetNotes(noteID)
    359         for id, note in notes.items():
    360             if note.type == 'F':
    361                 self.GroupDeleteFolder((0, id))
    362                 continue
    363             self.DeleteFolderNote(id)
    364 
    365         self.DeleteFolderNote(noteID)
    366         self.LoadNotes()
    367         self.SaveFolders()
    368 
    369     def GroupCreateEntry(self, id, sublevel):
    370         note, id = self.folders[id[1]], id[1]
    371         if note.type == 'F':
    372             groupID = ('notepad', id)
    373             return GetFromClass(ListGroup, {'GetSubContent': self.GetGroupSubContent,
    374              'label': note.data,
    375              'id': groupID,
    376              'groupItems': self.GroupGetContentIDList(groupID),
    377              'iconMargin': 18,
    378              'showlen': 1,
    379              'state': 0,
    380              'sublevel': sublevel,
    381              'MenuFunction': self.GroupMenu,
    382              'allowGuids': ['listentry.User', 'listentry.Sender', 'listentry.NoteItem'],
    383              'DropData': self.GroupDropNode,
    384              'ChangeLabel': self.GroupChangeLabel,
    385              'DeleteFolder': self.GroupDeleteFolder,
    386              'GetContentIDList': self.GroupGetContentIDList,
    387              'CreateEntry': self.GroupCreateEntry,
    388              'RefreshScroll': self.RefreshNotes})
    389         if note.type == 'I':
    390             charinfo = cfg.eveowners.Get(note.data)
    391             return GetFromClass(User, {'charID': int(note.data),
    392              'id': (note.data, id),
    393              'info': charinfo,
    394              'color': None,
    395              'GetMenu': self.GetCharMenu,
    396              'OnClick': self.OnCharClick,
    397              'sublevel': sublevel,
    398              'noteID': 'I:' + str(note.data),
    399              'label': charinfo.name})
    400         if note.type == 'N':
    401             noteID = 'N:' + str(note.data)
    402             if noteID in self.notes:
    403                 return GetFromClass(NoteItem, self.GetNoteItemData(noteID=noteID, noteType='N', sublevel=sublevel, folderID=id))
    404         del self.folders[id]
    405 
    406     def GetNoteItemData(self, noteID, noteType, sublevel = 1, folderID = None):
    407         note = self.notes[noteID]
    408         return {'itemID': None,
    409          'typeID': None,
    410          'id': (note.label, folderID or noteID),
    411          'label': note.label,
    412          'sublevel': sublevel,
    413          'noteID': noteID,
    414          'type': noteType,
    415          'ignoreRightClick': 1,
    416          'get_link': lambda : evelink.Link(url=format_note_url(noteID), text=note.label),
    417          'is_private': True}
    418 
    419     def GroupGetContentIDList(self, id):
    420         ids = self.GetNotes(id[1])
    421         return [ (self.folders[id].data, id) for id in ids ]
    422 
    423     def GetCharMenu(self, node):
    424         m = [None] + [(localization.GetByLabel('UI/Notepad/RemoveCharacter'), self.RemoveFolder, (node.id[1],))]
    425         return m
    426 
    427     def OnDelete(self, *args):
    428         if not self.destroyed:
    429             sel = self.sr.senderlist.GetSelected()
    430             for entry in sel[:]:
    431                 if not entry.noteID:
    432                     continue
    433                 if entry.noteID.startswith('F'):
    434                     self.GroupDeleteFolder(entry.id)
    435                 elif entry.noteID.startswith('N') and entry.type == 'NA':
    436                     self.DeleteNote(entry.id[1])
    437                 else:
    438                     self.RemoveFolder(entry.id[1])
    439 
    440             self.LoadNotes()
    441 
    442     def OnCharClick(self, entry):
    443         self.ShowNote(entry.sr.node.noteID)
    444 
    445     def OnBookmarkClick(self, entry):
    446         self.ShowNote(entry.sr.node.noteID)
    447 
    448     def ShowNote(self, id, force = 0):
    449         while getattr(self.sr.browser, 'loading', 0):
    450             blue.pyos.synchro.Yield()
    451 
    452         if not force and hasattr(self, 'activeNode') and id is not None and self.activeNode == id:
    453             return True
    454         if not force and not self.SaveNote():
    455             return False
    456         if id is None or not id.upper().startswith('I:') and str(id) not in self.notes:
    457             self.sr.titletext.text = (localization.GetByLabel('UI/Notepad/GeneralInformation'),)
    458             self.sr.icon.LoadIcon('res:/ui/Texture/WindowIcons/notepad.png')
    459             self.sr.icon.width = self.sr.icon.height = 32
    460             self.sr.browser.SetValue(localization.GetByLabel('UI/Notepad/NotepadIntro'))
    461             self.sr.browser.ReadOnly()
    462             self.activeNote = None
    463             return True
    464         self.sr.browser.Editable()
    465         noteID = id.split(':')
    466         if len(noteID) != 2:
    467             return True
    468         t, id = noteID
    469         if not force and self.activeNode == t + ':' + str(id):
    470             return True
    471         if t == 'I':
    472             charid = int(id)
    473             note = sm.RemoteSvc('charMgr').GetNote(charid)
    474             charinfo = cfg.eveowners.Get(charid)
    475             sm.GetService('photo').GetPortrait(charid, 64, self.sr.icon)
    476             self.sr.titletext.text = charinfo.name
    477             self.sr.browser.SetValue(note)
    478         if t == 'N':
    479             if 'N:' + str(id) in self.notes:
    480                 noteID = int(id)
    481                 if self.notes['N:' + str(id)].text is None:
    482                     note = sm.RemoteSvc('charMgr').GetOwnerNote(noteID)
    483                     self.notes['N:' + str(id)].text = note[0].note.strip()
    484                     self.notes['N:' + str(id)].label = note[0].label[2:]
    485                 self.sr.icon.LoadIcon('res:/ui/Texture/WindowIcons/note.png')
    486                 self.sr.icon.SetSize(32, 32)
    487                 self.sr.titletext.text = self.notes['N:' + str(id)].label
    488                 self.sr.browser.SetValue(self.notes['N:' + str(id)].text)
    489         self.activeNode = t + ':' + str(id)
    490         settings.char.notepad.Set('activeNote', self.activeNode)
    491         uicore.registry.SetFocus(self.sr.browser)
    492         return True
    493 
    494     def SaveNote(self, *args):
    495         if self.destroyed:
    496             self.sr.autosaveTimer = None
    497             return
    498         if getattr(self, 'activeNode', None) is not None:
    499             t, id = self.activeNode.split(':')
    500             txt = self.sr.browser.GetValue()
    501             if len(txt) > 3900:
    502                 return not prompt_player_fsd_dialog('NoteTooLong', {'total': len(txt)}, uiconst.YESNO) == uiconst.ID_YES
    503             if t == 'I':
    504                 uthread.pool('notepad::SetNote', sm.RemoteSvc('charMgr').SetNote, int(id), txt)
    505             if t == 'N':
    506                 if 'N:' + str(id) in self.notes and self.notes['N:' + str(id)].text != txt:
    507                     self.notes['N:' + str(id)].text = txt
    508                     uthread.pool('notepad::SetNote', sm.RemoteSvc('charMgr').EditOwnerNote, int(id), 'N:' + self.notes['N:' + str(id)].label, txt)
    509         return True
    510 
    511     def GetNotes(self, folderID):
    512         notes = {}
    513         for id, n in self.folders.iteritems():
    514             if n.parent != folderID:
    515                 continue
    516             notes[id] = n
    517 
    518         return notes
    519 
    520     def GetNote(self, noteID):
    521         if noteID in self.folders.keys():
    522             return self.folders[noteID]
    523 
    524     def GetParentFolder(self, nodeID):
    525         n = self.folders.get(nodeID, None)
    526         if n:
    527             return n.parent
    528         else:
    529             return
    530 
    531     def RenameFolder(self, folderID = 0, entry = None, name = None, *args):
    532         if name is None:
    533             ret = NamePopup(localization.GetByLabel('UI/Notepad/FolderName'), localization.GetByLabel('UI/Notepad/TypeNewFolderName'), maxLength=20)
    534             if ret is None:
    535                 return self.folders[folderID].data
    536             name = ret
    537         bannedwords.check_words_allowed(name)
    538         if self.AlreadyExists('F', name):
    539             prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Notepad/FolderAlreadyExists')})
    540             return
    541         self.folders[folderID].data = name
    542         return name
    543 
    544     def NewFolder(self, folderID = 0, node = None, *args):
    545         ret = NamePopup(localization.GetByLabel('UI/Notepad/FolderName'), localization.GetByLabel('UI/Notepad/TypeNewFolderName'), maxLength=20)
    546         if ret is not None:
    547             name = ret
    548             bannedwords.check_words_allowed(name)
    549             if self.AlreadyExists('F', name):
    550                 prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Notepad/FolderAlreadyExists')})
    551                 return
    552             self.AddNote(folderID, 'F', name)
    553             data = {'label': name,
    554              'folderid': self.lastid,
    555              'parent': folderID}
    556             self.LoadNotes()
    557             self.SaveFolders()
    558             return data
    559 
    560     def NewFolderClick(self, *args):
    561         self.NewFolder()
    562 
    563     def RemoveFolder(self, folderID = 0, entry = None, ask = 1):
    564         if folderID in self.folders:
    565             if not ask or prompt_player_fsd_dialog('DeleteEntry', {}, uiconst.YESNO) == uiconst.ID_YES:
    566                 if self.activeNode == self.folders[folderID].type + ':' + str(self.folders[folderID].data):
    567                     self.activeNode = None
    568                     self.ShowNote(None)
    569                 parent = self.GetParentFolder(folderID)
    570                 del self.folders[folderID]
    571                 self.LoadNotes()
    572 
    573     def NewNote(self, folderID = 0, node = None, *args):
    574         ret = NamePopup(localization.GetByLabel('UI/Notepad/NoteName'), localization.GetByLabel('UI/Notepad/TypeNewNoteLabel'), maxLength=80)
    575         if ret is not None:
    576             name = ret
    577             if self.AlreadyExists('N', name):
    578                 prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Notepad/NoteAlreadyExists')})
    579                 return
    580             noteID = sm.RemoteSvc('charMgr').AddOwnerNote('N:' + name, '<br>')
    581             if folderID:
    582                 self.AddNote(folderID, 'N', noteID)
    583             n = KeyVal()
    584             n.noteID = noteID
    585             n.label = name
    586             n.text = '<br>'
    587             self.notes['N:' + str(noteID)] = n
    588             self.LoadNotes()
    589             self.ShowNote('N:' + str(noteID))
    590 
    591     def RenameNote(self, noteID):
    592         if noteID in self.folders:
    593             noteID = self.folders[noteID].type + ':' + str(self.folders[noteID].data)
    594         if noteID not in self.notes:
    595             return
    596         ret = NamePopup(localization.GetByLabel('UI/Notepad/NoteName'), localization.GetByLabel('UI/Notepad/TypeNewNoteLabel'), self.notes[noteID].label, maxLength=80)
    597         if ret is not None:
    598             if self.AlreadyExists('N', ret):
    599                 prompt_player_fsd_dialog('CustomInfo', {'info': localization.GetByLabel('UI/Notepad/NoteAlreadyExists')})
    600                 return
    601             self.notes[noteID].label = ret
    602             sm.RemoteSvc('charMgr').EditOwnerNote(self.notes[noteID].noteID, 'N:' + ret)
    603             self.LoadNotes()
    604             if getattr(self, 'activeNode', None) == noteID:
    605                 self.sr.titletext.text = ret
    606 
    607     def NoteAlreadyExistsInFolder(self, noteID, folderID):
    608         notesInFolder = self.GetNotes(folderID)
    609         noteIdsInFolder = [ n.data for n in notesInFolder.itervalues() ]
    610         actualId = noteID
    611         if noteID in self.notes:
    612             actualId = self.notes[noteID].noteID
    613         if str(actualId) in noteIdsInFolder:
    614             return True
    615         else:
    616             return False
    617 
    618     def AlreadyExists(self, type, name):
    619         if type == 'N':
    620             for key, value in self.notes.iteritems():
    621                 if value.label == name:
    622                     return True
    623 
    624         if type == 'F':
    625             for key, value in self.folders.iteritems():
    626                 if value.type == type and value.data == name:
    627                     return True
    628 
    629         return False
    630 
    631     def AddNote(self, parent, type, data):
    632         self.lastid += 1
    633         n = KeyVal()
    634         n.type = type
    635         n.parent = parent
    636         n.data = data
    637         self.folders[self.lastid] = n
    638 
    639     def DeleteFolderNote(self, noteID):
    640         del self.folders[noteID]
    641 
    642     def DeleteNote(self, noteID):
    643         if noteID in self.notes:
    644             if prompt_player_fsd_dialog('DeleteNote', {}, uiconst.YESNO) == uiconst.ID_YES:
    645                 if self.activeNode == noteID:
    646                     self.activeNode = None
    647                     self.ShowNote(None)
    648                 t, id = noteID.split(':')
    649                 for key, value in self.folders.items():
    650                     if value.type == t and str(value.data) == id:
    651                         del self.folders[key]
    652 
    653                 note = self.notes[noteID]
    654                 sm.RemoteSvc('charMgr').RemoveOwnerNote(int(note.noteID))
    655                 del self.notes[noteID]
    656                 self.LoadNotes()
    657 
    658 
    659 class NoteItem(Generic):
    660     __guid__ = 'listentry.NoteItem'
    661     isDragObject = True
    662 
    663     def Startup(self, *args):
    664         Generic.Startup(self, *args)
    665         self.sr.icon = Icon(icon='res:/ui/Texture/WindowIcons/note.png', parent=self, left=0, top=-4, size=24, ignoreSize=True)
    666 
    667     def Load(self, node):
    668         Generic.Load(self, node)
    669         sublevelCorrection = self.sr.node.scroll.sr.get('sublevelCorrection', 0)
    670         sublevel = max(0, node.Get('sublevel', 0) - sublevelCorrection)
    671         self.sr.icon.state = uiconst.UI_NORMAL
    672         self.sr.label.left = 24 + max(0, sublevel * 16)
    673         self.sr.icon.left = 0 + max(0, sublevel * 16)
    674         self.sr.label.text = node.label
    675         self.sr.label.Update()
    676 
    677     def OnClick(self, *args):
    678         if self.sr.node:
    679             if not uicore.uilib.Key(uiconst.VK_CONTROL):
    680                 noteWindow = NotepadWindow.GetIfOpen()
    681                 if noteWindow and not noteWindow.ShowNote(self.sr.node.noteID):
    682                     return
    683             self.sr.node.scroll.SelectNode(self.sr.node)
    684 
    685     def GetMenu(self):
    686         m = Generic.GetMenu(self)
    687         noteWindow = NotepadWindow.Open()
    688         if self.sr.node.type == 'N':
    689             m += [(MenuLabel('UI/Notepad/RenameNote'), noteWindow.RenameNote, (self.sr.node.id[1],))]
    690             m += [(MenuLabel('UI/Notepad/RemoveNote'), noteWindow.RemoveFolder, (self.sr.node.id[1],))]
    691         if self.sr.node.type == 'NA':
    692             m += [(MenuLabel('UI/Notepad/RenameNote'), noteWindow.RenameNote, (self.sr.node.id[1],))]
    693             m += [(MenuLabel('UI/Notepad/DeleteNote'), noteWindow.DeleteNote, (self.sr.node.id[1],))]
    694         return m
    695 
    696     def GetHeight(self, *args):
    697         return 18
    698 
    699     def GetDragData(self, *args):
    700         nodes = [self.sr.node]
    701         return nodes
    702 
    703 
    704 LINK_SCHEME = 'note'
    705 
    706 def register_link_handlers(registry):
    707     registry.register(LINK_SCHEME, handle_note_link, hint='UI/Notepad/ShowNote')
    708 
    709 
    710 def handle_note_link(url):
    711     note_id = parse_note_url(url)
    712     note_window = NotepadWindow.Open()
    713     note_window.ShowNote(note_id)
    714 
    715 
    716 def parse_note_url(url):
    717     return url[url.index(':') + 1:]
    718 
    719 
    720 def format_note_url(note_id):
    721     return u'{}:{}'.format(LINK_SCHEME, note_id)