{"action":"created","comment":{"url":"https://api.github.com/repos/bram85/topydo/pulls/comments/51533371","id":51533371,"diff_hunk":"@@ -101,132 +119,199 @@ def _scroll_to_bottom(self, p_size):\n         self.listbox.calculate_visible(p_size)\n \n     def keypress(self, p_size, p_key):\n-        # first check whether 'g' was pressed previously\n-        if self.keystate == 'g':\n-            if p_key == 'g':\n-                self._scroll_to_top(p_size)\n+        urwid.emit_signal(self, 'remove_pending_action')\n+        requires_further_input = ['postpone', 'postpone_s', 'pri']\n \n-            # make sure to accept normal shortcuts again\n-            self.keystate = None\n-            return\n-        elif self.keystate in ['p', 'ps']:\n-            if p_key not in ['d', 'w', 'm', 'y']:\n-                if p_key.isdigit():\n-                    self._pp_offset += p_key\n-                elif self.keystate == 'p' and p_key == 's':\n-                    self.keystate = 'ps'\n-                else:\n-                    self._pp_offset = ''\n-                    self.keystate = None\n+        keymap, keystates = self.keymap\n+\n+        shortcut = self.keystate or ''\n+        if len(p_key) > 1:\n+            shortcut += translate_key_to_cfg(p_key)\n+        else:\n+            shortcut += p_key\n+\n+        try:\n+            action = keymap[shortcut]\n+        except KeyError:\n+            action = None\n+\n+        if action:\n+            if shortcut in keystates:\n+                # Supplied key-shortcut matches keystate and action. Save the\n+                # keystate in case user will hit another key and add an action\n+                # waiting for execution if user won't type anything further.\n+                self.keystate = shortcut\n+                if action not in requires_further_input:\n+                    self._add_pending_action(action, p_size)\n             else:\n-                self._postpone_selected_item(p_key)\n-                self._pp_offset = ''\n+                # Only action is matched. Handle it and reset keystate.\n+                self.action_handler(action, p_size)\n                 self.keystate = None\n-\n-            return\n-        elif self.keystate == 'r':\n-            if p_key.isalpha():\n-                self._pri_selected_item(p_key)\n-            self.keystate = None\n             return\n-\n-        if p_key == 'x':\n-            self._complete_selected_item()\n-        elif p_key == 'p':\n-            self.keystate = 'p'\n-        elif p_key == 'd':\n-            self._remove_selected_item()\n-        elif p_key == 'e':\n-            self._edit_selected_item()\n-            # force screen redraw after editing\n-            urwid.emit_signal(self, 'refresh')\n-        elif p_key == 'r':\n-            self.keystate = 'r'\n-        elif p_key == 'u':\n-            urwid.emit_signal(self, 'execute_command', \"revert\")\n-        elif p_key == 'j':\n-            self.listbox.keypress(p_size, 'down')\n-        elif p_key == 'k':\n-            self.listbox.keypress(p_size, 'up')\n-        elif p_key == 'home':\n-            self._scroll_to_top(p_size)\n-        elif p_key == 'G' or p_key == 'end':\n-            self._scroll_to_bottom(p_size)\n-        elif p_key == 'g':\n-            self.keystate = 'g'\n         else:\n-            return self.listbox.keypress(p_size, p_key)\n+            if shortcut in keystates:\n+                self.keystate = shortcut\n+            else:\n+                try:\n+                    # Check whether current keystate matches built-in 'postpone'\n+                    # action.\n+                    pp_mode = keymap[self.keystate]\n+                    if pp_mode in ['postpone', 'postpone_s']:\n+                        if self._postpone_selected(p_key, pp_mode) is not None:","path":"topydo/ui/TodoListWidget.py","position":137,"original_position":137,"commit_id":"28aeffef1db2279743593d945b3bfaa54f4899dc","original_commit_id":"28aeffef1db2279743593d945b3bfaa54f4899dc","user":{"login":"bram85","id":63248,"avatar_url":"https://avatars.githubusercontent.com/u/63248?v=3","gravatar_id":"","url":"https://api.github.com/users/bram85","html_url":"https://github.com/bram85","followers_url":"https://api.github.com/users/bram85/followers","following_url":"https://api.github.com/users/bram85/following{/other_user}","gists_url":"https://api.github.com/users/bram85/gists{/gist_id}","starred_url":"https://api.github.com/users/bram85/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bram85/subscriptions","organizations_url":"https://api.github.com/users/bram85/orgs","repos_url":"https://api.github.com/users/bram85/repos","events_url":"https://api.github.com/users/bram85/events{/privacy}","received_events_url":"https://api.github.com/users/bram85/received_events","type":"User","site_admin":false},"body":"These two `if` statements can be combined with `and`.","created_at":"2016-02-02T07:26:23Z","updated_at":"2016-02-02T07:26:23Z","html_url":"https://github.com/bram85/topydo/pull/101#discussion_r51533371","pull_request_url":"https://api.github.com/repos/bram85/topydo/pulls/101","_links":{"self":{"href":"https://api.github.com/repos/bram85/topydo/pulls/comments/51533371"},"html":{"href":"https://github.com/bram85/topydo/pull/101#discussion_r51533371"},"pull_request":{"href":"https://api.github.com/repos/bram85/topydo/pulls/101"}}},"pull_request":{"url":"https://api.github.com/repos/bram85/topydo/pulls/101","id":55818145,"html_url":"https://github.com/bram85/topydo/pull/101","diff_url":"https://github.com/bram85/topydo/pull/101.diff","patch_url":"https://github.com/bram85/topydo/pull/101.patch","issue_url":"https://api.github.com/repos/bram85/topydo/issues/101","number":101,"state":"open","locked":false,"title":"Introduce keymap for column_mode","user":{"login":"mruwek","id":500710,"avatar_url":"https://avatars.githubusercontent.com/u/500710?v=3","gravatar_id":"","url":"https://api.github.com/users/mruwek","html_url":"https://github.com/mruwek","followers_url":"https://api.github.com/users/mruwek/followers","following_url":"https://api.github.com/users/mruwek/following{/other_user}","gists_url":"https://api.github.com/users/mruwek/gists{/gist_id}","starred_url":"https://api.github.com/users/mruwek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mruwek/subscriptions","organizations_url":"https://api.github.com/users/mruwek/orgs","repos_url":"https://api.github.com/users/mruwek/repos","events_url":"https://api.github.com/users/mruwek/events{/privacy}","received_events_url":"https://api.github.com/users/mruwek/received_events","type":"User","site_admin":false},"body":"User can now specify key-shortcuts in main topydo config file under\r\n'column_keymap' section in form of:\r\n\r\n`<SHORTCUT> = <ACTION>`\r\n\r\nTwo main types of action are supported:\r\n- built-in (one of: 'home', 'end', 'up', 'down', 'postpone', 'postpone_s'\r\n  and 'pri')\r\n- topydo commands (aliases included). Prefixed with 'cmd'. Commands to\r\n  call on selected todo item should contain '{}' placeholder to mark its\r\n  place in final command call in similiar fashion as in aliases\r\n  definitions.\r\n\r\npostpone, postpone_s and pri shortcuts are sort of prefixes for\r\narguments for respective topydo commands triggered on selected todo item:\r\n- postpone<COUNT><PERIOD> will translate to 'cmd postpone {}\r\n  <COUNT><PERIOD>'. postpone_s will do the same but with '-s' flag\r\n  added.\r\n- pri<PRIORITY> will translate to 'cmd pri {} <PRIORITY>'\r\n\r\nDefault config as an example:\r\n\r\n```ini\r\n[column_keymap]\r\ngg = home\r\nG = end\r\nj = down\r\nk = up\r\npp = postpone\r\nps = postpone_s\r\npr = pri\r\nd = cmd del {}\r\ne = cmd edit {}\r\nu = cmd revert\r\nx = cmd do {}\r\n```\r\n\r\n- pp23d will postpone selected item by 23 days\r\n- ps1m will postpone selected item (threshold date included) by 1 month\r\n- prz will set priority of selected item to (Z)","created_at":"2016-01-12T21:16:30Z","updated_at":"2016-02-02T07:26:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"ddfe3f0698c5be8c9f1a26df6c47b29e1b69fe37","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/bram85/topydo/pulls/101/commits","review_comments_url":"https://api.github.com/repos/bram85/topydo/pulls/101/comments","review_comment_url":"https://api.github.com/repos/bram85/topydo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/bram85/topydo/issues/101/comments","statuses_url":"https://api.github.com/repos/bram85/topydo/statuses/28aeffef1db2279743593d945b3bfaa54f4899dc","head":{"label":"mruwek:column-ui-keymap","ref":"column-ui-keymap","sha":"28aeffef1db2279743593d945b3bfaa54f4899dc","user":{"login":"mruwek","id":500710,"avatar_url":"https://avatars.githubusercontent.com/u/500710?v=3","gravatar_id":"","url":"https://api.github.com/users/mruwek","html_url":"https://github.com/mruwek","followers_url":"https://api.github.com/users/mruwek/followers","following_url":"https://api.github.com/users/mruwek/following{/other_user}","gists_url":"https://api.github.com/users/mruwek/gists{/gist_id}","starred_url":"https://api.github.com/users/mruwek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mruwek/subscriptions","organizations_url":"https://api.github.com/users/mruwek/orgs","repos_url":"https://api.github.com/users/mruwek/repos","events_url":"https://api.github.com/users/mruwek/events{/privacy}","received_events_url":"https://api.github.com/users/mruwek/received_events","type":"User","site_admin":false},"repo":{"id":28913515,"name":"topydo","full_name":"mruwek/topydo","owner":{"login":"mruwek","id":500710,"avatar_url":"https://avatars.githubusercontent.com/u/500710?v=3","gravatar_id":"","url":"https://api.github.com/users/mruwek","html_url":"https://github.com/mruwek","followers_url":"https://api.github.com/users/mruwek/followers","following_url":"https://api.github.com/users/mruwek/following{/other_user}","gists_url":"https://api.github.com/users/mruwek/gists{/gist_id}","starred_url":"https://api.github.com/users/mruwek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mruwek/subscriptions","organizations_url":"https://api.github.com/users/mruwek/orgs","repos_url":"https://api.github.com/users/mruwek/repos","events_url":"https://api.github.com/users/mruwek/events{/privacy}","received_events_url":"https://api.github.com/users/mruwek/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mruwek/topydo","description":"A command-line todo list application based on the todo.txt format.","fork":true,"url":"https://api.github.com/repos/mruwek/topydo","forks_url":"https://api.github.com/repos/mruwek/topydo/forks","keys_url":"https://api.github.com/repos/mruwek/topydo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mruwek/topydo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mruwek/topydo/teams","hooks_url":"https://api.github.com/repos/mruwek/topydo/hooks","issue_events_url":"https://api.github.com/repos/mruwek/topydo/issues/events{/number}","events_url":"https://api.github.com/repos/mruwek/topydo/events","assignees_url":"https://api.github.com/repos/mruwek/topydo/assignees{/user}","branches_url":"https://api.github.com/repos/mruwek/topydo/branches{/branch}","tags_url":"https://api.github.com/repos/mruwek/topydo/tags","blobs_url":"https://api.github.com/repos/mruwek/topydo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mruwek/topydo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mruwek/topydo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mruwek/topydo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mruwek/topydo/statuses/{sha}","languages_url":"https://api.github.com/repos/mruwek/topydo/languages","stargazers_url":"https://api.github.com/repos/mruwek/topydo/stargazers","contributors_url":"https://api.github.com/repos/mruwek/topydo/contributors","subscribers_url":"https://api.github.com/repos/mruwek/topydo/subscribers","subscription_url":"https://api.github.com/repos/mruwek/topydo/subscription","commits_url":"https://api.github.com/repos/mruwek/topydo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mruwek/topydo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mruwek/topydo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mruwek/topydo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mruwek/topydo/contents/{+path}","compare_url":"https://api.github.com/repos/mruwek/topydo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mruwek/topydo/merges","archive_url":"https://api.github.com/repos/mruwek/topydo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mruwek/topydo/downloads","issues_url":"https://api.github.com/repos/mruwek/topydo/issues{/number}","pulls_url":"https://api.github.com/repos/mruwek/topydo/pulls{/number}","milestones_url":"https://api.github.com/repos/mruwek/topydo/milestones{/number}","notifications_url":"https://api.github.com/repos/mruwek/topydo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mruwek/topydo/labels{/name}","releases_url":"https://api.github.com/repos/mruwek/topydo/releases{/id}","deployments_url":"https://api.github.com/repos/mruwek/topydo/deployments","created_at":"2015-01-07T12:27:57Z","updated_at":"2015-02-07T11:40:50Z","pushed_at":"2016-01-30T11:43:27Z","git_url":"git://github.com/mruwek/topydo.git","ssh_url":"git@github.com:mruwek/topydo.git","clone_url":"https://github.com/mruwek/topydo.git","svn_url":"https://github.com/mruwek/topydo","homepage":"","size":1254,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"bram85:column-ui","ref":"column-ui","sha":"78f641b27176fb543d3749b7b89c01d5f8a02f54","user":{"login":"bram85","id":63248,"avatar_url":"https://avatars.githubusercontent.com/u/63248?v=3","gravatar_id":"","url":"https://api.github.com/users/bram85","html_url":"https://github.com/bram85","followers_url":"https://api.github.com/users/bram85/followers","following_url":"https://api.github.com/users/bram85/following{/other_user}","gists_url":"https://api.github.com/users/bram85/gists{/gist_id}","starred_url":"https://api.github.com/users/bram85/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bram85/subscriptions","organizations_url":"https://api.github.com/users/bram85/orgs","repos_url":"https://api.github.com/users/bram85/repos","events_url":"https://api.github.com/users/bram85/events{/privacy}","received_events_url":"https://api.github.com/users/bram85/received_events","type":"User","site_admin":false},"repo":{"id":25428376,"name":"topydo","full_name":"bram85/topydo","owner":{"login":"bram85","id":63248,"avatar_url":"https://avatars.githubusercontent.com/u/63248?v=3","gravatar_id":"","url":"https://api.github.com/users/bram85","html_url":"https://github.com/bram85","followers_url":"https://api.github.com/users/bram85/followers","following_url":"https://api.github.com/users/bram85/following{/other_user}","gists_url":"https://api.github.com/users/bram85/gists{/gist_id}","starred_url":"https://api.github.com/users/bram85/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bram85/subscriptions","organizations_url":"https://api.github.com/users/bram85/orgs","repos_url":"https://api.github.com/users/bram85/repos","events_url":"https://api.github.com/users/bram85/events{/privacy}","received_events_url":"https://api.github.com/users/bram85/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bram85/topydo","description":"A command-line todo list application using the todo.txt format.","fork":false,"url":"https://api.github.com/repos/bram85/topydo","forks_url":"https://api.github.com/repos/bram85/topydo/forks","keys_url":"https://api.github.com/repos/bram85/topydo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bram85/topydo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bram85/topydo/teams","hooks_url":"https://api.github.com/repos/bram85/topydo/hooks","issue_events_url":"https://api.github.com/repos/bram85/topydo/issues/events{/number}","events_url":"https://api.github.com/repos/bram85/topydo/events","assignees_url":"https://api.github.com/repos/bram85/topydo/assignees{/user}","branches_url":"https://api.github.com/repos/bram85/topydo/branches{/branch}","tags_url":"https://api.github.com/repos/bram85/topydo/tags","blobs_url":"https://api.github.com/repos/bram85/topydo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bram85/topydo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bram85/topydo/git/refs{/sha}","trees_url":"https://api.github.com/repos/bram85/topydo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bram85/topydo/statuses/{sha}","languages_url":"https://api.github.com/repos/bram85/topydo/languages","stargazers_url":"https://api.github.com/repos/bram85/topydo/stargazers","contributors_url":"https://api.github.com/repos/bram85/topydo/contributors","subscribers_url":"https://api.github.com/repos/bram85/topydo/subscribers","subscription_url":"https://api.github.com/repos/bram85/topydo/subscription","commits_url":"https://api.github.com/repos/bram85/topydo/commits{/sha}","git_commits_url":"https://api.github.com/repos/bram85/topydo/git/commits{/sha}","comments_url":"https://api.github.com/repos/bram85/topydo/comments{/number}","issue_comment_url":"https://api.github.com/repos/bram85/topydo/issues/comments{/number}","contents_url":"https://api.github.com/repos/bram85/topydo/contents/{+path}","compare_url":"https://api.github.com/repos/bram85/topydo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bram85/topydo/merges","archive_url":"https://api.github.com/repos/bram85/topydo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bram85/topydo/downloads","issues_url":"https://api.github.com/repos/bram85/topydo/issues{/number}","pulls_url":"https://api.github.com/repos/bram85/topydo/pulls{/number}","milestones_url":"https://api.github.com/repos/bram85/topydo/milestones{/number}","notifications_url":"https://api.github.com/repos/bram85/topydo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bram85/topydo/labels{/name}","releases_url":"https://api.github.com/repos/bram85/topydo/releases{/id}","deployments_url":"https://api.github.com/repos/bram85/topydo/deployments","created_at":"2014-10-19T13:49:43Z","updated_at":"2016-01-31T06:55:57Z","pushed_at":"2016-01-30T11:43:27Z","git_url":"git://github.com/bram85/topydo.git","ssh_url":"git@github.com:bram85/topydo.git","clone_url":"https://github.com/bram85/topydo.git","svn_url":"https://github.com/bram85/topydo","homepage":"","size":1461,"stargazers_count":88,"watchers_count":88,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":11,"mirror_url":null,"open_issues_count":12,"forks":11,"open_issues":12,"watchers":88,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/bram85/topydo/pulls/101"},"html":{"href":"https://github.com/bram85/topydo/pull/101"},"issue":{"href":"https://api.github.com/repos/bram85/topydo/issues/101"},"comments":{"href":"https://api.github.com/repos/bram85/topydo/issues/101/comments"},"review_comments":{"href":"https://api.github.com/repos/bram85/topydo/pulls/101/comments"},"review_comment":{"href":"https://api.github.com/repos/bram85/topydo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/bram85/topydo/pulls/101/commits"},"statuses":{"href":"https://api.github.com/repos/bram85/topydo/statuses/28aeffef1db2279743593d945b3bfaa54f4899dc"}}}}