def __init__(self, parent, env, path=""): StyledTextCtrl.__init__(self, parent, env) wx.FileDropTarget.__init__(self) self.SetDropTarget(self) self.path = path self.file_encoding = "utf-8" self.modified_externally = False self.static_title = None self.sig_title_changed = Signal(self) self.sig_status_changed = Signal(self) self.SetTabIndents(True) self.SetBackSpaceUnIndents(True) self.SetViewWhiteSpace(wx.stc.STC_WS_VISIBLEALWAYS) self.SetWhitespaceForeground(True, "#dddddd") self.SetEdgeMode(wx.stc.STC_EDGE_LINE) self.SetEdgeColumn(80) self.SetEdgeColour("#dddddd") self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.Bind(wx.stc.EVT_STC_SAVEPOINTLEFT, self.OnSavePointLeft) self.Bind(wx.stc.EVT_STC_SAVEPOINTREACHED, self.OnSavePointReached) self.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnStcUpdateUI)
def __init__(self, parent, env, url="", show_browser_ui=True): wx.Panel.__init__(self, parent) self.env = env self.show_browser_ui = show_browser_ui self.sig_title_changed = Signal() self.sig_status_changed = Signal() import wx.html2 as webview self.wv = webview.WebView.New(self, url=url) sizer = wx.BoxSizer(wx.VERTICAL) if show_browser_ui: btnSizer = wx.BoxSizer(wx.HORIZONTAL) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/back.png")) self.Bind(wx.EVT_BUTTON, self.OnBack, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Back, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/forward.png")) self.Bind(wx.EVT_BUTTON, self.OnForward, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Forward, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/stop.png")) self.Bind(wx.EVT_BUTTON, self.OnStop, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Stop, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/refresh.png")) self.Bind(wx.EVT_BUTTON, self.OnRefresh, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Refresh, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.location = wx.ComboBox(self, value=url, style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_COMBOBOX, self.OnLocationSelect, self.location) self.location.Bind(wx.EVT_TEXT_ENTER, self.OnLocationEnter) btnSizer.Add(self.location, 1, wx.ALIGN_CENTRE_VERTICAL|wx.ALL, 2) sizer.Add(btnSizer, 0, wx.EXPAND) sizer.Add(self.wv, 1, wx.EXPAND) self.SetSizer(sizer) self.Bind(webview.EVT_WEBVIEW_NAVIGATING, self.OnWebViewNavigating, self.wv) self.Bind(webview.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.wv) self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv)
def __init__(self, parent, filter=None, show_root=False): style = wx.TR_DEFAULT_STYLE | wx.TR_EDIT_LABELS | wx.BORDER_NONE if not show_root: style |= wx.TR_HIDE_ROOT wx.TreeCtrl.__init__(self, parent, style=style) wx.FileDropTarget.__init__(self) self.SetDropTarget(self) self.SetDoubleBuffered(True) old_font = self.GetFont() self.font = wx.Font(old_font.PointSize, old_font.Family, old_font.Style, old_font.Weight, faceName=old_font.FaceName) self.SetFont(self.font) self.filter = filter or DirTreeFilter() self.cq = CoroutineQueue() self.cm = CoroutineManager() self.select_later_parent = None self.select_later_name = None self.select_later_time = 0 self.expanding_all = False self.drop_item = None self.sig_update_tree = Signal(self) self.sig_update_tree.bind(self.UpdateFromFSMonitor) self.monitor = FSMonitor() self.monitor_thread = DirTreeMonitor(self, self.monitor) self.imglist = wx.ImageList(16, 16) self.imglist.Add(load_bitmap("icons/folder.png")) self.imglist.Add(load_bitmap("icons/folder_denied.png")) self.imglist.Add(load_bitmap("icons/file.png")) self.SetImageList(self.imglist) self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding) self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed) self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnItemRightClicked) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnItemBeginLabelEdit) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnItemEndLabelEdit) self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag) self.Bind(wx.EVT_MENU, self.OnItemRename, id=ID_DIRTREE_RENAME) self.Bind(wx.EVT_MENU, self.OnItemDelete, id=ID_DIRTREE_DELETE) self.Bind(wx.EVT_MENU, self.OnNewFolder, id=ID_DIRTREE_NEW_FOLDER) self.Bind(wx.EVT_MENU, self.OnExpandAll, id=ID_DIRTREE_EXPAND_ALL) self.Bind(wx.EVT_MENU, self.OnCollapseAll, id=ID_DIRTREE_COLLAPSE_ALL)
def __init__(self, parent, env): wx.Panel.__init__(self, parent, -1) self.env = env self.current = "" sizer = wx.BoxSizer(wx.VERTICAL) btnSizer = wx.BoxSizer(wx.HORIZONTAL) self.wv = webview.WebView.New(self) self.Bind(webview.EVT_WEB_VIEW_NAVIGATING, self.OnWebViewNavigating, self.wv) self.Bind(webview.EVT_WEB_VIEW_LOADED, self.OnWebViewLoaded, self.wv) self.Bind(webview.EVT_WEB_VIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) btn = wx.Button(self, -1, "<--", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnPrevPageButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.Bind(wx.EVT_UPDATE_UI, self.OnCheckCanGoBack, btn) btn = wx.Button(self, -1, "-->", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnNextPageButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.Bind(wx.EVT_UPDATE_UI, self.OnCheckCanGoForward, btn) btn = wx.Button(self, -1, "Stop", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnStopButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.Button(self, -1, "Refresh", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnRefreshPageButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.location = wx.ComboBox( self, -1, "", style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_COMBOBOX, self.OnLocationSelect, self.location) self.location.Bind(wx.EVT_TEXT_ENTER, self.OnLocationEnter) btnSizer.Add(self.location, 1, wx.EXPAND|wx.ALL, 2) sizer.Add(btnSizer, 0, wx.EXPAND) sizer.Add(self.wv, 1, wx.EXPAND) self.SetSizer(sizer) self.sig_title_changed = Signal() self.sig_status_changed = Signal()
class Preview(wx.Panel): def __init__(self, parent, env, url="", show_browser_ui=True): wx.Panel.__init__(self, parent) self.env = env self.show_browser_ui = show_browser_ui self.sig_title_changed = Signal() self.sig_status_changed = Signal() import wx.html2 as webview self.wv = webview.WebView.New(self, url=url) sizer = wx.BoxSizer(wx.VERTICAL) if show_browser_ui: btnSizer = wx.BoxSizer(wx.HORIZONTAL) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/back.png")) self.Bind(wx.EVT_BUTTON, self.OnBack, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Back, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/forward.png")) self.Bind(wx.EVT_BUTTON, self.OnForward, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Forward, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/stop.png")) self.Bind(wx.EVT_BUTTON, self.OnStop, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Stop, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.BitmapButton(self, bitmap=resources.load_bitmap("icons/refresh.png")) self.Bind(wx.EVT_BUTTON, self.OnRefresh, btn) self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI_Refresh, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.location = wx.ComboBox(self, value=url, style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_COMBOBOX, self.OnLocationSelect, self.location) self.location.Bind(wx.EVT_TEXT_ENTER, self.OnLocationEnter) btnSizer.Add(self.location, 1, wx.ALIGN_CENTRE_VERTICAL|wx.ALL, 2) sizer.Add(btnSizer, 0, wx.EXPAND) sizer.Add(self.wv, 1, wx.EXPAND) self.SetSizer(sizer) self.Bind(webview.EVT_WEBVIEW_NAVIGATING, self.OnWebViewNavigating, self.wv) self.Bind(webview.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.wv) self.Bind(webview.EVT_WEBVIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) @property def url(self): return self.wv.GetCurrentURL() @url.setter def url(self, url): self.wv.LoadURL(url) @property def path(self): url = self.url if url.startswith("file://"): if wx.Platform == "__WXMSW__": return url[len("file:///"):].replace("/", "\\") else: return url[len("file://"):] else: return "" @path.setter def path(self, path): oldpath = self.path if oldpath: self.env.remove_monitor_path(oldpath) self.env.add_monitor_path(path) self.wv.LoadURL("file://" + path) @property def modified(self): return False @property def title(self): return (self.wv.GetCurrentTitle() or self.path or self.url or ("Loading..." if self.wv.IsBusy() else "...")) @property def status_text(self): return self.wv.GetCurrentTitle() @property def status_text_path(self): return self.path or self.url or "" @property def status_text_syntax(self): return "" @coroutine def TryClose(self): yield True def SavePerspective(self): p = dict(view_type="preview") path = self.path if path: p["path"] = path else: p["url"] = self.url return p @coroutine def LoadPerspective(self, p): if "path" in p: self.path = p["path"] elif "url" in p: self.url = p["url"] self.wv.ClearHistory() yield @coroutine def OnModifiedExternally(self): self.wv.Reload() yield @coroutine def OnUnloadedExternally(self): yield def OnWebViewNavigating(self, evt): pass # to stop it navigating: evt.Veto() def OnWebViewTitleChanged(self, evt): self.sig_title_changed.signal(self) self.sig_status_changed.signal(self) def OnWebViewLoaded(self, evt): if self.show_browser_ui: self.location.SetValue(self.path or self.url) self.sig_title_changed.signal(self) self.sig_status_changed.signal(self) def OnLocationSelect(self, evt): url = self.location.GetStringSelection() self.log.write("OnLocationSelect: %s\n" % url) self.wv.LoadURL(url) def OnLocationEnter(self, evt): url = self.location.GetValue() self.location.Append(url) self.wv.LoadURL(url) def OnOpenButton(self, evt): url = dialogs.get_text_input(self, "Open Location", "Enter a full URL or local path", self.url) if url: self.wv.LoadURL(url) def OnBack(self, evt): self.wv.GoBack() def OnForward(self, evt): self.wv.GoForward() def OnStop(self, evt): self.wv.Stop() def OnRefresh(self, evt): self.wv.Reload() def OnUpdateUI_Back(self, evt): evt.Enable(self.wv.CanGoBack()) def OnUpdateUI_Forward(self, evt): evt.Enable(self.wv.CanGoForward()) def OnUpdateUI_Stop(self, evt): evt.Enable(self.wv.IsBusy()) def OnUpdateUI_Refresh(self, evt): evt.Enable(not self.wv.IsBusy())
class Editor(StyledTextCtrl, wx.FileDropTarget): def __init__(self, parent, env, path=""): StyledTextCtrl.__init__(self, parent, env) wx.FileDropTarget.__init__(self) self.SetDropTarget(self) self.path = path self.file_encoding = "utf-8" self.modified_externally = False self.static_title = None self.sig_title_changed = Signal(self) self.sig_status_changed = Signal(self) self.SetTabIndents(True) self.SetBackSpaceUnIndents(True) self.SetViewWhiteSpace(wx.stc.STC_WS_VISIBLEALWAYS) self.SetWhitespaceForeground(True, "#dddddd") self.SetEdgeMode(wx.stc.STC_EDGE_LINE) self.SetEdgeColumn(80) self.SetEdgeColour("#dddddd") self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown) self.Bind(wx.stc.EVT_STC_SAVEPOINTLEFT, self.OnSavePointLeft) self.Bind(wx.stc.EVT_STC_SAVEPOINTREACHED, self.OnSavePointReached) self.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnStcUpdateUI) @property def name(self): return os.path.basename(self.path) @property def modified(self): return self.GetModify() @property def title(self): if self.static_title is not None: return self.static_title path = os.path.basename(self.path) or "Untitled" return "* " + path if self.modified else path @property def status_text(self): return "Line %d, Column %d" % ( self.GetCurrentLine() + 1, self.GetColumn(self.GetCurrentPos())) @property def status_text_path(self): return self.static_title or self.path or "Untitled" @property def status_text_syntax(self): return "Syntax: " + self.syntax.description @property def dialog_parent(self): return wx.GetTopLevelParent(self) def GetModify(self): return (not self.GetReadOnly()) and ( self.modified_externally or super(Editor, self).GetModify()) def SetModified(self): self.modified_externally = True self.sig_title_changed.signal(self) @coroutine def TryClose(self): if self.modified: result = dialogs.ask_save_changes(self.dialog_parent, self.path) if result == wx.ID_YES: try: save_result = (yield self.Save()) if save_result: self.env.remove_monitor_path(self.path) yield save_result except Exception: yield False else: yield result == wx.ID_NO else: if self.path: self.env.remove_monitor_path(self.path) yield True def SetStatic(self, title, text): self.static_title = title self.path = "" self.SetText(text) self.SetSavePoint() self.EmptyUndoBuffer() self.SetReadOnly(True) self.sig_title_changed.signal(self) self.sig_status_changed.signal(self) @coroutine def LoadFile(self, path): self.SetReadOnly(True) self.Disable() old_path = self.path self.path = path self.sig_title_changed.signal(self) try: text = (yield async_call(read_file, path, "r")) text, self.file_encoding = decode_text(text) text = clean_text(text) self.modified_externally = False self.SetReadOnly(False) self.SetSyntaxFromFilename(path) self.SetText(text) self.SetSavePoint() if old_path: self.env.remove_monitor_path(old_path) self.env.add_monitor_path(path) except: self.path = old_path self.sig_title_changed.signal(self) self.sig_status_changed.signal(self) raise finally: self.Enable() self.SetReadOnly(False) @coroutine def TryLoadFile(self, path): try: yield self.LoadFile(path) self.EmptyUndoBuffer() yield True except Exception as e: dialogs.error(self.dialog_parent, "Error opening file:\n\n%s" % e) yield False @coroutine def Reload(self): line_num = self.GetFirstVisibleLine() yield self.LoadFile(self.path) self.ScrollToLine(line_num) @coroutine def WriteFile(self, path): def do_write_file(path, text): mkpath(os.path.dirname(path)) atomic_write_file(path, text) text, self.file_encoding = encode_text(self.GetText(), self.file_encoding) text = clean_text(text) with self.env.updating_path(path): yield async_call(do_write_file, path, text) self.modified_externally = False self.SetSavePoint() def SetPath(self, path): if self.path: self.env.remove_monitor_path(path) self.path = path self.static_title = None self.SetSyntaxFromFilename(self.path) self.env.add_monitor_path(self.path) self.env.add_recent_file(self.path) self.sig_title_changed.signal(self) self.sig_status_changed.signal(self) def HasOpenFile(self): return bool(self.path) @coroutine def SaveAsInSameTab(self): path = self.env.get_file_to_save(path=os.path.dirname(self.path)) if path: path = os.path.realpath(path) try: yield self.WriteFile(path) except Exception as e: dialogs.error(self.dialog_parent, "Error saving file '%s'\n\n%s" % (path, e)) raise else: self.SetPath(path) yield True yield False @coroutine def SaveAsInNewTab(self): path = self.env.get_file_to_save(path=os.path.dirname(self.path)) if path: path = os.path.realpath(path) editor = self.env.new_editor(path) editor.SetText(self.GetText()) try: yield editor.WriteFile(path) except Exception as e: dialogs.error(self.dialog_parent, "Error saving file '%s'\n\n%s" % (path, e)) raise else: editor.SetPath(path) def SaveAs(self): if self.path: return self.SaveAsInNewTab() else: return self.SaveAsInSameTab() @coroutine def Save(self): if self.path: try: yield self.WriteFile(self.path) self.env.add_monitor_path(self.path) yield True except Exception as e: dialogs.error(self.dialog_parent, "Error saving file '%s'\n\n%s" % (self.path, e)) raise else: yield (yield self.SaveAsInSameTab()) def OnReturnKeyDown(self, evt): start, end = self.GetSelection() if start == end: indent = self.GetLineIndentation(self.GetCurrentLine()) pos = self.GetCurrentPos() if self.GetUseTabs(): indent //= self.GetTabWidth() self.InsertText(pos, "\n" + "\t" * indent) else: self.InsertText(pos, "\n" + " " * indent) self.GotoPos(pos + indent + 1) else: evt.Skip() def OnKeyDown(self, evt): key = evt.GetKeyCode() mod = evt.GetModifiers() if mod == wx.MOD_NONE: if key in (wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER): self.OnReturnKeyDown(evt) else: evt.Skip() else: evt.Skip() def OnRightDown(self, evt): start, end = self.GetSelection() if start == end: pos = self.PositionFromPoint(evt.GetPosition()) self.SetSelection(pos, pos) self.SetCurrentPos(pos) evt.Skip() def OnSavePointLeft(self, evt): self.sig_title_changed.signal(self) def OnSavePointReached(self, evt): self.sig_title_changed.signal(self) def OnStcUpdateUI(self, evt): self.sig_status_changed.signal(self) evt.Skip() def OnDropFiles(self, x, y, filenames): for filename in filenames: self.env.open_file(filename) return True @coroutine def OnModifiedExternally(self): if dialogs.ask_reload(self.dialog_parent, os.path.basename(self.path)): yield self.Reload() else: self.SetModified() @coroutine def OnUnloadedExternally(self): if os.path.exists(self.path): if dialogs.ask_reload(self.dialog_parent, os.path.basename(self.path)): yield self.Reload() else: self.SetModified() else: if dialogs.ask_unload(self.dialog_parent, os.path.basename(self.path)): self.env.close_view(self) else: self.SetModified() def GetDyanmicEditMenuItems(self): items = [] if self.path: items.extend([ MenuItem(ID.COPY_FILE_PATH, "Copy File Path"), MenuItem(ID.OPEN_CONTAINING_FOLDER, "Open Containing Folder"), MenuItem(ID.OPEN_IN_WEB_VIEW, "Preview in Web View"), MenuSeparator, ]) selected = self.GetSelectedFirstLine() if selected: selected = shorten_text(selected, 40) items.append(MenuItem(ID.WEB_SEARCH, "Web Search for %s" % repr(selected)[1:])) return items def CopyFilePath(self): if self.path: set_clipboard_text(self.path) def OpenContainingFolder(self): if self.path: self.env.shell_open(os.path.dirname(self.path)) def OpenPreview(self): if self.path: self.env.open_preview(self.path) def WebSearch(self): selected = self.GetSelectedFirstLine() if selected: url = "https://www.google.com/search?" + urlencode([("q", selected)]) self.env.open_web_view(url) def GetSelectionWriter(self): if not self.GetReadOnly(): return EditorSelectionWriter(self) def GetAllTextWriter(self): if not self.GetReadOnly(): return EditorAllTextWriter(self) def SavePerspective(self): p = { "line" : self.GetFirstVisibleLine(), "selection" : self.GetSelection(), "view_type" : "editor", } if self.path: p["path"] = self.path else: p["text"] = self.GetText() if self.static_title is not None: p["static_title"] = self.static_title return p @coroutine def LoadPerspective(self, p): if "text" in p: self.modified_externally = False static_title = p.get("static_title") if static_title is None: self.SetSavePoint() self.SetText(p["text"]) else: self.SetStatic(static_title, p["text"]) elif "path" in p: yield self.LoadFile(p["path"]) self.EmptyUndoBuffer() self.ScrollToLine(p.get("line", 0)) self.SetSelection(*p.get("selection", (0, 0)))
class Preview(wx.Panel): def __init__(self, parent, env): wx.Panel.__init__(self, parent, -1) self.env = env self.current = "" sizer = wx.BoxSizer(wx.VERTICAL) btnSizer = wx.BoxSizer(wx.HORIZONTAL) self.wv = webview.WebView.New(self) self.Bind(webview.EVT_WEB_VIEW_NAVIGATING, self.OnWebViewNavigating, self.wv) self.Bind(webview.EVT_WEB_VIEW_LOADED, self.OnWebViewLoaded, self.wv) self.Bind(webview.EVT_WEB_VIEW_TITLE_CHANGED, self.OnWebViewTitleChanged, self.wv) btn = wx.Button(self, -1, "<--", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnPrevPageButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.Bind(wx.EVT_UPDATE_UI, self.OnCheckCanGoBack, btn) btn = wx.Button(self, -1, "-->", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnNextPageButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.Bind(wx.EVT_UPDATE_UI, self.OnCheckCanGoForward, btn) btn = wx.Button(self, -1, "Stop", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnStopButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) btn = wx.Button(self, -1, "Refresh", style=wx.BU_EXACTFIT) self.Bind(wx.EVT_BUTTON, self.OnRefreshPageButton, btn) btnSizer.Add(btn, 0, wx.EXPAND|wx.ALL, 2) self.location = wx.ComboBox( self, -1, "", style=wx.CB_DROPDOWN|wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_COMBOBOX, self.OnLocationSelect, self.location) self.location.Bind(wx.EVT_TEXT_ENTER, self.OnLocationEnter) btnSizer.Add(self.location, 1, wx.EXPAND|wx.ALL, 2) sizer.Add(btnSizer, 0, wx.EXPAND) sizer.Add(self.wv, 1, wx.EXPAND) self.SetSizer(sizer) self.sig_title_changed = Signal() self.sig_status_changed = Signal() @coroutine def TryClose(self): yield True def SavePerspective(self): return dict( view_type="preview", path = self.path, ) @coroutine def LoadPerspective(self, p): path = p.get("path", "") os.stat(path) # check it exists self.path = path yield @property def path(self): return self.current and self.current[len('file://'):] @property def modified(self): return False @path.setter def path(self, p): oldpath = self.path if oldpath: self.env.remove_monitor_path(oldpath) self.env.add_monitor_path(p) self.wv.LoadURL('file://' + p) @coroutine def OnModifiedExternally(self): self.wv.Reload() yield @coroutine def OnUnloadedExternally(self): yield @property def title(self): return self.wv.GetCurrentTitle() @property def status_text(self): return self.title @property def status_text_path(self): return self.title def OnWebViewNavigating(self, evt): pass # to stop it navigating: evt.Veto() def OnWebViewTitleChanged(self, evt): self.sig_title_changed.signal(self) def OnWebViewLoaded(self, evt): self.current = evt.GetURL() self.location.SetValue(self.current) self.sig_title_changed.signal(self) def OnLocationSelect(self, evt): url = self.location.GetStringSelection() self.log.write('OnLocationSelect: %s\n' % url) self.wv.LoadURL(url) def OnLocationEnter(self, evt): url = self.location.GetValue() self.location.Append(url) self.wv.LoadURL(url) def OnOpenButton(self, event): dlg = wx.TextEntryDialog(self, "Open Location", "Enter a full URL or local path", self.current, wx.OK|wx.CANCEL) dlg.CentreOnParent() if dlg.ShowModal() == wx.ID_OK: self.current = dlg.GetValue() self.wv.LoadURL(self.current) dlg.Destroy() def OnPrevPageButton(self, event): self.wv.GoBack() def OnNextPageButton(self, event): self.wv.GoForward() def OnCheckCanGoBack(self, event): event.Enable(self.wv.CanGoBack()) def OnCheckCanGoForward(self, event): event.Enable(self.wv.CanGoForward()) def OnStopButton(self, evt): self.wv.Stop() def OnRefreshPageButton(self, evt): self.wv.Reload()
class DirTreeCtrl(wx.TreeCtrl, wx.FileDropTarget): def __init__(self, parent, filter=None, show_root=False): style = wx.TR_DEFAULT_STYLE | wx.TR_EDIT_LABELS | wx.BORDER_NONE if not show_root: style |= wx.TR_HIDE_ROOT wx.TreeCtrl.__init__(self, parent, style=style) wx.FileDropTarget.__init__(self) self.SetDropTarget(self) self.SetDoubleBuffered(True) old_font = self.GetFont() self.font = wx.Font(old_font.PointSize, old_font.Family, old_font.Style, old_font.Weight, faceName=old_font.FaceName) self.SetFont(self.font) self.filter = filter or DirTreeFilter() self.cq = CoroutineQueue() self.cm = CoroutineManager() self.select_later_parent = None self.select_later_name = None self.select_later_time = 0 self.expanding_all = False self.drop_item = None self.sig_update_tree = Signal(self) self.sig_update_tree.bind(self.UpdateFromFSMonitor) self.monitor = FSMonitor() self.monitor_thread = DirTreeMonitor(self, self.monitor) self.imglist = wx.ImageList(16, 16) self.imglist.Add(load_bitmap("icons/folder.png")) self.imglist.Add(load_bitmap("icons/folder_denied.png")) self.imglist.Add(load_bitmap("icons/file.png")) self.SetImageList(self.imglist) self.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.OnItemExpanding) self.Bind(wx.EVT_TREE_ITEM_COLLAPSED, self.OnItemCollapsed) self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnItemRightClicked) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnItemBeginLabelEdit) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnItemEndLabelEdit) self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnBeginDrag) self.Bind(wx.EVT_MENU, self.OnItemRename, id=ID_DIRTREE_RENAME) self.Bind(wx.EVT_MENU, self.OnItemDelete, id=ID_DIRTREE_DELETE) self.Bind(wx.EVT_MENU, self.OnNewFolder, id=ID_DIRTREE_NEW_FOLDER) self.Bind(wx.EVT_MENU, self.OnExpandAll, id=ID_DIRTREE_EXPAND_ALL) self.Bind(wx.EVT_MENU, self.OnCollapseAll, id=ID_DIRTREE_COLLAPSE_ALL) def Destroy(self): self.cm.cancel() self.monitor.close() wx.TreeCtrl.Destroy(self) def SelectLater(self, parent, name, timeout=1): self.select_later_parent = parent self.select_later_name = name self.select_later_time = time.time() + timeout def TrySelectLater(self, item, name): if name == self.select_later_name \ and self.GetItemParent(item) == self.select_later_parent: if time.time() < self.select_later_time: self.SelectItem(item) self.select_later_name = None self.select_later_parent = None self.select_later_time = 0 @managed("cm") @coroutine def UpdateFromFSMonitor(self): for evt in self.monitor_thread.get_events(): if evt.action in add_events: item = (yield evt.user.add(evt.name, self, self.monitor, self.filter)) if item: self.TrySelectLater(item, evt.name) elif evt.action in remove_events: evt.user.remove(evt.name, self, self.monitor) def OnItemExpanding(self, evt): node = self.GetEventNode(evt) if node.type == 'd' and not node.populated: self.ExpandNode(node) def OnItemCollapsed(self, evt): node = self.GetEventNode(evt) if node.type == 'd': self.CollapseNode(node) def GetNodeMenu(self, node): return context_menu def OnItemRightClicked(self, evt): self.SelectItem(evt.GetItem()) node = self.GetEventNode(evt) menu = self.GetNodeMenu(node) if menu: self.PopupMenu(menu.Create()) def OnItemBeginLabelEdit(self, evt): node = self.GetEventNode(evt) if not (node and node.path): evt.Veto() def OnItemEndLabelEdit(self, evt): if not evt.IsEditCancelled(): evt.Veto() node = self.GetEventNode(evt) self.RenameNode(node, evt.GetLabel()) def OnBeginDrag(self, evt): node = self.GetEventNode(evt) if node: data = wx.FileDataObject() data.AddFile(node.path) dropsrc = wx.DropSource(self) dropsrc.SetData(data) dropsrc.DoDragDrop() def OnDragOver(self, x, y, default): self.OnLeave() item, flags = self.HitTest((x, y)) if item and (flags & wx.TREE_HITTEST_ONITEMLABEL): self.drop_item = item self.SetItemDropHighlight(item, True) return wx.DragCopy return wx.DragNone def OnLeave(self): if self.drop_item: self.SetItemDropHighlight(self.drop_item, False) self.drop_item = None def OnDropFiles(self, x, y, filenames): self.OnLeave() item, flags = self.HitTest((x, y)) if item and (flags & wx.TREE_HITTEST_ONITEMLABEL): while item: node = self.GetPyData(item) if node.type == 'd': for filename in filenames: filename = os.path.realpath(filename) if filename != node.path: fileutil.shell_move_or_copy(filename, node.path, parent=self) break item = self.GetItemParent(item) def OnItemRename(self, evt): node = self.GetSelectedNode() if node and node.path: self.EditLabel(node.item) def OnItemDelete(self, evt): node = self.GetSelectedNode() if node: next_item = self.GetNextSibling(node.item) fileutil.shell_remove(node.path, parent=self) def OnNewFolder(self, evt): node = self.GetSelectedNode() if node: if node.type != 'd': node = self.GetPyData(self.GetItemParent(node.item)) name = dialogs.get_text_input(self, "New Folder", "Please enter new folder name:") if name: self.NewFolder(node, name) @managed("cm") @coroutine def OnExpandAll(self, evt): del evt if self.expanding_all: return if not dialogs.yes_no(self, "Expanding all folders may take a long time. Continue?", icon_style=wx.ICON_WARNING): return self.expanding_all = True try: for item in iter_tree_breadth_first(self, self.GetRootItem()): if not self.expanding_all: return node = self.GetPyData(item) if node.type == 'd': if not node.populated: try: yield self.ExpandNode(node) except Exception: pass else: self.Expand(item) finally: self.expanding_all = False def OnCollapseAll(self, evt): self.expanding_all = False self.CollapseAll() def SetItemNode(self, item, node): self.SetPyData(item, node) node.item = item return node def GetEventNode(self, evt): item = evt.GetItem() if item.IsOk(): return self.GetPyData(item) def GetSelectedNode(self): item = self.GetSelection() if item.IsOk(): return self.GetPyData(item) def GetSelectedPath(self): node = self.GetSelectedNode() return node.path if node else "" def IsExpanded(self, item): return self.GetRootItem() == item or wx.TreeCtrl.IsExpanded(self, item) def Expand(self, item): if self.GetRootItem() != item: wx.TreeCtrl.Expand(self, item) @managed("cm") @coroutine def PopulateNode(self, node): if not node.populated: f = node.expand(self, self.monitor, self.filter) if isinstance(f, Future): yield f @managed("cm") @coroutine def ExpandNode(self, node): if not node.populated: try: yield self.PopulateNode(node) except OSError: return if node.populated: self.Expand(node.item) def CollapseNode(self, node): node.collapse(self, self.monitor) @managed("cm") @coroutine def RenameNode(self, node, name): newpath = os.path.join(os.path.dirname(node.path), name) if newpath != node.path: try: if (yield async_call(os.path.exists, newpath)): if not dialogs.ask_overwrite(self, newpath): return yield async_call(fileutil.rename, node.path, newpath) self.SelectLater(self.GetItemParent(node.item), name) except OSError as e: dialogs.error(self, str(e)) @managed("cm") @coroutine def NewFolder(self, node, name): path = os.path.join(node.path, name) try: yield async_call(os.mkdir, path) if self.IsExpanded(node.item): self.SelectLater(node.item, name) except OSError as e: dialogs.error(self, str(e)) @managed("cm") @queued_coroutine("cq") def _InitialExpand(self, rootnode, toplevel): yield self.ExpandNode(rootnode) if isinstance(toplevel[0], SimpleNode): yield self.ExpandNode(toplevel[0]) def SetTopLevel(self, toplevel=None): self.cm.cancel() self.cq.cancel() self.monitor_thread.clear() self.DeleteAllItems() toplevel = toplevel or make_top_level() if len(toplevel) == 1: rootitem = self.AddRoot(toplevel[0].label) rootnode = toplevel[0] else: rootitem = self.AddRoot("") rootnode = SimpleNode("", toplevel) self.SetItemNode(rootitem, rootnode) return self._InitialExpand(rootnode, toplevel) def _FindExpandedPaths(self, item, path, expanded): if self.IsExpanded(item): node = self.GetPyData(item) if node.type == 'd': subpath = os.path.join(path, os.path.basename(node.path)) if path else node.path len_expanded = len(expanded) for child_item in iter_tree_children(self, item): self._FindExpandedPaths(child_item, subpath, expanded) if len(expanded) == len_expanded: expanded.append(subpath) return expanded def FindExpandedPaths(self): expanded = [] for item in iter_tree_children(self, self.GetRootItem()): self._FindExpandedPaths(item, "", expanded) return expanded @managed("cm") @coroutine def _ExpandPaths(self, item, paths): expanded = [path[0] for path in paths if path] sub_paths = [path[1:] for path in paths if len(path) > 1] yield self.ExpandNode(self.GetPyData(item)) for child_item in iter_tree_children(self, item): node = self.GetPyData(child_item) name = os.path.basename(node.path) or node.path.strip(os.path.sep) if name in expanded: yield self._ExpandPaths(child_item, sub_paths) def ExpandPaths(self, paths): rootpath = self.GetPyData(self.GetRootItem()).path paths = [split_path(path, rootpath) for path in paths] return self._ExpandPaths(self.GetRootItem(), paths) def ExpandPath(self, path): return self.ExpandPaths([path]) def _SelectExpandedPath(self, item, path): node = self.GetPyData(item) if node.path == path: self.SelectItem(node.item) elif node.type == 'd': for child_item in iter_tree_children(self, item): self._SelectExpandedPath(child_item, path) @managed("cm") @coroutine def _SelectPath(self, path): yield self.ExpandPath(os.path.dirname(path)) self._SelectExpandedPath(self.GetRootItem(), path) @managed("cm") @coroutine def _SelectExpandPath(self, path): yield self.ExpandPath(path) self._SelectExpandedPath(self.GetRootItem(), path) @managed("cm") @queued_coroutine("cq") def SelectPath(self, path): yield self._SelectPath(os.path.normpath(path)) @managed("cm") @queued_coroutine("cq") def SelectExpandPath(self, path): yield self._SelectExpandPath(os.path.normpath(path)) def SavePerspective(self): p = {} expanded = self.FindExpandedPaths() if expanded: p["expanded"] = expanded selected = self.GetSelectedPath() if selected: p["selected"] = selected return p @managed("cm") @queued_coroutine("cq") def LoadPerspective(self, p): expanded = p.get("expanded", ()) if expanded: yield self.ExpandPaths(expanded) if "selected" in p: yield self._SelectPath(p["selected"])