annotate demo/more/grid.ur @ 1739:c414850f206f

Add support for -boot flag, which allows in-tree execution of Ur/Web The boot flag rewrites most hardcoded paths to point to the build directory, and also forces static compilation. This is convenient for developing Ur/Web, or if you cannot 'sudo make install' Ur/Web. The following changes were made: * Header files were moved to include/urweb instead of include; this lets FFI users point their C_INCLUDE_PATH at this directory at write <urweb/urweb.h>. For internal Ur/Web executables, we simply pass -I$PATH/include/urweb as normal. * Differentiate between LIB and SRCLIB; SRCLIB is Ur and JavaScript source files, while LIB is compiled products from libtool. For in-tree compilation these live in different places. * No longer reference Config for paths; instead use Settings; these settings can be changed dynamically by Compiler.enableBoot () (TODO: add a disableBoot function.) * config.h is now generated directly in include/urweb/config.h, for consistency's sake (especially since it gets installed along with the rest of the headers!) * All of the autotools build products got updated. * The linkStatic field in protocols now only contains the name of the build product, and not the absolute path. Future users have to be careful not to reference the Settings files to early, lest they get an old version (this was the source of two bugs during development of this patch.)
author Edward Z. Yang <ezyang@mit.edu>
date Wed, 02 May 2012 17:17:57 -0400
parents 9253765d7724
children e6bc6bbd7a32
rev   line source
adamc@944 1 con colMeta' = fn (row :: Type) (input :: Type) (filter :: Type) =>
adamc@915 2 {Header : string,
adamc@944 3 Project : row -> transaction input,
adamc@944 4 Update : row -> input -> transaction row,
adamc@944 5 Display : input -> xbody,
adamc@944 6 Edit : input -> xbody,
adamc@944 7 Validate : input -> signal bool,
adamc@944 8 CreateFilter : transaction filter,
adamc@944 9 DisplayFilter : filter -> xbody,
adamc@961 10 Filter : filter -> row -> signal bool,
adamc@961 11 Sort : option (row -> row -> bool)}
adamc@1002 12
adamc@1002 13 con colMeta = fn (row :: Type) (global :: Type, input :: Type, filter :: Type) =>
adamc@1002 14 {Initialize : transaction global,
adamc@1002 15 Handlers : global -> colMeta' row input filter}
adamc@915 16
adamc@935 17 con aggregateMeta = fn (row :: Type) (acc :: Type) =>
adamc@935 18 {Initial : acc,
adamc@935 19 Step : row -> acc -> acc,
adamc@935 20 Display : acc -> xbody}
adamc@935 21
adamc@915 22 functor Make(M : sig
adamc@915 23 type row
adamc@936 24 type key
adamc@936 25 val keyOf : row -> key
adamc@936 26
adamc@915 27 val list : transaction (list row)
adamc@915 28 val new : transaction row
adamc@936 29 val save : key -> row -> transaction unit
adamc@936 30 val delete : key -> transaction unit
adamc@915 31
adamc@944 32 con cols :: {(Type * Type * Type)}
adamc@915 33 val cols : $(map (colMeta row) cols)
adamc@915 34
adamc@915 35 val folder : folder cols
adamc@935 36
adamc@935 37 con aggregates :: {Type}
adamc@935 38 val aggregates : $(map (aggregateMeta row) aggregates)
adamc@937 39 val aggFolder : folder aggregates
adamc@964 40
adamc@964 41 val pageLength : option int
adamc@915 42 end) = struct
adamc@915 43 style tabl
adamc@915 44 style tr
adamc@915 45 style th
adamc@915 46 style td
adamc@937 47 style agg
adamc@915 48
adamc@944 49 fun make (row : M.row) [input] [filter] (m : colMeta' M.row input filter) : transaction input = m.Project row
adamc@915 50
adamc@944 51 fun makeAll cols row = @@Monad.exec [transaction] _ [map snd3 M.cols]
adam@1304 52 (@map2 [fst3] [colMeta M.row] [fn p => transaction (snd3 p)]
adam@1304 53 (fn [p] data meta => make row (meta.Handlers data))
adam@1304 54 M.folder cols M.cols)
adam@1304 55 (@@Folder.mp [_] [_] M.folder)
adam@1304 56
adam@1304 57 type listT = {Row : source M.row,
adam@1304 58 Cols : source ($(map snd3 M.cols)),
adam@1304 59 Updating : source bool,
adam@1304 60 Selected : source bool}
adamc@915 61
adamc@944 62 type grid = {Cols : $(map fst3 M.cols),
adam@1304 63 Rows : Dlist.dlist listT,
adamc@944 64 Selection : source bool,
adamc@960 65 Filters : $(map thd3 M.cols),
adamc@964 66 Sort : source (option (M.row -> M.row -> bool)),
adamc@964 67 Position : source int}
adamc@940 68
adamc@954 69 fun newRow cols row =
adamc@915 70 rowS <- source row;
adamc@915 71 cols <- makeAll cols row;
adamc@915 72 colsS <- source cols;
adamc@915 73 ud <- source False;
adamc@940 74 sd <- source False;
adamc@954 75 return {Row = rowS,
adamc@954 76 Cols = colsS,
adamc@954 77 Updating = ud,
adamc@954 78 Selected = sd}
adamc@954 79
adamc@954 80 fun addRow cols rows row =
adamc@954 81 r <- newRow cols row;
adamc@954 82 Monad.ignore (Dlist.append rows r)
adamc@915 83
adamc@944 84 val grid =
adamc@1093 85 cols <- @Monad.mapR _ [colMeta M.row] [fst3]
adamc@1093 86 (fn [nm :: Name] [p :: (Type * Type * Type)] meta => meta.Initialize)
adamc@1093 87 M.folder M.cols;
adamc@915 88
adamc@1093 89 filters <- @Monad.mapR2 _ [colMeta M.row] [fst3] [thd3]
adamc@1093 90 (fn [nm :: Name] [p :: (Type * Type * Type)] meta state =>
adamc@1093 91 (meta.Handlers state).CreateFilter)
adamc@1093 92 M.folder M.cols cols;
adamc@944 93
adamc@951 94 rows <- Dlist.create;
adamc@940 95 sel <- source False;
adamc@960 96 sort <- source None;
adamc@964 97 pos <- source 0;
adamc@951 98
adamc@951 99 return {Cols = cols,
adamc@951 100 Rows = rows,
adamc@951 101 Selection = sel,
adamc@960 102 Filters = filters,
adamc@964 103 Sort = sort,
adamc@964 104 Position = pos}
adamc@915 105
adamc@940 106 fun sync {Cols = cols, Rows = rows, ...} =
adamc@915 107 Dlist.clear rows;
adamc@915 108 init <- rpc M.list;
adamc@954 109 rs <- List.mapM (newRow cols) init;
adamc@954 110 Dlist.replace rows rs
adamc@915 111
adamc@965 112 fun myFilter grid all =
adamc@965 113 row <- signal all.Row;
adamc@1093 114 @foldR3 [colMeta M.row] [fst3] [thd3] [fn _ => M.row -> signal bool]
adamc@1093 115 (fn [nm :: Name] [p :: (Type * Type * Type)]
adamc@1093 116 [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
adamc@1093 117 meta state filter combinedFilter row =>
adamc@1093 118 previous <- combinedFilter row;
adamc@1093 119 this <- (meta.Handlers state).Filter filter row;
adamc@1093 120 return (previous && this))
adamc@1093 121 (fn _ => return True)
adamc@1093 122 M.folder M.cols grid.Cols grid.Filters row
adamc@965 123
adamc@961 124 fun render (grid : grid) = <xml>
adamc@915 125 <table class={tabl}>
adamc@915 126 <tr class={tr}>
adamc@961 127 <th/> <th/> <th><button value="No sort" onclick={set grid.Sort None}/></th>
adam@1641 128 {@mapX2 [fst3] [colMeta M.row] [tr]
adamc@1093 129 (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
adamc@1093 130 data (meta : colMeta M.row p) =>
adamc@1093 131 <xml><th class={th}>
adamc@1093 132 {case (meta.Handlers data).Sort of
adamc@1093 133 None => txt (meta.Handlers data).Header
adamc@1093 134 | sort => <xml><button value={(meta.Handlers data).Header}
adamc@1093 135 onclick={set grid.Sort sort}/></xml>}
adamc@1093 136 </th></xml>)
adamc@1093 137 M.folder grid.Cols M.cols}
adamc@937 138 </tr>
adamc@915 139
adamc@940 140 {Dlist.render (fn {Row = rowS, Cols = colsS, Updating = ud, Selected = sd} pos =>
adamc@937 141 let
adamc@937 142 val delete =
adamc@937 143 Dlist.delete pos;
adamc@937 144 row <- get rowS;
adamc@937 145 rpc (M.delete (M.keyOf row))
adamc@915 146
adamc@937 147 val update = set ud True
adamc@915 148
adamc@937 149 val cancel =
adamc@937 150 set ud False;
adamc@937 151 row <- get rowS;
adamc@937 152 cols <- makeAll grid.Cols row;
adamc@937 153 set colsS cols
adamc@937 154
adamc@937 155 val save =
adamc@937 156 cols <- get colsS;
adamc@1093 157 errors <- @Monad.foldR3 _ [fst3] [colMeta M.row] [snd3] [fn _ => option string]
adamc@1093 158 (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}]
adamc@1093 159 [[nm] ~ rest] data meta v errors =>
adamc@1093 160 b <- current ((meta.Handlers data).Validate v);
adamc@1093 161 return (if b then
adamc@1093 162 errors
adamc@1093 163 else
adamc@1093 164 case errors of
adamc@1093 165 None => Some ((meta.Handlers data).Header)
adamc@1093 166 | Some s => Some ((meta.Handlers data).Header
adamc@1093 167 ^ ", " ^ s)))
adamc@1093 168 None M.folder grid.Cols M.cols cols;
adamc@915 169
adamc@937 170 case errors of
adamc@937 171 Some s => alert ("Can't save because the following columns have invalid values:\n"
adamc@937 172 ^ s)
adamc@937 173 | None =>
adamc@937 174 set ud False;
adamc@937 175 row <- get rowS;
adamc@1093 176 row' <- @Monad.foldR3 _ [fst3] [colMeta M.row] [snd3] [fn _ => M.row]
adamc@1093 177 (fn [nm :: Name] [t :: (Type * Type * Type)]
adamc@1093 178 [rest :: {(Type * Type * Type)}]
adamc@1093 179 [[nm] ~ rest] data meta v row' =>
adamc@1093 180 (meta.Handlers data).Update row' v)
adamc@1093 181 row M.folder grid.Cols M.cols cols;
adamc@937 182 rpc (M.save (M.keyOf row) row');
adamc@937 183 set rowS row';
adamc@937 184
adamc@937 185 cols <- makeAll grid.Cols row';
adamc@937 186 set colsS cols
adamc@937 187 in
adamc@937 188 <xml><tr class={tr}>
adamc@937 189 <td>
adamc@940 190 <dyn signal={b <- signal grid.Selection;
adamc@941 191 return (if b then
adamc@940 192 <xml><ccheckbox source={sd}/></xml>
adamc@940 193 else
adamc@941 194 <xml/>)}/>
adamc@940 195 </td>
adamc@940 196
adamc@940 197 <td>
adamc@937 198 <dyn signal={b <- signal ud;
adamc@937 199 return (if b then
adamc@937 200 <xml><button value="Save" onclick={save}/></xml>
adamc@937 201 else
adamc@937 202 <xml><button value="Update" onclick={update}/></xml>)}/>
adamc@937 203 </td>
adamc@937 204
adamc@937 205 <td><dyn signal={b <- signal ud;
adamc@937 206 return (if b then
adamc@937 207 <xml><button value="Cancel" onclick={cancel}/></xml>
adamc@937 208 else
adamc@937 209 <xml><button value="Delete" onclick={delete}/></xml>)}/>
adamc@937 210 </td>
adamc@937 211
adamc@937 212 <dyn signal={cols <- signal colsS;
adamc@1172 213 return (@mapX3 [fst3] [colMeta M.row] [snd3] [_]
adamc@1093 214 (fn [nm :: Name] [t :: (Type * Type * Type)]
adamc@1093 215 [rest :: {(Type * Type * Type)}]
adamc@1093 216 [[nm] ~ rest] data meta v =>
adamc@1093 217 <xml><td class={td}>
adamc@1093 218 <dyn signal={b <- signal ud;
adamc@1093 219 return (if b then
adamc@1093 220 (meta.Handlers data).Edit v
adamc@1093 221 else
adamc@1093 222 (meta.Handlers data).Display
adamc@1093 223 v)}/>
adamc@1093 224 <dyn signal={b <- signal ud;
adamc@1093 225 if b then
adamc@1093 226 valid <-
adamc@1093 227 (meta.Handlers data).Validate v;
adamc@1093 228 return (if valid then
adamc@1093 229 <xml/>
adamc@1093 230 else
adamc@1093 231 <xml>!</xml>)
adamc@1093 232 else
adamc@1093 233 return <xml/>}/>
adamc@1093 234 </td></xml>)
adamc@1093 235 M.folder grid.Cols M.cols cols)}/>
adamc@937 236 </tr></xml>
adamc@951 237 end)
adamc@965 238 {StartPosition = case M.pageLength of
adamc@965 239 None => return None
adamc@965 240 | Some len =>
adamc@965 241 avail <- Dlist.numPassing (myFilter grid) grid.Rows;
adamc@965 242 pos <- signal grid.Position;
adamc@965 243 return (Some (if pos >= avail then
adamc@965 244 0
adamc@965 245 else
adamc@965 246 pos)),
adamc@964 247 MaxLength = return M.pageLength,
adamc@965 248 Filter = myFilter grid,
adamc@960 249 Sort = f <- signal grid.Sort;
adamc@960 250 return (Option.mp (fn f r1 r2 => r1 <- signal r1.Row;
adamc@960 251 r2 <- signal r2.Row;
adamc@960 252 return (f r1 r2)) f)}
adamc@951 253 grid.Rows}
adamc@915 254
adam@1304 255 <dyn signal={rows <- Dlist.foldl (fn row : listT =>
adam@1649 256 @Monad.mapR2 _ [aggregateMeta M.row] [ident] [ident]
adam@1304 257 (fn [nm :: Name] [t :: Type] meta acc =>
adam@1304 258 Monad.mp (fn v => meta.Step v acc)
adam@1304 259 (signal row.Row))
adam@1304 260 M.aggFolder M.aggregates)
adam@1649 261 (@mp [aggregateMeta M.row] [ident]
adamc@937 262 (fn [t] meta => meta.Initial)
adamc@1093 263 M.aggFolder M.aggregates) grid.Rows;
adamc@937 264 return <xml><tr>
adamc@941 265 <th colspan={3}>Aggregates</th>
adam@1649 266 {@mapX2 [aggregateMeta M.row] [ident] [_]
adamc@1093 267 (fn [nm :: Name] [t :: Type] [rest :: {Type}] [[nm] ~ rest] meta acc =>
adamc@1093 268 <xml><td class={agg}>{meta.Display acc}</td></xml>)
adamc@1093 269 M.aggFolder M.aggregates rows}
adamc@937 270 </tr></xml>}/>
adamc@944 271
adamc@944 272 <tr><th colspan={3}>Filters</th>
adamc@1172 273 {@mapX3 [colMeta M.row] [fst3] [thd3] [_]
adamc@1093 274 (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
adamc@1093 275 meta state filter => <xml><td>{(meta.Handlers state).DisplayFilter filter}</td></xml>)
adamc@1093 276 M.folder M.cols grid.Cols grid.Filters}
adamc@944 277 </tr>
adamc@915 278 </table>
adamc@964 279
adamc@964 280 {case M.pageLength of
adamc@964 281 None => <xml/>
adamc@964 282 | Some plen => <xml>
adamc@965 283 <dyn signal={avail <- Dlist.numPassing (myFilter grid) grid.Rows;
adamc@964 284 return (if avail <= plen then
adamc@964 285 <xml/>
adamc@964 286 else
adamc@964 287 let
adamc@964 288 val numPages = avail / plen
adamc@964 289 val numPages = if numPages * plen < avail then
adamc@964 290 numPages + 1
adamc@964 291 else
adamc@964 292 numPages
adamc@964 293
adamc@964 294 fun pages n =
adamc@964 295 if n * plen >= avail then
adamc@964 296 <xml/>
adamc@964 297 else
adamc@964 298 <xml>
adamc@964 299 <dyn signal={pos <- signal grid.Position;
adamc@964 300 return (if n * plen = pos then
adamc@964 301 <xml><b>{[n + 1]}</b></xml>
adamc@964 302 else
adamc@964 303 <xml>
adamc@964 304 <button value={show (n + 1)}
adamc@964 305 onclick={set grid.Position
adamc@964 306 (n * plen)
adamc@964 307 }/></xml>)}/>
adamc@964 308 {if (n + 1) * plen >= avail then <xml/> else <xml>|</xml>}
adamc@964 309 {pages (n + 1)}
adamc@964 310 </xml>
adamc@964 311 in
adamc@964 312 <xml><p><b>Pages:</b> {pages 0}</p></xml>
adamc@964 313 end)}/>
adamc@964 314 </xml>}
adamc@915 315
adamc@915 316 <button value="New row" onclick={row <- rpc M.new;
adamc@915 317 addRow grid.Cols grid.Rows row}/>
adamc@915 318 <button value="Refresh" onclick={sync grid}/>
adamc@915 319 </xml>
adamc@940 320
adamc@940 321 fun showSelection grid = grid.Selection
adamc@940 322
adamc@940 323 fun selection grid = Dlist.foldl (fn {Row = rowS, Selected = sd, ...} ls =>
adamc@940 324 sd <- signal sd;
adamc@940 325 if sd then
adamc@940 326 row <- signal rowS;
adamc@940 327 return (row :: ls)
adamc@940 328 else
adamc@940 329 return ls) [] grid.Rows
adamc@915 330 end