//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 12 Sep 08 Brian Frank Creation
//
using gfx
using fwt
**
** History maintains the most recent navigation history
** of the entire application.
**
@Serializable { collection = true }
class History
{
**
** Convenience for loading from "session/history"
**
static History load()
{
return Flux.loadOptions(Flux.pod, "session/history", History#)
}
**
** Convenience for save to "session/history".
** Return this.
**
This save()
{
Flux.saveOptions(Flux.pod, "session/history", this)
return this
}
**
** Log navigation to the specified resource
** into the history. Return this.
**
This push(Resource r)
{
// don't log flux: uris
if (r.uri.scheme == "flux") return this
// map resource to item
item := HistoryItem
{
uri = r.uri
iconUri = r.icon.uri
time = DateTime.now
}
// push as most recent (remove old record for uri if found)
dup := items.findIndex |HistoryItem i->Bool| { return i.uri == r.uri }
if (dup != null) list.removeAt(dup)
while (items.size >= max) list.removeAt(-1)
list.insert(0, item)
return this
}
**
** Get a readonly copy of all the items in the history.
** The first item is the most recent navigation and the last
** item is the oldest navigation.
**
HistoryItem[] items()
{
return list.ro
}
**
** Add a new history item to the end of the history.
** This method is typically only used for serialization.
** See `push` to log navigation of a Uri. Return this.
**
This add(HistoryItem item)
{
if ((Obj?)item.uri == null || (Obj?)item.time == null)
throw ArgErr("Invalid item: " + item)
list.add(item)
return this
}
**
** Iterate the history items from most recent to oldest.
**
Void each(|HistoryItem item| f)
{
list.each(f)
}
@Transient private Int max := 40
@Transient private HistoryItem[] list := HistoryItem[,]
}
**************************************************************************
** HistoryItem
**************************************************************************
**
** HistoryItem stores information about navigation to a specific uri.
**
@Serializable
const class HistoryItem
{
**
** Default constructor with it-block
**
new make(|This|? f := null)
{
if (f != null) f(this)
}
**
** Uri of resource.
**
const Uri uri := ``
**
** Last time of access.
**
const DateTime time := DateTime.now
**
** Uri for icon of resource or null.
**
const Uri? iconUri
**
** Return uri string.
**
override Str toStr() { return uri.toStr }
}
**************************************************************************
** HistoryPicker
**************************************************************************
class HistoryPicker : EdgePane
{
new make(HistoryItem[] items, Bool fullPath, |HistoryItem, Event| onAction)
{
model := HistoryPickerModel(items, fullPath)
center = Table
{
it.headerVisible = false
it.model = model
it.onAction.add |Event e|
{
onAction(model.items[e.index], e)
}
it.onKeyDown.add |Event e|
{
code := e.keyChar
if (code >= 97 && code <= 122) code -= 32
code -= 65
if (code >= 0 && code < 26 && code < model.numRows)
onAction(model.items[code], e)
}
}
}
}
internal class HistoryPickerModel : TableModel
{
new make(HistoryItem[] items, Bool fullPath)
{
this.items = items
this.fullPath = fullPath
icons = items.map |HistoryItem item->Image|
{
Image(item.iconUri, false) ?: def
}
}
override Int numCols() { return 2 }
override Int numRows() { return items.size }
override Int? prefWidth(Int col)
{
switch (col)
{
case 0: return fullPath ? 450 : 250
case 1: return 24
default: return null
}
}
override Image? image(Int col, Int row) { return col==0 ? icons[row] : null }
override Font? font(Int col, Int row) { return col==1 ? accFont : null }
override Color? fg(Int col, Int row) { return col==1 ? accColor : null }
override Str text(Int col, Int row)
{
switch (col)
{
case 0: uri := items[row].uri; return fullPath ? uri.toStr : uri.name
case 1: return (row < 26) ? (row+65).toChar : ""
default: return ""
}
}
HistoryItem[] items
Image[] icons
Image def := Flux.icon(`/x16/file.png`)
Font accFont := Desktop.sysFont.toSize(Desktop.sysFont.size-1)
Color accColor := Color("#666")
Bool fullPath
}