dbg_ui_filedialog_win.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 336 行

ERL
336
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(dbg_ui_filedialog_win).%% External exports-export([create_win/6, create_win/7, get_window/1,	 tag/2,	 handle_event/2]).-record(winInfo, {window,        % gsobj()		  extra,         % fun()		  cwd,           % string()		  pattern        % string()		 }).%%====================================================================%% External exports%%====================================================================%%--------------------------------------------------------------------%% create_win(GS, Title, Pos, Mode, Filter, Extra)%% create_win(GS, Title, Pos, Mode, Filter, Extra, FileName) -> #winInfo{}%%   GS  = term()%%   Title = string()%%   Pos = {X,Y}%%   Mode = normal | multiselect%%   Filter = string() File name that may include * symbols.%%   Extra = fun(File) -> {true, tag} | true | {error, term()}%%   FileName = string() Suggested file name when saving%%--------------------------------------------------------------------create_win(GS, Title, {X,Y}, Mode, Filter, Extra) ->    create_win(GS, Title, {X,Y}, Mode, Filter, Extra, null).create_win(GS, Title, {X,Y}, Mode, Filter, Extra, FileName) ->    Pad = 8,    Wlb = 480, Hlb = 130,    Font = dbg_ui_win:font(normal),    {Wlbl, Hlbl} = dbg_ui_win:min_size(["Directories"], 80, 20),    {Wbtn, Hbtn} = dbg_ui_win:min_size(["Filter","Cancel"], 70, 30),    %% Window    Win = gs:window(GS, [{title,Title}, {x, X}, {y,Y}, {destroy,true}]),    %% 'Filter' label and entry (for selecting directory)    gs:label(Win, [{label, {text,"Filter"}}, {font, Font}, {align, sw},		   {x, Pad+2}, {y, Pad}, {width,Wlbl}, {height,Hlbl}]),    gs:entry('Filter', Win, [{x, Pad}, {y, Pad+Hlbl},			     {width, Wlb}, {height, Hbtn},			     {keypress, true}]),    %% Listboxes (showing directories and files)    Xmid = Pad + Wlb/2,    Y2 = Pad + Hlbl + Hbtn + Pad,    gs:label(Win, [{label, {text,"Directories"}},		   {font, Font}, {align, sw},		   {x, Pad+2}, {y, Y2},		   {width, Wlbl}, {height, Hlbl}]),    gs:label(Win, [{label, {text,"Files"}},		   {font, Font}, {align, sw},		   {x, Xmid+Pad/2+2}, {y, Y2},		   {width, Wlbl}, {height, Hlbl}]),    gs:listbox('Dirs', Win, [{x, Pad}, {y, Y2+Hlbl},			     {width, Wlb/2-Pad/2}, {height, Hlb},			     {vscroll, right},			     {click, true}, {doubleclick, true}]),    gs:listbox('Files', Win, [{x, Xmid+Pad/2}, {y, Y2+Hlbl},			      {width, Wlb/2-Pad/2}, {height, Hlb},			      {vscroll, right},			      {click, true}, {doubleclick, true}]),    %% 'Selection' label and entry (for selecting file)    Y3 = Y2 + Hlbl + Hlb,    gs:label(Win, [{label, {text,"Selection"}}, {font,Font}, {align,sw},		   {x, Pad+2}, {y, Y3}, {width, Wlbl}, {height, Hlbl}]),    gs:entry('Selection', Win, [{x, Pad}, {y, Y3+Hlbl},				{width, Wlb}, {height, Hbtn},				{keypress, true}]),    %% Buttons    Y4 = Y3 + Hlbl + Hbtn + Pad,    Wb = Wlb - Wbtn,    Opts = [{y, Y4}, {width, Wbtn}, {height, Hbtn}, {font, Font}],    case Mode of	normal ->	    gs:button(Win, [{label, {text,"Ok"}}, {x, Pad},			    {data, select} | Opts]),	    gs:button(Win, [{label, {text,"Filter"}}, {x, Wlb/2-Wbtn/2},			    {data, filter} | Opts]),	    gs:button(Win, [{label, {text,"Cancel"}}, {x, Pad+Wb},			    {data, done} | Opts]);	multiselect ->	    gs:button(Win, [{label, {text,"Choose"}}, {x, Pad},			    {data, select} | Opts]),	    gs:button(Win, [{label, {text,"All"}}, {x, Pad+Wb/3},			    {data, multiselect} | Opts]),	    gs:button(Win, [{label, {text,"Filter"}}, {x, Pad+2*Wb/3},			    {data, filter} | Opts]),	    gs:button(Win, [{label, {text,"Done"}}, {x, Pad+Wb},			    {data, done} | Opts])    end,    %% Insert contents    {ok, Home} = file:get_cwd(),    {Cwd, Pattern} = update_win(Filter, Extra, Home),    if	is_list(FileName) ->	    gs:config('Selection', {text, filename:join(Cwd,FileName)});	true -> ignore    end,    Wwin = Pad + Wlb + Pad,    Hwin = Y4 + Hbtn + Pad,    gs:config(Win, [{width, Wwin}, {height, Hwin}, {map, true}]),    #winInfo{window=Win, extra=Extra, cwd=Cwd, pattern=Pattern}.%%--------------------------------------------------------------------%% get_window(WinInfo) -> Window%%   WinInfo = #winInfo{}%%   Window = gsobj()%%--------------------------------------------------------------------get_window(WinInfo) ->    WinInfo#winInfo.window.%%--------------------------------------------------------------------%% tag(WinInfo, File)%%   WinInfo = #winInfo{}%%   File = string()%%--------------------------------------------------------------------tag(WinInfo, File0) ->    File = relfile(WinInfo#winInfo.cwd, File0),    case member(File, gs:read('Files', items)) of	{true, Index} -> gs:config('Files', {change, {Index, tag(File)}});	false -> ignore    end.tag(Str) -> [$*|Str].untag([$*|Str]) -> Str;untag([$(|Str]) -> [$)|Rts] = lists:reverse(Str),lists:reverse(Rts);untag(Str) -> Str.member(E, L) ->        member(E, L, 0).member(E, [E|_], I) -> {true, I};member(E, [_|T], I) -> member(E, T, I+1);member(_E, [], _I) ->  false.%%--------------------------------------------------------------------%% handle_event(GSEvent, WinInfo) -> Command%%   GSEvent = {gs, Id, Event, Data, Arg}%%   WinInfo = #winInfo{}%%   Command = ignore%%           | {stopped, Dir}%%           | {win, WinInfo}%%           | {select, File} | {multiselect, Dir, FileNames}%%--------------------------------------------------------------------handle_event({gs, _Id, destroy, _Data, _Args}, WinInfo) ->    {stopped, WinInfo#winInfo.cwd};handle_event({gs, 'Filter', keypress, _Data, ['Return'|_]}, WinInfo) ->    handle_event({gs, null, click, filter, null}, WinInfo);handle_event({gs, 'Selection', keypress, _Data, ['Return'|_]}, WinInfo) ->    handle_event({gs, null, click, select, null}, WinInfo);handle_event({gs, 'Dirs', click, _Data, [0,"..",true|_]}, WinInfo) ->    Filter = filename:join(filename:dirname(WinInfo#winInfo.cwd),			   WinInfo#winInfo.pattern),    gs:config('Filter', {text, Filter}),    ignore;handle_event({gs, 'Dirs', click, _Data, [_Index,Str,true|_]}, WinInfo) ->    Filter = filename:join([WinInfo#winInfo.cwd, Str,			    WinInfo#winInfo.pattern]),    gs:config('Filter', {text, Filter}),    ignore;handle_event({gs, 'Dirs', doubleclick, _Data, _Arg}, WinInfo) ->    handle_event({gs, null, click, filter, null}, WinInfo);handle_event({gs, 'Files', click, _Data, [_Index,Str,true|_]}, WinInfo) ->    Selection = filename:join(WinInfo#winInfo.cwd, untag(Str)),    gs:config('Selection', {text, Selection}),    ignore;handle_event({gs, 'Files', doubleclick, _Data, _Arg}, WinInfo) ->    handle_event({gs, null, click, select, null}, WinInfo);  handle_event({gs, _Id, click, select, _Arg}, _WinInfo) ->    {select, gs:read('Selection', text)};handle_event({gs, _Id, click, multiselect, _Arg}, WinInfo) ->    Files = lists:map(fun(File) -> untag(File) end,		      gs:read('Files', items)),    {multiselect, WinInfo#winInfo.cwd, Files};handle_event({gs, _Id, click, filter, _Arg}, WinInfo) ->    {Cwd, Pattern} = update_win(gs:read('Filter', text),				WinInfo#winInfo.extra,				WinInfo#winInfo.cwd),    {win, WinInfo#winInfo{cwd=Cwd, pattern=Pattern}};handle_event({gs, _Id, click, done, _Arg}, WinInfo) ->    {stopped, WinInfo#winInfo.cwd};    handle_event(_GSEvent, _WinInfo) ->    ignore.%%====================================================================%% Internal functions%%====================================================================update_win(Filter, ExtraFilter, Prev) ->    {Res, {Filter2, Cwd, FilePattern}} = check_filter(Filter, Prev),    Dirs = [".." | get_subdirs(Cwd)],    gs:config('Filter', {text, Filter2}),    gs:config('Dirs', {items, Dirs}),    gs:config('Selection', {text, Cwd}),    case Res of	ok ->	    Matching = lists:sort(filelib:wildcard(Filter2)),	    Files = extra_filter(Matching, Cwd, ExtraFilter),	    gs:config('Files', {items, Files});	error ->	    gs:config('Files', beep)    end,    {Cwd, FilePattern}.%% check_filter(Filter, Prev) -> {ok, Res} | {error, Res}%%   Res = {Filter, Cwd, FilePattern}%%   Filter = Prev = Cwd = FilePattern = string()check_filter(Filter0, Prev) ->    Filter = case filename:pathtype(Filter0) of		 absolute -> Filter0;		 _Relative -> filename:absname(Filter0, Prev)	     end,    Comps = filename:split(Filter),    Last = lists:last(Comps),    FilePattern = case is_pattern(Last) of		      true -> Last;		      false -> "*"		  end,    {Cwd, Rest} = max_existing(Comps),    case Rest of	[] ->	    %% Filter = existing file or directory	    Res = case filelib:is_dir(Filter) of		      true -> {filename:join(Filter, "*"), Filter, "*"};		      false -> {Filter, filename:dirname(Filter),				filename:basename(Filter)}		  end,	    {ok, Res};	[FilePattern] ->	    %% Filter = existing dir and valid pattern	    {ok, {Filter, Cwd, FilePattern}}; 	Comps ->	    %% Filter = garbage	    {error, {Prev, Prev, "*"}};	[Name|_Names] ->	    %% Filter = existing dir ++ pattern or non-existing file/dir	    case is_pattern(Name) of		true -> {ok, {Filter, Cwd, FilePattern}};		false -> {error, {Cwd, Cwd, ""}}	    end    end.max_existing([Name | Names]) ->    case filelib:is_file(Name) of	true -> max_existing(Name, Names);	false -> {[], [Name | Names]}    end.max_existing(Dir, [Name | Names]) ->    Dir2 = filename:join(Dir, Name),    case filelib:is_file(Dir2) of	true when Names==[] -> {Dir2, []};	true -> max_existing(Dir2, Names);	false -> {Dir, [Name | Names]}    end.is_pattern(Str) ->    lists:member($*, Str).extra_filter([File|Files], Dir, Fun) ->    case Fun(File) of	true ->	    [relfile(Dir, File) | extra_filter(Files, Dir, Fun)];	{true,tag} ->	    [[$*|relfile(Dir,File)] | extra_filter(Files, Dir, Fun)];	{true,disable} ->	    [[$(|relfile(Dir,File)]++[$)] | extra_filter(Files, Dir, Fun)];	{error, _Reason} -> extra_filter(Files, Dir, Fun)    end;extra_filter([], _Dir, _Fun) -> [].get_subdirs(Dir) ->    case file:list_dir(Dir) of	{ok, FileNames} ->	    X = lists:filter(fun(FileName) ->				     File = filename:join(Dir, FileName),				     filelib:is_dir(File)			     end,			     FileNames),	    lists:sort(X);	_Error -> []    end.%% Return the "remainder" of a file name relative a dir name, examples:%%   relfile("/home/gunilla", "/home/gunilla/m.erl") -> "m.erl"%%   relfile("/home/gunilla/dir", "/home/gunilla/dir/m.erl") -> "dir/m.erl"%%   relfile("/home/gunilla", "/home/arne/m.erl") -> "/home/arne/m.erl"relfile(Dir, File) ->    case compare(Dir, File) of	error -> File;	RelFile -> RelFile    end.compare([_|Dir], [_|File]) ->    compare(Dir, File);compare([], [$/|File]) ->    File;compare(_, _) ->    error.    

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?