This is where we'll go into the magic of `~/.canto/conf`. This file is actually parsed and driven by the Python interpreter itself, so it's immensely powerful. It has the entirety of the Python language behind it, and allows for an incredible amount of flexibility.

If you've already gone through the basics, then you might be looking for [Advanced Configuration](/canto/advconfig/). That's were all the neat hooks and filters are covered.

[TOC]

## Example Config ##

This gives a fairly comprehensive view of the config. Everything that is not defined in it, is defined in **canto.extra**, which you can browse [here](http://codezen.org/cgi-bin/gitweb.cgi?p=canto.git;a=blob;f=canto/extra.py;hb=master)

    :::python
    from canto.extra import *
    import os

    if os.getenv("TERM") == "linux":
        link_handler("elinks \"%u\"", text=True)
    else:
        link_handler("iceweasel \"%u\"")

        # A dedicated image handler, that requires content
        # to be fetched for it.
        image_handler("xli \"%u\"", fetch=True)

        # A dedicated PDF handler (great for LtU)
        link_handler("evince \"%u\"", fetch=True, ext="pdf")

        # If we're in X, update xterm title
        # (Should work with most graphical terms)   
        select_hook = set_xterm_title
        end_hook = clear_xterm_title

    def my_resize_hook(cfg):
        cfg.columns = cfg.width / 70

    resize_hook = my_resize_hook

    # Global filters, switch with '[' and ']'
    filters=[None, show_unread]

    # You can also setup keybinds to set the global filter
    keys['a'] = set_filter(show_unread) 

    # Similarly you can set tag filters and keybinds
    # switch with '{' and '}'

    default_tag_filters([None, show_unread])
    keys['b'] = set_tag_filter(show_unread)

    # Tag sorts can also be set with a keybind
    keys['s'] = set_tag_sort(by_alpha)
    keys['d'] = set_tag_sort(reverse_sort(by_len))

    # Keys can be single or lists of actions
    # These keys kill the current reader, go to the next
    # or prev unread item and start another reader

    reader_keys["."] = ["destroy","next_unread","reader"]
    reader_keys[","] = ["destroy","prev_unread","reader"]

    # This sets the current item as read and automatically
    # continues to the next item.

    keys['x'] = ["just_read","next_item"]

    # Searches can be bound to keybinds as well
    keys['1'] = search(".*[Ll]inux.*", regex=True)
    keys['2'] = search("Obama")

    # And, of course keys can be unset.
    keys['Q'] = None

    # This makes the reader show links by default, rather
    # than the default, toggling with 'l'
    keys[' '] = ["reader","toggle_show_links"]

    # A whole mess of feeds with tags, and without.
    # A 'None' tag will be replaced with the feeds title
    # The first tag is what is show at the top of each feed by default

    add("http://rss.slashdot.org/slashdot/Slashdot", tags=[None, "news"])
    add("http://osnews.com/files/recent.xml", tags=[None, "news"])
    add("http://www.damninteresting.com/?feed=rss2")
    add("http://reddit.com/.rss", tags=["Reddit","news"])
    add("http://programming.reddit.com/.rss", tags=["Proggit", "news"])
    
    # Username / Passwords are supported for feeds protected by
    # Basic or Digest passwords

    add("http://feedparser.org/docs/examples/digest_auth.xml", username="test",
            password="digest")

    # Tags can work without configuration, or can be customized.
    # This makes the "news" tag default to showing only unread stories
    # and sorting by date.

    add_tag("news", filters=[show_unread], sorts=[[by_date]])

    # Finally, you can set the tags shown with the tags list,
    # and rotate with '<' and '>' by default.

    tags = [ None, ["news"], ["Reddit", "Proggit"]]

    # None will be interpreted as the default set of tags, i.e. one
    # per feed.

## add()
**add(URL,[tags=],[rate=],[keep=],[renderer=],[filter=],[username=],[password=])**

`add()` is the most basic function of Canto's config, adding a feed to your
reader. 

Example:

    :::python
    add("http://rss.slashdot.org/slashdot/Slashdot", rate=10, keep=20)

In this example, Slashdot gets added to your feed list. It's updated every 10 minutes (default is 5), and only 20 stories will be stored on disk. Both keyword arguments (rate and keep) are optional, so 

    :::python
    add("http://reddit.com/.rss")

Is also valid. Feeds that use basic or digest authentication can be fetched
using the `username` and `password` variables. An easy way to determine whether
a feed is using either of these types of authentication is to browse to the feed
with your browser. If the browser pops up a box asking for your username and
password (rather than taking you to a page to login to the site), then it's
fetchable. For an example, browse to the URL in this example:

    :::python
    add("http://feedparser.org/docs/examples/digest_auth.xml", username="test",
            password="digest")

The last simple option for `add` are the tags. Tags are applied to every story
inside of a feed. Tags are simply arbitrary strings that can be used to collect
items and filter/sort (or any other operation that works on groups of items). By
default a `None` tag corresponds to the name of the feed given in the XML, and
if no `tags` are specified, then `tags=[None]` is assumed. If tags are set
though, the first tag is considered the main tag, and is used as the tag name in
log output and in the default GUI.

    :::python
    add("http://reddit.com/.rss", tags=["Reddit", "news"])
    add("http://rss.slashdot.org/slashdot/Slashdot", tags=[None, "news"])

In the first `add` call, Reddit is added, but instead of using "reddit: what's
new online" (the name of the feed given by the feed itself), just plain "Reddit"
is used when printing messages or categorizing Reddit items. In the second `add`
call, Slashdot is added using it's given name. Both feeds are tagged "news".

In addition to any tags specified, every item has the tag "*", so it's possible
to view every single item in a single tag, which is neat for doing things like
sorting over every item.

The state of each item functions as a tag as well, so "read", "marked" can be 
implemented as tags.
However, anything that can be accomplished with those tags might be better
accomplished with filters.

The `renderer` option is covered [here](/canto/style/#per-feed-theming). The
`filter` option is used identically to the filters described [here](
http://codezen.org/canto/advconfig/#filters).

## add_tag()
** add_tag("name", [filters=],[sorts=]) **

`add_tag` is used to specify the attributes of tags. It's not necessary to add
every tag that you use, as tags are all generated on-the-fly, but any tag that
you want to have special attributes must be added. This includes the tags
defined by feeds.

    :::python
    add_tag("Reddit", filters=[show_unread])
    add_tag("news", sorts=[by_date])

## change_feed()
**change_feed(URL, [tags=],[rate=],[keep=],[renderer=],[filter=],[username=],[password=])**

`change_feed` acts identically to `add`, but (as you would imagine) changes
already added feeds. This is intended to add attributes to feeds added from
other sources (like `source_urls` and `source_opml`) where these options aren't
included in the input data.

## source_opml()
**source_opml("path", [append=True/False])**

`source_opml` allows you to add feeds from an opml file at run time, rather than permanently. If you're interested in permanently adding or exporting your feeds with OPML, use `canto -i` and `canto -o` to import and export, respectively.

Example:

    :::python
    source_opml("/home/myuser/feeds.opml")

That append option determines whether the feeds added will be appended to your config file. If true, they will be added in the same manner as `canto -i`. Note that this will be appended every time the config is executed, so only use it if you're sure you want to add the `add_feed` statements to your config.

## source_urls()
**source_urls("path", [append=True/False])**

`source_urls` works similarly to `source_opml`, except it generates `add_feed` statements from a newline delimited list of URLs.

## Setting defaults
**default_rate(rate)**

**default_keep(keep)**

**default_renderer(renderer)**

**default_tag_filters([filters...])**

**default_tag_sorts([[sorts],[sorts]])**

The functions set the default rate in minutes and number of stories to keep for
each feed added after the call. Both of these can be called at any point, and
will not change previously entered feeds.

Example:

    :::python
    # These are actually Canto's defaults.
    default_rate(5)
    default_keep(40)

## Content Handlers
**link_handler(path, [text=],[fetch=],[ext=])**

**image_handler(path, [text=],[fetch=],[ext=])**

`link_handler` handles content that's contained in `<a>` tags, while
`image_handler` handles `<img>` content. The `path` argument is the path to the
binary, with "%u" in place of the URL. `text` should be set to `True` if Canto
should wait for the program (like a text-browser), and `fetch` should be set to
`True` if the content must be fetched (if your program can only open local
files).

The `ext` argument can be used to associate a particular handler only with links
ending with a certain string.

Example:

    :::python
    # Your default link handlers
    link_handler("firefox \"%u\"")

    # xli can't open internet URLs
    image_handler("xli \"%u\"", fetch=True)

    # dedicated PDF handler
    link_handler("evince \"%u\"", fetch=True, ext="pdf")

    # text-browser
    link_handler("elinks \"%u\"", text=True)

## keys

**keys["somekey"] = str, list of strings, function, list of functions**

**reader_keys["somekey"] = str, list of strings, function, list of functions**

Canto has two main GUI elements. The main view, which lists the headlines, and the reader, which gives you more details on a particular story. Each of these views has a different set of possible keybinds.

The format of somekey follows the convention of `C-` for Ctrl, and `M-` for meta (usu. Alt). So, to bind Ctrl-Alt-J to a function somekey would be `C-M-j`, or `M-C-j` (order doesn't matter). In addition, more complex non-printable characters are looked up in the Python curses module. You can find a list of curses keys [here](http://www.mkssoftware.com/docs/man3/curs_getch.3.asp), which is the manpage for `getch`.

Also note that multiple keys can be bound to a single function, and multiple functions can be assigned to a single key. If you wish to unbind a key, set it to `None`.

The functions you can bind to are:

### Main view keys
<table>
<tr>
<td>Name</td>
<td>Function</td>
<td>Default Binding</td>
</tr>
<tr>
<td>`help`</td>
<td>Shows the man page (has all of these bindings listed).</td>
<td>`h`</td>
</tr>
<tr>
<td>`next_item`</td>
<td>Move to the next item.</td>
<td>`KEY_DOWN / j`</td>
</tr>
<tr>
<td>`prev_item`</td>
<td>Move to the previous item.</td>
<td>`KEY_UP / k`</td>
</tr>
<tr>
<td>`next_tag`</td>
<td>Move to the next feed/group of items</td>
<td>`KEY_NPAGE`</td>
</tr>
<tr>
<td>`prev_tag`</td>
<td>Move to the previous feed/group of items.</td>
<td>`KEY_PPAGE`</td>
</tr>
<tr>
<td>`just_read`</td>
<td>Mark current story read and nothing else.</td>
<td>`KEY_RIGHT`</td>
</tr>
<tr>
<td>`just_unread`</td>
<td>Mark current story unread and nothing else.</td>
<td>`KEY_LEFT`</td>
</tr>
<tr>
<td>`goto`</td>
<td>Open the current story in your browser.</td>
<td>`g`</td>
</tr>
<tr>
<td>`inline_search`</td>
<td>Mark all stories matching a search.</td>
<td>`f`</td>
</tr>
<tr>
<td>`next_mark`</td>
<td>Go to the next marked story.</td>
<td>`n`</td>
</tr>
<tr>
<td>`prev_mark`</td>
<td>Go to the previous marked story.</td>
<td>`p`</td>
</tr>
<tr>
<td>`next_unread`</td>
<td>Go to the next unread story.</td>
<td>`.`</td>
</tr>
<tr>
<td>`prev_unread`</td>
<td>Go to the previous unread story.</td>
<td>`,`</td>
</tr>
<tr>
<td>`reader`</td>
<td>Open the reader.</td>
<td>`Space`</td>
</tr>
<tr>
<td>`toggle_collapse_tag`</td>
<td>Collapse/Show a feed/group of items.</td>
<td>`c`</td>
</tr>
<tr>
<td>`set_collapse_all`</td>
<td>Collapse on all feeds/groups.</td>
<td>`C`</td>
</tr>
<tr>
<td>`unset_collapse_all`</td>
<td>Uncollapse all feeds/groups.</td>
<td>`V`</td>
</tr>
<tr>
<td>`toggle_mark`</td>
<td>Mark/unmark an item.</td>
<td>`m`</td>
</tr>
<tr>
<td>`all_unmarked`</td>
<td>Unmark all items</td>
<td>`M`</td>
</tr>
<tr>
<td>`tag_read`</td>
<td>Set all stories in a feed/group read.</td>
<td>`r`</td>
</tr>
<tr>
<td>`all_read`</td>
<td>Set all stories read.</td>
<td>`R`</td>
</tr>
<tr>
<td>`tag_unread`</td>
<td>Set all stories in a feed/group unread.</td>
<td>`u`</td>
</tr>
<tr>
<td>`all_unread`</td>
<td>Set all stories unread.</td>
<td>`U`</td>
</tr>
<tr>
<td>`force_update`</td>
<td>Reread stories from disk.</td>
<td>`C-r`</td>
</tr>
<tr>
<td>`refresh`</td>
<td>Redraw the screen.</td>
<td>`C-l`</td>
</tr>
<tr>
<td>`quit`</td>
<td>Quit Canto.</td>
<td>`q`</td>
</tr>
<tr>
<td>`next_filter`</td>
<td>Apply next global filter.</td>
<td>`]`</td>
</tr>
<tr>
<td>`prev_filter`</td>
<td>Apply previous global filter</td>
<td>`]`</td>
</tr>
<tr>
<td>`next_tag_filter`</td>
<td>Apply next tag filter (from filters)</td>
<td>`}`</td>
</tr>
<tr>
<td>`prev_tag_filter`</td>
<td>Apply previous tag filter</td>
<td>`{`</td>
</tr>
<tr>
<td>`next_tag_sort`</td>
<td>Apply next set of sorts to tag</td>
<td>`=`</td>
</tr>
<tr>
<td>`prev_tag_sort`</td>
<td>Apply previous set of sorts to tag</td>
<td>`-`</td>
</tr>
<tr>
<td>`prev_tagset`</td>
<td>Show previous set of tags</td>
<td>`<`</td>
</tr>
<tr>
<td>`next_tagset`</td>
<td>Show next set of tags</td>
<td>`>`</td>
<tr>
<td>`goto_reltag`</td>
<td>Goto the `nth` visible tag (filter aware)</td>
<td>`;`</td>
</tr>
<tr>
<td>`goto_tag`</td>
<td>Goto the `nth` tag (filter unaware)</td>
<td>`:`</td>
</tr>
<tr>
<td>`switch`</td>
<td>Switch focus to next gui item.</td>
<td>`TAB`</td>
</tr>
</table>

### Reader keys

<table>
<tr>
<td>Name</td>
<td>Function</td>
<td>Default Binding</td>
</tr>
<tr>
<td><code>scroll_down</code></td>
<td>Scrolls, if there's more text.</td>
<td><code>KEY_DOWN / j</code></td>
</tr>
<tr>
<td><code>scroll_up</code></td>
<td>Scroll up, if not at the top.</td>
<td><code>KEY_UP / k</code></td>
</tr>
<tr>
<td><code>page_down</code></td>
<td>Page down.</td>
<td><code>KEY_NPAGE</code></td>
</tr>
<tr>
<td><code>page_up</code></td>
<td>Page Up.</td>
<td><code>KEY_PPAGE</code></td>
</tr>
<tr>
<td><code>["destroy","next_item","reader"]</code></td>
<td>Goto the next story without closing the reader.</td>
<td><code>n</code></td>
</tr>
<tr>
<td><code>["destroy","prev_item","reader"]</code></td>
<td>Goto the previous story without closing the reader.</td>
<td><code>p</code></td>
</tr>
<tr>
<td><code>goto</code></td>
<td>Go to a specific link listed inside the item text.</td>
<td><code>g</code></td>
</tr>
<tr>
<td><code>toggle_show_links</code></td>
<td>Show/hide the list of links at the bottom of the reader.</td>
<td><code>l</code></td>
</tr>
<tr>
<td><code>switch</code></td>
<td>Change focus to next gui item</td>
<td><code>TAB</code></td>
</tr>
<tr>
<td><code>quit</code></td>
<td>Quit</td>
<td><code>space</code></td>
</tr>
</table>

Examples:

Set z in the main screen to open the story's link in a browser.

    :::python
    keys["z"] = "goto"

Set Ctrl-Alt-F1 to help.

    :::python
    keys["C-M-KEY_F1"] = "help"

Set 'j' to scroll up in the reader.

    :::python
    reader_keys["j"] = "scroll_up"

Set 'x' to set current item read and move to the next item.

    :::python
    keys["x"] = ["just_read","next_item"]

## Advanced

There are a handful of other configuration options that you can use.

Canto has many options that allow you to change (radically, I might add) the way that Canto draws the screen, and (a little more safely) change the default colors used to display. These are covered in detail on the [styling](/canto/style/) page.

**Filters** are covered [here](/canto/advconfig/#filters)

**Hooks** are covered [here](/canto/advconfig/#hooks).

**Sorting** feeds arbitrarily is covered [here](/canto/advconfig/#sorting).

As are a lot of other goodies, like writing your own [arbitrary keybinds](/canto/advconfig/#advanced-keybinds).

## Deprecated config options

Canto 0.6.0 deprecated a number of configuration options. `browser` and
`text_browser` are replaced with the much more flexible and robust
`link_handler`. Canto will still interpret these options with only a warning.

`add_feed` has been replaced with `add` and `add_tag`. Canto will interpret
`add_feed` still with a warning, but will ignore *all* extra options.

All of these configuration options **will** disappear from Canto in the **near
future**, so please update your configs.
