summaryrefslogtreecommitdiffstats
path: root/trunk/source/macro.cc
diff options
context:
space:
mode:
authordshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2006-08-02 12:54:15 +0000
committerdshaligram <dshaligram@c06c8d41-db1a-0410-9941-cceddc491573>2006-08-02 12:54:15 +0000
commitd5e5340c3926d1cf97f6cba151ffaecb20bfb35f (patch)
treed1faf7d5b27df8f3c523a8dd33357804118e62b1 /trunk/source/macro.cc
parent7b2204d69f21d7075e4666ee032d7a129081bc4b (diff)
downloadcrawl-ref-d5e5340c3926d1cf97f6cba151ffaecb20bfb35f.tar.gz
crawl-ref-d5e5340c3926d1cf97f6cba151ffaecb20bfb35f.zip
Integrated travel patch as of 20060727
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7 c06c8d41-db1a-0410-9941-cceddc491573
Diffstat (limited to 'trunk/source/macro.cc')
-rw-r--r--trunk/source/macro.cc454
1 files changed, 330 insertions, 124 deletions
diff --git a/trunk/source/macro.cc b/trunk/source/macro.cc
index 4cd5c62ede..13838a947c 100644
--- a/trunk/source/macro.cc
+++ b/trunk/source/macro.cc
@@ -37,11 +37,13 @@
#include <string>
#include <map>
#include <deque>
+#include <vector>
#include <stdio.h> // for snprintf
#include <ctype.h> // for tolower
#include "externs.h"
+#include "stuff.h"
// for trim_string:
#include "initfile.h"
@@ -50,11 +52,117 @@ typedef std::deque<int> keyseq;
typedef std::deque<int> keybuf;
typedef std::map<keyseq,keyseq> macromap;
-static macromap Keymaps;
+static macromap Keymaps[KC_CONTEXT_COUNT];
static macromap Macros;
+static macromap *all_maps[] =
+{
+ &Keymaps[KC_DEFAULT],
+ &Keymaps[KC_LEVELMAP],
+ &Keymaps[KC_TARGETING],
+ &Macros,
+};
+
static keybuf Buffer;
-
+
+#define USERFUNCBASE -10000
+static std::vector<std::string> userfunctions;
+
+inline int userfunc_index(int key)
+{
+ int index = (key <= USERFUNCBASE? USERFUNCBASE - key : -1);
+ return (index < 0 || index >= (int) userfunctions.size()? -1 : index);
+}
+
+static int userfunc_index(const keyseq &seq)
+{
+ if (seq.empty())
+ return (-1);
+
+ return userfunc_index(seq.front());
+}
+
+bool is_userfunction(int key)
+{
+ return (userfunc_index(key) != -1);
+}
+
+static bool is_userfunction(const keyseq &seq)
+{
+ return (userfunc_index(seq) != -1);
+}
+
+const char *get_userfunction(int key)
+{
+ int index = userfunc_index(key);
+ return (index == -1? NULL : userfunctions[index].c_str());
+}
+
+static const char *get_userfunction(const keyseq &seq)
+{
+ int index = userfunc_index(seq);
+ return (index == -1? NULL : userfunctions[index].c_str());
+}
+
+static bool userfunc_referenced(int index, const macromap &mm)
+{
+ for (macromap::const_iterator i = mm.begin(); i != mm.end(); i++)
+ {
+ if (userfunc_index(i->second) == index)
+ return (true);
+ }
+ return (false);
+}
+
+static bool userfunc_referenced(int index)
+{
+ for (unsigned i = 0; i < sizeof(all_maps) / sizeof(*all_maps); ++i)
+ {
+ macromap *m = all_maps[i];
+ if (userfunc_referenced(index, *m))
+ return (true);
+ }
+ return (false);
+}
+
+// Expensive function to discard unused function names
+static void userfunc_collectgarbage(void)
+{
+ for (int i = userfunctions.size() - 1; i >= 0; --i)
+ {
+ if (!userfunctions.empty() && !userfunc_referenced(i))
+ userfunctions[i].clear();
+ }
+}
+
+static int userfunc_getindex(const std::string &fname)
+{
+ if (fname.length() == 0)
+ return (-1);
+
+ userfunc_collectgarbage();
+
+ // Pass 1 to see if we have this string already
+ for (int i = 0, count = userfunctions.size(); i < count; ++i)
+ {
+ if (userfunctions[i] == fname)
+ return (i);
+ }
+
+ // Pass 2 to hunt for gaps
+ for (int i = 0, count = userfunctions.size(); i < count; ++i)
+ {
+ if (userfunctions[i].empty())
+ {
+ userfunctions[i] = fname;
+ return (i);
+ }
+ }
+
+ userfunctions.push_back(fname);
+ return (userfunctions.size() - 1);
+}
+
/*
* Returns the name of the file that contains macros.
*/
@@ -68,6 +176,29 @@ static std::string get_macro_file()
return (s + "macro.txt");
}
+static void buf2keyseq(const char *buff, keyseq &k)
+{
+ // Sanity check
+ if (!buff)
+ return;
+
+ // convert c_str to keyseq
+
+ // Check for user functions
+ if (*buff == '=' && buff[1] == '=' && buff[2] == '=' && buff[3])
+ {
+ int index = userfunc_getindex(buff + 3);
+ if (index != -1)
+ k.push_back( USERFUNCBASE - index );
+ }
+ else
+ {
+ const int len = strlen( buff );
+ for (int i = 0; i < len; i++)
+ k.push_back( buff[i] );
+ }
+}
+
/*
* Takes as argument a string, and returns a sequence of keys described
* by the string. Most characters produce their own ASCII code. There
@@ -81,41 +212,47 @@ static keyseq parse_keyseq( std::string s )
keyseq v;
int num;
+ if (s.find("===") == 0)
+ {
+ buf2keyseq(s.c_str(), v);
+ return (v);
+ }
+
for (std::string::iterator i = s.begin(); i != s.end(); i++)
{
- char c = *i;
-
- switch (state)
- {
- case 0: // Normal state
- if (c == '\\') {
- state = 1;
- } else {
- v.push_back(c);
- }
- break;
-
- case 1: // Last char is a '\'
- if (c == '\\') {
- state = 0;
- v.push_back(c);
- } else if (c == '{') {
- state = 2;
- num = 0;
- }
- // XXX Error handling
- break;
-
- case 2: // Inside \{}
- if (c == '}') {
- v.push_back(num);
- state = 0;
- } else if (c >= '0' && c <= '9') {
- num = num * 10 + c - '0';
- }
- // XXX Error handling
+ char c = *i;
+
+ switch (state)
+ {
+ case 0: // Normal state
+ if (c == '\\') {
+ state = 1;
+ } else {
+ v.push_back(c);
+ }
+ break;
+
+ case 1: // Last char is a '\'
+ if (c == '\\') {
+ state = 0;
+ v.push_back(c);
+ } else if (c == '{') {
+ state = 2;
+ num = 0;
+ }
+ // XXX Error handling
break;
- }
+
+ case 2: // Inside \{}
+ if (c == '}') {
+ v.push_back(num);
+ state = 0;
+ } else if (c >= '0' && c <= '9') {
+ num = num * 10 + c - '0';
+ }
+ // XXX Error handling
+ break;
+ }
}
return (v);
@@ -125,13 +262,25 @@ static keyseq parse_keyseq( std::string s )
* Serializes a key sequence into a string of the format described
* above.
*/
-static std::string vtostr( keyseq v )
+static std::string vtostr( const keyseq &seq )
{
std::string s;
+
+ const keyseq *v = &seq;
+ keyseq dummy;
+ if (is_userfunction(seq))
+ {
+ // Laboriously reconstruct the original user input
+ std::string newseq = std::string("==") + get_userfunction(seq);
+ buf2keyseq(newseq.c_str(), dummy);
+ dummy.push_front('=');
+
+ v = &dummy;
+ }
- for (keyseq::iterator i = v.begin(); i != v.end(); i++)
+ for (keyseq::const_iterator i = v->begin(); i != v->end(); i++)
{
- if (*i < 32 || *i > 127) {
+ if (*i <= 32 || *i > 127) {
char buff[10];
snprintf( buff, sizeof(buff), "\\{%d}", *i );
@@ -154,13 +303,13 @@ static std::string vtostr( keyseq v )
// in libutil.cc to make sure of that! -- bwr
//
// std::ostrstream ss;
- // ss << "\\{" << *i << "}" << ends;
- // s += ss.str();
- } else if (*i == '\\') {
- s += "\\\\";
- } else {
- s += *i;
- }
+ // ss << "\\{" << *i << "}" << ends;
+ // s += ss.str();
+ } else if (*i == '\\') {
+ s += "\\\\";
+ } else {
+ s += *i;
+ }
}
return (s);
@@ -174,6 +323,14 @@ static void macro_add( macromap &mapref, keyseq key, keyseq action )
mapref[key] = action;
}
+static void macro_add( macromap &mapref, keyseq key, const char *buff )
+{
+ keyseq act;
+ buf2keyseq(buff, act);
+ if (!act.empty())
+ macro_add( mapref, key, act );
+}
+
/*
* Remove a macro.
*/
@@ -190,15 +347,15 @@ static void macro_del( macromap &mapref, keyseq key )
static void macro_buf_add( keyseq actions )
{
for (keyseq::iterator i = actions.begin(); i != actions.end(); i++)
- Buffer.push_back(*i);
+ Buffer.push_back(*i);
}
/*
* Adds a single keypress into the internal keybuffer.
*/
-static void macro_buf_add( int key )
+void macro_buf_add( int key )
{
- Buffer.push_back( key );
+ Buffer.push_back( key );
}
@@ -206,7 +363,7 @@ static void macro_buf_add( int key )
* Adds keypresses from a sequence into the internal keybuffer. Does some
* O(N^2) analysis to the sequence to replace macros.
*/
-static void macro_buf_add_long( keyseq actions )
+static void macro_buf_add_long( keyseq actions, macromap &keymap = Keymaps[KC_DEFAULT] )
{
keyseq tmp;
@@ -221,36 +378,36 @@ static void macro_buf_add_long( keyseq actions )
while (actions.size() > 0)
{
- tmp = actions;
-
- while (tmp.size() > 0)
+ tmp = actions;
+
+ while (tmp.size() > 0)
{
- keyseq result = Keymaps[tmp];
-
- // Found a macro. Add the expansion (action) of the
- // macro into the buffer.
- if (result.size() > 0) {
- macro_buf_add( result );
- break;
- }
-
- // Didn't find a macro. Remove a key from the end
- // of the sequence, and try again.
- tmp.pop_back();
- }
-
- if (tmp.size() == 0) {
- // Didn't find a macro. Add the first keypress of the sequence
- // into the buffer, remove it from the sequence, and try again.
- macro_buf_add( actions.front() );
- actions.pop_front();
-
- } else {
- // Found a macro, which has already been added above. Now just
- // remove the macroed keys from the sequence.
- for (unsigned int i = 0; i < tmp.size(); i++)
- actions.pop_front();
- }
+ keyseq result = keymap[tmp];
+
+ // Found a macro. Add the expansion (action) of the
+ // macro into the buffer.
+ if (result.size() > 0) {
+ macro_buf_add( result );
+ break;
+ }
+
+ // Didn't find a macro. Remove a key from the end
+ // of the sequence, and try again.
+ tmp.pop_back();
+ }
+
+ if (tmp.size() == 0) {
+ // Didn't find a macro. Add the first keypress of the sequence
+ // into the buffer, remove it from the sequence, and try again.
+ macro_buf_add( actions.front() );
+ actions.pop_front();
+
+ } else {
+ // Found a macro, which has already been added above. Now just
+ // remove the macroed keys from the sequence.
+ for (unsigned int i = 0; i < tmp.size(); i++)
+ actions.pop_front();
+ }
}
}
@@ -275,7 +432,7 @@ static void macro_buf_apply_command_macro( void )
// Add macro to front:
for (keyseq::reverse_iterator k = result.rbegin(); k != result.rend(); k++)
- Buffer.push_front(*k);
+ Buffer.push_front(*k);
break;
}
@@ -299,6 +456,20 @@ static int macro_buf_get( void )
return (key);
}
+static void write_map(std::ofstream &f, const macromap &mp, const char *key)
+{
+ for (macromap::const_iterator i = mp.begin(); i != mp.end(); i++)
+ {
+ // Need this check, since empty values are added into the
+ // macro struct for all used keyboard commands.
+ if (i->second.size())
+ {
+ f << key << vtostr((*i).first) << std::endl
+ << "A:" << vtostr((*i).second) << std::endl << std::endl;
+ }
+ }
+}
+
/*
* Saves macros into the macrofile, overwriting the old one.
*/
@@ -309,30 +480,15 @@ void macro_save( void )
f << "# WARNING: This file is entirely auto-generated." << std::endl
<< std::endl << "# Key Mappings:" << std::endl;
-
- for (macromap::iterator i = Keymaps.begin(); i != Keymaps.end(); i++)
- {
- // Need this check, since empty values are added into the
- // macro struct for all used keyboard commands.
- if ((*i).second.size() > 0)
- {
- f << "K:" << vtostr((*i).first) << std::endl
- << "A:" << vtostr((*i).second) << std::endl << std::endl;
- }
+ for (int mc = KC_DEFAULT; mc < KC_CONTEXT_COUNT; ++mc) {
+ char keybuf[30] = "K:";
+ if (mc)
+ snprintf(keybuf, sizeof keybuf, "K%d:", mc);
+ write_map(f, Keymaps[mc], keybuf);
}
f << "# Command Macros:" << std::endl;
-
- for (macromap::iterator i = Macros.begin(); i != Macros.end(); i++)
- {
- // Need this check, since empty values are added into the
- // macro struct for all used keyboard commands.
- if ((*i).second.size() > 0)
- {
- f << "M:" << vtostr((*i).first) << std::endl
- << "A:" << vtostr((*i).second) << std::endl << std::endl;
- }
- }
+ write_map(f, Macros, "M:");
f.close();
}
@@ -341,17 +497,20 @@ void macro_save( void )
* Reads as many keypresses as are available (waiting for at least one),
* and returns them as a single keyseq.
*/
-static keyseq getch_mul( void )
+static keyseq getch_mul( int (*rgetch)() = NULL )
{
keyseq keys;
int a;
- keys.push_back( a = getch() );
+ if (!rgetch)
+ rgetch = getch;
+
+ keys.push_back( a = rgetch() );
// The a == 0 test is legacy code that I don't dare to remove. I
// have a vague recollection of it being a kludge for conio support.
while ((kbhit() || a == 0)) {
- keys.push_back( a = getch() );
+ keys.push_back( a = rgetch() );
}
return (keys);
@@ -361,7 +520,12 @@ static keyseq getch_mul( void )
* Replacement for getch(). Returns keys from the key buffer if available.
* If not, adds some content to the buffer, and returns some of it.
*/
-int getchm( void )
+int getchm( int (*rgetch)() )
+{
+ return getchm( KC_DEFAULT, rgetch );
+}
+
+int getchm(KeymapContext mc, int (*rgetch)())
{
int a;
@@ -370,9 +534,9 @@ int getchm( void )
return (a);
// Read some keys...
- keyseq keys = getch_mul();
+ keyseq keys = getch_mul(rgetch);
// ... and add them into the buffer
- macro_buf_add_long( keys );
+ macro_buf_add_long( keys, Keymaps[mc] );
return (macro_buf_get());
}
@@ -411,15 +575,31 @@ void macro_add_query( void )
{
unsigned char input;
bool keymap = false;
+ KeymapContext keymc = KC_DEFAULT;
- mpr( "Command (m)acro or (k)eymap? ", MSGCH_PROMPT );
+ mesclr();
+ mpr( "Command (m)acro or keymap [(k) default, (x) level-map or "
+ "(t)argeting]? ", MSGCH_PROMPT );
input = getch();
if (input == 0)
input = getch();
input = tolower( input );
if (input == 'k')
+ {
+ keymap = true;
+ keymc = KC_DEFAULT;
+ }
+ else if (input == 'x')
+ {
keymap = true;
+ keymc = KC_LEVELMAP;
+ }
+ else if (input == 't')
+ {
+ keymap = true;
+ keymc = KC_TARGETING;
+ }
else if (input == 'm')
keymap = false;
else
@@ -429,9 +609,14 @@ void macro_add_query( void )
}
// reference to the appropriate mapping
- macromap &mapref = (keymap ? Keymaps : Macros);
-
- snprintf( info, INFO_SIZE, "Input %s trigger key: ",
+ macromap &mapref = (keymap ? Keymaps[keymc] : Macros);
+
+ snprintf( info, INFO_SIZE, "Input %s%s trigger key: ",
+ keymap?
+ (keymc == KC_DEFAULT? "default " :
+ keymc == KC_LEVELMAP? "level-map " :
+ "targeting ") :
+ "",
(keymap ? "keymap" : "macro") );
mpr( info, MSGCH_PROMPT );
@@ -472,17 +657,19 @@ void macro_add_query( void )
// to resort to editing files and restarting the game. -- bwr
// keyseq act = getch_mul();
- keyseq act;
char buff[4096];
+ get_input_line(buff, sizeof buff);
- get_input_line( buff, sizeof(buff) );
-
- // convert c_str to keyseq
- const int len = strlen( buff );
- for (int i = 0; i < len; i++)
- act.push_back( buff[i] );
+ if (Options.macro_meta_entry)
+ {
+ macro_add( mapref, key, parse_keyseq(buff) );
+ }
+ else
+ {
+ macro_add( mapref, key, buff );
+ }
- macro_add( mapref, key, act );
+ redraw_screen();
}
@@ -495,6 +682,7 @@ int macro_init( void )
std::ifstream f;
keyseq key, action;
bool keymap = false;
+ KeymapContext keymc = KC_DEFAULT;
f.open( get_macro_file().c_str() );
@@ -506,20 +694,38 @@ int macro_init( void )
continue; // skip comments
} else if (s.substr(0, 2) == "K:") {
- key = parse_keyseq(s.substr(2));
+ key = parse_keyseq(s.substr(2));
keymap = true;
-
+ keymc = KC_DEFAULT;
+
+ } else if (s.length() >= 3 && s[0] == 'K' && s[2] == ':') {
+ keymc = KeymapContext( KC_DEFAULT + s[1] - '0' );
+ if (keymc >= KC_DEFAULT && keymc < KC_CONTEXT_COUNT) {
+ key = parse_keyseq(s.substr(3));
+ keymap = true;
+ }
+
} else if (s.substr(0, 2) == "M:") {
- key = parse_keyseq(s.substr(2));
+ key = parse_keyseq(s.substr(2));
keymap = false;
- } else if (s.substr(0, 2) == "A:") {
- action = parse_keyseq(s.substr(2));
- macro_add( (keymap ? Keymaps : Macros), key, action );
- }
+ } else if (s.substr(0, 2) == "A:") {
+ action = parse_keyseq(s.substr(2));
+ macro_add( (keymap ? Keymaps[keymc] : Macros), key, action );
+ }
}
return (0);
}
+#ifdef CLUA_BINDINGS
+void macro_userfn(const char *keys, const char *regname)
+{
+ // TODO: Implement.
+ // Converting 'keys' to a key sequence is the difficulty. Doing it portably
+ // requires a mapping of key names to whatever getch() spits back, unlikely
+ // to happen in a hurry.
+}
+#endif
+
#endif