emacs-ctable-0.1.2/0000755000175000017500000000000012275027457013677 5ustar dogslegdogslegemacs-ctable-0.1.2/ctable.el0000644000175000017500000021711712275027457015464 0ustar dogslegdogsleg;;; ctable.el --- Table component for Emacs Lisp ;; Copyright (C) 2011, 2012, 2013, 2014 SAKURAI Masashi ;; Author: SAKURAI Masashi ;; URL: https://github.com/kiwanami/emacs-ctable ;; Version: 0.1.2 ;; Keywords: table ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; This program is a table component for Emacs Lisp. ;; Other programs can use this table component for the application UI. ;;; Installation: ;; Place this program in your load path and add following code. ;; (require 'ctable) ;;; Usage: ;; Executing the command `ctbl:open-table-buffer', switch to the table buffer. ;; Table data which are shown in the table view, are collected ;; by the `ctbl:model' objects. See the function `ctbl:demo' for example. ;; See the README document for the details. ;;; Code: (eval-when-compile (require 'cl)) ;;; Models and Parameters (defstruct ctbl:model "Table model structure data : Table data as a list of rows. A row contains a list of columns. If an instance of `ctbl:async-model' is given, the model is built up asynchronously. column-model : A list of column models. sort-state : The current sort order as a list of column indexes. The index number of the first column is 1. If the index is negative, the sort order is reversed." data column-model sort-state) (defstruct ctbl:async-model "Asynchronous data model request : Data request function which receives 4 arguments (begin-num length fn(row-list) fe(errmsg)). This function should return the next data which begins with `begin-num' and has the length as `length', evaluating the continuation function `fn' with the data. If the function `fn' is given `nil', it means no more data. If the error function `fe' is evaluated with `errmsg', the message is displayed for the user. init-num : Initial row number. (Default 20) more-num : Increase row number. (Default 20) reset : Reset function which is called when user executes update command. (Can be nil) cancel : Cancel function of data requesting. (Can be nil) For forward compatibility, these callback functions should have a `&rest' keyword at the end of argument list. " request (init-num 20) (more-num 20) reset cancel) (defstruct ctbl:cmodel "Table column model structure title : title string. sorter : sorting function which transforms a cell value into sort value. It should return -1, 0 and 1. If nil, `ctbl:sort-string-lessp' is used. align : text alignment: 'left, 'right and 'center. (default: right) max-width : maximum width of the column. if nil, no constraint. (default: nil) min-width : minimum width of the column. if nil, no constraint. (default: nil) click-hooks : a list of functions for header clicking with two arguments the `ctbl:component' object and the `ctbl:cmodel' one. (default: '(`ctbl:cmodel-sort-action'))" title sorter align max-width min-width (click-hooks '(ctbl:cmodel-sort-action))) (defstruct ctbl:param "Rendering parameters display-header : if t, display the header row with column models. fixed-header : if t, display the header row in the header-line area. bg-colors : '(((row-id . col-id) . colorstr) (t . default-color) ... ) or (lambda (model row-id col-id) colorstr or nil) vline-colors : \"#RRGGBB\" or '((0 . colorstr) (t . default-color)) or (lambda (model col-index) colorstr or nil) hline-colors : \"#RRGGBB\" or '((0 . colorstr) (t . default-color)) or (lambda (model row-index) colorstr or nil) draw-vlines : 'all or '(0 1 2 .. -1) or (lambda (model col-index) t or nil ) draw-hlines : 'all or '(0 1 2 .. -1) or (lambda (model row-index) t or nil ) vertical-line horizontal-line : | - left-top-corner right-top-corner left-bottom-corner right-bottom-corner : + top-junction bottom-junction left-junction right-junction cross-junction : +" display-header fixed-header bg-colors vline-colors hline-colors draw-vlines draw-hlines vertical-line horizontal-line left-top-corner right-top-corner left-bottom-corner right-bottom-corner top-junction bottom-junction left-junction right-junction cross-junction) (defvar ctbl:completing-read 'completing-read "Customize for completing-read function. To use `ido-completing-read', put the following sexp into your Emacs init file: (eval-after-load 'ido '(progn (setq ctbl:completing-read 'ido-completing-read)))") (defvar ctbl:default-rendering-param (make-ctbl:param :display-header t :fixed-header nil :bg-colors nil :vline-colors "DarkGray" :hline-colors "DarkGray" :draw-vlines 'all :draw-hlines '(1) :vertical-line ?| :horizontal-line ?- :left-top-corner ?+ :right-top-corner ?+ :left-bottom-corner ?+ :right-bottom-corner ?+ :top-junction ?+ :bottom-junction ?+ :left-junction ?+ :right-junction ?+ :cross-junction ?+ ) "Default rendering parameters.") (defvar ctbl:tooltip-method '(pos-tip popup minibuffer) "Preferred tooltip methods in order.") ;;; Faces (defface ctbl:face-row-select '((((class color) (background light)) :background "WhiteSmoke") (((class color) (background dark)) :background "Blue4")) "Face for row selection" :group 'ctable) (defface ctbl:face-cell-select '((((class color) (background light)) :background "Mistyrose1") (((class color) (background dark)) :background "Blue2")) "Face for cell selection" :group 'ctable) (defface ctbl:face-continue-bar '((((class color) (background light)) :background "OldLace") (((class color) (background dark)) :background "Gray26")) "Face for continue bar" :group 'ctable) ;;; Utilities (defun ctbl:define-keymap (keymap-list &optional prefix) "[internal] Keymap utility." (let ((map (make-sparse-keymap))) (mapc (lambda (i) (define-key map (if (stringp (car i)) (read-kbd-macro (if prefix (replace-regexp-in-string "prefix" prefix (car i)) (car i))) (car i)) (cdr i))) keymap-list) map)) (defun ctbl:cell-id (row-id col-id) "[internal] Create a cell-id object" (cons row-id col-id)) (defun ctbl:tp (text prop value) "[internal] Put a text property to the entire text string." (if (< 0 (length text)) (put-text-property 0 (length text) prop value text)) text) (defvar ctbl:uid 1) (defun ctbl:uid () "[internal] Generate an unique number." (incf ctbl:uid)) (defun ctbl:fill-keymap-property (begin end keymap) "[internal] Put the given text property to the region between BEGIN and END. If the text already has some keymap property, the text is skipped." (save-excursion (goto-char begin) (loop with pos = begin with nxt = nil until (or (null pos) (<= end pos)) when (get-text-property pos 'keymap) do (setq pos (next-single-property-change pos 'keymap)) else do (setq nxt (next-single-property-change pos 'keymap)) (when (null nxt) (setq nxt end)) (put-text-property pos (min nxt end) 'keymap keymap)))) ;; Model functions (defun ctbl:model-column-length (model) "[internal] Return the column number." (length (ctbl:model-column-model model))) (defun ctbl:model-row-length (model) "[internal] Return the row number." (length (ctbl:model-data model))) (defun ctbl:model-modify-sort-key (model col-index) "Modify the list of sort keys for the column headers." (let* ((sort-keys (ctbl:model-sort-state model)) (col-key (1+ col-index))) (cond ((eq (car sort-keys) col-key) (setf (ctbl:model-sort-state model) (cons (- col-key) (cdr sort-keys)))) ((eq (car sort-keys) (- col-key)) (setf (ctbl:model-sort-state model) (cons col-key (cdr sort-keys)))) (t (setf (ctbl:model-sort-state model) (cons col-key (delete (- col-key) (delete col-key sort-keys)))))) (ctbl:model-sort-state model))) (defun ctbl:cmodel-sort-action (cp col-index) "Sorting action for click on the column headers. If data is an instance of `ctbl:async-model', this function do nothing." (let* ((model (ctbl:cp-get-model cp))) (unless (ctbl:async-model-p (ctbl:model-data model)) (ctbl:model-modify-sort-key model col-index) (ctbl:cp-update cp)))) ;;; ctable framework ;; Component (defstruct ctbl:component "Component This structure defines attributes of the table component. These attributes are internal use. Other programs should access through the functions of the component interface. dest : an object of `ctbl:dest' model : an object of the table model selected : selected cell-id: (row index . col index) param : rendering parameter object sorted-data : sorted data to display the table view. see `ctbl:cp-get-selected-data-row' and `ctbl:cp-get-selected-data-cell'. update-hooks : a list of hook functions for update event selection-change-hooks : a list of hook functions for selection change event click-hooks : a list of hook functions for click event states : alist of arbitrary data for internal use" dest model param selected sorted-data update-hooks selection-change-hooks click-hooks states) ;; Rendering Destination (defstruct ctbl:dest "Rendering Destination This structure object is the abstraction of the rendering destinations, such as buffers, regions and so on. type : identify symbol for destination type. (buffer, region, text) buffer : a buffer object of rendering destination. min-func : a function that returns upper limit of rendering destination. max-func : a function that returns lower limit of rendering destination. width : width of the reference size. (number, nil or full) height : height of the reference size. (number, nil or full) clear-func : a function that clears the rendering destination. before-update-func : a function that is called at the beginning of rendering routine. after-update-func : a function that is called at the end of rendering routine. select-ol : a list of overlays for selection" type buffer min-func max-func width height clear-func before-update-func after-update-func select-ol) (eval-when-compile (defmacro ctbl:dest-with-region (dest &rest body) (declare (debug (form &rest form))) (let (($dest (gensym))) `(let ((,$dest ,dest)) (with-current-buffer (ctbl:dest-buffer ,$dest) (save-restriction (narrow-to-region (ctbl:dest-point-min ,$dest) (ctbl:dest-point-max ,$dest)) ,@body)))))) (put 'ctbl:dest-with-region 'lisp-indent-function 1) (defun ctbl:dest-point-min (c) (funcall (ctbl:dest-min-func c))) (defun ctbl:dest-point-max (c) (funcall (ctbl:dest-max-func c))) (defun ctbl:dest-clear (c) (funcall (ctbl:dest-clear-func c))) (defun ctbl:dest-before-update (c) (when (ctbl:dest-before-update-func c) (funcall (ctbl:dest-before-update-func c)))) (defun ctbl:dest-after-update (c) (when (ctbl:dest-after-update-func c) (funcall (ctbl:dest-after-update-func c)))) ;; Buffer (defconst ctbl:table-buffer-name "*ctbl-table*" "[internal] Default buffer name for the table view.") (defun ctbl:dest-init-buffer (&optional buf width height custom-map) "Create a buffer destination. This destination uses an entire buffer and set up the major-mode `ctbl:table-mode' and the key map `ctbl:table-mode-map'. BUF is a buffer name to render the table view. If BUF is nil, the default buffer name is used. WIDTH and HEIGHT are reference size of the table view. If those are nil, the size of table is calculated from the window that shows BUF or the selected window. The component object is stored at the buffer local variable `ctbl:component'. CUSTOM-MAP is the additional keymap that is added to default keymap `ctbl:table-mode-map'." (lexical-let ((buffer (or buf (get-buffer-create (format "*Table: %d*" (ctbl:uid))))) (window (or (and buf (get-buffer-window buf)) (selected-window))) dest) (setq dest (make-ctbl:dest :type 'buffer :min-func 'point-min :max-func 'point-max :buffer buffer :width width :height height :clear-func (lambda () (with-current-buffer buffer (erase-buffer))))) (with-current-buffer buffer (unless (eq major-mode 'ctbl:table-mode) (ctbl:table-mode custom-map))) dest)) ;; Region (defun ctbl:dest-init-region (buf mark-begin mark-end &optional width height) "Create a region destination. The table is drew between MARK-BEGIN and MARK-END in the buffer BUF. MARK-BEGIN and MARK-END are separated by more than one character, such as a space. This destination is employed to be embedded in the some application buffer. Because this destination does not set up any modes and key maps for the buffer, the application that uses the ctable is responsible to manage the buffer and key maps." (lexical-let ((mark-begin mark-begin) (mark-end mark-end) (window (or (get-buffer-window buf) (selected-window)))) (make-ctbl:dest :type 'region :min-func (lambda () (marker-position mark-begin)) :max-func (lambda () (marker-position mark-end)) :buffer buf :width width :height height :clear-func (lambda () (ctbl:dest-region-clear (marker-position mark-begin) (marker-position mark-end)))))) (defun ctbl:dest-region-clear (begin end) "[internal] Clear the content text." (when (< 2 (- end begin)) (delete-region begin (1- end))) (goto-char begin)) ;; Inline text (defconst ctbl:dest-background-buffer " *ctbl:dest-background*") (defun ctbl:dest-init-inline (width height) "Create a text destination." (lexical-let ((buffer (get-buffer-create ctbl:dest-background-buffer)) (window (selected-window)) dest) (setq dest (make-ctbl:dest :type 'text :min-func 'point-min :max-func 'point-max :buffer buffer :width width :height height :clear-func (lambda () (with-current-buffer buffer (erase-buffer))))) dest)) ;; private functions (defun ctbl:dest-ol-selection-clear (dest) "[internal] Clear the selection overlays on the current table view." (loop for i in (ctbl:dest-select-ol dest) do (delete-overlay i)) (setf (ctbl:dest-select-ol dest) nil)) (defun ctbl:dest-ol-selection-set (dest cell-id) "[internal] Put a selection overlay on CELL-ID. The selection overlay can be put on some cells, calling this function many times. This function does not manage the selections, just put the overlay." (lexical-let (ols (row-id (car cell-id)) (col-id (cdr cell-id))) (ctbl:dest-with-region dest (ctbl:find-all-by-row-id dest row-id (lambda (tcell-id begin end) (let ((overlay (make-overlay begin end))) (overlay-put overlay 'face (if (= (cdr tcell-id) col-id) 'ctbl:face-cell-select 'ctbl:face-row-select)) (push overlay ols))))) (setf (ctbl:dest-select-ol dest) ols))) ;; Component implementation (defun ctbl:cp-new (dest model param) "[internal] Create a new component object. DEST is a ctbl:dest object. MODEL is a model object. PARAM is a rendering parameter object. This function is called by the initialization functions, `ctbl:create-table-component-buffer', `ctbl:create-table-component-region' and `ctbl:get-table-text'." (let ((cp (make-ctbl:component :selected '(0 . 0) :dest dest :model model :param (or param ctbl:default-rendering-param)))) (ctbl:cp-update cp) cp)) (defun ctbl:cp-get-component () "Return the component object on the current cursor position. Firstly, getting a text property `ctbl:component' on the current position. If no object is found in the text property, the buffer local variable `ctbl:component' is tried to get. If no object is found at the variable, return nil." (let ((component (get-text-property (point) 'ctbl:component))) (unless component (unless (local-variable-p 'ctbl:component (current-buffer)) (error "Not found ctbl:component attribute...")) (setq component (buffer-local-value 'ctbl:component (current-buffer)))) component)) ;; Component : getters (defun ctbl:cp-get-selected (component) "Return the selected cell-id of the component." (ctbl:component-selected component)) (defun ctbl:cp-get-selected-data-row (component) "Return the selected row data. If no cell is selected, return nil." (let* ((rows (ctbl:component-sorted-data component)) (cell-id (ctbl:component-selected component)) (row-id (car cell-id)) (col-id (cdr cell-id))) (if row-id (nth row-id rows) nil))) (defun ctbl:cp-get-selected-data-cell (component) "Return the selected cell data. If no cell is selected, return nil." (let* ((rows (ctbl:component-sorted-data component)) (cell-id (ctbl:component-selected component)) (row-id (car cell-id)) (col-id (cdr cell-id))) (if row-id (nth col-id (nth row-id rows)) nil))) (defun ctbl:cp-get-model (component) "Return the model object." (ctbl:component-model component)) (defun ctbl:cp-set-model (component model) "Replace the model object and update the destination." (setf (ctbl:component-model component) model) (ctbl:cp-update component)) (defun ctbl:cp-get-param (component) "Return a rendering parameter object." (ctbl:component-param component)) (defun ctbl:cp-get-buffer (component) "Return a buffer object on which the component draws the content." (ctbl:dest-buffer (ctbl:component-dest component))) ;; Component : setters (defun ctbl:cp-move-cursor (dest cell-id) "[internal] Just move the cursor onto the CELL-ID. If CELL-ID is not found, return nil. This function is called by `ctbl:cp-set-selected-cell'." (let ((pos (ctbl:find-by-cell-id dest cell-id))) (cond (pos (goto-char pos) (unless (eql (selected-window) (get-buffer-window (current-buffer))) (set-window-point (get-buffer-window (current-buffer)) pos)) t) (t nil)))) (defun ctbl:cp-set-selected-cell (component cell-id) "Select the cell on the component. If the current view doesn't contain the cell, this function updates the view to display the cell." (let ((last (ctbl:component-selected component)) (dest (ctbl:component-dest component)) (model (ctbl:component-model component))) (when (ctbl:cp-move-cursor dest cell-id) (setf (ctbl:component-selected component) cell-id) (ctbl:dest-before-update dest) (ctbl:dest-ol-selection-clear dest) (ctbl:dest-ol-selection-set dest cell-id) (ctbl:dest-after-update dest) (unless (equal last cell-id) (ctbl:cp-fire-selection-change-hooks component))))) ;; Hook (defun ctbl:cp-add-update-hook (component hook) "Add the update hook function to the component. HOOK is a function that has no argument." (push hook (ctbl:component-update-hooks component))) (defun ctbl:cp-add-selection-change-hook (component hook) "Add the selection change hook function to the component. HOOK is a function that has no argument." (push hook (ctbl:component-selection-change-hooks component))) (defun ctbl:cp-add-click-hook (component hook) "Add the click hook function to the component. HOOK is a function that has no argument." (push hook (ctbl:component-click-hooks component))) ;; update (defun ctbl:cp-update (component) "Clear and re-draw the component content." (let* ((buf (ctbl:cp-get-buffer component)) (dest (ctbl:component-dest component))) (with-current-buffer buf (ctbl:dest-before-update dest) (ctbl:dest-ol-selection-clear dest) (let (buffer-read-only) (ctbl:dest-with-region dest (ctbl:dest-clear dest) (cond ;; asynchronous model ((ctbl:async-model-p (ctbl:model-data (ctbl:component-model component))) (lexical-let ((cp component)) (ctbl:async-state-on-update cp) (ctbl:render-async-main dest (ctbl:component-model component) (ctbl:component-param component) (lambda (rows &optional astate) (setf (ctbl:component-sorted-data cp) rows) (when astate (ctbl:cp-states-set cp 'async-state astate)))))) ;; synchronous model (t (setf (ctbl:component-sorted-data component) (ctbl:render-main dest (ctbl:component-model component) (ctbl:component-param component))))))) (ctbl:cp-set-selected-cell component (ctbl:component-selected component)) (ctbl:dest-after-update dest) (ctbl:cp-fire-update-hooks component)))) ;; Component : privates (defun ctbl:cp-states-get (component key) "[internal] Get a value from COMPONENT with KEY." (cdr (assq key (ctbl:component-states component)))) (defun ctbl:cp-states-set (component key value) "[internal] Set a value with KEY." (let ((pair (assq key (ctbl:component-states component)))) (cond ((null pair) (push (cons key value) (ctbl:component-states component))) (t (setf (cdr pair) value))))) (defun ctbl:cp-fire-click-hooks (component) "[internal] Call click hook functions of the component with no arguments." (loop for f in (ctbl:component-click-hooks component) do (condition-case err (funcall f) (error (message "CTable: Click / Hook error %S [%s]" f err))))) (defun ctbl:cp-fire-selection-change-hooks (component) "[internal] Call selection change hook functions of the component with no arguments." (loop for f in (ctbl:component-selection-change-hooks component) do (condition-case err (funcall f) (error (message "CTable: Selection change / Hook error %S [%s]" f err))))) (defun ctbl:cp-fire-update-hooks (component) "[internal] Call update hook functions of the component with no arguments." (loop for f in (ctbl:component-update-hooks component) do (condition-case err (funcall f) (error (message "Ctable: Update / Hook error %S [%s]" f err))))) (defun ctbl:find-position-fast (dest cell-id) "[internal] Find the cell-id position using bi-section search." (let* ((row-id (car cell-id)) (row-id-lim (max (- row-id 10) 0)) (min (ctbl:dest-point-min dest)) (max (ctbl:dest-point-max dest)) (mid (/ (+ min max) 2))) (save-excursion (loop for next = (next-single-property-change mid 'ctbl:cell-id nil max) for cur-row-id = (and next (car (ctbl:cursor-to-cell next))) do (cond ((>= next max) (return (point))) ((null cur-row-id) (setq mid next)) ((= cur-row-id row-id) (goto-char mid) (beginning-of-line) (return (point))) ((and (< row-id-lim cur-row-id) (< cur-row-id row-id)) (goto-char mid) (beginning-of-line) (forward-line) (return (point))) ((< cur-row-id row-id) (setq min mid) (setq mid (/ (+ min max) 2))) ((< row-id cur-row-id) (setq max mid) (setq mid (/ (+ min max) 2)))))))) (defun ctbl:find-by-cell-id (dest cell-id) "[internal] Return a point where the text property `ctbl:cell-id' is equal to cell-id in the current table view. If CELL-ID is not found in the current view, return nil." (loop with pos = (ctbl:find-position-fast dest cell-id) with end = (ctbl:dest-point-max dest) for next = (next-single-property-change pos 'ctbl:cell-id nil end) for text-cell = (and next (ctbl:cursor-to-cell next)) while (and next (< next end)) do (if (and text-cell (equal cell-id text-cell)) (return next)) (setq pos next))) (defun ctbl:find-all-by-cell-id (dest cell-id func) "[internal] Call the function FUNC in each regions where the text-property `ctbl:cell-id' is equal to CELL-ID. The argument function FUNC receives two arguments, begin position and end one. This function is mainly used at functions for putting overlays." (loop with pos = (ctbl:find-position-fast dest cell-id) with end = (ctbl:dest-point-max dest) for next = (next-single-property-change pos 'ctbl:cell-id nil end) for text-id = (and next (ctbl:cursor-to-cell next)) while (and next (< next end)) do (if (and text-id (equal cell-id text-id)) (let ((cend (next-single-property-change next 'ctbl:cell-id nil end))) (return (funcall func next cend)))) (setq pos next))) (defun ctbl:find-all-by-row-id (dest row-id func) "[internal] Call the function FUNC in each regions where the row-id of the text-property `ctbl:cell-id' is equal to ROW-ID. The argument function FUNC receives three arguments, cell-id, begin position and end one. This function is mainly used at functions for putting overlays." (loop with pos = (ctbl:find-position-fast dest (cons row-id nil)) with end = (ctbl:dest-point-max dest) for next = (next-single-property-change pos 'ctbl:cell-id nil end) for text-id = (and next (ctbl:cursor-to-cell next)) while (and next (< next end)) do (when text-id (cond ((equal row-id (car text-id)) (let ((cend (next-single-property-change next 'ctbl:cell-id nil end))) (funcall func text-id next cend))) ((< row-id (car text-id)) (return nil)))) (setq pos next))) (defun ctbl:find-first-cell (dest) "[internal] Return the first cell in the current buffer." (let ((pos (next-single-property-change (ctbl:dest-point-min dest) 'ctbl:cell-id))) (and pos (ctbl:cursor-to-cell pos)))) (defun ctbl:find-last-cell (dest) "[internal] Return the last cell in the current buffer." (let ((pos (previous-single-property-change (ctbl:dest-point-max dest) 'ctbl:cell-id))) (and pos (ctbl:cursor-to-cell (1- pos))))) (defun ctbl:cursor-to-cell (&optional pos) "[internal] Return the cell-id at the cursor. If the text does not have the text-property `ctbl:cell-id', return nil." (get-text-property (or pos (point)) 'ctbl:cell-id)) (defun ctbl:cursor-to-nearest-cell () "Return the cell-id at the cursor. If the point of cursor does not have the cell-id, search the cell-id around the cursor position. If the current buffer is not table view (it may be bug), this function may return nil." (or (ctbl:cursor-to-cell) (let* ((r (lambda () (when (not (eolp)) (forward-char)))) (l (lambda () (when (not (bolp)) (backward-char)))) (u (lambda () (when (not (bobp)) (line-move 1)))) (d (lambda () (when (not (eobp)) (line-move -1)))) (dest (ctbl:component-dest (ctbl:cp-get-component))) get) (setq get (lambda (cmds) (save-excursion (if (null cmds) (ctbl:cursor-to-cell) (ignore-errors (funcall (car cmds)) (funcall get (cdr cmds))))))) (or (loop for i in `((,d) (,r) (,u) (,l) (,d ,r) (,d ,l) (,u ,r) (,u ,l) (,d ,d) (,r ,r) (,u ,u) (,l ,l)) for id = (funcall get i) if id return id) (cond ((> (/ (point-max) 2) (point)) (ctbl:find-first-cell dest)) (t (ctbl:find-last-cell dest))))))) ;; Commands (defun ctbl:navi-move-gen (drow dcol) "[internal] Move to the cell with the abstract position." (let* ((cp (ctbl:cp-get-component)) (cell-id (ctbl:cursor-to-nearest-cell)) (row-id (car cell-id)) (col-id (cdr cell-id))) (when (and cp cell-id) (ctbl:navi-goto-cell (ctbl:cell-id (+ drow row-id) (+ dcol col-id)))))) (defun ctbl:navi-move-up (&optional num) "Move to the up neighbor cell." (interactive "p") (unless num (setq num 1)) (ctbl:navi-move-gen (- num) 0)) (defun ctbl:navi-move-down (&optional num) "Move to the down neighbor cell." (interactive "p") (unless num (setq num 1)) (ctbl:navi-move-gen num 0)) (defun ctbl:navi-move-right (&optional num) "Move to the right neighbor cell." (interactive "p") (unless num (setq num 1)) (ctbl:navi-move-gen 0 num)) (defun ctbl:navi-move-left (&optional num) "Move to the left neighbor cell." (interactive "p") (unless num (setq num 1)) (ctbl:navi-move-gen 0 (- num))) (defun ctbl:navi-move-left-most () "Move to the left most cell." (interactive) (let* ((cp (ctbl:cp-get-component)) (cell-id (ctbl:cursor-to-nearest-cell)) (row-id (car cell-id))) (when (and cp cell-id) (ctbl:navi-goto-cell (ctbl:cell-id row-id 0))))) (defun ctbl:navi-move-right-most () "Move to the right most cell." (interactive) (let* ((cp (ctbl:cp-get-component)) (cell-id (ctbl:cursor-to-nearest-cell)) (row-id (car cell-id)) (model (ctbl:cp-get-model cp)) (cols (ctbl:model-column-length model))) (when (and cp cell-id) (ctbl:navi-goto-cell (ctbl:cell-id row-id (1- cols)))))) (defun ctbl:navi-goto-cell (cell-id) "Move the cursor to CELL-ID and put selection." (let ((cp (ctbl:cp-get-component))) (when cp (ctbl:cp-set-selected-cell cp cell-id)))) (defun ctbl:navi-on-click () "Action handler on the cells." (interactive) (let ((cp (ctbl:cp-get-component)) (cell-id (ctbl:cursor-to-nearest-cell))) (when (and cp cell-id) (ctbl:cp-set-selected-cell cp cell-id) (ctbl:cp-fire-click-hooks cp)))) (defun ctbl:navi-jump-to-column () "Jump to a specified column of the current row." (interactive) (let* ((cp (ctbl:cp-get-component)) (cell-id (ctbl:cursor-to-nearest-cell)) (row-id (car cell-id)) (model (ctbl:cp-get-model cp)) (cols (ctbl:model-column-length model)) (col-names (mapcar 'ctbl:cmodel-title (ctbl:model-column-model model))) (completion-ignore-case t) (col-name (funcall ctbl:completing-read "Column name: " col-names))) (when (and cp cell-id) (ctbl:navi-goto-cell (ctbl:cell-id row-id (position col-name col-names :test 'equal)))))) (defun ctbl:action-update-buffer () "Update action for the latest table model." (interactive) (let ((cp (ctbl:cp-get-component))) (when cp (ctbl:cp-update cp)))) (defun ctbl:action-column-header () "Action handler on the header columns. (for normal key events)" (interactive) (ctbl:fire-column-header-action (ctbl:cp-get-component) (get-text-property (point) 'ctbl:col-id))) (defun ctbl:fire-column-header-action (cp col-id) "[internal] Execute action handlers on the header columns." (when (and cp col-id) (loop with cmodel = (nth col-id (ctbl:model-column-model (ctbl:cp-get-model cp))) for f in (ctbl:cmodel-click-hooks cmodel) do (condition-case err (funcall f cp col-id) (error (message "Ctable: Header Click / Hook error %S [%s]" f err)))))) (defun ctbl:render-column-header-keymap (col-id) "[internal] Generate action handler on the header columns. (for header-line-format)" (lexical-let ((col-id col-id)) (let ((keymap (copy-keymap ctbl:column-header-keymap))) (define-key keymap [header-line mouse-1] (lambda () (interactive) (ctbl:fire-column-header-action (ctbl:cp-get-component) col-id))) keymap))) (defvar ctbl:column-header-keymap (ctbl:define-keymap '(([mouse-1] . ctbl:action-column-header) ("C-m" . ctbl:action-column-header) ("RET" . ctbl:action-column-header) )) "Keymap for the header columns.") (defvar ctbl:table-mode-map (ctbl:define-keymap '( ("k" . ctbl:navi-move-up) ("j" . ctbl:navi-move-down) ("h" . ctbl:navi-move-left) ("l" . ctbl:navi-move-right) ("p" . ctbl:navi-move-up) ("n" . ctbl:navi-move-down) ("b" . ctbl:navi-move-left) ("f" . ctbl:navi-move-right) ("c" . ctbl:navi-jump-to-column) ("e" . ctbl:navi-move-right-most) ("a" . ctbl:navi-move-left-most) ("g" . ctbl:action-update-buffer) ([mouse-1] . ctbl:navi-on-click) ("C-m" . ctbl:navi-on-click) ("RET" . ctbl:navi-on-click) )) "Keymap for the table-mode buffer.") (defun ctbl:table-mode-map (&optional custom-map) "[internal] Return a keymap object for the table buffer." (cond (custom-map (set-keymap-parent custom-map ctbl:table-mode-map) custom-map) (t ctbl:table-mode-map))) (defvar ctbl:table-mode-hook nil "This hook is called at end of setting up major mode `ctbl:table-mode'.") (defun ctbl:table-mode (&optional custom-map) "Set up major mode `ctbl:table-mode'. \\{ctbl:table-mode-map}" (kill-all-local-variables) (setq truncate-lines t) (use-local-map (ctbl:table-mode-map custom-map)) (setq major-mode 'ctbl:table-mode mode-name "Table Mode") (setq buffer-undo-list t buffer-read-only t) (add-hook 'post-command-hook 'ctbl:start-tooltip-timer nil t) (run-hooks 'ctbl:table-mode-hook)) ;; Rendering (defun ctbl:render-check-cell-width (rows cmodels column-widths) "[internal] Return a list of rows. This function makes side effects: cell widths are stored at COLUMN-WIDTHS, longer cell strings are truncated by maximum width of the column models." (loop for row in rows collect (loop for c in row for cm in cmodels for cwmax = (ctbl:cmodel-max-width cm) for i from 0 for cw = (nth i column-widths) for val = (format "%s" c) collect (progn (when (and cwmax (< cwmax (string-width val))) (setq val (truncate-string-to-width val cwmax))) (when (< cw (string-width val)) (setf (nth i column-widths) (string-width val))) val)))) (defun ctbl:render-adjust-cell-width (cmodels column-widths total-width) "[internal] Adjust column widths and return a list of column widths. If TOTAL-WIDTH is nil, this function just returns COLUMN-WIDTHS. If TOTAL-WIDTHS is shorter than sum of COLUMN-WIDTHS, this function expands columns. The residual width is distributed over the columns. If TOTAL-WIDTHS is longer than sum of COLUMN-WIDTHS, this function shrinks columns to reduce the surplus width." (let ((init-total (loop for i in column-widths sum i))) (cond ((or (null total-width) (= total-width init-total)) column-widths) ((< total-width init-total) (ctbl:render-adjust-cell-width-shrink cmodels column-widths total-width init-total)) (t (ctbl:render-adjust-cell-width-expand cmodels column-widths total-width init-total))))) (defun ctbl:render-adjust-cell-width-shrink (cmodels column-widths total-width init-total ) "[internal] shrink column widths." (let* ((column-widths (copy-sequence column-widths)) (column-indexes (loop for i from 0 below (length cmodels) collect i)) (residual (- init-total total-width))) (loop for cnum = (length column-indexes) until (or (= 0 cnum) (= 0 residual)) do (loop with ave-shrink = (max 1 (/ residual cnum)) for idx in column-indexes for cmodel = (nth idx cmodels) for cwidth = (nth idx column-widths) for min-width = (or (ctbl:cmodel-min-width cmodel) 1) do (cond ((<= residual 0) (return)) ; complete ((<= cwidth min-width) ; reject (setq column-indexes (delete idx column-indexes))) (t ; reduce (let ((next-width (max 1 (- cwidth ave-shrink)))) (incf residual (- next-width cwidth)) (setf (nth idx column-widths) next-width)))))) column-widths)) (defun ctbl:render-adjust-cell-width-expand (cmodels column-widths total-width init-total ) "[internal] expand column widths." (let* ((column-widths (copy-sequence column-widths)) (column-indexes (loop for i from 0 below (length cmodels) collect i)) (residual (- total-width init-total))) (loop for cnum = (length column-indexes) until (or (= 0 cnum) (= 0 residual)) do (loop with ave-expand = (max 1 (/ residual cnum)) for idx in column-indexes for cmodel = (nth idx cmodels) for cwidth = (nth idx column-widths) for max-width = (or (ctbl:cmodel-max-width cmodel) total-width) do (cond ((<= residual 0) (return)) ; complete ((<= max-width cwidth) ; reject (setq column-indexes (delete idx column-indexes))) (t ; expand (let ((next-width (min max-width (+ cwidth ave-expand)))) (incf residual (- cwidth next-width)) (setf (nth idx column-widths) next-width)))))) column-widths)) (defun ctbl:render-get-formats (cmodels column-widths) "[internal] Return a list of the format functions." (loop for cw in column-widths for cm in cmodels for al = (ctbl:cmodel-align cm) collect (lexical-let ((cw cw)) (cond ((eq al 'left) (lambda (s) (ctbl:format-left cw s))) ((eq al 'center) (lambda (s) (ctbl:format-center cw s))) (t (lambda (s) (ctbl:format-right cw s))))))) (defun ctbl:render-choose-color (model param index) "[internal] Choose rendering color." (cond ((null param) nil) ((stringp param) param) ((functionp param) (funcall param model index)) (t (let ((val (or (assq index param) (assq t param)))) (if val (cdr val) nil))))) (defun ctbl:render-bg-color (str row-id col-id model param) "[internal] Return nil or the color string at the cell (row-id . cell-id)." (let ((bgc-param (ctbl:param-bg-colors param))) (cond ((null bgc-param) nil) ((functionp bgc-param) (funcall bgc-param model row-id col-id str)) (t (let ((pair (or (assoc (cons row-id col-id) bgc-param) (assoc t bgc-param)))) (if pair (cdr pair) nil)))))) (defun ctbl:render-bg-color-put (str row-id col-id model param) "[internal] Return the string with the background face." (let ((bgcolor (ctbl:render-bg-color str row-id col-id model param))) (if bgcolor (let ((org-face (get-text-property 0 'face str))) (propertize (copy-sequence str) 'face (if org-face (append org-face (list ':background bgcolor)) (list ':background bgcolor)))) str))) (defun ctbl:render-line-color (str model param index) "[internal] Return the propertize string." (propertize (copy-sequence str) 'face (list ':foreground (ctbl:render-choose-color model param index)))) (defun ctbl:render-vline-color (str model param index) "[internal] Return the propertize string for vertical lines." (ctbl:render-line-color str model (ctbl:param-vline-colors param) index)) (defun ctbl:render-hline-color (str model param index) "[internal] Return the propertize string for horizontal lines." (ctbl:render-line-color str model (ctbl:param-hline-colors param) index)) (defun ctbl:render-draw-vline-p (model param index) "[internal] If a vertical line is needed at the column index, return t." (cond ((null param) nil) ((eq 'all param) t) ((functionp param) (funcall param model index)) (t (and (consp param) (memq index param))))) (defun ctbl:render-draw-hline-p (model param index) "[internal] If a horizontal line is needed at the row index, return t." (cond ((null param) nil) ((eq 'all param) t) ((functionp param) (funcall param model index)) (t (memq index param)))) (defun ctbl:render-make-hline (column-widths model param index) "[internal] " (let ((vparam (ctbl:param-draw-vlines param)) (hline (ctbl:param-horizontal-line param)) left joint right) (if (not (ctbl:render-draw-hline-p model (ctbl:param-draw-hlines param) index)) "" (cond ((eq 0 index) (setq left (char-to-string (ctbl:param-left-top-corner param)) joint (char-to-string (ctbl:param-top-junction param)) right (char-to-string (ctbl:param-right-top-corner param)))) ((eq -1 index) (setq left (char-to-string (ctbl:param-left-bottom-corner param)) joint (char-to-string (ctbl:param-bottom-junction param)) right (char-to-string (ctbl:param-right-bottom-corner param)))) (t (setq left (char-to-string (ctbl:param-left-junction param)) joint (char-to-string (ctbl:param-cross-junction param)) right (char-to-string (ctbl:param-right-junction param))))) (ctbl:render-hline-color (concat (if (ctbl:render-draw-vline-p model vparam 0) left) (loop with ret = nil with endi = (length column-widths) for cw in column-widths for ci from 1 for endp = (equal ci endi) do (push (make-string cw hline) ret) (when (and (ctbl:render-draw-vline-p model vparam ci) (not endp)) (push joint ret)) finally return (apply 'concat (reverse ret))) (if (ctbl:render-draw-vline-p model vparam -1) right) "\n") model param index)))) (defun ctbl:render-join-columns (columns model param) "[internal] Join a list of column strings with vertical lines." (let (ret (V (char-to-string (ctbl:param-vertical-line param)))) ;; left border line (setq ret (if (ctbl:render-draw-vline-p model (ctbl:param-draw-vlines param) 0) (list (ctbl:render-vline-color V model param 0)) nil)) ;; content line (loop with param-vl = (ctbl:param-draw-vlines param) with param-vc = (ctbl:param-vline-colors param) with endi = (length columns) for i from 1 for endp = (equal i endi) for cv in columns for color = (ctbl:render-choose-color model param-vc i) do (push cv ret) (when (and (ctbl:render-draw-vline-p model (ctbl:param-draw-vlines param) i) (not endp)) (push (ctbl:render-vline-color V model param i) ret))) ;; right border line (when (ctbl:render-draw-vline-p model (ctbl:param-draw-vlines param) -1) (push (ctbl:render-vline-color V model param -1) ret)) ;; join them (mapconcat 'identity (reverse ret) ""))) (defun ctbl:render-sum-vline-widths (cmodels model param) "[internal] Return a sum of the widths of vertical lines." (let ((sum 0)) ;; left border line (when (ctbl:render-draw-vline-p model (ctbl:param-draw-vlines param) 0) (incf sum)) ;; content line (loop with param-vl = (ctbl:param-draw-vlines param) with endi = (length cmodels) for i from 1 upto (length cmodels) for endp = (equal i endi) do (when (and (ctbl:render-draw-vline-p model (ctbl:param-draw-vlines param) i) (not endp)) (incf sum))) ;; right border line (when (ctbl:render-draw-vline-p model (ctbl:param-draw-vlines param) -1) (incf sum)) sum)) (defun ctbl:dest-width-get (dest) "[internal] Return the column number to draw the table view. Return nil, if the width is not given. Then, the renderer draws freely." (let ((dwidth (ctbl:dest-width dest)) (dwin (get-buffer-window))) (cond ((numberp dwidth) dwidth) ((eq 'full dwidth) (window-width dwin)) (t nil)))) (defun ctbl:dest-height-get (dest) "[internal] Return the row number to draw the table view. Return nil, if the height is not given. Then, the renderer draws freely." (let ((dheight (ctbl:dest-height dest)) (dwin (get-buffer-window))) (cond ((numberp dheight) dheight) ((eq 'full dheight) (1- (window-height dwin))) (t nil)))) (defun ctbl:render-main (dest model param) "[internal] Rendering the table view. This function assumes that the current buffer is the destination buffer." (let* ((EOL "\n") drows (cmodels (ctbl:model-column-model model)) (rows (ctbl:sort (copy-sequence (ctbl:model-data model)) cmodels (ctbl:model-sort-state model))) (column-widths (loop for c in cmodels for title = (ctbl:cmodel-title c) collect (max (or (ctbl:cmodel-min-width c) 0) (or (and title (length title)) 0)))) column-formats) ;; check cell widths (setq drows (ctbl:render-check-cell-width rows cmodels column-widths)) ;; adjust cell widths for ctbl:dest width (when (ctbl:dest-width-get dest) (setq column-widths (ctbl:render-adjust-cell-width cmodels column-widths (- (ctbl:dest-width-get dest) (ctbl:render-sum-vline-widths cmodels model param))))) (setq column-formats (ctbl:render-get-formats cmodels column-widths)) (catch 'ctbl:insert-break (when (ctbl:param-display-header param) (ctbl:render-main-header dest model param cmodels column-widths)) (ctbl:render-main-content dest model param cmodels drows column-widths column-formats)) ;; return the sorted list rows)) (defun ctbl:render-main-header (dest model param cmodels column-widths) "[internal] Render the table header." (let ((EOL "\n") (header-string (ctbl:render-join-columns (loop for cm in cmodels for i from 0 for cw in column-widths collect (propertize (ctbl:format-center cw (ctbl:cmodel-title cm)) 'ctbl:col-id i 'local-map (ctbl:render-column-header-keymap i) 'mouse-face 'highlight)) model param))) (cond ((and (eq 'buffer (ctbl:dest-type dest)) (ctbl:param-fixed-header param)) ;; buffer header-line (let* ((fcol (/ (car (window-fringes)) (frame-char-width))) (header-text (concat (make-string fcol ? ) header-string))) (setq header-line-format header-text) ;; save header-text for hscroll updating (set (make-local-variable 'ctbl:header-text) header-text))) (t ;; content area (insert ; border line (ctbl:render-make-hline column-widths model param 0)) (insert header-string EOL) ; header columns )))) (defun ctbl:render-main-content (dest model param cmodels rows column-widths column-formats &optional begin-index) "[internal] Render the table content." (unless begin-index (setq begin-index 0)) (let ((EOL "\n") (row-num (length rows))) (loop for cols in rows for row-index from begin-index do (insert (ctbl:render-make-hline column-widths model param (1+ row-index))) (insert (ctbl:render-join-columns (loop for i in cols for s = (if (stringp i) i (format "%s" i)) for fmt in column-formats for cw in column-widths for col-index from 0 for str = (ctbl:render-bg-color-put (funcall fmt s) row-index col-index model param) collect (propertize str 'ctbl:cell-id (cons row-index col-index) 'ctbl:cell-width cw)) model param) EOL)) ;; bottom border line (insert (ctbl:render-make-hline column-widths model param -1)))) ;; async data model (defvar ctbl:continue-button-keymap (ctbl:define-keymap '(([mouse-1] . ctbl:action-continue-async-clicked) ("C-m" . ctbl:action-continue-async-clicked) ("RET" . ctbl:action-continue-async-clicked) )) "Keymap for the continue button.") ;; async data / internal state (defstruct ctbl:async-state "Rendering State [internal] status : symbol -> normal : data still remains. this is the start state. requested : requested data and waiting for response. done : no data remains. this is the final state. actual-width : actual width column-widths : width of each columns column-formats : format of each columns next-index : row index number for next request panel-begin : begin mark object for status panel panel-end : end mark object for status panel " status actual-width column-widths column-formats next-index panel-begin panel-end) (defun ctbl:async-state-on-update (component) "[internal] Reset async data model." (let* ((cp component) (amodel (ctbl:model-data (ctbl:cp-get-model cp))) (astate (ctbl:cp-states-get cp 'async-state))) (when (and astate (ctbl:async-model-reset amodel)) (funcall (ctbl:async-model-reset amodel))))) (defun ctbl:async-state-on-click-panel (component) "[internal] This function is called when the user clicks the status panel." (let* ((cp component) (amodel (ctbl:model-data (ctbl:cp-get-model cp))) (astate (ctbl:cp-states-get cp 'async-state))) (when cp (case (ctbl:async-state-status astate) ('normal (ctbl:render-async-continue cp)) ('requested (when (ctbl:async-model-cancel amodel) (funcall (ctbl:async-model-cancel amodel)) (ctbl:async-state-update-status (ctbl:component-dest cp) 'normal))))))) (defun ctbl:async-state-update-status (component next-status) "[internal] Update internal status of async-state and update the status panel." (let* ((cp component) (dest (ctbl:component-dest cp)) (amodel (ctbl:model-data (ctbl:cp-get-model cp))) (astate (ctbl:cp-states-get cp 'async-state))) (with-current-buffer (ctbl:dest-buffer dest) (setf (ctbl:async-state-status astate) next-status) (ctbl:async-state-update-status-panel dest astate amodel)))) (defun ctbl:async-state-update-status-panel (dest astate amodel) "[internal] Rendering data model status panel with current state." (let ((begin (ctbl:async-state-panel-begin astate)) (end (ctbl:async-state-panel-end astate)) (width (ctbl:async-state-actual-width astate))) (save-excursion (let (buffer-read-only) (when (< 2 (- end begin)) (delete-region begin (1- end))) (goto-char begin) (insert (propertize (case (ctbl:async-state-status astate) ('done (ctbl:format-center width "No more data.")) ('requested (cond ((ctbl:async-model-cancel amodel) (ctbl:format-center width "(Waiting for data. [Click to Cancel])")) (t (ctbl:format-center width "(Waiting for data...)")))) ('normal (ctbl:format-center width "[Click to retrieve more data.]")) (t (ctbl:format-center width (format "(Error : %s)" (ctbl:async-state-status astate))))) 'keymap ctbl:continue-button-keymap 'face 'ctbl:face-continue-bar 'mouse-face 'highlight) "\n"))))) (defun ctbl:async-state-on-post-command-hook (component) "[internal] Try auto requesting for asynchronous data." (let* ((astate (ctbl:cp-states-get component 'async-state)) (panel-begin-pos (marker-position (ctbl:async-state-panel-begin astate)))) (when (and (eq 'normal (ctbl:async-state-status astate)) (< panel-begin-pos (window-end))) (ctbl:action-continue-async-clicked)))) ;; rendering async data (defun ctbl:render-async-main (dest model param rows-setter) "[internal] Rendering the table view for async data model. This function assumes that the current buffer is the destination buffer." (lexical-let* ((dest dest) (model model) (param param) (rows-setter rows-setter) (amodel (ctbl:model-data model)) (buf (current-buffer)) (cmodels (ctbl:model-column-model model))) (funcall (ctbl:async-model-request amodel) 0 (ctbl:async-model-init-num amodel) (lambda (rows) ; >> request succeeded (with-current-buffer buf (let (buffer-read-only drows column-formats (column-widths (loop for c in cmodels for title = (ctbl:cmodel-title c) collect (max (or (ctbl:cmodel-min-width c) 0) (or (and title (length title)) 0)))) (EOL "\n")) ;; check cell widths (setq drows (ctbl:render-check-cell-width rows cmodels column-widths)) ;; adjust cell widths for ctbl:dest width (when (ctbl:dest-width-get dest) (setq column-widths (ctbl:render-adjust-cell-width cmodels column-widths (- (ctbl:dest-width-get dest) (ctbl:render-sum-vline-widths cmodels model param))))) (setq column-formats (ctbl:render-get-formats cmodels column-widths)) (ctbl:render-main-header dest model param cmodels column-widths) (ctbl:render-main-content dest model param cmodels drows column-widths column-formats) (add-hook 'post-command-hook 'ctbl:post-command-hook-for-auto-request t t) (let (mark-panel-begin mark-panel-end astate) (setq mark-panel-begin (point-marker)) (insert "\n") (setq mark-panel-end (point-marker)) (setq astate (make-ctbl:async-state :status 'normal :actual-width (+ (ctbl:render-sum-vline-widths cmodels model param) (loop for i in column-widths sum i)) :column-widths column-widths :column-formats column-formats :next-index (length rows) :panel-begin mark-panel-begin :panel-end mark-panel-end)) (ctbl:async-state-update-status-panel dest astate amodel) (funcall rows-setter rows astate)) (goto-char (ctbl:dest-point-min dest))))) (lambda (errsym) ; >> request failed (message "ctable : error -> %S" errsym))))) (defun ctbl:render-async-continue (component) "[internal] Rendering subsequent data asynchronously." (lexical-let* ((cp component) (dest (ctbl:component-dest cp)) (buf (current-buffer)) (model (ctbl:cp-get-model cp)) (amodel (ctbl:model-data model)) (astate (ctbl:cp-states-get cp 'async-state)) (begin-index (ctbl:async-state-next-index astate))) ;; status update (ctbl:async-state-update-status cp 'requested) (condition-case err (funcall ; request async data (ctbl:async-model-request amodel) begin-index (ctbl:async-model-more-num amodel) (lambda (rows) ; >> request succeeded (with-current-buffer buf (save-excursion (let (buffer-read-only) (cond ((null rows) ;; no more data (ctbl:async-state-update-status cp 'done)) (t ;; continue data (goto-char (1- (marker-position (ctbl:async-state-panel-begin astate)))) (insert "\n") (ctbl:render-main-content dest model (ctbl:cp-get-param cp) (ctbl:model-column-model model) rows (ctbl:async-state-column-widths astate) (ctbl:async-state-column-formats astate) begin-index) (delete-backward-char 1) (ctbl:async-state-update-status cp 'normal) ;; append row data (side effect!) (setf (ctbl:component-sorted-data cp) (append (ctbl:component-sorted-data cp) rows)) (setf (ctbl:async-state-next-index astate) (+ (length rows) begin-index)))))))) (lambda (errsym) ; >> request failed (ctbl:async-state-update-status cp errsym))) (error ; >> request synchronously failed (ctbl:async-state-update-status cp (cadr err)) (message "ctable : error -> %S" err))))) ;; async data actions (defun ctbl:action-continue-async-clicked () "Action for clicking the continue button." (interactive) (let ((cp (ctbl:cp-get-component))) (when cp (ctbl:async-state-on-click-panel cp)))) (defun ctbl:post-command-hook-for-auto-request () "[internal] This hook watches the buffer position of displayed window to urge async data model to request next data chunk." (let ((cp (ctbl:cp-get-component))) (when (and cp (not (window-minibuffer-p))) (ctbl:async-state-on-post-command-hook cp)))) (defun ctbl:async-model-wrapper (rows &optional init-num more-num) "This function wraps a list of row data in an asynchronous data model so as to avoid Emacs freezing with a large number of rows." (lexical-let ((rows rows) (rest-rows rows) (init-num (or init-num 100)) (more-num (or more-num 100))) (make-ctbl:async-model :request (lambda (row-num len responsef errorf &rest ignored) (funcall responsef (cond ((null rest-rows) nil) (t (nreverse (loop with pos = rest-rows with ret = nil for i from 0 below len do (push (car pos) ret) (setq pos (cdr pos)) (unless pos (return ret)) finally return ret))))) (when rest-rows (setq rest-rows (nthcdr len rest-rows)))) :reset (lambda (&rest ignored) (setq rest-rows rows)) :init-num init-num :more-num more-num))) ;; tooltip (defun ctbl:pop-tooltip (string) "[internal] Show STRING in tooltip." (cond ((and (memq 'pos-tip ctbl:tooltip-method) window-system (featurep 'pos-tip)) (pos-tip-show (ctbl:string-fill-paragraph string) 'popup-tip-face nil nil 0)) ((and (memq 'popup ctbl:tooltip-method) (featurep 'popup)) (popup-tip string)) ((memq 'minibuffer ctbl:tooltip-method) (let ((message-log-max nil)) (message string))))) (defun ctbl:show-cell-in-tooltip (&optional unless-visible) "Show cell at point in tooltip. When UNLESS-VISIBLE is non-nil, show tooltip only when data in cell is truncated." (interactive) (let* ((cp (ctbl:cp-get-component)) (data (when cp (ctbl:cp-get-selected-data-cell cp)))) (when data (let ((string (if (stringp data) data (format "%S" data))) (width (get-text-property (point) 'ctbl:cell-width))) (when (or (not unless-visible) (and (integerp width) (>= (length string) width))) (ctbl:pop-tooltip string)))))) (defvar ctbl:tooltip-delay 1) (defvar ctbl:tooltip-timer nil) (defun ctbl:start-tooltip-timer () (unless ctbl:tooltip-timer (setq ctbl:tooltip-timer (run-with-idle-timer ctbl:tooltip-delay nil (lambda () (ctbl:show-cell-in-tooltip t) (setq ctbl:tooltip-timer nil)))))) ;; Rendering utilities (defun ctbl:format-truncate (org limit-width &optional ellipsis) "[internal] Truncate a string ORG with LIMIT-WIDTH, like `truncate-string-to-width'." (setq org (replace-regexp-in-string "\n" " " org)) (if (< limit-width (string-width org)) (let ((str (truncate-string-to-width (substring org 0) limit-width 0 nil ellipsis))) (when (< limit-width (string-width str)) (setq str (truncate-string-to-width (substring org 0) limit-width))) (setq str (propertize str 'mouse-face 'highlight)) (unless (get-text-property 0 'help-echo str) (setq str (propertize str 'help-echo org))) str) org)) (defun ctbl:format-right (width string &optional padding) "[internal] Format STRING, padding on the left with the character PADDING." (let* ((padding (or padding ?\ )) (cnt (or (and string (ctbl:format-truncate string width t)) "")) (len (string-width cnt)) (margin (max 0 (- width len)))) (concat (make-string margin padding) cnt))) (defun ctbl:format-center (width string &optional padding) "[internal] Format STRING in the center, padding on the both sides with the character PADDING." (let* ((padding (or padding ?\ )) (cnt (or (and string (ctbl:format-truncate string width t)) "")) (len (string-width cnt)) (margin (max 0 (/ (- width len) 2)))) (concat (make-string margin padding) cnt (make-string (max 0 (- width len margin)) padding)))) (defun ctbl:format-left (width string &optional padding) "[internal] Format STRING, padding on the right with the character PADDING." (let* ((padding (or padding ?\ )) (cnt (or (and string (ctbl:format-truncate string width t)) "")) (len (string-width cnt)) (margin (max 0 (- width len)))) (concat cnt (make-string margin padding)))) (defun ctbl:sort-string-lessp (i j) "[internal] String comparator." (cond ((string= i j) 0) ((string< i j) -1) (t 1))) (defun ctbl:sort-number-lessp (i j) "[internal] Number comparator." (cond ((= i j) 0) ((< i j) -1) (t 1))) (defun ctbl:sort (rows cmodels orders) "[internal] Sort rows according to order indexes and column models." (let* ((comparator (lambda (ref) (lexical-let ((ref ref) (f (or (ctbl:cmodel-sorter (nth ref cmodels)) 'ctbl:sort-string-lessp))) (lambda (i j) (funcall f (nth ref i) (nth ref j)))))) (negative-comparator (lambda (ref) (lexical-let ((cp (funcall comparator ref))) (lambda (i j) (- (funcall cp i j)))))) (to-bool (lambda (f) (lexical-let ((f f)) (lambda (i j) (< (funcall f i j) 0))))) (chain (lambda (fs) (lexical-let ((fs fs)) (lambda (i j) (loop for f in fs for v = (funcall f i j) unless (eq 0 v) return v finally return 0)))))) (sort rows (loop with fs = nil for o in (reverse (copy-sequence orders)) for gen = (if (< 0 o) comparator negative-comparator) for f = (funcall gen (1- (abs o))) do (push f fs) finally return (funcall to-bool (funcall chain fs)))))) (defun ctbl:string-fill-paragraph (string &optional justify) "[internal] `fill-paragraph' against STRING." (with-temp-buffer (erase-buffer) (insert string) (goto-char (point-min)) (fill-paragraph justify) (buffer-string))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; CTable API ;; buffer (defun* ctbl:open-table-buffer(&key buffer width height custom-map model param) "Open a table buffer simply. This function uses the function `ctbl:create-table-component-buffer' internally." (let ((cp (ctbl:create-table-component-buffer :buffer buffer :width width :height height :custom-map custom-map :model model :param param))) (switch-to-buffer (ctbl:cp-get-buffer cp)))) (defun* ctbl:create-table-component-buffer(&key buffer width height custom-map model param) "Return a table buffer with some customize parameters. This function binds the component object at the buffer local variable `ctbl:component'. The size of table is calculated from the window that shows BUFFER or the selected window. BUFFER is the buffer to be rendered. If BUFFER is nil, this function creates a new buffer. CUSTOM-MAP is the additional keymap that is added to default keymap `ctbl:table-mode-map'." (let* ((dest (ctbl:dest-init-buffer buffer width height custom-map)) (cp (ctbl:cp-new dest model param))) (setf (ctbl:dest-after-update-func dest) (lambda () (ctbl:dest-buffer-update-header))) (with-current-buffer (ctbl:dest-buffer dest) (set (make-local-variable 'ctbl:component) cp)) cp)) (defun ctbl:dest-buffer-update-header () "[internal] After auto hscrolling, update the horizontal position of the header line." (run-at-time 0.01 nil 'ctbl:dest-buffer-update-header--deferred)) (defun ctbl:dest-buffer-update-header--deferred () "[internal] Adjust header line position." (when (boundp 'ctbl:header-text) (let* ((left (window-hscroll)) (text (substring ctbl:header-text left))) (setq header-line-format text)) (force-window-update (current-buffer)))) (defun ctbl:popup-table-buffer-easy (rows &optional header-row) "Popup a table buffer from a list of rows." (pop-to-buffer (ctbl:create-table-buffer-easy rows header-row))) (defun ctbl:open-table-buffer-easy (rows &optional header-row) "Open a table buffer from a list of rows." (switch-to-buffer (ctbl:create-table-buffer-easy rows header-row))) (defun ctbl:create-table-buffer-easy (rows &optional header-row) "Return a table buffer from a list of rows." (ctbl:cp-get-buffer (ctbl:create-table-component-buffer :model (ctbl:make-model-from-list rows header-row)))) (defun ctbl:make-model-from-list (rows &optional header-row) "Make a `ctbl:model' instance from a list of rows." (let* ((col-num (or (and header-row (length header-row)) (and (car rows) (length (car rows))))) (column-models (if header-row (loop for i in header-row collect (make-ctbl:cmodel :title (format "%s" i) :min-width 5)) (loop for i from 0 below col-num for ch = (char-to-string (+ ?A i)) collect (make-ctbl:cmodel :title ch :min-width 5))))) (make-ctbl:model :column-model column-models :data rows))) ;; region (defun* ctbl:create-table-component-region(&key width height keymap model param) "Insert markers of the rendering destination at current point and display the table view. This function returns a component object and stores it at the text property `ctbl:component'. WIDTH and HEIGHT are reference size of the table view. If those are nil, the size is calculated from the selected window. KEYMAP is the keymap that is put to the text property `keymap'. If KEYMAP is nil, `ctbl:table-mode-map' is used." (let (mark-begin mark-end) (setq mark-begin (point-marker)) (insert " ") (setq mark-end (point-marker)) (save-excursion (let* ((dest (ctbl:dest-init-region (current-buffer) mark-begin mark-end width height)) (cp (ctbl:cp-new dest model param)) (after-update-func (lexical-let ((keymap keymap) (cp cp)) (lambda () (ctbl:dest-with-region (ctbl:component-dest cp) (let (buffer-read-only) (put-text-property (point-min) (1- (point-max)) 'ctbl:component cp) (ctbl:fill-keymap-property (point-min) (1- (point-max)) (or keymap ctbl:table-mode-map)))))))) (setf (ctbl:dest-after-update-func dest) after-update-func) (funcall after-update-func) cp)))) ;; inline (defun* ctbl:get-table-text(&key width height model param) "Return a text that is drew the table view. In this case, the rendering destination object is disposable. So, one can not modify the obtained text with `ctbl:xxx' functions. WIDTH and HEIGHT are reference size of the table view." (let* ((dest (ctbl:dest-init-inline width height)) (cp (ctbl:cp-new dest model param)) text) (setq text (with-current-buffer (ctbl:cp-get-buffer cp) (buffer-substring (point-min) (point-max)))) (kill-buffer (ctbl:cp-get-buffer cp)) text)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Demo (defun ctbl:demo () "Sample code for implementation for the table model." (interactive) (let ((param (copy-ctbl:param ctbl:default-rendering-param))) ;; rendering parameters ;;(setf (ctbl:param-display-header param) nil) (setf (ctbl:param-fixed-header param) t) (setf (ctbl:param-hline-colors param) '((0 . "#00000") (1 . "#909090") (-1 . "#ff0000") (t . "#00ff00"))) (setf (ctbl:param-draw-hlines param) (lambda (model row-index) (cond ((memq row-index '(0 1 -1)) t) (t (= 0 (% (1- row-index) 5)))))) (setf (ctbl:param-bg-colors param) (lambda (model row-id col-id str) (cond ((string-match "CoCo" str) "LightPink") ((= 0 (% (1- row-index) 2)) "Darkseagreen1") (t nil)))) (let ((cp (ctbl:create-table-component-buffer :width nil :height nil :model (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "A" :sorter 'ctbl:sort-number-lessp :min-width 5 :align 'right) (make-ctbl:cmodel :title "Title" :align 'center :sorter (lambda (a b) (ctbl:sort-number-lessp (length a) (length b)))) (make-ctbl:cmodel :title "Comment" :align 'left)) :data '((1 "Bon Tanaka" "8 Year Curry." 'a) (2 "Bon Tanaka" "Nan-ban Curry." 'b) (3 "Bon Tanaka" "Half Curry." 'c) (4 "Bon Tanaka" "Katsu Curry." 'd) (5 "Bon Tanaka" "Gyu-don." 'e) (6 "CoCo Ichi" "Beaf Curry." 'f) (7 "CoCo Ichi" "Poke Curry." 'g) (8 "CoCo Ichi" "Yasai Curry." 'h) (9 "Berkley" "Hamburger Curry." 'i) (10 "Berkley" "Lunch set." 'j) (11 "Berkley" "Coffee." k)) :sort-state '(2 1) ) :param param))) (ctbl:cp-add-click-hook cp (lambda () (message "CTable : Click Hook [%S]" (ctbl:cp-get-selected-data-row cp)))) (ctbl:cp-add-selection-change-hook cp (lambda () (message "CTable : Select Hook"))) (ctbl:cp-add-update-hook cp (lambda () (message "CTable : Update Hook"))) (switch-to-buffer (ctbl:cp-get-buffer cp))))) ;; (progn (eval-current-buffer) (ctbl:demo)) (provide 'ctable) ;;; ctable.el ends here emacs-ctable-0.1.2/test-ctable.el0000644000175000017500000002162312275027457016434 0ustar dogslegdogsleg;;; test-ctable.el --- tests for ctable ;; Copyright (C) 2012 SAKURAI Masashi ;; Author: ;; Keywords: test ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; ;;; Code: (defvar ctbl:test-buffer-name "*ctbl:test*") (defun ctbl:test-get-buffer () (cond ((not (equal (buffer-name) ctbl:test-buffer-name)) (pop-to-buffer (get-buffer-create ctbl:test-buffer-name)) (erase-buffer)) (t (insert "--------------------------------------------------\n"))) (get-buffer ctbl:test-buffer-name)) ;; test code (defun ctbl:test-adjust-widths () "[internal] Test function for `ctbl:render-adjust-cell-width'." (interactive) (let ((cmodels (list (make-ctbl:cmodel) (make-ctbl:cmodel :min-width 5) (make-ctbl:cmodel :max-width 5))) (init-column-widths '(1 6 3)) ; total = 10 (tests '( ; (total-width . (result widths)) (nil . (1 6 3)) (13 . (2 7 4)) ; res:3 -> 1 (21 . (6 10 5)) ; res:11 -> 3...2 (8 . (1 5 2)) ; res:-2 -> 1 (6 . (1 5 1)) ; res:-4 -> 1 ))) (ctbl:test-get-buffer) (loop for (total-width . expected-widths) in tests for result = (ctbl:render-adjust-cell-width cmodels (copy-sequence init-column-widths) total-width) if (equal result expected-widths) do (insert (format "OK : cols %S\n" expected-widths)) else do (insert (format "NG : Expected %S -> Result %S\n" expected-widths result))))) ;; (ctbl:test-adjust-widths) (defun ctbl:test-sort () "[internal] Test function for `ctbl:sort'." (interactive) (let ((cmodels (list (make-ctbl:cmodel :sorter 'ctbl:sort-number-lessp) (make-ctbl:cmodel :sorter 'ctbl:sort-string-lessp) (make-ctbl:cmodel))) (rows '((1 "c" "E") (2 "b" "D") (3 "b" "B") (4 "a" "C") (5 "a" "A"))) (tests '(( ( ) . (1 2 3 4 5)) ( ( 1) . (1 2 3 4 5)) ( (-1) . (5 4 3 2 1)) ( ( 2) . (4 5 2 3 1)) ( (-2) . (1 2 3 4 5)) ( ( 3) . (5 3 4 2 1)) ( (-3) . (1 2 4 3 5)) ( ( 1 2) . (1 2 3 4 5)) ( ( 2 1) . (4 5 2 3 1)) ( (-2 1) . (1 2 3 4 5)) ( (2 -1) . (5 4 3 2 1)) ( ( 2 3) . (5 4 3 2 1)) ( (2 -3) . (4 5 2 3 1))))) (ctbl:test-get-buffer) (loop for (keys . order) in tests for sorted = (ctbl:sort (copy-sequence rows) cmodels keys) for nums = (mapcar 'car sorted) if (equal order nums) do (insert (format "OK : Keys %S\n" keys sorted)) else do (insert (format "NG : Keys %S -> sorted %S\n" keys sorted))))) ;; (ctbl:test-sort) (defun ctbl:test-modify-sort-key () (interactive) (let ((model (make-ctbl:model :data 'data :sort-state nil)) (tests '((0 . (1)) (0 . (-1)) (0 . (1)) (1 . (2 1)) (1 . (-2 1)) (1 . (2 1)) (2 . (3 2 1)) (0 . (1 3 2)) (0 . (-1 3 2)) (1 . (2 -1 3)) (0 . (1 2 3))))) (ctbl:test-get-buffer) (loop for (col-index . order) in tests for keys = (ctbl:model-modify-sort-key model col-index) if (equal order keys) do (insert (format "OK : Col %s | Keys %S\n" col-index keys)) else do (insert (format "NG : Col %s | Keys %S -> sorted %S\n" col-index order keys))))) ;; (ctbl:test-modify-sort-key) (defun ctbl:test-render-join () (lexical-let* ((param (copy-ctbl:param ctbl:default-rendering-param)) (model 'model) ; dummy (src '("11" "22" "33" "44")) (tests (list (cons "|11|22|33|44|" (lambda () (setf (ctbl:param-vline-colors param) "DarkGray") (setf (ctbl:param-draw-vlines param) 'all) (ctbl:render-join-columns (copy-sequence src) model param))) (cons "|112233|44" (lambda () (setf (ctbl:param-vline-colors param) '((0 . "Red") (3 . "Blue"))) (setf (ctbl:param-draw-vlines param) '(0 3)) (ctbl:render-join-columns (copy-sequence src) model param))) (cons "|11|223344|" (lambda () (setf (ctbl:param-vline-colors param) '((0 . "Red") (-1 . "Blue"))) (setf (ctbl:param-draw-vlines param) '(0 1 -1)) (ctbl:render-join-columns (copy-sequence src) model param))) (cons "|1122|3344|" (lambda () (setf (ctbl:param-vline-colors param) (lambda (model col-index) (nth col-index '("Gray" "White" "Pink")))) (setf (ctbl:param-draw-vlines param) (lambda (model col-index) (memq col-index '(0 -1 2)))) (ctbl:render-join-columns (copy-sequence src) model param)))))) (ctbl:test-get-buffer) (loop for (exp . test) in tests for res = (condition-case err (funcall test) (t err)) if (equal res exp) do (insert (format "OK %s\n" res)) else do (insert (format "NG %s -> %s\n" exp res))))) ;; (ctbl:test-render-join) (defun ctbl:test-bg-colors () (let* ((param (copy-ctbl:param ctbl:default-rendering-param)) (model 'model) ; dummy (tests (list (cons '((0 0 nil) (1 1 nil)) nil) (cons '((0 0 "black") (1 1 "white")) '(((0 . 0) . "black") (t . "white"))) (cons '((0 0 "blue") (1 1 "red")) (lambda (m row-id col-id str) (let ((pair (cons row-id col-id))) (cond ((equal '(0 . 0) pair) "blue") ((equal '(1 . 1) pair) "red") (t (error "BUG %S" pair)))))) (cons '((0 0 nil) (1 0 "green") (2 0 nil) (3 0 "green")) (lambda (m row-id col-id str) (cond ((= 0 (% row-id 2)) nil) (t "green"))))))) (ctbl:test-get-buffer) (loop for (samples . test) in tests for test-id from 1 do (setf (ctbl:param-bg-colors param) test) (loop for (row-id col-id exp) in samples for enum-id from 1 for res = (condition-case err (ctbl:render-bg-color "dummy" row-id col-id model param ) (t err)) if (equal res exp) do (insert (format "OK [%s-%s] %s\n" test-id enum-id res)) else do (insert (format "NG [%s-%s] %s -> %s | %S\n" test-id enum-id exp res )))))) ;; (ctbl:test-bg-colors) (defun ctbl:test-make-hline () (let* ((param (copy-ctbl:param ctbl:default-rendering-param)) (model 'model) ; dummy (cols '(1 2 3 4)) (tests (list (cons 0 "1-2--2---2----3\n") (cons 1 "7-8--8---8----9\n") (cons -1 "4-5--5---5----6\n")))) (setf (ctbl:param-draw-vlines param) 'all (ctbl:param-draw-hlines param) 'all (ctbl:param-left-top-corner param) ?1 (ctbl:param-top-junction param) ?2 (ctbl:param-right-top-corner param) ?3 (ctbl:param-left-bottom-corner param) ?4 (ctbl:param-bottom-junction param) ?5 (ctbl:param-right-bottom-corner param) ?6 (ctbl:param-left-junction param) ?7 (ctbl:param-cross-junction param) ?8 (ctbl:param-right-junction param) ?9) (ctbl:test-get-buffer) (loop for (pos . exp) in tests for res = (ctbl:render-make-hline cols model param pos) if (equal res exp) do (insert (format "OK %s" res)) else do (insert (format "NG %s -> %s" exp res))) )) ;; (ctbl:test-make-hline) (defun ctbl:test-all () (interactive) (ctbl:test-adjust-widths) (ctbl:test-sort) (ctbl:test-bg-colors) (ctbl:test-modify-sort-key) (ctbl:test-render-join) (ctbl:test-make-hline) ) ;; (progn (eval-current-buffer) (ctbl:test-all)) (provide 'test-ctable) ;;; test-ctable.el ends here emacs-ctable-0.1.2/samples/0000755000175000017500000000000012275027457015343 5ustar dogslegdogslegemacs-ctable-0.1.2/samples/direx-ctable.el0000644000175000017500000000771212275027457020237 0ustar dogslegdogsleg;;; direx-ctable.el --- direx table extension ;; Copyright (C) 2013 SAKURAI Masashi ;; Author: SAKURAI Masashi ;; Keywords: dired, ctable ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; collaboration between direx and ctable. ;; ref: direx-el ;; https://github.com/m2ym/direx-el ;;; Code: (require 'ctable) (require 'direx) (defun dxt:collect-entries (direx-buf) "[internal] " (with-current-buffer direx-buf (goto-char (point-min)) (loop with data-list = nil with goaheadp = t for item = (direx:item-at-point) while goaheadp do (when (and item (direx:item-visible-p item)) (push item data-list)) (setq goaheadp (zerop (forward-line))) finally return (nreverse data-list)))) (defun dxt:item-render (item) "[internal] " (concat (direx:item-render-indent-part item) (direx:item-render-icon-part item) (direx:item-render-name-part item))) (defun dxt:make-model-data-sort-prop (row item) "[internal] " (loop with head = (ctbl:format-left 64 (file-name-directory (direx:file-full-name (direx:item-tree item)))) for i in row collect (if (stringp i) (propertize i 'dxt:sorter (message (format "%s%64s" head i))) i))) (defun dxt:make-model-data (buf) "[internal] " (loop for i in (dxt:collect-entries buf) for itree = (direx:item-tree i) for attr = (file-attributes (direx:file-full-name itree)) collect (dxt:make-model-data-sort-prop (list (dxt:item-render i) (if (direx:item-leaf-p i) (format "%d" (nth 7 attr)) " ") (format-time-string "%Y/%m/%d %H:%M:%S" (nth 5 attr)) i) i))) (defun dxt:make-model (buf) "[internal] " (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "File" :min-width 10 :align 'left :sorter 'dxt:sort-item-lessp) (make-ctbl:cmodel :title "Size" :min-width 6 :align 'right :sorter 'dxt:sort-item-lessp) (make-ctbl:cmodel :title "Last Modified" :align 'left :sorter 'dxt:sort-item-lessp)) :data (dxt:make-model-data buf))) (defun dxt:sort-item-lessp (i j) "[internal] Direx item comparator." (let ((ii (get-text-property 0 'dxt:sorter i)) (jj (get-text-property 0 'dxt:sorter j))) (cond ((string= ii jj) 0) ((string< ii jj) -1) (t 1)))) (defun dxt:node-action (direx-buf cp row) "[internal] action handler" (let ((item (nth 3 row))) (cond ((direx:item-leaf-p item) (direx:find-item item)) (t (with-current-buffer direx-buf (direx:item-toggle item)) (ctbl:cp-set-model cp (dxt:make-model direx-buf)))))) (defun dxt:open (dirname) (interactive "DDirex (directory): ") (lexical-let* ((dxbuf (direx:ensure-buffer-for-root (direx:make-directory dirname))) (cp (ctbl:create-table-component-buffer :width nil :height nil :model (dxt:make-model dxbuf)))) (ctbl:cp-add-click-hook cp (lambda () (dxt:node-action dxbuf cp (ctbl:cp-get-selected-data-row cp)))) (switch-to-buffer (ctbl:cp-get-buffer cp)))) (defun dxt:open-here () (interactive) (dxt:open default-directory)) ;; (progn (eval-current-buffer) (dxt:open-here)) (provide 'direx-ctable) ;;; direx-ctable.el ends here emacs-ctable-0.1.2/samples/large-table.el0000644000175000017500000000712512275027457020051 0ustar dogslegdogsleg;;; An asynchronous data model sample for ctable.el (require 'ctable) (require 'deferred) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; synchronous version (defun ctbl:sync-demo1 () (interactive) (ctbl:open-table-buffer-easy (loop with lim = 4000 for i from 0 upto lim for d = (/ (random 1000) 1000.0) collect (list i d (exp (- (/ i 1.0 lim))) (exp (* (- (/ i 1.0 lim)) d)))))) ;; (ctbl:sync-demo1) ; 5 seconds to display! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; asynchronous version (defun ctbl:async-demo () "Sample code for implementation for async data model table." (interactive) (let ((param (copy-ctbl:param ctbl:default-rendering-param))) (setf (ctbl:param-fixed-header param) t) (let* ((async-model ; define async-data-model (make-ctbl:async-model :request 'ctbl:async-demo-request :cancel 'ctbl:async-demo-cancel :reset 'ctbl:async-demo-reset :init-num 40 :more-num 20)) (cp (ctbl:create-table-component-buffer :model (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "row") (make-ctbl:cmodel :title "delta") (make-ctbl:cmodel :title "exp") (make-ctbl:cmodel :title "exp-delta")) :data async-model) ; here! :param param))) (ctbl:cp-add-click-hook cp (lambda () (message "CTable : Click Hook [%S]" (ctbl:cp-get-selected-data-row cp)))) (pop-to-buffer (ctbl:cp-get-buffer cp))))) (defvar ctbl:async-demo-timer nil) (defun ctbl:async-demo-request (row-num len responsef errorf &rest) (lexical-let ((row-num row-num) (len len) (responsef responsef) (errorf errorf)) (setq ctbl:async-demo-timer (deferred:$ (deferred:wait 500) (deferred:nextc it (lambda (x) (setq ctbl:async-demo-timer nil) (funcall responsef (if (< 500 row-num) nil (loop with lim = 100 for i from row-num below (+ row-num len) for d = (/ (random 1000) 1000.0) collect (list i d (exp (- (/ i 1.0 lim))) (exp (* (- (/ i 1.0 lim)) d)))))))))))) (defun ctbl:async-demo-reset (&rest) (message "RESET async data!!")) (defun ctbl:async-demo-cancel (&rest) (when ctbl:async-demo-timer (deferred:cancel ctbl:async-demo-timer))) ;; (progn (eval-current-buffer) (ctbl:async-demo)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; async wrapper version (defun ctbl:sync-demo2 () (interactive) (let* ((async-model ; wrapping a huge data in async-data-model (ctbl:async-model-wrapper (loop with lim = 4000 for i from 0 upto lim for d = (/ (random 1000) 1000.0) collect (list i d (exp (- (/ i 1.0 lim))) (exp (* (- (/ i 1.0 lim)) d)))))) (cp (ctbl:create-table-component-buffer :model (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "row") (make-ctbl:cmodel :title "delta") (make-ctbl:cmodel :title "exp") (make-ctbl:cmodel :title "exp-delta")) :data async-model)))) (pop-to-buffer (ctbl:cp-get-buffer cp)))) ;; (progn (eval-current-buffer) (ctbl:sync-demo2)) emacs-ctable-0.1.2/samples/simple.el0000644000175000017500000001326612275027457017166 0ustar dogslegdogsleg(require 'ctable) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Simple samples ;; popup a table (ctbl:popup-table-buffer-easy '((1 2 3 4) (5 6 7 8) (9 10 11 12))) ; <- C-x C-e here to evaluate ;; popup a table with header (ctbl:popup-table-buffer-easy '((1 2 3 4) (5 6 7 8) (9 10 11 12)) '(aaa bbb ccc ddd)) ; <- C-x C-e here to evaluate ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Complicated samples ;; Model and View (let* ((column-model ; column model (list (make-ctbl:cmodel :title "A" :sorter 'ctbl:sort-number-lessp :min-width 5 :align 'right) (make-ctbl:cmodel :title "Title" :align 'center :sorter (lambda (a b) (ctbl:sort-number-lessp (length a) (length b)))) (make-ctbl:cmodel :title "Comment" :align 'left))) (data '((1 "Bon Tanaka" "8 Year Curry." 'a) (2 "Bon Tanaka" "Nan-ban Curry." 'b) (3 "Bon Tanaka" "Half Curry." 'c) (4 "Bon Tanaka" "Katsu Curry." 'd) (5 "Bon Tanaka" "Gyu-don." 'e) (6 "CoCo Ichi" "Beaf Curry." 'f) (7 "CoCo Ichi" "Poke Curry." 'g) (8 "CoCo Ichi" "Yasai Curry." 'h) (9 "Berkley" "Hamburger Curry." 'i) (10 "Berkley" "Lunch set." 'j) (11 "Berkley" "Coffee." k))) (model ; data model (make-ctbl:model :column-model column-model :data data)) (component ; ctable component (ctbl:create-table-component-buffer :model model))) (pop-to-buffer (ctbl:cp-get-buffer component))) ; <- C-x C-e here to evaluate ;; Rendering parameters (let* ((param (copy-ctbl:param ctbl:default-rendering-param)) (column-model ; column model (list (make-ctbl:cmodel :title "A" :sorter 'ctbl:sort-number-lessp :min-width 5 :align 'right) (make-ctbl:cmodel :title "Title" :align 'center :sorter (lambda (a b) (ctbl:sort-number-lessp (length a) (length b)))) (make-ctbl:cmodel :title "Comment" :align 'left))) (data '((1 "Bon Tanaka" "8 Year Curry." 'a) (2 "Bon Tanaka" "Nan-ban Curry." 'b) (3 "Bon Tanaka" "Half Curry." 'c) (4 "Bon Tanaka" "Katsu Curry." 'd) (5 "Bon Tanaka" "Gyu-don." 'e) (6 "CoCo Ichi" "Beaf Curry." 'f) (7 "CoCo Ichi" "Poke Curry." 'g) (8 "CoCo Ichi" "Yasai Curry." 'h) (9 "Berkley" "Hamburger Curry." 'i) (10 "Berkley" "Lunch set." 'j) (11 "Berkley" "Coffee." k))) (model ; data model (make-ctbl:model :column-model column-model :data data)) component) (setf (ctbl:param-fixed-header param) t) ; set header parameters (setf (ctbl:param-hline-colors param) ; horizontal line color '((0 . "#00000") (1 . "#909090") (-1 . "#ff0000") (t . "#00ff00"))) (setf (ctbl:param-draw-hlines param) ; horizontal line draw conditions (lambda (model row-index) (cond ((memq row-index '(0 1 -1)) t) (t (= 0 (% (1- row-index) 5)))))) (setf (ctbl:param-bg-colors param) ; cell background color (lambda (model row-id col-id str) (cond ((string-match "CoCo" str) "LightPink") ((= 0 (% (1- row-index) 2)) "Darkseagreen1") (t nil)))) (setq component ; building a ctable component (ctbl:create-table-component-buffer :model model :param param)) ; apply the parameter to component rendering (pop-to-buffer (ctbl:cp-get-buffer component))) ; <- C-x C-e here to evaluate ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Event handling (lexical-let* ((column-model ; column model (list (make-ctbl:cmodel :title "A" :sorter 'ctbl:sort-number-lessp :min-width 5 :align 'right) (make-ctbl:cmodel :title "Title" :align 'center :sorter (lambda (a b) (ctbl:sort-number-lessp (length a) (length b)))) (make-ctbl:cmodel :title "Comment" :align 'left))) (data '((1 "Bon Tanaka" "8 Year Curry." 'a) (2 "Bon Tanaka" "Nan-ban Curry." 'b) (3 "Bon Tanaka" "Half Curry." 'c) (4 "Bon Tanaka" "Katsu Curry." 'd) (5 "Bon Tanaka" "Gyu-don." 'e) (6 "CoCo Ichi" "Beaf Curry." 'f) (7 "CoCo Ichi" "Poke Curry." 'g) (8 "CoCo Ichi" "Yasai Curry." 'h) (9 "Berkley" "Hamburger Curry." 'i) (10 "Berkley" "Lunch set." 'j) (11 "Berkley" "Coffee." k))) (model ; data model (make-ctbl:model :column-model column-model :data data)) component) (setq component ; building a ctable component (ctbl:create-table-component-buffer :model model)) ;; Click event handler (ctbl:cp-add-click-hook component (lambda () (let ((row (ctbl:cp-get-selected-data-row component))) (message "CTable : Click Hook [%S]" row) ;; increment ID column (when (= 0 (cdr (ctbl:cp-get-selected component))) (message ">> %S" row) (incf (car row))) (ctbl:cp-update component)))) ; update table ;; Selection change event handler (ctbl:cp-add-selection-change-hook component (lambda () (message "CTable : Select Hook %S" (ctbl:cp-get-selected component)))) ;; Update event handler (ctbl:cp-add-update-hook component (lambda () (message "CTable : Update Hook"))) (pop-to-buffer (ctbl:cp-get-buffer component))) ; <- C-x C-e here to evaluate emacs-ctable-0.1.2/img/0000755000175000017500000000000012275027457014453 5ustar dogslegdogslegemacs-ctable-0.1.2/img/direx-ctable.png0000644000175000017500000014344112275027457017533 0ustar dogslegdogslegPNG  IHDRdsRGB pHYs+tIME4FiTXtCommentCreated with GIMPd.e IDATxy\Gp%@" PT h^ZzU+PkڊZ+]jփzZ?@9Cd?Fi!>? ɛٙdv2ŋ EQE ї_<Uiii.]*F}.PˮAi`4iguu5xbpx<OkfHnnC?TUUCZYY 7l/Ů+'>+jkkh:4,,.<<}cǎ;۾}yM3o<g͚5Z5SO4ĉ/_6mZh/*m(\]]N BM/ Znóga\ ǯ^vmeeU^^u@/H._믿_Q^)mڪtppҰb??ѣC>} "-P$W^|{$K:88Y[[gѢE7nܠ9??H$W^-**JJJ8p .\تU+קO;w?:99|'r߶^_/_W9̯]~DNO[nݳgϵkhFFFm۶ݾ}{iie˖۷oj?֯JSNMHH?9΄ H;֭[s]z@=o޼;fmyyȑ#>H(֬Yqqq7oNHH0660`@II ,]4**۷gΜ^<e˖x<^L2PIIIp=fĈs\W)D+** Ξ=[O]ܸqwޱ {v:uj*++ߤ}=z( 9uꔡamm-EQ-ruu-++S̶t.][n zH$ݻɓ'CCCǏSCCÄlΝ &&;wNr_RRv عsgZZZZZڳg4T?YQv5k׮"BqqǏ'OLKGa68>>>[l~zv6lؠΫ~;wřWϞ=cǎ666GH$ɓkjj\\\̙Ch1|}}\SSS%I=H-Zt!===33ŋ~~~j ۷wwwGG۷oJNmx6\.cǎoA"֭[uF'33zѣGGFF6ɉ%~~)S.]wTPPuV$z955u޽{^t… \.|W円'.RpHY=,S8׏s :`nn5Ө9rT*}!K&NرcǯZڗ3gD"dSN=wܒ%KV\IiO>o!ær2N~:VZZ{s8~ԩs⨿)ɓ|233ؘ)}?E^$ݻw{׮]:u,**ڰa!2ϝ;wtյѶݻwMLLfffי3g.]Jo߾틋Sy}iz=*mPPy==='@iii,UUUsε722۷/mҥ㳲۷k׮rZo}%))3gδk׎ ^p A}_?0IL"iӦmے;AAAG.((LJ 2իty꟢(ܹs\L6/GΝEEEzu/8׏ `׏ Q> AAAAAAiD]JJ lA.G <)644p8']_ ~y)5oaaarr\ggnݺ>|NwppL&C.}ӮVVV@VVVqq{&&&fffE={)ȆݘM>F/ })'}ߴIII)((ӧΈ!\xy͋ P~ښa >^xyܴAف> # # M y)(C]fGGYdxw7r"m_].]u x]硖]? ҄ѠhpGZe{kd%3ΨܱoȟMs PLx%G9\Y4f{lwwVZŋڵǥ)FS깚`5  BZUh~[[[~XW_X|-[֭[gee`QF]rp̙wӦMUVj*+++,,/]T6CFFիWwI*ooo1%O<6mq ?>`ڹsk֖k„ %%%GJ_~e\\Kz }Ν;|O?4jԨ嗷Lq{5k_z~9 "6+| Ǹlxu0u$jP?$iEy%׻f]DIwjfv䔙u[Vlܻo|v{忸_/K_?w%q2sf.J56W-e,Zdhi!]:tr( _.\jZ%w+FR)_j3ڱYUt7nׇ\~=!!!""jÆ ׯ`^^^/^ h۶ƛz"tg¶mL*[͟?G8֭v횚W*=SN]xa͚5 ***jٲ%駟]xҒf̘ѽ{w1c/SSM1vM'|ޜ4]m/ZK_pV+7 *^;Sbv{{6];F28Ȧ <6Tͱ9{~7H[&W_h`;CCd\ {wy1&׻>>R4%%ΐ7oޜVUUzj///GGG8JE҃?*wƄ + R&~bDR{Um(jڋ c +ɫ8+?_u{ڟW?5J: xag+qil% o}k|ō{&ā|ȶ^U .#FB_{m@.GiR7:+˥ܜȟ!yURнJ+ \bۧD)ϟ??tЈ#LI-|}}vp̞=裏d:r䈥%=W'O?(B8wѣS^=Etbƍ/_tqqk׮޽Ω2~LLܬqHpp0ywG# 2dȐOZZZfdduAzh۶g}oڴ 6l7Ӈk/_nذaΜ9DI(;W3Ϸu"%IUU@BVZwN'ܿTϞ=IĉܹCwqΏK]PXH@8iUA?0zUI$iA*2s8`o'픩g͙KQSSʺرjʔ)/4㣢yggM6 <+''[nzjBBȂ7o@QիvaffF LqXEj.]Zl2FWzѡŽ4^x:XA/aFO_ТGvݻTZlDr=V[~dkdH}ط r1/.5q~=.b/S^1yςyZj)g)b*ϸ;}ٙ1WZXBO{aiڻjZH?x3蔍7N8qժUgdk֬yu}-^|I_|WZh``2sLȑ#-Zݻ2ů]n]ppJ8BBB|}}O2ίΝOݮ|m\?*ءw??>d?nkʼn4^^)w]4ڝ;~98h 5 IԾoco  (*◥uU3URPdX[)(2?17 yUc"R]]ݻwF}4|\)>cϜxmY%\IJ.ՑJf}]V-,1ޢD=.XAA--;ZfӼN4r"m_]v: HC_ ~nsN6m̘1C ͛7O:k)(pz#qi'<<\6ĉU' UUU...ի$ŋӦM `ܸqϟ?)a '"DjfGq8TWMG7>|ѣG3gδX|9DDDlڴiӦMVVV:t͛7 M}v|222H+VfHOO|޽{CdwJ͝8qs}đCqizj?S}J31c8qB*N>=88XGƼzMJJʅ ݻҧ{.\8p@_) ߸q$\?'G!RrVTTx{{SK>/t2 ?E|9HKKS.|I``\ܹsGIg/KhhhϞ=)btѨ,bby||<$%%QC믿HcDAAnæv7<ի_@$>}`BPΆe722 , :vW2;"HA *%%좜GIJJ"[x޺uƍMٹsgHHy(&&o8J'j؏Ki˗/gffWYNq!C)O>zҮ]JR0cLAh(@ Y[77.^i'dfɓ;wD={T}<ǧgϞÆ }_߿?=s2~TTKWq@igϞ>}ZnCq6mL6Hr*ƹt)C DEEkQtnIDDą hDp Ce%:zQi^֎3Ņm*<<|ԨQ+Vؽ{7UiZd۷9.RƬ:_xqƯZҥ8Z~+0@6%,,ݻIII+W<{,,\T:gΜ vڻw@ Ũ1#Fڵ+ɱwN4ΜiϞn^ HƎѣDbjժUEEѣG_?2d{[[[[[gee-LУ X:e&  ؓ'PY @Q@* || =_?χ= I1eʔ#G_>99tܹM6&M `޼y>ܹsΝ;c//-[?tUK, ";w߿_M---@lllUUJ8L0{>}ҪŤ$CQTrrw}fnn,TvŋJKKk.2]\\,7o|ʕDݟjqSK IDAT\YSCQCZHݻGQկ"Nٕ]edD98PϞ"F-Tt=zxxxZ_ܤRzGkjj͛'_Wq`:.8Cuuu-h>s __ݻwf*'SBx,511qvvȨP-KnYbɓ'ϟ?ѫdZ,_aZ!:^ .g޽?#k+Z<OMMM~~>ب[aaD"P-(Z((U(,nTAzo߾'On$q'%%n2223{{{svf ̚V1RpAA-K ҔP.BD_ ~<ԲGu/Zv H~At Z\ү_?>oii٣GvMM[nmjj@~wM3o<p85k0@em>d;v(Zط&''&&&..._}}^e)iiiXX]xx8]gx.JmY_|E6m,,,/\-],Հ.dCu8`>}еk׈ mʔ)'NXnuDDD߾}A||<޽ʪ)bccˣ미8r6֭[@~Xk.PcN:Žj6?-[v˗/i999L ZnógțٳgC ٵkѽ{p#zɖ5cƌ}Cl.l@KK}*KFN:}D6 ;Fe˖?~{"())z̘1cJu*;S9/ ߯2pr//++ C}drtt\f |gggK-]JSi|t̻wN>ݢEABp/^,//7009r8G B2WٳFFFm۶ݾ}{iil%K਼aJ맟~JIIZ|Kt'{zt'@G;99~MkӦM^^^yy1c}n޼)+6a-]*gߦ>-6/fff:::ͥ(>7対А붴;vT95rH-~=zёÆ Dm۶y P5|={nݺ֮]گ_?ba-]*y˖.TG:G[[[GGG˥ 把N>=p@>}: '|>|kȑRÇdb!odkr`TJnu̙Fe3gN,]q4%Njj*3,],qS1>uIDD̙3駟ݸqcȐ!=z;vlTT@ D"QPPPxx8"iFrUVVVPPPYYymӹsgJK"#qݻwWZ5eb}ڵSNEEE6lprr֭yhhhxxx~~Ajjjpeg=drrrZ|yllKt'4 ).i;)ڵk7\۷4gر5EM4iʔ)ݴiS۶m)khhN8NhѢEnbbbh[FT.]XZZ988?>++dJׯ066 u.9LV jѢ@ x"ZTL҅ z Z@K޵c!''GiuPKAV(rܹq.}-] t! i2틖.Qt!M}q~Afvtaׯ,^XֆcggGҕZZ/3ǏoTJC(u& /^x1m4P(ƍGRp\+uX&=vOMu_JsϞ(ޅ48B^^^[n}ܹs5 ,]aBzСKE^ { ᨿ`}7wٶm9sR.Mk{H [YYYY<;ݻwwsscK8LhZZ۾ԴtǑCW1k K&ۗn9|EBB!I:u#GޅbxW\ILL$ŋ }ٵk^EK8LѴ}i233c)pq'NZ"V,/.T,O=e#DEEx<$.D,]&&& Aŝҥi׃ZؾԷthg CKUZ}q§@Ҕ@K>]6.).}-] t! i2틖.Qt!M}'] %h®V,dRrNt@IIɢELLL.lT~MX/;v0Mۅ){Nŝ ipZ*-Wji(KWiii^jkk;t@vq`T~MX/;v0Mۅ){nh9 i9(k\5~K׷~*[*vKS=[Էt]K;bhG# ZhBJZ(\-]f͒-Kz`bɡ*t֏Fq4\6 iڵ,+54lnصkW[[nݺ]G,.z`bɡ2~ԏ ~ @+ȑkX,Wji(KɳWNHHݻѣSSSKZv0Q36hB5v-J8 e"mHHH>}sqqq $KEidxa.LqᨿA+}Er+4;88ܸq.[UU@ `T~V,9>^9v0vQ36b! ]ritM4)22]vm۶ݰaСCҥ"݊%KyXlY`bqx<6mŝhBN&J_,]555g϶4hPjj*{[Էt[ZLv0vQ?6t!H=.&ҥwshGpA7-]ZAhBuAKd-].hBLm^Afvtaׯ̟?u֦|@򴳳=zM/kSٳ~:ӛ &Rhx5m]Y4=.5fʔ)۶m{ȑ}۱M8޽{۷oOII޻w/88SNݣ?*3`ss]vٳgذaeeeD&d/^\pÇ @CiӦ<{Ljʕ۷o_d Iްaǟ>}:tPDX.]bŊ={qh]^KW|||xxsq0ƌnڷoߥK݇J*(Eմ]T֏q4=.mh9cǎ%%%|>ٲeE{ܹ$}͚5d#9WYYP(`NPjkbJK-]Dž7-Z4hy( xE<쮜R5?`&-9zepGco'''zN]kfff"wݶmK@.]x<S+ք.tit\kbҟAAAϟ7WJ]]1o߶V9v0ۚҥ.MKޯ9_ZE'Yvv6J(v-::{gfQ:D ssmIvtkkBKX4=.kOAAp~z8dgttt=`ښcpԯ1=z;vlTT@ D"QPP̙3###[jUVVVPP@^؎BCC TSSN@;UJtXZlqiIs^I$8cǎ|>ԨQ?&鲋;%K( IT~zccc@0`[n wDV)t黥҅ ot1.k_T#999JYW zZGsαF At#hBiv A-]Hi_t! i2틪Afv$''gСVVV@vI&yzzVWWc!Mta4iRqq'._ܹs.M(^re_xLl"7wJh90YlVϗX靥KО={jGr”_SKKػՈ O<ݛϚ5+..n  ())!O͞=]v=ׯЏxd>Z~!444;;[qScǎ^_~D}Yp!~-L*vo@KMQQQLL VƔ_ K,&{Wt5QYkjjd>}jhh@ܹH˗$--lBsδ4zg(F;v$QcjܾȈ&LХK{ᘘDSYN EQ'N{9}t:QpHH9rdUUS{quu7_S zxxצiG޽{io]JŋbXn;68LFm```cc뛟󰹏\ʙURSS%I=-Zt!==<|weGRB}d2}۷g~ܹ3g+++O:#_DBb {nRRʕ+Ϟ=KFĚo(Ksƍ_K8]UZՈ022R4jRtgϞ֭Î YXlVZ"|}||LMMGQtfQZFV5t5$pu򰴴4%%E}{:l޼9..?lݺ5}ͼWP۬h \P(4~CYȳ/_\v9slҚy ٵ_ -]Ю]N<̽qFXXaBBB͗-[JF]н{5k̚5}ժUcƌAt#hBiv A-]Hi_t! i2#4;s8YKF}+77СCoAta׏@#{ۉ^ J[,thԴk*:q^x1m4P(ƍG6cAW/.5cR;ŝ ]hkجTڲЕ)v-,J8cƌ!JTT:}`VW/PfR>&S;tF3A*._ؘKK%KZYYmܸ51c}:73&&&((D""?b24YPjRj˪?KML KJ8>'&&{(K#KK{)t)mG&;GDDǧDDD] \]]ϟ?6~xgggm6kkcǎ;w.99 p¥KSήC׮]322?AޥPX. +**222:v񚺺:8_|񅓓Ç `ccCv455 CBBݱC׋Lq7o8p@rΚ5Ϗl=d?'OYK@TWWlْ3f̧~t5QWNNFfKΜ9tZ/Ǎ+m/8Lخ];_ ن']H$ٳE-ZյL9EZhA:uZbӓB'.]ڥK"GS鍿WYزҥG J8ϟ?UUUdZdǫ>Gi;ҥh/^P Ǔ'O B^*;B'Oqqq3g╠E𠽚W^ c/̬u֏?4z#G͊ŖńN,]qޘeff@k׮{1nIibo/q# orIy-H`֭[;Ω{ݳgҥKN]KSSSJXYYi`[!o6+&[t)!obSiB<==JC%Mi{1؎$R;Of=귷p8999S^^ <|YZNzܹ%K\$*}!x IDAŤ(T,c[f%;Ɣe1:t)CP߮negg֬YoggTxm_jZT{մ_{Ba߾}###=<<G?5}~¢W^ݻAAAp.koo_UUu5孕ڻ7ox"44W^7~TiZƐG͊ɖń,]Lq4kج4tegg͛7_r}KMKK{1ajG&; 7drjܹFFFVVV}۾ϟL.OOOPhddԢE/RQz$###P)1eY1ٲGSJ8&&&*S;ۗ.bԎLv6t5Y:v8qį.&ҥw9 FKy#ҥ Aف.Qt!M}҅ ꂖ.ɴ/Eivq׿b ڇ@~ĎH$" D'v:(̨Q0e N.???X,۶m2srrX,={6:@KvZ.˴"ds\ń4C-]Ï?ػwoO}LO-\p2S˗SSP攻;u?`ekKqT@ZrŘ>r$,-[ՋZ )++JFFohBK/ʕPQ :ԩg͂8ؼ t5k ://;`:w_idw&711!nr|^^qÕơٹsgHHy|2dҩ 5<}^]vJc-p̼<+(&&FnG 8n:>O>!3&&&VVV'OܹH$ٳl_ҲSK. K,Ԍ!_|y12TfٳOСlC\n6mMVTT$(ggg]/x'CM 9 #B-]hiR{1?'''w`4d?q8PWǘN|غƏZ w/ްt),\] -]Mvoeeegg!ג+(-x=WFyy0q"ȮEaT8w,R/y>ZdG ssX \]ap00PNccAϞwt)#傽=TUkZ otZ?jSNEGG󵴊u ,,(ssjh^0Ɣ.s "K.lm)##ϧ);^9҅cZbɓ'ϟ?ѫ'OHt1.k_𩩩'LJ$  &MY]]ѫΟ?p*ق.XW[[U7گQYtebJѨkiUE.Pe%S]ԩ77r8~xuu5Ǫ@MWL58dQǮJWqԩ-2K.*YF:^Gre===lg;wK˗;::^|IZ|@ 0`u^^uHHpu}v#MWL58dba5_SRKr*Qi=SZ:韛{_RR~!!!$=""6>>>%%%""7lz;;;s8K.|g؁>]ϬYk.**Jrld'OYKSΛ7o8p@6e˖O&U'{n8ZԿ8d7ٍtv*˩ݙ:@(:;;ӎ7YFx-[|eF!ʕ+vr۷=zDo|ɧ~ JWr-]V)-]4JmV۵-]*˩4S3_}]1dVTTdffxO.gqqǏ'O, BT*lܸx޼y۶mkѢV,ҧ+_ɡI'.EgY*jڵZTSYom/]nڭ[7:&Q[[[WWox^"M&"7+KU4kڬtG ԏҥ[S=㨿p8999S^^ <|YZ0iҤv͟?„ dwrQWhaqҕKnLR䡦v-&hv-ҥ>r~ɓ|233XifPطoH}OM>~իD"{nPPOgϞs玳#G&Nxq-گkjqҕ*] lViv-&KK9ajz%|O~*\Zu-(,,(*''LJ 2իzOUUܹs퍌K~ q._l``0i+|4_1+KUJSJWq4MZL.v+bvagt!H.&ҥwshGp oZt!4;҅ ꂖ.ɴ/ZD]҅4ۼ =WXAȯD$A䞚4 <=A}IWN  VV @HEh®bm۶*3'''ٳg+>%C^/4 |MóibQiעrqqp8בZyŴiӄB@ 7n؎M .-l_,6.,lZߋ;\H$R30b:~AM2$|~|}KA]YШcעUL+Ռ3f̘'NHӧw/ .-l_L6.M-lڠ9[LO-\p29) l>25ܨג.2 _)ER PKPoC#*,]*R۵by||<$%%iÇ_DjJK KS Z4&4A@elh=~^M-[|xAd$Y7CBÀPRcM Ӹ r|^^ٹ(Õơٹsg_,!C(P3ӧOAfw]J{1UK|rffJ,EEE111r;RĹug͚?< A FŋeK txF۷ʕWo0y2-  ={PQGb4:ҥF}֥K~H2nR@8 **˗ǎ#LexմtɢK&fƒ[+.\%]@>z+*@(ݝLAо=XYAj*H$Уǫ-Z@ ҸЉ mbjڵR9s,XtN8`׮]{533111%ǫKv]M(qimaӈ&ψ#v ?srrrll,}^_JDF,]LV)Z[n}ܹs/ Owtt%_ CJ -;BbR߮u,pL}4Cgxk֬' LMKy틶qiaak~-x*+!?( œAň# !!l ]YhdZvŋ_Jk.}]SKWvvvqqX,޼y+WY*% 4t/&6mh;O:~^-.OO굤u wPAAenNM1/0CpqgSW.8ڵh^ܩi@gg 5jjdּ,]+V8y5zշ~|I ".&ҥw>555`ccCDBDRQQt#'%%n222DLx{{DWKAK H-].hBL A-]Hi_t!4;kbp5v@D5h®ysK˦d@Kv˄,],Qy]O,]"H~DrX6mF&mZrۗ,YB#""7lpO:T"i|||={fbע3't3f̘{8qĉAe^CXhv.zТ<-]xyKu *8gq(Rjޞ24q-]hRmRZAKW=d c`jOX`Xbb]"tAct)O}bJL3`7BCA,zzn5t5*K{=`ׯ.tX:w3g*+)~gd3iQYTC.%X\] ; ֽ w/ްt髉 i$YG+Kukfb()?֭yS¹sd \] Ҹ}t56K{=ਿ~QjbnQS@\i9\.CU\A hgbt)t1Y_{g㢸}ٛH ePј^$Cbp1.1QFըa$. I(#".DJOwUM+ts@uSV_nrIX͍᜜h5oDKZ Xh-]u*w-`jV E-]f׿h@e%44@e%> UUЧH-]Z$+ V`egGҦAK9.A6ZD,hB,҅ bAKb15|AmE#0%6m^hV2ؽ@@KϜ[rFi$%<Leڲe\.ԩԩSkkkeЮ%fe8:q-]`J=iMyI,]"W&=Luӂ0q"kݸqcڴiIIIdɒׯ_uΜ9QQQϟ4iҦMK{zz8˗/^صɟJoرculV~rJ;_#֞ oJڸf VL Nរt ?hs8S(!C?qCrBA?#r}a!n歷ފ6ŰY0apص-]t '.@qgAy90jb۶AI Nk!- o3g򕺢zkk̄9sf͂@(*|@,0… :u%Y6+S{7 kÇ б#xy<-;+Veݺ pOJ77ػá_?9C\]t|)DLikʔ)vvvaaaoK-]<6+S1v-tS0=0 . "# @nޤnnބA}& xNW_vھgI,]K.x޽{/_3DkimY*gصb2~Z{-`&M[6.846W)7rF¸hBtuMRի>>> ѮEY*#h2a{-`8EaҦnބvU*P(ڵbn=jj yI,]:sdLDjע٬LG*Z:.ɎӞqo2h6.ŋpY0| !!0~VAe Á. Z_t!X҅XL ÁCi5j_Wޛ+x{ ~zTTB "3-]8#-̸q㪫8pI&a<4iRT(_dOf1Gv-n}/ Aq0Z;H~O<9}O͐؟_&Ljrr}kŊ6mZt)Ydɒu֭YfoߎjllI&?ص{i /2} |)g`%Fc̴L 9Ը8'''F+uN8-)**:tBA[7r &?C .< V Ő!C 껚<==۵kRѣG<͛sΗ_~%>>[0>q555p8MMMK.U֮_rv&;vʪiRGGnj {>.8ٵn}ta;t>ҥKG5j1#755M81++2l̘1d劊޽{[[[gffDŪU֭[ XvmZZϜ9>|p9111%%?xiyȑE:t(55ԢqF;;;R]rr2l޼/HMMߵkW_Ԁbn߾ 萐 T.8F[DZol61`o&˿;bp֤}}}u!m۶UNNNCCBHMM%v~_œ'OYF_N޽i޽{ҥs-[.]t%RIjѢE>>>5558o+ZR~Ϟ=?~ѱJ̬_^]wwUVqo>\q܁T*ѣ[8<۷o῏2>.rF8qbUU~Hkq퀳gKpp0?yo޼ 3f TT111O<SN+/^;v&Ko޼9~xRT*juSSӍ7q N:[=Bl2/25|1y맿\0>26x\őc ::Zɓ'ݺu+//޻wO.Oh 8> &&F{ ->;qhCÈ3''vÆ :=jkkVX!xh1؏c{ڪ꺺3guSRRR]]]ZZW_:uرczŋguAt.ZIv-FR-]Ca).bb;f֯T*mllڷo󍐝ݵkW[[<+++44IT򗈵o${fϞVmll\]]#껮_lgg믓8f .tssqrr=돎^^^&L(**_Ae^SYhqڵhKc}DơTKTuKI ;v~L h颁._Af.s#,e Á. Z_t!X҅XLe^A6˗/}h4daÆ5sׯCTB(Bm҅C3'44^2ٳgKKKg̘p< (BZ?h2niX˼o_LqSc#< vֺGޮuƍiӦ9;;'%%%K֯_~zWW9sDEE?zҤI6m/OCZOZ|AK8DA?aP+u 1B˖-|O>p¡CjHbc;_\\x<)S92Ӓ_q55S[S9kkՕCrhUyCKX!HMLk!- o3g:ظP 9`f HM|B:h鲤s&c8pz>}Ve "u  ˃QE8u `Fx.^yI (0`iܻw/##'rkfff=4MXXN;Ϯ]\\\̟?qw}GP(RRR?~ѣ~Ơl:tO{ݻmmm;u4iҤ;w%%%K ~v;/7K+K,9zT~OìYP] 7oTR j545TBn@{zBnЭ 3gʯ ZcQ5||MRzÆ gϞݸq#F$ii% avWH-]hYkPPPPPSj  v yjb'‘#t)XC Һ{_~k?W\[jBjjjttW^dL+ҥKqVZկ_?r# F/\qVT?=zz3grvv;ିeO?ggF [[PΜ_! tZtț;YG-]8 6mGABBBIIɺuQ:>҂0lM tEuE.}l-;u4uZvJtٮAHHۗ`0ܯ{M2SN;z(;틇m1k&XART*Um8>҂0lM4t޽a陒^TTD,_,]+Ʈ%f%Ek:ujqq;RRR*++i _@ð-PTT4tP{{{B1brN8ɋoVX1eBhx!##VgIIIjZP 2P[E`| 9hۚh+飏>~>oVttt+ŰY#}tU.\]%پ6m骨ݻuff?s)**5kV```S̺iĉYYYdcƌC}g틈5j;ڵkҶo~wwÇ ^0tlM4ʔ1}twp…\MeboWK}1>'X+<<|ڵuuuIIIyEkl_ YO>׷^^z1{Akk놆2W9{w}ǘk4oIYYY0^g?_p~=wﺻ3MlƌӫW/Jջw={hO>rȺ:yjow߾}rTѣG1۷;k0>}DaRT}^???СC:Ahh8͓dr\L8,?~ciiNY7éS^}vډ\zƌ**&&ɓ'H+++r]V߼y͛Ǐ'gjuSSfX4[rɵ+WfeeEDDW.]zŽ{^|YУ &t1+Үe}Da$nܸqŁ?^[B.F;0Gػ2q*++<<G<Ǎ1",,,-- V^0h vZ;HA׏?]:ijjjt6++kڴi}h4;KJJn޼IT T*Bq5/-]7oůX0&J'''sﭫS(-d2{7]v-J|#z̕+WFA0aBSSK`[h\ػu֟2~xr'sf͚E^ rJJJʠA***oooVŋ.]5kU[n֒S~йsvፍqO]ƍۗ,O# hkYhVq%''w^Zf\.\2-,,\ti\\FSYhەjע8AۯΝ;'%%9888::nܸ!44~ibػL|:Lo urrR*/-YÃxvjkk;`L:ȝ]vk߾}||ÇIA4(TWW7{lZmcc絛 }嗵-ܳ,]JkLj8C_˰aڷoP( pq]~oٻ҅ -Zh˷!,e`!AZt!9҅ bAKb1. Z_̋ h弚ڿ?fA;t-Z h.4p¡Cjpvtw\\Ç 8wpヒ aj y.A[^?!JJ@j^^0v,ȑп?A@?ʕ7p!Z)t Z&eR ^^_?&Mww8v N1cw!!JKq޽ (++宮=zh4aaa)S/ڵeSRRtp .g޽5{ս{wOO7nCi 'N%mE?޽ֶSN&Ms5oܸ\XdѣGseS' 8W =z@v6<|p 쉃@K I.-Z"-]&Uy7I5 6={vƍ&@*׆_8-EC+--T-AKm]]]ݝ&W_mP] w8 mkGK .-Z"-]"[¬n݂ZXUPS9* ,ZE0aѢKc)iU7w}B.se_| ^ٳgYY :Ըͽ $'êU0}:XY/|b# @K9.A6ZD,hB,҅ bAKb1.A6CnnL&-,^H2XAZpGZ %%%]O>,H-͊E_p}JU -]2  V3h[(JRүvm&aAjhV,Z3lVRRhb?tdqI/cxZ'N uvvvtt:MMMK.U֮_~%q2ۻt2w\/ҥˑ#GujooP(FH&==~w\\\>;v,--yyy ӧY_w~(((8rٳg9{q>S)O{{O>jɾdrR9|G8q"%%=.X`̙jz#G\hqSLܹ={;6dJE*,Y-##`ɒ%пfK.{^^^IC`;ڍ@@SBBBiiJa;ICvi߱c͛77f5\L>=44T{ ;I ѣGkya'H=[n b<۷~/¡Cȟ8]ve/v{k:ط~[ՏCC9x𠵵5Hh"m=z8ɓ'[__-hofeea`'#˿;F>% ۥW``ҥK.]4#w]www~Fi0IB~ZDHKi2~ᬟL&S NLL_ڻwZ;s |[>̙__&BBBƏO=wܬYoL<9%%ψ#"##wرm6-~sxxxccc~~~\\ܺu,..Ν; ~/a? dFOˇ?ÂTQQ1n8߿߾}ۥMjjjf (͊E˟>٬ Z烖.Z R,]R oLvvv׮]7LpB777''k׮~ Ç/'\R0rņLCCCJ%ɑvO@@^8nԩ'O&W͔111-ׯ_{s%$ٳgjWWȧvA:b)ۍ@@Ri߾l|h3,H/]Z:tҹ\ЬXilVlZZES_xOp;Λ7B>~„ dHYh _bR-] 뜠˔XF5}t%~ /@zܹ_vqq'|gةh,A+mgMhŲ(-T,Q׮]߼ʊ`[EC~#ho#,]|h ,`2ZkܹCSSScc#qW&)͛7ٳgΜ3TiV,vC X0 ZDZj ZZZK-+-nڰaCHH 29s/!j߾=? t}v*4+ &bY/hiBHHH%%uMV/t= ol#,]_e-]MGu떷7yΝO&şiNm.b6h*}-Q b֭ }3-`h_v-v0ECмx IDATޅyMIty{{WVVTTT۷ztgNEbbB-TptKTttttttEER|rPPP@@yW-`h2h`\{ "h0VdܹUjjkk׮r~/5_nVVBAKݪU&KdZ|;4Ewd6KMNҾ={Ι3ʊ1mY4;rAbB-TR-Q%%%ե_}թSi%ZOˇoעTK- o4q\ddɡO>ڵ#̛7_~gةӬXrŊeyi*h\5a„">ͺE-]CзkAޅw$fa٩-˴V,˸-䉖._|Wh2-hŲ e/tbYh2+hBik A¡ EK-]/Gis2,^H LA-]8#TJJJ֭[Maڗh,AEwhU.g)f-IdO&Kh5q}IjA222lmm,Q'ؐxccc|M69::o#ˇzԩ'Ntޝjb">#Ov?cJsΜ^zuޝ埓o߾3g|7JR˗/rRt--y JCP,N4[ۖ.fabi^AqXbʔ) BZSA(YjooP(FAWӺEvV8,T4r&- -]*faҁoJLҹs={;vlȐ!*TСCtt#G}]RI֊\p͚5>>>.]zrR9|G8q"%%;w͚5+00)d_w~(((8rٳg NNN O&OK֏ر͛yAw#CCCG_=zL.'H[n?͍ɓ`|/Ǐ DL:#`%r+;; /:ط~[ᩪrpp p7 W@@qݻwmllJ{.}ҬXӧOy‡fb hjCfq\ζ51lYh\,]hL;>˼lWdd$ѕo҇a:uԫʨƬu ڵZ-*Ip9İe-]ҥ}!VfZt 6.*F]o('???l[͖%>tNK o4 "m\%%%7o aX|}}i -kѸoYhEښtlYAKWtWv67ŋ.]5k{Eb%$$?q|9q'+W 44]ˬPi4Aİ5lYAKWt%v6dl\III]vk߾}||Ç;&'ÊT*K%Kxxxxxxh-ZfufYhi&-'Z6KΆw%B%w]hQ.]r9,_bSGNSǏ mI&mڴIԩSw䔒UYYigg陒^TT4m4gg˗JNNG[ܹss _dׯ_:gΜ[[[g W\9ydzz:S0GVVͱczyyIcmm}޽YEL>"366MMM&L` 1&?;o޼؝;w...-势V;>&hѢw[uĉPR"22>gXh׿:::8qS`Y'fqb؝ZK} 8}/|ZiˠgaB ~~~gϞ=s挍͏?ѭ[4ݻwKJJ֭[33,`-4{_ǎKKK;}t^^ޠA1a...yyy~~~~ I\1l'dYv'3t Zڵ+L6_ڵ8ۗ~Ciս{˗str=-`-j /Gw,`|.Ak-~@@@TT kv9#˰5gXW4dF.A 80f̘^zT޽{kѷk /8-]߿sӧOϚ5aݒdb[-Zs'] ~@uu3U*ULLLccFLpr ~!&Lfq2hw,]4 8"j]]ʕ+"""bbb s[0=ھhq 3^5=nI\8;;?~\{&]"-`d ..2--#//ﭷ2p|Hm8I~#hq2hwYbv`ҥKG5jۤvڴ۷9s}ߝJK]3vzƍAM<?\h"҈H.\Ș>}:1H;wΝ;9r8@ >|ꫯ@yy9\FTS ᩯ߲e 0Kkɓ'o߾ O@HHHSS?,5(ʈiXX:wsΑ:O):i$wwwR? V^h-ZT[[+-[ѭ[ 6ܸq?jAlOLLŋ_xᅸ+WۗMѬR4LbU .0]Zvu;۵f͚bŊK`r/WRmV:-T EC&,Ԣ|з/Fئi2EJ۵hKCnCǎIۿeeeR-]88b8q+V,\?ANKؠG!.ے>ah?۷W&韡FۗxEڦUjV)ؠ9K`]{q}PXXxm;TVV*J~ 1s Z$a/ǡYگ6+*A{Wv-` ѢՂK4۔qV)}Lek1жKCn]+//O&qwُ?x֬YD*EC[nG[fY[^{5N:ydfYڅX߂-ZN}f2*,]84-m8A@k=ٹo߾?Zhqhe*h}hBKZ3-Z6h2GpG$-Dt#hBis AĂ.b-]"t!ӿXÁC-]H[-]8#-fcSB?4Kawޔ)SH1~=zԸ-]}7"fm3xs糥,]mJH@ta:ujqq;RRR*++,_K0#fm3%hHKZZmeҷPyzzZJFII Zt|ص/xKچ.a.V#}<ѱtDBvڂΝ;K=atZ#Ed,Zǧ Ń-[8u)Sqll,?}c֯h[~}++gO<~଄>qwuwwgY`qqqdɓ'K]t)">}1۷_Ӊcp8Y} "4?-8~1cKR{Ϟ=871mYAKAL7n\xqϟ#Ht=itmm˼`Kj-]@ö2X81bDXXXZZ^:!!aРA-]\_R>FYp藆EZV#@KW뀷t,ױ2[ܹsʕ?9a„>… -]b>4tZt9::amá_jbXՂ,] +^ܹsRRƍBCCEK;crqqaXtѬmxdXf#բyi.f_ ־}{B1`ǏDK;oעGfm3\{hm#u4˘6hMeOsňϣ-6cGIƾٛ6mrtt^ĉ666N bGih?o޼_xʯJ*jΝ999z޽;DR?//'ݜ}9soQ*Ir/"Mox։ct>G;ZN>}N8qűc:;;?z5%bjݒjBKi~Z ZeqWZZ蘑!f7Km]$/14_xZ&}1ja3tKuK eR$,;w믿.Rm*KeLǮňoۗ|L!Il_Zd مZh~tvRd-㎎UUUbfǏq.AAǮ%fۗ|/p@SSS|| iFJ`Jiyܹ_vqq矽_ Zȅ$OOvڑڙ`%ɮň#]|Lbٚh%%꣏>vB-Q<[K0GR\v ?;feeUYYɿTv-/5?rȢE: tͺ_xqڴi|pK]]2ٚڗ222O_UYxܹ:w\GRܾ}VM_Tv-۷ougݗ/_ζ_֭B!~KL|Y`vqZmaSg4%ƌӫW/Jջw={Dk…:D>b "!!Cʮ%ŶY?ӳfbد[<*hbħ!]IJٗ-Qbc]]ʕ+"""bbb hR-Q/?rc}DP(nݚ@dߥ Leob[2~%h"OĉȈ/ E,h%AK L<=zm۶,jjm.XnY#qZH":::::BT^|9((( @j;gG#aR;yeeeM6o߾SXVdׯ? l\ Gpxb.lM4U__?cƌ: 6mmkzOO9soD/rk„ EEE:2]Km 3,\Ν;8}C/fxyFDDk:h`<ֹ~A?h2G -]ZAhB EK-]/^Eisп|r^AP\\lp}FCV6lsKrb@&CM/҅C?җ^zgϞ---1c0!JJ`:}ݻ27K,9z+$C}=x{̙j1Ahl} ??ogFp&up&\  tco8:v3g.X@%ۮo2fZE 7|3$$6lpٍ7B۷CϞ Ae%[oIˤ 46K|`y.v#'--gI];E]]]ݝw99ĉp,] +V>ʂiӠo_h@ EEPU={RWP@kޠq,]8Raѱk߿իvvv2Y_y.]K߮Ŷ}Yڬ[[PΜ__ jط ;ʸ p<o݂ZYЮç3Cc#t KtР;]kՋ/&/ݻwo[n%-v-}KT;)iU7wٳM&0Ǝ?y!bt@Ko˜𩯯Jemm^~.GVZ̑^|E(**ҿͺE-]t/ЪhjjJJJl׮=zD|a}l[5o޼؝;w...IIIjZP 2GTT,a* (3UZ㣈EJGZS)-jk(:UV4P>VETb (ם}{N6!@/~{lr'ٜڵw}WPÇDҮ.ooŋ BBBJKK322h~uuu\\g h...<{lyyyppɓ?x`JUXX&O#ni=~-bTCܽ{wkŚ5k|}}iO/Mӄy…C~O>2dӧCh D1zqݴ>r{xx>^S*ڵ xGG[nǏ/hvuue^O<(((@/^T*SSSr9{vgϸMJJ!N^^%ZQKTΙ3'N^())c+++sss.]kÇiiinO/D\,Xhؿ:t萛|,q㴖93V7y"GTD"kTTTss39g(Zl믿~1p7oWƍ9r$ٳqqq---+V@Wrϙ۷lذa޽̵<'OD̙3`iiiYv… fΜiaaz~9U^BEA![:KΫ`2"j}(,}3ى1PRR~;! E755eff%i#PlPu XtbV..r\$ݺu =w qrrJ5557""#22rٲeDZZZsE"kݻwO2=z0Eeee=xylnCv'fԩSΟ?kںu+o7o2֭BZu,]ׯ7/{BL87.vL{;;;dB-{eӼgϾ{nZZÙkwܩF~4qsʋ/߿?5͘1cBBB9H$(*****b_E(Î /]&+ʰsM6Mdh晹xqp隚/@ghoʕC=rӧ'LHn|r[[ݻw|۷ ĵ/M[{G 333Gl2^ <{ .m۶IVv{c9gС" 򲲲m۶dB'|2lذ'N\|9::zذai5ny뭷2P zD' W5rX,4h̙30~g͙3g,,,|___{{{Lu?X,vqqA7 ZՏ!XpV&mmm2L*|9f %<ji\L---ϟ?$*tYZZPiKq,]^z荙/[_gY"##٥0,, pظg!-W8+~w¢OSt-TZ9 wmKa;kbٯpaÆeee.nn==tyzz]-]-`P{ݻw;::iF#;r///Z󭢽=//=}RQ>aݕ4oo7nddd766SQQqڵtill(ˇN~C/骪p/"""55W_Esֶܹ 'dx?m645>k><"""!!A*J 6N6K.577_t ]h2dO>ݿÇ;8p .w\9qǁ;.٣hVZ檪1cP駟_=z(((&MݎA}Dhzqqqvvv|ӘR8ҵzjvȈ#._L_WYpV&vJeeeP("##u-]DžW_t tse*_cǎ=|y'b??wy'==?.`sΦ:Ξ=̞~ X"PԩSSSS֬YcaauPKW_,]fX@(`K,]@9DDDR| ` tA544tvv, ѣUV >ϯ^[Bɤ,]Xm_^cYÌx\z6h4(z]Ϛ5 o}ɒ%'O.--X|vTTX,Fjp/m}}Gh͛9RPP3~x~8n999Ν;xL&׭ |YYY<#.(F`޽駟^ZXX0F t!E4?^n\.߱cǂ 6mڄJ}lll]]]BBزU.]:jԨ+W? .f/[ 8F ׊oOnk⎃~j|\j12lƌƍ d|2LPpצJHHpvvvvvF=ΝiӦZMoJJ>cƌ1c|woF=cLGZ[[><::h;#F}Sl!Z8q"55U߽?z~@p߹sjРA˄ \]] Vxٿ8k,PH~FFBZÙ^cǎ%~j2ҿbŊgϞ5jݺuh)W2h"c") s7A"+`Z(}vEEEPP A,N&bbO4Zmv- kV=.s_GPTUUegg:thĉIII7nylGGز.V&\>o޼Lcccq6lP)YV,r^ =゛;){{ĔVWW777#=//JΎi_ oE+oܸV(*22h$'S=\шR/҅~X֭Ӳb/PUU.Ag?r^qǏ[[[rFs9wwwBEpիWbccO>- `keH$#GLNNH$vvv{H$*e,]Ɗo =9k2c23gƍC*`V*֫7|ʕL&W^ &ز;yxLW\={T* ,))![8׊oh"pZYø9XLOO˗0tKW;pX`ꋀ K,]@9` 7y̎>\ܹs>ɻXXS[`BIDAT.KNNg9`ߴEGGd2TcYp9lڵ(]1!9XV,^ۚ0m >n辒 |rnnnnn+W"##hooh߲e˷~_ݛ;wnww rP(;;;;o߾DB>?7n9s7HMMMW '}۶cǐ>C||<@6+^9Guuoakk+J͛G)--upp9s&ʂRLIIYjT*,PSSCLMMM8+޳tf"XƓX\KNC>//qK?߯~rZlV6.BLRTTTZZA)..<{lyyyppɓ?x`JUXXV/s(Ȯs-j5d_ts k,cst/ҥ^a󓔔㣵Օ=y¢M6 ̡i~fwwgϞ a˳D+=)9stww#X,>q4 =R[[[RillF@K;v Rɱi:77iҥ111Z7Nk3B?>|<: p!7773;;:ޒ'0GxjaVVV2CLDVVVQQQu!煛C4 `#'OD̙#|U䖖kzzz:99-\ j̙H為 V~T*=p@vvD"Jv_7r(.n,!_eL`҅ϋV5Я!جq9880_X,k5𦦦LrDB4XJRe2׽J%a+³(.nB]lemEbm J?%H[oE6)))LPeii qww/..wލ+;kkk qQ9=rqvv(*--̋,Wz,]=t ۵n݊,cL˩S!א.'{UUUBJpǏ~Qssfyٲeឞzz,˗/ׯ?l4mv* !t#Ft:__g2u {o} ={lf~|pwgΜz…LF3ϰw~B&###gCڮK ^{ѣG`sK8gΝ;ҥKc_~_~3gD?~޼y 8Dܰau?;<&饗^U*?o7|pO>{ii޽{Bk֬)++_߆>oHCѣ޽{w=j(ޮ[MMM_|ŨQ&Lp]8󟗗e˖>~:T.|Sfs]]͂}voooG}tԩׇFݽ{vٲeC 1g…qqqnݲ.|A޽ʘo``\)A]vͦѣGggl.((ٳ?OG\blضmSxJϼ}޿ߦ-ϣ>Otuuׯ{zzܹ]ysq濥GLa{{F㏡rMid Fs玄VMv9sXFW\ikk\o_0`@PPĉ>>::ѣkoorʴiL&Scc#&#Gx ///~2+gr劈v@wuu7a {_*&.2&&O>_c-[^c~)3uֲI&qV{衇B]]]aÆY~eyzz-W~CDn&&/Sd)WT<'ihnݺe__ߗ^zoYto STϟg*|'Z`00_[f#"",ϖ t/XIbI߾}8.cK.]r%11QDn}";ێCB77wmnn6.\@EGG* z>==7 HaaaFoe, `ԨQjoR/w}oM_ s̭[͛O2DDn~z̙z^/7ng}<3g>Yd ~Iĝk;v؋/2/^|xeeϾ}흖_Z$''?OaaaoyTTT$%%dgg3~s֬YoҮ[>yקNj'Nڊ/7\ɓl6׷*䗈8?OKK3 Dw}P9b$})}DEET襉^c芥+X***b芥)r[WW.UU|JިGzr%gwC,8\i~\ŧ䍖GT[n>&&fذa0999<<!/;vƟ~\ŧ䍖b_?3gjF1?NS!߿)ďtrV,suʹVW*yg'**W{ԫW/"q7*{Ƒr%]1]TTTtRQQy"~<_VU>0"<Tʑc 4?TҎ#5̙3Ruq7*{Ƒ},;XɴdUXP#xGefd2qqq{~g._LP?.<+v*{oz/Kwm֖3FޱcGkk 4?TŞqa+)]jO>1z->yLUQ h|wzLMM]~ӧ}$GlokkKMML;6~{vG!>Jq>J芥r%qHbT*iǑcϼ;_J*>]%oT#ePOʠpQX**bԩSO=J:{4&q1L/ h4O=TccW3?OROW[w,Y{wvҒ%A![Gu?:Qi>]%oMDb4KJJ#lҿtAġtu7K]]۷uMMMjRQuO]zUVݻwРA)))MMMtRQ)Q p„ +VشiTTY!!!w/xЕ+WƌCTTІ ~m˷VgkCCٳgʽv?ʕ+ݻr4&̸477 7ÇG :N秥9Rhcfnf'ꩧ_Z~|?U݄'%%ӧR`0eggO(5rߝJ*ynŒ/7>>>k׮]v},+X**WJXl2Ii~\ŧʞq I!ʠr]1]8]rHHH^^hTrFcllJ:zT͍KNN޿??/&>f|9|DZj:22rnkaaJ999r >#j:-- .g .X0~ v͛7g̘&O| ^Y"A`?_QQbWlqqB̞=… ~^jmm1cIԲî/'**Jׇ?b G+Wd JMM,,,o3~xxxEEիWsrrbbb0~,Y¼uĉ=zdggٳd2͚5kʇ?rmwܱ|=s{L[ZN˗ZB*))aA]t gǎoY_!~&M4vX낂dzqqq֭W֚;wnJJ `ݼy3S\/"{<&&~v}z{ ͛7!.3;;;666//Od2͙3W_PѣG'%%8qb۶m2>.]ܹs3o޼'OVWW曕yc4W\YQQ1jԨ'oynΟ?>Ďt7nO4N+--EA\<0aB|||[[>i*--3fڵkǚL}N6ã׏Xnj/Ç6;v`ߺu !TYYi?yۅ/_<,,޽{UUUxKoil6755 Zf ̓c?>}z߾}zRx׮]?JbRF#姭aܸq̷ӧO7L'qa#SߞyJ)omm ?~e.FN']aժUsQPPȿ@W>vS2hРHEUV-^r_4z7&&&wEEEFծ[N$%%a _8B~Q/]tԩlU*l>vآE͛jB/Bqqq~~_;j:++ .!n:ȑ#`@bȲ eRP®l6>}:33gϞ:.--x?lx>4PSVǿ[L)Sxxx >|ӦM&b_իfff}}=](mՇcǎU111ӧO?%l6׷*4?UFe8\ItRQ(ծ>)=D'eP(ʽvTTTtW R00:db2@!g}R.g9.Ə= yR0% [d)` X" b5 Շ!bY@rCb`@6@!o)b)f&H d|Bɀԇ, ϛu= c2dTe)@fa2@>!V>'sCH|X냗kSnEg(Axs1i%X LM6S'bY@^4.P\Ж3gΔ0 ==]dOOό ݗ}޿?33sȑ#nܸG < M+挏/jZl6gdd+fԩSǏ߲eK\\\bb"~]]]>}ӳV^l9oVVVΟ?ӓ9.?.P94y%}, v[>1, Պd|l P}e,`ٳGR577+ƧN윽φܽ{7887d+~b鳣CV[.)ΝC>|&}m~kk+:8im-+WZr޳gϏ>j*ǷY_2?P atp2 1(1 Ô`7͜ (>䟈eѫW/v h\8brM+ b)@L'j P}Շ,vr] ePOʠpQX**WePPQ5'eP( ePPQ׮X~XJuQ8N]E 3% yb<)X ·Q1"eJ`]W\Ybņ &N({ccc=z֭ѣG[ɓC]E半1ѣG݋YF?~#1 -L#")C+СC;w,..V0`ry”P֊5LsyW#""}h\reEEŨQ&NX__/Wnؼ>>*!1GQ?~2$FQծ{.?O޷o ~m˷VgkCCٳgkU/^l=zƍ2fb5p2 ݴ4半)f}쪫eP@Qhv7*^'bhPEI!ʠrKEJ **WR{ ePtA **SQQSaaJ999fb8''B.?,V###gϞ}]?dk` .\Pg!n޼9cƌN7ydLT]L!D{FFʕ+]C}B QQQz>44L+ր +**,^noo߳gd5k?3>&#63>i8BL9C?u;wX9sc=˗/?v"kOI&;_W+{ud'ksַܹsSRRfŋB=S~V>Zv藷AǏ#E! 'B.?֯9>P][ZZΜ9S^^>`tyg}vuuu[njZl6gdd+P}K7n|Xl.// Ȱ쮡]]] o]|}8|l6رy(} ڜs凷]f޺u !TYYi?뫪+e5_CݻwϦŦ&P[[ly۔FT#]c[[[dg8ߧ\~X }޼ySRBl ɀg UV͙3GV۴SRR.e}p'mGݩVTfرc-7oVqB Gr!b)`WUU1KN: '[d0@֭[g4T]t|#G8`g}r #O.BcL7|M6L&yc!}B ?Z뭷,[b2@߿oرj:&&fϟC(>iP}ʠ^>)=DTT#b\IAAEjXRpA 2(CAAE^b***by"Ka>p)Cʚ`f2@x~GGGnnndd_rr1qD#8x/D)3A*A M!v IDATR#6*3gϞ}…O?Wgeeā@sD`o%e&8)Cʚd20qJJJkjjB.]ǔ/q8` 4^+/[@. a&H()#qf2@xg5kƢhqL 3O_Y#!C :?;sLf ǏϜtׯZ~ruv:xV|͛7nts5wݶmB Źuĉ=<<܌cko9MN?<%Wv^,+V83i  @jԯӧOzzzVVիE0%0e3" ?wWe {LpfHG8kb2&o߾ӦMcUIIo"lLm'+4clL-! G䍈51HY Pq1j&y)$bG`ˎfS-:a ""fB M!"d!b5@qz]TThZu4MRR"d;``@/kWLLp4bSH凔51HY PӧOgffSӥX|֭Z@Ֆ挌W^yEr];翜+l\+ G8kb\c4B™6UUU̕y߿^6j/ۮ}6_.d4cS51.imm864VϏG544={vС=Ovr|a`i1 &HKKTWWT*|رE͛7Ob1HvGaaNtiii#Gj׮/MD,}BL>)kϸ`2e7md{B Շ`0eggO'vEʠpA **]TT$ʠrk,e)OʠpQEwI!ʠr]1]8uttFFF%''߿_D))AZ(~y㌃g2ȃM""gXKgϞ}…O?Wgee2E L и@qg A\le>}BCĠѣ)K |N69|.]-ERT>I x h\8q̛?Lyew08Cӏ !,Yh4%$$DGG˸%`n@ʔ  3ycdx@k׮2dwޭRZXR`ٶ2%D8P}޼A~H 6qx۵Y38%A!!.O>YYY^^^WT>E00L ;ŽY7o 0ćCGBKD6d۷iӘoKJJ<<<3d?Yd0%dPDždՇoRKDضq1N>d21-d,$ɀgJַaP`ƅ3T7"&gxC늈Aa!" ݻwtttQQFj֭h4III2>y"eH%R&))"f\8DDDp9r$7N&Q0㇔AA>ә={tiiiAA"')ÔA ?Aq &<38s)%B2(CAAE>+ʕDTTv,I!ʠ.>)=DTT++'̾\°8F J#RĦ"yЙ} $° ƂFň))532 #|Z8 [lx;ٗw$RˆXP`ިl$)!MX̾ E™x6WN۸q'|ht:]ii)BOt766֎=:))ĉ۶mssw}w6&:;;ccc7*.]Z[[[^^~9)=D'eP(ʽvTTTt,N=gGBKꇔ`.\#j#C raq@B bpbZW!uܻ2/'r- *++cn?`|Ч$釗anqqqd +S17r]rL~H1I )((^gL1o3RVdٲeN{?V}ٮ=~N8m6>fs~Ul$׮]h4/isCrGxL{i0qĄiJd~lg犕ɓg>IG8Rggsjjj V^^nSǚ!bnXCB $?coo3f)Yg>IG8h4\bԨQ'N֬ raS / OfȼyNoݺTx-B C.څy#_L(P f;v0Oו+ 8'ive#@ ?~8mWWhtfe` r]4?PL|BB|̐VooXp HSvI>_wQYYYfe@`n@PQz1I01CU*lѢEjC {;ӏT>j)y_~W^xVT170BP~H1I|B!fȔ)S<<<i&єA]|R{2(GtRQ(ծ>)=D'eP(ʽvTTTt,?T. O8#Î%o, XBXJuQ$,~AL 8QQQz>44L+V˕7i}GGGjjjgggaak˫P94^܄WTTXzjNNNLL B(;;}Ϟ=&i֬YӧOgV%K֮]v'N+W2_Z>bccc=z֭ѣG )wvwG=w^fHcvذa;w\vr! ɓ'-/ƋXbÆ 'NdJz行 ˿7o^JJJbbKv'&&"֬Yv5??-[߿*==S}*bW\XXyA-] VjrS`}l%/;f}JKK,XjiiA1?JLL4LN}6sfyJ[xɓPA bώpO듲&0uС;wCm aDYP g9Ob b&8T~vs|_5d3gΫِ@Fe/)YGg>L'ψ`˕7R@ hJKKF#CHcB6^>lM9O1(( 3ib3"re럔5׮]~Jܝ1Ga~*!e bJX>e~V =۠v!fT>8*+o, RD /VZxbѣ7n<F$ Ĕ,yW bP@|Bq T.WHYIYxYswe bJYvŔA>>)=DTT#b\IAAEjXRpA 2(CAAE^b***b)A? &8Vo6H??%eG` 1"yMY|3^XĠ3 mF3{ .|駾z>++ b;)0| b@/$5!r^9˗;v}Ν;gΜc9 l6_|Yn߾Ơr"v-;wnJJ >?QQQ%%%#^tII&;*g_8kW5 7ofeu_5&?PPNj?qʡv1aP`A0@4+@`Yh4%$$DGG 1 _/@ʦ eqf، oO|@$k6&?k׮2dwƈ_ (?x[BlA,qƅ]NĚpU, &a#`X[[;z褤'Nl۶7p_A<ہ-^[ B\~qa&Wr{2ز7nOX.{=Fc%cl67.%% !jժs禧3!?(((99~#fΜɐ e˖Ç'$$Ν;_~w}xrʘ1cP0Ozgyի9.޽C*;" `'+@8ikkkhh7nM&Iώ H#`A!D B`~ feaӮ< b 8]TThZ-MJJ´KĎ8YiiiP} k@ʦC,R4.P9ky(\ <@+@Hӧ333{)=DTT#b\IAAEjXRpA 2(CAAE^b***b͛3ft'OfM! sgD3?Sb2#By g^àFpu޾g4k֬ӧ[!S.@$Cf21L$dD@b׼9|?/"{xxx 1%8 M3?d -0an/NfJE:hiiA1+B&ԩS"څ~+sÔpx2LdD`aƋb缒X:N߻w`0|W=m anib#8"P~L AZ..0" v0 w^9tʈsWr̙;gW9?VVVΟ?ӳl6xxxxzzfddXv}|޿?33sȑgh8oƍ}M|"?s/ag֭Z@Ֆ挌W^yl6777:u[lKLLdBc&pJHHx7l`7h铞zj!GVEfɵboݺTxzۦ&P[[l| jwaaaݳgbo#(?UUU2o7F;v`.&P~l◖F;ٌd۷iӘxxxXr%z^X6e$''hBs]^aS@ȔXi?#B 7eW0(0ge.]~>ȑ#.܀`څD"bS A1% iqW޽!f]J u0vXZ3}ϋޅBL .@ Cʦ1% &i9)D*F_(oxA{ePOʠpQX**WePPQ5'eP( ePPQ׮؟1p_?&'T|~獈5!Ӄi8ۅhώ{,0(;ZDH%ɏ&D!ez .]?vl/]<;Bx~bŠkgI&de(3cg^^^RRVFcA{P~ĉ۶mhRƻbr2eJllӧ ĉWㅉ/]|B )A78YL ȧdZxqXXFyꩧt8xƠ` N\*sςpHO(bJ͂%K]vڵYYY'N͎ rGXP5~1 07NĦi0|}}7o̔3FOdсg_yv<"FcZV{{{%WMMMUUɓ.X~lf͚:XTTəϖs!h2N:%$-X`̘16 6oIDAT۷Y}O<jjjb 0 ̸p;Ō,Q T*Zx饗]f롡P>gΜL3g)^{xNé[nM8#((hxqN;wh{mB9>>Q9 x6>nݪj ZmYYlxWbtMd6 ~ !O.dz"ɧ Á7A,< !oп B4_%٬e"p !O9`SFp6|ӆဏ/9Dyð 6^(,,t:.???--mȑPE0(Đtb8zW r|Bb#8Ħ08}BP|ҼA,M4 ~~~?Lʠ.>)=DTT#b\IAAEjXRpA 2(CAAE^b***b'LGKkBBlF'F%n|!A.D0( &Eʚ'Q:ѣj+oT4A1bP@l }j+oT"/B h&$f@ AQ_HBzyܹaÆ;CGG'Md)ٱcwdd3ڤyС;w[^zUVݻwРA)))MMM:N߻w`0|W̝F%||!AM&Ӝ9s^}Ո^^^&LXbŦM}||t:ƍ?FJKKB~~~獊h|18]h{gPr1(l|B^ց A޼Q/$qWpXRxbdTaz݁CLG5!OQ8Y RVbz`]]1 >'@.vܧͮ b@F%n|!4Aѽ|R{2(GtRQ(ծ>)=D'eP(ʽvTTTt,6… *ێĠ͍KNN޿?SaAQcccjuddٳ޽+q3d eH!PJExxxEE۫W8aB ٳg_pO?YYY>>> B،(>ϟgXBB?D'a2Ab[(Q|SgPXkܹ)))2(,|K0,IB6iҤcJ1P>!q0LR6Ty _ JKK,X{Af͚:XTTaA0 0yVSSSUU5yd{ ?LqD0 Ty _Oz}LL eàXv!Cظ{nJ D4(?77'!!!%%駟}"f|3ıAP1C\7o|wϟR.AX[[;z褤'Nl۶ ! H/]ܹs~ӑ2D' hƈͼ8|n>,,޽{d&o߾ӦMc-)),Qf`Jhyl6qx Dl #-Gt|1( UV͙3GV;QNE[[[CCøqoOn2OlX2 ſyJ܏ 34>2Aa[(!0(B֭3cA]TThZu4MRRYD0 Pުb}}ҥKN*2NLO @1B:o8"˼+0(LʆOٳNKKK;x !ToVgg~ f'1H R1FH-4>ePt/A **]TT$ʠrk,e)OʠpQEwI!ʠr]1]?=|p 6.`Dj@ '4?3ĦpώX3mabJ^.@$8P$~18ÙO ;P9 !e@A)3(8N'k6a5 1y Pi~3MOkv4?91" f>(A|p̀ . b57}_CL8676h~rc!Dl||pNx6 @BVORi~c q 6&Ry""muuu}IOOZzN9s|6'0233Gɜ5JHHx7}677:u[lKLL̓p~x4?6:>leeemm===8[njZl6gdd+Pyuƍ>H q]^^^陑a٥C惐qa2skF` xY |Zy9 CL(Ħb_f|P.Y8q0'D0(HYb2@q 6O 'lf8|P_wт9 iiiq!6g^za';P9 !e@L cѻb 0"ʃ8cP18bG@*!lh>PEI!ʠrKEJ **WR{ ePtA **SQQ;ZlQ@*3 ͛p7RT>/RP"&}IJMaP@g-1 "b&`M8"dk@PHC"!bb`}0d,\ i1 H}28捈A協Yi}!_*&ľm"sia;@LQ@*M[&o~qcEZ_#ٰ/l< 98$L`3 $̛ bkpcEZ/"R11,a_6A?di?,Чf($oL RT>_Kd_6gb;A1 Z μcP@L l 􋴾~/dXY~gvR5$`Q`gD0(8_~ /;m":hAlN?iQ ID0(50~?D`/e ŠRQ y2(5r_~/aP ePPQ芥r%Q]c)K=|R{2(OʠpQ{튩E+W 3L9,$- O@ڮsFFa/@RvĦ eM@qHIBosQvv3J8Ճv`ٲeqM˻&|>sÆ ;tPmmmNN`ѮsQwVXiÆ |x饗<=="O\TVV~~͛ ,'322j~ѿ/'LJJ:rȡCh9Շ !-څgwOϜ9ßwŠ''K]䍔M[ߚ9xY6kCZ.!Sja;Ad)뜼m  `}aGctCqHI烲Dl }rHuBHm /r!33sȑ̙ ,wŤl }rHutHm |˂5CZNP&LokkcJ^l|B,v7R6o}4xY 8X<߿u:::jWsBv;v,{gkk+:X<߼y3iK 7|w>\{QT6۷mʧN̎c_|rK|PĊy衇>/Qcc#088>>hР4'hx9;396n>Hd2EGG1b߾}G}5 s mסy䇹0sѣB^ZݸqC|`'?裊k׮1k,::g9rȑ#W(,,X:vjkk118b+W^6>CY~ HuZ){냔)HYPrRFePt/A **^1 **WJ!IENDB`emacs-ctable-0.1.2/img/sample-2-1.png0000644000175000017500000003524612275027457016751 0ustar dogslegdogslegPNG  IHDR.|sRGB pHYs+tIME & 1iTXtCommentCreated with GIMPd.e IDATx{ &6oI(Z֍])vTDloV)thۭ(lVEWj|BŖr+Bd7?ffTs9fΜ9sޯ~y<_/+ `0FA/2ܹ#8*hAv@e\*! WH@@٧O _zuܹ5@Av&C\@5@ ~NOO' Fa^|cǎm߾}@SuuuSݻnnnd2YAAɓ'߶?,EH!~)..~ѣsssmllϞ=LV˟>}"C6ERT۷M⒕())y{60֭[nݺ)S )`DEE2lڴ DZZ`566bCo߮APMVUUcaa1}}}}6lPUUvvv.//g0YYY .UPP~ԩS*++ٕȑ#-/,,d\44ĉϗ6mZ{{; ??_GGFϝ;ԩS88v 322z⅛[pp0oG={"+++R:::SYYY___IIIZZZ?$$$pIHHرcoŋbawŧO9r$55wtt<}Ǐ/^sttLPPzkk+N9ϟ7rHOvww0.s455-,,ddd\\\B[[[CCҥKᝤNⅰϟĪ~VQQ1cFEnƍ/H$Ǐw|e3f1cFDDDKKHb? --mҤIX,hhhT--.Jӱ kV{ѣG%%%%%%׬YC/\ \f"HTQQP(ϟ?aBNNNO eeOn ߇o;::*++'NmٍqA"ɑǮsss/_.hxuWWWSSkKڒ++O&%%555988ʲjժm۶O2_O))͛7^{…$sĉgNJJۺuO`7\+.X+h>|߿600TWWÅ gg͛7ٳ'22RMMBVV]۷S(KKKpE@HHBٷoFZx1 ##c͚5 .'Nm:vc9.Bj^ʌ7oL2E@z,hAv@e\TB>4sG6664mȑ Dnj(..vqqQSSSUUٵϮ>36l5jTEE)..7oɓǍÇfKoݺJ߯{WWW;~QFEDDXvcNNǦMfNEE@keyNNLf-kaa?#WUxbSSѣG`kמ>}:%%ŋo]n?[?tz@@K.]D XS|ȑsYXXpRٽ{倴شiSCCCfff[[7mE@[FFƱcn߾ gWܺuk˖-/^}ǏCCCZgYf)**>}V(//왏"qqq7oWWW[[[;vy(W \ uww 駟?L$ٵSeggO<}߿ʕ&MRRR;vlNN\GG'((h޽ ,剉;wtttjjjߟ2e N?} =ӧOg0%L9gYp¬HٳgǍgff\+jժ{>}Xhw}믿RRR֯_SCIIi7oޜ:u*,|[477&ƠqkVI5gk#,_9pI-[&//e 8rĉ~*[xvUUUSL|rjj[YY%'''$$@'ΝP(^^^Ϟ=;./Ytݻ;ǏvNMM}Eppp``ݻwxEGGʚٳ(++~I nHZ1?bs;iaadɒo;.{s Ν+++~z!9rEHo-YޏW|&L} AXƅȳ* !hAv@e\_XAvqA@\3o޼˗ ~?كK\aѢE jjj8W1@ %%%|Lzz:a4MЌV__O 0<&ܽ{͍L&+((888|> =qġC%%%Mkx :u?bl>$11/_`rrrOvxy''y|i"""YYYÇgVRR:qvɒ%t޿g?*++oݺ'&&2ш#ltԨQ=b0N[[D`0fff5dglmm}z5 >g?we˖8qBZZڵkC'Mqϟa{?"x…a޽7 pDG;w) 511Fs__wGWW3aaaW^566vrr~XAQQ4n8Ǐ<-,,\EEe*{///hq(pvڣGbQ|xgGG<0@ ZT*UCCY›T[[O)lVŋ۷oc_W477x𠫫뀏%! !!ARR:?Fzz۷o׮]\c幹,?~<~(Ǐ&&&xzz*((̞=;888))INNn֭zzz$@8eAA!뫨;wqƍ̲ܶe3Έ/KIIm޼y .$Heee'N]|a7Yxqdd/#a455 ZA/vwwzǵk𿒔daaGP vvv .R7n鴴I&]p>of}~m֭p0 vǥVVV3gδbFw܁+++;v;v`?#G>}y˕ GWk?B i4ϱyܸ=랞Ìa%FIIIY`baa!j%hpG dVd0O9d_dt@*KTqqqP KIIB,^A+fddJII3&$$dȰH}}$|իWڎ=C?PHGܶϯ~r ӧYǛD"y&|\UUe555 ?뮮&Q[[ ֖}Yq펎NNN{ $\QQax u?{ekk+T3 ===###uuN g?kkubcc:;;9T#u?_T7+5klذrBXXXyyիWZZZUUns///hTCOOݻ%K.]f :C#""pF\1--ݻwa3f}]^^<޽{iii_G*-[/ZC"dr```aa-[`!W|zWܷo߃(,,dff^x[P_XX UZZZPşw |!EEž}VX1P…7>Wgee=~J۱ziRRңG=|SBU~ccTCuٳgO:u…M6ܶŋ߼y/ :u!-`pl9MMM__ϟcqϟβMAA3i]}l맧?SgӧO/͛s MMMd1&&FUUH$9s`mmmoܸ233'L --MR]]]vYZe9d֬Y Ç̅P&,6H/ ;T#?B=u?rEP"z~G dVD*~Adt@*K U:uݻkN$H8y򤟟gff&H|4+rp?|pyooo// 6ݹU3;vhkkKII1'i䋝"u(*-[ vOJJڻwoyyի/mgqAGEE))ѣGsT,}vJZZZZZȑ#8jwmL|`Q(Ç P4w; ;DDDTTT'$$ܸq}ȯ ϟ,gΜ)//WVVUwtt3߿P+-|3`a+++YY~իWC+O Ho뽼( U+KIIqۜ|aںƾ>[o4`O1G7uttL2/::n ǁۜÇĉa8G}O>4¯hEEE=?}ÍEӧOPoÆ pXyy9oy1̘1CVVBx{{co߾|r999CC;wM6+##;v.\544n*Xx;;;aյk _-,,'쉓]̙3 V{YgggmXׯGzůAvvvhh`=KprTs[755M8QBBիW\qrr>JHHMLLD38p@ZZɓ'KJJ=<<`6QRSSeddF >} -.=q(,,?>|Uv|BfE***'Na󝝺<̊g ///8ܺu wgggV̀`=??~8bĈ7nގ322a7'Mt;w)g@fEW===wVRR:~>|a󝝺J9!ܽ{S23exK }AFF [l٫WDbCCҥKINxwƍ^%JR>155ֆݻ>ؕY[[ggg\-| ?~'ٹ"}@`8vrnbS"~G3M4 3s+s]9Q @&555\.;;u9y8CC۷o[3b TTT(u6JdepDć{122;vlJJ0ի.\H"<=='NR]>bٳg---]]]02ҥKa񲲲v^~`0jkkڼͫVZm6yy)SWTTs%s%QEaaa 3gq2;;u9YlI |;;;2LR}||1`~ղ `͠gڵD"QII? 3ٓ]({{{ `,E!hH/v( R#DB\!ЈOG dVD*~Adt@*_޼y˗/G\5A9s[HT}EEW5>MSS3??{+駟wVQQ1jԨ/=dHp3ᎎHϭǏiii8Ŝ6gݺu X;?ƛ7o>~z ZgqC$8y\|%`8 ͊111IݐH_|tREE7oO8!--}5NTH/Fu߃q>okkψ#ƌSZZ*++Wyy9FKNN>}:,ikk(T\j}LDƲ< 񑝝-S`!$H/ƯTʚ9LNKK~Fϕ9a{߾}k֬¯Ť5!H$fgg766h4(gEcVLMMYbŐ5---RQQ9wƍ#/jiiEFFr~[Tf0f?G*~TEn\T@T@THŏ@جT R +"6.^ T4?oK?+򁎎+V3F^^A*iӦ3ߥlڴIEEݲ'Rs n| =uLNJJjiiCT,=))i|y?;p㛖Vbb"1f9R񳳃^__z|`ܹsn|suuv-[hhhHHH())/]}n)~E]qɒ%??zx@jjjyyyYbvNN{VQQikk>HR=<W[uH_xQ^^bgg?Tmmm ;w]UTTϟt^]]5k0 Tx .$$$WUUY 8*~T3f ݻT\GGj۶m&Lضm~}`ggΑe)1uii߼ySLۺl2:/cXT H˛PL ,,,ܲeKBB9aȑ#oN"RSSI$Hŏ@8eAACrss544zzzJJJN}03gTTTP(Rs<0x>x"\J$d9s?_~d ~GGJDOݏT̊H/ ;ظ  b!@EYE8ZJa?B\AAAT*B,^g)^QQٹ)JynihhJD(>>>={eˆ-=:s PTg/۷]\\$%%?,Q\2yd{{䴶֚ڵk;;;a;455O^]] }}}Ϟ=khhHRCBBoR;3gΌ1DEE}M g#2 g=;X sׯh4s:88:u ~k׮;99͛7 'X-::ҥKw>~ѣ]\\bbbcfjkk+**"""Dkp7/A; u999zzzGGhiif+vc_a!ND,>>$ȵ3g$H ?VH&`kjj '::@ Ɩa&_hϘ>WX(..0##/^¦'JHH\zʕ+NNNX?^~#==}8{,m}"##kjj"""kC8BRRR #//OEEeɒ%VB*!:2'`Dnffihh;///:`0nݺ򷘉P>pf3GRR2::z޼y;wߥy^ +//zSKK "RP"RhdUƍ/Ǐwﺻa:eՄ{>|ҥK{{{uuu׬Y/^QMMǏ/^) yW? "/9uttN<9a„Uhcii)%%n:O>}ĉ`rrrCٍ4 _>N/vCCȵ!Ɂ[lIHH>$ [Z|w^4+~>| OↆKKˑ#GDGGS( wb⯨H$nU666K.h2B+rss544zzzJJJ a9}>}Aj-0tŋ {ŊT*U^^۷H 38vv90LR>>>?숉QUU%d2yΜ9CyfCSSzռ, -B" ~%Mŏ@("R#1+"` bT WD !*z///%%% 3p,U8\!n*~Sn!_o-((رc%ye˖577{{{~|iӦdeeJJJM_TT,&/GRŏSbg111jjj222...;»?bWn8.4\Aݱcx'1%%EOO͛~~~:::PnbbRZZZRRB$\¼u޽$Ν;aIYYy۶mT-VTTkcMM //op+VhkkiSEEȑ#IIIzqƮ\<LR_G.7}ssD~~>|F"2221sFFFuuuuu۷oٕ*"$?UUU𭢢ӧOT1.iii+))+gw\H/ 6_QM]y@\RÌȫv}kH{^fEv*~v|SXX8 xH}T\T"`G W1"R#hVD "\@ WD +"0tUYO={IENDB`emacs-ctable-0.1.2/img/sample-1-1.png0000644000175000017500000000704112275027457016740 0ustar dogslegdogslegPNG  IHDR^,sRGB pHYs+tIME  ;7iTXtCommentCreated with GIMPd.e IDATx{PB@ &P )v(EjEO T'3S-@DiG)>"NTpBPd!H~\T"Jd%{* RT*Ւ%Kb}ׯ}Yx` 3JK/(Z__foo(˖-cX p___ b``k2+0wQəH$D%&&/"VfHg~,kddO?T]]xڐ֋:>">S9hNNNNNNAűX2|P~,,,֭[jժ` h|D"JC&J8qD֖d^zUbX&[l6[WWΔ---33Ф2nii۫9+..677wvvaDY||[***}||A]b6f8i@|䤟H$_gHyy9(333I؜IR笳ɓ$D"%$$(K.QU*ZZZ~w!x ¥K%|@ X}}}}YYYt:}ƍL0 KMMݳgOPPжmdrCCƍ׬Y~EuMq{Q(P|||\b``pR찰0]]]&cX@@e^y啣G.hrV]]HIBBBBBJx}6BCCCCC`>=UG 3bUg s9@` 0g3dAr&Hu0YL>=9ƄK(r8|Ae2k* >?]inn Ȃwtt@=xdwӡ0 #*jhhhOT*>wA/8|u0 ---CCÇDQ' Y}@uttc}Q 9A < '~_X#s''iy L>欵upp)rSb<Vgdjj*<<[(\dtSSSSSS[[[__ߎ''' &gΜaHDRQQaddD(1EYQQ1Zl6dDYcKPcK+ ۽&\T?w.))̼q^zj[[[P>X>۾}AɤL_|͂&YJzF长k_2 %On\ ʧlbb"66v9ȨZd ޳gEӧ_w\Nrv)B SRRB8oBDz y|y&Ys,&WBg8=?\.AXX?/psssvv*++ spD"QzzիWʌ ">, }||n H<? N8::DAr؅CڞTy7p@Bu 0ghTT!Jݱc~%[= Eڦ JG}dffF&<۷{=LYHHX,./////onnPvLG⤦斖NMMG :u?wYVV믿sD;bLMMz vll #˳w}A"{{SA4׵s_X{ׇ tݕJX,Væ\ۣwK${sz{{K"SA BAQO׬yonh{pa~֭k.+>Dp o <>>Ғ$u /^ .384ט_kִ1Ns'?6rJK--T:3Cf 񇒪*koG)_\xN7//AilGv8>zz!ޞ:U?8!??on1ǫLHp>xP_Dv"vt(+É =h{{Çs?kvt5566l%UU)O?E{oĈzϞ0Kpk{cG rwH'g϶1,i[QC=}*|0[fi\Y55[Zh,VzYE r>겳qc/ ##yzF:8$30'e_~b<^o805`蜋_E><>;,-3vNI JKI&Q o ٳ1?_`7|x$%wٓu]W>}/_n<⃣h)xKa/_iSkKADmggPEB)SnѷoYuoސ#i$@O+=9EEK8ħ'"nXXfeaND&Mjmu>ϯŋg5 7WW;{U9.E::ݻWUWL}/HGg%rphihGeSSoXX4UT|pᦕM+7{_g9Bd0Seqq/~a<m{FU%'W&&U2/(W;wʟ=3'ٹJ ?{yd4UTZ.-[]=oi!D57G::f8JIaaQA_N{oPQz$HH#zll 0>a?b&N0ٳYeŋOgDH.Iy8A4"q89~\x|Hs% %K.|  "Ͳص+ݝ[_/f|}9\]֫WݻDӽ6;f+ٵ͕Dv ֳgil,[7=3&3K',WǏ? ͽrE ?{yǏS3!ᆅEKc#Iep6MM޸Q՘}c1 #G4oi}h(,. LJ "#`os`reb%11azi%*O>"g|[rAH۶ _#>"ͱA}oRfB- t¦SWqfSۭ_w` Rfmle6!jdIJl[U)*zvmO?}ܒk=U_EIIH_"1Oc1 eݻw44!kl&_I:S;}:%Eή "̙!I!:!ILSBPuYYܪ*NI ={bc\.a--Pv?Ręh|70!v֬,?66iRb2ynȐ6jed}]X `JJYSSٯ<$_h?ntʔpH4 }}D4oKbb:"!*Å #FFF62>~c-!E$)%*/]D/*xܸˆ[+u'b7ۘff&8Ni)°BT(*J3yMMj=z[Uuzz]vvK}}-vv!Uѝ;f|S$g:p=p j^68QFpĈW٭_up(Y yvv޽=qBwȐRmgg%(nR'=(3}7on]<ѣ= n̿~#)2>"KRTuO5lmӶmPjBaEE˞>ܿ_"Ǘ ;).nu-{lJuܳM={[ZŴ>/W7|xʦM|.o!TzuO*չ5NN~8~|/i^{r%#-!}YŌtܿopعS!aŊ!~>o7.q>C7)KsuhFG돤m[Syy{YlS+gdO!9=pe0.ѭ~WhĢwRe.QyI@4$ؽ{skk [ݙL ""Lϴ߿l._ns:FK]=OOɓ?s&yFH^=h]ag:}^KB@a'vvh砵.;#v#Dv4ޝ>b]CcsOA*?r:Duoi56 ?Gdf>KSSk$( IBʊ*G?扰2.lk[u@,/#zh"N<;ٳZo!יR[YUY'<V]5<-[:OIUEEq!i>(ĉ$ݻqIPād.b->-+$UJvq)"xqmVkV' 41Aѵ ޽[D+L[W@8ݼ׮x!!9/rkk[xy0zɖ "j9]KKdϗ/gRa iAAiݴ eOݽ00 qqI߱Cyا{Gũl>}7nĿlzLK}=B"K/AODΓ{d݋cەEHR?ER7cayHe82U@@YAԹ۷k;9I73.$Txvz0ڭ\b:+䖃Cܼyeϟ9qBy*D*"+0DTHuHUgUm%UY^H[Uq!YSnnsM.&ݼyN۶.*::GǏϻ~ LBW7ya^j22ܲ]N!o[U|;h"?I)< HR?ECnG[̛*y$4*:..4&Z/"j| i"NojH: 5.$Txvz0Z/Y+^0pss\UITXI+j/qש$}Zݻ6.LҺuۃ`rkQM嗄+0 3pwwܽ[?Mi߾Ρ0MM^:¶IM&ohD?[P\m5-(HEW="|,O/¹nk;ԩ׻w??B黹;ki:tif5>H+Oz"D_yuopǝ;,KK";UIIs 'j[ZՖݪ TTt " ":? j@!,5P\`P:1UgZ,A: t @=!,.~ iBHvMGvtdHۡQ@ܿ/TlQE!QYiu/~)T5Wh\:=e/)*8 T .9])*ՃP7Ej"z#~=R㒨vh_Gwe B*)Ɏ!!թJ4^]^Wё& a5i5]) aI߸Acܩ;d"U&R&: 2WBݔHMTZ/ #:.*oGAɿye%\),ZD_S%Q&B4得WEn9844pō$2YG2AD(yH(]$??^8 b|>ae7,,n2$?</ֳgil,^ŏ?~ 7,,ZI~Փ}I''a^^G<z^iMILLX^t&յٸy+ٵ͕$"K:^vEsSV֫Wݮ ͽr1>>[9XW/+z*{(ō$2\GIRIDW%W%U=ª!MӶm2?8rSE I H+iuwSfEERMss BV*_;~K .SܱB:/ZI*L&-5]u2|W^H4אڸE"*@Mht~?IO\earji*a}0**lYd>Lje\M%tbKCnncQf߾HBnܿj\c# UI%S%Fzd+8HӧbcÙ&&EQQ/Wv9~\ ?IT[;0>_ .BHUX$Dj*HMW 벳[Sl1pwװCpv=6&Yvv)$T%TMTDWq{X&NIs%DC A&M+)>>yW<?^07fZ. 糲V͕,IQt`ϤGQ}S(`z0]]hTQ@4i9Hs4i9Hs i9HsrKsOrN&44@:ӕ'5wT&Lp~]ݕ,1 ۝<OR)ݻũϝ;0Ёi.%%~x񢩩igR_o(Z⧹))[>E'w 1BR^uQЁiĉ#^)iLJǥ9 쑦Jw =&S133۰aúu몪`vLyژS_7bS=řܙ/_57tgpkGz…%K_ncccmm^rg^3j[BlGu5o= !R5lrs2Py*ڬ*D3}-[g(QSDLfjZ=`@ւNNvY a :7P-AU;ĉn&&S_o zǎ5PMm̕+t:믿|2N40`rf4⤚N_%]{%͞J]):5>^ɼ6a0~!U՞ڪ*JJ=55?% !jb2o_nuf>jwS#Fq99$n,J[#sjjb ZpqqIII rZ\\}z/'aX ORB̨ ~>Έʫ%$v^d;w9.߭[sdtzCC(1(xrA ݮkOFchS1$+MTe% 񜈈^^,VlA7aa"a<|ѣ m.S477h4%%% 4gjjCz:((9c[ۙQP{9!?ghӭ<<&jW zkiI6> ҭV$磌jk[61l:mU7}311P(0A@iϯNVI(.JUf,'l -q7d|<笙RniW-ϷsC1A|1J]):|̼ȹvv''#GʐOsUi4#zv~tNU%67ܿ?!TPWRT_!]U2gԥN35Islm[vΝο*:B|PvNK_e0 nc9΢h(:_PoA9=~V /5'l0nϨo(3ɿ!LaI])ǏShTwZ/G^*nh0`2f0,{}u`#]ϠтO~QѦ[YpsSSVF6"ͳn=zQ[EƍF Os~˩ʟ@q|K OMMg0qqݺu<~ItD\~=44 4v .}ԙ~ V OO2:/tكVD`r_=8??ʕ+mvvv$ZqF BP _]/}\ֶ CWNs8qb֭b6ݻ*N%Q|"p(ӕ\lq(,MMM, BuaiiiؿRUUe'N0lڵµbfkk}%KlwﶮҥK***>>>={$ooz/_իҥK|>֭[lȑ#xAԆN?Q="!GdG|Hr'}}}wwD BCCM6c w1 55ȑ#saXY^ѣ~jϞ=߿OB4͇$r IDAT:9s&a?3M/PMsAAA666m jjj3Ç? 0,+++ .---̙ܿ9]]6Vd644~y|||8J7Ⱥo߾#G]sssu\\\@@@EEE^^޼y3\FFF|>ΫXrԩSy<^K :I=UUU#l]QG D---}W[---kkL)~s5bL&[nyyy$(('_dyx_`oo]WWp4*i<p Bz0F èTiC]UUUC?~|/s)))9~qlllk)aHQWWQEG D ϰ|?j $ @R%I3KKh={H D;&&F3Z=@{.//ٹ]WҊ=<|... ޯ rD;;;ϛ7oܹAMfffHHgQQW_}&&&3 !TPPPTTaXvv6B\(--MKKj],]_UWW6lKJJ3gB122~Ctt͛[HECG+Q|$GdƏx␐55-[XZZN8?$ŋ555Æ wF"t\[d ۷o?f-,,Ao(TQQxbsxQQQ bX86m266hU>}0 --K%===۴.ðqYZZװ]vСCO4r8ȈFikk>GGG?JD#\QG DƭrΜ9 .SN-))o(/;ðe˖-^?ŮIѴ֓DΝ;/_DOT($WZ%} %''_F_.vvvs]jUWԛ7o/^ :a„ݻwÈwU:\=.[RRɓ֗)D` 3x]vYZZk;v>}:D vs8iҜB9%LO{ )/Txʔ) ,xBp8$eǍf/^ }!S"`9]|77q?.A2\.nzɢED*rT3>";RQchh2%ܠ~ܡHɓ W`͉͟~&atFƛ;~ /1!.͘p0}9B:x]|] g㿛zd][ų&>{.~(?6lhc=z4И4iB(00B_>!! q.544LMMiz„ z^poU3Gihh3?ydl6byzzfdd׶zjMMDmPPƨQI ieݺu իW 277mt]r\ng̘_[SSsٲeH?I%,omu'tttm <ZKM/ȯ(X^>oMcmCz5y:}/*mQ[wO)QR]-!z*ڥI|ݸvx cCQ[ 9;_ncccmmXTT4`%%Ȉ777< wQXXxu77)SS ɓB%**{sΝ3gɓ .9t…QQQwޥP(}]vup?~xCC1cƐ;#jwŊΝ;|pTTȑ#+++B7o /_.GvEr1KC5`0 _>44Ν;G$_:=y={9$xˠͣ${S{w'PtliG<]wߩSښF!wo&s+EhםMM\ fM=^ǭ݂_|4qw O{vUp%R3&R-RJR~f|wzo޼yݺu#[i.}yee}3bƦZE">+f@]jk"d4jGB%MUav%''s^pBHf, By%Bۂ&DH'ɝ2ip… nٲe4<H[wyfd |gɊ USFFC]{Q{G.m]at}=qLz_{S>Mh[PyY]SSKEE=PA~^9FR(cʕ+6l?B}W"\*:|` .;wN/D3NX$D~ٯ]bddpK>Ov +i\X<amXavݻauFL3adȯnTU~O+K-Gߟ9XߦݷvqѮ;[|('wRqԏITyTK,~|v߈/TTTTVVvss#)%{%{/^lbbb z…3f̨jݺuSUU3ḟGFTdMVvHs͊+'^oV… i4,8n&M>qℚwϞ=vZqyf\u0,$$DWWRwڵ#Fxvkee]>}=㬬, QIeƌ|1㢮z~~nj3hРO>~_~'O&h>lذAOOsrrׯ_m(1t'Dq\ruֵ1iii؃?+3Zo/^ܿo|||0]tI8555:tHDwڵ.817mwuu%/cffkG/_"޽{'wY lLL J<'󡱱bOaKC߽{WzoD!Qde4'lllķc'O?֭[II &%Ibcco~=oooBgΝQQQnnnSN'ijjj8jժ69KDAxBXsfO>d2qx$գAGYY900pʔ)۶m;s挊J!Qde;x6Lb7cƌh[YY :͛7ڥ0ǏIjknnhJJJ!訪:iҤ|ccc5+yŸs碢=x ɜ9s&y1 &Nzqݻ===[)**|#n``P__3ܷZ|MVvj$vIٳSRRutt$Bhxx7o 0}zo>:>n8v;p,YҮ[ne2jjjǎc2m~3ݻÇ?}HXflի݇ "E=7C.WZԆEڅO_]az$oD!QdeJիٿ͛qvm&+;9bbڿ]qva&+;9"--MWW͛7bgaaqiY%!CU"/TXj0ehWD9g>{@g? 4i?0 ֭Bx-G( BrwwKO>dX_}J&?N[XXRr1Ɩӡ'''_|!$x.R^xxx(++={#w}Ѻu^:rHgZ^:d{{k׮mٲE400khh?~|mmoYb{DDJE) >}Z[[_rEu<O^흚 -,, 0Bw9xbNׯ}v„ 6.WիWգ .vڂ └]vu]t:}ӦM˗/8qiӘLfBB_-_Ǐرcji7|7ܹsH~٠ ٳg[ttl26{s窪:::/رczw^~!=z'N"fϞ={lE0X @i 9Hs @4^۸q?9B}NZUUU>vkG0իu2|a/.ߟ?~1Nj]mmmP+**l&"֍7>x " ∸1dq@p i@' 5c3444& H(n.((hܸq"@Ҝ|I;g.<#ܝ w?r8s9[[[MM޽{[8ڳg`! MX&ğUr/^X,֦M菩; ޼yb6o,(ٳ ͉Y܍kS)?\7|YTT|ܰaᙙ>Hsd|(3#Fعs'ZVUU 6Z[[cc㨨(s~YlYvv X,VHHȸqJJJ ,' oؘ7I1B6 Y.=~С!??'O7*.]ZbVVVvȑ5k׍aҺIDATÆ߿?55lݺ i\bjjhѢ29zr_~Kggg}}^vMAҽ{w:tɩ_~fff999 i۷߻w[TWW#8Ν;ܦN&(8p`ժUr)999 zş Pdܜ~QUUuҤIO/^!dooܹ(FL&s̙uð'?~!{nOOOU@II`X [pl|ظ{n???:._O233'N?>%%vsdR( Þ?aÆ5559ܧO=z۷Ncǎq8%K}tuuͷnd2Ԏ;d2 k 4lll' 6 d2l#G\p8ʁSLٶmۙ3gTTTgrӧOXbD4>>>EEE![[[y9c``P__ٳy!`͘1#''ƍ4 (2 4A Bv:t(Z.888UVߙ :͞=;%%:::\{"77O>v_zUZZ+Plll^^o`` f٫Wvww2d!˟ \vm޽ϟ?KۛrO`ؼy։ݻϟ??++ͧ/aظdMMMuuӧ"=zTQݜ~+ 8tСC"3@``7i9Hs i9Hs @9Hs:~ًB] Jn9Hs i9Hs @9Hs:ԃA%@4i9Hs@4i9Hs4i9HsH$U`EIENDB`emacs-ctable-0.1.2/img/normal_use.png0000644000175000017500000003126312275027457017332 0ustar dogslegdogslegPNG  IHDR~2AIDATx tUڰ; Y `@083e@L"G978 $!A 2q\5$C [JUt;;9=]{WUT=y[ɖ-[z.ꫯ֘zy6LaDGFIF HKdTiiPXVγu  MaaVbΏiӦ#&t6G|[^ u4#YG\quVK[(HGeGU˾5zhŕqlPl:Zsg#GqWFGK:b[키%ߡ?$i+:\,[z#a;u\ٗ&io8z-:Ny}ONNuxwG1qŊW\O>y>l˵=[=mzz^)tL.=ެclSN$ϾyZؚ:s#nqWq"]s+=/JO}q@N4O?T]VQk(#Fr1'+;'Y;=x9.-/ظtÆ kn$EuK$8 '?ڽ{ھ}!_~wtնD9gGw]Dan~{n⼞G1ѣɓj׮]8G-ExWe?PIGwlql#:8srrrFGywԂZ~T1GHC31JJJJ^nvz1!Lzz~و8 -gϞUڿsj%O졞S:>q7Qt1&qe?+3[ci:q@^VZa7lZ~~]X@*⨥Q}gjݺujƍRQ:|q Rqz߾}ᆱc}={jq 86qO9zcǎ>};uzTbb[eyO.gΜiq7n\Ǝo߮.^hԣٳ?8ޠN}&LSO+1k֬o>nsK;yIEh<ߒ++ԛ7:VeLW۵Ӓg\w6tzj'''>}w%}ܾMHۄ[(i~Gaʕ;Pk5ؑޫT߾}qWe5dGZ0& \f>{iiqtn-xuu)(7覣::=y_]322q/ɻgҖkyToP/%ά.i-==^N =[Nb:GOާ(=}quGoŝ{86lXsJZ#ܛcEGG($B[ &d#Fr%Xᔔ;hvaD=þlmCI؊lI0=[qXG!) c=(̔4ES3b͈Qaq֬YsT~ `CEWFgQmluiVaR_КٜgH4}iOnx#Cđ8"Wűǩ[E3%ؤ6#kg;(u G-A331zG/2n*I8>Ýg-2KZZZK/~ #[]D̚5vjG#ާ J!D"^вd}B5@BmW!H=}{Ifۼl=CGr6wWX"zg{)nqDxsL)V1j{VrY؄hӶ,]SrSj7qT)to˭ҐF kY?2O+-tn2.EQQ1cGO8"crЅ88a35GQRu|qwrrrJ,q߿gш#@pt#?ip?[qDz)\q@q( #cMđ# #ccxT#n8xi5QDհΏ+9g\qg?:HۻQΥq^ #sDŒrͬt'Zl=ǓoQGqiIЦC'.Ώ+뼽i>=pîg%6~qDtPws):O8"Vo9gYǃ_;߯mY%o*Bz{ Ƈ5oDy@GWX5p 9ΟYnndy*2噰(cDίu)#8TvQK+H3ąM0JђF6^|qDGđ‚8Eђ׫uB&D]8"#c6/^,VD# :g-yܸqc##&BHCŋGqDG@FF[oڴ)ObDX+W!(xqDGǠGhxGw?8"##)w %Cl8a„# #8BGGy ☓3f uqF8"#4Nqҿҿʕ+;[\8.1##-86 tqfqDG@XG->:qHcaaǁ8w6Nl_>f…GqDq:V#NK]V?F?h0/t"/v Tyt%6,zïGqDqDk-Z({Li\oTX[jIƂ1 8"8"5ek3֖Y16Z@7裏^il(]&8"8"5{t]ŵ)4EK3hIc:*#1Z o5kϟ=إqWթS ##Xcצ4'|4HN\4%埩6"##XیR׎M Ey#@ewXTƌÇVա#F*fѣG+qq@Fp"qk^jjj^C=}Jb=5vV+/GG 866GqqqDG@G@GGGqDqqDGqDq@GGGqDqDq@_#oҥKc×0@G8|*;;4!,,^CFNNN* =x…m}͛w߭7os{6M7݄86pYר_|۶MhEtto~nպyyy:?xaq8ESgΜ1sε;14[o*;wTڵ3n\gϞUvRwuZhQږ]ırFF3cǎm-hq9{OITTw<8 ~LJzH}GƸ|>.j%%%jFVS'Nw1էO`}SNx@%&&ӧO# |uȔgIyLOOHUIIIFXB}Ǫ}Uneˌtw#GxsQݺuʧ <E,KY)ru7g*M1c[Gın {J &yH?bHBVVqaz'OV;vP.\PK,(ql؛ycKo۶mաC<.Hq-_\vm駟6Qa/;rHUZZΝҼ'QmVW>ݭG1078^xQ͜9ӈpueZ!8%Bm۶J]Vdq$өSy:y1\TTTi.]+WeeeFڻUu7eCkȔSّ2`ee;2.˸9J6ڎ<"w8onşR,7tG+䟢cM48֭[7-[VT Ȟ}4 y= c`k|~;t8Pe]<O7FW7G5vj5)c. qSudž"!$ R_~U.2?~xDiѱcGco߾Jɼ˗/kıEX?t*u׭[wU٩8WlE&q X애( dRqlk.B8{lg}Ef̘1F#%uRRR*I}2#ƍWicǪ۷weϞ=cGtƬ_ Q_ziSo~W1RAKF<7xQV);5G(!N4Qifuu;,"- Q]Y>A8ƪx=T*gRVrʔ)^cC_sGǂcxÆ .۽{U\\۷1n!%K)!7oؒɌW~QђGi'6##cԩSmڏʕ+wܡbccյ^k4TE,IcwԤ|LJ boı)I?1-t$8Jkj)V=^j%Շrrrdž"! 8ZF2o;mڴS\ 7MzqFiYLG 9e9;;{GXX$742Gıű)#ꐸQ ǝٳ7o7Zhı'Qp}hQbs0;ɖMDGq/qn !vWEsu鼫ő~kFyWp,8 c}c(ިhuD#cn3|r`cGrGq C #g(28"#8F+3G1H˴= GqDGhGı18"#8qDq@qDGGGqqqDG@q@GqDG@GqqqDG@G@_~9!''fqDG@qNt(-k222:lo@Gqq8*{̟?O<HqDG@Gp)f$++i=;qDGG܊ٻkqDG@GOlٲWaa⯾j0έg9ߍ`^ZƦK]B[y@-8"# +LaDzu[F,)EqDG@G;Z`FZ"STTJKK:rs("ivs&؅QbWթS(#88:eso96mZ :mY233|yꩧ>}y_E[6dT'?SKבmDGGUK.qoܛf6y Bin0aBБhFBC;a8|;hU8"# c5g֭8Z3EKw oXpagU8"!:q&D84feem@qDGF XK.!ދbK322޲c48rFGıFU8ջwoj3M7݄8z5Fc%<0G{Gđ58 ucǎ'xBm8^7XbfE9 ƿ>wn s&n'Q{Ƿv?uBCyV XQ8s挊/))Q P͛7Ww:q1WEEEmیm}]u]Wc^^R[nQU2Lb{9s<&*l8'͞3yk39wCn<K(4>Dί-(Ν뮊i&LP3R ?~1}ҤIjYTfԌ3LcڊHcvv*++S˗/7$gƿ(K/ԏƣǭ%r̂sq;zO{z%W?{G`G{HqРAj߾}tڵ"(N:+WTpՓO>cV~{QZz:w\<^b X0oټ KQ^;M–?kIQXǥτ{=fQ%6mg0 <kQ2{=zDaJ6P8رxj߾*--Ug˖-ٳgk-GUzRF͛7#5y۷oS5SR1*6"r?ޝr72]y|5ɼIKv'z-c{ugv|=J2N|i UW&FyLmTf>~\m۶b^~G4w? [\͓.-RڵCy,=k֬=#=q:"ȳ%̤ʱ6m ƼI[ٝe;^uzp; (O}F1cƨ\#(uSRR*IǤ$C8(ugΜY'q޽ZlrJEC{ÇcW͋p{E.)_cFyMjf[ފ?M:*cg~??lμfQG߈EϽk<2۷1n!e?1.e|^թ˦MҪZD.C 1ԏ(ݪaz)RW(^m7X{o"!nA^ƦLWs/u\mt@y@?h= 6/07 qiw#_|MXeC2e,++uOUW̙n Sz)#;k[%AغG8x7eѕ1-yS'qH#!FNNo1333:q4\};W;DG!qkMqɽ qD|h73H'mk՟+k 8@a3)YD,:n%Az@Gڋ\z|nW^8!,ILar,-G@ :'rG^]cJu݊2%8!980Yt 8p8:Aqqq@qDkO޽V@q eq5jT7ˍ=["qqqDBWKD^Ο?9r Oԇ8ڥ/--ѣ?qBOOGF=,NOOZ t췖qmq)..o9:9E-}㒓2ۼoXޯEi|X;J>Gk@Gpl-I6llˑ#h-NHI-q\KdڵkNG"o^#jɻ0 g¡CHOO\Ǐy9qD"bZ w#t/r{7ɔF6&f"Eg?1bD'9\%p܉r#8E),@vg>64ZF1u%/ScRQf9 a< ]xᇓ\,q@G3%QA ЊY4/4GF?:F:5z;JOv%:NZq@GnG<ƙBӜYě^'ivqUGM#M1MĘ9iPG< P^TPM_/^2”HwapBYP\qѣGwCJOOoӛRSSo֟XMN0]mZFOK6q!rf-8qDBs# ䷒O|8?o=,Vod^,p0~GnxՏKoFGK:btDPl,?c?) xk큽jHґ(gE?Ant_-tEXtSoftwareby.blooddy.crypto.image.PNG24EncoderIENDB`emacs-ctable-0.1.2/img/sample-1-2.png0000644000175000017500000000677212275027457016753 0ustar dogslegdogslegPNG  IHDR^sRGB pHYs+tIME  8 iiTXtCommentCreated with GIMPd.e cIDATxkPw !D;%*Ldx? HiE>Pdh(TW-C[V¤P H)ˣ4)!aosK6!'ߧ?$s<=VTT JRKKK0 -Zd cZ]]m8ǘ,B `$Q[j@ ^޽{(r-ӨA`hyݺu4JI$bQTvb2w!` beeNXԩSNNN$i˖-uQST|H$Q&~?""⯿"넁///PXZZOL'Ox޽{ Z!Jŕ( - Q*=}֭[t]ILLdSSSZ:P(h4Z~~>~_dZ1%бǏ߽{wpppjjJTNOOj{t}oo/1]'Ԅi)JZZBm۶ .888֪sMyu׬5Չ+s^,  EK@_ 0Hww}w牮^OOO-um`UUU:E'Ot0}5Ԕdu[þZTTTKK/JLוFEcbb*+++++333fff2Lukk`ܹB&V4uU...K,ٽ{ωWT*NGFFuɓ'\.|Æ uuuTK+;ZZZFFF cL~lNLAPj5F Q@` F5XlPw C?j=׫MOOMLLPՏ5T*nhhvZBq W1?3F&8:::Ƽ!H ðQoh4 ȼSSSЏqـH$MMMt S~@@ lll cL~"" (F h;,}Wqc~M98ҏnwp;CЏF~7WN& l@!Q-Ir'I{p3}}}˖-cX~a-)**p8t:f'%%@.s8 zw5ի wBqʕO?J8q&`0:::Tɓ'Rvv61qvrusbr2psΩS"l]lOOϒ6C322Ο?G-gCҐ#Hd2@555UUU}/#GBBB {<{:ix`ԔnP(+Vصkp3GrJ[[T|E J2&&G$vchbٱl[[۠UVqreTo>b/Q;wbPhmmmP&&&PR %00ٹcDuxtA~w?sgzj6 O?M$ɩSnݺtRPf>p܅QNK/s?q0AKڰRLgϞ/ZzzzUUբEu`BX,1~3ʕ+]\\Fn_hPTOOA6@lll&''bAěnc4rA?yS޼R@?%p\ǘ 0j:/}v%ӣG[$) vt=@K L&ҒUVV@ׯ_iBhA$Oo%{gϞJfee qQrwwG L#?/^~4))WՔx<Aҥ9^ڵk:t ??gϊb\r̰d2dōtP(~xzz.]tڵ7o4eeeX,p<<<֬Y]ZZ:pG-))ѣGiiiw hٳgӧOD"͛7ϙT*=w\rrDwwÇ߿3,`0xכ.^8<G* >>x %%%J喗\raDpq33lNp82b zF n&PaF`31&?p.'?Jqqqt:FEEE944EQDU*)Jhhhww7X?oЁ FbbF{ZdddsssYYYYYǏcccqqq\.ŧ8q"777''dpp0,,LT:C:NKKxg}69[UZZ믿+KϞ=0<Fh)))>>>NgΝ7o/tssc2szSTX,ƍI$Ŋgt̾˭0L@---###GGGy쫯-l tP~Q΁BDOUXA` 5o%IENDB`emacs-ctable-0.1.2/img/objects.png0000644000175000017500000007020312275027457016614 0ustar dogslegdogslegPNG  IHDR6pIDATx xe!&@YD\ef@'G.pңG?aP:(3|ꠣ(@EBPT Sڻ}G'En6aÆ} :hk3C_56k; ƹR ap'Rј }pF5] w D$WG7u+Ӳ<"CۀZ~v|{|gdۖ; hr#̽4]"_JdI2\*]MdzI&T>Γ2,D2rD#yoV׭aX,륺%#6#ؒ+ b)A,OINAI: ćL$N7x bzŦX9O"eh\i+ڿ_mg![Bd/e0ătYr߃Mbv#>ѧ؄b7 V[pҒ W3[bi$UOe![ * 0OH\~7>%ay bzlg![=zUZB훖ri}mh@bŽ&gXFbp?bSGjRW4H'6GbJlc;BՍ#P*M iy5lӴv`{ Bu$pv?#aul֜eitEDO8Eϑ+غE[u|n-((L7}a̗y?IU3 C/ TPֽF7{{l0sĉ~Ci4#mJ$za:oϗO$x%ݾ}|H^? :Ae!b%rH$qM%WK<J}nD#+E[dx#ӡg~vh})"ͼ۔Ise^L~+zՕ~zyw_MQG 8%W Ԛn$ں]8Eϑsps: 2kBKVd|G&:˄J>L 5x4s$i錟ه.:ܫWPK)[I'b1+[)l!` &|w&+.wFNCs?#`鰖Fl"GZMſb=WyS%7I 0u12CM&//OIHC Iy|IHd_.᜜4*zY>>ԉ@lڇ (Y{hRc3]:'ZEgTl.3Z9!Ųy)C+XsmCɆć*Z wuTxd?'$ -eh(I抮ZEbhygL%vKMk!6%vvika}SWgnS#Y,2[bO(Q3M$PM3$$b-yhl@l@l@l@l&$IIIUEV=/^ޫ!Clxm,)5gi{ι''\}_֪YgYD Cbq*6nva=֥^5bC D\#o- TD3NlZd 6޽{ݻw[rժU+_~֞={?i$[nVjj5o޼*ԳgO+33zgWu|' .} ~?uWZ:t\ۇ8y?wY[w81w |/$|Jp*t qɓmAp5jn:ٳ￿w}o>[2T6 Sn*&v *6oĈz ԃؔsƽ$R-S.%7 6,6В}Z}]2]t9b=z.-qm۶ԩS'맟~KKK̫6;䓭۷}?Fl*GH =~Ϛ&!Jmfܹygܹ2iiiUiӦ!K|)))8%Ay5fyZzȒK{kZrHR@l||դIYi 6M~gm߿///dddX]t~z_{eu*))2 7OKm hkl 6q-6 6Mi\s b bbVVV_nmذAl@lAl@lAlh@lAlĦ N1CV^ m @lm=lօ^dAl1Q+V؝ehԩ/VY'._j5ڧ~^NKjrժU+_~֞={+] \}Xj-]Al f߾}+ʣ_uA瞳ĽnqqeN[>Νk =c޽{+aԨQֺu̞=ۺou˹MGOGlAl'q"--.qY~}eQ5Xm"^ȑ#xyyyPaҥ"ҭ[-޷77l-\.Q4@l8p#]שztp*Xn6k}v{2ܪU;ϭO>}/]vXDxk_nhFKu-Uw#6M_n?2`/++,n\3-n.F[{g;"6wbwЍTj"J bS -~ꩧ|RGS 6 &6Z1_駟ɲGa]:t5c 8 ?sW^y~t[h_\~ȢY%tn&i+RښvxW?Nk1]"DiMciz]:BzV>Zo[3"6*6Eoڟl܋1zoƫxDѣϑY]íLdRsرhb?ahJmTRM(0KC] t_9xu[[l7iYO?ga8pFya7;7/6=Gz|ƛz`M:2+Ң=%+t^]͚5ks9Ǻk;wLqw$Pvmr-VV~Y{\_>a\mbs)io:|眒:lKӃ{Wl}Y+//r|ǎVϞ=~it: wí~n=\7xr+8[hauzkubb7s&b#f`e_x(Ejq}I$ۘ WbbwI41sss/ T<Δ62do>k֝wY؄5jn:_ٳgWy#\8Ve}Vw/#n)&6϶Cfj%6iiiU FbulⲃN 396 buQPRRRlaP52?zarnTQF-9Skt %D'I$UDMGlڛpi$&I5%oq!6sf5"G%S^f.6Yd=~;a ,~O` 6B-:XeeeUαSw>so߾J _n=c#_|Epcǎ @sȍjrL'9v?Z:?+hRDl2]"~'Xp? Dۿw^k}w\g*gHNܚjx㶁c~[oE+xěd+3qZAkmbIYge|UZRф1++Kϙ/)//keddX]t~z&z;RVve.())}ímγKt}=yvˤIӿy:  qWnڒV,qsi-&I I$tڼys^ ٴ-Al Z&;w-!6M,2w\D/^n)i 6p=@lAlb b1p@lHs 6 6@r ;Alcb b1p@lHs 6 6@r ;Alcb b1Dkb 6@rp@lHs 6 6@r d[&{LHs0gqN 9] xyɶyTq/ b#^ &b Y$60z̝7!61pjF[KhWs\tho s-&'5~sǹo DpA26^zMS^h-ikr-]59y]N~sǹDcB Tt $qJkВϙO䚈8:tQaò\^IKe2mn.((ͽ?$YV2e5L?$їIsd要=1iC4HdБ9\4XeH̓OEBnNU)]ϲ2hРAe^%DJ 1bù?#7&WKA"He"JCuY@M7i%[N%9HIω5X{ƕ.I8"%7)FpLIcRTs,." >+WGg"2&JQD,b;/$$ڥ֮]쀺9zct@Eq @⊍D2Z$ck6(p[jݛjd%e@|r՚`o޼h@xq_|?@fBlH9w Bl~z뭷,\p%K_t\@r ;&FzwEY˖-z7֊+Jb1s Թtm-ᄈ>b1s ԋظoܸў^_-s8w@+ Ci$ǜ; 6Pbsakub1s 4ظJlIhg-6\@r ;@laժUW_ml~=6m;OJJj4y8*t\ran@ p_ر5o<;ްauWZ>lԋifݻΝ;xHl:=,PQ8w؄[o=NbTn *˭o.ѹ馛;wM w^IIնm[;ϟowR٩S'+55+&MuvJw*Kk֬|}" z]t<.GK"-rX=O^*2mcwI$$ǜ;N:.=▎aM+~&\=HNo׮\VVqˇ{%&t܉Mƽ؄@E:@rLr̹>0`qul-Zds9ZTתPbӷo_+//իqˇO>ٞ|wUy?sqBcǶ67Ds=bnihou3<Ӯ0|p{\h=ISѣLl&Nh<-nߵ:6wqGFa]:t]K/m:6gHt8%pMG}z &(**cқ5lj j#ts 6!X`u[֩jWw/_բE 뮳ǃ%NE}$L+VK, Zrjweةx[SӒ 뢋.4؜)MD'q\o $6$ǐnذa:C=jpzZl؜.E:3)fZd%W\rC 1$s'ј} 6Mxqͨ5T43n s0a <CaÆuD}n-(($N8yyy' d(ved!yX>BJK6hРe)L[^7zۿp s~${bobQDsRH17dW`B߿>$pIW* 1Ӥ\{%D %,L$dZ. @F&T_sѧ7<+BHTm#yo@lIlN &\~$[@l'C|6_>uL+8bS6x6@srrp^J2ч؄ܮK J]$mO!9L鐯m`#yo@lF8Vjq4FO$MOO_ i}v 6 7MjM?ٸ 8BUlG"ySW2 ؼjM-碩Gڥ6/~#bt 6$T:<| 89\]$6%6% r'z|ccc^k4׭2'p[d|ih'52Y;>ds&'q_\G)D=z?cI\%6ziip\%6 {=6ؔ ߟdʕ}dy˖-=1߸q#b$D$$H%6[H*ZbَlscmCKLCT'6> 8%6ߟx%iŊ%NL4\_n"UEgǎ֮][˗/Gl&^ZDgI٬9Kp~K޷c֒*K6^‰r Y5~LJ6y睝vEwimq4+Wr(ewޝ͵ <͜93b 6 b /saB rRso#BN4 ؿbҥKߌ s,**z0USG}7رc[; 6 b//>}Z]֝9z̝|KnKl r"*[oVkMRim-qqqoHZvvI懪I,ݥNӦM{TAlxƽ|1 =z%7 Qj4xG}<>x^!>/_nXŽ+WVi`ߟ#ǿD{sDg$Qv4Ev6N<~[0KpJ_| b 6M܉MoGb4z]nTOkiXBi "<򗿌4R7ᶮǻRK4f̘,5d!csAlC;vx Sj!bӸb4SЁho)iŴMxmAl{D?ġm1\mz,sq&|Qlk$M &״iƺ̘{n~DAl_I$bӸbTi1$-z #.5k$oG~xhꤦd֜E 6 bbM"q-fDE9)1 5^Ns97ШPBSXXlРAsKСCYnذaV]/bPbp@lq+8#/yc*GnR͏TsO^f4WhM浵цAlķԦ/*b̘1g7n|s󋎛[:tXbiXPPp o8(:䴒2'p3O˒w$vʼ<{dgg 49b 6 6 b䒜iӦ*,,w TqZӖp󋾛" EWI<"RrZPn't=AߡcM;W8Al@l 9:͘17 n~Q+6'&:%3!JwvJit䚷y-ͼ.)u%yI2Alvӌה_tq.9t戠2%=z]Oǝe 6 bb 6юDSi 7M0NY!6 bb 6ъ?'ŅL׆DRi"40ճ^?O"ehAlAln~ܴU4bcZ>{]btܙ!˾)ӿ7UgZS'vI|$띅 6 6 7?nZ H"6 bo;p@ln~ܴ@lmϭO>:zqvjZJJJD^7N4mկ~e^׺ͳ׹ #6Al~Cҽ{wkU-\NDG 6=ugZ7550bAlI{Z3f̰.j͞=ۺ[,3p@u-bjׯg{駟nk֬ 6Xv=%<-6Ν;̐qwI;]zO:6|p[n֨QlɨnÇSN9N<]vY'tuP]oݺum!r9r-%ʄ ͛IRTTdu]s=Xz/Oqm9t5~x;pá?gbElĦ^9sfbd277~'y jRѣ&]t9bk$Eeζ{m=VϞ=nz׫CNNN;q-ɓk%6?gm oBlAlWlOǑI!6[bMk§rvZ_q_֭/&iiiU WtG!;udk kVVo߾R]Ket_%6q-|7ڈM/#$"1oƌAoΈMf#6EEE/ʤ.i/b#slv{]tE]s5v%m%X¨?}^z+zȺꪫ*v׍3{D7F-kٲe;v%6? ϯ|Auz]v9nZNJdRK}H&'Օ迩W_}}W7mbEĦJ)_s->ӷQɈ3)w}J4|XjܹVvP#FKzdF%K/7em۶cum*Cl:6o*9h  w!6 .6#-8-kΝkD]BiC.I8'6b~zcǎMI1Xg}6Ŧn<5h0)?sѻ r'F.\xᅕu]V߾} E ܏_=7Ħ~Mرc{ul5뮻ξFr*pbXvWm#r7tM8EȂ`iBIPĦq,Fl:Jް |];z hS~~6g.+W\Qrhu1i$>w]sng'b㻤fڛ?cοxe bWB}wm(yYj=JlGy1uMJ=%T4ƈM\=;k9'9ld^z3\~|'tԑ㮟16=u 6q)6ZtfɥӱC׮]+ǵw~cHdlћD#C4R4>%ܵnSLt}.n3&CC=L|^C;^Y// 9ld'>刍>:WcLibGbe:b$(ZYTG}՛iӊNII)ZT45Ɉᅬcm~8n[N]; &Nkd="> ߱c拯FL0iqԩ9UruRLĘ 6 )6Z///nӛ?N?6Ӕ.dDl3Ϋ0`qul-Zds9Hf͕/~QDl:X/%ai|gi_+~HFh}VGYtkPuuBcQB]=C\ꫯ-((ոq:]EEʲ0;'|d<5`P5=?8-Yha~ClNbvvvT]co4$جX^dq?$_ohnQ2he45io\mMRVXX;١,X⋭tpmxMk'RQkI)wG$ק{筅t2ɽ9"6dGۻoرwmX#q>}N~e9HhQ+6ĦoZM<1f̘3 O{'rJF='G#[SqbϞ=OR 'zꩧ6]xM<>IM:{Wxw;tb&Zꮄk\U1TI*7Qtpcp!5-˹kZpa\cdy{&yIR<7xd/^ snzG"6@~ls'q+Gd wյy\-?qkng':Bc>shw?׆6on{55JrE Ʀ'ϒn*m]iL(1zm?'I:M9z5El]lzt &\sbAnz'޾wM9XNiqp͟6kŐ=۷Yc_cqf"6 7FIX9qJĦ!E3c9$.H[zvv\4M;w\oMB&44=FwYYY\cqmݱ3b 6yrcANeS;Dl$8,wMUsKF =cwy&MN$M#6kݻ\cqݯn@l8}.Al숍$AGkgbbsgGV۶mɓ'[AIM_ *Zc^cqmb 6qb4bM@z6Mmn1"M^q@}c7oUט%E 7?nZ 6)6Mtoeeeٕ/rkÆ #̝;׺曣>s^}պxĆb 6a&vb0 6i!6 b=Alঅ 6,6<#pBlĆv 6i!6 6s@lM>ss㦅 6 b# o/r^aaa׀Gxn~ܴAl{bM^cڴiS|6p㦅 6 b# ƔޔO:1͏b 6 p@lh')8Q\\͋o7-AlĪ|W\9룏>zτEyVxΘ1Hh]Whb 6 p@lbMl  +V( TKRDĊnZ b 6P? ԹH=IKKK]vusl8;|ҥrR:4^ ƌYwElAl{b+br՚`o޼ր|iHh꒛Xy BQQ~}njs/hQԔ}ךnZ b 6=;>|0{nqq]И}DlAlf>h`;o:&f5ף>͑(b5&&oxMYP)Ajn3#jAl[q<oaӔ?zJ"TGL/ج9y]Nițf뱺hy,;٥J b 6MܒdνVgeJ,S&WHNq]i&Wj5q 6MM+S$O~"6 b 6]j㒛t\fkpJDq?9jA Al톙鮌a6 hE@Al&1w]zߌhp?)Rrq]D bӠ%6 AlAl]pZ$?ܭ&%W@l|DlAlbAlAlAl&ulAlAlAlAlĦޙ9sfgEAl87̩S.AlAl>Y %4ElAl*ѢE G_|QoWyn>l2SĦܹa>erk Al;qc.F:tթS'k׮]VV׬,k߾}5۷[ݻw6mj7hj*&˜2eʴ뮻Yjݚ8Q&M7Ȋx6My&uQ=Jv9xeU:6G{Mׯ_iZBٕ=СC^zُGz!몫yXų>kuل oF"PZr841OI3v F{D:tDƮ{SR_|I\ @BrWەn%0Z&''rֽi۶-DֱѺ6Ǐ؜s9+b9r!wcUI:n IIjs`>Ws6 6u868qSxꩧ&߬7H<іδn6}ۏ]wu~qmM׮]N3,_n M[ESqVmyj@Q9uӉhFhƘBWn>s8GIԑF}*o[XLG |((&ApI& I3|4 ùFUx:u M4iϘFʸ&F@VŚ@$J$&%Gr^#qw\?|SXU)\ bD ӧOϏ- AǼ b ğԜIk{c_b 6 D،5I㡃^⁙3gv)#5?v9Y,#6 b@T0c#S=R./GdxgܶA{ 9&9I&n(5ǯ Qoen2Ԩ\&Dl܂!ojkfC<LKu FyfĞ`!6$$ǜp4V'޷1;{a6@i-iaDEZI b}ܝ<L ׹ߎck5fԞ$ܤI3I7Q?fs R$O+!6u^t Pl$H6Ǻ R$ nذa$^'[ z;yyy' dHʴd#LQm\A5^9񴻃H7~Ta ُ̰6p ];M%WD22%7ӄ] g̀$NezYn\uΫj?n?yɴ#kTLpFl&\"Aի$}ͷvK}M `TpQ؄)2D^3bùG*;'6Z Cl:\Zђ}8^G"ڏ;Gt/Iҽ:H~䤩HoS޻1>1b/bknwĦ-IIkAlr!9Fl \%6$ZKT$#6@6lX7IւN3w~"@ͬNmYFek.81LKeny,۹͙%ߑ)ngo62J6BIDٗmzP^:rrrZ|>y2o1pZ0"b IJb|%2e(aeJi/E]ݖLϐב2>3t}*roHD,[C0Gs#dǺ}y]2ViFMQISQq+uLOQnQ9$ClbPl:=,PQj%6en,!wKbE{T>&v 0Od>m8p`K.~ٟ)Ku1-~22<=#%JAlIF-pNlmZS}M0`@낂G+OLu{~{Ȝ/j;-3okV4'<}?u"9b 6 $<oQNl$yL*&vd\K,X?b개٧o<$}u1-q r\7fα|sDY$Clf?Kb>BS`nnn['Pex ]VAhfߴ 2)i)N;ύ'>ΥdTj3b3]u[RlCKr!˝Gr ;AlHhq*%Z2JgKqiMuR"Q?c*6%vy*agZc.)ɐ7 yplv,zmE*6׵7鸟yc~TGr ;Al$Zcb b 6$Lj Al8w bb$ClHt\}1Al 0npNV|C}~ʙ'9!6n|ѣG׵kWkժUVRRbb~N$RT]lhĀs46ݻw,XPe… _W 6@ D OYg\uUdw0WcdHD~~sĉ<;;gqKenٞF#Ϲ}1{Z3f̰.j͞=ۺ[,3p@v޽ۺ[VZYcO?ӭR{x͚5:~=aN=u 6 Ē,J\7//:ny}؏ĦD;ޔב2>%V;TN̰[>YKDjre^Y9$>Weq\$ǜ;&ѱÇ[ݺuFeKFu>lr)Ν;]vY'tuF[nuA[~{ȑ#m)Q&L`5oz'"뮻{:S}}r7Ale] ۄpUB$Jԁt᜜V2k4zL%9LS+K \$x$=TBrssG4OѣGWYϡK.֑#Galfzᇭ={7pWy? Z'<Al ;&~&ؔשbj,q˸Sa+((x÷ZՀĩr]aaKs,^F>[3kc}G."k޼y%-Fz&Ol+zȺꪫ*v 7Z*6cMQyFl O?|ֱny-a?8N&zOP+hYn3Od} Q5w:.GV+#;.ݻwgs(\|>{AJsZڵ_C͈#M.UB.ySLڶm[HֱѺ6ǏUk3gv69kYg@ԋqi-ђWk#nTjT6$vH "6%vrTbyڠLSH Ȉ,ls6v%>?+@lrnΡLb4IeԩS4fR֝ +ro߾VFF]²~*_=7ĦѮgƎڹjSJwƴz0u$RkNEMhPII2ivf^i"8gO0$\HbS2ݨOc9FzlzbML7Y%њK7f]in5.6VYY= qc-tctbmݺ$ ?S]/:QnNp%esαc>"tVb9FcFT* -LPbaaA]bӿ#%TV[_} ?$uOSLtKRͲm/ހZB G;txsv)vfmg;~/11㨳 L8eOL?ChI57M NKe]vI;Ă|sIĦ))t:uJB͵4)-\3aZB̕d޼KdKwB.'KҞ戄+Tdf-m=Wyhk]OeMI2\NYn\}>b$I َ wLB w:FbY*7$घ,-Wo 2"GhL.Klb9FO@cc}*%; t7nGlwUw[jF)CU%D"q%:e13d!qD2ѹ7&{ALӄ@< g(Le6 )#قAP +=;sȱ{+OsG"t~U$^g }scxA+JpQXg4B†Q *7"qalNOJ_ G<_ё>_åGZUN/8ytȢO@U'qJl*XZ fBe{+7;@:,BIw?Oǜ*H… ?hNLyidJSd z| NCˣ/!d}3Q6P ڳ}æn+lݚSt4a#l ?*'b"4S/V6O W8WoO1:rZr/bde{y0klk*Yqc_ T%==-~ٚ=.]Q+aSӷ6.^"z6;cwe#0L#&8M+ h[q KKDZsٝ6;w/^r9܏nh]Ѿq3ĝ̆atZf=Wz" (n >4JxPa>J6@Ȯ-Y7u,Zhfܰ 56pJLtu6e aLpikGB:u.^[f%l6a#lya6aa#l 6F l6Fa666a#l6 l65@ l6a#l6a#l6 l6aFaacr(l@ F a#l@Hum=Κ X {  F`M1~&c$}lu[o6sVZŰRq]bŶN|]oE?&{ GVcl牸XG]鶘kv4 m1F?v4_(^zpQSc3q5}%i|,2FwЁ6X5qat4)'#VMϿ_wut↉gR60pX uuуkQdQOC&l.W'N t9P#b[nA^Gm&lg˳IƨX QTga`~7%[6Ԗy8OR&fr6ȶqGG5/ \M4Zb:Xl[eLҏl7i%[1a656uw&SΗ6^7h,_aCaSwWaaaa#l@ l@ l@ l@669s挒6pæqq)|e_C5.˼TSh6` 67xcwn*=ꫯ{7>}z2ĉXs=̞=;iiiI6m#C# wy';)S$3gL^z ~pȑd޼yɴiӒUV gy&Jc}[o5ٱc6?x|+V$O<{I___~ϗ裏&'O<MꫯիΊ%Ke˖% XǏ/=W^)1rlٲt"ьĬYsΕ>[թ_^C?{췖[{ѝ/x߾}e_O5鮻J,Xlݺ59uS`Ӳ⺝3fa_p_ Jv]:rEDF\3HVxGI}56?pٿW{[7N]|M1 m9tP2y҅G&sOё~|PQdw}I{{&"GVhM\w]߫=ܹt78m]؄ AA6aaa#l aaaF6  6 l  ytm=Ϊ*YWw1a6ݟ9&Z1ԈVmٿY@6~ ?iamoWn/l |lXG]鶻&W#mhKPb2>=^v },fצ3ўtL;/i$-tEXtSoftwareby.blooddy.crypto.image.PNG24EncoderIENDB`emacs-ctable-0.1.2/img/async-wrapper.png0000644000175000017500000021432412275027457017762 0ustar dogslegdogslegPNG  IHDRQ>(bIDATx G6.1a@ LX@$ #fA@,D&a7@[T"!$rIYH"%6r *`$?l3V>g\̩oU=U}ݻoĔTVV|K7^O|/^;Ĕ5>`bbbbJuzC讀C/.xT#_rwVLX6TIf͝~uoks}mLH0M;}?u.~/O"{?uigҞjҧܩ"k>vOh#}{i 铦EO]LQOI韑21e\rԖ#.j ,﾿}椮t3{7~occܽoLLwӝO{wqw昰tüPw s~}Ւ2+e;Wz_`eפ$64S$q%,9',T~*sln?A~W凯K\Z 1a'8}ʕ&2kq+C;7]LLUcӝw u!&& aY’ACXֻ..oy/\_  IabbH S ;VX>s(y;c >R͚5=S^^n?Aa!τe-7]Ka#,0$%'/&~獰<9rw_q̙?Aag²Ƈ<KKuZֺosAd+\MFX:Xf %²JƇ<w^_vX K KOw rSX~ZXN<<'4[o5G%[ڵk;1/,y7cǎpիW|#V|7wy^W^ 64rѣ9p㈲9өS;;v0wMAAg>c^}U8.?nݺ}O_$,;wws_Mf6olׯ/}74_WdVXaIX NJ: -[,%30‘*s>8qrO:o޼ϰ+"U폙mm%eaɐh`HJ~; IaHJV KXD&n[*%;|8l޽)--_j3rϞ=+v;,ŋS8۴o^a~),߇m>}Zp+:wMd6*:}1m%eTaɐ!)3$!)9#,;&3+ 7t̬b@aÆ hѢV4^ MX~̞=[-|4osaS'?Ii;8GpG_8ND*? K ˨’!) ă!);̐a | 3?ZUpfE1+ӧzJ}H~?ACϞ=ɓ'͂ G?8‘LO~K/9zefřzjN\[xױo߾q4q|P1P)eɀS-%eTaɐ CRw!) I9a a_yq+ B9 {=/"KnYַ%30Xv/ K K:fA~䗵'/Vrϐ_gH %%A_%dH CO~1QԩSUf\U??'/aɐ 0b>5$%%AAPX22[E_A%%%"/ K ’2dHJ~O_EaIAPXRXFAn{)?-|frv/&ˏ_A`~Uz~69fh?D~’’  (,3QX21QXRXA%%2%CR2D~1_Ǧ nnЇL|+{ZX^p|;1 407tg?k<?ɓ'f͚ի.]W1楗^˛0a|N_W3YGKA&_/,9 8& Hӿ Sn]f'LI돛c9U:uG>bV\i6mdڶmkZjeY5ɓ'M˖-MaaOjlbWs7 O_%:LR/%%%2cۯ.(0bs my? }7MڵMiii^~e׿U9+og=cO /~pOPM~1U%(,),),_L#,7'WG>w}I CKkM>ݪѰR9w\{LQR2o*Ea;’!)L `H S估P S㿔-ro3kC ]zQTeKחeӹsg3rH1~ ח'x"VѨQ#ٚvq8LSU2w%CR2Iyag?5jɣ..?O>fj._ȏ ~&qƩ _s׮]dϞ=Vۑ#GݻΝ;c=&Ack&_!,! Iaʏ?J*eB[b}n3 u(N6oj{3gzJXNޖ0)ӟ)bJ~QX憰dH  Iaʏy [ym{tj:uJr-^g?={vFmذKϷv[=fѢEf͚ٳpOSU27%CR8~pdH S~q#?sGޏ6-Rۗ 6ԫWO&?Sx_=W؂s5kȖ֡CΏW N֭[s='Ο?z[И3SU27%CR8gH S~䅰D|Ǩ>hnssےca?y"Oy%eh>1y\bѣ͇?as{6ݻ7߳g oCz'%3 7p<K.5/^4~bJ~QX憰dH : Iaʏ~QX憰dH :0GH %%DaIaɐ01$D~1QXRXfdH C”!)%,P eL eb"r7?VqU| ’’’bLVD6Vh.O_EaIaIaD~1QXRX'e K/0$!LCR(,), "(,),%CR~"/ K ۷KޢꫯrlP3f0wq馛>ǎ mom6m*6Wz^?<æq6N:ɫSE-_W3by7l,oJEM5y_׬˧/[Lڭzx`,ON8a $o"nj#\H?4z-#[ӵkJMEMj?m? (,~+,'8G_EaypnFs&%,O.ൾ8êĴnڼ;9s뮻gKp+w!>C &d/jHw}^qm~RoS?pw.:jن LYYyPm<Ӗ_<۷Dc3uԔCOVZ-ZG}TSY#?ZV(i__Qv|MD%%3 +c%spqXa p2|EMڎ_ɶOE%%3 +ducpQɓ0o Za;o(_V-SO>{Mf+"܀Lqq셎JC9ݣG>* > ]| r-?vիW旃c RdMb%i…I 8۔/ ´ iŏ6/  ICRBH RbVF Md1(86ؾ}c`ӄôiL˖-㜺 .U<[N>BD-: j\|YV8(u"jժI9)yQ6-?:|͚5.ؚAq+DQdVW^ ˆo޳?*?Z ʷ)_ٶƯdߦ~/*(,sGX2$% IaH CR !)JZJ .]T>'L`5 YfV$@][nO>2ӰhѢ"jכck[k'xfϞ=fʕ2Ӯ];ٖ̙3؊Ή/8={k׮}A<30M8Q>ToS(m__ɶV~Z2w%CRWQ0$%h|ɖ KBkvw-YDYA nÆ ͕+WbayfС@>2Yq)!8G2߫~vZ\-_2߹s\sak~a֨sΥa_Vpv?Z)_i_0_V̽Kx!) IaH CRr!$RPV/*¡C>p} p$̙w=f3]7ܳ_yaȐ!s)/,^X^xQ-_;?l}L܎/J pPT~ϯ~5jd͛go:u\fSW$(sʈvI@*?~YGÿN2ź|Z> GV?;(\#;acV"7n-#G+?Z_ZOk__Q_+ ?E%%CRt0$Ŏ_ I0$%BRBXs=qX5j X}r݃Ux,\Փ&MK1Nf/R5|XlI&M̰a*G33謰<+ViʧC: l,ܹsc?H6[g>f*gSƍ;2p-j?hZOk__Qv|M’’!)`H CR2? IV? -((0SNӧz0[n5%%%ur [jeZh!ޱcGܫ|8r:tq c(~e?E\ۯ_ñ޽{9=nT_ˇS@^̖3gΔePp.:r'9cVٯG?lÆ L&?ͯOʯ;(_ ~j’’’ /aտmZU(V p;8x8^[lz3S du,X'ȑۏYN&m69/ٳߦ|g9& 3~`U8naiSD?tz $c̥/(ߦAMƟ()VA~WD;Vfm*FO<)3`3 .L:6kLP#7`S\\wk쇀w=  +|Q5Z?Llk/~`Ue)VZCu\gڷw^zJ/(ߦ o)V!,0$!)w? 4[ z>֨QCi&}t| W^[p}ͺu .*ӕb?Ȍ/_6ƨNV؉agX{cZDaiS>7MfZlslf͚9D |&??amZm7  IaH CRI+a 'gϞ2x5 ]j p ]t|N0jL?H'NyZcWaa֭ӧ4,ZR֯_oׯ/vZV7{1+WٔvY|caux֬YqAQ(-- UAgΜ|\W۔?6l5'O6aɐ0$!) IPsYW(qp***ޅF\ ,Wz-+͛77C q̢ƩSpqO|k׮r0 ~XdtFgKXږKÆ ͕+W_/5$S;wÇg=?-?&_㏖%OaIa_1 IaHJX~0$!)3’!)"0 s=?t4p7}cۨQ#3o޼8{ԩgP>f#,7Aܳ?C 1;wN[ŋe[ŋUÔϯC3~{!8mˇA?sI~[3[g+?-?Lmdž_A?hӦaɐ0$!) I1Vg[HJ^KB !Мtq EgӅ˛7o6mڴـ|Gpڵ>mܸQ9RvHb!cǎ23{>\f3lVRa?N !XHU>b/M2~-žu-v?ݽ/_n8`U> ]ٯK?ʄGVFԞlk5hS+V%,0$!)²nݺ+N6m/FF>V%g`mƍ'vN4Qb7 paj?V0ҤI3lذ Q10 bŊ~-wޞM+9&Mvf=z,ܹslWkkmZaɐ0$!)aCR7BRBXڂgEd/(,3WX2$!)A5CRضOPXRXEPXRX3$!) IaH CRr?$’’ /’2!) IaH CR!)%, 2/A_VaPw (,),), "ȯ’?hp (,),9NE_$ (,TXA%g:EPXRXF Io (,,[4mT^4n،=\tڱA%Ϙ1C{e;f˫ڵkN:Ɂ&O\MQLWx#MRxܜT8q 4H޴%3Fpe[?Zk۷Ow_>w֚5k^+P_6oq&׵kķjzV~|ZhMVXNE_Y!,nj6l`0cΚmN>ԫWO·JJJL֭QСٱc;9t=&ZÑ+qʀk뗲|o߾r>-((0SNsG+v}T5x ǁ8ȝ{wVzA6v~i§UVEj{*o͜9SAzV~|ZhOaIaIaIA~eLL Yv8\<(ߖ-[i,X!?rHqt;jd?eM۶m~Ξ=9H\=F۶mk)v}fϞ-%%%A_r޽rVzA'OVlW5k&,Q0`)..FXVppݣG>*> `|/;VVHldʗx}hFX`Zj [?k[?5~y-\0*^wd)Plen K0$!) IaHJ䍰D֬YS'V-b5jԐoڴIQŠ.\g [&O>Gjժs LdVL8p:aǺu"{7h n#~–ZRXN6ʹl2T򇭟 իWc? ۷o֦|۷oF8N~0G^+V>~lW?2%CR0$%CRFX9sFKvaժU2ҥKsl„ K˅>}ȌĢEbpocV\)ڵc׺Fw'gϞ2r5 S>k*a 'Eiii ^+/!t p'N(Ayaof͊)Qʯf|W?22$!) IaHO䍰tcΝRV+K.Vq܎XQQ24o :4pύk3˫~7N:%)SYΝ ]?*,װaCsʕza_/Ct66dpVa^_+_Z ӿ(,GX2$!) IaH CRƗl IKa-mZÌ-hUCIs;ϟf]!|}qcŲ-ŋUn b:w|>th8~b~lt0 9s$U~km_ZiԨ7o^ԩ#eʇEFQ)PŒ/^OEa’!) I?CR+bHJ~䅰@ a+O{= {7olڴi#%Ck׮1z#:V/6[QCAE`7ʍkEPhkig;-*qFr:rHi ǩY|9pߨ۔_+V?AMaɐ0$!) I퐔=zLfΝ7iDzQ#_Β{Ķ41bNR̎,7fVX7SUcUnҤ6lX\q*֭b!hS?ZkӻwoU-u&MV?Z|/q O5pusoaJAzOZ),3ƒ!) II}1$%BRBXڂgEd/(,GX2$!)ɴCRX>dVH %%A_%eZ9CR0$%CR(,), "(,),0$!)Q’  (,s+,'x "ȯ KPw (,),), "ȯ’?hp (,),9NE_$ (,TXA%g:EPXRXF Io (,-J5mS磒g̘!uٽz2ǎ l ƙ8իW7]v }d|x5#MWxcܡTkE7ϟWe7nԮ]tIul2y.x=\t)%xsLk֬L)iZ8q 4HԆש3FӞQXr+,'x "ȯ3gΔ\ ӧz9[n5%%%ur jʴhB^cǎW?Y5D-Il_~)싚oT!T:t y8wyA8;lڰa)++j:m=Qq2r'q1aٽ{saG՟v!JpKiצWΗfԩ)k_>Aoc%%%A_Up0KFy,=:{lٲJ{$_CD'q۶mrٳg#kE7ΦZ`A, 9rijN(g!jM.]bCT'ՏA={cΡʉMkbum۶)k_>A'QXRXRXE_Y%,1+^^=XO<)3p+ .'kMD)w=  + Q5k~6kL#>`S\\y8޽{e;ի=;j>WVءQeG~-LcǎTf ö}!, /CR? ؾ}F 3lAQӦM fA, W^ "۷oo֭[g}(kZ> X|8pza#5_/jVQ.\ 8V|lI<}tU5kuyW*L6ʹl2NZjRO0*?ZhkFٴǣAq+Q7z~a?>  ICR!) IaH eY@̮Ϛ5+6caժU ]t|`„ a& BL8Qij֯_oׯ/'ʅk[kEͷߨCnݺ>}ȌעE̙32^| l 64W\ lǵkiO^뿚pm*_ܹsl Kki>  I10$!)s IQ[ AU(xc{СCr=<7|= 64j̛7/ΉSST5|2dܹsJ5懩dlXg%7[SK&A9s[xlxbt'K>j?6éh_k۶}-,0$!) I z~6估{ϺӨ˗/7P N.o޼ٴiFfW;Ak׮<,cƍeȑ#og*|Nt ꓪ|;(6~t\ċtQfPwÇ"g (.. @Lſ+N2zth]_|1 (DFM#VsSҾAiϷߦ죰}aɐ`0$!) IaHJ)$%煥L,;5Jwd5oJ7n[ qBG_Ca%M41Æ NEf_|~ڏ hsrӍY=z,ܹsh;ϟ4ig1uY+Vth]޽{{x;|Z^C襢}5kkGa’!)`H CRڟi!)y',T%xA~/  I CR0$L I$򋠰L[?gH0$!) IɅ K K  K J I1)/CRdBHJF K ma ^ +3x;XUA~QXRXRXE_%8A~QXRXr$ (,IAPXf$  K:t 򋠰$,A~QX*Wfԩy̙rVŸg:Կ ? qOOac’’A_QQP8OCxmٲ%%2 , ?r(gT8d۶mΞ='SǛ.]X_2K}mʟ:՟m}U5y d 3_ ~̞=[)n/a>Qo;~$?'’’A_3򺱱FO<)3 8Yfr*bq+V^])Áw=  +@Z~`Zj/ldv'bرVհ]`(IȯT?DQ⇭L[a/  IaH CRIaYZ5SF PT>( .\lڴIŒ>&Ͽp8keVYj֬)DzJd@U <N#Bرn:5߶|MfZl':/ldvAq+@۷o( ~2YLW*_㇍0_^㇍6G2ö(,sKX2$!) IaH CRr;$%/%={+W۴kΪBWZ% .]*&LHBӭ[7ӧOQXhQ=Μ9#aUVʲϬ_8蘸ֽQ˷)㤂쥥|~Z\zSf]eϬYb+F?hQJ3_Q߆0_ކ6یOv-aɐݟ!) I CR'$%/e"֮]+P^^V(]#AQQYpaceyfС|p?ύ;wTO:%fKpO):~Æ ͕+W/LAY}Ν;|ɒ%"$mE*,Re+jaaƏdGIftڕ!) IaH CR!)y),/^,۲.^V((c{СC`pC= @X9g'7[VC 1;wʷ-:>gΜPg[>k?lcoo=ETLW?[a7$s-?#L(,sKX2$!)~gH CRa# !)y!,…^SLP4.]߼yiӦ%;߮]`vǎef>\f+}t\~Dh'[ A`J۔@:tgS>ا_G0>*?.׺;́RŸg2ֿ p/<@~hhr ?4mƏ(CenK0$% IaHJ?4aȐ I aT6]WX9|,;5JꇳY뭷B=Khu N9fGCb̝;׳C~M41Æ 7XʇgcIj?|5k[+*ڟZ?zyO5~hkG27aH CRX Ik_0$%fBHJ^K[, eK0$CRۗ!) I2$’’ /’2!) IaH CR!)A~i IaH CRI(aIAA~WZ%fBU??'/$ " Kq!/8IA~QXRaIAtAaIaIX2$%'/ހ#ș3իW7]vvlP3f0wq.W^رcVśοYf|gٲeiӦbWƍѣͥKbv~iQmW/ m7 Kn/A_5SVL-;v|Onի'mݺՔ֭[ˁp)q'8Zpv-=7l`S.Τ[xu=/=pjõ·#ۡCù:8jӌ(^rWfԩa̙rj_6ʧ՟~/-?ԿMjCoEFaIaIaIA~etV*fɵ sY|8D<(Ӗ-[gK.3iѯ qIm&={Vp6ς bpq͑#G?{lshmc'7m۶ Bpر %, ˏig~ίT?J۴򥚿~SXRXRXE_/,Rp¤*fO<)3PVUVPDݻW^:%2HѣGcaaDplfW8 0űg~?h`رV`\ Ï|ig~ίT?J۴mo*˗ ]Oa’!) IaH CR!)9/,^#aKZͺu*[jԨ!ߴi\G 3h6miٲe Y 'q~jW3`6)S;QZ>pqVJcӧTKt ĭl߾]V#1px G-_k_~{O?v~Qߦ}mSU2%CR0$%CRr^X:>fPh' 2ch"?$DϞ=eڵkY.̚5+OoӾ6*Mi?jkkE-,0$+!)hJ7=Xw Ѣ"kÆ ͕+WsNyÇ=׮]+jSB[EСCcCAJ^ caYsΝ;|ɒ% 9+,QӾ?|?}_aQv|~MGL-,۝CCR Iya 4j̛7/ SNuc4{!!$7=|߭8h9sbym/^,.^Xi1dӹsg|Yncegd O3ۻA۶x/Li훍J7J?Fa’!) I #,doHJ^KlV7ʖ#G߉{>Nfo޼ٴiFf;WAk׮ 2 U`F}lʔ)քj?Njr`&Fy:v(33e6YʧُgaO9D ܈ǏK{AkjMjio6ˆQGSQ2%CRbϐ0$%CRBXǍg֭+odB;NMbYzԨQXꀳ~A .1'Mi_=h ,ܹs,Ɗ+p>TcvѶ|{\1tVa|++_k_ZV>v~iQߨ_z  IaHM>CR!)y!,m"_cɐ3$Ů}|=CR>$’’ /’2!) IaH CR!)A~i IaH CRI(aIA',9KA~U۽ U| ’’’ /,, ’’$A_EaIBp2K%AAPXqO_%e$aɐ 2xFk֬rlP3f̐Ü:^zcǎc߾}rM-[f6mjWn7nlFm.]dE.1booxqfڵk'Z߶ |-/_k?Z ^ +% Y6!gV:}tS^=9f֭ĴnZDR(r\ 629,K3gK+q~ώ;~YСq.}=8L:5q޽{9UnW_z~Om©UVEjkԳUQ'Zߦ`̙r6O㇖ok?%%%A_ Ǐ7]tP8X`ppAqζlbٳgK9C{ݎk28pSPDm69ٳj>u,Xˇk9"'bum۶q[(_7tyb~2__P_k?}|X`*˷i?ZA~W K[VءZ^76vɓ'R|0r\޽lՕb?P|ѣϰJϰ8XYfr,båc QX aXh0_ҵp´O&+kmnwp_ME6SX涰dH CRGHJ iӦ-[zcc56m-cpT"+?:d͚5z8q/_љADp!`Ǻu|… cǖӧO{>O VaU&r]Wh[5~zի[a_cuT~^cs'L۷j$~8ePMio/),s_X2$!) IaH CR#$%%D 6+2V@ߥKh &D?s\~z!n*~<~☣ZV9-v}EUx={LֵkbC߿ٳǬ\Rfڵkg-lT?Da0€=qDi{'*Տv'Lyrbu֬Yqs~|aMa[a 0$~CRcHJ兤䕰q6lh\baT*+** }qM;wk>\:uJ:_kXi޼:th{\0 LsڱvZyy0ޫ~S>5~zq lH/jd"R?ў~Zض_Xd:+QOoӿ),GX2$!)A`HJ8aɐOa !9sÍ-^UCIC@PaYvt`Ȑ!sVM³+;̙Ӊg6YŋeŋN~{o*'6+_F̼y:uTXZ?ȯT/lx=?fbK (d|Ma?’!) IaH CR!)y#, A:64b͛M6md6!q5qvS|8qith avc [P(M>:6ر,Ç$gE{}N:~cO=ʎюn8eծ7jh[8Usƍ%qȑ~2_V?GS(/_8p@ͷi?Zֿ),KX2$!) IaH CRr7$%/%*'MzeQF Ylwg ,+RnsYEߣG!8f"0 3wPj?V=&MaÆ4cU8pZ1{nݺGBckŊq3Zjk}kʇǍ'7ѐt~EV?GG kC˷2?%CRtW2$%*CROU͊ x~A_D2%CR]U Il~a!)BaIaIAaIa~ΐ0$!) I K K  K ˴s0$!) IaHJd$  ?ǒA~WZ%5ޞTUE_A~Wda48E_' "/ KCY*, ’3"(,),# KE_ Ο?/mܸ]ԩHjؠg̘!uz2ǎz6\w~͚5k;x5#MTx\'N0 7iucƌ7HU6iZkؿo>t kx]5$^ڵU ?-[f6m*ѣGK.5_lԃo>Z-_>-+tǼUVEf|*|~6l0eeer0^G jV/3gΔMPmG럨ݻǝcZD۔ߖ?~OaIaIaIA~e6 , ͑#G sY|8|<(oٲ%){Əot"n' ضmGsY;qu m۶"DT5l>gϞ-Q=v|+M?%%%A_56kLh#<`S\\\Y_76vPɓ'`W|jՊ  :zh;X%gXرcedžQ쳱?5ئ~+E .C-?ػwl\zu/XUvՠm>^3jӞo;>j?maɐ0$!) I.\g ز-[OPlQQӦMr=1؃xa1m4Ӳe˘ӆΊU˗/ n: ĭ0~>_/^mzjlPo>vý VfMDlMe~2l߾]VUXOPJjդga>Qg|Q+_?en K0$!) IaHJ䅰} MnL>}d&aѢEVj*!(H.]* &Led4כcb^[ DSf2]VikiA~?6;2f00a@8qi6Μ9#傭`K*/bm֬Yq핌 j ~grJl׮uDmG^),s[X2$!) IgH CRr%$%%f֛7on {=Khһ_TTd 64W\?u pLN;Ν;g= c6 ~pvاᇃ;w=>d~2_K,Y",Ǧ:Xv<\mӞf|Ua2%CR=!) IMA3=|!2,Cz8n`}[q@9s62t9i!':ەe}AZkv+52͋k:ul[Nobe:0c[/^,^/^O*|ц^),s[X2$!) IaH CRr?$%/V,cǎ2k.3|pQ+6~Ta͛M6md6(q5qD`k:&:6HtVe`5o?^)kԿfga9D CNn[57n([G);5~`5_lu48́>ZDL >N:eIo;>?l2?%CRD9ĐIya `iD,>#8PeQFIc"Ζ7bPy?=i$O0Y&MaÆu#[kiZki{g&ƍ'7n&?=z 6bjܹqf|_[HTZh31F`rŊq3ZD|ߖ?Fa’!) II IaHmt J"^~QXfdH CRi?0$%3BR(,), "(,),0$!)BaIaIAaIa~ΐ0$!)CRr;$%%AAPX枰/A_Uo/*T~"/ K K K +"/ K KA~% Ap! ,AAaIǙ?A~%CR~"/ Kx5#MMx#[w~Κ5k|`ܯ7Q' ƙCxN׮]SfUķi峩?~_?_ 8 +c%AbرônϺBOnի'mݺՔ=p }48L:>UVEl~wTD/Ug̙rTʧ=?lߩ_Zi:~QXRX1# 2BX`Pl۶MΓ9{ZX]8D<(ܲe 8ȶm۶s0p@9SȆQKǛ.]>31Nбߨק}yDGTR*UQJ… bU`UMg0ڿ/LŽ_#,0$!)d0KeqԠ֭[V(pըQCi&ʌ?&,mAf*E͚5s8 !گ~iӦ-[JU&@٦~^WlDo޳nGTR>۷oJp$ K|ö?;WKvaɐ0$!) IMe{ڍ7(UVҥKsc„ ֳgOөS'sڵP9sƼ][nVH} 'd.--^O67{1+W٘vYo룶O;B3e^~e3qD D/J`̚5+4=?Li#*/-_enK0$!) ICRr#$%/SN A[^Y~TTTj{"fU/9w\R9عs>|췱O˟={iذr|k׮ߨGmd.v7~*G*K},Y"(,Ô?*/ Sd/  IaH CRFHJ^ KC 1;wP1,Cp=Bt8~ή}%[װP+D3g5Z/mu/^L~^l$FyN:6M?Re_2;0{~;jmSd/  IaH CRh+[BRBXbA%c+U Ec)͛76mlIjڵ+.`c{ ?nes=' [ԛn5lwѢE9/c<ʎ=l2euF>jD}>8`ƍeȑ#ogЉjUEi\iwáʧ=_k?Qiu̍K0$!) IaHJ䅰Īf4ib jS6j(!6gIM6,ʛܨ[Ez!w01w8B~>-;ͤI|;jP>f0X{׬X"nGߨGmG7N7aPsDO*վT4?4Gw՟Ven7D!) I *CR˗Eag;QXf #p`yUA~QX扰C9=D\g7Ow)(enfӹc{󇗶Iޛ'.(,?s'Mn5=JߛmSҶu*K~ tϯ,ra g[r~"/ <]t{kMšݰ旒oUV?$PXb%CmK/S K'QXRXRXRXE_Y),[\Px?aiU[o0tӇdE7nngr}{< @Ε7S'3wNf-ug|y͚5۾>!?Ǿ3SM;KٺS敽OM=Mjd5Lhz-Dz?o%_6>ԿٿyR6 w~^qrۖ sڲb{gަcCǟ 2/%߬_OXj[ev-s&p4{rV;6:{RcGV-Nr><|fD@\²A=- w*>Z= b?[izpD8?E@)ߴ(k~"j_]?[ +;{Jwh֚_ASXRXRXc߾}W^?_}ո<̘1q榛n;vڱϟ7?iܸ]ԩyc'O0}t׿Ռ1B?!6_qXۚ5kʯٿl2ӴiSC=\tɚ'N0 2r͘1com=?j83qyoyGw-vt5TO+J|}6???m'%_]!pIVXjc>4/lf.XV\LVSG~fV>x]Y K6VygϜ&ojcyxp MkRjW{C_dm_Je^9GXwUM~VKa2z+,qm2[a 6L({~Mz̓O>injJJJL֭;c^8:t0;v0/y衇tO8fݻw7/R,st3/؀kg<3q &iVg6l0eee7vӖ<۷ygc=f ԩSG3g4wuWa ЪU+ӢE 裏3 lOlWgsZ4|??AaIaKW߹ܝ6a YA1fBxnJcD 'VXeF.{ݼ}yܿv$|ؒZ<oVM4mrk}&,1]a9rW?뗰^#u?,B4’’2gϖ![Y0z K K K;x9>ļɓ'/aHsl\|4k̼+0m3t=z4VqVhDVZ橧rLڿw^َzj+~xaر”L~SQ?Ϊ["?QXbj…/a lǏ6kiABXO K KY:"%,slhf@ǽ&!6m?J,:Ą;&/b K$P~/}E_\' KOE%< //K&kLLgx#{[4Dl0k?J#3ot K K ː`ըQCi&ٲG +0… abcKӧ35xb a*˗)Sz^niӦ-[ƉڏU5ks–d%6h n&۷j%V-իWc1m؂i߾}a?a ~;|SY a]o?),),%x,!wko$[xY ^'yy3e1 ,b$d/ >TL<>#$l{SOV>gx E,[pm jl61~3>ZKG?S z1%%eEo?gXep-1燹 Xh޼:th3ftύk3C]8ul{0jذrJsO;w5NxL9Ν odgɒ%;+<Q=QЊ SLWg_~xٯ٧kn%^LLL(,),),+:>x?hW:$߃96a!ϟ^Ёc/?U#,e ˎȖ<$ >&&OXEƘy),cC8)q7o6mڴnŲڵ+nGDtQ^ցÇ @{C(-jn7ei?7[~C(>S#a+&}Ə*҄jLcD.g4 !,!5 F3W,.aX ᅧağ_/2eVeU}9q _䋸q㾬{#/E*7n0k?rW^Ο?]Aj}v?ڿl_~?M~I>?C_z>H, (%=ꅅ%9 RkgĒIJ =z8FtH?=zvxp~ .&X_ ہP"N76dNƚ5k6O!*UfM5bĈ3L6<4iIڵ^f8o޼5n}+WαD^߯~p7NGi[H$uIK!/'Kw}HK% %eҥE^tl틠}ybIAqCbIbI :AbIA$$tEоH, @Bn_'h_Ē  $d*l.틠}XA$$$t A"$ H,I,I,t Ē  H,$… uA3gT+WVŋ\~=nZJժUK)RDUVMedd/^WO4}:A4hzQ=w:\eƍ;7oT ЇWPA3F ?yl:H?R?~Fe+Uj׮ڿ+5}tUN=;$H!Dd/zIS9z%JK'͏4~I>~7iĒ  H,X©*\jݺ#ƌ|jӦMj߾}yqݻwqmn۶M?~\]V)SF;kb6mԑ#GԡC}1ڽ{wX#qfee 8P[ݻW\R,YRM:51ҥ:uTW7H'ߪU+ӧOaÆi`Fe˖Z#ه?Id/zIf͚jԨXOi/'KOKXAe9sv233OpEChŋ={EH_H~]r%$PbƏ:t}EtiӦaQM?Ot}… CXCgW^՟HSD?%I%}؟_QIFD#_4md|R>$AAb4k,%bbs#۪SN" 8yN۰a~僃^v-t Q:\CGăT%Ԏ;9vXXǤD̿i$AAbYS ._\_ǚ &M{[n:wl$e,i:n'͏iMhO&o>%AAXhb&TvGAMpa˗/S;wT<8hcV*Uׯ>|7oߺu˓~OT="SÇ]C $hd;lNj~I|yɾ%.]T+BXjm̏'ZS,}K  ,#tQr%RΐZ$<0dվ}{OHڱs:Jq3cɒ%:mq?@Dd׬ OVϟeˆ̫~هL V_lxا4ne~L#=?~̿>%AAX:z |H~( lk.դIpH-رcC Rp$sʇ'1OXT@̦RpNq$p|οQ pr ]c0vvn:eʔ0Wҏ[G mVoݎ9RocEk(Ro߮Sgk%Hwӟ|n_R~q <^Z?>.)ͯ̏4~I>^}_jĒ  H,X1`ѣGk !ah볰$Jt/wUBd)g˶M~!k֬F @mȄI&9\r#`3Avl\f͚q$H'yG N?nlcܸqZ8ߵH~KKv2?7 >G$T/>I/ObIATʁ@)(%(eSa"/:6I~Eо}Bhs۶mjڵL2ڙ;5RW˖-maL_|vVc3֬YFQh0W]tQN ;%%p7ѯ~n?~G};~oVZ{O>  *șڷ[In^OĒH.hbHT*[$ 2"uKAXΙ3G8FF_hڴ Q8.^ٳg(ە+W9QP_+يT:E:f_,?~С|#Q':Z v" Xi^rn<~G};xW.\ 6^j<ڷi[X #O4nNcusRm:vÈ5L$AXZF,#1vXa0qlNz|mթS'} o%Aw]78yN'۰aC"-JDG|4vl;#}v"נAT;ܹS@&>;ú:Tځ8#E99>kT^~m,TtRMQ,27oncѿ>;|b?^^-YD=>\haRM^OËIJzyd9Dd"E_67%2;,o\"-A0b N.>[렰}?Qpr #8]v&M J"Cvر:qbRŰ& 2^DPJ-aWXm߾]䥧u|~IRpNWѷ\^Z?^7L`/Ɓt)SKCkrn>~G};~oD,m۶zfȑz{8ڷK5}uĒ]RĚKH> $(IJGoܭ(@r86Ho=zv(q[`a'DꫯEթY1bD3(vf;H%7o^%=n8=_'գm0iҤ3߂ɮجY&ls^_qӯtn?~G};~ogY _ Pt|~[~_M'$rTnR'ڪk~C%%AD B^tl틠}X^H1 :[;i#UJyG)k-I, H,I, EоH,cG?3ct_-$^@>Ē ĒĒ h_IJ ǙQ'ڝRfJ|_GZ?@;v-bYĒ "K  $e%i.~g{( ^ jR1%|+1ҽO&HOHrw3~rZ!3s^ÿv/#cOH{(_JS&7D^%vr.?A"h_$gb(E~1Vŷ_;]]i(9P'fT )>~H6byo"%PwoU L vC8D?\~EI, `Kl A"$b")ڋC/?-H,['PjJ?7R2E8n^F  M!"hw2OOҖ'/}zz"o}!C!Z( V9}}u/t~"` EstiN4>'>}G}4hzq=W_ӧ:uyر~&OnСz7DI;$پLh<~X5JTR]vjoiMp=jl}$$A.?xvSi(MfOJD;K *"EmJ+bY(<6?V:ьςi?7:O7j}}OHް*PX܈Ad^ȅ}@2!^e{]wjj(.6 _`ҏ/XHy5%$%… ֭[;:FS۶mSǏWk׮UeʔΜc3c U|yi&VUƍջwD^|vVBά,#zݫV\J,N#p$۴i9:ӧq=Fe˖6K.ԩSbwlz7DIKK?diժ~i5l0M,&'ھ7}5kQF6'هFk__K"_6>ΤW.egVFK+Zi'acMFn܃4ƈ%M&F o"twOG 9_b(෾U2x흍t{-/kw>IhB,9v6_ n 3ǗI,O*{E!WD=RdcX[wb9gNffc^z96pfŋ={䌌?^uX'M6\Pb=`Er(&䈚֛/Q_z7M/ٗj_IWzupg;yUD۷Wu/D=AH%$~na:2$DHfq.xȕJ:Jym7ǤߨG K ZґQ{f~>!Es"%;k3HrXFSt=7WʈXz]V~{|η]>~K@1%Ub93]} R~KX&}I'Uڵչs4ׯj֬>¤>~HE)t~eH,jv5Ry}AP z\L-dJ,{""(v5%?v R&GO_ٜ2F tAIZ$mM_ؘtLbIh%vs79J,XbNܫWg-ZTΝ:% &"XӦMS 6 9^%*UAIX]@gp B-[o޼ C oٲ3B q` R֛/KgbNK7%&KF={"hGJݻws¾M~<*z%&־#$b& 7'F,}TA*&8Zbʟ"&ʻiu)Xz#ZL\Ү'Cy%5Ibm~2`J!wvMnN,͏3mt1%RnrX޻wO_ߺu^չsg#yi/_cMԄ <' VXSNln!o߾1+jkpo-έr4,Q8q5gϪ'Nu-Zכ/KgbG/S%}IgbnݺZ={ԛ,^8ls¾M7`DWgϞ&)פ}#$|K?xΨt$f?k~0r\VF-[څXFF΁ ߋaIX$;}Ӵ_bsqύ69{$҃ =nIv7@_!"񝌴5s,uZe S(cK"#@&OED,ubiÇ._,:6E(6h8;J*מF}r<|8U1߹sGyF,#ڃ;LF/͛un27_"7?z1}>ɟ%͟d֫WO >͝;׳p* \3S1dվ}{U燍lٲa2’%KtύMM/ZKc|D?YK?72վE\Æ5R}Nط6Ir dzi__K/ĺR[&R_]bQ/QJIJ)|O.>MG>}6"r m߻LǛmú$}DK'#Ȍ{D'##]v&=݌u#F*TYiLN2?>IY(069"N,M܈%ހc8phᳵN ޠ [{9b]v&M ,"[ձcDz95SU~op\ܸq#GΎ'B*Mk&Ro߮Sg:27N8ez%zI~I}I'}IfHBm۶՛@#G vs¾w~EW^Ο?odnkҾXq! [c8ct#IXp%"H-~%ȩ[uABuHnPD^yXeڵ^ۃ7H7oE[GίC%Ѿ ;5b-KÑ&MIr9 j֬FvI=7nIsi9&f!H=8e͚5aHݿdR$ɾ$}͟dei†4oiMߢJa2nKGbIA ,x϶;ZPǷc/Z.JJ`#k Gn /:8QSq#l`J%"#gr\ǘmU:&K:fA"h_$yX=ǚT"F(xCp-%d`zFʧ9־J3!?9 nh$<1AXX틠}XuRho#Uo$~%Ej0:Y=XW"`x ` $ KˆT:o@$5Mǯ|I#6A2Y%J+֊Vr |F,a-v]~EоH, #bˏ?Dy}VάY)}DAX>H0R>H:-RY2H*4XK |B, ['h_Ē0"o>fSb)YA,K׃$*\6X_*,A*ή$ H,I, EоH, @#X F"? K|A(?B(I* H,8'h_IJAR gΜQݻwׇs_p;8VZǺ3gʕ+ŋ~_YFh'OvxСCsLO4};H4hzq=W_ӧ:u"E;D~M#Z=sOq8<eƍ;?VFRժUSJRڵS7t~Ǐ_2ۗ<V1o###Cx>y0`*]P3fŤޤ7%̉X Em"V)”W KC,\jݺc=k,UF rƌ|jӦMj߾}yqݻwVեKuԩPqr%?6mڨ#GC{c\ǿQF~jٲe;pXM'//ه[˗/ݻ u|/++KDUVӧOaÆid9ϒ|>ڿ4n_>ӶmڵkU2e͚ڇ4?T{V{U+WT%KTSN57M~>sb|$_bY8\K` %Ǎ̙X9eB," ŋu{1Q߭N_r%̉8RSSzDFILg"Ծd&cUBW.\ bv^*g:?nM/Kҿ/Wz2i~"צMKKKOGbI!vrPAXzcHz'btԣG۷oN:kpB߭+?w]5D zEEm|&K؇I=)QڱcGUڵչs4ׯj֬>>At~L77d/I~?;N<97l`l^ ;vR4MDĒC. %Ae܈u4Q )dE߹sN#7XdīPBZBc}F!J+)[l߼yZ3I߲eK]g2>I~I,M6lFZ={ h)wu|&cjؿ4n_YQbŊ:,žZRhGXKBAXƍXbcD/fϞzX_^; ?w:N0AID>8gϞU'NP֭ik-Z[+jkpo-ѭAX5p'N߃$Mڏ+Vd{ݺuUΝUϞ='/hIbڿ4l_z,XLԾ@u7zzIH, 2ҥK5Q{%][zՠA1/Yؼyέ[.wѩp>&>K&pZOK~%֒URE~:t zÇ!y%y/o2ڗD<߹|hC&;IH, 2%6H]>JɱHt钾paפgaɒ%: C Q۷7Z?~Yl٨L7mD &֦͝;7:6=5g|؇_7K"?"u۞n>L 5 dڤ޴hI~}70 b"f8m۶z3nȑz+%6>k%}IkOsdه}x>@r zI~>=>K  Lb٣GLj΅^Saߍ=Z.Ckpx4HvN }YqDɏR5kT#F;Ѥm7N+WNrr`'$%>4ia7R(o*lc*ه_/Kҿk׮z!d@*yJ!©D̤^D~7LH, 2ie$?A"h_$AAbIbInjO$AAXX틠}XAD&AAbIA$%6l幅Oо%AAX&L-/EbIA$$t:AbIA%%Aо%AAPbyս{w} &OpC;6X3sLUreUxq׍ey0`>ܽB j̘1y VRjREQժUSŋqsӧ*--M}M 4H=yĸꫯU:u8;vhox8;ƍ2?R\5J5m4jWzcw|W\ srTu}"uNQXίǫ:m~LQ82E:n7џdnr"h_4zOo2~>ڷI=%AAX&btԣG۷oN:kxoxxر:'Ot 61;>8k¢dT Rh"c#q*QڱcGB'Z=>H'DM'ٿܟï_/Kn>ڷi$AAb'eBTѢE&1ql;wꔿBIz?"+U.\-JPX1=>8^cw|XW@&8p!-[7oބ֜!lR9I? L6M5lPq?x jXKE/m[o:~/~MOn>ڷI, 2ݻwٳgՉ'Ժuta-k!]|5S&Ler?HFnTv۷oݻǵuVsqqL+VuX{Slnh֚>}ZM8Q1я+V}~ccf͚ٳg"Nvd~Mܶxԛ_Ӿ$%k^'$(K! XFb{n"J`': 40J4WÇm>|XyeߎqmT3*;h6XVJOKj"aE"kW%m?7IoNӯ}ObIfN?$ A,,YΞ?.:6ؖ)l(Kt?p ,|?6A3:)HC$įcw|N2dj߾q}ժUÜزeˆdE ֮͝;7VMXRduhK,~UoW۾ا6iĒ HeVV ._~F2 6pZgkrxbN6e7X)ڵK5iDoP cǎyذa?5M9rƍQj-)$k*5]z슊T۷tٚ?I?&^/VۏիW_ҟdMO~K7y+٧%ob~ۤ}Ke_bA"$AXGoĭppf/ѣGkm }vJ[>ʕsеkW6 !͛7q_;>D% [͚5Ո#`4Gƍz!p~Ni~~դIbԿToS*4~IKO~K㗞?I}m__d~۴Ē \%"~#Gf)@.I, Lc'h_Ē Ç0·Ē K:6t A"$ RH,Ke Ē K:6t A"$IJ0H!hdE-tX |G, Ē ‰e? S*"jYĒ J,-v]~EоH, "X~b9vJGD-I, ȷĒ[~EоH, XbW8R$AXұOо%AbIbI%%'h_AbI$AX&+|… q(U:uT"ETǎ9s\*^eAjv@UuUZ5^xi|ӧ*--M}GA'OKsa)lܸHǏQFiݗ*UJkg2~?wɓ'gСC}IwM5`UtiUB5f=&cb_JIw?'Oz~Ē eXr H,pªu֎3Fe˖Ciq1c*_ڴiڷoj޼jܸz'9g͚jԨͱC۶mSǏWk׮UeʔΦ-?B6mԑ#Gpo>}%Kտ|R޽;QQiժ;}6l&0s,/͟~W.]ԩSBʼn&}I7p@ջwow^rJUdI5uT]'ُ}I+'M?_Ke\᮰AXaΜ9LGz_rl!ŋu?{1op-}c^z/_r%RSSzI|?~xաC#ի 㞫W/͟%|>1>"æMFn?n+'߫?xĒ \"L%2gh3޴/Z(&94GϷoV:u0#]ɓ'u:ن ǗH!'{ڵ5D!p  ^ҿ$W%J;v RF޽k|4~߯MeB3/ؗ4@ZgJ"ncR/ͯ$Oy_K $2Hm=aĉ3'96ׯN?|r}k&L ʄM={v<ݽ{[n:wbW~2UXQ;M:K"?HևX“nݺZ={ԛ,^~ѿ={V8qB[N=h"Oؗ_2uMo[Gd?n&+g7e^~?Ē H, Ljbh);XFw9q?޲ -]T;V@JE>seүvܹsGD,K"?VRE~X~DNի- 14~Ǣ/ca{nJz2?l$Էo_-Çvzi~ؗMO~C$AbIeRKjժjaNPٲe9w EK.~ف,Xv `DnRԸtX_0dվ}{z7KʏI ;w' ڲBdx}9+\dN|yۗްd7~K}EDDڟX%A$IA,6pZ0gkv5D*uYzz)ZX)ڵK5iDoPMcǎcWK1szjuÏ R q$;i| 몐5oɴ^ҿ$H-Dp#&"Jw۶mf0ȑ##VPc_ま/t)S;i_&=p&ʍ7ǭ^_$K'7%$K H,X%ڟqƩrCZNcѣGk }}vBZ*& Nh]vkA*ټy(IKz@5k#Fd;T/'գ}0i$GKaNȄ( _"lxbz4~߯%f;Ḧ́n̚5kռysոqc;#jJ:tH>}Z 6L;p,ǫK.ԩSbw6jHկ__-[LBfh6mTǸm|&#G_̟Yf5jds۶mSǏWk׮Ueʔ:1z%!O_~>%y?#$K H,XΙ3G5"cU8BpEϞ={^Zpa3+Րew#aE:"(9!+WTu}d|/o2~~n޼J,/Wz2ޏ}IH3_4JKvϗoObI$AX:`B,/#Կ;ُ4K.Վt̟>/_X/ m^_~>'Kj__^_K $2OKaܹs=HG.]jʕ+Ղ ®a |׾f9\oaǒ%KtZU걛cٲeE! ڷos}Lh{HM^R)H-YcǾ$$y~7yX/}~{y~I, Ē \'xc*¡u$mYN[Nȏ5kT#FvơTo2>/ُ4v8J'ߵkWAJy۷o7y>ؗܶ?i|~%2ѿ#$K H,X yѱO/EbI$AXX1#%AXAXX틠}X$AyXA%AXAbXbC[ 틠}X%A$%Sa 틠}X%A$$t:AbI$A$$A"h_$%AD%gΜQݻwׇs_p!jԨQZjTR]v5vlg̙rʪx⺟ׯSG! RO< >}S*Rرcn:th/Km~M~i|}ݼyS 0@.]ZUPA3FZVg7}Of~;]jU zK ,(srfX)*\jݺcGUVСCjذaځ;nϘ1C/_^mڴI۷O5o\5nX{H>8|mڴQ ZۧO0bҨQ#U~}l2cץKuԩPqrsK~^_WI5p@ջwow^rJUdI5uԸ˗ja<ƙe4>}I[/'ٟ$_N~!ӶmڵkU2e=$Abɤ݁ X $aVHg sdff::VիWW . }c]zUtl@Cdŋ={]r%8RSSg+r쎞TDo2>I~m~M~I9n6m>LU#_^/i|~M~k9H_s=%AX+BguZd@ s’,~7DT?2 M~ɓ:{Æ I, 2% K^- ;.B0 4|v)eH{cEܹS" :D^z68Us˖-͛75KHqC-[uvdzPBZQBD2/՛o:>2X7}Yi*U E8?;M6l"L//ۗ4>&hx~(|b} hK=%AX9φY4~B Fa?̘"XݺuΝ;={M$/^lج_^s/_cф ںuX&X {T68h?6wZ$ћ8q@RΞ=N8֭[ZhKi~/HJn>o߾5֯~@~mŊ3l_֛'>׾sݻE[XgK=%AX;++ɓ'k$xSDpOy;~,Yr[a˘X"rS^=5|5DD8m" d~ sNs6.N2Z7oܺu+R//;ͯi|VyaLvUTQ_6ˋ%o?7wn</_TObI'eX$ e _}dTyvJ dFKFK+-Kl@k֚3+k 1mf]tI`Q!Cۇ>WZU͟??-[6O;,Yʞ?{&T4^~; Ds)X6w\ojyվ7ϔXk߹"eՓXDeC25H,_~尔*}o&Ē * {QߍF,,ic' ƆR5T۶mfǎS#G1 8׮]I&zȷ)3ЇyĚ%BbMd]'}vr?[p 6HD)Sə[KRn3/?qTUnܸ@jοdyݾO߯}'Z GA.9R=%AC,?g\eґ3; UAy훉$EbIu`ioƇe&=zp< ފ"aN8L2iSvAcaG'DڥaOav`k}Dj֬F@;n8U\9}H9d=|u }cc5k88[KR4/O/̫S GL4'_^/i|~%$k߉o׮]Ly慭!I, €XٳbQٜ"%~kN7*,EbI#bY&H.)ʫ~$?A"h_$XY~-$Zчd"܈e\^۹sgK[52zIbI#ed:|@bIbI/EbI-b~eVV֓9ygBID, G#IH.waK"˯LK:fA"h_$DrKb%Ǐ~UdH) ;vJɾ[enĒ L"bIA$@,p_bi/MDjfEMw ϯ$.I,H,M-b !?s.?A"h_$L,:\M} X%:틠}XXv"ZPѣGWSĒ '̙3{` .=}TqAԓ'O9s\*^qOo.]ԩSBqOzI޽{{+W%KS95RW˖-}a6՟m۶a#=vZUL=H̚5KըQ#q/O߯}jJ}i5l0M"d/;H)Ϳ?Kf/_Tw+ h'++h|~I, Œ|t>DDш\^ú\wIbIXF`ҥrɒ%WXΙ3GrJ8p@ CD!pYxnsϞ=3(8f~g_I?M6 }"%NQBm?WzÌ(H7џ$_^Zpa3wդ/;xyͿ!4^wCsk?KbIY[fM}GM}uIA,ӳO?T;wCe0I7(|0aZ`:qDʟ*2@$A,3K' $]v-t oq o%&4GϷoV:u+IbW~I&긏;VG@, Rh">pIηaÆlQQ=x Hbk_VkVΝD_~Yfd/SE4nn~1%J;vϭx/%Aæx@~rŊ LڏXǨʕ+ Փˢ ZƕXZVt!9n8tRc/sR:e_J~ZQF| ?h u^d@!*z ndBp^ @_lX~W4V/!uhܹZ>lDpc% F@9#E;];j 40N?Gb{nQ%InS߾}> pj/[8|DĊD/w_4fԫWO >:z$%q 4^M~ ɸf_skfߐ'/ٗ^/ϗ{џE_E*Neby>b_K!jXKrСu=RyXZ1{*P`5`cۿ[ԶAro?ÍkX`9|~&:v"/1IO5 gϟY77>oֳ^RxKI>L\ߠ{ѯbθ@ )8Lc܈%ސ% |IaB5?Hš c #NSصkjҤޠ$m;v̓|R= & [E:^zI~πqt H۾}NY3Ϧ>zȏTOYasۏ|~$Kc_ai~%0.딝6>/%A #wWQ9sΌ~ubuNp4Y 3Fz%6!/~"I"oDA/2X S? ;+ !)R{725*zYHb /"ֿ+s1nIJG +*5kT#FfRH88o6cNXkzl:A֬Y-=K%˕+8~8lp;W)I~ڵ^;1"o޼ykĢZJ'׾a1# RR5yžLh#٧4/͟dǑ(&Mrԉ4>}KbIRbC_ff䬬'PBHǸxPgyXb]#FȣSxÈ)mJ}l2`m wQ%3]Sn"ގT~F:ш|.G,ÍX`2!~$?A"h_$-%ZoiRcǎ9`=zٳ?\0ZQ0ZYK̙3a$nF Ýkd)ͬ ٱg%Rlu+VHM?~Z_i/'g}6۔\@ԒĒ/ $+ ~~_#GsA!Űaj",;+vfL4i36I{N2XQJ@ Ⱦ V&3-wE,AAU7Ѯ~Mg 9yPqgWZ%3}/K"[bY_|#33>/ǎ;H}3\V VƝX8 eIFZŀȠ__5amc+TLߋ NNEFLȀMUl,b͡F@ TŪhMwù_BXq#3՜$AAbIebA"U`~tד } ^SHlLDkNʸh6H ӾL$un}EZψ_:&F4{n~ϱvU7Ѯ~Mu('q#vwr0jXb k@n_'h_Ē bOf)ɴ͏"HgnMtY5!_~>SZNpi5DʸKl"Μ;첆N$kp#.A tK9"e6܋Rq?q!P:;W/ݸ]7?zm%y"ArCk- K AY"$\~"vYF6v&=oJ&=nLfXy٧'OUbhghD%3}/K"E/ GZ$N4KD,zHڤ'Ze=|E,q b]! {&ֆLl fA*.]!z?KbInj h_ĒL;,f#P,[*@gG#;vbiO˝5kDr`ʕQ޺u+We0j͛7Հ*Tzu;W\Y/^\u]]~ر1_dUVMq+Rԩ):vcGZZ>4(ljQчI>db:pႧw/6Kq8ڽZ$tnZjTR]vjcv_x'K?~d~>7_3?%A$`:L;,A:s#S%62He#p'?!'R˿1c3E2Rbdɒd&T{V{դdɒzwd snܪyqSֿ§FQez<ύ5R:t(lWDGM6: }>}㷩K.ԩSbw|%@* .ZnHܤw/2=vV@0IIKKժU+}jذaYAj:߶mΠXv*ScJ~2ڗ4~ڗ}%o2~g:~KYi/$I)lÇN*q<… ls{=eYcI<edtoǛ6m-9f /^ԎӞ={DRTNҭފ8E-+WœL*55Uo|%[Nʇx`'mMo6IС%Lza#ϫWǎ^zrx/i~<&:k&'$ ]R-ryȑYcVK ֞ Ē '2XÊN -SN"c~)t4YDl)XhG".v1(!"Kҋ|шK(%Fr4b9&cڴiaÆ!d|nyI~ǹpC=Rry+UX1^@ּb_nXK?ϿWGh㏇'%A!by~d?**I%AbY%nݺ!X~v@nuVɱrϞ=;;Rь3:d0A P+AEbEZ#U?E ^X JJ4RhDڈ a C>a"Z[/ξ{9{ss/]k}?k)Q4i]'+tRӲeK\ Os+ܵkٶmYpV׫Ws tվ"G ϟU]O׿;w67 ̙3ǫ}?nZOkп\/_ϦO%삢צ [/$*kDX² KmT1x`kɓ' ~Xuڵtg?:FFB*J ]_ԅ_a=j)5:ŋtmVQ2wolݺOrKFkdK.fԨQei gcÆ }Uqs_mqoDX@L=%ժz=%5TX*h%S%T{hW`M~8{ri +t6mژYfoӦM+T1b۷osε.Ϟ=[i_)Z[8cƌ45 lTfxMF^hJ.NW,q/?n:lXȬST)R6m&CtSФWXawn7_[nڴ}j>f#=ٽ{3]hWFM\lR3O:XXu}Һ*MӚ7_Mt|?]K H?z?_[Ao*swߨ!QѧOݘ1c,jRzsF _8uu]|' K1(0a (,5kRBƍgOӯ.>O~_NZK3+ij=qD[O:,*hTCftk3M}Sڸc6Gq7p7qՑ'ON{O\suۦ(āD693gLPMۿ\'jj8ϷK(*|f i2w% ,1I _K9PFXa a ܹsO)x7;T;otRX^ ².a @*_X.Up6G?a UX^0gΜ[_ A}GM²qRXO ˬ6Ϫ*a ]t`xUQ _KR㟤V]lΜ9VTq}es+$7L0U_@BX@ 1O>X| ʯF+/DX",_@BX+,& 6H\ қ$^ajM#De+Kп=wd ~aH\6N' }󢐨lP`F ÇaÆ÷[haƏo 7|=7*Ѻ)SK/4h^=.3u5m۶5?i:(_|ѻ~3gLqq=]~YP:ާ:bөS'ێ7pCv]we埫E>>QI#n:u;v[ƍu]g֬Yu}Oq|犯jҨ@^haH`^aj4{ ,nnVrJ3ϘFz,]AM:u5\S裏͛^zɬZ\y?Oy^~eyfϛ&M`N|GW_-g ֭[W+Pkƍmn;=`ԩ}w++_nz)[_W͎;,}IOzU?qW_mviFiL㋯u}Oq|犯j"./ @dbX!Y()#9(,SG_UW]Uz鶜իWg8+xү ck :r-^s{ׯ2߿9%֮]kj׮mN8L*Kk/pT`$&վ ]Wd~Wծ];㏗K)ρjߨ)|W|PSe:L +D F(e5^X2a y*ɠ{#GL~{Ȗ۷鬋-J ˗g]_>oQ<TWzxTYyU(ay'*}Q'S>TzU<_wqرy72dѣ=׿?]/|<qwa 5U\uCB dXP,*ԴVZ=N)dճW/ZZ%_4X~}[?8~aӭ[1UZ K,qu Qj?Ӳ5_b'{]v|Zjv:2M ޟtW>SG>|`ŎFT;v?WF]OU~>8sPeVV;P]nn.g%p~lЩK>}I};~-wҥv=TZ4WY-[Rk(4/*]{htcڴie#~ehoiפIl9o>WA]̶m… ^ze}/m?W}ڧ8+sζN ̙3??q|)ߧ~UlbX![ިRE <؎&$UGSzJ7o T(a-4ϥ},^wʟwO6窿+\FtbFU9Ku]_.|R~6s_%@ K.R0 -zGk-vl={_MԳ`:YHkf̘s,Ga땮MB2סާmڴ1f*M68rm2w\;mٳYԿwo^(G6KW,=m(վ˦}?|Z~.t*^i}@:tȦrV N]G|Ov+V0ݻwT6mkDb: |N2ERЖm*5Q+?զ>J c{޽۫RS-[f암۾:Ay>>w~W>>窿+8K#jO>}f01cK\WD?>qw>Kef͚qLg5}kܸq6m= vA NZ 0-/26srk$W[O<9UՑ:t0Gp+=L+}ĉtȺӰ(۾lDS6ژd6qw/qJ#nnjSIHhClj_]w?Wq@XV1I _K%’ DXпa %,a,* a -, {~/!,K3 @Xa @<6Æ whŒ?*_~Lb.RӠAs7z_?#SNnݺn(|v3ofVY\Ϝ9c:o7ONP_ve{a뮻:=ځ3>[۶mMii?u;v۸qcsuי5kdվQ7//WzӿʏGt?W~ӾKe Ç[oլ\<3QF桇Nw裏͛^zɬZ\y?ϕ_W\a.rSOۀ1@As:u5\S^{ٸqMyo6Sۧ oFcǎ2 W]]>2j9S-?nj?kf7M4>/!tW۲wiFiR \j ]ϗ+=nuʏGt?W~Wm_%²ʅeN*_~W@g@kN|0}tNW:bڵvĉb QGw0{3;tPs-xv?^.x>qzrtW_OW?>티@XLe„ v#t'{G1i'p/Os=_>_J}}߲ퟮ|뇰@XԴVZ7=]V^=W^yNS 5r駟B,Y$/2Z]mЮR~GWXnѨOVZO:}.a)>yMn*E_'k|`y)]S ;+|<qoTz6Ͽ[|Glꇰ@XTsM7'>쳬ӕ /ؠR}Z3u}9}rUh-8M4ɮY?qK-[S~)oQhcT6vǴiFTRsv2۶m3 .z-,UW/]K][ΝmAM̙+|ҳy]_OaYR 2=sεΞ= ϦQK-1c}4%TS5R*_5qy{ q|߷}r_'|EfJwk̥7%²ʅy^R`JwE tŠ+L%%AiӦkDM[l2WRRb_4c :ube7u.JSM>+-|B|>sf3:&B#@WS4,'t]; S2MՑs ׈>ߧOٌ~̘1v`D埫}/z].|r>տ]U?%²`efҎ((IwuƍwA fxvjZ%M~8qS+8V1pXYkFR:t0Gp+=LES6Y`AG\wwFG2L<9m `׌8srk\k7NsHHi_\7/|_W|W۾Ke |Q?п@X", ̀a K% _Kj,,XRZ@򪢪?п’@a$0% ,/!,Caya3l0{w->LݺuM۶mMiih]Д)S̥^j4h`nfsA<@뮼׿3gΘb{yӧtOT]OG1:up 7dU~>*_|ͬ;u;v;76]wYfMWTG[oU!-_||ܟ]տ>_>GX , BX>zfʕg152=PYU/l6olyӤI+yGMK/dٳ+-,oFcǎ2 qklܸѬ_ۼNjڷo_!t?W\qSO=eP*?n\KԩS\s5iq"W_}M۹s9r(xڿ\վ}yW˙֭[{\}\GX , FX*jn喼6 4ʠ1`Ϟ=6z׼e@>ohvZSvms gzxTRQ<#%Fi}ңG\O>իWW}OT]v/KW`<ܿ'߽kzƽ?>G=yi_%²ʅe*&LcvE$ {G1| lD}.{O8zO#𨤦OHOy6lh/_u}|ݟ?W<_KeժUr#(XmУ ?K`)nճW@Z*_aYV-{6h?ZߥQ jˏ%K8źuhF5\2}ek4RݻڮtWqS~TsOVhKהcǎy/j<?֭}˨7_'S/qla ,a 覛n_|gҎ?nKBK` /ؠJ}ZSt}y]CA]̶m… ^z|2[li_RޢTǨtmїiӦd_yh֠M4ɖ}ʏS?Wѹsgg d7_3gwB_>ﺾ䓬e7']tj_%²ʅD(,є=M )_kWxc޽6pRF;Ξ=y͹sisgϞe7bӷo_tmRR>M6f֬YwӦMS~gs|G?mkjM7!lWSP6rƌYu=?ϥ?W@XV:?O[+@ СCe~Ai9jZ?S-Xtn`:\6m*c8M{qv}Һ*MU՚/+-|@>sf^ORj*e씺:XS~ *\tq(4"ҧOً1c4~ܿ|ߕվB+D_*D]|/aY²Yfi1P k4){3gά3N6n809djSaaى)6X`AA?J:ѣGW8Ε&T9o'N! 'U~8p`T.ۨ|(DD6\jr+}u} 2y⺿7q>|@X1I _K%’ DXпa %,ܮ* a -, {~/!,K3 @Xa @<6Æ hŒ?ڞ޻**_~Lb.RӠAs7fgT| yu#m۶)--5~w-w?裦y楗^2V2={4W^yϳ3S ԩc暴+\~橧2ׯklܸіvw.磏>2j9Pno꿄͍7hvQfaw=_|sмmΝ;ȑ#MKe :~UWUP@_ ~+={kkYf*V^6 Fҍ"U/kךڵk'N8]>{߷}/aNhgbС[n?*Kt]+=We?Qo׮y%Kea T&L`AOtw}7+a {g_9rϾlFU]g 5OF)S}ˍ=`]?YoذY|ltEy?nեegO_We=Q;Lǎoa!CL="Ke KMklժU_׭[gׯ KMW+b)P/ZoO?lM޽{%K|)kVi?AD|tW'p~MnʂV> Zji.:@׷ז>-:~>ex>\(WSv;Ke Knngm̡_ǧMV}6^ASOfsM/Q4i]S%+tRӲeK\- MJw?\\?͇ wemf.\hիǏZٿw[~!/Ult| ˸_|ɯ~ӹsgۧ d73gaYRm <ɲ͛g`&[a y]7,v5=5_IWa=jI|zJw?\֚n|'7,^ywҦoذ۷/w[~!lgqe>*pʯ.]QF}N>?!,_TУ5H SMmBRa}lFk\W{ hٳg\~M6f֬Y۴i #:Fa6EΚ3fͧsεNϞ=6]S25u3tAwϵB_p}~2__|Dʯ |SxMFN^x$%² s:*BL`{޽+3e:`Ŋ{v_냀oӦMe_u n)`v-Te˖)}%%%u:?Li*Tgts:5Qu Ft| PLS1uDEq/s]?JUGTӧG~̘1v!q%K͚5KT +4kܸq6 k>E2H;%j-￟t80ĉm=uTQ\kD::t0Gpƣ+U># &OL훫 FSK,XPnصiQS gΜYn WzrW]꟮_|snz4 @S]aYp2_T&/!,K3 @Xa @ K%2'a?!,aYګ{f.n2'7|4j|墋L>Xk;柟),7M&3҅ʂm|kbeei K@X>iVrW h_~'־ww5Qs),5vެ}CXY La j),f@gyLg*`nB;"VFa߽\m~Yﻧ߰iH!䉦EZ]پ5Sk_>e:2%#gWii}iK[>훩>7ԪUˎ?N*uwٿ=:fп~E\oZhnvm[qto6nݺWk_{َ_Ը}0Zs#,Ky!,%$҅kd \?oGG` ӧ uwB[Wcnj0۵5:߬Y {[Xj|f__N:KsqԱYYAqCY*_&1mn;t4mb{X>f#Wi_LVWbWR>듏 )WʻEGX",BX6ȖpUX=t||`Tg=oSM ̊x= gon)7;}m\j^X/eiߦvf+/zMt]վG9\ӻGYF*a7.a)zomxY{;]:;VP>7iTˏfyOUʛTtK%²F ˥A=[+MX* hVK}F̼a$"YR;{rSͻoܸ{f2]Sk܏uhgfxw ?e}n,}W}3F8ãЏW]bhQ'?a@Xhay+6,a2#P럦JTKXjL(W^=[A ]Z(ߟؗm_5Q;T}\O}3*owm(,sdj-FG>K%²F K 5w%,&.jY0V"km[!}->nFbKLׄj!mWX4BſҷIZ1Ns>ퟏd*KiSz|S?ӎi0//?0?DX",5sWz| ot 7g6O-mVMTDk&],FiYx3=Ag?-4Ϭ(f0*[h#.OBES5Վ*[٦ߌu}WcJ{.mղBO\O}]?1EXi#fFJzTau|kn ,KesU+수vpՑ o\_9n!8RLIՑ t܈)?ʪMVvGQo՝);bh6ǍD /yqvGT]R~j_a79sUX?_ɴ)P?Co .,4 ΝʎBYOҎ8f>#SڍaeU!,bܹr޼y7 ,^X' 0M#z.;a\EXV, M= 46x6KHSR%aDXd3b=ue%Z KorGL]K%ڰ'݂%ge:αK@X$D3I\"ሰ@XB$lIk$%| ,a K@X'a @X ,K@XK%(,ΟtaV]-#PXnݺA aV]mg'ꨝz k4avi&CѹE1 0L:&EeT 8a'q>)01 0k kIZaY'a΍Z˖?m0 0uG kƟtVRXj:!qsb%f 0 +0ߧ& ?U+,/H ֨ܚK &IaadM䏢P²vH\jel1 0QT-(qȬaV`"0S6aVQvԊ08eX4^: d'tm67նlw^²֭[6ܸqu.LXMKemذiF\",j֭N ,j6v'X#,׮]\  2Yfz:'qQQ:K/,/+,W^X:MKlK% ,a K%aY#G6TcagԨ7')>ul¶$tJ%D|~/ѥaY ѱv1j)^Ɛ!*҉H% ,]II% dEXrt'D٩Zf ^=ſ:4 }$REjK]\VXSQ]pKKާ+ғc%Zc)tȱoB K}"/a˵ַW) KON=8=cnN,B%'I36a{XX~͆2yngx*,24di+aFFJcy֦F,ClټW 7YX&&:D4#? 6<>>vTWF T諳iӦQX^:[^ϙߔ=V:fxB0;#TjyYk*͉ҒEUΧ9qF)>ĪМhtt:vOK O;W^Kȇ &z7nayI´N0*.rk,Jd|ڵk{DfX\@R+!*Q~EF/릈KF- YXJmٲ剪K%@uq[n]Srk7*:wɅEG-EX.Z-[2EM^0aBۢ/6 ηDXT7a)!jժ w[n=E_Aa Pͅe^z!,/^|GQ#H5AX&uMLQ_D9_-xe&a D\M 7n\PWտ'<$,Sa{Rj".rڴi-7o|ȮaÆu*8Z: Q P eX\)SLi|Y -.]zM>O>/)*%i*a@uqf͚8rέ D!QQ Pe?"*W^=7(d ,@M7o^f=3`]e"UXaYaXmSM^;/+:QO:Qf=牸L));6Nu5_\V)vʕC~;ެ`cq`ڵӉ+V|/deڝbE[Sn]%qY~3iӦ 6ѿ%%%έJ@ KRr˖-oB2`M% OF}x-tEXtSoftwareby.blooddy.crypto.image.PNG24EncoderIENDB`emacs-ctable-0.1.2/readme.md0000644000175000017500000004424012275027457015462 0ustar dogslegdogsleg# Table Component for elisp `ctable.el` is a table component for emacs lisp. Emacs lisp programs can display a nice table view from an abstract data model. The many emacs programs have the code for displaying table views, such as `dired`, `list-process`, `buffer-list` and so on. So, ctable.el would provide functions and a table framework for the table views. # Installation To use this program, locate this file to load-path directory, and add the following code to your program code. ```lisp (require 'ctable) ``` # Quick Start ## Hello World Giving a list of the rows list to the function `ctbl:popup-table-buffer-easy', a simple table buffer is popped out. ```lisp (ctbl:popup-table-buffer-easy '((1 2 3 4) (5 6 7 8) (9 10 11 12))) ``` Here is the result image. The header titles are generated automatically. ![sample-1-1](img/sample-1-1.png) Giving two lists, the latter list is displayed at header titles. ```lisp (ctbl:popup-table-buffer-easy '((1 2 3 4) (5 6 7 8) (9 10 11 12)) '(aaa bbb ccc ddd)) ``` Here is the result image. ![sample-1-2](img/sample-1-2.png) ## Basic Use The objects of ctable are designed by the MVC pattern. Programmers can customize ctable objects to use rich table views in the applications easily. First, one defines the column model and data model for the user application. The former model defines how the column should be display, the latter one does the contents to display. Second, one chooses builds the view component with the models. Here is an illustration for the object relations in this basic case. ![Object relations](img/normal_use.png) Here is a sample code for the model and view. ```lisp (let* ((column-model ; column model (list (make-ctbl:cmodel :title "A" :sorter 'ctbl:sort-number-lessp :min-width 5 :align 'right) (make-ctbl:cmodel :title "Title" :align 'center :sorter (lambda (a b) (ctbl:sort-number-lessp (length a) (length b)))) (make-ctbl:cmodel :title "Comment" :align 'left))) (data '((1 "Bon Tanaka" "8 Year Curry." 'a) (2 "Bon Tanaka" "Nan-ban Curry." 'b) (3 "Bon Tanaka" "Half Curry." 'c) (4 "Bon Tanaka" "Katsu Curry." 'd) (5 "Bon Tanaka" "Gyu-don." 'e) (6 "CoCo Ichi" "Beaf Curry." 'f) (7 "CoCo Ichi" "Poke Curry." 'g) (8 "CoCo Ichi" "Yasai Curry." 'h) (9 "Berkley" "Hamburger Curry." 'i) (10 "Berkley" "Lunch set." 'j) (11 "Berkley" "Coffee." k))) (model ; data model (make-ctbl:model :column-model column-model :data data)) (component ; ctable component (ctbl:create-table-component-buffer :model model))) (pop-to-buffer (ctbl:cp-get-buffer component))) ``` Here is the result image. ![sample-2-1](img/sample-2-1.png) The models have further options and functions to customize the display and behavior, such as column width, text alignment, sorting and so on. (See Model section) The key-binding on the table can be customized by the keymap object in the usual way. Then, the user program implements the custom function which refers the focused cell. (See Key Bindings section) The ctable framework provides some hooks to notify the usual events: click, selection change and update view. (See Event Handling section) The appearance of the table can be customized, such as foreground and background color, tabular lines. (See Display Parameter section) ![ctable components](img/objects.png) ## Sample Codes - samples/simple.el - sample codes mentioned above. - samples/large-table.el - large data and async-model samples. - samples/direx-ctable.el - directory tree and table list in collaboration with direx.el - ref: https://github.com/m2ym/direx-el ![direx-ctable image](img/direx-ctable.png) # Advanced Topics ## Column Model The struct `ctbl:cmodel` is a data type defined by cl-defstruct. This model defines how to display the content along with the each column. Here is the details of the slot members of `ctbl:cmodel`. |slot name | description | |-----|------------------| |title | **[required]** column header title string. | |sorter | sorting function which transforms a cell value into sort value. It should return -1, 0 and 1. If nil, `ctbl:sort-string-lessp` is used. | |align | text alignment: `left`, `right` and `center`. (default: `right`) | |max-width | maximum width of the column. if `nil`, no constraint. (default: `nil`) | |min-width | minimum width of the column. if `nil`, no constraint. (default: `nil`) | |click-hooks | header click hook. a list of functions with two arguments the `ctbl:component` object and the `ctbl:cmodel` one. (default: '(ctbl:cmodel-sort-action)) | ## Data Model The struct `ctbl:model` is a data type defined by cl-defstruct. This model defines contents to display with column models. Here is the details of the slot members of `ctbl:model`. |slot name | description | |-----|------------------| |data | **[required]** Table data as a list of rows. A row contains a list of columns. Or, an instance of `ctbl:async-model`. (See the async-model section for details.) | |column-model | **[required]** A list of column models. | |sort-state | The current sort order as a list of column indexes. The index number of the first column is 1. If the index is negative, the sort order is reversed. | ## Key Bindings The keymap `ctbl:table-mode-map` is used as a default keymap on the table. This keymap is a customization variable for the end users, so it should not be modified by applications. The component functions `ctbl:create-table-component-buffer` and `ctbl:open-table-buffer` receive a `custom-map` argument to override the keymap on the table buffer. Because the functions connect the given keymap to the default keymap `ctbl:table-mode-map` as parent, application program may define the overriding entries. The component function `ctbl:create-table-component-region` receives a `keymap` argument to define the keymap on the each characters in the table region. The ctable framework provides some hooks for the usual event cases. In such cases, the application should use the event handlers, instead of defining the keymap. See the next section. ## Event Handling The ctable provides some hooks for the particular events: clicking, selection changing and updating view. The application program can implement some actions without defining keymaps. Here is a sample code for the click action: ```lisp (ctbl:cp-add-click-hook cp (lambda () (message "CTable : Click Hook [%S]" (ctbl:cp-get-selected-data-row cp)))) ``` where, `cp` is an instance of `ctbl:component`. The function `ctbl:cp-add-click-hook` adds the given function as an event handler to the component instance. Here are event handler functions: - `ctbl:cp-add-click-hook` : on click - `ctbl:cp-add-selection-change-hook` : on selection change - `ctbl:cp-add-update-hook` : on update view The function `ctbl:cp-get-selected-data-row` returns a row object which is defined by the model. Some component access functions are useful for the action handlers. - `ctbl:cp-get-selected` : returns a Cell-ID object which is currently selected, such as (1 . 2). - `ctbl:cp-get-selected-data-row` : returns a row data which is currently selected. - `ctbl:cp-get-selected-data-cell` : return a cell data which is currently selected. ## Display Parameter The ctable renders tabular form with many rendering parameters. The parameters are set at the slot members of the cl-defstruct `ctbl:param`. To customize the parameters, one should copy the default parameters like `(copy-ctbl:param ctbl:default-rendering-param)` and set parameters with setter functions. Then, at the building ctable component instance, this parameter object is given by the `:param` keyword. Here is a sample code for parameter customize. ```lisp (let ((param (copy-ctbl:param ctbl:default-rendering-param))) (setf (ctbl:param-fixed-header param) t) (setf (ctbl:param-hline-colors param) '((0 . "#00000") (1 . "#909090") (-1 . "#ff0000") (t . "#00ff00"))) (setf (ctbl:param-draw-hlines param) (lambda (model row-index) (cond ((memq row-index '(0 1 -1)) t) (t (= 0 (% (1- row-index) 5)))))) (setf (ctbl:param-bg-colors param) (lambda (model row-id col-id str) (cond ((string-match "CoCo" str) "LightPink") ((= 0 (% (1- row-index) 2)) "Darkseagreen1") (t nil)))) ... (setq cp (ctbl:create-table-component-buffer :model model :param param)) ... ) ``` Here is the details of the slot members of `ctbl:param`. |slot name | description | |-----|------------------| |display-header | if t, display the header row with column models. | |fixed-header | if t, display the header row in the header-line area. | |bg-colors | '(((row-id . col-id) . colorstr) (t . default-color) ... ) or (lambda (model row-id col-id) colorstr or nil) | |vline-colors | "#RRGGBB" or '((0 . colorstr) (t . default-color)) or (lambda (model col-index) colorstr or nil) | |hline-colors | "#RRGGBB" or '((0 . colorstr) (t . default-color)) or (lambda (model row-index) colorstr or nil) | |draw-vlines | 'all or '(0 1 2 .. -1) or (lambda (model col-index) t or nil ) | |draw-hlines | 'all or '(0 1 2 .. -1) or (lambda (model row-index) t or nil ) | |vertical-line | vertical line character | |horizontal-line | horizontal line character | |left-top-corner | corner character | |right-top-corner | corner character | |left-bottom-corner | corner character | |right-bottom-corner | corner character | |top-junction | junction character | |bottom-junction | junction character | |left-junction | junction character | |right-junction | junction character | |cross-junction | junction character | ## View Components Ctable has three destination components to display the tabular data. - Independent buffer - Region in the other buffer - Text output ### Buffer The 'buffer' destination displays the tabular view as ordinary Emacs applications do. The function `ctbl:open-table-buffer` makes a new ctable buffer and displays it by `switch-to-buffer`. The major mode of the ctable buffer is `ctbl:table-mode` and the keymap `ctbl:table-mode-map` is bound. Using this destination with the `fixed-header` parameter, the application can use the fixed column header. This destination is easy to use for applications and users, because the buffer is usual application boundary and users know how to use buffers. ### Region The 'Region' destination embeds the tabular view in the buffer which is managed by the other applications. This destination can give the other applications a nice tabular view. Let's try a simple demonstration. Evaluate this code in your scratch buffer. Region destination example: ;; Evaluate this code in the scratch buffer (require 'ctable) (ctbl:create-table-component-region :model (ctbl:make-model-from-list '((1 2 3 4) (5 6 7 8) (9 10 11 12)))) Then, the tabular view will be embedded in the scratch buffer. You can navigate the ctable view in the buffer. Undoing for the some times, you can remove the ctable view. ![ctable in scratch buffer](img/region-scratch.png) Because this destination never interacts anything out of the region and has its own key-binds as a text property, users can easily embed a tabular view in the other applications. ### Text The 'text' destination generates just a text which represent ctable view. The function `ctbl:get-table-text` returns the text. ### Column Width TODO... - Unlimited mode - Limited mode - expand strategy - shrink strategy ## ctable Component An instance of struct `ctbl:component` manages all ctable states, such as models, view, event handlers and some internal status. If an application wants to interact a ctable component, the application should hold the instance and access the component through the following ctable component interface. ### Getting ctbl:component Instance To access ctable component, the application program should bring an instance of `ctbl:component`. The instance of the ctable component is stored at following places: - `buffer` view: the buffer-local variable `ctbl:component` - `region` view: the text property `ctbl:component` - `text` view: N/A Calling the utility function `ctbl:cp-get-component`, one can obtain the ctable instance at the appropriate places. The stateless functions, such as simple event handler functions, can use this function to get the instance. The applications those have the state-full operations, however, should hold their own ctable instance for the safety object reference. ### Access Internal Objects The application can get some internal objects. - model object : `ctbl:cp-get-model` - parameter object : `ctbl:cp-get-param` - buffer object : `ctbl:cp-get-buffer` ### Cursor Position The application can get the current cursor position and modify the position. Here, *cell-id* is an object that represents the physical cursor position. *cell-id* is a cons pair which consists of positive integers: `(row . column)`. The index number begins from zero. One can access the values with `car` and `cdr` directly. - getting cell-id : `ctbl:cp-get-selected` - moving cursor to cell-id : `ctbl:cp-set-selected-cell` Note that the position which is indicated by *cell-id* is not the same as the position of the model's row. Because the ctable component changes the row order with sorting by clicking header column, the rows order is not corresponding to the model's ones. If the application need to get the selected row's data, following functions are available: - current row data : `ctbl:cp-get-selected-data-row` - current cell data : `ctbl:cp-get-selected-data-cell` ### Modifying Model and Update Table View The application can update the table contents. Creating a new model instance and setting it to the component with `ctbl:cp-set-model`, the component replaces the model and refresh the buffer. - replace model instance and update view : `ctbl:cp-set-model` Another way is updating model instance destructively and refresh the buffer with `ctbl:cp-update`. If the modification of model data is little, this way is lightweight in the viewpoint of calculation and memory usage. However, such the destructive modification complicates the application logic. - update view with current model state : `ctbl:cp-update` ## Async-Model and Incremental Update Ctable has incremental data interface which enables the application delay rendering or append subsequent data with the user action. This mechanism can avoid Emacs freezing during visualizing a large amount of data. ### Case 1: Huge Data When a model which consists of a large number of rows (more than ~1000) is given to the synchronous interface mentioned above, Emacs blocks UI response until rendering is completed. Because the text rendering on the buffer is the heaviest task in ctable, it is effective that the application displays a front part of data and delays the rendering of rest data. In the most cases, users are interesting in the such first page of the large data. Just wrapping data in async-model via `ctbl:async-model-wrapper`, the application can use this interface. Here is a sample code: ```lisp (let* ((large-data ; large data : 4000 rows (loop with lim = 4000 for i from 0 upto lim for d = (/ (random 1000) 1000.0) collect (list i d (exp (- (/ i 1.0 lim))) (exp (* (- (/ i 1.0 lim)) d))))) (async-model ; wrapping a large data in async-data-model (ctbl:async-model-wrapper large-data)) (cp ; just build a component (ctbl:create-table-component-buffer :model (make-ctbl:model :column-model (list (make-ctbl:cmodel :title "row") (make-ctbl:cmodel :title "delta") (make-ctbl:cmodel :title "exp") (make-ctbl:cmodel :title "exp-delta")) :data async-model)))) (pop-to-buffer (ctbl:cp-get-buffer cp))) ``` And here is the result image: ![async data wrapper](img/async-wrapper.png) ### Case 2: Asynchronous Retrieving In the case of retrieving large data asynchronously from an another process or remote servers, the application needs to append retrieved partial data without blocking UI response nor updating whole table view. Defining some functions in `ctbl:async-model` struct, the application can control asynchronous data retrieving and updating table view. Here is a minimum sample code: ```lisp (defun async-response (row-num len responsef errorf &rest a) (funcall responsef (loop for i from row-num below (+ row-num len) collect (list i (* i i) (* i i i) (sqrt i))))) (ctbl:open-table-buffer-easy (make-ctbl:async-model :request 'async-response) ; defining async-model '("int" "square" "cube" "root")) ``` In this sample code, we defined just a `request` function in `ctbl:async-model`. The `request` function should have 4 arguments: - `row-num` : an index number of the requested first row - `len` : a number of requested rows - `responsef` : the continuation function to which the result rows should be passed - `errorf` : the error continuation function Here is the result image: ![defining async model:1](img/async-model-sample1.png) #### ctbl:async-model struct |slot name | description | |-----|------------------| |request | Data request function mentioned above. | |init-num | Initial row number. (Default 20) | |more-num | Increase row number. (Default 20) | |reset | Reset function which is called when user executes update command. (Can be nil) | |cancel | Cancel function of data requesting. (Can be nil) | For forward compatibility, these callback functions should have a `&rest' keyword at the end of argument list. For more complete example, see the demo function `ctbl:async-demo` at `samples/large-table.el`. ### Sorting Async-Model The ctable doesn't provide default sorting function `ctbl:cmodel-sort-action` for the async-model data, because ctable can not receive whole rows of async-model. If sorting function is needed, the application program must implement it manually. * * * * * (C) 2012,2013 SAKURAI Masashi All rights reserved. m.sakurai at kiwanami.net