From db65829fe111d3d352d9f8dd9fa902eb49cbd1e9 Mon Sep 17 00:00:00 2001 From: Kochi Date: Mon, 24 Aug 2020 01:29:44 +0900 Subject: [PATCH] =?UTF-8?q?#369=20and=20#143=20=E3=83=91=E3=82=B9=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E3=82=92os.path=E3=83=A2=E3=82=B8=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=81=AB=E7=B5=B1=E4=B8=80=E3=81=97=E3=81=AA=E3=81=8C?= =?UTF-8?q?=E3=82=89=E6=B0=97=E3=81=A5=E3=81=84=E3=81=9F=E4=B8=8D=E5=85=B7?= =?UTF-8?q?=E5=90=88=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DefaultSettings.py | 2 +- FalconConfigParser.py | 2 ++ app.py | 2 +- browsableObjects.py | 10 +++++----- constants.py | 1 + fileSystemManager.py | 3 +-- lists/file.py | 23 +++++++++++++++-------- lists/grepResult.py | 2 +- lists/searchResult.py | 2 +- misc.py | 9 ++++----- tabs/base.py | 4 ++-- tabs/fileList.py | 39 ++++++++++++++++++++++++++------------- tabs/streamList.py | 41 +++++++++++++++++++++++++---------------- views/base.py | 28 ++++++++++++++++++++++++++++ views/main.py | 2 +- 15 files changed, 114 insertions(+), 56 deletions(-) diff --git a/DefaultSettings.py b/DefaultSettings.py index ca4ffc4..eda9724 100644 --- a/DefaultSettings.py +++ b/DefaultSettings.py @@ -78,7 +78,7 @@ def get(): "sizeY": "600", } config["browse"]={ - "startPath": "%HOMEDRIVE%" + "startPath": "%HOMEDRIVE%\\" } config["search"]={ "history_count": 20, diff --git a/FalconConfigParser.py b/FalconConfigParser.py index 6a267b3..adeea3d 100644 --- a/FalconConfigParser.py +++ b/FalconConfigParser.py @@ -63,6 +63,8 @@ def getboolean(self,section,key,default=True): def getint(self,section,key,default=0,min=None,max=None): if type(default)!=int: raise ValueError("default value must be int") + if (min!=None and type(min)!=int) or (max!=None and type(max)!=int): + raise ValueError("min/max value must be int") try: ret = super().getint(section,key) if (min!=None and retmax): diff --git a/app.py b/app.py index 46e209f..0ba6951 100644 --- a/app.py +++ b/app.py @@ -95,7 +95,7 @@ def initialize(self): def InitLogger(self): """ロギング機能を初期化して準備する。""" - self.hLogHandler=FileHandler("falcon.log", mode="w", encoding="UTF-8") + self.hLogHandler=FileHandler(constants.LOG_FILE_NAME, mode="w", encoding="UTF-8") self.hLogHandler.setLevel(logging.DEBUG) self.hLogFormatter=Formatter("%(name)s - %(levelname)s - %(message)s (%(asctime)s)") self.hLogHandler.setFormatter(self.hLogFormatter) diff --git a/browsableObjects.py b/browsableObjects.py index e2886cd..535bcf2 100644 --- a/browsableObjects.py +++ b/browsableObjects.py @@ -67,7 +67,7 @@ def Initialize(self,directory="", basename="", fullpath="", size=-1, modDate=Non """必要な情報をセットする。継承しているクラスのうち、grepItemだけはここを通らないので注意。""" self.directory=directory self.basename=basename - self.fullpath=directory+"\\"+basename + self.fullpath=os.path.join(directory,basename) self.size=size self.modDate=modDate.astimezone(globalVars.app.timezone) self.creationDate=creationDate.astimezone(globalVars.app.timezone) @@ -100,7 +100,7 @@ def GetNewAttributes(self,checks): #end GetNewAttributes def GetRootDrivePath(self): - return self.fullpath[0] + return misc.GetRootObject(self.fullpath).fullpath class Folder(File): __slots__=[] @@ -176,7 +176,7 @@ def Initialize(self, letter, free, total, type, name="",hIcon=-1): self.type=type self.UpdateTypeString() self.basename=name - self.fullpath=letter+":" + self.fullpath=letter+":\\" self.hIcon=hIcon def UpdateTypeString(self): @@ -209,7 +209,7 @@ def GetListTuple(self): return (self.basename, self.letter, misc.ConvertBytesTo(self.free, misc.UNIT_AUTO, True), misc.ConvertBytesTo(self.total, misc.UNIT_AUTO, True), self.typeString) def GetRootDrivePath(self): - return self.fullpath[0] + return self.fullpath class Stream(FalconBrowsableBase): """NTFS 副ストリームを表す。このオブジェクトは情報を保持するだけで、指し示すファイルにアクセスすることはない。フルパスは計算可能なのだが、二重に値を生成したくはないので、あえて値を渡すようにしている。""" @@ -226,7 +226,7 @@ def GetListTuple(self): return (self.basename, misc.ConvertBytesTo(self.size,misc.UNIT_AUTO,True)) def GetRootDrivePath(self): - return self.fullpath[0] + return misc.GetRootObject(self.fullpath).fullpath class NetworkResource(FalconBrowsableBase): """ネットワーク上のディスクリソースを表す。このオブジェクトは情報を保持するだけで、指し示すリソースにアクセスすることはない。フルパスは計算可能なのだが、二重に値を生成したくはないので、あえて値を渡すようにしている。""" diff --git a/constants.py b/constants.py index 0ac7479..da81ad2 100644 --- a/constants.py +++ b/constants.py @@ -15,6 +15,7 @@ SETTING_FILE_NAME="settings.ini" +LOG_FILE_NAME="falcon.log" KEYMAP_FILE_NAME="keymap.ini" HISTORY_FILE_NAME="history.dat" diff --git a/fileSystemManager.py b/fileSystemManager.py index 3594493..7dc09c4 100644 --- a/fileSystemManager.py +++ b/fileSystemManager.py @@ -63,7 +63,7 @@ def ValidationObjectName(path,pathType,path2=""): return _("この文字列は、Windowsによって予約された特別な名前のため、ファイルやディレクトリの名前として使用できません。") #末尾が.と半角スペースでないことの確認 - if re.sub("(.*\\.$)|(.* $)",r"",s.upper())=="": + if re.sub("(.*\\.$)|(.* $)",r"",s)=="": return _("名前の最後を半角の.(ピリオド)または半角スペースとすることはできません。") #パス長の確認 @@ -80,7 +80,6 @@ def ValidationObjectName(path,pathType,path2=""): elif pathType==pathTypes.FILE: if len(path)>drive.MAX_FULLPATH_LENGTH: return _("このドライブでは、ファイル名のフルパスが以下の文字数を超えないように名前を付ける必要があります。\n\n制限文字数:"+str(drive.MAX_FULLPATH_LENGTH)) - #問題なし return "" diff --git a/lists/file.py b/lists/file.py index 79b1563..87ce02b 100644 --- a/lists/file.py +++ b/lists/file.py @@ -48,20 +48,27 @@ def Initialize(self,dir,silent=False): #end copy self.files.clear() self.folders.clear() - dir=dir.rstrip("\\") - dir_spl=dir.split("\\") + dir_spl=dir[len(os.path.splitdrive(dir)[0])+1:].split("\\") level=len(dir_spl) + if dir_spl[0]=="":level-=1 if not silent: r=[] - if globalVars.app.config['on_list_moved']['read_directory_level']=='True': r.append("%s%d " % (dir[0],level)) - if globalVars.app.config['on_list_moved']['read_directory_name']=='True': r.append(dir_spl[level-1]) + if globalVars.app.config['on_list_moved']['read_directory_level']=='True': + drive=os.path.splitdrive(dir)[0] + if len(drive)==2: + drive=drive[0] + if level==0: + r.append("%sルート" % drive) + else: + r.append("%sの%d " % (drive,level)) + if globalVars.app.config['on_list_moved']['read_directory_name']=='True': r.append(dir_spl[level-1]) if len(r)>0: globalVars.app.say("".join(r)) #end read self.rootDirectory=dir self.log.debug("Getting file list for %s..." % self.rootDirectory) t=misc.Timer() try: - lst=win32api.FindFiles(dir+"\\*") + lst=win32api.FindFiles(os.path.join(dir,"*")) except pywintypes.error as err: self.log.error("Cannot open the directory! {0}".format(err)) if err.winerror==5: @@ -69,14 +76,14 @@ def Initialize(self,dir,silent=False): dialog(_("エラー"), _("フォルダを開くことができませんでした(%(error)s)") % {"error": str(err)}) return errorCodes.FATAL #end except - if "\\" in self.rootDirectory: #ルート以外では余計な.と..がが一番上に入っている - del lst[0:2] if len(lst)==0: self.log.debug("Blank folder.") return errorCodes.OK #end 空のフォルダだったらさっさと帰る for elem in lst: - fullpath=dir+"\\"+elem[8] + if elem[8]=="." or elem[8]=="..": + continue + fullpath=os.path.join(dir,elem[8]) ret, shfileinfo=shell.SHGetFileInfo(fullpath,0,shellcon.SHGFI_ICON | shellcon.SHGFI_TYPENAME) if os.path.isfile(fullpath): f=browsableObjects.File() diff --git a/lists/grepResult.py b/lists/grepResult.py index 34a3921..45bfda5 100644 --- a/lists/grepResult.py +++ b/lists/grepResult.py @@ -49,7 +49,7 @@ def Initialize(self,rootDirectory,searches=[],keyword="",isRegularExpression=Fal def HitTest(self,path,ret_list): """_performSearchStepから呼ばれ、与えられたpathのファイルが検索にヒットするならリスト追加する""" if misc.isDocumentExt(path.split(".")[-1]): - fullpath=self.rootDirectory+"\\"+path + fullpath=os.path.join(self.rootDirectory,path) content=misc.ExtractText(fullpath).split("\n") fileobj=None#複数ヒットでファイルオブジェクトを生成し続けないようにキャッシュする ln=1 diff --git a/lists/searchResult.py b/lists/searchResult.py index cb79c01..981284b 100644 --- a/lists/searchResult.py +++ b/lists/searchResult.py @@ -50,7 +50,7 @@ def Initialize(self,rootDirectory,searches=[],keyword="",isRegularExpression=Fal def HitTest(self,path,ret_list): """_performSearchStepから呼ばれ、与えられたpathのファイルが検索にヒットするならリスト追加する""" if re.search(self.keyword,path): - fullpath=self.rootDirectory+"\\"+path + fullpath=os.path.join(self.rootDirectory,path) if os.path.isfile(fullpath): f=self._MakeObject(browsableObjects.SearchedFile,fullpath) self.files.append(f) diff --git a/misc.py b/misc.py index a3ca235..0691735 100644 --- a/misc.py +++ b/misc.py @@ -26,7 +26,7 @@ log=getLogger("falcon.misc") -falconHelper=ctypes.cdll.LoadLibrary("falconHelper.dll") +falconHelper=ctypes.cdll.LoadLibrary("./falconHelper.dll") class Timer: """シンプルなタイマー。経過時間や処理時間を計測するのに使う。単位は秒で、float。""" @@ -129,7 +129,6 @@ def AddCustomContextMenuItem(name,identifier): falconHelper.addCustomContextMenuItem(makeStringForFalconHelper(name),identifier) def AddContextMenuItemsFromWindows(path): - print("len %d" % len(path)) path_bytes=makeStringForFalconHelper(json.dumps(path)) ret=falconHelper.addContextMenuItemsFromWindows(path_bytes) return ret==1 @@ -161,12 +160,12 @@ def disableWindowStyleFlag(hwnd,flag): def IteratePaths(path,append_eol=False): """FindFirstFile 系を使って、パス名をイテレートする。append_eol=True にすると、最後に "eol" という1行をリストに入れるので、検索の終了判定などに使える。""" try: - for elem in win32file.FindFilesIterator(path+"\\*"): + for elem in win32file.FindFilesIterator(os.path.join(path,"*")): if elem[8]=="." or elem[8]=="..": continue if elem[0]&win32file.FILE_ATTRIBUTE_DIRECTORY: - yield from IteratePaths(path+"\\"+elem[8]) + yield from IteratePaths(os.path.join(path,elem[8])) #end ディレクトリ - yield path+"\\"+elem[8] + yield os.path.join(path,elem[8]) #end iterate except pywintypes.error as e: log.error("Access denied while searching paths at %s (%s)." % (path,e)) diff --git a/tabs/base.py b/tabs/base.py index 77bf16b..e69818e 100644 --- a/tabs/base.py +++ b/tabs/base.py @@ -396,7 +396,7 @@ def MakeShortcut(self,option): target=self.GetFocusedElement().fullpath dest=option["destination"] if not os.path.isabs(dest): #早退の場合は絶対に直す - dest=os.path.normpath(os.path.dirname(target)+"\\"+dest) + dest=os.path.normpath(os.path.join(os.path.dirname(target),dest)) #シンボリックリンクはNTFSにしか作成できない if option["type"]=="symbolicLink": @@ -700,7 +700,7 @@ def GoForward(self,stream,admin=False): def GoBackward(self): """内包しているフォルダ/ドライブ一覧へ移動する。""" self.StopSound() - if len(self.listObject.rootDirectory)<3: #ドライブリストへ + if len(self.listObject.rootDirectory)<=3: #ドライブリストへ target="" cursorTarget=self.listObject.rootDirectory[0] else: diff --git a/tabs/fileList.py b/tabs/fileList.py index 5a3ba2b..86be26a 100644 --- a/tabs/fileList.py +++ b/tabs/fileList.py @@ -51,10 +51,10 @@ def OnLabelEditEnd(self,evt): e=self.hListCtrl.GetEditControl() f=self.listObject.GetElement(self.GetFocusedItem()) if isinstance(f,browsableObjects.Folder): - newName=f.directory+"\\"+e.GetLineText(0) + newName=os.path.join(f.directory,e.GetLineText(0)) error=fileSystemManager.ValidationObjectName(newName,fileSystemManager.pathTypes.DIRECTORY,e.GetLineText(0)) elif isinstance(f,browsableObjects.File): - newName=f.directory+"\\"+e.GetLineText(0) + newName=os.path.join(f.directory,e.GetLineText(0)) error=fileSystemManager.ValidationObjectName(newName,fileSystemManager.pathTypes.FILE,e.GetLineText(0)) #end フォルダかファイルか if error: @@ -71,7 +71,7 @@ def OnLabelEditEnd(self,evt): #end fail f.basename=e.GetLineText(0) if isinstance(f,browsableObjects.File): - f.fullpath=f.directory+"\\"+f.basename + f.fullpath=os.path.join(f.directory,f.basename) if isinstance(f,browsableObjects.Stream): f.fullpath=f.file+f.basename #end onLabelEditEnd @@ -101,12 +101,12 @@ def ChangeAttribute(self,attrib_checks): def MakeDirectory(self,newdir): dir=self.listObject.rootDirectory - error=fileSystemManager.ValidationObjectName(dir+"\\"+newdir,fileSystemManager.pathTypes.DIRECTORY,newdir) + error=fileSystemManager.ValidationObjectName(os.path.join(dir,newdir),fileSystemManager.pathTypes.DIRECTORY,newdir) print("@@@@"+error) if error!="": dialog(_("エラー"),error) return False - dest=dir+"\\"+newdir + dest=os.path.join(dir,newdir) inst={"operation": "mkdir", "target": [dest]} op=fileOperator.FileOperator(inst) ret=op.Execute() @@ -223,8 +223,12 @@ def _dirCalc_receive(self,results,taskState): self.background_tasks.remove(taskState) def ReadCurrentFolder(self): - f=self.listObject.rootDirectory.split(":\\") - s=_("現在は、ドライブ%(drive)sの %(folder)s") % {'drive': self.listObject.rootDirectory[0], 'folder': f[1] if len(f)==2 else "ルート"} + curdir=os.path.basename(self.listObject.rootDirectory) + drive=os.path.splitdrive(self.listObject.rootDirectory)[0] + if self.listObject.rootDirectory[0]!="\\": + s=_("現在は、ドライブ%(drive)sの %(folder)s") % {'drive': drive[0], 'folder': curdir if curdir!="" else "ルート"} + else: + s=_("現在は、%(drive)sの %(folder)s") % {'drive': drive, 'folder': curdir if curdir!="" else "ルート"} globalVars.app.say(s, interrupt=True) def ReadListItemNumber(self,short=False): @@ -233,13 +237,22 @@ def ReadListItemNumber(self,short=False): globalVars.app.say(_("フォルダ数 %(folders)d ファイル数 %(files)d") % {'folders': folders, 'files': files}) return #end short - tmp=self.listObject.rootDirectory.split("\\") - curdir=_("%(letter)sルート") % {'letter': tmp[0][0]} if len(tmp)==1 else tmp[-1] + curdir=os.path.basename(self.listObject.rootDirectory) + if curdir=="": + if len(self.listObject.rootDirectory)<=3: + curdir=_("%(letter)sルート") % {'letter': self.listObject.rootDirectory[0]} + else: #ネットワークルート + curdir=self.listObject.rootDirectory globalVars.app.say(_("%(containing)sの中には、フォルダ %(folders)d個、 ファイル %(files)d個") % {'containing': curdir, 'folders': folders, 'files': files}, interrupt=True) def ReadListInfo(self): tmp=self.listObject.rootDirectory.split("\\") - curdir=_("%(letter)sルート") % {'letter': tmp[0][0]} if len(tmp)==1 else tmp[-1] + curdir=os.path.basename(self.listObject.rootDirectory) + if curdir=="": + if len(self.listObject.rootDirectory)<=3: + curdir=_("%(letter)sルート") % {'letter': self.listObject.rootDirectory[0]} + else: #ネットワークルート + curdir=self.listObject.rootDirectory prefix=_("フォルダ ") if len(tmp)>1 else "" globalVars.app.say(_("%(prefix)s%(dir)sを %(sortkind)sの%(sortad)sで一覧中、 %(max)d個中 %(current)d個目") %{'prefix': prefix, 'dir': curdir, 'sortkind': self.listObject.GetSortKindString(), 'sortad': self.listObject.GetSortAdString(), 'max': len(self.listObject), 'current': self.GetFocusedItem()+1}, interrupt=True) @@ -339,11 +352,11 @@ def PastOperation(self,target,dest,op=clipboard.COPY): self.UpdateFilelist(silence=True) #end past - def GetTabName(self): """タブコントロールに表示する名前""" - word=self.listObject.rootDirectory.split("\\") - word=word[len(word)-1] + word=os.path.basename(self.listObject.rootDirectory) + if word=="": #ルート + word=self.listObject.rootDirectory word=StringUtil.GetLimitedString(word,globalVars.app.config["view"]["header_title_length"]) return word diff --git a/tabs/streamList.py b/tabs/streamList.py index 3ea3014..f1b4ae3 100644 --- a/tabs/streamList.py +++ b/tabs/streamList.py @@ -186,8 +186,12 @@ def MarkSet(self): return errorCodes.NOT_SUPPORTED#基底クラスではなにも許可しない def ReadCurrentFolder(self): - f=self.listObject.rootDirectory.split(":\\") - s=_("現在は、ドライブ%(drive)sの %(folder)s") % {'drive': self.listObject.rootDirectory[0], 'folder': f[1] if len(f)==2 else "ルート"} + curdir=os.path.basename(self.listObject.rootDirectory) + drive=os.path.splitdrive(self.listObject.rootDirectory)[0] + if self.listObject.rootDirectory[0]!="\\": + s=_("現在は、ドライブ%(drive)sの %(folder)s") % {'drive': drive[0], 'folder': curdir if curdir!="" else "ルート"} + else: + s=_("現在は、%(drive)sの %(folder)s") % {'drive': drive, 'folder': curdir if curdir!="" else "ルート"} globalVars.app.say(s, interrupt=True) def ReadListItemNumber(self,short=False): @@ -196,27 +200,32 @@ def ReadListItemNumber(self,short=False): globalVars.app.say(_("ストリーム数 %(streams)d") % {'streams': streams}) return #end short - curdir=self.listObject.rootDirectory.split("\\")[-1] - globalVars.app.say(_("%(containing)sの中には、ストリーム %(streams)d個") % {'containing': curdir, 'streams': streams}, interrupt=True) + curdir=os.path.basename(self.listObject.rootDirectory) + if curdir=="": + if len(self.listObject.rootDirectory)<=3: + curdir=_("%(letter)sルート") % {'letter': self.listObject.rootDirectory[0]} + else: #ネットワークルート + curdir=self.listObject.rootDirectory + globalVars.app.say(_("%(containing)sには、ストリーム %(streams)d個") % {'containing': curdir, 'streams': streams}, interrupt=True) def ReadListInfo(self): - tmp=self.listObject.rootDirectory.split("\\")[-1] - globalVars.app.say(_("%(dir)sに含まれるストリームを %(sortkind)sの%(sortad)sで一覧中、 %(max)d個中 %(current)d個目") %{'dir': tmp, 'sortkind': self.listObject.GetSortKindString(), 'sortad': self.listObject.GetSortAdString(), 'max': len(self.listObject), 'current': self.GetFocusedItem()+1}, interrupt=True) + tmp=self.listObject.rootDirectory.split("\\") + curdir=os.path.basename(self.listObject.rootDirectory) + if curdir=="": + if len(self.listObject.rootDirectory)<=3: + curdir=_("%(letter)sルート") % {'letter': self.listObject.rootDirectory[0]} + else: #ネットワークルート + curdir=self.listObject.rootDirectory + globalVars.app.say(_("%(dir)sに含まれるストリームを %(sortkind)sの%(sortad)sで一覧中、 %(max)d個中 %(current)d個目") %{'dir': curdir, 'sortkind': self.listObject.GetSortKindString(), 'sortad': self.listObject.GetSortAdString(), 'max': len(self.listObject), 'current': self.GetFocusedItem()+1}, interrupt=True) def GetTabName(self): """タブコントロールに表示する名前""" - word=self.listObject.rootDirectory.split("\\") - word=word[len(word)-1] + word=os.path.basename(self.listObject.rootDirectory) + if word=="": #ルート + word=self.listObject.rootDirectory word=StringUtil.GetLimitedString(word,globalVars.app.config["view"]["header_title_length"]) return word def GetRootObject(self): """ドライブ詳細情報表示で用いる""" - rootPath=self.listObject.rootDirectory[0] - elem=None - if len(rootPath)==1: - try: - elem=lists.drive.GetDriveObject(int(ord(rootPath)-65)) - except: - pass - return elem + return misc.GetRootObject(self.listObject.rootDirectory) diff --git a/views/base.py b/views/base.py index d1f39d7..6ba704e 100644 --- a/views/base.py +++ b/views/base.py @@ -141,6 +141,34 @@ def Enable(self,ref,enable): def IsEnable(self,ref): return self.blockCount[menuItemsStore.getRef(ref)]<=0 + def getItemInfo(self): + """ + メニューに登録されたすべてのアイテムを[(表示名,ref)...]で返します。 + """ + ret=[] + print(self.hMenuBar.GetMenus()) + print(self.hMenuBar.GetMenuCount()) + + if self.hMenuBar==None: + return ret + for menu,id in self.hMenuBar.GetMenus(): + print(menu) + self._addMenuItemList(menu,ret) + return ret + + def _addMenuItemList(self,menu,ret): + if type(menu)==wx.Menu: + items=menu.GetMenuItems() + else: + items=menu.GetSubMenu().GetMenuItems() + for item in items: + if item.GetSubMenu()!=None: + self._addMenuItemList(item,ret) + else: + if item.GetItemLabelText()!="": #セパレータ対策 + ret.append((item.GetItemLabelText(),item.GetId())) + + class BaseEvents(object): """イベント処理のデフォルトの動作をいくつか定義してあります。""" def __init__(self,parent,identifier): diff --git a/views/main.py b/views/main.py index fd8d091..57b9f5d 100644 --- a/views/main.py +++ b/views/main.py @@ -114,6 +114,7 @@ def MakeFirstTab(self): return else: dialog("Error",_("引数で指定されたディレクトリ '%(dir)s' は存在しません。") % {"dir": sys.argv[1]}) + result=self.Navigate(os.path.abspath(os.path.expandvars(self.app.config["browse"]["startPath"])),as_new_tab=True) if result==errorCodes.OK: return @@ -975,7 +976,6 @@ def ShowDetail(self,elem): dic[_("短い名前")]=elem.shortName else: dic[_("短い名前")]=_("なし") - h=win32file.CreateFile( elem.fullpath, 0,