rails-3.1.0をcygwinでやるメモ
javascript engine
なんとなくspidermonkeyにする。
http://ftp.mozilla.org/pub/mozilla.org/js/
から
js-1.7.0.tar.gz
をダウンロードする。
$ cd /usr/local/src $ wget http://ftp.mozilla.org/pub/mozilla.org/js/js-1.7.0.tar.gz $ tar xzf js-1.7.0.tar.gz $ cd js/src $ make -f Makefile.ref OS_ARCH=Linux LD=gcc $ cp cp Linux_All_DBG.OBJ/js.exe /usr/local/bin/
export EXECJS_RUNTIME=SpiderMonkey
Ruby version 1.9.3 (i386-cygwin) RubyGems version 1.8.23 Rack version 1.3 Rails version 3.1.0 JavaScript Runtime SpiderMonkey Active Record version 3.1.0 Action Pack version 3.1.0 Active Resource version 3.1.0 Action Mailer version 3.1.0 Active Support version 3.1.0 Middleware ActionDispatch::Static Rack::Lock #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x80ef9ba8> Rack::Runtime Rack::MethodOverride Rails::Rack::Logger ActionDispatch::ShowExceptions ActionDispatch::RemoteIp Rack::Sendfile ActionDispatch::Reloader ActionDispatch::Callbacks ActiveRecord::ConnectionAdapters::ConnectionManagement ActiveRecord::QueryCache ActionDispatch::Cookies ActionDispatch::Session::CookieStore ActionDispatch::Flash ActionDispatch::ParamsParser ActionDispatch::Head Rack::ConditionalGet Rack::ETag ActionDispatch::BestStandardsSupport Application root /cygdrive/c/work/_rails/demo Environment development Database adapter sqlite3 Database schema version 0
JSONと相互変換できるXMLの記述
JsonReaderWriterFactoryはどんなJSONでもParseしてXElemnt化できるが、XmlをJSON化するには特定の構造を持っている必要がある。
objectとarrayとその他の単一項目(number, boolean, stringなど)の持ち方が決まっている。
見た目に分かるようなものをwpfで作ってみた。あとで続きを作るかもしれないが原型ができたのでメモ。
<Window x:Class="XMLExperiment.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TreeView x:Name="tree" Grid.Column="0" ItemsSource="{Binding Root.Children}"> <!--DirectoryViewModel のデータを表示するときに使うテンプレート--> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Children}"> <TextBlock Text="{Binding Name}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> <TextBox x:Name="xml" Grid.Column="1" Text="{Binding Xml}" /> <TextBox x:Name="json" Grid.Column="2" Text="{Binding Json}" /> </Grid> </Window>
namespace XMLExperiment { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var tree = new Tree(); DataContext = tree; } } }
namespace XMLExperiment { public class Node : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(String info) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(info)); } } private XElement _e; private ObservableCollection<Node> _children; public Node(XElement e) { _e = e; } public String Name { get { return _e.Name.ToString(); } } public ObservableCollection<Node> Children { get { if (_children == null) { _children = new ObservableCollection<Node>(_e.Elements().Select(e => new Node(e))); } return _children; } } } public class Tree { public Tree() { Json = @"{ ""hoge"":[ 1 ,2 ,3 ,{ ""nazo"": true } ] }"; } private XElement _root; public Node Root { get { return new Node(_root); } } public String Xml { set { } get { return _root.ToString(); } } public String Json { set { using (var reader = JsonReaderWriterFactory.CreateJsonReader(Encoding.Unicode.GetBytes(value), XmlDictionaryReaderQuotas.Max)) { _root=XElement.Load(reader); } } get { using (var ms = new MemoryStream()) using (var writer = JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.Unicode)) { _root.WriteTo(writer); writer.Flush(); return Encoding.Unicode.GetString(ms.ToArray()); } } } } }
自分はobjectをシリアライズしたりデシリアライズしたいのではなくて動的に組み立ててそれをJSONと相互に変換したいので、
いまいち既存のライブラリとは目的が一致しないのだよな・・・。
泥臭いXElementラッパを作るのが早いかも知れぬ。
ファイラーその4
アイコンをつける。
Win32apiのSHGetFileInfoを呼べるようにする
[StructLayout(LayoutKind.Sequential)] public struct SHFILEINFO { public IntPtr hIcon; public IntPtr iIcon; public uint dwAttributes; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szDisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] public string szTypeName; }; class Win32 { public const uint SHGFI_ICON = 0x100; public const uint SHGFI_LARGEICON = 0x0; // 'Large icon public const uint SHGFI_SMALLICON = 0x1; // 'Small icon [DllImport("shell32.dll")] public static extern IntPtr SHGetFileInfo( string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags); [DllImport("user32")] public static extern int DestroyIcon(IntPtr hIcon); }
ファイルパスからアイコン画像を得る。ObservableCollection
class Item { public FileSystemInfo Info { set; get; } public String Name { get { return Info.Name; } } public String Length { get { var file = Info as FileInfo; if (file == null) { return ""; } else { return file.Length.ToString(); } } } public BitmapSource Bitmap { get { SHFILEINFO shinfo = new SHFILEINFO(); var hImgLarge = Win32.SHGetFileInfo(Info.FullName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | Win32.SHGFI_LARGEICON); BitmapSource source = Imaging.CreateBitmapSourceFromHIcon(shinfo.hIcon, Int32Rect.Empty, null); Win32.DestroyIcon(shinfo.hIcon); return source; } } };
ListViewのDataTemplateにBitmapをバインディングする
<ListView Grid.Row="1" ItemsSource="{Binding Path=Files}" AlternationCount="2"> <ListView.View> <GridView> <GridView.Columns> <GridViewColumn Header="Name"> <GridViewColumn.CellTemplate> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Image Grid.Column="0" Source="{Binding Path=Bitmap}" Margin="5,0,5,0" /> <Label Grid.Column="1" Content="{Binding Path=Name}"/> </Grid> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Size"> <GridViewColumn.CellTemplate> <DataTemplate> <Label Content="{Binding Path=Length}" HorizontalAlignment="Right"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView.Columns> </GridView> </ListView.View> <ListView.ItemContainerStyle> <Style TargetType="{x:Type ListViewItem}" > <Setter Property="HorizontalContentAlignment" Value="Stretch" /> <EventSetter Event="MouseDoubleClick" Handler="listBoxItem_DoubleClick"/> </Style> </ListView.ItemContainerStyle> </ListView>
アイコンが出るようになった。
別スレッドでアイコン画像の読み込みをする
public DirectoryInfo Current { get { return current_; } set { current_ = value; NotifyPropertyChanged("Current"); NotifyPropertyChanged("Path"); files_.Clear(); var workList=new List<Item>(); try { foreach (var e in current_.GetFileSystemInfos()) { // FileSystemInfoだけのリストを作って、 // 後でアイコン画像をロードする var item=new Item { Info = e }; files_.Add(item); workList.Add(item); } // 別スレッドでビットマップ更新を呼び出す var task = new Task(() => { foreach (var item in workList) { var source = LoadBitmapSource(item.Info); source.Freeze(); // 重要 Action action = () => { // UIスレッド item.Bitmap = source; }; dispatcher_.Invoke(action); } }); task.Start(); } catch (UnauthorizedAccessException e) { // do nothing } catch (DirectoryNotFoundException e) { // do nothing } } }
ファイラーその3
ListBoxからListViewに変えてみる。
MainWindow.xaml
<Window x:Class="filer.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:IO="clr-namespace:System.IO;assembly=mscorlib" Title="Filer" Height="350" Width="525"> <Window.Resources> <Style TargetType="ListViewItem"> <EventSetter Event="MouseDoubleClick" Handler="listBoxItem_DoubleClick"/> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBox Grid.Column="0" Name="currentFolder" Text="{Binding Path=Path}" /> <Button Grid.Column="1" Name="goParent" Width="30" Content=".." Click="goParent_Click" Cursor="Hand"/> </Grid> <ScrollViewer Grid.Row="1" > <ListView ItemsSource="{Binding Path=Files}" AlternationCount="2"> <ListView.View> <GridView> <GridView.Columns> <GridViewColumn Header="Name"> <GridViewColumn.CellTemplate> <DataTemplate> <Label Content="{Binding Path=Name}"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Size"> <GridViewColumn.CellTemplate> <DataTemplate> <Label Content="{Binding Path=Length}"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView.Columns> </GridView> </ListView.View> </ListView> </ScrollViewer> </Grid> </Window>
ListBoxをListViewに変更して、中のGridViewにカラムを2つ作った。
さらにDataTemplateをWindow.ResourcesからGridViewColumn.CellTemplateに移動した。
DataTypeとColumn番号からDataTemplateを選択する方法を模索したのだが、
CellTemplateSelectorを書く方法しか見つからなかったのでなるべくxamlだけで済ます方針から見送った。
右寄せにする
Styleで伸ばしてから
<Style TargetType="ListViewItem"> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> </Style>
DataTemplateで右に寄せる
<DataTemplate> <Label Content="{Binding Path=Length}" HorizontalAlignment="Right"/> </DataTemplate>
スタイルをResourcesからListViewの下に移動する
<ListView.ItemContainerStyle> <Style TargetType="{x:Type ListViewItem}" > <Setter Property="HorizontalContentAlignment" Value="Stretch" /> <EventSetter Event="MouseDoubleClick" Handler="listBoxItem_DoubleClick"/> </Style> </ListView.ItemContainerStyle>
FileSystemInfoのラッパを作ってLengthプロパティアクセス時にエラーが出ないようにする
DirectoryInfo.Lengthにアクセスしたとき、そんなプロパティは無いというエラーが出る。
ObservableCollection
class Item { public bool IsDirectory { set; get; } public FileSystemInfo Info { set; get; } public String Name { get { return Info.Name; } } public String Length { get { var file = Info as FileInfo; if (file == null) { return ""; } else { return file.Length.ToString(); } } } };
ToDo: アイコンをつける
ファイラーその2
ListBoxItemのダブルクリックイベントを実装する。
MainWindow.xaml
<ListBox Name="currentFolderFiles" ItemsSource="{Binding Path=Files}"> <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}"> <EventSetter Event="MouseDoubleClick" Handler="listBoxItem_DoubleClick" /> </Style> </ListBox.ItemContainerStyle> </ListBox>
MainWindow.xaml.cs
public partial class MainWindow : Window { private FileView fileView_ = new FileView("C:\\"); public MainWindow() { InitializeComponent(); DataContext=fileView_; } private void listBoxItem_DoubleClick(object sender, MouseButtonEventArgs args) { var item = sender as ListBoxItem; var directory = item.Content as DirectoryInfo; if (directory != null) { fileView_.Current=directory; return; } var file = item.Content as FileInfo; if(file != null) { return; } } }
FileView.cs
private DirectoryInfo current_; // 追加 public DirectoryInfo Current { get { return current_; } set { current_ = value; NotifyPropertyChanged("Current"); NotifyPropertyChanged("Path"); /* 入れ物が変わってしまう・・・ files_ = new ObservableCollection<FileSystemInfo>( current_.GetFileSystemInfos().ToArray()); */ files_.Clear(); foreach (var e in current_.GetFileSystemInfos()) { files_.Add(e); } } } public string Path { get { return current_.FullName; } set { Current = new DirectoryInfo(value); } }
ディレクトリの上に来たらマウスカーソルを変える
MainWindow.xaml
<DataTemplate DataType="{x:Type IO:DirectoryInfo}"> <Label Content="{Binding Path=Name}" Foreground="#FF2222" Cursor="Hand"/> </DataTemplate>
横いっぱいに広げる
<ListBox x:Name="currentFolderFiles" ItemsSource="{Binding Path=Files}" HorizontalContentAlignment="Stretch">
親ディレクトリに戻るボタン
MainWindow.xaml
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBox Grid.Column="0" Name="currentFolder" Text="{Binding Path=Path}" /> <Button Grid.Column="1" Name="goParent" Width="30" Content=".." Click="goParent_Click" Cursor="Hand"/> </Grid>
立て分割のGridに、横分割のGridを入れ子にした
MainWindow.xaml.cs
private void goParent_Click(object sender, RoutedEventArgs e) { if (fileView_.Current.Parent != null) { fileView_.Current = fileView_.Current.Parent; } }